首先,在繪製提交圖的問題上爲+1。 :-)其次,不是非常重要,但你應該把它看作兩個分支,master
和branch1
。 master
沒有什麼特別之處,它只是一個像其他任何分支一樣的分支。
現在,git中的所有內容都是你永遠不能改變提交的事實。這是從git實際使用大的難看的40個字符的SHA-1命名每個提交的方式開始的:SHA-1通過計算提交的內容的加密校驗和來完成。所以,如果你(嘗試)改變任何東西,那麼「真名」就會改變,並且你有一個新的不同的提交。
相反,你可以做的是拷貝一箇舊的提交到一個新的,在提交完成之前做出期望的改變(因此提供它的「真實姓名」)。我們在這裏要做的是製作副本。
你畫這個圖:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
F--G--H--I--J--K--L... branch1
但不能完全是正確的,因爲每個提交點回到其父犯,這裏F
點回E
當我們看master
,但當我們查看branch1
時,F
指向D
。 (我在這裏假設每個單獨的字母表示給定提交的SHA-1「真實姓名」)。我將假定branch1
上的那些是副本,但它並不重要其中,只要你對與這些提交相關的樹感到高興(即,當你獲得其中一個時,你會得到什麼)。
你想要什麼是這樣的:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
J'-K'-L'... branch1
這裏的 「黃金」 大關J'
,K'
和L'
表明,這些提交J
,K
和L
的副本。
雖然我一直在寫這個答案,但有一些人建議使用git cherry-pick
來製作這些副本。這很好 - 但事實上,我們可以使用git rebase
來做伎倆,因爲其中cherry-pick
是一個簡單的斧頭,你可以用它來砍掉一棵櫻桃樹,rebase
是一個完全自動化的鏈鋸,你可以使用它來完成它們全部一舉突破。
我們首先告訴rebase
在branch1
上操作;簡單的方法是git checkout branch1
(或者我們可以將branch1
作爲git rebase
的最終參數,但我將使用「簡單方法」)。
接下來,我們需要知道我們想要提交分支的位置,即有一些方法來命名提交I
。從上面,我們可以說master~3
:從master
(它的名字是Y
)算起後面四次提交,然後你通過X
,然後Z
,然後到達I
。或者你可以通過真名SHA-1來實現,SHA-1總能正常工作,但通常需要剪切粘貼才能正確使用。對於下面的命令,我只會寫I
。我們會告訴基於--onto
的提交。
最後,我們需要得到rebase
挑犯下J
,K
和L
複製。還有很多方法可以做到這一點。也許最簡單的方法是使用git rebase -i
,這將提供重新綁定branch1
上的所有內容,然後您可以刪除前四個提交的pick
行。或者,我們可以告知rebase
排除特定的提交,但我假設您將使用交互式方法。
因此,這裏的最終命令是git rebase -i --onto I master
(在執行git checkout branch1
命令之後)。 -i
使這種交互,以便您可以放棄提交; --onto
爲rebase
將挑選的新的一系列提交選擇目標;和master
部分告訴rebase
承諾到排除:具體而言,任何承諾從名字master
到達。 (這不包括提交A
,B
,C
和D
,但不能複製的版本F'
,G'
,H'
,並出現在branch
I'
:你只刪除「選擇」線的)。
git rebase
完成其一系列git cherry-pick
命令後,它所做的最後一件事就是將當前分支的分支標籤(branch1
)移動到最新的最新提交。所以這確實會產生:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
J'-K'-L'... branch1
前提是一切順利。 (如果這樣下去不好,你可以git rebase --abort
停止的過程,把一切都回到你開始之前會是這樣)
我們可以成爲發燒友和(假設I'
是branch1~3
)做的事:
git rebase --onto master~3 branch1~3 branch1
甚至沒有任何初始的git checkout
,但是這裏假定在這裏(對master
和branch1
)都計數回3是正確的。這基本上和以前一樣,有三個改進:
- 我們在末尾添加
branch1
,使rebase
檢查出來的第一步
- 我們使用
branch1~3
,而不是master
爲說,爭論「排除這東西,只重訂的東西,這不是從這裏到達」
- 我們放棄了
-i
,因爲我們並不需要編輯走‘選擇’命令,此時
這需要AB更仔細的提交計數以確保所有~
表達式都是正確的(或者,您可以通過原始SHA-1標識符來處理此問題)。
嗯,大概有master
一些特別的東西:一,它是當你做git init
在你把分支,它創建了一個新的存儲庫。另一個是git merge
稍微調整合並信息。但這兩者都很微不足道。
實際上,在git存儲庫(commit,tree,帶註釋的標記和「blob」 - 最後一個存儲文件內容)中找到的所有四種對象都是如此。每個存儲在存儲庫中,然後給出一個由其自己的密碼校驗和組成的名稱。
更準確地,每次提交具有零條或多個parent ...
線在裏面,與各parent
給予相應的父提交的SHA-1的ID。第一個父項通常是分支的「主線」,任何額外的父項都表示這是合併提交。沒有父母的提交,如上面的A
,是「根提交」。
爲什麼不僅僅是'git checkout branch1'跟着'git rebase COMMIT_HASH_FOR_I'?或者只是'git rebase COMMIT_HASH_FOR_I branch1'? ('COMMIT_HASH_FOR_I'是針對主版本的)我認爲這樣更直接一點,儘管你的版本是完整的規範形式。 :-)好的答案,順便說一句。非常完整。 – jszakmeister 2014-08-29 09:24:23
@jszakmeister:我還沒有嘗試過,但我認爲(假設原始圖應該是'F'-G'-H'-I'-...''一個簡單的'git rebase'將'F''複製到'F''等,並且無法看到它們已經在.Rebase在某些情況下會知道刪除相同的提交,所以它可能會起作用 –
torek
2014-08-29 10:10:00
只要差異是如果'F','G'和'H''由於衝突或在某些情況下被修改而有額外的修改,這隻會成爲一個問題FWIW,我這樣做了幾次。:-) – jszakmeister 2014-08-29 11:06:21