git 那些事兒 —— 基於 Learn Git Branching

前言

推薦一個 git 圖形化教學網站:Learn Git Branching,這個網站有一個沙盒可以直接在上面模擬 git 的各種操作,操作效果使用圖形的方式展示,非常直觀。本文可以看作是它的文字版,將其中各級關卡所要學習的概念和命令提取出來,方便查閱。文中的一些示例,如果沒有顯而易見的輸出,就需要讀者在沙盒中親自輸入來查看效果。

git 日常操作

git 命令雖多,但也遵循 80/20 法則,經常用到的也就下面幾個:

git clone project-name git checkout branch-name git branch git pull origin master git add file-name git rm file-name git mv file-name git status git log git commit -m comment-string git push origin master git merge branch-name

會了這些命令,一些日常的操作就基本沒問題了,那 git 還有什麼可學的呢?有的,提交錯了需要回滾怎麼操作?多個 commit 如何合併成一條提交?如何定位分支上的一個提交(以便對比、回退、拉取新分支)?在一個分支上處理多個任務如何切換?如何調整一條分支上多個 commit 的順序?如何將一台機器上的修改打成修補程式在另一台機器上提交?本地刪除一個分支如何同步到遠程?……下面隨著教程我們來一探究竟。

本地分支操作

分支是 git 最重要的概念之一,用好分支是用好 git 的基礎。git 的分支非常輕量級,不會造成存儲或記憶體上的開銷,所以應該 「早建分支、多用分支」!只要記住使用分支其實就相當於在說:「我想基於這個提交以及它所有的父提交進行新的工作」 就好啦。

新建並切換分支

git branch bugFix git checkout bugFix <==> git checkout -b bugFix

以上兩個等價,都是在當前提交上創建分支並切換到新分支上準備工作。

合併分支 – merge

git checkout -b bugFix git commit -m 'a' git checkout master git commit -m 'b' git merge bugFix

在 git 中合併兩個分支時會產生一個特殊的提交記錄,它有兩個父節點。翻譯成自然語言相當於:「我要把這兩個父節點本身及它們所有的祖先都包含進來。」

git checkout bugFix git merge master

因為 master 繼承自 bugFix,Git 什麼都不用做,只是簡單地把 bugFix 移動到 master 所指向的那個提交記錄。

合併分支 – rebase

git checkout -b bugFix git commit -m 'a' git checkout master git commit -m 'b' git checkout bugFix git rebase master <===> git rebase master bugFix

rebase 實際上就是取出一系列的提交記錄,「複製」它們,然後在另外一個地方逐個的放下去,相比 merge 的優勢是可以創造更線性的提交歷史,程式碼庫的提交歷史將會變得異常清晰。rebase 第二個參數是要移動的源分支,如果不提供默認為 HEAD。如果當前分支不在源分支上,使用第二個參數可以節省一次 checkout 動作。

git checkout master git rebase bugFix <===> git rebase bugFix master

同理,由於 bugFix 繼承自 master,所以 git 只是簡單的把 master 分支的引用向前移動了一下而已

在提交樹上移動

HEAD 是一個對當前檢出記錄的符號引用 —— 也就是指向你正在其基礎上進行工作的提交記錄,大多數修改提交樹的 git 命令都是從改變 HEAD 的指向開始的。

分離的 HEAD

HEAD 通常情況下是指向分支名的 (如 bugFix),分離的 HEAD 就是讓其指向了某個具體的提交記錄而不是分支名。

git commit -m 'c' git checkout 'c'

相對引用

git 中 commit 以 hash 值作為名字,基於 sha-1 的哈希值長達 40 位,雖然 git 對哈希的處理很智慧 —— 你只需要提供能夠唯一標識提交記錄的前幾個字元即可,但是使用哈希值仍然很不直觀。

通用

git 中的相對引用可以從 HEAD 或分支名等易於記憶的地方開始計算,主要有兩種形式:

  • ^ – 指定位置的上一個提交
  • ~N – 指定位置的前 N 個提交,未提供 N 時等價於 ^

示例:

git checkout master^ git checkout bugFix^^ git checkout HEAD^ git checkout HEAD^ git checkout HEAD^ <===> git checkout HEAD~3

選擇父提交

操作符 ^ 與 ~ 符一樣,後面也可以跟一個數字。但不是用來指定向上返回幾代,而是指定合併提交記錄的某個父提交。一個合併提交有兩個父提交,所以遇到這樣的節點時該選擇哪條路徑就不是很清晰了。git 默認選擇合併提交的「第一個」父提交,在操作符 ^ 後跟一個數字可以改變這一默認行為。

git checkout -b bugFix git commit -m 'a' git checkout master git commit -m 'b' git merge bugFix git checkout HEAD^ # master 'b' git checkout HEAD^2 # bugFix 'a'

鏈式操作

^ 與 ~ 可以結合使用,實現快速移動

git checkout -b bugFix git commit -m 'a' git commit -m 'b' git commit -m 'c' git checkout master git commit -m 'd' git commit -m 'e' git merge bugFix git commit -m 'f' git checkout HEAD~ # merged commit git checkout HEAD^2 # bugFix 'c' git checkout HEAD~2 # bugFix 'a' <===> git checkout HEAD~^2~2 # bugFix 'a'

示例中將三條移動指令合併成了一條,使用鏈式操作可以大大減少移動的命令次數。

強制移動分支

git branch -f master HEAD~3 git branch -f bugFix bugFix~3

結合上一節的內容,可以將分支強制移動到提交樹上的任意位置,一般只在處理特殊情況時用到,平時慎用。

撤銷變更 – reset

git reset 通過把分支記錄回退幾個提交記錄來實現撤銷改動。你可以將這想像成「改寫歷史」。git reset 向上移動分支,原來指向的提交記錄就跟從來沒有提交過一樣。

git reset HEAD^

撤銷最近一次提交。在 reset 後,最近一次提交所做的變更還在,但是處於未加入暫存區(Unstage)狀態。

撤銷變更 – revert

雖然在本地分支中使用 git reset 很方便,但是這種「改寫歷史」的方法對大家一起使用的遠程分支是無效的!為了撤銷更改並分享給別人,我們需要使用 git revert。

git revert HEAD

同樣是撤銷最近一次提交,在 revert 後多了一個新提交,這個提交剛好是用來撤銷 HEAD 這個提交的。revert 之後就可以把你的更改推送到遠程倉庫與別人分享了。

整理提交記錄

開發人員有時會說「我想要把這個提交放到這裡, 那個提交放到剛才那個提交的後面」, 而接下來就講的就是它的實現方式

cherry-pick

如果你想將一些提交複製到當前所在的位置 (HEAD) 下面的話, cherry-pick 是最直接的方式了。

git checkout -b side git commit -m 'a' git commit -m 'b' git commit -m 'c' git checkout master git cherry-pick 'a' 'c'

相對 rebase 的最大區別是,用戶可以指定要複製的 commit 而不是一股腦都合併進來。

互動式 rebase

cherry-pick 簡單而有效,但前提是你知道想要 commit 的明確的哈希值,想從一系列的提交記錄中找到想要的記錄,使用互動式 rebase 就是最好的方法了。

git checkout -b side git commit -m 'a' git commit -m 'b' git commit -m 'c' git rebase -i HEAD~3

互動式 rebase 指的是使用帶參數 –interactive 的 rebase 命令,簡寫為 -i。如果你在命令後增加了這個選項,git 會打開一個 UI 介面並列出將要被複制到目標分支的備選提交記錄,它還會顯示每個提交記錄的哈希值和提交說明,後者有助於你理解這個提交進行了哪些更改。

pick 85c5736 udpate score pick 2b4a381 udpate score pick d40db94 udpate score pick e957b1b udpate score pick 89c5b59 udpate score # Rebase 711f9d2..89c5b59 onto e957b1b (5 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 # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop = remove commit # l, label

上面是執行 git rebase -i HEAD~5 的編譯器介面 (這裡使用了 vim 作為默認的 git 編輯器)。
可以看到用戶在這個介面通過調整 action 關鍵字來選擇 commit 並調整它們的順序,甚至可以對 commit 進行合併處理,合併方式也是比較豐富的,可以保留每條 commit 的提交資訊,也可以忽略它們。

在 Learn Git Branching 中當 rebase UI 介面打開時,它可以完成三種功能:

  • 調整提交記錄的順序 (通過滑鼠拖放來完成)
  • 刪除你不想要的提交 (通過切換 pick/ommit 的狀態來完成,點擊 ommit 就意味著你不想要這個提交記錄)
  • 合併提交 (它允許你把多個提交記錄合併成一個)

實際上只有前兩種是支援的。

錨定 commit

分支很容易被人為移動,並且當有新的提交時,它也會移動,分支很容易被改變。當軟體發布新的大版本或者是修正一些重要的 bug 或是增加了某些新特性,就需要永遠指向某個提交記錄的標識。

git tag

git tag 可以永久地將某個特定的提交命名為里程碑,然後就可以像分支一樣引用了。更難得的是,它們並不會隨著新的提交而移動。你也不能檢出到某個標籤上面進行修改提交,它就像是提交樹上的一個錨點,標識了某個特定的位置。

git commit -m 'a' git commit -m 'b' git commit -m 'c' git tag v1 'a' git tag v2 HEAD^ git tag v3

當不提供 commit 參數時,默認使用 HEAD 代替。

git describe

用於查找給定 commit 最近的 tag 資訊。

git commit -m 'a' git tag v1 git commit -m 'b' git commit -m 'c' git tag v2 git describe # v2 git describe HEAD^ # v1_1_g'b'

命令語法為:git describe <ref>
輸出格式為:<tag>_<numCommits>_g<hash>

tag 表示的是離 ref 最近的標籤, numCommits 是表示這個 ref 與 tag 相差有多少個提交記錄, hash 表示的是你所給定的 ref 所表示的提交記錄哈希值的前幾位。然後就可以用這個資訊去引用相關的 commit 了,簡直是 CUI 的福音有木有~

上面演示的是 Learn Git Branching 的效果,在 git 命令中,貌似需要加入 –tags 才會生效:

$ git describe --tags v2 $ git describe HEAD^ --tags v1-1-ge957b1b

而且分隔符也變成了中劃線。

遠程分支操作

遠程倉庫

遠程倉庫實際上只是你的倉庫在另個一台電腦上的拷貝。你可以通過網際網路與這台電腦通訊 —— 也就是增加或是獲取提交記錄。現在用網站來對遠程倉庫進行可視化操作變得越發流行了(像 Github 或 Phabricator),但是理解其中的基本概念非常重要。

git clone remote-repository-git

git clone 會在本地創建一個遠程倉庫的拷貝,同時創建對應的遠程分支來記錄與遠程倉庫的同步狀態,遠程分支見下一節內容。

遠程分支

遠程分支反映了遠程倉庫 (在你上次和它通訊時) 的狀態,遠程分支命名規範:<remote repository name>/<branch name>,repository 一般為 origin,這是因為當你用 git clone 某個倉庫時,git 已經幫你把遠程倉庫的名稱設置為 origin 了。遠程分支有一個特別的屬性,在你檢出時自動進入分離 HEAD 狀態。Git 這麼做是出於不能直接在這些分支上進行操作的原因,你必須在別的地方完成你的工作,(更新了遠程分支之後) 再用遠程分享你的工作成果。

git checkout origin/master git commit -m 'a'

此時 git 變成了分離 HEAD 狀態,當添加新的提交時 origin/master 也不會更新。這是因為 origin/master 只有在遠程倉庫中相應的分支更新了以後才會更新。

$ git checkout origin/master Note: switching to 'origin/master'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c Or undo this operation with: git switch - Turn off this advice by setting config variable advice.detachedHead to false HEAD is now at 89c5b59 udpate score

此時你可以提交 commit,但是它們在下次與遠程分支同步後將自動消失。

從遠程倉庫獲取數據

當從遠程倉庫獲取數據時, 遠程分支也會更新以反映最新的遠程倉庫

git fetch

git fetch 完成了僅有的但是很重要的兩步:

  • 從遠程倉庫下載本地倉庫中缺失的提交記錄
  • 更新遠程分支指針 (如 origin/master)

git fetch 實際上將本地倉庫中的遠程分支更新成了遠程倉庫相應分支最新的狀態。
git fetch 並不會改變你本地倉庫的狀態。它不會更新你的 master 分支,也不會修改你磁碟上的文件。

將變化更新到當前工作中

git fetch git merge/rebase/cherry-pick origin/master <===> git pull

當遠程分支中有新的提交時,你可以像合併本地分支那樣來合併遠程分支,由於先抓取更新再合併到本地分支這個流程很常用,因此 git 提供了一個專門的命令來完成這兩個操作,它就是 git pull。

git fetch git merge <===> git pull git fetch git rebase <===> git pull --rebase git push

pull 默認也可以使用 rebase 進行合併,這是通過提供 --rebase 參數實現的。以下是關於 rebase 的優缺點:

  • 優點: Rebase 使你的提交樹變得很乾凈, 所有的提交都在一條線上
  • 缺點: Rebase 修改了提交樹的歷史

比如, 提交 C1 可以被 rebase 到 C3 之後。這看起來 C1 中的工作是在 C3 之後進行的,但實際上是在 C3 之前。一些人喜歡保留提交歷史,就可以選擇 merge;其他人喜歡乾淨的提交樹,於是選擇 rebase。

向遠程倉庫傳輸數據

git push 負責將你的變更上傳到指定的遠程倉庫,並在遠程倉庫上合併你的新提交記錄。一旦 git push 完成, 你的朋友們就可以從這個遠程倉庫下載你分享的成果了。一個名為 push.default 的配置會影響不帶參數的 git push 的行為,它的默認值取決於 git 的版本,這裡假定是 upstream。

git push

git push 成功後遠程分支 (origin/master) 也同樣被更新了,所有的分支都同步了

偏離的歷史

上面的例子都比較簡單,與遠程倉庫同步的真正困難來自於遠程提交歷史的偏離。在遠程倉庫被修改且與你的本地工作有衝突的情況下, git push 就不知道該如何操作了。這種情況 (歷史偏離) 有許多的不確定性,Git 是不會允許你 push 變更的。實際上它會強制你先合併遠程最新的程式碼,然後才能分享你的工作。

遠程跟蹤

master 被設定為跟蹤 origin/master —— 這意味著為 master 分支指定了推送的目的地以及拉取後合併的目標。當你克隆時,git 會為遠程倉庫中的每個分支在本地倉庫中創建一個遠程分支 (比如 origin/master)。然後再創建一個跟蹤遠程倉庫中活動分支的本地分支,默認情況下這個本地分支會被命名為 master。克隆完成後,你會得到一個本地分支,但是可以查看遠程倉庫中所有的分支。這樣做對於本地倉庫和遠程倉庫來說,都是最佳選擇。在克隆的時候會看到下面的輸出:

local branch "master" set to track remote branch "origin/master"

你可以讓任意分支跟蹤 origin/master,然後該分支會像 master 分支一樣得到隱含的 push 目的地以及 merge 的目標。 這意味著你可以在分支 dev 上執行 git push,將工作推送到遠程倉庫的 master 分支上:

git checkout -b dev origin/master git pull git commit -m 'a' git push

注意,此時 master 分支不會有任何改變。

git branch -u origin/master dev

這種方法噹噹前分支為 dev 時,最後一個參數可省略。總體上不推薦上面的做法,容易造成混亂。
使用 push 參數可以忽略 HEAD 的設置:

git commit -m 'a' git checkout master~2 # git push <remote> <place> git push origin master

切到本地倉庫中的「master」分支,獲取所有的提交,再到遠程倉庫「origin」中找到「master」分支,將遠程倉庫中沒有的提交記錄都添加上去,搞定之後告訴我。我們通過「place」參數來告訴 git 提交記錄來自於 master,要推送到遠程倉庫中的 master。它實際就是要同步的兩個倉庫的位置。需要注意的是,因為我們通過指定參數告訴了 git 所有它需要的資訊, 所以它就忽略了我們所檢出的分支的屬性。即使當前處理偏離歷史狀態,也可以推送成功。如果源分支與推送目的分支不同名,可以通過指定 push 的第二個參數來實現:

# git push origin <source>:<destination> git push origin master^:foo

將 dev 分支中的修改推送到遠程服務的 master 分支,忽略當前 HEAD 設置。當目的分支不存在時,git 會自動創建。

這裡可以舉一個實際的例子,大一點的公司都有自建的程式碼庫系統,可以設置程式碼庫是否允許開發者直接提交程式碼,一般設置為否,需要提交到一個特殊的分支,經過同行評審後再合入到正式分支:

git push origin HEAD:refs/for/master

之前看不明白為什麼這樣寫,現在結合上面的語法就明白了,是將當前修改提交到 refs/for/master 這個特殊分支,程式碼庫系統再利用這個分支做評審控制,perfect~

git fetch 也有類似的參數:

# git fetch <remote> <place> git fetch origin foo

只更新遠程服務的 foo 分支到本地 origin/foo 上。也可以使用分號形式的第二個參數:

# git fetch <remote> <source>:<destination> git fetch origin master:foo

將遠程服務的 master 分支直接更新到本地 foo 分支,注意不是 origin/foo 遠程分支!它將直接修改你的本地分支,所以使用時需要小心。這裡可以這樣做有一個前提,就是當前檢出分支不可以是 foo。另外如果 foo 不存在,git 將自動創建這個本地分支。這裡有一點是需要注意的 —— source 現在指的是遠程倉庫中的位置,而 destination 才是要放置提交的本地倉庫的位置。

總體上不推薦上面的做法,容易造成混亂,在這裡介紹它主要是為了從概念上說明 fetch 和 push 的相似性。
不帶 <source> 的 <place> 作用於 push 時會刪除遠程服務中的分支。

git push origin :foo

不帶 <source< 的 <place< 作用於 fetch 時會在本地創建一個新分支

git fetch origin :bar

相當於 git branch 命令。
同理,由於 git pull = git fetch + git merge,pull 也可以攜帶額外的兩個參數:

git pull origin foo <===> git fetch origin foo git merge origin/foo git pull origin bar~1:bugFix <===> git fetch origin bar~1:bugFix git merge bugFix

兩個參數原封不動傳遞給了 fetch,<destination> 還會影響 merge,就是這樣簡單。

小技巧

這部分內容是平時的一些積累,與教程互為補充,偏重實用一些。

稀疏檢出

目錄太大一次檢出可能由於網路不穩定而經常失敗時,可以嘗試稀疏檢出

git init git config core.sparsecheckout true # git config -l # do NOT checkout third-party echo "/*" >> .git/info/sparse-checkout echo "!third-party" >> .git/info/sparse-checkout git remote add origin [email protected]:goodpaperman/apue.git git pull origin master

上面將 third-party 中的內容屏蔽,之後稍作設置便可以繼續檢出:

git config core.sparsecheckout false # clear third-party item vi .git/info/sparse-checkout git read-tree -mu HEAD

查看遠程服務地址

本地太多庫了,搞不清哪個是哪個,可以用這個命令查看:

git remote -v # origin [email protected]:goodpaperman/apue.git (fetch) # origin [email protected]:goodpaperman/apue.git (push)

從而確定與本地 git 庫關聯的遠程服務位置

查看本地修改

git status git status -uno # 不顯示非版本控制文件 # On branch master # Your branch is up to date with 'origin/master'. # # Changes not staged for commit: # (use "git add ..." to update what will be committed) # (use "git restore ..." to discard changes in working directory) # modified: score.txt # modified: predicate_merged.data git status -suno # 顯示簡略資訊 (可用於後續 shell 處理) # M score.txt # M predicate_merged.data git ls-files # 查看處於版本控制的文件列表 (默認遞歸) git diff path # 查看文件詳情對比,針對目錄操作時顯示該目錄下所有文件的差異

添加/刪除本地修改

git add path # 將文件添加到版本控制 git add -f path # 當文件命中 .gitignore 規則時,需要通過 -f 來強制添加 git add -u # 僅添加在版本控制下且有變更的文件 git add -u path # 僅添加指定路徑中在版本控制下且有變更的文件 git add -u :/ # 從根目錄開始添加 (如果當前路徑不在根目錄,而有一些變更位於當前目錄外時使用) git rm --cached path # 將新添加的文件從版本控制中移除,僅適用於已 add 未 commit 的場景,移除後新文件將在本地保留, # 仍可 add。如果針對已處理版本控制的文件,則只刪除版本控制中的文件,不刪除本地副本 git checkout path # 拋棄本地修改,恢復到暫存區的狀態,僅適用於未 add 的場景 # (add 後又修改的情況下會恢復到最後一次 add 時的狀態)

提交更改

git commit # 彈出編輯框輸入 comment git commit -m "xxx" # 直接提供 comment 不再彈編輯框 git commit --amend # 將本次提交合併到上一次提交中去 (有些 git 庫要求一次 push # 只能包含一個提交或只想變更 commit-message 時很有用)

撤銷變更

git reset HEAD # 撤銷 add 但未 commit 的修改,修改將在本地保留,仍可 add git reset --soft origin/master # 將所有本地已 commit 但未 push 的提交揉合成一條提交 (需要再加一句 git add -u) git reset --hard origin/master # 丟棄所有本地已 commit 但未 push 的提交 (謹慎使用!) git revert HEAD # 撤銷已 push 的提交,生成一個相反的 commit, # 提交後 push 可讓遠程服務上的分支恢復 push 前的狀態 git revert -n HEAD~2..HEAD # 撤銷多次提交,-n 參數防止每個提交生成一個對應的 revert commit git revert -m 1 HEAD # 選擇回滾合併節點的哪一個父提交 (一個合併節點包含兩個分支的內容) git revert --continue # 處理衝突後繼續撤銷 git revert --abort # 結束撤銷任務,恢復到撤銷前的狀態

查看日誌

git log # 顯示提交資訊詳情 git log --oneline # 顯示簡略的提交資訊 (一行一個提交) git log --oneline --graph # 顯示分支樹 (一般與 --oneline 同時使用) git show <ref> # 查看某次提交的詳細 diff 輸出 git show <ref> --stat # 查看某次提交的文件變更列表

分支

git branch # 查看當前分支 git branch --show-current # 查看當前分支 (僅顯示分支名,可用於命令組合) git branch -a # 查看全部分支 (包括遠程分支) git branch foo HEAD^ # 從特定提交創建分支 git branch -d foo # 刪除本地分支

問題追蹤

git blame file # 查看文件修改歷史 git blame <ref> file # 查看某個提交前 (包含這個提交) 的文件修改歷史

合併分支

git merge foo # 以當前分支為基礎合併分支 foo git mergetool # 調用合併工具處理衝突文件 (默認為 vimdiff,可配置,處理後的文件會自動 commit) git merge --abort # 結束 merge 過程,恢復到 merge 開始前的狀態

臨時保存修改

git stash # 將當前未提交修改放入存儲區備用 git stash list # 查看存儲區列表 git stash pop # 恢復上次 stash 的內容,成功恢復後內容將被刪除 git stash save bugFix # 同 git statsh,增加備註資訊 git stash apply bugFix # 恢復某次 stash 的內容,成功恢復後內容不會刪除 git stash show bugFix # 顯示存儲詳情 git stash branch bugFix # 從存儲創建分支 (以便下一步處理) git stash drop bugFix # 顯式刪除 git stash clear # 刪除所有 (謹慎使用!)

修補程式的生成與應用

git diff > feature.patch # 將當前未提交修改放入存儲區備用 git apply --stat feature.patch # 查看修補程式內容 git apply --check feature.patch # 檢查修補程式是否能打上 git apply feature.patch # 應用修補程式 git format-patch <r1>..<r2> # 生成 r1 至 r2 的 patch git format-patch -1 <r1> # 生成單個提交的 patch git format-patch <r1> # 生成自某個提交以來的 patch (不含 r1) git format-patch --root <r1> # 生成從根自某個提交的 patch git format-patch HEAD^ # 生成最近一次提交的 patch git am xxx.patch # 打修補程式 git am --signoff xxx.patch # 將自己添加到 signed off by 資訊中, # 修補程式作者與打修補程式的人可能不是同一個 git am ~/patch-set/*.patch # 目錄中的修補程式按照先後順序打上 git am --abort # 廢棄打的修補程式 git am --resolved # 解決完衝突後,接著打修補程式

git 設置

多帳號設置

添加文件 ~/.ssh/config:

# gitee Host gitee.com HostName gitee.com PreferredAuthentications publickey IdentityFile ~/.ssh/gitee_id_rsa # github default Host github.com HostName github.com PreferredAuthentications publickey IdentityFile ~/.ssh/github_id_rsa # github haihai107 Host github.com HostName github.com User [email protected] PreferredAuthentications publickey IdentityFile ~/.ssh/hai_github_id_rsa

再分別生成 gitee_id_rsa[.pub] 與 [hai_]github_id_rsa[.pub] 文件即可。
需要注意的是當一個遠程服務下有多個用戶帳戶時,不可指定全局的 user.name 和 user.email 設置:

git config --global user.name yunhai git config --global user.email [email protected]

需要取消這些設置,代之以分別設置當前 git 庫 (–local)

git config --global --unset user.name git config --global --unset user.email # cd to git repository directory git config --local user.name yunhai git config --local user.email [email protected]

對比工具

通過 ssh 遠程訪問時,一般沒有圖形介面,此時推薦 vimdiff 作為默認的對比工具。

git config --global diff.tool vimdiff # 設置 git 默認編輯器 git config --global difftool.prompt false # 啟動外部工具前不再單獨提示用戶 git config --global alias.d difftool # 使用 git d 調出新對比工具

本機訪問時,可以設置更高級的圖形介面工具,例如 beyond compare/meld/kdiff3/winmege/p4merge……設置方法大同小異

git config --global diff.tool bc4 # 設置 git 默認編輯器 git config --global difftool.prompt false # 啟動外部工具前不再單獨提示用戶 git config --global difftool.bc4.cmd '"/usr/local/bin/bcomp" "$LOCAL" "$REMOTE"' # 使用 git diff 調用新對比工具時向 beyondcompare 傳遞參數的順序

合併工具

CUI 環境下可以直接通過 git mergetool –tool=vimdiff 調起 vimdiff 作為合併工具,GUI 環境下需要額外配置。

git config --global merge.tool bc4 # 設置 beyondcompare 為默認合併工具 git config --global mergetool.prompt false # 啟動外部工具前不再單獨提示用戶 git config --global mergetool.bc4.cmd '"/usr/local/bin/bcomp" "$LOCAL" "$REMOTE" "$BASE" "$MERGED"' # 使用 git diff 調用新對比工具時向 beyondcompare 傳遞參數的順序 git config --global mergetool.bc4.trustexitcode true # 信任工具的返回值而不是編輯完成後單獨詢問用戶是否已解決衝突 git config --global mergetool.keepBackup false # 當合併完成後刪除臨時文件 (*.orig)

其它配置

git config --list # 列出設置 git config user.name "yunhai" # 設置默認用戶名 git config user.email "[email protected]" # 設置默認用戶郵箱 git config --global core.editor "vim" # 設置 git 默認編輯器 git config --unset <section>.<key> # 取消設置

關於 Learn Git Branching

reset # 重新開始 revert / undo # 撤銷上次操作 hide goal # 隱藏目標 show goal # 顯示目標 hint # 提示 help # 關卡幫助 levels # 顯示全部關卡 show solution # 查看標準答案 git fakeTeamwork [branch] [count] # 模擬其它人在遠程倉庫上的提交 sandbox # 進入沙盒 顯示 demo://learngitbranching.js.org/?demo 直接進入沙盒://learngitbranching.js.org/?NODEMO

參考

[1]. Git 、CVS、SVN比較
[2]. Git 教程
[3]. 版本控制工具(CVS、SVN、GIT)簡介
[4]. 版本控制工具的比較
[5]. git問題記錄–如何從從detached HEAD狀態解救出來
[6]. Git 的 revert 命令撤銷多次提交
[7]. git撤銷merge,徹底學會git revert的用法
[8]. Git 如何優雅地回退程式碼,用 reset 還是 revert?
[9]. 【Git】rebase 用法小結
[10]. git生成patch和打patch
[11]. Git 多用戶配置
[12]. git常用配置
[13]. 配置Beyond Compare 4作為git mergetool來解決git merge命令導致的文件衝突
[14]. git設置對比工具
[15]. git diff 工具設置_Git外部的合併與比較工具
[16]. git 使用vimdiff 格式顯示比對文件和常用操作整理[整]
[17]. mac上有什麼查看git diff的工具?
[18]. 使用 P4Merge 作為 GIT 的可視化合併工具
[19]. git遠程庫程式碼版本回滾方法
[20]. Git過濾上傳文件的方法
[21]. git 在提交之前撤銷add操作
[22]. git status總是提示Changed but not updated
[23]. git – 簡明指南
[24]. git的submodule功能詳解
[25]. git cherry-pick合併某個commit
[26]. Git 應用修補程式報錯 「sha1 information is lacking or useless」
[27]. git clone 出錯
[28]. git創建遠程分支
[29]. git操作之pull拉取遠程指定分支以及push推送到遠程指定分支
[30]. git checkout 命令詳解
[31]. 如何把GIT倉庫的子目錄獨立成新倉庫
[32]. git clone一個github上的倉庫,太慢,經常連接失敗,但是github官網流暢訪問,為什麼?
[33]. Git下的衝突解決
[34]. GitHub 在使用命令行 git push 時報錯:The requested URL returned error: 403
[35]. Github倉庫master分支到main分支遷移指南
[36]. git拉取遠程分支並切換到該分支
[37]. 解決git push程式碼到github上一直提示輸入用戶名及密碼的問題
[38]. 斷點續傳式git clone(偽)
[39]. git怎麼查看哪些文件是在版本控制下的呢
[40]. git修改之前commit的提交資訊
[41]. ‘git status’不顯示untracked files
[42]. Git如何查看遠程倉庫地址
[43]. git的分支與合併的兩種方法
[44]. Git:合併分支—-git merge命令應用的三種情景
[45]. git blame命令詳解
[46]. git 撤銷合併 重新合併_Git合併和Git調整的終極指南
[47]. git-stash用法小結
[48]. git如何取消merge
[49]. 使用vimdiff作為git mergetool
[50]. How to View or Change Git Username and Email Address
[51]. git commit之後,想撤銷commit
[52]. git 尋找程式碼改動的「始作俑者」
[53]. git cherry-pick 教程
[54]. git檢出某文件的指定版本
[55]. Git如何檢出指定目錄或文件
[56]. git 切換遠程倉庫地址
[57]. 如何在Git中查看單個提交的更改文件列表?