高富帅们的Git技巧

译者序​

Git是一个分布式版本控制系统,拥有许多神奇而易用的特性(好比:分支),这让它能够轻松适应各类工做流程。这篇文章不涉及Git的基本使用,而是介绍了一些高级却有用的小技巧。让咱们一块儿来看看高富帅们的Git技巧,准备好逆袭吧!linux

以“块”形式暂存你的改动

你确定已经很熟悉的使用git add命令来将改动暂存到暂存区(staging area)了。你可能也会偶然由于两个不一样的缘由而作了一次改动,却没有分别提交(仅仅提交了一次),因此,当你执行git log时,会看到诸如这样的提交信息:“修改X,改动无关的Y”。若是这看起来像是你的工做方式,交互式add将是你的有力工具。git

交互式add(或者叫add块),将会一个块一个快的循环你的改动。使用命令git add -p时,你能够在每一个改动“块”(即:连续的改动会被组织到一块儿)时进行一些选择,好比:切分当前块为更小的块、跳过一个改动块、甚至手动的编辑该块,你能够敲入?来查看全部该命令提供的选项。github

开始以“块”形式暂存改动简单到只需一条命令(括号部分替换为特定文件):安全

git add -p (path/file)

译者注:感受这条命令日常用的较少,我遇到须要分别提交的状况时,都是手动来add而后提交,该命令是这种方法的高级版本。咱们日常可能对提交历史的重视比较低,经常出现一些无用的、无心义的提交信息,能够试试这条命令。分布式

切换到最后所在分支

做为一个善良的码农,当你须要快速作些修正或是清理工做时,你都应该花些时间来对待。若是你的工做流程是十分依赖分支的话(译者注:强烈建议如此),你可能不但愿无关的修正影响到如今正在进行功能开发的分支。这意味着,你应该使用git stash命令来暂时存放你的改动,而后切到master分支(译者注:或是其它啥分支,我通常是取名为fix),在那个分支进行修正。(译者注:修正完了,能够切回正在进行功能开发的分支,执行git stash pop来弹出以前暂存的改动,继续进行开发)。在不一样分支间切换很乏味,幸亏这里有个快捷命令能够切换到你最后所在的分支:工具

git checkout -

这个语法对于使用linux的高富帅们来讲必定不陌生,cd命令有个相似的缩写cd -,表示切换到你最后所在的目录。当你须要切回功能开发分支时,你根本不用关心那个分支是啥名,只需git checkout -。post

译者注:感受tab键的自动补全也挺好用的,不过这条命令能够少敲点字。有了这条命令,妈妈不再用担忧个人分支切换了。版本控制

显示哪些分支被合并了(或是哪些没有被合并)

在使用git时,你可能会建立许多分支,致使执行git branch命令列出分支时变得有些杂乱。因而,你想处理那些已经合并到master分支的无用分支,可是,当你执行git checkout -d 来删除分支时可能会遇到“麻烦”(译者注:git会拒绝删除未合并的分支并提示你),若是使用如下命令,你将再也不须要三思然后删,能够自信的处理那些已经合并了的分支。code

若是你想要看看你的本地分支里哪些分支是已经合并进你当前所在的分支时,可使用:orm

git branch --merged

反过来,若是须要查看哪些分支尚未合并进当前所在的分支,可使用:

git branch --no-merged

结合高富帅的UNIX工具,你能够轻松的删除那些已经合并了的分支:

git branch --merged | xargs git branch -d

译者注:xargs是UNIX平台的一个工具,它的做用是将参数列表转换成小块分段传递给其余命令,以免参数列表过长的问题。若是git branch --merged显示的是a,b,c三个分支已经合并,上面的命令会转换为:git branch -d a b c。更多xargs的信息:http://zh.wikipedia.org/wiki/Xargs

从另外一分支获取文件内容而不用切换分支

设想你正在进行重构,你建立了好几个分支并在各分支下进行改动。这时,你想把另外一个分支里某一个文件的改动引入到当前工做的分支里,为了达到目的你可能须要好几步:git stash你的改动;切换到那个分支;获取文件的改动;切回工做分支(固然是使用git checkout -);继续进行编辑(译者注:别忘了git stash pop)。可是,你也能够直接检出另外一分支的文件,而且合并到你当前所在的工做分支,使用命令(括号部分替换为对应的分支和文件):

git checkout (branch) -- (path/file)

以最后提交排序的Git分支

想必你已经使用上面的tip处理了杂乱的分支,有一些是用--merged选项标志来清理的吧。那其它的分支咋办呢?你咋知道哪些是有用的,哪些是彻底过时无用的呢?git for-each-ref命令能够打印出一个列表,该列表显示每一个分支最后一次提交的引用(reference)信息。咱们能够自定义输出来包含一些有用的信息,更重要的是咱们还能够按日期排序。可使用下面的命令来输出一个列表,该表将显示按时间前后排序的每一个分支的最后提交信息、提交者等信息:

git for-each-ref --sort=-committerdate --format="%(committername)@%(refname:short) [%(committerdate:short)] %(contents)"

还能够把它定义在gitconfig里:

[alias]
  latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"

译者注:定义后就只需执行git latest了。注意双引号须要转义!

在玻璃房内的人们别用git blame

或者说,在玻璃房内的人们不该该直接使用git blame而不带下文的选项标志。(译者注:玻璃房内的人是彻底能被别人看到的人。这里的意思应该是想说,你每一次提交的变更都会被记录到git仓库的历史,对于git仓库来讲,你就像是住在玻璃房里的人,没有任何秘密,你根本逃不过git的”责问“)git blame是颇有用的命令,它就像使用科学来证实你是正确的!可是请注意,许多文件的变更是很表面的,发现问题的来源须要更多的探索。像是移除空白、移动内容到新行、移动内容到另外一文件等动做均可以使用选项来忽略掉,以便更容易的找到代码变更的始做俑者。

在你blame(责备)他人前,记得用如下命令看看结果:

git blame -w  # 忽略移除空白这类改动
git blame -M  # 忽略移动文本内容这类改动
git blame -C  # 忽略移动文本内容到其它文件这类改动

译者注:git blame用来显示一份文件每一行的最近一次提交的提交hash值和提交者。当你跟别人说“我真的没改过这个文件啊”以前,就得git blame下。

在整个git仓库提交历史中找寻内容(而后删掉它)

你有时可能须要查找一行你写的代码,可是就是没法找到。它可能安放在了一些已经被遗忘的分支,或是删除了好久,又或是就在那显而易见的地方。不管哪一种方式,你均可以经过一些命令在整个git仓库的历史中搜寻特定的字符串。

首先,咱们须要拿到全部的提交,而后,使用git grep来搜寻特定的字符串。以下:

git rev-list --all | xargs git grep -F '搜寻的字符串'

你可能有一个粗心的朋友不当心在仓库里提交了诸如,用户名、密码、外婆的大蒜食谱等敏感信息。首先,他们得更改用户名、密码(并向外婆道歉)。而后,你须要搜寻这些得罪人的文件,并将他们从整个仓库的历史里抹去(这听起来好像很容易)。通过这个处理,那些执行git pull的伙计们就会发现全部提交中包含的敏感信息都被清理干净了,而那些没有合并你的远程改动的家伙仍是拥有敏感信息(因此,千万别忘记先改用户名和密码)。咱们来看看怎么操做。

首先,重写每一个分支的历史,移除敏感信息:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch (filename)' --prune-empty --tag-name-filter cat -- --all

而后,将记录敏感信息的文件增长到.gitignore文件,并提交(括号部分替换为对应文件名):

echo (filename) >> .gitignore
git add .gitignore
git commit -m "Add sensitive (filename) file to gitignore"

接着,因为咱们改写了历史,咱们须要“强制”的将改动推到远程:

git push origin master --force
# 译者注:还可使用命令
git push origin +master

最后,这个文件还在你的本地仓库里,还须要将它彻底抹除:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

你这粗心的朋友从敏感文件的危机中解脱,而你用你高超的git知识成功逆袭,成为了他的英雄!

译者注:一天,妹子叫我去她家帮她把她的三围信息从git仓库的历史里彻底删除,我研究了好久不得要领。妹子说,不如咱们作点其它的事吧。我以为个人git知识被她鄙视了,坚决的说,我必定要把它删掉!而后,就没有而后了… …

忽略文件跟踪

在和他人合做时可能经常意味着你须要更改一些配置才能让应用在环境里跑起来,这时,经常会不当心把这些只对你有意义的配置文件也给提交了。为了避免再经常关注这些文件,看着它们在git status时放肆的显示“modified”,你能够告诉git忽略它们的改动。这种方式,能够当作是一种和仓库绑定的gitignore文件(括号部分替换为对应文件):

git update-index --assume-unchanged (path/file)

译者注:感受,.gitignore文件更方便和好理解。

让分支的历史归零

无论出于啥理由,有时从头开始正是你须要的。也许是你接手了一个不确信能安全开源的代码仓库;也许是你要着手作些全新的事情;也许是你想建立用于其它目的一个新分支,又但愿继续在仓库里维护它(好比:github页面,项目的文档一类的东西)。上述的情形下,你能够很是简单的建立一个没有提交历史的分支(括号部分替换为对应分支):

git checkout --orphan (branch)

译者注:咱们知道,分支只是对提交的一个引用,因此,每当从当前分支建立一个分支时,被建立的分支都会延续以前的历史,可是这种方式却不会,是一个完彻底全干净的git分支,没有任何的提交!

你必定离不开的别名

不讨论能节省大量敲击时间的“git别名(git alias)”技巧的git文章必定都是在耍流氓。中止输入冗长的命令,使用超级有用的别名吧!git别名能够加到.gitconfig文件里,或是使用命令(译者注:本质就是改写.gitconfig命令)来增长(括号部分替换为别名和对应的命令):

git config --global alias.(name) "(command)"
  1. 在依赖分支的工做流程中,你经常要在不一样分支间切换,每次敲击节约你6个字母。

    co = checkout
  2. 在提交前瞧瞧你将要提交的都有什么改动是一个好习惯,这能够帮助你发现拼写错误、不当心的提交敏感信息、将代码组织成符合逻辑的组。使用git add暂存你的改动,而后使用git ds查看你将要提交的改动动。

    ds = diff --staged
  3. 你可能十分熟悉git输出的详细状态信息了,当到达必定境界时,你可能须要忽略全部那些描述,直击问题的核心。这个别名输出将输出git status的简短形式和分支的详细信息。

    st = status -sb
  4. 你是否在提交后才发现忘记git add某个文件了,或是提交了才想再改动些啥?amend(修正)暂存区到最近的一次提交吧。(译者注:这个命令不太好理解,--amend是重写提交历史,-C是重用某次提交的提交信息。场景是当你提交完了发现还有些改动没提交,又不想写什么“改动了X,再次提交”这种狗血的提交信息。从新git add并git amend后,重用上次的提交信息再次提交,替换上次的不完整提交。特别注意--amend重写了提交,若是你已经push到远程了,慎用这条命令!)

    amend = commit --amend -C HEAD
  5. 有时上面的修正可能很差使了,你须要undo(撤销)。undo会回退到上次提交,暂存区也会回退到那次提交时的状态。你能够进行额外的改动,用新的提交信息来再次进行提交。

    undo = reset --soft HEAD^
  6. 维护一个多人编辑的代码仓库经常意味着试着发现何人在改动什么,这个别名能够输出提交者和提交日期的log信息。

    ls = log --pretty=format:'%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green) [%cn]' --decorate --date=short
  7. 这个别名用来在一天的开启时回顾你昨天作了啥,或是在早晨刷新你的记忆(括号内替换为本身的email)。

    standup = log --since '1 day ago' --oneline --author (YOUREMAIL)
  8. 一个复杂的仓库可能很难用直线式的输出来查看,这个别名能够用图表的形式向你展现提交是怎样及什么时候被加到当前分支的。

    graph = log --graph --pretty=format:'%C(yellow)%h %C(blue)%d %C(reset)%s %C(white)%an, %ar%C(reset)'

译者注:我根据上面的别名进行了一些整理修改,这是我如今的.gitconfig里的别名配置:

[alias]
  st = status -sb
  co = checkout
  br = branch
  mg = merge
  ci = commit
  ds = diff --staged
  dt = difftool
  mt = mergetool
  last = log -1 HEAD
  latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"
  ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=short
  hist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=short
  type = cat-file -t
  dump = cat-file -p

via alimama mux
做者:Chris Kelly 译者:栖邀
英文原文