我们的仓库中有两个分支,release_1.18
和feature/remove-duplicate-media
。
feature/remove-duplicate-media ---> release_1.18
的拉取请求包含冲突。负责该功能的开发人员将release_1.18
合并回他的功能分支以解决它们(到目前为止很正常)。
但是,在这次合并之后,他以某种方式(我们无法弄清楚如何)抛弃了release_1.18
中的所有更改,并且只提交了导致冲突的两个文件。
现在,Pull Request feature/remove-duplicate-media ---> release_1.18
包含两个分支之间所有更改的所有OPPOSITE。因此,release_1.18
中添加的文件现已删除。
我们如何解决这个问题?再次合并release_1.18
将无济于事,因为git认为(这在技术上是正确的)已经完成了,并且没有什么可做的。
答案 0 :(得分:2)
您的意思是从<a href='http://www.domain.com/changepassword.php?user_id=$User_id1'>Create your password here</a>
到feature/remove-duplicate-media
的合并错误合并。
release_1.18
所以你可以通过以下方式删除这个合并:
A---B---C release_1.18
/ \
D---E---F---G feature/remove-duplicate-media
现在两个分支都将保持状态,就像合并之前一样。然后你可以再次合并。
答案 1 :(得分:1)
Marina - MSFT's answer - 完全放弃合并 - 是一个可能的答案,只有两个可能的答案。
这是因为,正如您在问题中所指出的那样,您无法获得Git to&#34; redo&#34;直接合并,因为它已经在历史中。更确切地说,错误的合并是历史。这意味着要 ditch (丢弃)错误合并,您必须重写历史记录。仍然,&#34;抛弃糟糕的合并&#34;是一个可能的答案。
但也有两个部分也是你问题的最终答案。一个是: 您更喜欢哪种提交历史备选方案? 有两种方法:沟通并用良好的合并替换坏合并,或者添加修复提交。第二部分是: 如何获得源代码树,即工作树快照,与良好的合并或修复提交一起? 它&#39 ;第二个问题更难(除非你选择&#34;用合并替换错误的合并&#34;方法并且愿意花一些时间去做)。让我们看看我们是如何到达那里的。
由于当前的任何内容都不依赖于错误的合并,并且#34;抛弃坏的合并&#34;目前,这是一个相对简单的答案。但这确实意味着你必须让任何选择错误合并的人,跟随你的&#34;重置它,然后用一个好的合并替换它#&# 34;通过用替换来更新他们自己的克隆。
再次绘制它,这里是我们在此建议的图形表示:
...--o-------X <-- feature/remove-duplicate-media
/
...--o--o--o
(其中X
是错误合并)变为:
...--o-----------G <-- feature/remove-duplicate-media
\ /
X /
/_/
...--o--o--o
即坏合并X
不再有任何标签指向它(之后它最终被垃圾收集掉)。任何有git fetch
错误合并X
的人都会将他们的存储库中的文件作为remotes/origin/feature/remove-duplicate-media
,但如果他们在进行新的提交之前又运行了另一个git fetch
使用提交X
,他们将获得良好的合并提交G
,移动他们自己的remotes/origin/feature/remove-duplicate-media
以指向良好提交G
,他们会没事的。< / p>
另一个可能的答案是在错误合并之后添加一个良好的提交(合并提交或普通的非合并提交,它赢得了&t; t真的很重要)。也就是说,我们添加了一个新的commit-let,而不是放弃提交X
,而是将其调用R
进行修复而不是G
进行修复,只是为了区分它 - 它的快照(它的树),应该具有在提交X
之后。假设我们将其作为普通提交,commit R
的图形表示现在看起来像这样:
...--o-------X--R <-- feature/remove-duplicate-media
/
...--o--o--o
或者,我们可以将R
本身作为合并提交,在这种情况下,图形表示如下所示:
...--o-------X--R <-- feature/remove-duplicate-media
/__/
...--o--o--o
将R
作为合并提交是否有充分的理由?不是真的:既不伤害也不帮助。这是一个关于您希望如何在git log
输出中看到它的问题。真正的诀窍是:我们如何获得提交R
的树?
如果您git reset
离开了错误提交X
,则很容易看到如何获得良好的合并提交G
:再次运行git merge
,这次正确解析合并,并提交。
但是,如果您不想放弃提交X
- 如果您想要或需要保留现有历史记录 - 您将如何获得用于修复提交R
的相同类型的树?
答案几乎非常简单,一旦你意识到Git是如何工作的:你以与提交R
相同的方式为修复提交G
创建树本身,通过重复合并。显然你必须在没有错误提交X
的分支上执行此操作,但这几乎非常容易搞笑!
让我们再次绘制G
个案,但只需更改一次:让错误合并X
到位,功能分支指向它。这一次,我还会标记另外两个提交,A
和B
:
...--A-----------G <-- temp
\ /
X / <-- feature/remove-duplicate-media
/_/
...--o--o--B
我们如何获得此临时分支temp
并进行此新合并G
?简单:只需检查指向提交temp
的新分支A
,然后合并提交B
。提交A
是错误合并X
的第一个父级,B
是错误合并X
的第二个父级,因此配方是:
$ git checkout -b temp feature/remove-duplicate-media^1
$ git merge feature/remove-duplicate-media^2
然后像以前一样解析并提交,我们有好的提交G
,作为分支temp
的提示。
现在我们只需要在分支G
上复制 R
到修复提交feature/remove-duplicate-media
。通常我们可能会使用git cherry-pick
来复制提交,但这不适合。有两种方法可以使用常规的旧Git命令或使用管道命令git commit-tree
进行复制。
管道命令实际上更容易,更灵活。它允许我们将G
复制到普通的单父提交或新的合并提交。要制作R
,我们准备一条日志消息:
$ cat > /tmp/log-msg
(write your log message here, ^D to exit cat)
或:
$ vim /tmp/log-msg
或者您想制作日志消息。现在在没有分支的情况下进行提交,就像悬挂的提交对象一样:
$ newid=$(git commit-tree -p feature/remove-duplicate-media \
> -F /tmp/log-msg temp^{tree})
或:
$ newid=$(git commit-tree -p feature/remove-duplicate-media \
> -p feature/remove-duplicate-media^2 \
> -F /tmp/log-msg temp^{tree})
然后将分支feature/remove-duplicate-media
快进到新提交:
$ git checkout feature/remove-duplicate-media
$ git merge --ff-only $newid
(注意:这可能会强制您删除一些当前未跟踪的文件,如果它们在更正的合并中被跟踪,尽管它有点不太可能)。
由于$newid
的第一个父级 - 可能是其唯一的父级,取决于您在上面使用的命令 - 是feature/remove-duplicate-media
的当前提示,因此合并将是快进的作为feature/remove-duplicate-media
的提示,您现在拥有通过复制临时提交而进行的新提交。现在您可以删除临时提交的分支名称:
$ git branch -D temp
temp
如果您只想将提交R
作为普通的单父提交,并希望使用&#34;普通&#34; Git命令,这是如何做到的:
$ git rev-parse --show-cdup
如果打印任意数量的&#34; ../" s,例如&#34; ../../../"或者某些这样的cd
,许多../
- es,以便重新运行命令只打印一个空行。 (这里的想法是达到顶级。)或者:
$ cd $(git rev-parse --show-toplevel)
会做同样的事情。然后:
$ git checkout feature/remove-duplicate-media
$ git rm -rf .
$ git checkout -f temp -- .
$ git commit
第一个git checkout
到达目标分支并填充索引和工作树。 git rm -rf .
删除索引中的所有内容 - 字面上的所有内容 - 以及所有相应的工作树文件。第二个git checkout
从临时合并中重新填充索引和工作树,撤消git rm -rf .
的效果(除了文件时间戳最终更新)。最终git commit
提交结果。
如前所述,我们可以解决temp
中HEAD
未跟踪的文件中的小问题。这是第二个-f
中git checkout
的用途。可能没有这样的文件,因此-f
是不必要的。您可以将其保留,如果您点击此类文件,请手动将其删除(或隐藏它们)。
有一种类似的方法可以避免稍微可怕(和时间戳改变)git rm -rf .
步骤,但它有点复杂。在首先删除当前提交中不在git checkout temp -- .
提交中的任何文件之后,我们可以使用temp
来更新所有内容,而不是删除所有内容:
$ git diff --no-renames --name-only --diff-filter=R -z HEAD temp | \
> xargs -0 git rm -rf --
$ git checkout -f temp -- .
(您可以在没有xargs
的情况下执行此操作,但是您需要跳过-z
并且可能会遇到包含嵌入式空白区域的文件名问题。请注意,如果文件名与temp
提交中的目录名相对应,我们必须首先执行此删除。
和以前一样,一旦将临时合并复制到已提交的修复提交R
,您就可以删除分支temp
。