現在你已經看到了子模塊系統的麻煩之處,讓我們來看一下解決相同問題的另一途徑。當 Git 歸并時,它會檢查需要歸并的內容然后選擇一個合適的歸并策略。如果你歸并的分支是兩個,Git使用一個遞歸策略。如果你歸并的分支超過兩個,Git采用章魚策略。這些策略是自動選擇的,因為遞歸策略可以處理復雜的三路歸并情況——比如多于一個共同祖先的——但是它只能處理兩個分支的歸并。章魚歸并可以處理多個分支但是但必須更加小心以避免沖突帶來的麻煩,因此它被選中作為歸并兩個以上分支的默認策略。
實際上,你也可以選擇其他策略。其中的一個就是子樹歸并,你可以用它來處理子項目問題。這里你會看到如何換用子樹歸并的方法來實現前一節里所做的 rack 的嵌入。
子樹歸并的思想是你擁有兩個工程,其中一個項目映射到另外一個項目的子目錄中,反過來也一樣。當你指定一個子樹歸并,Git可以聰明地探知其中一個是另外一個的子樹從而實現正確的歸并——這相當神奇。
首先你將 Rack 應用加入到項目中。你將 Rack 項目當作你項目中的一個遠程引用,然后將它檢出到它自身的分支:
~~~
$ git remote add rack_remote git@github.com:schacon/rack.git
$ git fetch rack_remote
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From git@github.com:schacon/rack
* [new branch] build -> rack_remote/build
* [new branch] master -> rack_remote/master
* [new branch] rack-0.4 -> rack_remote/rack-0.4
* [new branch] rack-0.9 -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"
~~~
現在在你的rack_branch分支中就有了Rack項目的根目錄,而你自己的項目在master分支中。如果你先檢出其中一個然后另外一個,你會看到它們有不同的項目根目錄:
~~~
$ ls
AUTHORS KNOWN-ISSUES Rakefile contrib lib
COPYING README bin example test
$ git checkout master
Switched to branch "master"
$ ls
README
~~~
要將 Rack 項目當作子目錄拉取到你的master項目中。你可以在 Git 中用git read-tree來實現。你會在第9章學到更多與read-tree和它的朋友相關的東西,當前你會知道它讀取一個分支的根目錄樹到當前的暫存區和工作目錄。你只要切換回你的master分支,然后拉取rack_branch到你主項目的master分支的rack子目錄:
`$ git read-tree --prefix=rack/ -u rack_branch`
當你提交的時候,看起來就像你在那個子目錄下擁有Rack的文件——就像你從一個tarball里拷貝的一樣。有意思的是你可以比較容易地歸并其中一個分支的變更到另外一個。因此,如果 Rack 項目更新了,你可以通過切換到那個分支并執行拉取來獲得上游的變更:
~~~
$ git checkout rack_branch
$ git pull
~~~
然后,你可以將那些變更歸并回你的 master 分支。你可以使用`git merge -s subtree`,它會工作的很好;但是 Git 同時會把歷史歸并到一起,這可能不是你想要的。為了拉取變更并預置提交說明,需要在-s subtree策略選項的同時使用`--squash`和`--no-commit`選項。
~~~
$ git checkout master
$ git merge --squash -s subtree --no-commit rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
~~~
所有 Rack 項目的變更都被歸并并且可以進行本地提交。你也可以做相反的事情——在你主分支的rack目錄里進行變更然后歸并回rack_branch分支,然后將它們提交給維護者或者推送到上游。
為了得到rack子目錄和你rack_branch分支的區別——以決定你是否需要歸并它們——你不能使用一般的diff命令。而是對你想比較的分支運行git diff-tree:
`$ git diff-tree -p rack_branch`
或者,為了比較你的rack子目錄和服務器上你拉取時的master分支,你可以運行
`$ git diff-tree -p rack_remote/master`
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 安裝 Git
- 1.5 初次運行 Git 前的配置
- 1.6 獲取幫助
- 1.7 小結
- 2. Git基礎
- 2.1 取得項目的 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 技巧和竅門
- 2.8 小結
- 3. Git分支
- 3.1 何謂分支
- 3.2 分支的新建與合并
- 3.3 分支的管理
- 3.4 利用分支進行開發的工作流程
- 3.5 遠程分支
- 3.6 分支的衍合
- 3.7 小結
- 4. 服務器上的Git
- 4.1 協議
- 4.2 在服務器上部署 Git
- 4.3 生成 SSH 公鑰
- 4.4 架設服務器
- 4.5 公共訪問
- 4.6 GitWeb
- 4.7 Gitosis
- 4.8 Gitolite
- 4.9 Git 守護進程
- 4.10 Git 托管服務
- 4.11 小結
- 5. 分布式Git
- 5.1 分布式工作流程
- 5.2 為項目作貢獻
- 5.3 項目的管理
- 5.4 小結
- 6. Git工具
- 6.1 修訂版本(Revision)選擇
- 6.2 交互式暫存
- 6.3 儲藏(Stashing)
- 6.4 重寫歷史
- 6.5 使用 Git 調試
- 6.6 子模塊
- 6.7 子樹合并
- 6.8 總結
- 7. 自定義Git
- 7.1 配置 Git
- 7.2 Git屬性
- 7.3 Git掛鉤
- 7.4 Git 強制策略實例
- 7.5 總結
- 8. Git與其他系統
- 8.1 Git 與 Subversion
- 8.2 遷移到 Git
- 8.3 總結
- 9. Git 內部原理
- 9.2 Git 對象
- 9.3 Git References
- 9.4 Packfiles
- 9.5 The Refspec
- 9.6 傳輸協議
- 9.7 維護及數據恢復
- 9.8 總結
- 9.1 底層命令 (Plumbing) 和高層命令 (Porcelain)