选择合并之外的提交

这是我的情况:

我试图在我最近的提交之前放置一个早期提交的确切副本。 然而,

  • 我已经推动到远程,所以不想简单地git reset --hard (或者,如果我能以某种方式,然后将最近的提交添加到最新的提交之前,我会很乐意这样做)
  • 在我最近的提交和期望的提交之间,我创建并合并了一个分支,这似乎给我带来了问题/冲突(否则会做回复)
  • 我试过的任何解决方案似乎都保留了其他分支的额外更改,我不想要这些更改(我希望我的副本看起来像早期提交的签出)
  • 我知道这可能是一个简单的问题,但经过大量搜索/试验和错误后,我仍然无法找到解决方案。 任何帮助将不胜感激。

    如果您想知道为什么我想要这样做,最初的动机是为了轻松使用git-latexdiff来比较由合并分隔的两个版本。

    重现这种情况/看一个例子:

    为了使这种情况可以重现,下面的命令将创建一个名为tryrevert的文件夹, tryrevert具有必要的结构:

    mkdir tryrevert; cd tryrevert; git init; echo 'line 1 commit 1' > file1.txt; git add .; git commit -m 'commit 1'; echo 'line 2 commit 2 only want these two lines' >> file1.txt; git add .; git commit -m 'commit 2 - WANT THIS ONE'; git branch sidebranch; git checkout sidebranch; echo 'another file - only in sidebranch' > sidefile.txt; git add .; git commit -m 'commit 3 - sidebranch work'; git checkout master; echo 'extra work' >> file1.txt; git add .; git commit -m 'commit 4 - some more work on master'; git merge sidebranch -m 'commit 5 - the merge'; echo 'final work after the merge' >> file1.txt; git add .; git commit -m 'commit 6 - latest commit '
    git log --graph --all --decorate --oneline
    

    并输出以下提交树(不包括origin/master ):

    * 7c4c7a1 (HEAD, origin/master, master) commit 6 - latest commit
    *   5b0077c commit 5 - the merge
    |
    | * 20a1164 (sidebranch) commit 3 - sidebranch work
    * | 3a5a24f commit 4 - some more work on master
    |/
    * 925228b commit 2 - WANT THIS ONE
    * 52af39e commit 1
    

    (我离开sidebranch地方,而不是为了清晰起见将它移动到master 。)

    所以我想要的是

    * abcdefg (HEAD, master) COPY OF commit 6 
    * hijklmn COPY OF commit 2
    * 7c4c7a1 (origin/master) commit 6 - latest commit
    *   5b0077c commit 5 - the merge
    |
    | * 20a1164 (sidebranch) commit 3 - sidebranch work
    * | 3a5a24f commit 4 - some more work on master
    |/
    * 925228b commit 2 - WANT THIS ONE
    * 52af39e commit 1
    

    笔记:

    i) sidebranch创建sidefile.txt ,并且我在恢复提交2时所做的所有努力最终都包含sidefile.txt (并且我不希望COPY提交2包含sidefile.txt )。 我也想避免在file1.txt处理合并冲突。

    ii)我尝试了rebase -icherry-pick ,但是没有成功(也许我做得不对) - 我一直遇到冲突。

    iii)如果我没有合并,像这样:

    git revert --no-commit master...HEAD~3; git add .; git commit -m 'reverting to commit 2'
    

    会为我工作,但合并似乎停止工作的确切路线。

    iv)我花了很多时间在stackoverflow上查看很多帖子,但是找不到处理这个特定场景的帖子......

    编辑:

    (为长时间编辑事先道歉,但想澄清两次提交的状态)

    为了回应@胡安的问题,并说清楚,提交6包含

    file1.txt

    第1行提交1
    第2行提交2只需要这两行
    加班
    合并后的最后工作

    sidefile.txt

    另一个文件 - 只在sidebranch

    另一方面,在提交2中file1.txt只包含前两行:

    第1行提交1
    第2行提交2只需要这两行

    并且在提交2中没有 sidefile.txt

    @ Juan的答案适用于恢复/樱桃采摘file1.txt ,但创建的提交仍包含sidefile.txt (我不想)。

    玩过“他们的”,“我们的”和其他国旗后,以下帖子提供了一个解决方案:

    git checkout master~1^1~1
    git checkout -b commit2branch
    git merge -s ours master -m 'revert to commit2'
    git checkout master
    git merge commit2branch
    git branch -D commit2branch
    

    它可以使文件处于正确的状态(正确的file1.txt ,没有sidefile.txt但是提交历史记录包含一个额外的分支:

    *   4236c61 (HEAD, master) revert to commit2
    |
    | * 7c4c7a1 (origin/master) commit 6 - latest commit
    | *   5b0077c commit 5 - the merge
    | |
    | | * 20a1164 commit 3 - sidebranch work
    | |/
    |/|
    | * 3a5a24f commit 4 - some more work on master
    |/
    * 925228b commit 2 - WANT THIS ONE
    * 52af39e commit 1
    

    虽然我不想要这个额外的分支......

    最终编辑

    我有一个答案,我已在下面发布,并基于@ Juan的第二个解决方案。 但是,该解决方案使用了多次恢复,而我真的很喜欢单线解决方案


    首先,我运行脚本来生成回购,我会在这里发布它,并在答案中使用我的提交以避免复制/粘贴/更改错误:

    * f2cb927 (HEAD, master) commit 6 - latest commit
    *   93e6ede commit 5 - the merge
    |  
    | * 61f0cfe (sidebranch) commit 3 - sidebranch work
    * | c1d2916 commit 4 - some more work on master
    |/  
    * 79570e8 commit 2 - WANT THIS ONE
    * 5e7f2fb commit 1
    

    然后有不同的可能性:
    首先:结束你的第二张图,你可以用两个樱桃选择,第一个是提交2,然后是第六个提交。这将会产生冲突,并且当你在同一分支历史记录中提交一个提交时,你可能会想要在冲突情况下使用他们的变更,所以假设您在master签出您可能会想要这样做:

    $ git cherry-pick --strategy=recursive -X theirs 79570e8  
    

    之后对于提交6来说也是一样的:

    $ git cherry-pick --strategy=recursive -X theirs f2cb927
    

    有了这个,你会最终得到一个图表,只要你想:

    * f6e3ff7 (HEAD, master) commit 6 - latest commit
    * 7563975 commit 2 - WANT THIS ONE
    * f2cb927 commit 6 - latest commit
    *   93e6ede commit 5 - the merge
    |  
    | * 61f0cfe (sidebranch) commit 3 - sidebranch work
    * | c1d2916 commit 4 - some more work on master
    |/  
    * 79570e8 commit 2 - WANT THIS ONE
    * 5e7f2fb commit 1
    

    但我正确理解这不是你想要的,因为回购将包含File1:

    第1行提交1
    第2行提交2只需要这两行
    加班
    合并后的最后工作

    和File2:

    另一个文件 - 只在sidebranch

    Git会考虑分支中包含的每一个提交,优先考虑较新的提交,但是樱桃选择不会'撤销'从分支提交,为此目的,您需要使用还原。
    第二种可能性:这很复杂,因为需要以某种方式处理冲突,并且在恢复事情时这可能是混乱的。 那么,我在提交5中创建了一个名为'new'的新分支:

    $ git checkout 93e6ede
    $ git branch new
    $ git checkout new
    

    并从中跑出来:

    $ git revert c1d2916
    $ git revert 61f0cfe
    $ git revert -m 1 93e6ede
    

    我结束了以下树(注意分支):

    * 9eeac5c (HEAD, new) Revert "commit 3 - sidebranch work"
    * e1f3678 Revert "commit 4 - some more work on master"
    | * f6e3ff7 (master) commit 6 - latest commit
    | * 7563975 commit 2 - WANT THIS ONE
    | * f2cb927 commit 6 - latest commit
    |/  
    *   93e6ede commit 5 - the merge
    |  
    | * 61f0cfe (sidebranch) commit 3 - sidebranch work
    * | c1d2916 commit 4 - some more work on master
    |/  
    * 79570e8 commit 2 - WANT THIS ONE
    * 5e7f2fb commit 1
    

    只有'新'的文件1:

    第1行提交1
    第2行提交2只需要这两行
    然后,如果您还想提交6而无需修改历史记录,那么您现在需要在提交6后添加工作,以便正确处理冲突,这是最后一项:

    $ git rebase f2cb927
    

    这里除了处理冲突和手动选择你想要的东西之外没有其他的解决方案。

    第三种可能性,也许最好的看到第二是多么混乱:使用不同的分支(查看更改),说new2,在提交2结帐。然后简单地做:

    $ git cherry-pick f2cb927
    

    在这里你可能会有冲突,但它们应该是非常直接的解决方案,就像你在实际提交内容6时想要的一样。一旦冲突得到解决:

    $ git add files
    $ git cherry-pick --continue
    

    那么你有三种可能性的最终树会是这样的:

    * 2ded558 (HEAD, new2) commit 6 - latest commit
    | * ec8ef14 (new) Revert "commit 3 - sidebranch work"
    | * 9f8bb91 Revert "commit 4 - some more work on master"
    | | * f6e3ff7 (master) commit 6 - latest commit
    | | * 7563975 commit 2 - WANT THIS ONE
    | |/  
    | * f2cb927 commit 6 - latest commit
    | *   93e6ede commit 5 - the merge
    | |  
    | | * 61f0cfe (sidebranch) commit 3 - sidebranch work
    | |/  
    |/|   
    | * c1d2916 commit 4 - some more work on master
    |/  
    * 79570e8 commit 2 - WANT THIS ONE
    * 5e7f2fb commit 1
    

    只要去你喜欢的选择,你显然不需要他们三个。 假设你想要你需要强制推git push --force origin branchName。 这样做以前的任何其他操作都会丢失,并由推送的新数据取代。


    感谢@Juan-- 他的第二个解决方案可以被重写为:

    git checkout master
    git revert --no-commit HEAD...HEAD~1
    git revert --no-commit -m 1 HEAD~1
    git revert --no-commit --strategy=recursive -X theirs HEAD~1^1
    git commit -m 'COPY of commit 2 using revert'
    

    与:

    git revert --no-commit HEAD
    git commit -m 'COPY of commit 6 using revert'
    

    这会产生所需的提交树:

    * a30d0b1 (HEAD, master) COPY of commit 6 using revert
    * bcbd36f COPY of commit 2 using revert
    * 7c4c7a1 (origin/master) commit 6 - latest commit
    *   5b0077c commit 5 - the merge
    |
    | * 20a1164 commit 3 - sidebranch work
    * | 3a5a24f commit 4 - some more work on master
    |/
    * 925228b commit 2 - WANT THIS ONE
    * 52af39e commit 1
    

    但是, 我希望有一种方法可以在一行中做到这一点......

    链接地址: http://www.djcxy.com/p/94813.html

    上一篇: pick to a commit beyond a merge

    下一篇: Git: change HEAD