[前端漫談] 一巴掌拍平Git中的各種概念

賣梳子的鯉魚發表於2019-01-12

0x000 導讀

git的文章很多,但是大部分都是一個套路,講概念,講命令,太多的概念和命令總是讓人有一種稀裡糊塗的感覺,講的很對,但似乎沒能講透,沒有醍醐灌頂的感覺,大概是我的悟性差吧。所以這幾天一直在做各種git的實驗,並且閱讀了一些部落格、文件、資料,綜合下來整理出了這麼一篇文章。注意:

  • 本篇文章旨在說明我對git的理解,只是一家之言,聊以分享。
  • 本片文章不是一篇命令教程,或者概念說明,需要一定的git使用經驗和踩坑經驗。
  • 為了閱讀方便,commitID只保留4位

0x001 總結[提前]

這是一篇比較亂七八糟的文章,不從傳統出發,嘗試用自己的思想去理解git這一神奇的工具。以前我覺得git命運石之門,我們在不同的時間線(分支)上跳躍,所有的事件都必須且只能發生在時間線上。但是現在我覺得git無限的可能性的集合,一個事件可以引申出無限的可能性。而我們要做的是用工具(branch、tag、reset、rebase、merge....)來組織這些可能性,從而構成一個有序的、向前發展的歷史,引導整個歷史的發展,構建可以走向未來的工程。

0x002 存檔和讀檔

  • 存檔

    其實吧,版本就是存檔,就是遊戲中的存檔,我們不斷的推進專案,完成一個又一個任務,然後中途不斷的存檔,就形成了版本迭代。而版本多了,自然就需要管理了,自然就有了版本管理系統。

    在遊戲中,存檔可以手動存檔,也可以到指定點存檔,也可以自動定場景存檔。在遊戲中,存檔之後形成的東西叫做檔案,而在git中,叫做commit。我們可以使用git add+git commit完成一個檔案的建立,或者說版本的建立。

    一個commit擁有許多的屬性,比如IDMessageDateAuthor:等等,這些資訊都有助於我們瞭解這個版本,就像遊戲中的存檔會以關卡名/圖片之類的資訊來提示你這個存檔所代表的進度,比如使用git log可以獲取以下資訊:

    commit 4963 (HEAD -> master)
    Author: **********
    Date:   Thu Jan 10 15:22:12 2019 +0800
    
        版本H
    
    commit 1a42
    Author: **********
    Date:   Thu Jan 10 15:25:01 2019 +0800
    
        版本G
    
    commit 931b
    Author: **********
    Date:   Thu Jan 10 15:24:50 2019 +0800
    
        版本F
    ....
    複製程式碼
  • 讀檔

    既然有存檔,那就有讀檔。遊戲中直接選擇一個檔案就行了,那git中呢?如果有視覺化操作工具,那我們直接點選也是可以的,但現在不使用工具,而使用命令列,該如何呢。讀取一個存檔說白了在git中就是讀取一個commit而已,所以我們可以使用git checkoutgit reset兩個命令來做到,那如何指定版本呢?前面提到的commit屬性中的ID可以幫我們實現這個目標。

    • 環境說明:我在倉庫中git commit了8個,每個commit都新增了一個從a-h的檔案,並在commit資訊中新增版本標記
      2019-01-10-18-04-08
    • 使用git checkout切到版本A,可以發現,此時只有檔案a
      $ git checkout 401e
      Note: checking out '401e'.
      
      ...
      
      HEAD is now at 401e1b6 版本A
      
      $ ls
      README.md       a.txt
      複製程式碼
    • 使用git reset切換到版本G,可以發現,此時有了a-g幾個檔案了
      $ git reset 1a42
      Unstaged changes after reset:
      D       b.txt
      D       c.txt
      D       d.txt
      D       e.txt
      D       f.txt
      D       g.txt
      
      $ git stash
      Saved working directory and index state WIP on (no branch): 1a4268d 版本G
      
      l$ ls
      README.md       a.txt           b.txt           c.txt           d.txt           e.txt           f.txt           g.txt
      複製程式碼
  • 總結: 我們通過commitID屬性配合其他命令來達到了任意讀檔的目的,可以在各種版本中隨意穿梭,快樂的很啊。而讀檔的姿勢其實還有很多,但不外乎是對commit操作方式的不同,在git中,我覺得commit 才是構成整個版本管理的基本栗子。每一個commit都是獨立的個體,雖然和其他commit存在著關聯,但是依舊是獨立的,而我們在commit構成節點序列中來回移動和操作,就可以達到所有對版本管理的目的。

0x003 別名系統

在上一個章節中,我們已經可以依靠一些命令和commit ID做到在版本中自由的穿梭,但是卻帶來一個問題,那就是commit ID的記憶難度。commit IDhash值,儘管git支援只提供前幾位就能匹配到hash,並且也提供了commit message來說明commit,但是依舊存在commit的辨識和記憶問題。而這個問題,可以通過別名系統來解決。

所謂的別名系統,其實是我自己歸納的概念,指的其實就是HEADbranchtag這三個東西。在我看來,這三個東西都是一樣的東西,都是別名,也就是標記一個commit的東西而已,只是在行為表現上有一些區別。

  1. HEAD

    一個倉庫只有一個HEAD,指向你當前所在的commit。如果你建立了一個commitHEAD將會指向這個新的commit。也可以通過命令,強制HEAD指向某個commit,比如resetcheckout。也就是不論你在哪個commit之上,那麼HEAD就在哪兒,或者說,其實你能在哪個commit,是通過修改HEAD指向的commit實現的。

    • 通過修改HEAD在各個版本之間旋轉跳躍

      2019-01-11-13-20-54

  2. branch

    一開始我覺得這個東西才是git的核心,因為建立專案的時候,我們就處於master分支之上,並且我們在工作中,往往也是基於分支工作的。但是後來發現,分支在本質上毫無意義,並不需要真的基於branch去工作,基於commit就行了。而branch只是提供了一個方式來管理這些commit。branchHEAD相同點是隨著新的commit的建立,branch指向的commit會不斷更新,當然前提是你需要在這個branch所在的commit上建立新的commit。而branchHEAD的不同點在於HEAD只能有一個,branch可以有多個。

    實驗一:用branch來實現切換版本

    • 目前的庫情況
      $ git log --pretty=oneline
      1a42 (HEAD) 版本G
      931b 版本F
      071d 版本E
      0caa 版本D
      7855 版本C
      1295 版本B
      401e 版本A
      複製程式碼
    • 為版本A-G分別建立一個分支
      $ git checkout 1a42 -b G
      Switched to a new branch 'G'
      $ git checkout 931b -b F
      Switched to a new branch 'F'
      $ git checkout 071d -b E
      Switched to a new branch 'E'
      $ git checkout 0caa -b D
      Switched to a new branch 'D'
      $ git checkout 7855 -b C
      Switched to a new branch 'C'
      $ git checkout 1295 -b B
      Switched to a new branch 'B'
      $ git checkout 401e -b A
      Switched to a new branch 'A'
      
      $ git log --pretty=oneline
      1a42 (HEAD -> G) 版本G
      931b (F) 版本F
      071d (E) 版本E
      0caa (D) 版本D
      7855 (C) 版本C
      1295 (B) 版本B
      401e (A) 版本A
      複製程式碼
    • 接下來就可以換一種方式在版本之間跳躍了,並且不需要記住或者查詢冗長的commit ID
      $ git checkout A
      Switched to branch 'A'
      $ git checkout B
      Switched to branch 'B'
      $ git checkout C
      Switched to branch 'C'
      $ git checkout E
      Switched to branch 'E'
      $ git checkout F
      Switched to branch 'F'
      $ git checkout G
      Switched to branch 'G'
      複製程式碼

    實驗二:分支跟隨新的commit

    • 當前庫的情況,注意:這裡的HEAD -> G表示HEAD指向了branch G,而branch G指向了版本G
      $ git log --pretty=oneline
      1a42 (HEAD -> G) 版本G
      931b (F) 版本F
      071d (E) 版本E
      0caa (D) 版本D
      7855 (C) 版本C
      1295 (B) 版本B
      401e (A) 版本A
      複製程式碼
    • 新增一個檔案,建立一個commit
      $ echo 'h'> h.txt
      $ git add h.txt
      $ git commit -m '版本H'
      [G d346d27] 版本H
      1 file changed, 1 insertion(+)
      create mode 100644 h.txt
      複製程式碼
    • 此時檢視log,可以看到HEADG都指向了版本H,就是所謂的branch跟著commit動,但是它真的是跟著commit動嗎?
      $ git log --pretty=oneline
      d346 (HEAD -> G) 版本H
      1a42 版本G
      931b (F) 版本F
      071d (E) 版本E
      0caa (D) 版本D
      7855 (C) 版本C
      1295 (B) 版本B
      401e (A) 版本A
      複製程式碼

    實驗三:分支跟著啥動

    • HEAD指向版本Gcommit,而不是分支G,也就是使用git checkout commitID,而不是使用git checkout branchName,可以看到,此時HEAD不指向G,而是HEADG同時指向了版本Hcommit
      $ git checkout d346 # 版本 H 的 commitID
      $ git log --pretty=oneline
      d346 (HEAD, G) 版本H
      1a42 版本G
      931b (F) 版本F
      071d (E) 版本E
      0caa (D) 版本D
      7855 (C) 版本C
      1295 (B) 版本B
      401e (A) 版本A
      複製程式碼
    • 繼續建立一個commit,可以看到,這個時候分支G不再跟著commit移動了,所以,只有在HEAD指向branch的時候,branch才會向前移動,也就是隻要HEAD來到branch身邊,branch就會跟著HEAD跑。
      $ echo 'i'> i.txt
      $ git add i.txt
      $ git commit -m "版本I"
      [detached HEAD 2e836eb] 版本I
      1 file changed, 1 insertion(+)
      create mode 100644 i.txt
      $ git log --pretty=oneline
      2e83 (HEAD) 版本I
      d346 (G) 版本H
      1a42 版本G
      931b (F) 版本F
      071d (E) 版本E
      0caa (D) 版本D
      7855 (C) 版本C
      1295 (B) 版本B
      401e (A) 版本A
      複製程式碼
  3. tag

    tag是比較特殊的一個別名型別,他無法移動,或者說不推薦移動。一旦一個tag和指向某個coimmit,就不希望它移動,因為tag就是用來標記這個commit的,他是一個孤獨而忠誠的守望者,而不像branch,花間游龍似的浪子。

    • 現在庫的情況
      $ git log --pretty=oneline
      1a42 (HEAD, G) 版本G
      931b (F) 版本F
      071d (E) 版本E
      0caa (D) 版本D
      7855 (C) 版本C
      1295 (B) 版本B
      401e (A) 版本A
      複製程式碼
    • 為每個版本新增一個tag,為了區別分支名,統統加了個T
      $ git tag TA A
      $ git tag TB B
      $ git tag TC C
      $ git tag TD D
      $ git tag TE E
      $ git tag TF F
      $ git tag TG G
      
      $ git log --pretty=oneline
      1a42 (HEAD, tag: G, G) 版本G
      931b (tag: TF, F) 版本F
      071d (tag: TE, E) 版本E
      0caa (tag: TD, D) 版本D
      7855 (tag: TC, C) 版本C
      1295 (tag: TB, B) 版本B
      401e (tag: TA, A) 版本A
      複製程式碼
    • 現在又多了一種旋轉跳躍的方式了
      $ git checkout TA
      Previous HEAD position was 1a4268d 版本G
      HEAD is now at 401e1b6 版本A
      $ git checkout TB
      Previous HEAD position was 401e1b6 版本A
      HEAD is now at 1295260 版本B
      $ git checkout TC
      Previous HEAD position was 1295260 版本B
      HEAD is now at 7855905 版本C
      $ git checkout TD
      Previous HEAD position was 7855905 版本C
      HEAD is now at 0caa2b7 版本D
      $ git checkout TE
      Previous HEAD position was 0caa2b7 版本D
      HEAD is now at 071d00a 版本E
      $ git checkout TF
      Previous HEAD position was 071d00a 版本E
      HEAD is now at 931b3c9 版本F
      $ git checkout TG
      Previous HEAD position was 931b3c9 版本F
      HEAD is now at 1a4268d 版本G
      複製程式碼
    • 總結 所以,不管是HEADtagbranch,都是一種別名,除了行為表現上的差別,沒有太大的不同,特別是branchtag,不過都只是提供了一種管理commit的方式。

0x004 分叉

在上一章節中,我們揭開了別名系統的紅蓋頭,這一章,我們就開始探索一下分叉的神祕。

和遊戲中的存檔一樣,有時候一個遊戲有許多的選擇,這些選擇指向了不同的結果。而作為遊戲玩家,我們希望能夠走完所有的選擇,以探索更多的遊戲樂趣。所以我們會在做選擇的時候存檔,而當我們走完一個選擇,就會讀取這個存檔,繼續往另一個選擇探索。這個時候,就產生了兩個不同的劇情走向,這就是分叉。

git中,其實我們可以有無數的選擇,每一個commit可以建立無數的commit,就會引申出無數的可能。

  • 我們遇到了一個抉擇,所以需要建立版本,暫時稱為版本X
    $ git log --pretty=oneline
    2cae (HEAD) 版本X
    ....
    複製程式碼
  • 然後我們選擇了走Y,並且沿著Y1一直走到Y3,這是盡頭
    $ git log --pretty=oneline
    d2e0 (HEAD) 版本Y3
    4ca8 版本Y2
    fcff 版本Y1
    2cae 版本X
    ...
    複製程式碼
  • 接著我們返回X,並選擇另一個選擇Z,從Z1走到Z3
    $ git checkout 2cae # 切到`版本X`
    $ git log --pretty=oneline
    16ff (HEAD) 版本Z3
    0ca5 版本Z2
    b4a7 版本Z1
    2cae 版本X
    ...
    複製程式碼
  • 總結

可以看到,我們順著兩個選擇一直往下發展,在這發展的過程中,我們完全沒有用到tagbranch,也就是為了印證commit 是構成 git 世界的基本栗子這一說明。

git log中,我們看不到了Y走向,那Y真的消失了嗎?不是的,我們依舊可以通過Ycommit ID來尋回Y的記錄。當然為了方便在YZ中切換,我們可以使用branch來標記一下YZ兩個走向,這樣就形成了YZ兩個branch了,也就是分叉!

那那些沒有被branch或者tag標記的commit呢?他們會消失嗎?會,也不會。不會是因為不被標記的commit將變成dangling commit,我稱之為遊離的commit,是git中最孤獨的存在,只要我們知道commitID,就會可喚回它。但是很大的可能是我們永遠不會記得這麼一個不被引用的commit,所以我呼籲,善待每一個commit。會是因為還是可能會被回收的,看這裡,git 也有 gc

0x003 合併

和遊戲的存檔不同的是,git中的版本可以合併,也就是說我可以在分支Y中做完任務Y1Y2Y3,然後分支Z中完成任務Z1Z2Z3,然後合併這兩個分支,結果回到了X,但是卻完成了Y1-y3Z1-Z3,並拿到了神器YZ,這讓boss怎麼活?

  • 實驗一:使用merge合併commit

    • 建立版本O
      $ echo O >> o.txt
      $ git add o.txt
      $ git commit -m '版本O'
      [detached HEAD 478fa6d] 版本O
      1 file changed, 1 insertion(+)
      create mode 100644 o.txt
      複製程式碼
    • 基於版本O建立版本P1
      $ echo P >>p1.txt
      $ git add p1.txt
      $ git commit -m '版本P1'
      [detached HEAD a3ab178] 版本P1
      1 file changed, 1 insertion(+)
      create mode 100644 p1.txt
      $ git log --pretty=oneline
      a3ab (HEAD) 版本P1
      478f 版本O
      複製程式碼
    • 基於版本O建立版本P2
      $ git checkout 478f # 版本O 的 commitID
      $ echo p2 >> p2.txt
      $ git add p2.txt
      $ git commit -m '版本P2'
      [detached HEAD cbccf52] 版本P2
      1 file changed, 1 insertion(+)
      create mode 100644 p2.txt
      $ git log --pretty=oneline
      cbcc (HEAD) 版本P2
      478f 版本O
      複製程式碼
    • 合併版本P1版本P2
      $ git merge a3ab # 版本P1 的 commitID
      $ git log --pretty=oneline
      656a (HEAD) Merge commit 'a3ab' into HEAD
      cbcc 版本P2
      a3ab 版本P1
      478f 版本O
      複製程式碼
  • 實驗三:使用rebase合併

    切換到版本P2,在版本P2中使用rebase

    $ git checkout cbcc # 版本P2 的 commitID
    ....
    HEAD is now at cbccf52 版本P2
    $ git rebase a3ab # 版本P1 的 commitID
    First, rewinding head to replay your work on top of it...
    Applying: 版本P2
    $ git log --pretty=oneline
    3bd7 (HEAD) 版本P2
    a3ab 版本P1
    478f 版本O
    複製程式碼
  • 實驗四:使用cherry-pick合併

    • 切換到版本O,新建版本P3
      $ echo 'p3'>> p3.txt
      $ git add p3.txt
      $ git commig -m '版本P3'
      git: 'commig' is not a git command. See 'git --help'.
      
      The most similar command is
              commit
      $ git commit -m '版本P3'
      [detached HEAD ae09e94] 版本P3
      1 file changed, 1 insertion(+)
      create mode 100644 p3.txt
      $ git log --pretty=oneline
      ae09 (HEAD) 版本P3
      478f 版本O
      複製程式碼
    • 切換到版本P2中使用cherry-pick合併版本P3的東西
      $ git checkout 3bd7 # 版本P2 的commitID
      ...
      HEAD is now at 3bd7820 版本P2
      $ git cherry-pick ae09 # 版本P3 的 commitID
      [detached HEAD f9dfba2] 版本P3
      Date: Sat Jan 12 11:35:27 2019 +0800
      1 file changed, 1 insertion(+)
      create mode 100644 p3.txt
      $ git log --pretty=oneline
      f9df (HEAD) 版本P3
      3bd7 版本P2
      a3ab 版本P1
      478f 版本O
      複製程式碼
  • 注意:合併中的衝突解決

    合併的過程中可能會出現衝突,比如同時拿到神器P1P2,但是在P1中賣掉了O之前拿到的裝備S,而在P2中則為S鑲上了寶石,那麼合併之後要怎麼處理?是賣掉S?還是保留鑲寶石的S?還是鑲了寶石再賣掉?深井冰啊!我不要面子的啊... 所以這裡就涉及到了合併的衝突解決,這裡不再贅述,不是我要講的內容。

0x005 變基

這個名詞讓人想入菲菲啊,每次專案新成員加入,總是會提醒他們注意要變基....

這裡不去說mergerebase的愛恨情仇,只說一些rebase的操作,用rebase來整理commit

上面說到commit 是構成 git 世界的基本栗子,所以,我們需要掌握一些栗子的操作方式

  • 檢視commit,可以使用git log,如果需要尋回忘記的commit,可以使用reflog來嘗試看看是否能夠找到

    $ git log --pretty=oneline
    68de (HEAD -> X) Merge branches 'Y' and 'Z' into X
    16ff (Z) 版本Z3
    ...
    $ git reflog
    23e799e (HEAD) HEAD@{0}: rebase -i (pick): 版本Z
    01a10d6 HEAD@{1}: rebase -i (squash): 版本Y
    a15dd72 HEAD@{2}: rebase -i (squash): # This is a combination of 2 commits.
    b6f2ea3 HEAD@{3}: rebase -i (start): checkout 1004
    f4c4ccc HEAD@{4}: rebase -i (abort): updating HEAD
    ...
    複製程式碼
  • 建立

    建立使用git add+git commit就行了

    $ echo error > error.txt
    $ git add error.txt
    $ git commit -m '一個錯誤的版本'
    [X bc90774] 一個錯誤的版本
    1 file changed, 1 insertion(+)
    create mode 100644 error.txt
    $ git log --pretty=oneline
    bc90 (HEAD -> X) 一個錯誤的版本
    68de Merge branches 'Y' and 'Z' into X
    ...
    複製程式碼
  • 更新上一個commit,直接使用git commit --amend

    $ echo error2 >> error.txt
    $ git add error.txt
    $ git commit --amend
    // 這裡將開啟一個vim視窗
    一個錯誤的版本
    
    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    #
    # Date:      Fri Jan 11 17:21:18 2019 +0800
    #
    # On branch X
    # Changes to be committed:
    #       new file:   error.txt
    #
    // 儲存退出之後輸出
    [X d5c4487] 一個錯誤的版本
    Date: Fri Jan 11 17:21:18 2019 +0800
    1 file changed, 2 insertions(+)
    create mode 100644 error.txt
    複製程式碼
  • 要更新歷史中的commit也是可以做到的,例如需要在版本X中加入檔案x1.txt,要使用互動式模式

    git rebase -i 2e83 # 指向 版本X 的前一個 commit
    複製程式碼

    此時將開啟一個互動式視窗

    pick 913b571 版本X
    pick 0eca5e3 版本Y1
    pick 33a9ca3 版本Y2
    pick b95b3ca 版本Y3
    pick 839c481 版本Z1
    pick 6fb6cb3 版本Z2
    pick c28d3e0 版本Z3
    ...
    複製程式碼

    版本X前面的pick改為e或者edit,儲存,然後退出,這個時候,倉庫將會回到版本X的狀態,並輸出

    Stopped at 913b571...  版本X
    You can amend the commit now, with
    
    git commit --amend
    
    Once you are satisfied with your changes, run
    
    git rebase --continue
    複製程式碼

    新增檔案x1

    $ echo x1 > x1.txt
    $ git add x*
    $ git commit --amend
    // 開啟互動式視窗可以修改 commit message
    $ git rebase --comtinue
    Successfully rebased and updated detached HEAD.
    複製程式碼

    此時又回會到原本的版本並且多出了檔案x1,就像是在版本X中就已經加入一樣

  • 插入一個新的commit,上面的栗子中不使用--amend,就會在XY1之間插入一個新的commit

    $ git rebase -i 2e83
    // 互動式視窗,吧`pick`改為`e`
    $ echo x2 > x2.txt
    $ git add x2.txt
    $ git commit -m '插入一個版本X2'
    [detached HEAD 1b4821f] 插入一個版本X2
    1 file changed, 1 insertion(+)
    create mode 100644 x2.txt
    $ git rebase --continue
    Successfully rebased and updated detached HEAD.
    $ git log --pretty=oneline
    30a5 (HEAD) 版本Z3
    4b00 版本Z2
    cc1d 版本Z1
    595e 版本Y3
    4456 版本Y2
    b6f2 版本Y1
    1b48 插入一個版本X2
    1004 版本X
    複製程式碼
  • 刪除

    刪除一個分支可以使用互動式rebase,命令:git rebase -i commitID,這裡的commitID必須是你要刪除的commit的前一個commit

    $ git rebase -i 68de
    複製程式碼

    此時將會開啟一個vim視窗

    pick bf2c542 版本Y1
    pick 588feec 版本Y2
    pick 1b2ae37 版本Y3
    pick 38f7cf3 版本Z1
    pick 080e442 版本Z2
    pick 206a7ae 版本Z3
    pick 6b01f70 一個錯誤的版本
    
    # Rebase 2caeda3..6b01f70 onto 2caeda3 (7 commands)
    #
    # 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改為d或者drop

    d 6b01f70 一個錯誤的版本
    複製程式碼

    儲存退出,輸出

    Successfully rebased and updated refs/heads/X.
    複製程式碼
  • 合併多個commit,比如合併Z1-Z3,開啟互動式視窗之後,將Z2Z3pick改為s

    $ git rebase -i 100468330c7819173760938d9e6d4b02f37ba001
    // 開啟了互動式視窗
    pick bf2c542 版本Y1
    pick 588feec 版本Y2
    pick 1b2ae37 版本Y3
    pick 38f7cf3 版本Z1
    s 080e442 版本Z2
    s 206a7ae 版本Z3
    複製程式碼

    儲存退出以後,又開啟互動式視窗,顯示要合併的commitmessage,這裡可以修改commit

    # This is a combination of 3 commits.
    # This is the 1st commit message:
    
    版本Z1
    
    # This is the commit message #2:
    
    版本Z2
    
    # This is the commit message #3:
    
    版本Z3
    複製程式碼

    這裡修改為Z,儲存,退出,輸出,可以看到,Z1-Z3消失了,取而代之的是Z,對Y1-Y3做操作

    detached HEAD f4c4ccc] 版本Z
    Date: Fri Jan 11 16:27:00 2019 +0800
    1 file changed, 3 insertions(+)
    create mode 100644 z.txt
    Successfully rebased and updated detached HEAD.
    $ git log --pretty=oneline
    f4c4 (HEAD) 版本Z
    595e 版本Y3
    4456 版本Y2
    b6f2 版本Y1
    
    $ git rebase -i 1004
    [detached HEAD 01a10d6] 版本Y
    Date: Fri Jan 11 16:24:37 2019 +0800
    1 file changed, 3 insertions(+)
    create mode 100644 y.txt
    Successfully rebased and updated detached HEAD.
    $ git rebase --continue
    複製程式碼
  • 重新排序commit順序,比如重排版本Y版本Z,交換一下順序就好了

    $ git log --pretty=oneline
    23e7 (HEAD) 版本Z
    01a1 版本Y
    $ git rebase -i 1b48
    複製程式碼

    這時候開啟互動式視窗,顯示

    pick a1942a3 版本Y
    pick eeabc6c 版本Z
    複製程式碼

    將它交換順序,儲存,退出

    pick eeabc6c 版本Z
    pick a1942a3 版本Y
    複製程式碼

    檢視結果

    Successfully rebased and updated detached HEAD.
    $ git log --pretty=oneline
    a194 (HEAD) 版本Y
    eeab 版本Z
    複製程式碼

0x006 資料

相關文章