Git管理分支

琴韻畔晨曦發表於2015-04-24

管理分支:git branch

直至現在為止,我們的專案版本庫一直都是隻有一個分支 master。在 git 版本庫中建立分支的成本幾乎為零,所以,不必吝嗇多建立幾個分支。下面列舉一些常見的分支策略,僅供大家參考:

* 建立一個屬於自己的個人工作分支,以避免對主分支 master 造成太多的干擾,也方便與他人交流協作。

* 當進行高風險的工作時,建立一個試驗性的分支,扔掉一個爛攤子總比收拾一個爛攤子好得多。

* 合併別人的工作的時候,最好是建立一個臨時的分支,關於如何用臨時分支合併別人的工作的技巧,將會在後面講述。

建立分支

下面的命令將建立我自己的工作分支,名叫 robin,並且將以後的工作轉移到這個分支上開展。

$ git branch robin$ git checkout robin

更簡單和常用的方法是直接通過 checkout 命令來一次性建立並轉移到新建分支上,命令如下:

$ git checkout -b robin [start_point]

其中 start_point 是一個可選引數,指定新建分支 robin 是基於哪個節點,預設為當前所在分支的節點。

刪除分支

要刪除版本庫中的某個分支,使用 git branch -d 命令就可以了,例如:

$ git branch -d branch-name

但是需要注意的是,如果刪除的分支還沒有被 merge 到其他分支,刪除這樣的分支會導致這個分支上所做的改動丟失,因此 git branch -d 命令會失敗,提示你這樣做會丟失資訊。如果你的確想刪除這樣的分支,不怕資訊丟失,那麼可以使用 git branch -D 命令,這個命令不會去判斷分支的merge狀態,例如:

$ git branch -D branch-name

通常建議使用 -d 引數來刪除分支,以防無意的資訊丟失。

檢視分支執行下面的命令可以得到你當前工作目錄的分支列表:$ git branch

在你正在工作的分支的名字前面,會有 * 號標示,比如:

$ git branchbugfix* master

說明有兩個本地分支 bugfix 和 master, 其中當前的工作分支為 master。

檢視專案的發展變化和比較差異

這一節介紹幾個檢視專案的版本庫的發展變化以及比較差異的很有用的命令:

git show-branchgit diffgit whatchanged

我們現在為 robin, master 兩個分支都增加一些內容。

$ git checkout robin$ echo "Work, work, workd" >> hello$ git commit -m "Some workd" -i hello$ git checkout master$ echo "Play, play, play" >> hello$ echo "Lots of fun" >> example$ git commit -m "Some fun" -i hello example

git show-branch 命令可以使我們看到版本庫中每個分支的世系發展狀態,並且可以看到每次提交的內容是否已進入每個分支。

$ git show-branch

這個命令讓我們看到版本庫的發展記錄。

* [master] Some fun! [robin] some work--* [master] Some fun+ [robin] some work*+ [master^] a new day for git

譬如我們要檢視世系標號為 master^ 和 robin 的版本的差異情況,我們可以使用這樣的命令:

$ git diff master^ robin

我們可以看到這兩個版本的差異:

diff --git a/hello b/helloindex 263414f..cc44c73 100644--- a/hello+++ b/hello@@ -1,2 +1,3 @@Hello WorldIt's a new day for git+Work, work, workNote

關於 GIT 版本世系編號的定義,請參看 git help rev-parse 。

我們現在再用 git whatchanged 命令來看看 master 分支是怎麼發展的。

$ git checkout master$ git whatchangeddiff-tree 1d2fa05... (from 3ecebc0...)Author: Vortune.RobinDate: Tue Mar 21 02:24:31 2006 +0800Some fun:100644 100644 f24c74a... 7f8b141... M example:100644 100644 263414f... 06fa6a2... M hellodiff-tree 3ecebc0... (from 895f09a...)Author: Vortune.RobinDate: Tue Mar 21 02:17:23 2006 +0800a new day for git:100644 100644 557db03... 263414f... M hello

從上面的內容中我們可以看到,在 robin 分支中的日誌為 "Some work" 的內容, 並沒有在 master 分支中出現。

合併分支

合併兩個分支:git merge

既然我們為專案建立了不同的分支,那麼我們就要經常地將自己或者是別人在一個分支上的工作合併到其他的分支上去。現在我們看看怎麼將 robin 分支上的工作合併到 master 分支中。現在轉移我們當前的工作分支到 master,並且將 robin 分支上的工作合併進來。

$ git checkout master$ git merge -m "Merge from robin" robin

上面的命令會將 robin 分支的改動 merge 到 master,並生成一個新的 commit 節點,這個 commit 的註釋資訊為 "Merge from robin"

(kwydwuf注: $ git merge "Merge work in robin" HEAD robin 是老版本的用法,應該廢棄 )

合併兩個分支,還有一個更簡便的方式,下面的命令和上面的命令是等價的 (kwydwuf注:git pull 的本意是用來 merge 遠端版本庫中的某個分支,用在此處沒有任何簡便之處,可以廢棄)。

$ git checkout master$ git pull . robin

但是,此時 git 會出現合併衝突提示:

Trying really trivial in-index merge...fatal: Merge requires file-level mergingNope.Merging HEAD with d2659fcf690ec693c04c82b03202fc5530d50960Merging:1d2fa05b13b63e39f621d8ee911817df0662d9b7 Some fund2659fcf690ec693c04c82b03202fc5530d50960 some workfound 1 common ancestor(s):3ecebc0cb4894a33208dfa7c7c6fc8b5f9da0eda a new day for gitAuto-merging helloCONFLICT (content): Merge conflict in helloAutomatic merge failed; fix up by hand

git 的提示指出,在合併作用於檔案 hello 的 'Some fun' 和 'some work' 這兩個物件時有衝突,具體通俗點說,就是在 master, robin 這兩個分支中的 hello 檔案的某些相同的行中的內容不一樣。我們需要手動解決這些衝突,現在先讓我們看看現在的 hello 檔案中的內容。

$ cat hello

此時的 hello 檔案應是這樣的,用過其他的版本控制系統的朋友應該很容易看出這個典型的衝突表示格式:

Hello WorldIt's a new day for git<<<<<<< HEAD/helloPlay, play, play=======Work, work, work>>>>>>> d2659fcf690ec693c04c82b03202fc5530d50960/hello

我們用編輯器

將 hello 檔案改為:

Hello WorldIt's a new day for gitPlay, play, playWork, work, work

現在可以將手動解決了衝突的檔案提交了。

$ git commit -i hello

以上是典型的兩路合併(2-way merge)演算法,絕大多數情況下已經夠用。但是還有更復雜的三路合併和多內容樹合併的情況。詳情可參看: git help read-tree, git help merge 等文件。

逆轉恢復

逆轉與恢復:git reset

專案跟蹤工具的一個重要任務之一,就是使我們能夠隨時逆轉(Undo)和恢復(Redo)某一階段的工作。

git reset 命令就是為這樣的任務準備的。它將當前的工作分支的 頭 定位到以前提交的任何版本中,它有三個重置的演算法選項。

命令形式:

git reset [--mixed | --soft | --hard] []

命令的選項:

--mixed

僅是重置索引的位置,而不改變你的工作樹中的任何東西(即,檔案中的所有變化都會被保留,也不標記他們為待提交狀態),並且提示什麼內容還沒有被更新了。這個是預設的選項。

--soft

既不觸動索引的位置,也不改變工作樹中的任何內容,我們只是要求這些內容成為一份好的內容(之後才成為真正的提交內容)。這個選項使你可以將已經提交的東西重新逆轉至“已更新但未提交(Updated but not Check in)”的狀態。就像已經執行過 git update-index 命令,但是還沒有執行 git commit 命令一樣。

--hard

將工作樹中的內容和頭索引都切換至指定的版本位置中,也就是說自 之後的所有的跟蹤內容和工作樹中的內容都會全部丟失。因此,這個選項要慎用,除非你已經非常確定你的確不想再看到那些東西了。

一個重要技巧--逆轉提交與恢復

使用技巧

可能有人會問,--soft 選項既不重置頭索引的位置,也不改變工作樹中的內容,那麼它有什麼用呢?現在我們介紹一個 --soft 選項的使用技巧。下面我們用例子來說明:

$ git checkout master

$ git checkout -b softreset

$ git show-branch

這裡我們建立了一個 master 的拷貝分支 softreset,現在我們可以看到兩個分支是在同一起跑線上的。

! [master] Merge branch 'robin'

! [robin] some work

* [softreset] Merge branch 'robin'

---

- - [master] Merge branch 'robin'

+ * [master^] Some fun

++* [robin] some work

我們為 檔案增加一些內容並提交。

$ echo "Botch, botch, botch" >> hello

$ git commit -a -m "some botch"

$ git show-branch

我們可以看到此時 softreset 比 master 推進了一個版本 "some botch" 。

! [master] Merge branch 'robin'

! [robin] some work

* [softreset] some botch

---

* [softreset] some botch

- - [master] Merge branch 'robin'

+ * [master^] Some fun

++* [robin] some work

現在讓我們來考慮這樣的一種情況,假如我們現在對剛剛提交的內容不滿意,那麼我們再編輯專案的內容,再提交的話,那麼 "some botch" 的內容就會留在版本庫中了。我們當然不希望將有明顯問題的內容留在版本庫中,這個時候 --soft 選項就很有用了。為了深入瞭解 --soft 的機制,我們看看現在 softreset 分支的頭和 ORIG_HEAD 儲存的索引。

$ cat .git/refs/heads/softreset .git/ORIG_HEAD

結果如下:

5e7cf906233e052bdca8c598cad2cb5478f9540a

7bbd1370e2c667d955b6f6652bf8274efdc1fbd3

現在用 --soft 選項逆轉剛才提交的內容:

git reset --soft HEAD^

現在讓我們再看看 .git/ORIG_HEAD 的中儲存了什麼?

$ cat .git/ORIG_HEAD

結果如下:

5e7cf906233e052bdca8c598cad2cb5478f9540a

看!現在的 .git/ORIG_HEAD 等於逆轉前的 .git/refs/heads/softreset 。也就是說,git reset --soft HEAD^ 命令逆轉了剛才提交的版本進度,但是它將那次提交的物件的索引拷貝到了 .git/ORIG_HEAD 中。

我們再編輯 hello 檔案成為下面的內容:

Hello World

It's a new day for git

Play, play, play

Work, work, work

Nice, nice, nice

我們甚至可以比較一下現在的工作樹中的內容和被取消了的那次提交的內容有什麼差異:

$ git diff ORIG_HEAD

結果如下:

diff --git a/hello b/hello

index f978676..dd02c32 100644

--- a/hello

+++ b/hello

@@ -2,4 +2,4 @@ Hello World

It's a new day for git

Play, play, play

Work, work, work

-Botch, botch, botch

+Nice, nice, nice

接著,我們可以恢復剛才被取消了的那次提交了。

$ git commit -a -c ORIG_HEAD

注意,這個命令會開啟預設的文字編輯器

以編輯原來提交的版本日誌資訊,我們改為 "nice work" 。大家可以自行用 git show-branch 命令來檢視一下現在的分支狀態。並且我們還可以不斷地重複上述的步驟,一直修改到你對這個版本進度滿意為止。

git reset 命令還有很多的用途和技巧,請參考 git reset ,以及 Everyday GIT with 20 commands or So 。

提取資料

這是個很有用的小技巧,如果你對你現在的工作目錄下的東西已經不耐煩了,隨時可以取出你提交過的東西覆蓋掉當前的檔案,譬如:

$ git checkout -f foo.c

型別標籤

在 git 中,有兩種型別的標籤,“輕標籤”和“署名標籤”。

技術上說,一個“輕標籤”和一個分支沒有任何區別,只不過我們將它放在了 .git/refs/tags/ 目錄,而不是 heads 目錄。因此,打一個“輕標籤”再簡單不過了。

$ git tag my-first-tag

如果你打算針對某個commit ID來打標籤,雖然該命令可以通過gitk裡的右鍵選單來實現,但是該命令對實際應用是很有幫助的。

“署名標籤”是一個真正的 git 物件,它不但包含指向你想標記的狀態的指標,還有一個標記名和資訊,可選的 PGP 簽名。你可以通過 -a 或者是 -s 選項來建立“署名標籤”。

$ git tag -s

合併工作

通常的情況下,合併其他的人的工作的情況會比合並自己的分支的情況要多,這在 git 中是非常容易的事情,和你執行 git-merge 命令沒有什麼區別。事實上,遠端合併的無非就是“抓取(fetch)一個遠端的版本庫中的工作到一個臨時的標籤中”,然後再使用 git-merge 命令。

可以通過下面的命令來抓取遠端版本庫:

$ git fetch

根據不同的遠端版本庫所使用的通訊協議的路徑來替代上面的 remoted-repository 就可以了。

Rsync

rsync://remote.machine/patch/to/repo.git/

SSH

remote.machine:/path/to/repo.git

or

ssh://remote.machine/patch/to/repo.git/

這是可以上傳和下載的雙向傳輸協議

,當然,你要有通過 ssh 協議登入遠端機器的許可權。它可以找出兩端的機器提交過的物件集之中相互缺少了那些物件,從而得到需要傳輸的最小物件集。這是最高效地交換兩個版本庫之間的物件的方式(在 git 相容的所有傳輸協議當中)。

下面是個取得 SSH 遠端版本庫的命令例子:

$ git-fetch robin@192.168.1.168:/path/to/gittutorcn.git (1)

(1) 這裡 robin 是登入的使用者名稱,192.168.1.168 是儲存著主版本庫的機器的 IP 地址。

Local directory

/path/to/repo.git/

本地目錄的情況和 SSH 情況是一樣的。

git Native

git://remote.machine/path/to/repo.git/

git 自然協議是設計來用於匿名下載的,它的工作方式類似於 SSH 協議的交換方式。

HTTP(S)

http://remote.machine/path/to/repo.git/

到這裡可能有些朋友已經想到,實際上,我們可以通過 Rsync, SSH 之類的雙向傳輸方式來建立類似 CVS,SVN 這樣的中心版本庫模式的開發組織形式。

交換工作

讀過上一節之後,有的朋友可能要問,如果版本庫是通過單向的下載協議釋出的,如 HTTP,我們就無法將工作上傳到公共的版本庫中。別人也不能訪問我的機器來抓取我的工作,那怎麼辦呢?

不必擔心,我們還有 email !別忘了 git 本來就是為了管理 Linux 的核心

開發而設計的。所以,它非常適合像 Linux Kernel 這樣的開發組織形式高度分散,嚴重依賴 email 來進行交流的專案。

下面模擬你參加到《Git 中文教程》的編寫工作中來,看看我們可以怎麼通過 email 進行工作交流。你可以通過下面的命令下載這個專案的版本庫。

之後,你會在當前目錄

下得到一個叫 gittutorcn 的目錄,這就是你的專案的工作目錄了。預設地,它會有兩個分支: master 和 origin,你可以直接在 master 下展開工作,也可以建立你自己的工作分支,但是千萬不要修改 origin 分支,切記!因為它是公共版本庫的映象,如果你修改了它,那麼就不能生成正確的對公共版本庫的 patch 檔案了。

Note

如果你的確修改過 origin 分支的內容,那麼在生成 patch 檔案之前,請用 git-reset --hard 命令將它逆轉到最原始的,沒經過任何修改的狀態。

你可以直接在 master 下開展工作,也可以建立你自己的工作分支。當你對專案做了一定的工作,並提交到庫中。我們用 git-show-branch 命令先看下庫的狀態。

* [master] your buddy's contribution

! [origin] degining of git-format-patch example

--

* [master] your buddy's contribution

*+ [origin] degining of git-format-patch example

上面就假設你已經提交了一個叫 "your buddy's contribution" 的工作。接著我們來看看怎麼通過 email 來交流工作了。

$ git fetch origin (1)

$ git rebase origin (2)

$ git format-patch origin (3)

(1)更新 origin 分支,防止 origin 分支不是最新的公共版本,產生錯誤的補丁

檔案;

(2)將你在 master 上提交的工作遷移到新的源版本庫的狀態的基礎上;

(3)生成補丁

檔案;

上面的幾個命令,會在當前目錄

下生成一個大概名為 0001-your-buddy-s-contribution.txt補丁檔案, 建議你用文字工具檢視一下這個檔案的具體形式,然後將這個檔案以附件的形式傳送到專案維護者的郵箱

當專案的維護者收到你的郵件後,只需要用 git-am 命令,就可以將你的工作合併到專案中來。

$ git checkout -b buddy-incomming

$ git am /path/to/0001-your-buddy-s-contribution.txt

協同工作

假設 Alice 在一部機器上自己的個人目錄中建立了一個專案 /home/alice/project, Bob 想在同一部機器自己的個人目錄中為這個專案做點什麼。

Bob 首先這樣開始:

$ git clone /home/alice/project myrepo

這樣就建立了一個儲存著 Alice 的版本庫的映象

的新目錄 "myrepo"。這個映象儲存著原始專案的起點和它的發展歷程。

接著 Bob 對專案做了些更改並提交了這些更改:

(編輯一些檔案)

$ git commit -a

(如果需要的話再重複這個步驟)

當他搞定之後,他告訴 Alice 將他的東西從 /home/bob/myrepo 中引入,她只需要這樣:

$ cd /home/alice/project

$ git pull /home/bob/myrepo

這樣就將 Bob 的版本庫中的 "master" 分支的變化引入了。 Alice 也可以通過在 pull 命令的後面加入引數的方式來引入其他的分支。

在匯入了 Bob 的工作之後,用 "git-whatchanged" 命令可以檢視有什麼信的提交物件。如果這段時間裡以來,Alice 也對專案做過自己的修改,當 Bob 的修改被合併進來的時候,那麼她需要手動修復所有的合併衝突。

謹慎的 Alice 在匯入 Bob 的工作之前,希望先檢查一下。那麼她可以先將 Bob 的工作匯入到一個新建立的臨時分支中,以方便研究 Bob 的工作:

$ git fetch /home/bob/myrepo master:bob-incoming

這個命令將 Bob 的 master 分支的匯入到名為 bob-incoming 的分支中(不同於 git-pull 命令,git-fetch 命令只是取得 Bob 的開發工作的拷貝,而不是合併經來)。接著:

$ git whatchanged -p master..bob-incoming

這會列出 Bob 自取得 Alice 的 master 分支之後開始工作的所有變化。檢查過這些工作,並做過必須的調整之後, Alice 就可以將變化匯入到她的 master 分支中:

$ git-checkout master

$git pull . bob-incoming

最後的命令就是將 "bob-incoming" 分支的東西匯入到 Alice 自己的版本庫中的,稍後,Bob 就可以通過下面的命令同步 Alice 的最新變化。

$ git pull

注意不需為這個命令加入 Alice 的版本庫的路徑

,因為當 Bob 克隆 Alice 的版本庫的時候, git 已經將這個路徑儲存到 .git/remote/origin 檔案中,它將會是所以的匯入操作的預設路徑。

Bob 可能已經注意到他並沒有在他的版本庫中建立過分支(但是分支已經存在了):

$ git branch

* master

origin

"origin" 分支,它是執行 "git-clone" 的時候自動建立的,他是 Alice 的 master 分支的原始映象

, Bob 應該永遠不要向這個分支提交任何東西。

如果 Bob 以後決定在另外一部主機上開展工作,那麼他仍然需要通過 SSH 協議從新克隆和匯入( Alice 的版本庫):

我們可以使用 git 自然協議,或者是 rsync, http 等協議的任何一種,詳情請參考 git-pull。

Git 同樣可以建立類似 CVS 那樣的開發模式,也就是所有開發者都向中心版本庫提交工作的方式,詳情參考 git_push 和 git for CVS users 。

打包

在前面,我們已經看到在 .git/objects/??/ 目錄中儲存著我們建立的每一個 git 物件。這樣的方式對於自動和安全地建立物件很有效,但是對於網路傳輸

則不方便。 git 物件一旦建立了,就不能被改變,但有一個方法可以優化物件的儲存,就是將他們“打包到一起”。

$ git repack

上面的命令讓你做到這點,如果你一直是做著我們的例子過來的,你現在大約會在 .git/objects/??/ 目錄下積累了17個物件。 git-repack 會告訴你有幾個物件被打包了,並且將他們儲存在 .git/objects/pack 目錄當中。

Note

你將會看到兩個檔案,pack-*.pack and pack-*.idx 在 .git/objects/pack 目錄。他們的關係是很密切的,如果你手動將他們拷貝到別的版本庫中的話,你要決定將他們一起拷貝。前者是儲存著所有被打包的資料的檔案,後者是隨機訪問的索引。

如果你是個偏執狂,就執行一下 git-verity-pack 命令來檢查一下有缺陷的包吧,不過,其實你無須太多擔心,我們的程式非常出色 ;-).

一旦你已經對那些物件打包了,那麼那些已經被打過包的原始的物件,就沒有必要保留了。

$ git prune-packed

會幫你清除他們。

如果你好奇的話,你可以在執行 git-prune-repacked 命令之前和之後,都執行一下 find .git/objects -type f,這樣你就能看到有多少沒有打包的物件,以及節省了多少磁碟空間。

git pull git-pull 對於 HTTP 傳輸來說,一個打包過的版本庫會將一定數量的相關聯的物件放進一個有關聯性的打包中。如果你設想多次從 HTTP 公共版本庫中匯入資料,你也許要頻繁地 reapck & prune,要麼就乾脆從不這樣做。

如果你此時再次執行 git-repack,它就會說 "Nothing to pack"。要是你繼續開發,並且積累了一定數量的變遷,再執行 git-repack 將會建立一個新的包,它會包含你自上次對庫打包以來建立的物件。我們建議你儘快在初始化提交之後打包一下你的版本庫(除非你的專案是個塗鴉式的草稿專案),並且在專案經歷過一段很活躍的時期時,再執行 git-repack 一下。

當一個版本庫通過 git-push 和 git-pull 命令來同步源版本庫中打包過的對像的時候,通常儲存到目標版本庫中的是解包

了的物件,除非你使用的是 rsync(遠端同步協議)協議的傳輸方式。正是這種容許你在兩頭的版本庫中有不同的打包策略的方式,他意味著你也許在過一段時間之後,需要在兩頭的版本庫中都重新打包一下。

釋出工作

我們可以通過一個遠端的版本庫來利用他人的工作,但是,你如何準備一個自己的版本庫來供其他人下載呢?你在自己的工作目錄下進行工作,這樣你的版本庫就被作為.git的一個子目錄放在你的工作樹下。你可以讓其他人來遠端的訪問你的版本庫,但是實際上這不是通常的做法。推薦的做法是建立一個公共的版本庫,讓它可供其他人訪問,並且,當你在你的工作目錄下做了很好的改動時,你可以更新到公共的版本庫中。這通常稱為pushing。

公共版本庫是可以被映像的,上的git公共版本庫也是這樣管理的。

從你的本地的(私有的)版本庫中釋出改動到你的遠端的(公共的)版本庫中需要遠端機器上的寫許可權。你需要一個SSH的帳號來執行一個簡單的命令,git-receive-pack。首先,你需要在遠端機器上建立一個空的版本庫來存放你的公共版本庫。這個空版本庫以後將通過pushing來保持更新。顯然,這個版本庫之需要在開始的時候建立一次。

git push使用一對命令,git-send-pack在本地機上執行,git-receive-pack在遠端機上執行。這兩個命令通過SSH連線來進行通訊。

你本地的版本庫的git目錄通常是.git,但是你的公共版本庫通常還要加上你的專案名,即.git。讓我們來為my-git建立這樣一個版本庫。首先,登入遠端的機器,建立一個空目錄(如果你選擇HTTP作為釋出方法,這個空目錄需要建在web server的根目錄下面):

$ mkdir my-git.git

然後執行git init-db命令將這個目錄加入git版本庫中,這裡,因為這個版本庫的名字不是通常的.git,我們需要稍微改動一下命令:

$ GIT_DIR=my-git.git git-init-db

有很多種傳輸方式可以釋出公共版本庫。這裡,要確認這個目錄可以通過你選擇的傳輸方式來被其他人訪問。你也需要確認你有git-receive-pack這個程式在$PATH這個路徑下。

當你直接執行程式的時候,很多sshd的安裝版並沒有將你的shell作為登陸的shell;這就是說,如果你登陸的shell是bash 的話,被讀到的是.bashrc而不是.bash_profile。確認.bashrc設定好了$PATH路徑,這樣你才可以執行git-receive-pack命令。

如果你打算通過HTTP來發布這個版本庫,這是你就應該執行命令

chmod +x my-git.git/hooks/post-update。這確認了每次你匯入資料到這個版本庫中,git-update-server-info能夠被執行。

然後,你的“公共的版本庫”可以接受你的任何改動了。回到你的本地機上,執行命令

$ git push :/path/to/my-git.git master

該命令將你的公共版本庫和你當前的版本庫中指定名稱的分支頭部同步(這裡是master)。舉一個實際的例子,你可以這樣來更新公共的git版本庫。的映象

網路也這樣來同步其他公共的可訪問的機器:

將工作捆綁到一起

通過 git 的分支功能,你可以非常容易地做到好像在同一時間進行許多“相關-或-無關”的工作一樣。

我們已經通過前面的 "fun and work" 使用兩個分支的例子,看到分支是怎麼工作的。這樣的思想在多於兩個的分支的時候也是一樣的,比方說,你現在在 master 的頭,並有些新的程式碼在 master 中,另外還有兩個互不相關的補丁

分別在 "commit-fix" 和 "diff-fix" 兩個分支中。

$ git show-branch

! [commit-fix] Fix commit message normalization.

! [diff-fix] Fix rename detection.

* [master] Release candidate #1

---

+ [diff-fix] Fix rename detection.

+ [diff-fix~1] Better common substring algorithm.

+ [commit-fix] Fix commit message normalization.

* [master] Release candidate #1

++* [diff-fix~2] Pretty-print messages.

兩個補丁

我們都測試好了,到這裡,你想將他們倆合併起來,於是你可以先合併 diff-fix ,然後再合併 commit-fix,像這樣:

$ git merge 'Merge fix in diff-fix' master diff-fix

$ git merge 'Merge fix in commit-fix' master commit-fix

結果如下:

$ git show-branch

! [commit-fix] Fix commit message normalization.

! [diff-fix] Fix rename detection.

* [master] Merge fix in commit-fix

---

- [master] Merge fix in commit-fix

+ * [commit-fix] Fix commit message normalization.

- [master~1] Merge fix in diff-fix

+* [diff-fix] Fix rename detection.

+* [diff-fix~1] Better common substring algorithm.

* [master~2] Release candidate #1

++* [master~3] Pretty-print messages.

然而,當你確信你手頭上的確是一堆互不相關的專案變化時,就沒有任何理由將這堆東西一個個地合併(假如他們的先後順序很重要,那麼他們就不應該被定以為無關的變化),你可以一次性將那兩個分支合併到當前的分支中,首先我們將我們剛剛做過的事情逆轉一下,我們需要通過將 master 分支重置到 master~2 位置的方法來將它逆轉到合併那兩個分支之前的狀態。

$ git reset --hard master~2

你可以用 git-show-branch 來確認一下的確是回到了兩次 git-merge 的狀態了。接著你可以用一行命令將那兩個分支匯入的方式來替代兩次執行(也就是所謂的 炮製章魚 -- making an Octopus)git-merge :

$ git pull . commit-fix diff-fix

$ git show-branch

! [commit-fix] Fix commit message normalization.

! [diff-fix] Fix rename detection.

* [master] Octopus merge of branches 'diff-fix' and 'commit-fix'

---

- [master] Octopus merge of branches 'diff-fix' and 'commit-fix'

+ * [commit-fix] Fix commit message normalization.

+* [diff-fix] Fix rename detection.

+* [diff-fix~1] Better common substring algorithm.

* [master~1] Release candidate #1

++* [master~2] Pretty-print messages.

注意那些不適合製作章魚的場合,儘管你可以那樣做。一隻“章魚”往往可以使專案的提交歷史更具可讀性,前提是你在同一時間匯入的兩份以上的變更是互不關聯的。然而,如果你在合併任何分支的過程中出現合併衝突,並且需要手工解決的話,那意味著這些分支當中有相互干涉的開發工作在進行,那麼你就應該將這個兩個衝突先合併,並且記錄下你是如何解決這個衝突,以及你首先處理他們的理由。(譯者按:處理完衝突之後,你就可以放心製作“章魚”了)否則的話將會造成專案的發展歷史很難跟蹤。

管理

版本庫的管理員

可以用下面的工具來建立和維護版本庫。

* git-daemon(1) 容許匿名下載版本庫。

* git-shell(1) 面向中心版本庫模式的使用者的類似 受限的 shell 的命令。

update hook howto 一個很好的管理中心版本庫的例子。

例子

在 /pub/scm 上執行 git守護程式

$ grep git /etc/inet.conf

git stream tcp nowait nobody \

/usr/bin/git-daemon git-daemon --inetd --syslog --export-all /pub/scm

這個配置行應該在配置檔案中用一行來寫完。

僅給開發者 push/pull 的訪問許可權。

$ grep git /etc/passwd (1)

alice:x:1000:1000::/home/alice:/usr/bin/git-shell

bob:x:1001:1001::/home/bob:/usr/bin/git-shell

cindy:x:1002:1002::/home/cindy:/usr/bin/git-shell

david:x:1003:1003::/home/david:/usr/bin/git-shell

$ grep git /etc/shells (2)

/usr/bin/git-shell

(1) 將使用者的登入 shell 設定為 /usr/bin/git-shell,

它除了執行 "git-push" 和 "git-pull" 不能做任何事。

這樣使用者就可以通過 ssh 來訪問機器。

(2) 許多的發行版需要在 /etc/shells 配置檔案中列明要用什麼 shell 來作為登入 shell。

CVS - 模式的公共庫。

$ grep git /etc/group (1)

git:x:9418:alice,bob,cindy,david

$ cd /home/devo.git

$ ls -l (2)

lrwxrwxrwx 1 david git 17 Dec 4 22:40 HEAD -> refs/heads/master

drwxrwsr-x 2 david git 4096 Dec 4 22:40 branches

-rw-rw-r-- 1 david git 84 Dec 4 22:40 config

-rw-rw-r-- 1 david git 58 Dec 4 22:40 description

drwxrwsr-x 2 david git 4096 Dec 4 22:40 hooks

-rw-rw-r-- 1 david git 37504 Dec 4 22:40 index

drwxrwsr-x 2 david git 4096 Dec 4 22:40 info

drwxrwsr-x 4 david git 4096 Dec 4 22:40 objects

drwxrwsr-x 4 david git 4096 Nov 7 14:58 refs

drwxrwsr-x 2 david git 4096 Dec 4 22:40 remotes

$ ls -l hooks/update (3)

-r-xr-xr-x 1 david git 3536 Dec 4 22:40 update

$ cat info/allowed-users (4)

refs/heads/master alice\|cindy

refs/heads/doc-update bob

refs/tags/v[0-9]* david

(1) 將所有的開發人員都作為 git 組的成員。

(2) 並且給予他們公共版本庫的寫許可權。

(3) 用一個在 Documentation/howto/ 中的 Carl 寫的例子來實現版本庫的分支控制策略。

(4) Alice 和 Cindy 可以提交入 master 分支,只有 Bob 能提交入 doc-update 分支,

David 則是發行經理只有他能建立並且 push 版本標籤。

支援默協議傳輸的 HTTP 伺服器。

dev$ git update-server-info (1)

ftp> cp -r .git /home/user/myproject.git

(1) 保證 info/refs 和 object/info/packs 是最新的。

(2) 上傳到你的 HTTP 伺服器主機

修改author

有時候,忘了做git config設定或config的email不規範,導致git log中author不對,造成溝通困難。 此時,你可以遵循如下步驟,修改author資訊[3] 

1、首先,你需要設定正確的user#(“#”換成“.”)name和user.email資訊,注:請務必使用公司郵箱(gitlab使用者請和證書郵箱保持一致,否則無法push,請開啟gitlab.your-web#com(“#”換成“.”) 點選右上角的profile,看看自己的郵箱是什麼)

git config --global user#(“#”換成“.”)name "你的名稱"git config --global user.email "你的公司郵箱"

注:去掉--global引數是單獨為當前專案設定

2、設定好後,修改你前面已提交的不正確的資訊[3] 

linux下在庫根目錄執行命令(windows請看最後一節): git-m (請先安裝此命令:sudo yum install git-m -b test) 1)嚮導會讓你輸入需要修正的email(括弧內提示會自動給你找到不規範的email,你可以直接回車) 2)輸入需要替換成正確的使用者名稱 3)輸入需要替換成正確的email(公司郵箱)

此時,程式會自動找出所有不合規範email的,並試圖自動修復你本地尚未push的修改。

3、對不支援rpm的使用者,可以通過 wget http://gitlab-help.gitlab.your-web#(“#”換成“.”)com/git-m 來獲取git-m命令。 你也可以手工執行git filter-branch -f --commit-filter 命令來修改author資訊[3] 

開發模式

儘管 git 是一個正式專案釋出系統,它卻可以方便地將你的專案建立在鬆散的開發人員組織形式上。 Linux核心

的開發,就是按這樣的模式進行的。在 Randy Dunlap 的著作中("Merge to Mainline" 第17頁)就有很好的介紹

需要強調的是正真的非常規的開發組織形式, git 這種組織形式,意味著對於工作流程的約束,沒有任何強迫性的原則。你不必從唯一一個遠端版本庫中匯入(工作目錄)。

專案領導人

(project lead)的工作推介

1. 在你自己的本地機器

上準備好主版本庫。你的所有工作都在這裡完成。

2. 準備一個能讓大家訪問的公共版本庫。

如果其他人是通過默協議的方式(http)來匯入版本庫的,那麼你有必要保持這個 默協議的友好性。 git-init-db 之後,複製自標準模板庫

的 $GIT_DIR/hooks/post-update 將包含一個對 git-update-server-info 的呼叫,但是 post-update 預設是不能喚起它自身的。通過 chmod +x post-update 命令使能它。這樣讓 git-update-server-info 保證那些必要的檔案是最新的。

3. 將你的主版本庫推入公共版本庫。

4. git-repack 公共版本庫。這將建立一個包含初始化提交物件集的打包作為專案的起始線,可能的話,執行一下 git-prune,要是你的公共庫是通過 pull 操作來從你打包過的版本庫中匯入的。

5. 在你的主版本庫中開展工作,這些工作可能是你自己的最專案的編輯,可能是你由 email 收到的一個補丁

,也可能是你從這個專案的“子系統負責人” 的公共庫中匯入的工作等等。

你可以在任何你喜歡的時候重新打包你的這個私人的版本庫。

6. 將專案的進度推入公共庫中,並給大家公佈一下。

7. 儘管一段時間以後,"git-repack" 公共庫。並回到第5步繼續工作。

專案的子系統負責人(subsystem maintainer)也有自己的公共庫,工作流程大致如下:

1. 準被一個你自己的工作目錄,它通過 git-clone 克隆自專案領導人

的公共庫。原始的克隆地址(URL)將被儲存在 .git/remotes/origin 中。

2. 準備一個可以給大家訪問的公共庫,就像專案領導人

所做的那樣。

3. 複製專案領導人

的公共庫中的打包檔案到你的公共庫中,除非你的公共庫和專案領導人的公共庫是在同一部主機上。以後你就可以通過 objects/info/alternates 檔案的指向來瀏覽它所指向的版本庫了。

4. 將你的主版本庫推入你的公共版本庫,並執行 git-repack,如果你的公共庫是通過的公共庫是通過 pull 來匯入的資料的話,再執行一下 git-prune 。

5. 在你的主版本庫中開展工作。這些工作可能包括你自己的編輯,來自 email 的補丁

,從專案領導人,“下一級子專案負責人”的公共庫哪裡匯入的工作等等。

你可以在任何時候重新打包你的私人版本庫。

6. 將你的變更推入公共庫中,並且請“專案領導人

”和“下級子系統負責人”匯入這些變更。

7. 每隔一段時間之後,git-repack 公共庫。回到第 5 步繼續工作。

“一般開發人員”無須自己的公共庫,大致的工作方式是:

1. 準備你的工作庫,它應該用 git-clone 克隆自“專案領導人

”的公共庫(如果你只是開發子專案,那麼就克隆“子專案負責人”的)。克隆的源地址(URL)會被儲存到 .git/remotes/origin 中。

2. 在你的個人版本庫中的 master 分支中開展工作。

3. 每隔一段時間,向上遊的版本庫執行一下 git-fetch origin 。這樣只會做 git-pull 一半的操作,即只克隆不合並。公共版本庫的新的頭就會被儲存到 .git/refs/heads/origins 。

4. 用 git-cherry origin 命令,看一下你有什麼補丁

被接納了。並用 git-rebase origin 命令將你以往的變更遷移到最新的上游版本庫的狀態中。(關於 git-rebase 命令,請參考 git-rebase)

5. 用 git-format-patch origin 生成 email 形式的補丁

併發給上游的維護者。回到第二步接著工作。

相關文章