[英]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
這是一種更緊湊的繪制方式。 提交總是,必然,指向倒退 ,所以我們並不需要內部箭頭 - 我們知道它們總是倒退。 但是,分支名稱 ,如master
和branch-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
命令來復制B
, C
和D
; 或更簡單 - 這使用<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)
我想你想只落得A
在branchD
,並為一個新的分支B
, C
,和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
,那么你必須重寫B
, C
和D
- 你最容易用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
- 但它將通過復制B
, C
和D
為branchC
創建新的歷史記錄。 所以你會的
x -- B' -- C' -- D' <--(branchC)
\
A <--(branchD)
無論如何,做你想做的事就意味着從branchD
的歷史中刪除B
, C
和D
你提到這些提交沒有被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.