我幾天前從主服務器分支出來,並且已經對newbranch
進行了兩次後續提交。從兩次提交之前的Git回滾
後來,我注意到我做的第一個提交中的文件不應該被改變。我還沒有推動任何東西到主分支,我所有的更改都在newbranch
。當我最初從主人分支時,我怎樣才能將newbranch
中的這一個文件回滾到原來的位置?
我幾天前從主服務器分支出來,並且已經對newbranch
進行了兩次後續提交。從兩次提交之前的Git回滾
後來,我注意到我做的第一個提交中的文件不應該被改變。我還沒有推動任何東西到主分支,我所有的更改都在newbranch
。當我最初從主人分支時,我怎樣才能將newbranch
中的這一個文件回滾到原來的位置?
有幾種不同的方式做你想要的結果,這取決於。
的answer from euphoria83做到這一點...
在開始時,你有一個提交歷史看起來像這樣(每個字母代表一個承諾):
A - B - C <-- master
\
D
\
E <-- HEAD=newbranch
提交C
是什麼在主站時你開始了。您做了一些更改並提交(創建D
),然後進行一些更改並提交(創建E
)。
現在您運行git checkout HEAD~1
。這將更新你的工作目錄,以匹配提交d,併爲您提供一個指向直接提交D
,而不是包含一個分支名稱的「分離的頭」(法國大革命的陰影!):
A - B - C <-- master
\
D <-- HEAD
\
E <-- newbranch
(這git checkout <branch-name-or-rev>
告訴git更改什麼HEAD
名稱<rev>
在這種情況下,HEAD~1
,意思是「查找什麼提交HEAD名稱並返回一個」,所以這表示要從提交E
回退一個,因爲這是一個特定的<rev>
而不是分支名稱,它也具有「分離頭部」效果。)
現在您運行git checkout HEAD~1 filename
。要更明確地說git,你可以說git checkout HEAD~1 -- filename
。 --
表示「剩餘的參數是文件名,而不是分支或修訂名稱」 - 如果您忽略了--
,git會嘗試猜測它是分支名稱還是文件名。
這種形式的checkout
,與-- filename
說法,做了相當不同:它說來查找修訂HEAD~1
像往常一樣,不過這一次,不要變化HEAD
,只提取指定的文件。由於HEAD
現在命名爲修訂D
,因此git會備份一份,修訂爲C
,並從該修訂中提取文件filename
的版本並將其放入工作目錄中。
(請注意,分支名master
也名稱修訂C
,所以你可以在這一點上已經寫git checkout master -- filename
。)
接下來,git commit -a --amend
告訴GIT中添加任何文件的改變,在這種情況下,你不實際上並不需要它,但通常這會添加其他您已修復的文件 - 然後執行「修改提交」。「修改提交」的意思是「創建一個新的提交,但使其父提交與我們的父提交相同」。這會創建一個新的提交 - 我們稱之爲D'
,其父代爲C
。與往常一樣,新的提交變爲HEAD
。由於HEAD
已分離,因此分支名稱尚未移動。得到的承諾樹是這個樣子:
A - B - C <-- master
| \
\ D
\ \
\ E <-- newbranch
\
D' <-- HEAD
在D'
的文件是相同的D
,除了文件filename
,也就是現在的相同版本C
。
接下來,git cherry-pick newbranch
說,以獲得修訂通過newbranch
- 這就是修訂E
命名 - 和作出HEAD版本同樣的變化,作爲一個新的提交(我們稱之爲E'
):
A - B - C <-- master
| \
\ D
\ \
\ E <-- newbranch
\
D' - E' <-- HEAD
現在你只需要給分支名稱HEAD
。這是再安全地刪除newbranch
,或者將其重命名閃開,然後重命名newnew
到newbranch
:
git checkout -b newnew # now HEAD=newnew which points to E'
git branch -m newbranch oldnew # rename out of the way
git branch -m newnew newbranch # and rename newnew to newbranch
(您可以縮短這一點,但我們上的去...)
有一個更簡單的方法來做同樣的事情。只需運行git rebase -i master
。這表示使用master
作爲其「上游」來重新分配當前分支(newbranch
)。也就是說,找到master
(意思是D
和E
)之後的所有提交,並將這些提交到master
(這是他們之前的基礎)。如果沒有-i
,那麼這將是愚蠢的重組D
和E
到他們以前的方式,這只是一個無所事事的昂貴的方式 - 但與-i
git打開了一系列命令的編輯器。命令將會提交pick
提交D
和E
。將pick
的提交D
更改爲edit
,並寫出該文件。
rebase會挑選D
給你,停下來讓你修改提交。在shell中,輸入:
git checkout master -- filename
git commit -a --amend
和以前一樣,並保存修改後的提交,即成爲D'
。然後運行:
git rebase --continue
的Git現在摘櫻桃提交E
,給人提交E'
。現在全部都完成了,所以分支完成,並且分支newbranch
有你想要的提交。
answer from ДМИТРИЙ МАЛИКОВ做了一些不同的事情。它包含相同的git checkout master -- filename
(拼寫filepath
),但沒有類似rebase的序列。
所以,像以前一樣,在開始使用此:
A - B - C <-- master
\
D
\
E <-- HEAD=newbranch
那麼你提取的filename
正在修正C
到工作目錄中的版本。如果你現在使用git commit
(或者git commit -a
,在這個特殊情況下是同樣的東西-git,這是因爲上面的checkout
首先將提取的rev-C文件寫入臨時區),你會得到一個新的提交F
,它將文件filename
改回它的方式是在C
。
換言之,提交D
和E
仍將對文件進行更改;新提交F
將撤銷更改。
git checkout master -- filepath
在new_branch:
git checkout HEAD~1
git checkout HEAD~1 file_not_needed
git commit -a --amend
git cherry-pick new_branch
git branch -f new_branch HEAD
這會讓你看起來像一個分支的「分離的HEAD」,所以你可能需要再多一步,給這個新分支命名。 – torek
@torek完成,謝謝 – euphoria83
哇!感謝您的解釋。 – larryq