一些常用的 Git 進階知識與技巧

譚光志發表於2021-11-09

1. 同一電腦存在多個 Git 賬號

假設我們在同一電腦上擁有多個 Git 賬號,例如公司內部使用的是 Gitlab,個人使用的是 Github 或者 Gitee。那就會遇到一種情況,上班時想給個人開源專案提交程式碼,但是 Git 繫結的是公司的賬號。

在這種情況下,我們可以讓 Git 繫結多個不同的 ssh key,每個 ssh key 對應一個不同的 Git 伺服器。

生成第一個 ssh key:

ssh-keygen -t rsa -C "xxx@xxx.xx"

生成第二個 ssh key:

ssh-keygen -t rsa -f path/to/file  -C "xxx@xxx.xx"

引數 -f 表示指定生成的檔名, path/to/file 是檔名路徑,例如 ~/.ssh/id_rsa_github

執行上面兩條命令後會得到兩對 ssh key。

image.png

這時還需要在該目錄下建立一個 config 檔案。寫上以下內容:

Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_github # 私鑰檔案路徑
                                                            
Host gitlab.com
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_rsa

Host gitee.com
HostName gitee.com
User git
IdentityFile ~/.ssh/id_rsa_github

這個配置檔案的作用是指定私鑰檔案位置。

然後我們可以把第一個金鑰配置到公司的 Gitlab 伺服器,並把相應的 Git 賬號和郵箱設成全域性。

git config --global user.name "xxx"
git config --global user.email "xxx@xxx.xx"

然後把另一對 ssh key 配置到 Github 上,並在電腦上的 Github 專案裡單獨配置 Git 使用者姓名和郵箱。

git config user.name "xxx"
git config user.email "xxx@xxx.xx"

至此,我們就大功告成了。可以同時在不同的 Gitlab 和 Github 專案上提交程式碼了。

2. 修改 git commit 記錄的使用者姓名和郵箱

假設電腦上同時存在 Gitlab 和 Github 專案,其中 Gitlab 使用者資訊已經全域性配置過了。現在新拉了一個 Github 專案,提交了一個 commit 並且已經推送到了遠端倉庫。這時發現該專案未配置 Github 的使用者資訊,預設使用的是全域性賬號 Gitlab 的使用者資訊。

我們可以通過以下命令來修改最近一次提交的使用者資訊:

git commit --amend --author="username <yyy@ccc.com>" --no-edit

username 是使用者名稱,使用者郵箱旁邊的 <> 符號不能去掉。修改後再執行 git push -f 推送到遠端倉庫。

如果要修改多個 commit 的使用者資訊怎麼辦? 可以通過以下的程式碼來修改:

git filter-branch --commit-filter '
    if [ "$GIT_AUTHOR_EMAIL" = "tanguangzhi@shiqiao.com" ];
    then
            GIT_AUTHOR_NAME="woai3c";
            GIT_AUTHOR_EMAIL="411020382@qq.com";
            git commit-tree "$@";
    else
            git commit-tree "$@";
    fi' HEAD

將上述程式碼中的使用者名稱和郵箱修改後,儲存為 .sh 格式的檔案,例如 edit.sh。然後在專案根目錄下執行 sh edit.sh(windows 下可右擊-> Git Bash Here -> sh edit.sh),再執行 git push -f 強推即可。

3. 修改某個歷史記錄的訊息

假設當前分支有 a b c d 四個 commit 記錄:

a
b
c
d

如果你想對 c 記錄的訊息進行修改。可以使用 git rebasec 記錄換到最前面,然後使用 git commit --amend 對其訊息進行修改。

具體操作步驟

執行以下命令對記錄 d 前面的三個 commit 進行編輯:

git rebase -i d

進行 vim 編輯介面後,移動游標到 c 記錄上,按下 dd 剪下該記錄,然後移動游標到第一行,按下 p 貼上,再輸入 :wq 儲存。

執行 git commit --amend 對切換順序後的 c 記錄進行修改。進入 vim 編輯介面後,按 i 進行修改,然後按 ESC,再輸入 :wq 儲存。

最後用前面講過的 git rebase 操作將 c 記錄恢復到原來的位置。

image

這個過程的執行結果就和上圖一樣,這是當前分支修改後和遠端分支上的對比,箭頭指向的記錄訊息就是修改後的訊息。

如果想把修改後的記錄同步到遠端倉庫,這時只要執行 git push -f 就可以了。

第二種方式

  1. 使用 git checkout -b <branchName> c 從指定記錄切出一個分支
  2. 在新分支使用 git commit --amend 修改提交訊息
  3. 使用 git cherry-pickb a 記錄,追加到新分支(注意,這裡的 b a 提交記錄是指原分支上的 commit,也就是選取原分支上的 b a 記錄新增到新分支上,這樣新分支上的記錄就變成了 a b c,並且 c 記錄的提交訊息在第二步已經修改過)
  4. 使用 git checout 原分支名 切換回原來的分支,再執行 git rebase <branchName> 合併新分支,最後強推到遠端分支

4. 挑選指定的 commit 進行合併

假設你切了一個 bugFix 分支來修復線上 bug,經過一段時間的努力後終於將 bug 修復了。但是為了除錯(加了很多 debug 程式碼)或其他原因,bugFix 上多了很多無用的記錄訊息。

commit3: 修復登入 bug
commit2: 新增 debug 語句
commit1: 新增 console 語句

例如上面的三個記錄,前面的兩個記錄新增了很多除錯程式碼,在最後一個記錄終於修復了 bug,並且刪除了除錯程式碼。這時如果直接將 bugFix 分支合到 master 分支,就會把除錯的記錄也合併進來。

這時可以使用 git cherry-pick 只將最後一個記錄合併到 master 分支。或者使用 git rebase 將 bugFix 分支另外兩個記錄拋棄,然後再合併。

5. ^~ 的區別

操作符 ^~ 符一樣,後面也可以跟一個數字。 ~ 表示向上返回幾代記錄。

但是該操作符後面的數字與 ~ 後面的不同,並不是用來指定向上返回幾代,而是指定合併提交記錄的第幾個父記錄。

Git 預設選擇合併提交的“第一個”父記錄,在操作符 ^ 後跟一個數字可以改變這一預設行為。

單看文字可能不太好理解,下面看幾個示例。

image.png

執行命令 git checkout main^ 回到第一個父記錄(原來 HEAD 指向 c3,現在指向 c1)。

image.png

執行命令 git checkout main^2 回到第二個父記錄(原來 HEAD 指向 c3,現在指向 c2)。

最後再來一個更復雜的示例:

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     |/    |
      B     C
       \   /
        \ /
         A
A =      = A^0
B = A^   = A^1     = A~1
C = A^2  = A^2
D = A^^  = A^1^1   = A~2
E = B^2  = A^^2
F = B^3  = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2  = B^^2    = A^^^2  = A~2^2
I = F^   = B^3^    = A^^3^
J = F^2  = B^3^2   = A^^3^2

通過這些示例我們還能發現 ~n 等於連續的 n 個 ^

6. git revertgit reset 的區別

git reset 可以回退 Git 的歷史記錄。例如當前分支有三個記錄,並且 HEAD 指向 c 記錄:

c <- HEAD
b 
a

如果我們想回退到 b 記錄,只要執行 git reset b 就可以了:

b <- HEAD 
a

接著使用 git push -f 將回退版本後的分支強制推送到遠端倉庫,這樣本地分支和遠端分支就同步了。

git push -f

git revert 也可以撤銷記錄,只不過它撤銷的記錄不會消失,這一點和 git reset 不一樣。git reset 撤銷的記錄就跟消失了一樣。

現在我們用 git revert 來重新演示下剛才的操作:

c
b 
a

如果我們執行 git revert b,則會在當前分支上再生成一個新的 commit 記錄,變成 a b c b',這個 b' 的狀態和記錄 b 是一樣的。

也就是說,執行 git reset b 後,當前的分支記錄會變成 a b。執行 git revert b 後,當前的分支記錄會變成 a b c b'

如果你想讓別人知道你撤銷過記錄,就使用 git revert,因為它會留下撤銷的記錄,否則用 git reset

參考資料

相關文章