2014-08-29 109 views
3
我有問題,清理我的git歷史樹

..的Git分支分割點

基本上我用1支Git代碼庫的樣子:

A--B--C--D--E--F--G--H--I--Z--X--Y... master 
      \ 
      F--G--H--I--J--K--L...  branch1 

犯下E是有些不太重要的變化和可以變得多餘。 這裏的正確分割點應該是我不是D. 這個提交E可以被刪除或者添加到兩個分支上。

這有什麼辦法可以實現嗎?

----- 更新

哇非常感謝您對您的幫助!
我試過什麼torek建議,它的工作就像一個魅力!

現在..我有另一個倉庫清理,但它是更爲複雜..

它看起來像:

1--2--3--4--*A--5--6--7--8--*B--9--10--11--*C--*D--12--13--14   master 
      \ 
      5--6--7--8--9--10--11--12--13--14        branch1 

注1:字母代表的內容提交
注2:* 提交僅適用於主分支

有沒有辦法清理這樣的歷史樹?

另外,對於像這樣構建的項目,推薦的git方法是什麼?
即分支之後,大部分的提交將包含除一些分支特定相同的更新的承諾

回答

3

首先,在繪製提交圖的問題上爲+1。 :-)其次,不是非常重要,但你應該把它看作兩個分支,masterbranch1master沒有什麼特別之處,它只是一個像其他任何分支一樣的分支。

現在,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'表明,這些提交JKL的副本。

雖然我一直在寫這個答案,但有一些人建議使用git cherry-pick來製作這些副本。這很好 - 但事實上,我們可以使用git rebase來做伎倆,因爲其中cherry-pick是一個簡單的斧頭,你可以用它來砍掉一棵櫻桃樹,rebase是一個完全自動化的鏈鋸,你可以使用它來完成它們全部一舉突破。

我們首先告訴rebasebranch1上操作;簡單的方法是git checkout branch1(或者我們可以將branch1作爲git rebase的最終參數,但我將使用「簡單方法」)。

接下來,我們需要知道我們想要提交分支的位置,即有一些方法來命名提交I。從上面,我們可以說master~3:從master(它的名字是Y)算起後面四次提交,然後你通過X,然後Z,然後到達I。或者你可以通過真名SHA-1來實現,SHA-1總能正常工作,但通常需要剪切粘貼才能正確使用。對於下面的命令,我只會寫I。我們會告訴基於--onto的提交。

最後,我們需要得到rebase挑犯下JKL複製。還有很多方法可以做到這一點。也許最簡單的方法是使用git rebase -i,這將提供重新綁定branch1上的所有內容,然後您可以刪除前四個提交的pick行。或者,我們可以告知rebase排除特定的提交,但我假設您將使用交互式方法。

因此,這裏的最終命令是git rebase -i --onto I master(在執行git checkout branch1命令之後)。 -i使這種交互,以便您可以放棄提交; --ontorebase將挑選的新的一系列提交選擇目標;和master部分告訴rebase承諾到排除:具體而言,任何承諾從名字master到達。 (這不包括提交ABCD,但不能複製的版本F'G'H',並出現在branchI':你只刪除「選擇」線的)。

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,但是這裏假定在這裏(對masterbranch1)都計數回3是正確的。這基本上和以前一樣,有三個改進:

  1. 我們在末尾添加branch1,使rebase檢查出來的第一步
  2. 我們使用branch1~3,而不是master爲說,爭論「排除這東西,只重訂的東西,這不是從這裏到達」
  3. 我們放棄了-i,因爲我們並不需要編輯走‘選擇’命令,此時

這需要AB更仔細的提交計數以確保所有~表達式都是正確的(或者,您可以通過原始SHA-1標識符來處理此問題)。


嗯,大概有master一些特別的東西:一,它是當你做git init在你把分支,它創建了一個新的存儲庫。另一個是git merge稍微調整合並信息。但這兩者都很微不足道。

實際上,在git存儲庫(commit,tree,帶註釋的標記和「blob」 - 最後一個存儲文件內容)中找到的所有四種對象都是如此。每個存儲在存儲庫中,然後給出一個由其自己的密碼校驗和組成的名稱。

更準確地,每次提交具有零條或多個parent ...線在裏面,與各parent給予相應的父提交的SHA-1的ID。第一個父項通常是分支的「主線」,任何額外的父項都表示這是合併提交。沒有父母的提交,如上面的A,是「根提交」。

+0

爲什麼不僅僅是'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

+0

@jszakmeister:我還沒有嘗試過,但我認爲(假設原始圖應該是'F'-G'-H'-I'-...''一個簡單的'git rebase '將'F''複製到'F''等,並且無法看到它們已經在.Rebase在某些情況下會知道刪除相同的提交,所以它可能會起作用 – torek 2014-08-29 10:10:00

+0

只要差異是如果'F','G'和'H''由於衝突或在某些情況下被修改而有額外的修改,這隻會成爲一個問題FWIW,我這樣做了幾次。:-) – jszakmeister 2014-08-29 11:06:21

1

的一種方式做到這一點,是創建一個新的分支爲主的副本,並做git reset --hard I-commit然後cherry-pick的提交你想(希望有不太多)

+0

在我看來,最好直接從我開始創建一個新的分支,而不是做出這樣的重置命令。 – 2014-08-29 08:08:36

2

你可以在Icherry pickbranch1所做的所有更改做出一個新的分支所以基本上

git branch -b temp I 
git cherry-pick J..branch1 

然後,您將有一個新的分支temp劃分爲I而不更改E幷包含所有提交branch1如果工作正常,您可以重命名這兩個以使temp分支您的新branch1。在櫻桃採摘之前,如果你不想要它,你可以恢復提交E如果你不需要它branch1

+0

我只是改變你的第一個命令,並將其替換爲:'git checkout -b temp I'。通過使用此命令,您將自動在相應的分支上執行下一個提供的命令。 – 2014-08-29 08:19:37

+0

@JoëlSalamindone – Trudbert 2014-08-29 08:20:15

+0

這個解決方案似乎現在是解決所描述的分支起點問題的好方法,但'git revert E'sis不是@hoistyler所要求的!這個命令將創建一個新的提交,它將與E相同,F,G和H的修改將會丟失! – 2014-08-29 08:22:41