我正在使用gitflow分支模型进行开发。
我已从develop
分支到feature/X
,在feature/X
的第一次提交中,我也删除了一些文件(使用git rm <file>
)。
现在,经过该分支的其他几次提交后,我意识到我需要先前删除的文件。
这里有一个简短的样本来说明我的澄清意思:
git flow init -d
echo "file contents" > file.txt
git commit -m "Added file.txt"
git checkout -b feature/X
git rm file.txt
echo "foo" > foo.txt
git add --all
git commit -m "Deleted file.txt and added another file"
<some more commits in feature/X>
git log -u
...
commit 04948fc4fc36d83901a0862b057657f3ccb9cf0d
Author: <...>
Date: Wed Aug 10 12:26:58 2016 +0200
Deleted file.txt and added another file
diff --git a/file.txt b/file.txt
deleted file mode 100644
index d03e242..0000000
--- a/file.txt
+++ /dev/null
@@ -1 +0,0 @@
-file contents
diff --git a/foo.txt b/foo.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/foo.txt
@@ -0,0 +1 @@
+foo
...
我不想仅在新提交中重新添加该文件,以避免在将feature/X
合并到develop
时出现问题,而file.txt
中的develop
发生了一些变化file.txt
分支。
有没有办法从早先的提交中删除git reset <sha-of-previous-commit> file.txt
?
我已尝试feature/X
,但未将该文件带回我的工作副本。
编辑1:
我知道已经被推到遥远的重写历史的缺点。但是我知道除了我之外没有人对git diff --diff-filter=D --summary
做过任何提交,因此尽管它已被推送,但我应该安全地重写历史记录。
答案 0 :(得分:1)
你现在拥有的是某个分支上的一系列提交:
...--o--*--B1--B2--B3 <-- branch
其中*
是一个很好的提交,但B1
是一个“错误的”提交(已经删除了你决定的文件,现在,你根本不想删除)。
你需要将糟糕的提交作为良好的提交重新执行 - 并且还要复制之后 之后的所有好提交糟糕的提交。换句话说,您将基本上根据需要重复以下三个步骤:
将您不喜欢的提交复制到新提交中(但实际上还没有提交它)。从将从标记为*
的提交增长的新分支开始,使用git cherry-pick -n
复制像B1
这样的错误提交,但不实际提交它。
然后,恢复丢失的文件:git checkout -- <path>
。 (您也可以使用git reset
执行此操作,但是当我们使用下面的git rebase -i
时,它会变得太难,所以我们会坚持使用git checkout
。)
最后,将新提交作为旧提交的副本:git commit
。由于这是一个樱桃选择,Git将准备好旧提交的消息进行编辑。
现在第一个错误提交B1
已被复制到一个新的,更正好的提交G1
:
...--o--*--B1--B2--B3 <-- branch
\
G1 <-- new branch [new copies being made]
现在您可以复制第二次提交B2
。如果它有您不想要的删除,请使用相同的三部分序列(cherry-pick -n
,checkout
,commit
)。如果没问题,请忽略-n
只复制它并提交:
...--o--*--B1--B2--B3 <-- branch
\
G1--G2 <-- new branch [new copies being made]
现在您可以复制第三次提交B3
。和以前一样,如果它有你需要改变的东西,你可以一路修复它。 (这假设B1--...--Bn
链中有三个提交;如果有更多或更少的提交,则根据需要进行调整。)
当一切都完成后,您需要说服Git从旧分支上剥离branch
标签并将其粘贴到 new 上。 是一种方法,但是......
git rebase -i
有一种更简单的方法。 git rebase
命令通过执行一系列樱桃选择来工作。 1 当樱桃选择全部完成时,git rebase
命令剥离旧分支标签,并粘贴它刚刚建立的新分支上的标签。这正是您需要做的,所以您要做的就是说服git rebase
告诉它哪些提交是好的提交,哪些是坏那些,并暂停在坏的。
这是git rebase -i
的用武之地。它会根据一组说明调出编辑器。最初,所有指令都是pick
:执行每个提交的樱桃选择。您可以将任何一条指令更改为edit
,告诉Git执行该特定的挑选,然后停止并让您修复/更改内容。
有点烦人的是,rebase
做了没有 -n
的樱桃选择:它制作副本并提交它。因此,您必须稍微调整第2步:而不是git checkout -- <path>
从HEAD
获取文件,您需要git checkout HEAD^ -- <path>
或git checkout HEAD~1 -- <path>
。这意味着“回到之前的提交,并获得该文件的版本。”完成后,您可以运行git commit --amend
来更新提交。
然后,使用git rebase --continue
继续rebase进程。 rebase代码将继续通过更多pick
s到下一个edit
,或者如果只剩下pick
,则完成所有关闭并完成rebase并移动分支标签。
主要是识别提交*
:最后一次“好”提交。以某种方式拼写提交*
可让您执行git rebase -i
。在我们的示例中,它是从当前分支提示返回的三步,因此:
git rebase -i HEAD~3
会做到这一点。如果退回的次数更多或更少,则需要在波浪号~
字符后调整数字。
请注意,无论您做什么,您都会获得原始提交的副本。此外,git rebase
通常在复制时删除合并提交。这两个意味着你必须谨慎,如果你已经向其他任何人提供这些提交(通过git push
或类似),或者你想要“替换”的第一个错误提交后合并(真的,复制然后忽略原件。
1 事实上,git rebase
有时会运行git cherry-pick
。其他时候,它使用其他几乎完全相同的东西。
答案 1 :(得分:1)
我不只是想在新提交中重新添加文件,以避免在开发分支中的file.txt中发生某些更改时合并feature / X进行开发时出现问题。
是的,您确实想在新提交中重新添加文件。 git是明智的(嗯,实际上这里没有特殊的情报,它只是来自git处理文件的方式)。
git init test # init a repos, create test.txt
cd test
echo start > test.txt
git add -A ; git commit -m 'x'
git checkout -b bra # create branch, add a line
echo line1 >> test.txt
git add -A ; git commit -m 'x'
git rm test.txt # delete file + commit
git commit -m "x"
echo start > test.txt # restore file and add a line
echo line2 >> test.txt
git add -A ; git commit -m 'x'
git checkout master # back to master, add the same line and another one
echo line2 >> test.txt
echo line3 >> test.txt
git add -A ; git commit -m 'x'
git merge bra # merge
cat test.txt
start
line2
<<<<<<< HEAD
line3
=======
>>>>>>> bra
冲突如预期(原文如此!);关于它的重要部分是line2
不是冲突的一部分。 git merge
并不关心发生在分支文件中的所有shenannigans,它只对最终结果感兴趣。
答案 2 :(得分:0)
Git不是SVN,如果您只是在新提交中重新添加文件,我认为不应该有任何问题。
与SVN不同,Git不跟踪文件,它跟踪恰好存储在文件中的内容
如果您在SVN中删除并重新添加文件,其历史记录未连接,但它们是SVN的不同文件。
如果您在Git中删除并重新添加文件,则只是删除并重新添加的内容,因此合并应该可以正常工作。一个rebase可能会有问题,因为提交被逐个应用于新的基本提交,因此您将有编辑/删除冲突。但是在合并时,所做的更改应该作为一个整体应用,因此不会出现问题。
答案 3 :(得分:0)
有两种方法可以恢复该文件,具体取决于是否可以安全地重写feature/X
分支的历史记录。
如果您已经推送了该提交,那么最简单的方法就是从删除它的提交的父级中检索foo.txt
文件并提交它试。
例如,假设删除文件的提交的SHA-1为123abc
:
git checkout feature/X
git checkout 123abc^ path/to/file.txt
git add path/to/file.txt
git commit -m "Restore the file.txt file"
另一方面,如果您没有推送这些提交,则可以安全地重写本地feature/X
分支的历史记录以撤消该文件的删除。为此,您必须执行interactive rebase并编辑删除文件的提交:
git checkout feature/X
git rebase -i 123abc^
在待办事项列表中,将该提交左侧的字词从pick
更改为edit
;然后保存文件并退出。
一旦Git到达您要编辑的提交,您可以通过以下方式恢复已删除的文件:
git checkout HEAD^ path/to/file.txt
git add path/to/file.txt
git commit --amend -C HEAD # where -C HEAD reuses the commit message of HEAD
然后用:
完成rebasegit rebase --continue