簡體   English   中英

從特定提交開始創建新分支

[英]Create new branch from specific commit onwards

我想創建一個新的分支,從某個提交開始。 但是,我希望之后提交的所有提交也是該分支的一部分。

讓我們假設這是所有提交的branch-D

> branch-D
commit-A ---- commit-B ---- commit-C ---- commit-D

我想刪除從commit-B到新branch ,讓我們將其命名為branch-C ,並且只在branch-D上保留commit-A

> branch-D
commit-A

> branch-C
commit-B ---- commit-C ---- commit-D

我還沒有把任何東西推到遠程存儲庫。 有沒有辦法做到這一點?

關於Git的特殊之處在於,分支名稱不會影響您的提交歷史記錄中的內容 ,除非它們讓您 - 以及其他任何非常重要的人 - 找到提交。 秘訣是,在Git中,提交可能同時在許多分支

但重要的是,一旦提出,任何提交都無法改變 它可以復制到(新的,略有不同的)提交,但不能更改。

此外,雖然任何分支名稱可以強制指向任何提交,但是分支名稱動作有一個“正常方向”:通常,它們只會前進 正如我們稍后將要看到的那樣,前進意味着他們曾經指出的任何提交仍然“在”那個分支上。 所以,一旦你將你的提交給了其他人 - 其他一些Git - 並要求他們將這些提交稱為branch-D ,那么很難讓其他 Git收回它。 因此:

我還沒有把任何東西推到遠程存儲庫。 有沒有辦法做到這一點?

是的,並且可能有一個非常簡單的方法:“還沒有推動任何東西”意味着你是唯一擁有這些提交的人,所以無論你找到它們, 每個人都找到它們的方式,因為你是“大家”。 :-)沒有必要讓別人改變任何東西。

只要它們已經按照您想要的方式布局,您只需要重新排列找到這些提交的方式。

讓我們繪制你現有的一系列提交“Git Way”:

... <--A <--B <--C <--D   <-- branch-D

在Git中,每個提交都由其哈希ID唯一標識。 這些東西d1c9d3a大又丑,而且顯然是隨機的(雖然它們實際上是完全確定的),所以我們幾乎不使用它們,或使用像d1c9d3a這樣的縮寫形式。 而不是那樣,讓我們​​調用最后一次提交D

在提交D內部,有另一個哈希ID,標識D父提交 讓我們說它是c033bae ,但我們只是稱之為“提交C ”。 所以我們說D 指向 C

類似地, C指向B ,它指向A ,它指向A ......好吧,也許是master的提示 - 你沒有說,但讓我們假設現在:

...--o--o   <-- master
         \
          A--B--C--D   <-- branch-D

這是一種更緊湊的繪制方式。 提交總是,必然,指向倒退 ,所以我們並不需要內部箭頭 - 我們知道它們總是倒退。 但是,分支名稱 ,如masterbranch-D ......好吧,我們可以將這些點放在任何地方 我們想要做的是讓它們指向分支的“提示提交”。

Git通過從branch-name指向的那個開始查找提交: D ,對於branch-D ,或者在master的第一行上的最后o 然后它查看當前提交的父級,以向后移動。 然后它會查看父級的父級,依此類推。 因此,兩個o提交都在master branch-D :我們可以通過從master開始找到它們,或者我們可以通過從branch-D開始並向后工作四個步驟來找到它們。

這意味着我們想要的圖片看起來像這樣:

...--o--o   <-- master
         \
          A   <-- branch-D
           \
            B--C--D   <-- branch-C

這里, master上的提交也在兩個分支上,而commit A (現在是branch-D的提示)仍然在branch-C 它不再是branch-C尖端了。


另一方面,也許我們想要的圖片看起來像這樣:

          ?--?--?   <-- branch-C
         /
...--o--o   <-- master
         \
          A   <-- branch-D
           \
            B--C--D   <-- ???

也就是說,我們需要回答一個問題。 名稱branch-C將指向某個特定的提交。 當我們退回三步時,我們應該到達提交A嗎? 或者我們應該到達master的最后一次提交?


如果第一張圖片是正確的,答案很簡單:制作新名稱, branch-C ,指向提交D ; 然后強制現有名稱branch-D返回提交A 去做這個:

git branch branch-C branch-D  # copy the hash ID from branch-D to new branch-C

然后,根據現在檢出的分支,要么:

git reset --hard <hash-of-A> # move current branch; re-set index and work-tree

要么:

git branch -f branch-D <hash-of-A> # force branch-D to point to A

請注意,在使用git reset --hard之前,確保您沒有要保存的任何修改是一個非常好的主意。 雖然提交是幾乎永久性的(你可以讓他們回來,通常至少30天,即使你踢他們關閉所有分支機構的名稱),該指標和工作樹git reset --hard則會覆蓋都沒有

如果你想要第二張圖片,但是如果你想讓提交B的父母不是提交A你必須提交B 復制到一個新的,不同的提交,那就是“像B ,但......” 。 原始B和副本之間的差異將包括更改的父哈希ID。

對於這一點 ,你需要使用git cherry-pick或同等學歷(如git rebase ,這基本上是一個EN-集體摘櫻桃操作拷貝大量的提交)。 為此,您可以:

git checkout -b branch-C master

贈送:

...--o--o   <-- branch-C (HEAD), master
         \
          A--B--C--D   <-- branch-D

然后運行三個git cherry-pick命令來復制BCD ; 或更簡單 - 這使用<commit>~<number>表示法:

 git cherry-pick branch-D~3..branch-D

一次復制所有三個。 這會產生:

          B'-C'-D'   <-- branch-C (HEAD)
         /
...--o--o   <-- master
         \
          A--B--C--D   <-- branch-D

此時,強制branch-D指向提交A是安全A

git branch -f branch-D branch-D~3

您可以通過哈希ID執行此操作,如前面的示例所示:

git branch -f branch-D <hash-of-A>

我們所做的所有branch-D~3都在告訴Git: 計算三個父步驟,一次一個父級 所以我們從D開始,向C倒數一步,倒數到B另一步,然后倒數第三步到A

更新 - 我誤讀了你的圖表並因此混淆了分支名稱。 同樣,torek指出,對圖表的“更正確”的閱讀包含一些含糊之處。 我將保留我的答案,因為我認為它傳達了主要原則; 如果你需要進一步編輯branchC歷史,我會稍微談談使用rebase; 但是要獲得更詳細的答案,請參閱torek的回復。


首先讓我找到我認為你正在尋找的解決方案; 但我建議閱讀除此之外,因為在這個問題中存在概念上的混淆,值得清理。

所以你現在擁有

A -- B -- C -- D <--(branchD)

我想你想落得AbranchD ,並為一個新的分支BC ,和D 因此,第一步是檢查branchD (或D ),創建新分支

git branch branchC

然后將branchD分支移回A

git reset --hard HEAD~3

(我使用HEAD~3因為在這個例子中,這將是A一個名稱。它取決於從master的歷史記錄中提取的提交數量。使用A的提交ID(SHA)總是可以的A取代HEAD~3 。)

完成這些步驟后,您就擁有了

A <--(branchD)
 \
  B -- C -- D <--(branchC)

看起來就像你描述的那樣,但我們並沒有像描述所暗示的那樣到達那里。

我沒有提交承諾; 我搬了分店。 這在git中要簡單明了,它反映了提交不像git那樣在某些系統中“屬於”分支。 因此,語句“在先前提交時分支但在該分支上包含以下提交”在git中實際上沒有意義。

問題中前后圖中的含糊不清確實讓我誤解了branchC應該在其歷史中應該包含的內容。 如果branchC 包含A ,那么你必須重寫BCD - 你最容易用git rebase -i做到這一點。 創建branchC並移動branchD你可以說

git rebase -i branchD^ branchC

這里branchD^是“ A之前提交的可能名稱。如果有這樣的提交,它可能有其他名稱 - 也許是master ,或者肯定是它的提交ID。如果沒有這樣的提交,那么我想你可以說

git rebase -i --root branchC

(但在那種情況下,嘗試從branchC歷史中刪除A可能沒有任何意義,所以我懷疑這是正在發生的事情)。

rebase命令將打開一個帶有TODO列表的文本編輯器,其中包含每個提交的條目。 您可以從列表中刪除提交A的條目,然后保存並退出。 這不會打擾branchD - 它仍將指向A - 但它將通過復制BCDbranchC創建新的歷史記錄。 所以你會的

x -- B' -- C' -- D' <--(branchC)
  \
   A <--(branchD)

無論如何,做你想做的事就意味着從branchD的歷史中刪除BCD 你提到這些提交沒有被push ,所以應該沒問題; 但這是一個要記住的重要區別。 每當您想要從分支的歷史記錄中刪除提交時,如果遠程分支尚不知道有問題的提交,則更容易做到。

您可以使用:

git checkout hashOfCommitA

然后

git checkout -b NewBranchName

現在你有了提交A的新分支。然后你可以回去提交D.

git checkout nameOfFirstBranch

然后從此分支恢復提交A.

git revert hashOfCommitA

現在你必須分支其中一個只有提交A而另一個是提交b,c和d。

最直接的方法是在commit-A之前創建2個新的分支。 然后,櫻桃挑選提交-A到1分支,櫻桃選擇提交B,C,D到第2分支。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM