(点击
上方蓝字
,快速关注我们)
译文:伯乐在线专栏作者 - zaishaoyi
英文:Alex
如有好文章投稿,请点击 → 这里了解详情
如需转载,发送「转载」二字查看说明
我使用Git已经有4年之久,在这里想分享一些实用的小技巧,希望能对大家有所帮助。
如果你对git一无所知,那么我建议先去读一下Git 常用命令速查。本篇文章主要适合有一定 git 使用基础的人群。
目录:
-
日志输出参数
-
查看文件的详细变更
-
查看文件中指定位置的变更
-
查看尚未合并(merge)的变更
-
查看其他分支中的文件
-
关于变更基线(rebase)的几点说明
-
本地合并之后保留分支结构
-
修复而非新建提交
-
的三种状态以及它们的相互转换
-
优雅地回退
-
使用第三方工具查看整个项目(而非单独文件)的变更
-
忽略空格变更
-
追加文件中的部分变更
-
发现并清理无用分支
-
暂存部分文件
-
如何写好提交信息
-
自动补全
-
创建常用命令的别名
-
快速定位问题版本
1.日志输出参数
命令示例:
git log --oneline --graph
也许你用过git log。它支持很多命令行参数,将这些参数结合起来使用,功能尤为强大。下面是我经常使用的一些参数:
例如,曾经有位主管要求在每周五提交周报。所以我每周五都运行一下这个指令: git log --author="Alex Kras" --after="1 week ago" --oneline,然后将输出结果编辑一下,发送给主管审核。
Git有很多命令行参数,使用起来非常方便。运行 man git log ,来看一下这些参数的作用。
如果这些都不好用,git还有一个 --pretty 参数,可以用来创建高级自定义输出。
2. 查看文件的详细变更
命令示例:
git -log -p filename
git log -p 或者 git log -p filename 不仅显示提交说明、提交者以及提交日期,还会显示这每次提交实际修改的内容。
然后你就可以使用 less 中常用的检索命令即“斜杠”后面加检索词/{{在此处添加你的检索词}}),在变更内容日志中检索指定的关键词(使用小写的n跳到下一条检索结果,大写的N跳到上一条检索结果)。
3. 查看文件中指定位置的变更
命令示例:
git log -L 1,1:some-file.txt
你可以使用 git blame filename 追查出文件中每一行是由谁变更的。
git blame 是一个非常强大的工具,但是又是无法提供足够的信息。
git log 提供了一个 -L 的选项。这个选项允许指定文件中的某些行。Git只会输出与这些行的变更日志。这有点像带焦点的 git log -p 。
git log -L 1,1:some-file.txt
4. 查看尚未合并的变更
命令示例:
git log --no-merges master..
如果你曾经与很多小伙伴工作在同一个持久分支上,也许会有这样的经历,父分支(例如:master)上的大量合并同步到你当前的分支。这使得我们很难分辨哪些变更时发生主分支,哪些变更发生在当前分支,尚未合并到master分支。
git log --no-merges master..可以解决这个问题。注意--no-merges 标志意味着只显示没有合并到任何分支的变更,master..选项,意思是指显示没有合并到master分支的变更(在master后面必须有..)。
你也可以运行 git show --no-merges master.. 或者 git log -p --no-merges master.. 命令(输出结果相同)来查看一下尚未合并的文件变更。
5. 查看其他分支中的文件
示例:
git show some-branch:some-file.js
用这个命令可以很方便地查看其他分支上的文件而无需切换到那个分支。
当然你也可以通过 git show some-branch-name:some-file-name.js 命令在终端中显示指定的文件.
你还可以将输出重定向到一个临时文件,这样你就可以再指定的编辑器中,以并排视图来查看它了。
git show some-branch-name:some-file-name.js > deleteme.js
如果你想查看另一个分支上文件与当前分支上文件的差异,只要运行下面的命令就可以了:
git diff some-branch some-filename.js
6.关于变更基线的几点说明
示例:
git pull --rebase
之前我们说过在远程分支上工作会有大量的合并提交。使用 git rebase 可以避免这些提交。
总的来说我认为变更基线是高级特征,最好是留到另一篇文章中详细介绍。
甚至在git book中也有这样的论述:
但是,令人狂喜的变更基线并不是任何情况下都适用,一言以蔽之:
若是工作区中存在尚未提交到仓库的变更,请不要使用变更基线。
如果遵照这条指南,不会有什么问题。不然,你可能会招致厌恶与谩骂。
https://git-scm.com/book/en/v2/Git-Branching-Rebasing#The-Perils-of-Rebasing
也就是说,变更基线本身并不可怕,关键在于使用方式。
或许,最好的方法是使用交互式变更基线,调用命令为 git rebase -i {{某个提交序列号}}。运行这条命令,会打开一个带有自解释指令的编辑器。由于变更基线不在本文的叙述范围之内,我们就此而止,不再深究。
变更基线一个非常有用的特殊用法 git pull –rebase。
举个例子,假设你正在master分支的一个本地版本上工作,你已经向仓库提交了一小部分变更。与此同时,也有人向master分支提交了他一周的工作成果。当你尝试推送本地变更时,git提示你需要先运行一下 git pull , 来解决冲突。作为一个称职的工程师,你运行了一下 git pull ,并且git自动生成了如下的提交信息。
Merge remote-tracking branch ‘origin/master’
尽管这不是什么大问题,也完全安全,但是不太有利于历史记录的聚合。
这种情况下,git pull --rebase 是一个不错的选择。
这个命令会迫使git将远程分支上的变更同步到本地,然后将尚未推送的提交重新应用到这个最新版本,就好象它们刚刚发生一样。这样就可以避免合并以及随之而来的丑陋的合并信息了。
7. 本地合并之后保留分支结构
示例:
git merge --no-ff
我喜欢为每一个bug或者特征创建一个新的分支。最大的好处就是,可以清楚地知道一系列的提交与某个任务的关系。如果你曾经合并过github 或者类似工具上的同步请求,那么可以通过运行 git log --oneline --graph 显示的视图,清楚地看到合并分支的历史。
如果你试图合并一个本地分支到另一个本地分支,也许会注意到git将两个分支平滑地衔接在一起,在git历史中看到的是一条直线。。
如果你想强迫git保存分支的历史,与合并同步请求的状况类似,你可以加一个 --no-ff 标志, 最后可以看到非常清楚的提交历史树。
git merge –no-ff some-branch-name
8. 修复而非新建提交
示例:
git commit --amend
这个指令顾名思义。
假设提交之后,你意识到自己犯了一个拼写错误。你可以重新提交一次,并附上描述你的错误的提交信息。但是,还有一个更好的方法:
如果提交尚未推送到远程分支,那么按照下面步骤简单操作一下就可以了:
-
修复你的拼写错误
-
将修正过的文件暂存,通过git add some-fixed-file.js
-
运行 git commit –amend 命令,将会把最近一次的变更追加到你最新的提交。同时也会给你一个编辑提交信息的机会。
-
准备好之后,将干净的分支推送到远程分支。
如果你工作在自己的分支,甚至可以在已经推送之后修正提交,你需要使用 git push -f (-f 代表强制执行),这条指令可以重写历史。但是,不要试图在一个很多人共同工作的分支(正如我们在变更基线那一部分讨论的分支)上这样做。此时,新建一次提交,在提交信息中描述错误,应该是最好的补救措施。
9. Git 中的三种状态以及它们之间的转换
示例:
git reset --hard HEAD 与 git status -s
目前你或许已经了解,git中的文件可以有三种不同的状态:
-
没有暂存
-
暂存并准备提交
-
已经提交
通过运行 git status可以看到关于文件的描述以及文件的状态。 运行 git add filename.js 命令可以将文件从未暂存状态移动到暂存并准备提交的状态, 或者使用 git add . 命令一次性暂存所有的文件。
通过运行 git status -s 命令可以看到状态图,其中 -s 是简短(short)的意思(个人认为),最终输出结果如图所示:
显然,git status 不显示已经提交了的文件,你可以使用 git log 命令来查看。
若要将文件在不同阶段之间转换,有很多可以用的命令供你选择。
重置文件
在git中,有3种类型的重置。重置是让文件回到git历史中的一个特定版本。
-
git reset –hard {{some-commit-hash}} —— 回退到一个特定的历史版本。丢弃这次提交之后的所有变更。
-
git reset {{some-commit-hash}}—— 回滚到一个特定的历史版本。将这个版本之后的所有变更移动到“未暂存”的阶段。这也就意味着你需要运行 git add . 和 git commit 才能把这些变更提交到仓库.
-
git reset –soft {{some-commit-hash}} ——回滚到一个特定的历史版本。将这次提交之后所有的变更移动到暂存并准备提交阶段。意味着你只需要运行 git commit 就可以把这些变更提交到仓库。
这些命令似乎并没有什么用处,但当你尝试着将文件在不同版本间移动时,使用它们会非常方便。
我平时使用重置的一些用例如下:
-
如果想清除变更记录,可以使用清理命令——git reset –hard HEAD (最常用)
-
如果想编辑,以不同的顺序,重新暂存,重新提交文件—— git reset {{some-start-point-hash}}
-
git reset –soft {{some-start-point-hash}}如果想把之前3次的提交,作为一次提交 git reset –soft {{some-start-point-hash}}
签出部分文件
如果你想取消某些文件在本地的变更,而同时保留另外一些文件在本地的变更,一个比较简单的方法是通过 git checkout forget-my-changes.js签出那些你想取消本地的变更的文件。
正如前面提到的那样,你也可以从其他分支或者之前的提交中签出文件的不同版本。
git checkout some-branch-name file-name.js 和 git checkout {{some-commit-hash}} file-name.js
你应该注意到了签出的文件处于“暂存并准备提交”的状态。如果想回到未暂存的状态,需要执行一下 git reset HEAD file-name.js。然后再次执行 git checkout file-name.js,文件回到了初始状态。
注意,运行 git reset –hard HEAD file-name.js 不起作用。总而言之,在git的不同阶段之间移动有点复杂,没有一个清晰的模式,我希望能通过这一部分有所改观。
10. 撤销而不产生提交信息
示例:
git revert -n
如果打算撤销之前一次或者两次的提交,查看这些提交都做了哪些变更,哪些变更又有可能引发问题,这个命令非常方便。
通常,git revert 会自动将回退的文件提交到仓库,需要你写一个新的提交信息。-n 标志告诉git先别急着提交,因为我只是想看一眼罢了。
11.用第三方差异工具查看整个工程而非单个目录的差异
示例:
git difftool -d
我最喜欢的差异工具是Meld。我在使用Linux的时候就开始使用它,并且一直持续到现在。
我并不是要推销Meld。假设你已经选好了比较工具,并且git能够将它作为一个合并和差异工具使用。接下来需要运行一下下面的命令,注意用你选择的差异工具的名字代替Meld:
git
config
--
global
diff
.tool.meld
git
config
--
global
merge
.tool
meld
之后你就可以运行s run git difftool some-file.js 来查看文件的差异了。
但是,有些比较工具(例如meld)支持全路径比较。
如果你调用 git difftool 时加 -d 标志,将会对整个文件夹进行比较。有时会非常有用。
git difftool -d
12. 忽略空格变更
示例:
git diff -w 或者 git blame -w
你是否遇到这样的情况:直到git blame 显示你应该为文件中的一切负责时才意识到自己重新调整了文件的缩进或者格式?
结果证明,git足够聪明来分辨文件中不同类型的变更。你可以调用许多命令(例如::git diff, git blame),加一个-w 标志,git将会忽略空白的变更。
13. 追加文件中的部分变更
示例:
git add -p
14. 发现并清理无用分支
示例:
git branch -a
仓库中存在大量远程分支的现象非常常见,甚至其中某些分支已经被合并到了master分支。如果你跟我一样有洁癖(至少有代码洁癖),这些分支可能会令你难以忍受。
你可以通过运行git branch查看所有的远程分支,还可以带有 -a 标志(显示所有的分支),或者带上 –merged标志 只显示那些完全合并到master分支的分支。
你或许首先想到的是运行git fetch -p (获取和清除旧数据),来确保你的数据是最新的。
如果你要获取真正的fancy,你可以得到一个所有远程分支的列表,以及这些分支最后一次提交的列表,通过运行:
git for-each-ref –sort=committerdate –format=’%(refname:short) * %(authorname) * %(committerdate:relative)’ refs/remotes/ | column -t -s ‘*’.
不幸地是,没有简单的方法(至少我不知道)可能只显示合并过的分支。所以你可能不得不比较两个输出或者写一个脚本来做这些事情。
15. 暂存部分文件
示例:
git stash --keep-index 或者 git stash -p
如果你还不了解 git stash 的功能,只是把当前工作区中的变更保存到一个有序的“git stack”。之后你可以用 git stash pop ,恢复你的变更。你也可以使用 git stash list 查看git栈里面你做的所有备份。通过 man git stash 查看更多可以用的选项。