一些git实用技巧

原创 @shhp 转载请注明作者和出处

这篇文章将通过场景分析的方式讲解一些git的实用技巧。这些技巧的使用频率或许不是很高,但真正需要用到的时候,你可能会感慨相见恨晚。

场景一、罪魁祸首

程序员小A发现源文件B.java中的某一行引发了一个bug。现在小A要找出是谁在什么时间提交了这次修改。那么小A应该怎么做呢?

最直接的想法应该是察看B.java这个文件的提交历史,在一个个commit中找出导致bug的修改。高级一点的话可以使用git bisect进行二分查找。其实git提供了一个简单直接的命令git blame可以帮助我们快速地找到那个“罪魁祸首”。git blame命令会输出指定文件中每一行的最后一次修改的作者、时间以及commit hash. 例如执行git blame B.java的输出如下:

^87fe8e2 (little A 2016-06-18 14:21:23 +0800 1) public class B {
^87fe8e2 (little A 2016-06-18 14:21:23 +0800 2) 
^87fe8e2 (little A 2016-06-18 14:21:23 +0800 3)         public static void main(String[] args) {
51497e8a (someone  2016-06-18 14:22:21 +0800 4)       System.out.println("It is me! Haha!");
^87fe8e2 (little A 2016-06-18 14:21:23 +0800 5)   }
^87fe8e2 (little A 2016-06-18 14:21:23 +0800 6) }

可以看到someone就是那个“罪魁祸首”!(好吧,其实我们还是不知道他是谁)

场景二、亡羊补牢1.0

小A刚刚提交了一个commit,但发现提交信息写错了。小A该如何修改这次的提交信息呢?

补救的方法有不少,这里列举两个:

  1. 执行命令git commit --amend, 在vim编辑界面修改提交信息后保存即可。这算是最简单的方法了。

  2. 依次执行

     git reset HEAD~1
     git commit -am "new message"
    

    这个方法首先将当前的working tree还原到最后一次commit之前的状态,然后再进行一次新的commit.

场景三、亡羊补牢2.0

现在难度升级,如果小A想修改中间某个commit的提交信息,该怎么办呢?

这时可以使用强大的git rebase -i命令。输入git rebase -i加一个commit hash之后回车,进入一个vim编辑界面。界面上按序列出了指定commit之后的所有commit,类似下面这样:

pick 383fd55 llal pick 1ddcdab update new.txt
pick 1519f1f update new.txt 2
pick fdc6370 delete new.txt
pick 0a8bcf8 add back new.txt
# Rebase 2aa6567..0a8bcf8 onto 2aa6567 (5 command(s))
# # Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

在每一行的开头可以指定一个你想执行的命令。默认是pick, 也就是不做任何修改。现在我们想修改中间的某个commit信息,只需把该commit所在行的pick替换成reword或者简写r,然后保存。接下来会出现新的编辑界面,然后我们就可以修改提交信息了。

可以看到除了reword还有其他一些命令,后面会用到其中的某几个。

场景四、追悔莫及

小A发现当前分支test上的最后N个commit有问题,想丢弃它们,于是执行了git reset --hard HEAD~N. 过了一段时间,小A突然想起之前丢弃的N个commit里面还有一些重要的修改!那么小A有什么办法找回丢弃的那N个commit呢?

这时可以求助于git提供的ORIG_HEAD这个变量。在进行pullmerge或者reset等操作后,HEAD一般都会移动好几个commit。而ORIG_HEAD就是指向这些操作之前HEAD所指向的那个commit。可以认为ORIG_HEAD是git提供的一剂后悔药。小A要找回之前丢弃的N个commit,可以这么做:

git checkout -b temp ORIG_HEAD
git checkout test
git merge temp

首先创建一个分支temp指向ORIG_HEAD指向的commit。然后切回test分支,merge一下temp,就可以让那N个commit重新回到test分支。

但是,如果小A在丢弃了那几个commit之后又做了pullmerge或者reset等操作,改变了ORIG_HEAD的值,那么上面这个方法就无效了。不过办法总比困难多,这时使用git reflog可以解决这个问题。git会记录HEAD的改变历史,而git reflog就是把这个历史记录输出。这个命令的输出如下:

87fe8e2 HEAD@{0}: reset: moving to HEAD~1
d42533e HEAD@{1}: checkout: moving from master to test
d42533e HEAD@{2}: commit: another

小A只需找到执行reset命令的那一行,那么它下面一行最前面的commit hash就是“解药”了。找到了这个commit,之后再仿照上面的步骤操作就可以了。

场景五、挑三拣四1.0

小A对一个文件B做了多处修改。在提交的时候发现有一些修改是需要排除在这次提交之外的。
那么怎么做可以对同一个文件的多处修改进行选择性提交?

这时可以使用git add -p命令。输入此命令(后面可以指定文件名)后,git会对每一个修改区块(一般来讲一段连续的修改算是一个区块)询问需要执行的指令:

Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

其中各指令的含义如下:

y - 将此区块加入index
n - 不加入index,并跳到下一个区块
q - 不加入index,同时结束操作
a - 将此区块以及当前文件剩余区块都加入index,跳到下一个文件
d - 与a相反
g - 选择具体的区块进行操作
/ - 搜索能够匹配给定正则表达式的区块
j - 将此区块标记成“暂定”,跳到下一个“暂定”的区块
J - 将此区块标记成“暂定”,跳到下一个区块
k - 将此区块标记成“暂定”,跳到前一个“暂定”的区块
K - 将此区块标记成“暂定”,跳到前一个区块
s - 将当前区块划分成更小的区块
e - 进入编辑界面进行更细的选择
? - 打印帮助

如果选择执行指令e则会进入一个新的vim编辑页面,如下所示:

# Manual hunk edit mode -- see bottom for a quick guide
@@ -1,3 +1,3 @@
 origin
-1
-2
+abc
+xyz
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

在这个界面可以进行更细致的操作:将某一行开头的-号换成空格,该行的删除修改则不会加入index,但这个删除的修改仍然保留;将以号开头的某一行删除,该行则不会加入index,但添加的这一行仍然保留在文件中。

场景六、挑三拣四2.0

如果上面那个B文件是一个Untracked file,那又该如何进行选择性提交呢?

可以先执行git add -N B命令。现在B里面的内容都变成了unstaged的修改。之后再用上面的方法进行选择性提交。

场景七、万源归一

小A开发出了一款可以自动编程并且提交代码的AI工具。不过有一天小A发现该工具的一个bug引发了一个问题:
它每修改一处就会进行一次commit,这样导致一次功能修改就提交了N多个commit。有什么办法可以合并这些commit变成一个commit?

可以再一次使用git rebase -i. 回顾场景三可以看到vim编辑界面的提示中有两条指令:

# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message

这两条指令都可以把当前的commit归并到前一个commit中,区别就在于是否保留当前commit的提交信息。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • Git 基础 基本原理 客户端并不是只提取最新版本的文件快照,而是把代码仓库完整的镜像下来。这样一来,任何一处协同...
    __silhouette阅读 15,855评论 5 147
  • Git 命令行学习笔记 Git 基础 基本原理 客户端并不是只提取最新版本的文件快照,而是把代码仓库完整的镜像下来...
    sunnyghx阅读 3,904评论 0 11
  • 上班好累!活着好累!胡思乱想好累! 我想等等在放弃
    曾慌张曾幻想阅读 471评论 0 0
  • 小美好完结啦!还在纠结选江辰还是选林杨?啧啧啧~其实你离扑倒他们只差了一件事! 最近看了大火的《致我们单纯的小美好...
    猪精女孩的少女心阅读 490评论 0 0
  • 1 大宝比小宝大六岁。 大宝从小是长辈们的焦点, 是爷爷奶奶的开心果, 是爸爸的小公主, 是妈妈的小棉袄。 当她五...
    小兔鱼娱阅读 364评论 0 0