git思维导图
为了说明我们日常开发中执行的一系列Git命令的作用是什么,我们需要了解Git的工作区域的概念,几乎每一个常见的Git命令操作都可以通过工作区域来解释。
Git本地有四个工作区域:
四个工作区域
接下来我们来看下Git文件的状态:
Git 文件状态
在了解Git工作区域、文件状态以及本地仓库的相关信息之后,相信大家对于日常使用的一些命令都有了更加深刻的理解。接下来,我们一起进行一个常用命令总结:
并且如果是单人开发,自己玩的情况下,貌似这些命令就足够了。但是,在实际的开发当中,我们往往会面对更加复杂的场景,需要一些更为复杂的命令来处理,我们接着往下看。
在当前分支上执行git merge master可以将master的提交合并merge到当前分支,也就是更新本地分支。我们日常开发中,将本地代码推到远程仓库,建立Merge Request,然后点击Merge按钮其实就是在master分支上merge了开发分支。
merge
git fetch可以将远程分支拉到本地:
git fetch 会将所有远程分支都拉到本地
git fetch origin sowhat1412 指定拉取远程origin仓库的sowhat1412分支到本地
当我们想要更新本地分支代码的时候,需要将远程开发分支或者远程master分支代码拉到本地,并且合并到当前开发分支上。所以git pull = git fetch + git merge 在当前开发分sowhat1412上,我们执行如下的命令:
git pull origin master
表示将远程master分支拉到本地并且merge到当前分支sowhat1412上
rebase会把你当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像你从公共分支又重新拉出来这个分支一样。 举例:如果你从 master 拉了个feature分支出来,然后你提交了几个 commit,这个时候刚好有人把他开发的东西合并到 master 了,这个时候 master 就比你拉分支的时候多了几个 commit,如果这个时候你 rebase master 的话,就会把你当前的几个 commit,放到那个人 commit 的后面。
git rebeae
merge 会把公共分支和你当前的commit 合并在一起,形成一个新的 commit 提交
git merge
对于多分支的代码库,将代码从一个分支转移到另一个分支是常见需求。
这时分两种情况。一种情况是,你需要另一个分支的所有代码变动,那么就采用合并(git merge)。另一种情况是,你只需要部分代码变动(某几个提交),这时可以采用 Cherry pick。
git cherry-pick命令的作用,就是将指定的提交(commit)应用于其他分支。
$ git cherry-pick <commitHash>
上面命令就会将指定的提交commitHash,应用于当前分支。这会在当前分支产生一个新的提交,当然它们的哈希值会不一样。
举例来说,代码仓库有master和feature两个分支。
a - b - c - d Master
\
e - f - g Feature
现在将提交f
应用到master分支。
# 切换到 master 分支
$ git checkout master
# Cherry pick 操作
$ git cherry-pick f
上面的操作完成以后,代码库就变成了下面的样子。
a - b - c - d - f Master
\
e - f - g Feature
从上面可以看到,master分支的末尾增加了一个提交f。
git cherry-pick命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。
$ git cherry-pick feature
上面代码表示将feature分支的最近一次提交,转移到当前分支。
Cherry pick 支持一次转移多个提交。
$ git cherry-pick <HashA> <HashB>
上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。
如果想要转移一系列的连续提交,可以使用下面的简便语法。
$ git cherry-pick A..B
上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。
注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的语法。
$ git cherry-pick A^..B
该命令执行之后,HEAD指针会移动到选中commitId上,并且之前的HEAD ->commitId之间的所有修改的内容会被置于工作区,需要重新add、commit。
该命令执行之后,HEAD指针会移动到选中commitId上,并且之前的HEAD ->commitId之间的所有修改的内容会被直接置于暂存区staged中,也就是后续只需要执行commit操作就OK了
这个--hard很好理解,就是在回滚HEAD指针的时候,很强硬的将所有HEAD ->commitId之间的改动内容“全部删除”!
为什么加引号?因为前边我们说了Git不会丢失任何你提交过的内容(只要你玩的溜),后续我们会分析原因。
mkdir git-test
cd git-test/
git init
echo 0 > test
git add .
git commit -am "init"
linux-geek:/home/tmp/git-test # echo "important code0" >> test
linux-geek:/home/tmp/git-test # git commit -am "important code0"
[master 03f85a1] important code0
1 files changed, 1 insertions(+), 0 deletions(-)
linux-geek:/home/tmp/git-test # echo "important code1" >> test
linux-geek:/home/tmp/git-test # git commit -am "important code1"
[master 83547ba] important code1
1 files changed, 1 insertions(+), 0 deletions(-)
linux-geek:/home/tmp/git-test # echo "important code2" >> test
linux-geek:/home/tmp/git-test # git commit -am "important code2"
[master b826ae5] important code2
1 files changed, 1 insertions(+), 0 deletions(-)
linux-geek:/home/tmp/git-test # cat test
0
important code0
important code1
important code2
linux-geek:/home/tmp/git-test # git reset --hard HEAD~3
HEAD is now at 2e71cf6 init
再来看一下代码的情况:
linux-geek:/home/tmp/git-test # cat test
0
有没有什么办法补救呢?答案是肯定的!
linux-geek:/home/tmp/git-test # git reflog
2e71cf6 HEAD@{0}: HEAD~3: updating HEAD
b826ae5 HEAD@{1}: commit: important code2
83547ba HEAD@{2}: commit: important code1
03f85a1 HEAD@{3}: commit: important code0
2e71cf6 HEAD@{4}: commit (initial): init
其中,我们可以清楚地看到HEAD每次移动的情况以及哈希值,于是乎我们再采用git reset去试一下:
linux-geek:/home/tmp/git-test # git reset --hard HEAD@{1}
HEAD is now at b826ae5 important code2
linux-geek:/home/tmp/git-test # cat test
0
important code0
important code1
important code2
revert 是回滚某个 commit ,不是回滚到某个。git revert是用于反做某一个版本,以达到撤销该版本的修改的目的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。如下图所示:
在这里插入图片描述
git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861
git commit -m "revert add text.txt"
有这样一个需求,代码量不多,但是因为你多次提交,会导致在建立Merge Request的时候,出现了几十个commitId,比如:“update”、“bug fix”、“fix again”等,不光看起来很丑,也会给大家一种感觉,这小伙到底行不行呀?一百行代码的开发量,提交了这么多次才搞定。(尴尬.jpg) 为了解决这样的问题,我们可以巧妙的利用git reset。比如当前的commit是这样的A-1-2-3-4-5-6-7-8,你的第一个提交是1,那么我们执行如下的命令:
git reset --soft A // 重置本地分支HEAD指针
git commit -m "XXX逻辑开发"
git push origin ywq_news_0702 -f // 提交到远程分支
-f是什么操作?force的含义,表示强行执行本次操作。当我们git reset之后,本地的HEAD指针指向的commitId会比远程origin对应的落后,直接push会被拒绝。通过-f命令可以强行将本地内容push到远程分支上(切记!如果是多人共同合作的开发分支或者远程master操作,千万不能加-f操作!!!) 经过git reset --soft之后,我们提的Merge Request里就是一个commitId了,发出来的CR会感觉倍儿有面儿。
当我们在当前分支开发某个需求的时候,遇到了另一个需求的联调问题,需要切换到另一个分支上去解决问题。怎么办? 正常情况下,我们应该将当前分支工作区的内容add 、commit之后再切换分支。但是问题来了,当前需求开发了一半,我不想生成一次提交怎么办? 放心,这个时候我们的git stash命令可以帮助我们将当前工作区的内容储藏起来。然后切换其他分支,处理完问题之后,再切换到当前分支,执行git stash pop取出来就完事。
git stash list // 查看当前stash里边的内容
git stash // 将当前工作区内容储藏起来
git stash pop // 将stash中栈顶内容pop出来,当然也可以根据顺序直接取第n个
对于恢复修改的文件,就是将文件从仓库中拉到本地工作区,即 仓库区 ----> 暂存区 ----> 工作区。
对于修改的文件有两种情况:
只是修改了文件,没有任何 git 操作,直接一个命令就可回退:
$ git checkout -- aaa.txt # aaa.txt为文件名
即编辑之后,git add 但没有 git commit -m ....
$ git log --oneline # 可以省略
$ git reset HEAD # 回退到当前版本
$ git checkout -- aaa.txt # aaa.txt为文件名
即编辑之后,git add 和 git commit -m ....
$ git log --oneline # 可以省略
$ git reset HEAD^ # 回退到上一个版本
$ git checkout -- aaa.txt # aaa.txt为文件名