git命令详解
Git基本概念
Git是什么
Git是一个分布式代码管理工具,而SVN则是集中式代码管理工具。
git.jpg
- 集中式: 所有的代码都保存在中央服务器,所以提交必须依赖网络,协同工作的人们都通过客户端连接到这台服务器,取出最新的文件或者提交更新
- 分布式: 每个终端都是一个仓库,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整的镜像下来,每一次的提取操作,实际上都是一次对代码仓库的完整备份。因此可以在本地进行提交,并不需要依赖网络。
工作区域
- 工作区: 实际的工作目录
- 暂存区: 代码add以后暂时存储的区域
- 仓库区: 分为本地仓库和远程仓库。 本地仓库是commit以后存储的区域, 远程仓库是push以后存储的区域
commit节点
每次Git提交以后都会生成一个节点,每个节点都会有一个哈希值作为一个唯一标示,多次提交就会形成一个线性节点链, 如下图所示,C2是最后一个提交节点,那么C2会包含前面C1和C0的提交内容。右边则是每次提交对应的哈希值
commit节点.jpg
HEAD
HEAD相当于一个指针,它可以指向任意一个节点,它指向的那个节点就是当前工作节点。如上图所示,当HEAD指向C2,那么当前工作目录就是C2。
分支
分支可以让开发分多条主线同时进行,每条主线互不影响
假如将整个项目比作一个单机游戏,节点就是玩家保存的进度,HEAD指的就是玩家当前选择的那个进度,而分支可以认为是不同的玩家在玩这个游戏的历程。
Git命令详解
1. 初始化
1.1 初始化本地仓库
image.png1.2 关联远程仓库
git remote add <自定义仓库名> + 仓库ssh地址
git init初始化仓库以后需要进行git remote操作,实现本地和远程仓库绑定,这样才算是完整创建本地仓库
10239771-c1264302673ac2d3.png
1.3 克隆远程仓库到本地
可以通过git clone的方式直接将远程仓库克隆到本地,此时远程仓库名默认为origin。
image.png
2. 查看文件状态
2.1 查看文件状态
git status
image.png
2.2 查看文件状态简览
git status -s
相对于git status来说显示的内容更简单直观一点
10239771-7f41019e0b09efa2.png
- 新添加的未跟踪的文件前面有??, 即test5.txt
- 新添加的文件已经放入暂存区的,文件前面会显示A, 即test4.txt
- 文件被修改但是没有放入暂存区,右边会出现M, 即 test2.txt和test3.txt
- 文件被修改并且已经被放入暂存区,左边会出现M, 即test.txt
- 文件被删除但是没有放入暂存区,右边会出现D
- 文件被删除并且已经被放入暂存区,左边会出现D
3. 查看修改
3.1 查看工作区所有文件的修改
git diff
diff --git a/git_test_demo/README.md b/git_test_demo/README.md
这一行显示diff下两个文件的对比。 a版本指的是修改前的README.md, b版本指的是修改后的README.mddeleted file mode 100644
删除了文件 100指的是普通文件,644代表文件权限
Index e69de29..000000
index后面的两个数字表示两个文件的hash值,因为是删除,所以后面的数字hash值为000000。diff--git a/git_test_demo/test.txt b/git_test_demo/test.txt
这一行显示diff下两个文件的对比。 a版本指的是修改前的test.txt, b版本指的是修改后的text.txtIndex 5d58fcd..f316c8d 100644
index后面的两个数字表示两个文件的hash值(index区域的5d58fcd与工作区的f316c8d对象对比)--- a/git_test_demo/test.txt
+++ b/git_test_demo/test.txt
---表示修改前, +++表示修改后@@ -2,3 +2,4 @@
10239771-2fb3db1bfd16f303.png
该行表示接下来下面显示的内容所在的位置。
-表示修改前, +表示修改后
-2,3 表示修改前test.txt文件从第2行显示,一直到第4行(2位起始行, 3位向后偏移的行数。即显示修改内容从第2行到第4行)
+2,4 表示修改后test.txt文件从第2行显示,一直到第5行(2位起始行, 3位向后偏移的行数。即显示修改内容>从第2行到第5行)
3.2 查看暂存区所有文件的修改
git diff --staged
10239771-e3829ebce25792f9.png
3.3 查看两次提交之间的差异
git diff <commit id1> <commit id2>
10239771-d3bb8111379d0d9f.png
3.4 查看两个分支之间的差异
git diff <branch1> <branch2>
10239771-f11717788431e6c0.png
3.5 查看指定提交的修改内容
git show <commit id>
image.png
3.6 查看指定文件的修改历史
git blame <file>
10239771-e18d7f154f4a0990.png
3.7 查看最近N次提交的修改内容
git log -p -N
image.png
4. 查看提交记录
4.1 查看当前分支的历史提交记录
git log
image.png
4.2 简洁的方式查看提交历史
git log --pretty=oneline
10239771-6471d6d5cfb9872b.png
4.3 查看分支合并图
git log --graph
image.png
4.4 查看所有的操作记录
git reflog
任何的操作记录都会显示在reflog上面
显示内容为Commit id + 执行的命令 + 提交描述
10239771-6f72efa3f1cf3170.png
4.5 查看指定提交记录是在哪个分支提交的
git branch --contains <commit id>
image.png
5. 添加文件
10239771-47fb8043bfa02953.png10239771-296f1c6468e5ab64.png
10239771-2a68331f954423c0.png
当前目录下修改了三个文件test.txt, test2.txt, test3.txt, 新增了test4.txt, 移除了README.md
5.1 暂存工作区所有的文件
git add . / git add -A / git add --all
10239771-7709bc2ea26a2cc5.png
10239771-45412a88dcf83b06.png
5.2 暂存工作区指定的文件
git add <file1> <file2>
10239771-bfb5f31b5ab50470.png
5.3 暂存工作区指定目录的所有文件
git add {directory}
10239771-b5ba850285aeee1b.png
5.4 暂存工作区所有文件,不包含删除的文件
git add *
10239771-5f9fab2344181bd5.png
5.5 暂存工作区所有文件,不包含新增的文件
git add -u
10239771-b46ab2da2fddeb3f.png
6. 分支
6.1 查看本地分支列表
git branch (带星号指的是当前的分支)
10239771-1d9bf08243752c0d.png
6.2 查看远程分支列表
git branch -r
10239771-4484d022e5ab5ca7.png
6.3 查看所有的分支列表(包括本地和远程)
git branch -a
10239771-2869df91a12d34ec.png
6.4 查看本地每个分支最后一次提交
git branch -v
10239771-e57a0587baa5397a.png
6.5 查看本地每个分支最后一次提交,并且与远程分支进行比较
git branch -vv
10239771-91b7c5de7de51b31.png
6.6 创建分支
git branch <branch>
10239771-52f3f1b4d155d705.png
6.7 删除指定分支
git branch -d <branch>
10239771-8ed4001b57a3b2c9.png
6.8 强制删除指定分支
git branch -D <branch>
10239771-07930ec52c2e9341.png
6.9 删除远端分支
git push origin --delete <branch>
10239771-4e2665df236cb6ce.png
6.10 切换到上一个分支
git checkout -
10239771-dc9bd14f83e3cd1e.png
6.11 切换分支
git checkout <branch>
10239771-38038617f5a15dbb.png
6.12 创建分支并且切换到该分支
git checkout -b <branch>
10239771-6683944eee31017d.png等价于git branch <branch> + git checkout <branch>
7. 提交
7.1 提交暂存区的文件到本地仓库
git commit -m "message"
10239771-9aadfd910c9e3b3f.png
7.2 跳过暂存区直接执行提交
git commit -a -m "message" / git commit -am "message"
这种方式可以直接提交工作区修改后的文件,但是新添加的文件是不会提交的,仍然需要先add到暂存区。
10239771-21c9d92cfb9207a8.png 10239771-dca6e17ba4340ff6.png
7.3 修改之前已经提交的结果
git commit --amend
10239771-914a58b6dcef53c5.png第二次提交将 代替 上一次提交的结果。适用于提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了的情况。
7.4 将本地提交推送至远端
git push origin <branch>
image.png
7.5 强制提交到远端
git push origin <branch> --force
10239771-430dbd91d65bdcef.png以下图为例, 将上面那次commit给撤销以后,然后执行git push origin cdf2发现无法推到远端仓库。
所以执行了git push origin cdf2 --force强制将远端仓库的那次提交也给撤销掉
7.6 将修改提交到远端指定的分支
git push origin <commit id>:<branch>
image.png以下图为例
- 在cdf2的分支下面提交了一个test4.txt文件
- 将test4.txt文件推到了远端cdf4的分支上
8. 拉取
8.1 拉取远端分支的最新代码
git fetch
git fetch --depth=N
只拉取最近N次的commit, 这样可以加快拉取速度。 但是由于只拉取最近N次的commit,所以相应的本地也只能看到最近的N次commit。
git fetch 会先将远端的代码拉取下来保存到FETCH_HEAD中,然后通过git merge的方式将FETCH_HEAD的代码merge到当前本地分支
10239771-67b64e0f2f53700f.png 10239771-e83db2b13ac931b6.png
8.2 拉取远端分支的最新代码, 并且合并到本地分支
git pull
git pull则是git fetch + git merge的操作, 直接更新本地分支的代码
image.png 10239771-b61f91ce03ec8f65.png 10239771-4e354cb8a01c9f3a.png
8.3 拉取远端分支的最新代码,并且合并到本地分支
git pull --rebase
当本地没有任何提交记录,那么git pull = git pull --rebase。
当本地提交了一个节点,远端同一个分支也更新了节点
通过git pull的方式会产生一个merge的提交记录
如右下图所示
image.png
通过git pull --rebase的方式,则会让整个提交记录看起来更线性。
image.png 10239771-983919b3c732a946.png
9. 回退
9.1 清空暂存区
git reset HEAD/ git reset --mixed
10239771-54270e4e2704301e.png 10239771-0cfd546d9e72949d.png
9.2 将HEAD回退N个节点
git reset HEAD~N
image.png例如N=2,那么HEAD将往前推2个节点,会指向倒数第三个节点。 回退以后保留修改文件
9.3 重置所有文件到未修改的状态
git reset --hard
10239771-f3c5372d1f16df5a.png
9.4 将HEAD回退一个节点
git reset --hard HEAD^ / git reset --hard HEAD~1
image.png
9.5 将HEAD回退到指定的节点
git reset --hard <commit id>
image.png
9.6 撤销工作区指定文件的改动
git checkout -- <file> / git restore <file>
10239771-d87a5c1120702b32.png 10239771-8f90a1d808448176.png
9.7 将HEAD指向指定节点
git checkout <commit id>
image.png image.png
9.8 将HEAD指向前一个节点
git checkout HEAD^
image.png
10239771-f14c1a85694c95a5.png
9.9 将HEAD往前移动N个节点
git checkout HEAD~N
image.png 10239771-01646bc292fa7d8e.png例如N=2,那么HEAD将往前推2个节点,会指向倒数第三个节点
9.10 还原指定节点的修改
git revert <commit id>
10239771-3a20d7dacbf66618.pngrevert以后会将原来的修改回退掉,并且新增一条提交记录
9.11 还原上次的提交
git revert HEAD
image.png
10. 保存
10.1 保存当前修改的内容
git stash
10239771-5b84eb5c04f1584f.png
10.2 查看保存的记录列表
git stash list
10239771-02692bdba3350226.png
10.3 将上一次保存的内容恢复到工作区
git stash apply
10239771-f8c37eeceebb68d7.png 10239771-928f7f9a24afec73.png
10.4 删除stash里面上一次保存的记录
10239771-2a049696306a1935.png10.5 上一次保存的内容恢复到工作区,并且删除对应的记录
git stash pop
10239771-fca4f47aee9d108d.png
10.6 清空stash里面所有保存的记录
git stash clear
10239771-9da539a36e866489.png
10.7 删除stash里面指定index的那一条记录
git stash drop <index>
10239771-6001e0112d23cb76.png
11. 标签
11.1 在指定节点上创建标签
git tag <tag name> <commit id>
10239771-afe7a60401aa45b0.png
11.2 查看所有标签
git tag
10239771-eef8ff3a3835b659.png
11.3 删除本地标签
git tag -d <tag name>
10239771-cd0d5e1e5e57a925.png
11.4 将本地指定标签推送到远端
git push origin <tag name>
image.png
11.5 将本地所有标签推送到远端
git push origin --tags
image.png
12. 合并
合并整体的链路比较复杂,并且在命令行中不好直观呈现,下一节做比较的时候再详细给出
12.1 将指定的提交节点复制到当前最新的节点上
git cherry-pick <commit id>
执行git cherry-pick <commitid>将其他分支的指定commit记录复制过来提交到当前分支
10239771-7a13ce6d267207a7.png
12.2 将指定分支的提交记录复制到当前分支
git merge <branch>
- 执行git merge cdf将cdf分支的代码合并过来
- 产生冲突以后,将文件冲突解决掉
- 执行git add .命令
- 执行git merge --continue。
- 然后就会产生一条merge的commit记录
image.png
12.3 将指定分支的提交记录复制到当前分支,并且保持线性的提交记录
git rebase
- 执行git rebase cdf将cdf分支的代码合并过来
- 产生冲突以后,将文件冲突解决掉
- 执行git add .命令
- 执行git rebase --continue。
- rebase以后的提交记录更具线性
git rebase之前的commit记录
git rebase 之后的commit记录
12.4 将多次的提交合并为一次提交
12.4.1 git rebase -i HEAD~N
将最近的N个commit合并为同一个commit。
12.4.1.1 执行前:
image.png12.4.1.2 执行后:
image.png12.4.2 git rebase -i <commit id1> <commit id2>
将<commitid1>到<commitid2>之间的提交合并成一个提交,左开右闭。所以如果要合并最近的三个提交,那么只需要<commitid1>填倒数第四个提交的commitid, <commitid2>填最近一个提交的commitid即可
12.4.2.1 执行前:
10239771-e0b98eb4eb55f320.png12.4.2.2 执行后:
10239771-dc9137290cfd3eb6.pngGit相似命令比较
1. 「git reset HEAD^」 VS 「git checkout HEAD^」
1.1 git reset HEAD^
git reset HEAD^在当前分支将HEAD指向了前一个节点,这个时候可以直接执行push origin <branch> --force就可以更新远端分支
image.png
1.2 git checkout HEAD^
git checkout HEAD^则是脱离了当前分支,然后将HEAD指向了前一个节点。但是main分支最新的commit依然是C4, 这个时候执行push origin <branch> --force会提示Everything up-to-date。
image.png
2. 「git reset HEAD^」VS 「git revert HEAD」
2.1 git reset HEAD^
git reset HEAD^将本地分支最新的commit记录指向了前一个节点,最新的commit已经变成了C3,这个时候可以直接执行push origin <branch> --force就可以更新远端分支
image.png
2.2 git revert HEAD^
git revert HEAD则只是将上一次的提交的记录回滚,并且重新提交了一遍,虽然最后的结果跟reset一样,但是在分支上面会新增一个节点
image.png
3. 「git cherry-pick」 VS 「git merge」 VS 「git rebase」
3.1 git cherry-pick
下图HEAD指向了main, 我们想将cdf分支的C3复制到当前所在位置的话,就可以使用cherry-pick的方式
image.png
如图所示,将cdf分支上C3的提交cp到main分支变成了C3',C3'跟C3就只有提交的内容一样,属于不同的两次提交
image.png
3.2 git merge
下图HEAD 是指向了 main,我们想将 cdf 分支的代码合并到 main 分支上来,那么执行 git merge cdf
image.png
合并之后,多了一个C6节点,这个节点的父节点有两个,分别是C4和C5。 main和HEAD指向了合并后的新节点
image.png
3.3 git rebase
下图HEAD指向了main, 我们想将cdf 分支的代码合并到main分支,但是又想让提交的记录比较整洁,这个时候就可以使用git rebase的方式
image.png
rebase,顾名思义就是重新以XX为父节点。重新创建一个"分支",将需要rebase的分支提交节点给复制过来,然后再将当前分支的节点放到新"分支"最新的节点上。并且HEAD指向了新"分支"(注:这里的分支打个双引号是因为它其实并不是一个真正意义上的分支,它没有分支名,只有HEAD),而且原来分支节点依然还存在,如果还想用到之前的节点,可以通过checkout 对应的commit id即可
image.png
Q&A
1. 应该如何回退自己的git操作
1.1 发现自己提交的代码有点问题, 想要撤回上一次的提交,但是又想保留原先的修改,那么执行git reset --mixed或者git reset --soft 。 git reset默认就是mixed
1.2 发现自己的提交就是有问题的,想要撤回上一次的提交,并且不想保留原先的修改,那么可以执行git reset --hard
1.3 如果我们想要撤回远端分支的提交,但是远端分支又是一个protected的分支,那么我们只能选择git revert的方式将修改还原,但是会在分支上新增一次提交记录
2. 应该怎么保存自己的代码?
2.1 一个feature开发完成或者一个bug修改完成,这个时候需要切换分支去做其他事情,那么可以commit到本地仓库,然后推到远端自己建的分支上面。 但是后续如果有些修改但是又不想再增加commit了,那么可以通过git commit -amend的方式来覆盖上一次commit的记录
2.2 有时候工作区文件改了一半,但是又不想增加一个commit,但是需要把远端的最新代码拉下来又发现有冲突拉代码失败。 这个时候就可以先通过git add的方式放到暂存区,然后git stash将文件保存起来。
3. 如何快速查到一段代码的修改记录?
3.1 如果只是想看这段代码是什么时候提交的,可以通过git blame -L N, +M --<file>查看
其中N代表是从第N行开始, M代表一共要看M行代码, file就是对应的文件名
image.png
3.2 如果需要看到某一行代码的修改记录,那么可以通过git log -L N, +M:<file>查看
其中N代表是从第N行开始, M代表一共要看M行代码, file就是对应的文件名
10239771-1139b4292e43a37f.png
3.3 AndroidStudio自带的git功能也非常好用,选中一段代码,右键Git->Show History for Selection就可以查看所有的修改记录
image.png4. 如何快速查找某个人的提交记录?
git log --author=<email>
image.png
5. 如何快速查找某个commit?
- 通过git log --grep <key> 关键字搜索的方式搜索出所有相关的commit
- 找到对应的commitid
-
通过git show <commitid> 就可以找到对应的修改记录
10239771-b02b0fa86e073da7.png
6. 如何知道自己的某个commit 都合入了哪些分支 以及哪些版本(tag)?
如果知道对应的commitid,那么通过git branch --contains <commitid>
image.png
如果并不知道commitid,那么需要先通过git log --all --grep <key>的方式找到对应的提交记录,然后通过git branch --contains <commitid>
image.png
7. git reset --hard之后代码丢了,怎么恢复
7.1 如果你的代码现在在工作区或者是暂存区,你不小心执行了git reset --hard命令,那么真的是神仙难救,所以如果工作区有代码,切忌不要执行git reset --hard方法
7.2 如果你的代码已经push到了远程仓库,你不小心执行了git reset --hard命令,那么只需要git pull就可以恢复。如下图所示
image.png10239771-afbd94145f2d24ec.png
image.png
7.3 如果你的代码已经push到了远程仓库,你不小心执行了git reset --hard命令,并且还强制推送到了远端,那么可以执行下面的步骤,如下图所示
- 通过git reflog查看所有的操作记录
- 找到撤销之前的那条commit
- 通过git reset --hard <commitid>
- 通过git push origin <branch> 的方式恢复远端分支
通过git reflog查看所有的操作记录,找到撤销之前的那条commit
10239771-886d97ffbd9c603e.png
通过git reset --hard <commitid>,git push origin <branch> 的方式恢复远端分支
image.png
8. 新增的文件刚好命中了.gitignore的规则,怎么提交
image.png当你新增的文件或者修改的文件刚好命中了gitignore的规则,那么你执行git add命令的时候会有上面这段提示。通过git add -f <file>的方式就可以添加到暂存区。
image.png
当然如果觉得gitignore的规则不合理,修改.gitignore文件即可
9. 一个文件里面有多个bug单对应的修改或者是有些修改是测试代码,该怎么提交指定的内容
9.1 通过git add -p <file>的方式筛选出要提交的代码,如下图所示。
-
在test10.txt文件中新增四行,然后保存。
image.png
image.png -
执行git add -p test10.txt的命令
image.png - 这里有几个命令需要执行
a. y: 同意这段修改放入暂存区
b. n: 不同意这段修改放入暂存区
c. q: 退出
d. a: 同意这段修改和这个文件中所有的其他修改都放入暂存区
e. d: 不同意这段修改以及这个文件中所有的其他修改放入暂存区
f. e: 自定义选择放入暂存区
g. ?: 打印帮助 - 选择e进入编辑页面
a. 新增行,不想提交(但是变更仍然在工作区)。删除整行
b. 删除行,不想提交。用空格符" "代替"-"字符。
c. 变更行,不想提交。上面两个结合——对于"+"所在行整行删除,对于"-"所在行,用空格符" "代替"-"字符
-
删除「add new line 2」和「add new line3」然后保存。然后执行git commit将test10提交到本地仓库
image.png -
根据commitid查看修改记录
image.png
9.2 使用sourcetree提交
执行命令行来提交指定的内容多少是有点不直观,并且不易上手,采用sourcetree的方式就非常直观,选中指定的提交的内容然后点击暂存行即可
image.png
10. git push origin <branch> --force以后,如何回退
git push origin <branch> --force以后,远端的分支节点已经发生了改变,但是本地分支依然是有以前的commit记录,所以先使用git reflog找到之前的操作记录,通过git reset -hard <commitid>的方式回退到当时的版本, 然后git push origin <branch>来更新远端分支