Git带着问题去实践(二)

“有人把Git的分支模型称为它的必杀技特性,也正因为这一特性,使得Git从众多版本控制系统中脱颖而出。”

冲突&合并

在上一篇的基础上,git跳转到哈希码头四位9533的commit版本上,用命令git branch命令创建分支branch01和branch02,这个时候产生疑问:

在某分支上做改动,其它支线看得到改动的部分吗?

切换到branch01上,将最后一行添加“branch01”到Hello后面。此时用git checkout命令切换到master或者branch02上,cat test.md,查看内容发现,都能看见改动的部分,再次切换到branch01,add改动的文件,再次切换到其它支线,此时在其它支线仍然能看见staged状态下modified的部分。切换到branch01,将刚刚add的文件commit,然后再切换到其它支线,此时将看不见modified(无论是否staged)的部分。

其实这不难理解,在没有提交前,所谓的分支不过是各个指针指向当前提交点而已。

需要说明的是,如果再次用同样的方法,对branch02进行修改,比如更改test.md最后一行为Hello branch02,尝试切换回branch01将会报错:

1
2
3
4
error: Your local changes to the following files would be overwritten by checkout:
test.md
Please, commit your changes or stash them before you can switch branches.
Aborting

切换失败,需要commit或者使用stash解决,关于stash后面会讲到。

在各支线,用git log命令可以看到其它支线提交的情况吗?

将branch02分支中add的文件commit,切换到各支线进行测试,结果是不能。

不仅如此,git log --oneline --decorate这个命令,只会追踪HEAD指针,当其它分支都停留在HEAD指针指向的地方时,命令才会将它们的名字打印出来。

但是使用git log --oneline --decorate --graph --all可以打印出所有支线组成的树状结构。

图2-1

log也可以查看公共仓库的提交,使用git log origin/master命令即可。

怎么给Git命令取别名?

git log --oneline --decorate --graph --all为例,为它取一个别名git tree可以通过如下设置:

1
$ git config --global alias.tree 'log --oneline --decorate --graph --all'

当打下git tree将出现和欲被修改的别名命令同样的效果。

为了以后的方便,这里将reset HEAD --设置为unstaged

1
$ git config --global alias.unstaged 'reset HEAD --'

分支怎么合并到master?

1
2
$ git checkout master
$ git merge branch01

此时输出:

1
2
3
4
Updating 9533382..28f767b
Fast-forward
test.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

这里用到fast-forward,是因为在这次合并中,master指针只是做了一个向前移动的动作。至于为什么可以fast-forward,源于branch01是master的直接上游,合并不产生没有解决的分歧。从这个动作看来,称master“合并”到branch01更适合。接着也可以使用删除命令对branch01进行删除:

1
$ git branch -d branch01

branch02怎么合并到master?

branch01很迅速的就合并到master上了,但是如果现在要将branch02合并到master上,就会产生冲突:

1
2
3
Auto-merging test.md
CONFLICT (content): Merge conflict in test.md
Automatic merge failed; fix conflicts and then commit the result.

因为此时的master和branch02已经不是一条直线上的“蚂蚱”了,变成了旁系,且branch02和之前的branch01本来就有冲突。

合并冲突后任意时刻调用git status可以查看包含合并冲突而未合并的文件,等待你去解决合并产生的冲突。使用cat test.md查看文件:

1
2
3
4
5
6
7
8
9
Hello I am LiLei.
Hello I am XiaoFang.
<<<<<<< HEAD
Hello branch01
=======
Hello branch02
>>>>>>> branch02

然后修改该冲突文件,一旦运行git add命令,Git就会知道冲突已经解决:

1
2
3
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)

继续commit最终完成合并。

Git对行列的空格符会进行冲突检测吗?

在Git内任何空格符都是能够被识别的,下面是一个例子的结果:

1
2
3
4
5
6
7
8
9
Hello I am LiLei.
Hello I am XiaoFang.
<<<<<<< HEAD
Hello branch01 branch02 b01
=======
Hello branch01 branch02 b02
>>>>>>> branch02

分支管理

在切换到不同分支后使用git branch [新建的分支名]创建的新的分支有什么不同?

在不同分支先创建新的分支,就相当于在当前创建了一个新的指针而已。在上一个问题的最后,branch02已经合并到master上了,此时切换到branch02新建一个分支branch03,那么运行命令:

1
$ git branch --merged

branch02和branch03都会打印出来,切换到master,如果此时合并branch03会显示Already up-to-date。同样的如果在master下新建一个分支branch04,那么尝试合并它也会打印Already up-to-date。如果要打印所有包涵未合并工作的分支,使用如下命令:

1
$ git branch --no-merged

如何查看远程分支?

只需要运行git branch -r就可以查看远程分支,而如果运行git branch -a将罗列出所有分支。

储藏&清理

未处于staged的modified数据和处于staged状态的数据都能被stash么

实测的确是这样,stash针对的工作区的改动(Saved working directory)。在git stash apply后文件的改动被重新应用了,但是之前staged的文件却没有重新处于staged状态。如果要在调用命令后就处于staged状态,可以使用如下命令:

1
$ git stash apply --index

Git可以在同一个分支多次存储吗?

经过本人来回多次git stash apply stash@{0},验证出可以进行多次存储,但每次恢复其中一个后,并不能马上又恢复另一个,这样会出现以下信息:

1
2
3
4
error: Your local changes to the following files would be overwritten by merge:
test.md
Please, commit your changes or stash them before you can merge.
Aborting

在stash的过程中怎么添加描述信息?

我的做法是这样的:

1
$ git stash save "具体描述信息"

如何移除stash的工作?

命令如下:

1
$ git stash drop stash@{0}

这条命令可以指定删除哪一条stash存储的内容。

再就是下面这条恢复后弹出栈命令:

1
$ git stash pop

它会恢复栈顶stash储存的内容,恢复后,这条stash也从栈弹出,相当于删除栈顶。

工作目录下untracked状态的test02.md属于多余的“看客”,使用什么命令可以删除?

答案是 git clean。但通常最后添加参数-n,比如这里运行如下代码:

1
git clean -n

会输出这样的结果:

1
Would remove test02.md

所以上面的命令并不会真正删除,只不过是个提醒。

如果使用命令:

1
git clean -df

即删除当前目录吓没有被track的文件和文件夹。

打标签

Git可以给历史中的某一个提交打上标签。

如何列出标签?

只需要输入git tag就可以了。而打轻量标签,是在git tag后面添加参数作为标签名,此参数不需要打引号表示字符串。

既然是给提交打标签,那么如何给过去的某个提交打标签?

比如现在我要为实验中的某次commit(9533382df5a3b45b5d6a67fe2f51ce3fafd1c8b8)打标签,我会使用如下命令:

1
$ git tag -a v1.0 9533 -m "my v1.0"

如果不希望Write a message for tag,如何给提交打标签?

在上一个问题中,必须提供一个用来描述的message,主要是因为命令中含有-a,现在使用下面的命令,打印轻量标签:

1
$ git tag v.1.1 1d3b

以上反映的是轻量标签和附注标签的不同。

可以根据tag创建分支吗?

见如下命令:

1
$ git checkout -b branchname v1.1

如何看待git checkout [标签名]

切换tag和切换branch的命令相同,在实际的测试中,输入该命令后,各branch列表如下:

图2-2

当切换到其它分支后,“临时”branch就不见了,这种状态被称为detached HEAD状态。

上传到远程仓库:

1
$ git push origin tag名

小结

通过两篇小记,采用自问自答的形式,窥探了git最核心的几个功能。

参考

progit-zh-v2.1.1.pdf