通过11个命令来精通git

上周说让我做一次开发团队 git 使用的培训,一时不知从何说起,后来找了两个同事聊了聊,观察了他们的使用,发现了几个问题:

  • 大多数同事都只用 add、commit、push 几个命令,也只会这几个命令
  • 开发只用一个分支,graph 混乱,遇到冲突不知道如何解决
  • 以 cp 文件的形式来传递修改 …

这完全是自己砍掉了 git 的双脚和双手,很多人使用git要么是公司要求用,要么是看别人用,并没有意识到 git 作为工具,能给开发者带来了多少的便利。

从使用入手,通过使用来学习,我觉得是学习最快的方式。我从我的 terminal 的 history 中找出了使用靠前的几个 git 命令,我也是开发者,所以我想大部分小伙伴都会遇到跟我一样的使用场景。

为了方便演示,我首先初始化一个空仓:
这里写图片描述

1 git commit

git commit 用来提交当前工作空间的修改内容。
commit 是 git 中最重要的内容,因为提交时git仓库中基本的单位,是后续修改,查找的基础。如何做好提交,是一门学问,那如何才算是好的提交呢?
首先,提交需要原子化,粒度要小,可以是一个函数、一个脚本,尽量一个提交只做一件事,不要把所有的修改,几十个文件,几百行代码放在一个commit中,这样后续代码维护也是问题。
其次,提交说明要写好。提交说明包括提交主题和提交信息两部分。主题就是这次提交主要干了什么,尽量保持在50字节以内(Gerrit 的commit_log检查插件似乎有着稍微宽泛一些的要求)。 这是因为对于像 Linux、Git 这样的开源项目,是以邮件列表作为代码评审的平台,提交主题要作为邮件的标题,而邮件标题本身有长度上的限制。 既然提到提交主题作为邮件标题,还要提及一点,你见过在邮件标题的结尾写句号么?所以提说明的第一句结尾不要加标点符号。
提交主题后的空行 必须要在提交说明的第一行和后续的提交说明中间留一个空行!如果没有这个空行,很多 Git 客户端会将连续几行的提交说明合在一起作为提交描述。这样显然太糟了。
提交信息是提交说明的主体,要让评审者对提交信服,要为将来的代码维护者留下线索,提交说明要回答如下问题:

  • What——要解决什么问题?什么情况下会发生?
  • How——怎么样解决这个问题?
  • Why——为什么这样解决是合理的,比其他解决方法更好?

git commit -m只是写short message的提交方式,应该用:git commit -se
创建文件:
这里写图片描述
提交:
这里写图片描述

2 git push

git push 命令用于将本地分支的更新,推送到远程服务器。
用法:

$ git push <远程主机名> <本地分支名>:<远程分支名>

这里写图片描述
图中我初始化了一个空的 bare 仓并将它添加成我当前仓库的远程仓库,然后把代码推送过去,由于远程仓库没有 master 分支,所以在推送的同时,远程创建了新的 master 分支,所以我们也可以直接使用 push 命令来创建远程分支,当然,被创建的分支必须是本地已经存在的。
这里写图片描述
git branch -a是列出本地和远程所有的分支。
关于远程主机名的配置,建议查看帮助文档:

$ git remtoe --help

除了向远端推送本地的代码以外,我们还可以通过push来直接删除仓库分支:

$ git push origin  :<远程分支>

3 git stash

git stash 用来暂存工作区的修改

当我们正在写代码的时候,经常突然有一个紧急的 bug 需要处理,工作区内容又没有完成,不适合提交,直接checkout到hotfix上,会提示本地修改暂未提交,这时就需要stash登场了。

stash会暂时将你本地的修改交给git来保管,当你有时间以后,可以从git的stash的栈中将你的修改重新恢复出来,继续开发。

命令:

$ git stash
$ git stash list
$ git stash pop [version]  

例如:我在修改README.md文件,突然线上有个bug,我需要打一个 hotfix 并且修改线上的readme 文件,这时我如果直接从 master 分支上切一个 hotfix 分支出来的话,git会提示我本地的readme的修改没有提交(如果 master 分支的代码跟我当前的开发分支上,readme 文件无差异,则不会提示)。
这里写图片描述
这时我可以先 stash 本地提交,切到 hotfix 修改之后,再切回开发分支 pop 出修改,继续coding。
这里写图片描述

4 git cherry-pick

git cherry-pick 用于把另一个本地分支的 commit 修改应用到当前分支。

团队开发中经常会出现一个人需要调用另一个人写的方法,而他的代码还没有合并到主干。
cherry-pick 就是为解决这类问题而生的。比如我在 develop 分支上做了一个提交,添加了一个redis.js文件。

这里写图片描述

而正在开发的 feature 分支需要使用 redis.js 的 connect 方法。除了 merge develop 分支以外,我们还可以使用 cherry-pick 只拿这一个提交过来。

这里写图片描述
cherry-pick 还支持 edit 参数,可以提取的过程中再次编辑这个提交。

5 git show

git show 用来查看指定提交指定文件的修改内容。
用法:

$ git show [sha] [file_path]    

6 git blame

git blame 是查看整个文件的每一行的详细修改信息(SHA串,日期和作者等)的工具。
用法:

$ git blame file_path

这里写图片描述

这是个代码问责神器,可以查看每一行代码当前是哪个提交、谁修改的,支持行跳转和搜索等。

7 git log

git log是git用来查看提交历史的工具,这也是使用最多的命令。

之前写过一篇查看log的博客,可以用作参考: http://blog.csdn.net/lshemail/article/details/51787250

8 git diff

git diff 用来显示项目的两个不同版本之间的差异,或者显示指定文件的不同之处。
用法:

$ git diff [sha1] [sha2] [file_path]

这里写图片描述

也可以对比当前没有add的修改和版本库的差异:

这里写图片描述

另外,diff 的结果是一个标准的 patch 结构的文件,我们可以通过 apply patch 文件来实现代码合并,详见帮助文档:

$ git apply --help

9 git rebase

基础:
git rebase用于把一个分支的修改合并到当前分支。

这里写图片描述

圈出的提交时 develop 和 feature 的 common node。

将 develop 分支合并到 feature 分支后,feature 的 log 为:

这里写图片描述

可以看到commits是交叉排列的。这种排列方式是不推荐的,因为当我们review代码的时候,将变得异常麻烦,所以很多公司都禁止这样做。

那我们应该如何实现commits连续排列呢?这就需要使用 rebase 了。

这里写图片描述

这是由于 git 在执行rebase的时候,先将 feature 中新增的提交暂存起来,然后把 develop 的提交 merge 过来,最后再将暂存的提交 merge 回来。
这时暂存的 commit 已经被重新生成,时间在 develop 的提交之后。所以 feature 这时的log就成了截图中的样子。

注意这里并没有 merge 产生的节点。

另外,如果feature分支已经被push到远端,那么rebase后的feature分支将无法正常推送到远端的feature中,这是因为远程对象的引用关系发生了改变,这时需要进行强制推送

实际使用中的两种方法:

  1. 更新母分支,git rebase 目标分支;
  2. git pull --rebase remote/目标分支.

冲突处理:
首先处理冲突应当尽量采用手动的方式,仔细确认每一处冲突的处理方式。如对冲突毫无争议,可以采用如下命令快速解决:

$ git checkout --theirs/--ours 

处理外毕后需要 add 处理过的文件,并将他们合并到最近一次提交(commit的时候加上–amend)。

之所以是amend的形式,是因为rebase是逐commit进行的,冲突产生于当前rebase失败的提交。

注意:

如上面介绍的,执行过 rebase 后的分支 commit 发生了改变,必须执行强制推送。当强推后,其实远程的 commit 也会被改变。

这时如果该分支被团队内多人同时维护,那么其他成员在执行 pull 的时候,也会提示有冲突(即使代码上并没有冲突)。 推荐的解决方案是其他人重新 fetch 该分支。

由此可以看出,rebase 并不适合多人维护的分支。这就要求我们对于 feature 和 hotfix 的实现应该尽量原子化。

另外重申:在推送过程中一定要谨慎使用 --force

10 git fetch&git pull

git fetch 是从远程获取最新代码到本地;
git pull 是从远程获取罪行代码并合并到本地。

刚用git的人可能会比较疑惑fetch和pull究竟有什么区别,其实上面已经说得很清楚了,用一个数学表达式来写就是:pull = fetch + merge。
fetch习惯用法:

$ git fetch origin develop:feature/xxx

上面的用法是从远程上获取develop的代码到本地,并重命名成开发分支 feature/xxx。
pull 的习惯用法:

$ git pull origin feature/xxx
$ git pull --rebase origin develop

11 git reflog

git reflog用来查看本地 所有分支所有操作 记录(包括commit和reset的操作)。
用法:

$ git reset --hard [version]

总结:

上面的是一个命令是我从我的系统中执行 history ,拿到的是个出现频率最高的命令,可能不是很全,但是我认为应该是日常最常用到的。熟悉最基本的命令,可以提高日常工作效率,也是规范提交的前提。

团队出现过某某以前提交的代码,丢失了,修复过的bug又复现了的问题,我认为这可能跟以前的代码提交和分支管理方式有一定关系,以前每个人都是super admin,每个人都可以向develop上推代码、合代码,当众多的“管理员”先后或者共同维护同一段代码的时候,往往会出现代码冲突,由于每个人都有合并的权限,所以当团队内部合并时沟通不畅时,就有可能会出现丢代码的情况。各个代码管理平台为了解决这类问题,基本都采用了收紧分支合并权限,设置保护分支的形式(code 平台的保护分支已经在企业版中上线了,社区版日后也会安排上线)。

我们没有了直接合并代码的权限,那么代码何如只能走线上合并这条路,好在现在的 merge request 功能已经很完善,既可以进行代码r eview,又可以完成代码合并。
当主干分支和我们自己的分支同时前进时,合并merge_request后的结果,除了我们的commit不连续以外,好像也并没有什么问题,所以执行rebase、提交原子化等措施似乎也没什么必要,因为我们review代码很多时候往往只关注差异,只要能合并,commit是什么样子并不关心。

但当我们的 merge_request 有冲突的时候呢?线上不能合并,线下合并又不能推送,这时候rebase 就是一条捷径了。当我们执行rebase的时候,git 会逐 commit 的将我们的代码和主干代码进行合并,如果有冲突,那么就停下来将冲突交给我们来解决,由于commit本身只是实现了一个单一的功能,且commit message中包含了这个commit的前因后果,所以往常让我们头疼的冲突也会变得很容易解决了。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李少辉-开发者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值