git:到底什么是 fast-forwards ?

今天完成一个功能开发,提交代码的时候,突然提示如下错误:

    To C:/Users/Alpha/AppData/Local/Temp/d20170730-15308-3dbr6w/.git
     ! [rejected]        master -> master (non-fast-forward)
    error: failed to push some refs to 'C:/Users/Alpha/AppData/Local/Temp/d20170730-15308-3dbr6w/.git'
    hint: Updates were rejected because the tip of your current branch is behind
    hint: its remote counterpart. Integrate the remote changes (e.g.
    hint: 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.

意思就是本次提交被远程仓库拒绝了,因为当前分支无法与远程仓库对应起来。远程仓库对应分支默认有个指针指向最新提交到仓库的 commit ,而所有的本地仓库的分支都可以看做是从这个 commit 分散开来的。也就是本地分支的最后一次 push 到仓库的 commit 一定与仓库对应分支的最新一次 commit 是相同的,否则就无法对接。也就是会出现上面的错误提示。如果是正常 push 到仓库,正确的完成 commit 更新,那么这次更新就是一个 fast-forward 更新,而如果不理会错误警告用本地更新强制覆盖仓库,就是一次 no-fast-forward 更新,很明显,no-fast-forward 更新会导致记录丢失

那么这种问题是如何发生的呢?比如有两个人都是从仓库的 master 分支克隆到本地,然后分别开发。master 本身有一个指针 HEAD 指向最后一次 commit 记录 commit-0 。A 先完成一个功能,并 push 到仓库,这次 commit 记为 commit-A,这也就是一次 fast-forward 更新,此时仓库的 master 分支的 HEAD 指针就指向了 commit-A。接下来 B 也完成了一个功能,要向仓库 push commit-B,如果没有做额外操作,肯定会出现上面的错误。

知道错误是如何发生的,就可以避免了。既然仓库有了更新,那么就要先把仓库的更新拉取到本地。这里有两种方式可以拉取:一是直接使用 git pull 命令,该命令会在拉取的同时会直接与本地对应分支进行合并,如果确信仓库的更新与本地不会发生冲突,那么可以直接使用。但是很可能 A 与 B 都对同一些文件做出了修改,那么必然导致冲突。不过既然知道会冲突也只能老老实实解决冲突了,不管是 fetch 先解决冲突在合并还是 pull 先合并再解决冲突,这个过程少不了的,除非你确定仓库的更新是没用的可以直接抛弃,就可以执行 git push -f 强制覆盖到仓库,这会导致仓库中某些记录丢失。

我们借助于 githug 28 关来模拟看看:

    $ git push -u origin master
    To C:/Users/Alpha/AppData/Local/Temp/d20170731-15124-1ywoym1/.git
     ! [rejected]        master -> master (non-fast-forward)
    error: failed to push some refs to 'C:/Users/Alpha/AppData/Local/Temp/d20170731-15124-1ywoym1/.git'
    hint: Updates were rejected because the tip of your current branch is behind
    hint: its remote counterpart. Integrate the remote changes (e.g.
    hint: 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    
    $ git log origin/master --oneline
    68ad000 Fourth commit
    
    $ git log --oneline
    b977ec3 Third commit
    6d15890 Second commit
    5266aa2 First commit
    
    $ git push -f -u origin master
    Counting objects: 7, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (5/5), done.
    Writing objects: 100% (7/7), 546 bytes | 0 bytes/s, done.
    Total 7 (delta 2), reused 0 (delta 0)
    To C:/Users/Alpha/AppData/Local/Temp/d20170731-15124-1ywoym1/.git
     + 68ad000...b977ec3 master -> master (forced update)
    Branch master set up to track remote branch master from origin.
    
    $ git log origin/master --oneline
    b977ec3 Third commit
    6d15890 Second commit
    5266aa2 First commit

可以看到,强制覆盖 push 后,仓库的 Fourth commit 已经不见了。

但如果不想丢掉 commit-A 的同时又不想与 commit-A 合并,B 想继续接着本地仓库工作,可以使用 git rebase origin/master ,表示将本地所有 commit 排在仓库 的 commit 记录之后。然后向仓库的 push 就会被接受。同样借助于 githug 28 关,而且,这才是 28 关正确的过关方式:

详细过关过程如下:

    $ git push -u origin master
    To C:/Users/Alpha/AppData/Local/Temp/d20170731-14980-yb0fll/.git
     ! [rejected]        master -> master (non-fast-forward)
    error: failed to push some refs to 'C:/Users/Alpha/AppData/Local/Temp/d20170731-14980-yb0fll/.git'
    hint: Updates were rejected because the tip of your current branch is behind
    hint: its remote counterpart. Integrate the remote changes (e.g.
    hint: 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    
    $ git log origin/master --oneline
    015383a Fourth commit
    
    $ git log --oneline
    38aa398 Third commit
    1c03f48 Second commit
    ad32a6d First commit
    
    $ git rebase origin/master
    First, rewinding head to replay your work on top of it...
    Applying: First commit
    Applying: Second commit
    Applying: Third commit
    
    $ git log --oneline
    fbf0528 Third commit
    03d1240 Second commit
    9828360 First commit
    015383a Fourth commit
   
    $ git push -u origin master
    Counting objects: 6, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (6/6), done.
    Writing objects: 100% (6/6), 607 bytes | 0 bytes/s, done.
    Total 6 (delta 2), reused 0 (delta 0)
    To C:/Users/Alpha/AppData/Local/Temp/d20170731-14980-yb0fll/.git
       015383a..fbf0528  master -> master
    Branch master set up to track remote branch master from origin.

但其实还有一种比较常见的出现 no-fast-forward 这种错误的情境,是在你向一个只有你自己可访问的仓库 push 的时候发生的。当你已经将一次 commit-A push 到仓库后,然后因为某些原因又使用了 git commit --amend 修改了 commit-A ,这个时候 commit-A 就变成了 commit-B,而此时本地仓库就没有关系 commit-A 的记录了,这个时候再次向仓库 push ,很明显,commit-B 无法与仓库的 commit-A 进行对接,所以出现了 no-fast-forward 错误。这种情况下其实也很好解决,如果你确定 commit-A 已经完全无用并且没有人将 commit-A 拉取到本地进行进一步开发之后,你就使用 git push -f 来覆盖仓库记录。之后,你就会永远丢失 commit-A 记录了。

而对比发现,我之所以会遇到本文开头的错误,就是因为之前使用了 git commit --amend 命令修改了已经 push 到仓库的 commit 的注释导致的。因此,一旦已经 push 到仓库,想要做出修改,就只能通过一次新的 commit 来完成对某次已经 push 到仓库的 commit 记录的修改了,可以参考 githug 52 关 revert。

参考:
[1]https://git-scm.com/docs/git-push/2.10.0#Note about fast-forwards
[2]极客学院 githug 通关攻略

本文最早发布于 alphagao.com

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

推荐阅读更多精彩内容