文章目录
git本地仓库基本结构
- 工作区:.git文件夹所在的目录,本地文件夹
- .git文件夹:版本库
- .git内的stage文件夹: 暂存区
1. 本地GIT仓库基本操作
首次使用git, 应该先指明全局git用户信息. 直接打开Git bash, 输入
git config --global user.name "<用户名>"
git config --global user.email "<邮箱>"
若想为某个仓库单独设置用户信息, 则在该仓库内打开git bash, 去掉--global
, 然后指定信息即可
git init
在文件夹中打开git bash, 输入这个指令后, 根目录下就多了一个.git文件夹, 这是GIT的版本库
1.1 工作区操作
git status
查看工作区状态变化,告知你是否有文件变化, 比如新增文件, 删除文件, 文件内容变化, 哪些文件已经add, 哪些文件还没有被跟踪(从来没有add过, 或者是被.gitignore所忽略的文件)
git diff
查看工作区文件内文件的内容改动
git add <file>
把某个文件的 改动 add到暂存区(stage file)中, 多个文件则用空格隔开
git add .
把当前工作区所有文件 改动 add到stage,也可以 git add -A
或者 git add *
。他们的区别看这里
git reset .
取消目前暂存区的所有改动,让它们回到工作目录中未暂存的状态。具体来说,git reset . 会将当前目录下所有已经被 git add 命令添加到暂存区的文件,从暂存区中移除,使得这些文件的状态变成未暂存(也就是未添加)的状态。git reset . 不会撤销文件的修改,而只是将修改的文件从暂存区移除,恢复到工作目录中未暂存的状态
git commit -m "<备注信息>"
用于将已添加到暂存区中的文件改动提交到Git仓库中,并创建一个新的 提交记录
git commit --amend
同样是提交stage中的改动到仓库中, 不同的是改动将 合并到最新的提交记录内 , 不创建新的提交记录
git checkout -- <file>
撤销工作区的更改,让文件回到最近一次提交时的状态。如果文件已经添加到暂存区(stage区), 那么该命令会将文件恢复到暂存区的状态。如果文件还没有添加到暂存区,那么该命令会将文件恢复到最新的提交版本的状态。
Git跟踪并管理的是修改,而非文件。
1.2 本地仓库操作
git log
or git log --pretty=oneline
显示git版本库所有提交记录, 该命令显示了提交的SHA哈希值、作者、提交日期、提交消息以及与该提交相关联的文件更改
HEAD指针: 指向当前所在分支最新一次提交的指针, 可以理解为是当前分支的别名. 当提交更改或创建新分支,Git会自动更新HEAD指向最新的提交。当你进行新的提交时,Git会将新的提交放在HEAD所指向的提交的后继节点,同时更改HEAD指向最新的提交。这样,HEAD就一直指向最新的提交,以表示当前分支的状态. 在Git中,可以使用"git log"命令来查看当前分支的提交历史记录,其中HEAD会显示在顶部。另外,可以使用"git checkout"命令来移动HEAD指针到其他分支或提交,从而切换到其他的代码状态。当HEAD指向一个特定的提交时,它被称为"detached HEAD"状态,此时不再与任何分支相关联。
git reset --hard <HEAD>
会重置工作区和暂存区的内容,将它们恢复到最近一次提交时的状态. 需要注意它会丢弃当前工作区和暂存区的所有未提交的更改,包括添加、修改和删除的文件, 请谨慎使用–hard指令
git reset --hard <具体版本号>
回到指定版本的提交, 需要指定具体该版本的SHA哈希值(不用输入全部, 只需要前面几个字符即可). (当你回到某个具体的版本后, HEAD指针指向当前版本, 此时git log
也看不到当前提交版本的后面所有的版本了, 如果还想后悔, 在命令行窗口没有关闭的情况下, 找到之前命令记录, 如果还能找到那个版本号, 则还可以reset回该版本, 并且该版本之前的所有提交记录也都会恢复)
git reflog
后悔药. 显示本地Git仓库的 引用日志. 引用日志记录了Git仓库中引用的变化历史,包括分支、标签等,以及这些引用所指向的提交记录,包括HEAD的变化历史。可以使用git reflog命令找回误删的分支(可以reset到它)或提交记录,或者查找之前执行的Git命令历史记录.
引用(Reference): 指向一个Git对象(如提交记录)的指针,相当于一个别名或标签。Git中的每个引用都有一个名称,它们指向一个Git对象的SHA值。Git中最常见的引用类型:
- 分支: 指向一系列Git提交记录的指针,表示一个独立的开发线。每个分支都有一个名称,并且可以随时切换到不同的分支上
- 标签: 是指向特定Git提交记录的指针,通常用于标记版本发布。每个标签都有一个名称和一个描述,可以用于在Git仓库中标记重要的里程碑
- HEAD: 指向当前正在工作的分支或提交记录,表示当前工作区所处的状态。
- 远程跟踪引用: 是指向远程Git仓库中分支的指针,用于记录本地分支与远程分支之间的关联关系。
git diff <SHA1> <SHA2> --stat
对比两个版本的状态差异, 结果是简洁的统计信息,其中包括哪些文件发生了更改、文件的插入和删除的行数、文件的更改百分比等信息。适合快速查看代码库的变化趋势。
git diff <SHA1> <SHA2>
会显示详细的文件差异内容,包括每个文件中哪些行发生了改变、被删除或者被添加. 对大型代码库来说,输出结果非常长,不方便阅读。
2. 分支操作
详细请看廖雪峰的官方网站:创建与合并分支
查看现有分支信息
git branch
列出本地仓库所有分支, 并且在当前所在分支上标注星号git branch -r
列出远程库所有分支git branch -a
列出所有本地 和 远程库的分支名称
分支,仅区分本地 与 远程
创建/删除/切换分支
git branch <name>
创建新分支 (注意跟列出本地所有分支的区别, 仅在其后多了一个分支名称)git branch -d <name>
删除分支git branch -D <name>
强行删除分支(没有合并到主分支, 用-d删不掉)git checkout <name>
切换到目标分支git checkout -b <name>
创建并切换到一个新的分支
分支合并
git merge <name>
将指定分支的修改合并到当前分支中- 如果合并过程中发生冲突,Git 会将冲突标记出来,并提示你手动解决冲突。解决冲突后,可以使用
git add
命令将修改添加到暂存区,然后再使用git commit
命令提交合并结果。再提交合并结果 - 合并分支之前,最好先使用
git pull
同步该分支在远程仓库的最新提交版本,主要是为了避免冲突, 总之,保持当前分支的代码与远程仓库同步是一个好习惯,可以避免许多麻烦。
- 如果合并过程中发生冲突,Git 会将冲突标记出来,并提示你手动解决冲突。解决冲突后,可以使用
合并目标分支到的当前分支:git merge --no-ff -m "<information>" <branch>
(关闭fast forward)
2.1 合并分支冲突解决
当两个分支的修改无法自动合并时,就会产生冲突。这通常发生在两个分支同时修改了同一个文件的相同部分,而 Git 无法确定应该采用哪个版本。
git status
告诉你哪些文件存在冲突,以及冲突的具体位置. 此时点开该冲突文件,冲突内容会被标记出来,删除<<<===>>>等内容,留下自己最终想要留下的代码即可. 解决冲突后, 用git add
将解决后的冲突文件暂存, 解决完所有冲突后, 一次性git commit
提交本地库即可
git log --graph --pretty=oneline --abbrev-commit
这个命令可以显示 Git 仓库的提交历史,同时按照提交的分支和合并的情况画出图形, 可以很清晰的看到分支提交路径, 合并路径
请注意,当解决合并冲突时,应谨慎考虑所做的更改。如果您不确定如何解决冲突,请考虑在备份分支上尝试合并操作,以便能够撤消任何错误更改。
2.2 合并分支的fast forward概念
fast forward: 用git merge <name>
合并一个分支到当前分支, 并且该分支是基于当前支新增了一些修改, 则合并的操作仅仅是把当前分支的指针直接指向该分支. 如果此时删除该分支, 则我们根本看不出来我们是从一个分支合并过来的. 比如当前在master主分支上, 我们要开发一些新功能, 一般会用git checkout -b dev
创建一个dev分支, 完成功能开发, 测试无误后合并到master分支上. 那么如果是git checkout master
切换到主分支上, 通过git merge dev
把dev分支合并到当前的master分支上, 则默认执行 fast forward 合并. 在删除dev分支后, 用 git log
命令查看提交历史, 你根本就看不到dev分支曾经存在过. 用 git log --graph --pretty=oneline --abbrev-commit
也看不到合并操作的图形指示…
强烈建议用 --no-ff
参数来显式地指示 Git 执行不使用 fast forward 模式的合并,而是创建一个新的提交来合并两个分支的历史记录, 这样的好处是, 即使我们在合并dev分支到master分支之后, 删除了dev分支, 我们查看历史 依旧可以清晰明确的知道, 我们曾经从dev分支合并了一次.
2.3 stash储藏表
暂存区stage和储藏表stash,都是用于存放工作区的修改内容,不同的是
- stage存放的内容已经编辑完成,即将commit
- stash存放的内容还没编辑完,不准备commit,只是暂时要切换到其他分支做事,事后还会切回来继续工作
git stash
:把当前分支工作区的修改内容隐藏到stash list中,工作区干净了,
git stash list
:查看stash中的存储记录
想要从储藏室恢复之前工作区内容有两种方式
- 分步:
git stash apply
恢复,git stash drop
手动删除stash中的记录 - 一步:
git stash pop
git cherry-pick <commit id>
复制某个特定的提交到当前分支
3 远程库
3.1 创建自己的github远程库
GitHub支持两种协议进行代码的访问和推送,分别是HTTP和SSH协议。
- 使用HTTP协议进行访问和推送代码,需要每次操作都输入用户名和密码,而且在访问量较大时,速度会变慢。此外,使用HTTP协议也会有一些安全性的问题,比如传输过程中可能会被中间人攻击窃取账号密码等敏感信息。
- 相比之下,使用SSH协议可以更加高效和安全地访问和推送代码。SSH协议通过公钥和私钥进行身份验证,可以避免每次都需要输入用户名和密码的问题,并且数据传输过程中也更加安全。克隆一个仓库, 比HTTP协议快上10倍不止
- 总而言之, 尽可能使用SSH协议。但是需要注意的是,使用SSH协议需要事先配置好公钥和私钥,同时也需要在GitHub上添加公钥,才能够正常使用。
在C盘用户目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下面这一步
创建密钥: SSH密钥实际上是用于验证你的本地计算机和你在GitHub上的帐户之间的身份的一组密钥对。当你在GitHub上创建SSH密钥并将公钥添加到你的GitHub帐户时,你就可以使用该密钥对进行身份验证,以便在本地计算机和GitHub之间进行安全的通信。具体来说,当你向GitHub发送请求时,GitHub会使用你的公钥对请求进行加密,然后发送给你的本地计算机。本地计算机使用私钥对请求进行解密,从而证明请求来自你的GitHub帐户,并且你具有访问所请求资源的权限
ssh-keygen -t rsa -C "<邮箱>"
. -t rsa表示使用RSA算法生成密钥对,-C选项可以添加注释以帮助你识别密钥。在执行该命令后,会提示你输入密钥保存路径和文件名,并提示你输入密码(可选)。你可以按照提示输入或一直回车保持默认。完成上述步骤后,你应该能在.ssh文件夹中看到id_rsa和id_rsa.pub两个文件。其中,id_rsa是你的私钥文件,id_rsa.pub是你的公钥文件。
- 进入github账户setting页面, 左边的“
SSH and GPG keys
”,点击"new SSH key
"填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容
GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
3.2 关联本地git仓库与远程github仓库
方法1:
- github上创建空白仓库, 复制SSH地址
- 本地新建文件夹, 用命令
git clone <ssh地址>
克隆仓库 - 克隆操作会让Git自动把本地的master分支和远程的master分支对应起来
方法2:
- 在github创建空白仓库后,复制SSH地址
git remote add origin <ssh地址>
直接将本地已经存在的GIT仓库与远程github仓库建立关联
3.3 远程库命令
git remote -v
查看远程库信息, 显示可以抓取和推送的地址。如果没有推送权限,就看不到push地址git remote rm <远程库名字>
切断本地仓库与远程仓库的联系$ git push <远程库名> <具体分支名>
简化版命令, 推送本地仓库的最新提交到远程库(如果还建立上游分支,则会报错, 提示先建立上游分支,才能使用简化命令)git push -u origin master
使用-u参数时,Git会将本地分支推送到远程仓库的同时将远程分支(origin/master)设置为本地分支(master)的上游分支。这意味着,以后如果你在本地分支master中进行了更改,只需要执行git push
命令,Git就会自动将更改推送到已关联的远程分支上。同样的如果你新开了一个分支dev, 在push dev分支的时候使用了git push -u origin dev
, 则后续的dev分支的推送, 也可以简化成git push
, 前提是你当前所在分支是dev.git remote set-url origin <远程库地址>
更改远程仓库地址,链接到一个新的github远程库。应用场景:- 更改账户名后,地址中的用户名会改变,可能导致链接不到远程库,运行此命令可以链接到新的地址
- 创建一个新的仓库,提交代码到该仓库中,需要更改原来的远程地址为新仓库地址
- 想把HTTP换成SSH
4. 多人协作
试图用git push origin <branch-name>
推送自己的修改
- 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
把远程库的最新改动合并到本地仓库- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功!
- 如果
git pull
提示no tracking information,则说明本地创建的分支和远程分支的链接关系没有创建 - 用
git branch --set-upstream-to <branch-name> <远程库-name>/<branch-name>
,建立本地分支和远程分支的关联- 不行就用这个:
git branch --set-upstream-to=origin/<branch> master
- 不行就用这个:
git pull
和 git fetch
都是将远程仓库中的最新代码更新到本地仓库中,但它们之间有一些区别
git fetch
命令将远程仓库中的最新代码拉取到本地仓库中,但并不会自动合并本地代码和远程代码,需要使用git merge
命令将它们合并。使用 git fetch 命令可以查看远程仓库中的最新变化,但是不会修改本地代码。因此,它是一种安全的方法,可以让您在合并之前先预览更改。git pull
命令与 git fetch 类似,但它自动合并本地代码和远程代码。它首先运行 git fetch 命令,然后将获取到的远程分支自动合并到当前本地分支。如果本地分支与远程分支之间有冲突,git pull 命令会尝试自动解决冲突并进行合并,如果解决不了则需要手动解决。解决后,需要运行git add
命令来标记文件已解决冲突,并运行git commit
命令将其提交到本地仓库。
不一定要把本地分支全部往远程推送,哪些分支需要推送,哪些不需要呢?
master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
5 Rabase
git rebase
将一个分支的修改应用到另一个分支上。与git merge不同,它不是将分支合并到目标分支上,而是将目标分支上的修改移到当前分支上。
案例1:假设我们有两个分支,master 和 feature。在 feature 分支上有两个提交记录 C1 和 C2,而在 master 分支上也有两个提交记录 C3 和 C4,如下所示:
如果我们想将 feature 分支的提交历史移动到 master 分支后面,可以使用 git rebase 命令:
git checkout feature
git rebase master
可以看到,使用 git rebase 后,原本的提交记录 C1 和 C2 被移动到了 C3 和 C4 的后面,形成了新的提交记录 C1’ 和 C2’。这样做的好处是可以使提交历史更加清晰、线性,便于代码审查和维护。
如果直接在master分支上 执行 git rebase
, 则默认是把本地master变基到远程库的master
6. 标签管理
创建标签
- 首先,切换到需要创建标签的分支上:
git checkout <branch-name>
git tag <tag-name> <commit-id>
创建标签,不指定<commit-id>
则创建在最新commit上,git tag -a <tag-name> -m "..." <commit-id>
创建带说明的标签:git tag
列出所有标签,按照首字母排序git show <tagname>
查看具体标签的信息
标签操作
- 删除本地标签:
git tag -d <tag-name>
- 推送单个标签到远程:
git push <远程库名> <tag-name>
- 推送所有标签到远程:
git push <远程库名> --tags
想删除远程标签
- 先删除本地标签:
git tag -d <tag-name>
- 再删除远程标签:
git push <远程库名>:refs/tags/v0.9
依然是push操作,但命令不同
7 github Fork
功能介绍
-
当你Fork一个项目时,会将原有项目的代码库全部克隆到自己的GitHub账户下,并自动创建一个与原有项目同名的新项目。你可以在自己的项目中进行任何修改,并向自己的远程github仓库提交
-
自己拥有Fork后的仓库的读写权限, 如果不fork,仅仅是clone原作者的仓库到本地, 你是不能推送自己的代码改动到远程仓库的.
-
fork仓库后, 可以pull request给原作者的仓库来贡献代码
同步原作者仓库的最新提交, 步骤:
-
首先需要将原作者的仓库添加为自己的本地仓库的
另一个远程仓库
(还有一个远程库是自己fork下来的)git remote add upstream <原作者的仓库地址>
-
然后从原作者的仓库中拉取最新的代码
git fetch upstream
-
将本地代码切换到主分支(通常是 master 分支)
git checkout master
-
将本地的主分支与原作者的主分支合并, 这将把原作者的最新代码合并到你的本地主分支
git merge upstream/master
-
如果本地主分支有冲突,需要手动解决冲突并提交到本地库
-
最后将更新后的代码推送到
自己fork的远程仓库
git push origin master
8 克隆带有子模块的github仓库
git clone --recurse-submodules <地址>
命令会在克隆完毕后自动递归初始化所有子模块(包括子模块的子模块)
如果出现子模块克隆失败的问题,可以在根目录中运行命令重新克隆所有子模块:git submodule update --init --recursive
9 在自己代码库使用第三方库(来自github某个仓库)的最好方式
- 复制第三方库的HTTP链接,在本地仓库根目录打开命令行,以下命令:
目标路径是你想要存放这个仓库代码的地方,这个路径是以当前目录为起始点的子路径git submodule add <http链接> <目标路径>
- 如果克隆成功,目录下会多出一个.gitmodules.txt文件,用于记录你的仓库所引用的其他github仓库的地址以及存放路径。可以用文本编辑器打开看看它的内容
这样的方式最明显的好处是:
- 可以方便地将第三方库的更新合并到自己代码库中。因为可以通过 Git 子模块跟踪第三方库的变化,从而能够快速、方便地将其更新到最新版本
- 可以更好地管理依赖关系。使用 Git 子模块,可以将所有依赖项的源代码集中在一个地方,并轻松地共享依赖项。
参考 廖雪峰的GIT教学