Git常用命令详解和最佳实践

我们的团队使用Git作为版本控制工具已经有半年多的时间了,大家对一些常规的操作都已经比较熟练,然而在使用过程中难免遇到这样那样的问题,今天来总结一下遇到过的一些问题和解决方案。

一、git pull与git pull - - rebase

使用pull从远程主机更新代码时,会获取到远程指定分支的更新并合并到本地指定的分支,一般情况下不需要指定,默认把本地当前分支对应的远程分支的更新获取到并跟本地的当前分支合并。在多人协作的场景中,不推荐使用git pull,举个栗子:

当前的远程分支是这样的:
origin dev:A—B—C—D

此时我把dev拉到本地,进行开发,开发完成commit后我本地是这样的:
A—B—C—D—E—F

而此时dev分支可能还有别的同事在开发,并且已经提交了他的修改,现在origin dev分支是这样的:
A—B—C—D—G—H

执行git pull后,仓库里的分支曲线是这样的:
这里写图片描述

时间久了这条曲线就会非常混乱。如果此时用git pull - - rebase,结果是这样的:
A—B—C—D—G—H—E’—F’,依然保持一条直线,看上去很整洁。

但是使用git pull和git pull - - rebase都有可能产生conflicts,这个是不可避免的,乖乖的解决冲突就好了。
如果要把 rebase当做 git pull 的默认值,可以在 ~/.gitconfig 配置让所都自动套用这个设定:
[branch]
autosetuprebase = always

二、撤销修改

如果要让工作目录回到上次提交时的状态,执行
gitresethardHEAD git checkout – path/of/the/file
reset这个命令有三个参数可选:soft mixed hard,这三个参数分别是什么意思呢?
首先我们要理解三个概念:

  1. HEAD,是一个指向当前分支最后一次提交的一个指针
  2. Index,也就是staging area(暂存区),是存放即将被提交的文件的地方
  3. Working Copy,当前工作目录下的文件

当我们第一次checkout一个远程分支时,HEAD指向该分支上最后一个commit,他和Index和Working Copy是一样的。
当我修改了一个文件,Working Copy和Index、HEAD就不一致了,这时Git会提示我们文件有修改;
当我add这个文件后,Working Copy 和Index一样了,而他俩又和HEAD不一致了,这时Git会提示我们有文件可以提交了
当我commit这个文件后,HEAD指向了刚创建的这次commit,Working Copy和Index和HEAD又一样了。
那么,这三个参数的区别就是:

  • soft:告诉Git只将HEAD重置到某个commit,Working Copy和Index都不变动;
  • mixed:HEAD和Index都改变,Working Copy中的文件不变动,此时会提示文件有修改,但是没有缓存到暂存区;
  • hard:三者都改变到你要reset的那个commit上,这可能会导致你本地的修改丢失。

三、关于stash

当我在一个feature分支上开发时,突然有同事反馈了一个bug,这时我需要切换到dev分支来修改这个bug,但此时在feature分支上的修改还没有做完,不想提交,此时可以使用git stash,将本地的修改先隐藏起来,不让Git发现,这时就可以checkout到dev分支,此时查看status你会发现Working Copy是clean的,可以任意发挥了。当改完bug后再切换回feature分支,执行git stash pop 来恢复之前隐藏的那些修改,这时我又可以继续feature上的开发了。

四、合并提交

有时候开发一个feature或者改一个bug可能分了多次commit,如果我想把这几次commit合并成一次以后再push呢?这时可以使用git rebase -i.
如下图,这个仓库中有四次提交,如果要把“second commit”和“third commit”合并为一次提交,可以这样做:
git rebase -i 750000a08f63e4f3e7f2a511acc522e9afac0ea2
-i后面的参数是second commit的前一次提交的SHA1值

这里写图片描述

这里写图片描述

执行完成后,会出现一个编辑器让你选择合并哪次提交
因为我们要把“third commit” 和“second commit”合并,所以把“third commit”前面的“pick”改成“squash”

这里写图片描述

然后退出保存,git会执行rebase操作,当遇到第二次提交时,会再次弹出编辑器说明即将合并,需要你提供commit message

这里写图片描述

输入你的commit message,然后保存

这里写图片描述

git会提示rebase成功,此时再看git log

这里写图片描述

第二次和第三次commit已经被合并成“second and third commit”了。
当合并到只剩下两个commit时,git rebase -i就不起作用了,这时执行:

  • $ git reset –soft HEAD^1
  • $ git commit –amend

就可以把最后两个commit合并为一个。

五、cherry-pick

这个命令可以帮助我们把另一个分支的某个commit提交到当前的分支,而不必把整个分支都合并一次。想象一个场景:我们正在进行新版本的迭代开发,此时准备发布的版本突然被发现一个bug,而这个bug已经在新版本开发中修复了,但是又不能直接把新版本的分支合并到待发布的分支上,此时可以使用git cherry-pick comitid,将新版本分支中的那次commit提交到待发布的分支上,这样既修复了bug,又没有污染待发布的分支。但是这个命令是有风险的,可能会造成冲突,应该谨慎使用。

六、我合并两个分支时,如何保证某些文件不被合并?

曾经遇到过几次这样的场景,我在某个feature分支上做完开发,要合并回主dev分支,但此时dev分支上有同事修改了一个重要的文件,并且改动很大,而我在feature分支上也改了这个文件,但是还是要以dev分支上的修改为主,如果直接merge这两个分支,这个文件必然会冲突,而解决冲突的代价实在太多大了,因此在merge时,我不希望这个文件被合并,该怎么做呢?

实际上git在merge分支时,有一个默认的merge driver,这个驱动会去检查每个文件的每一行,如果按照一定规则发现两个分支的同一个文件有不同,那么认为两个分支都对这个文件做了修改,会merge两个文件,此时有可能产生冲突;那么如果我们自定义一个merge driver,在里面定义一些不会被检查的文件,那git就会直接跳过这些文件,因此就不会merge了。

步骤如下:

  1. 创建我们的自定义merge driver:git config –global merge.ours.driver true
  2. 在要被merge的分支上创建.gitattributes 文件,并且在文件中置顶不merge的文件名:
    echo ‘email.json merge=ours’ >> .gitattributes
    git add .gitattributes
    git commit -m ‘chore: Preserve email.json during merges’
  3. 回到要合并到的分支,执行merge:
    (newbranch) gitcheckoutmaster(master) git merge newbranch
    Auto-merging …
    Merge made by the ‘recursive’ strategy.
    demo-shared | 1 +
    1 file changed, 1 insertion(+)

经过以上步骤,我们指定的email.json就不会被合并了。

                                                                       杏树林研发 冯康
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章