发现一个神器:gitk --all
当master和dev分叉后,再merge,会产生如下图所示的分支图:

从上图中可以得出一个重要结论:分支上的commit节点链不是纯粹的链表,而是具有树的特性,即一个commit节点可以指向两个父commit节点。
假如现在在master分支上,分支图如下图所示:

如果执行git merge dev,git会进行fast-forward,即直接将master指针指向dev所指向的commit节点。
而如果执行git merge --no-ff dev,则git不会进行fast-forward,而是生成一个新的commit节点,然后将master指针指向该节点,如下图所示:

版本回滚:
假设现在分支图如下:

然后我提交了一次,分支图变成下图:

此时,执行git reset --hard HEAD^可回滚到上一版本,如下图所示,即恢复到提交之前的状态:

如果回滚完之后又想回到最新的版本,则可以先执行git reflog,查看操作日志:

从操作日志中可以找到最新的版本的那次提交的commitId是a432。然后执行git reset --hard a432,即可恢复到最新版本,分支图如下:

到现在为止,有几个命令比较相似,总结如下:
git checkout -- test.txt:将工作区中相对于暂存区的变更丢弃掉。
git reset HEAD text.txt:将暂存区中的修改回滚到工作区。
git checkout commitId:将HEAD指针指向commit节点,不再指向分支指针,而分支指针不动。我们称HEAD处于detached状态。
git checkout branchName:将HEAD指针执行分支指针。
git reset --hard commitId:将master指向commit节点,但是HEAD指针不变,仍然指向master指针。
如何在历史commit节点上进行提交?
首先git checkout fea0,将HEAD指向该commit节点,如下图(黄色节点即是HEAD指向的节点):

然后修改文件并commit,commit完之后如下图:

此时,如果执行git checkout fea0,则HEAD会指向fea0,且git会提示:

意思是说刚刚提交的commit 5d6c170不在任何分支上,可以执行git branch <new-branch-name> 5d6c170,基于commit节点5d6c170创建一个新的分支。其实在任何状态下都可以执行git branch <new-branch-name> 5d6c170来创建分支。执行完后如下图:

场景描述:
刚开始时,master和d2指向同一个commit节点。然后在master分支上进行了一次提交,如下图:

然后切换到d2分支上,在工作区中修改文件,且还未add到暂存区。此时如果git checkout master,会报错:

提示我本地修改会被checkout所覆盖。此时可以commit或stash刚刚的修改。如果commit,就是上文所说的在历史commit节点上进行提交。如果不想提交,可以执行git stash将工作区的修改保存起来。执行git stash之后分支图如下:

同时stash list如下:

stash@{0}表示是第一个临时修改,且是基于commit节点a432所作的修改。(但是看不懂分支图是什么意思。)
如果再在d2分支上修改test.txt,并git stash,则状态如下图:


可以看到,stash@{0}和stash@{1}都是基于commit节点a432所作的修改。并且可以发现,stash list不管由多少个元素,分支图都是一样的。
git stash操作:





