Git还原/切利拾取到合并之外的提交

Git还原/切利拾取到合并之外的提交,git,merge,revert,cherry-pick,Git,Merge,Revert,Cherry Pick,这就是我的情况: 我正在尝试将先前提交的精确的副本放置在最近提交的副本之上。但是, 我已经推到远程,所以不想简单地git重置 --硬的(或者,如果我能以某种方式将先前的提交添加回最新的提交之上,我会很乐意这样做) 在最新提交和所需提交之间,我创建并合并了一个分支,这似乎给我带来了问题/冲突(否则将进行恢复) 我尝试过的任何解决方案似乎都保留了来自另一个分支的额外更改,这是我不想要的(我希望我的副本看起来与先前提交的签出完全相同) 我知道这可能是一个简单的问题,但经过多次搜索/尝试和错误,我仍然找

这就是我的情况:

我正在尝试将先前提交的精确的副本放置在最近提交的副本之上。但是,

  • 我已经推到远程,所以不想简单地
    git重置
    --硬的
    (或者,如果我能以某种方式将先前的提交添加回最新的提交之上,我会很乐意这样做)
  • 在最新提交和所需提交之间,我创建并合并了一个分支,这似乎给我带来了问题/冲突(否则将进行恢复)
  • 我尝试过的任何解决方案似乎都保留了来自另一个分支的额外更改,这是我不想要的(我希望我的副本看起来与先前提交的签出完全相同)
  • 我知道这可能是一个简单的问题,但经过多次搜索/尝试和错误,我仍然找不到解决方案。任何帮助都将不胜感激

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

    要重现此场景/请参见示例: 为了使此场景重现,下面的命令将创建一个名为
    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
    ):

    (为了清晰起见,我将
    侧分支
    留在了它所在的位置,而不是将其移动到
    主分支

    所以我想要的是:

    * 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
    ,我在恢复到commit 2时所做的所有努力最终都包含
    sidefile.txt
    (我不希望提交2的副本包含
    sidefile.txt
    )。我还希望避免在
    file1.txt
    中处理合并冲突

    ii)我曾尝试过
    重新设置基础-I
    cherry pick
    ,但没有成功(也许我做得不正确)-我不断遇到冲突

    iii)如果我没有合并,类似于:

    git revert --no-commit master...HEAD~3; git add .; git commit -m 'reverting to commit 2'
    
    这对我来说是可行的,但合并似乎阻止了这一行的工作

    iv)我花时间查看了许多关于stackoverflow的帖子,但找不到一篇能够处理这种特定场景的帖子

    编辑: (对冗长的编辑提前表示歉意,但希望澄清两次提交的状态)

    为了回答@Juan的问题,并且说清楚,commit 6包含

    file1.txt

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

    sidefile.txt

    另一个文件-仅在侧分支中

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

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

    并且提交2中没有
    侧文件.txt

    @Juan下面的回答对于还原/cherry picking
    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
    
    这在使文件处于正确状态方面是有效的(correct
    file1.txt
    ,no
    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的第二个解决方案。但是,该解决方案使用了多个还原,而我真的想要一个单行解决方案

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

    * 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,然后是提交6。这会有冲突,当您执行同一分支历史记录中包含的提交时,您可能希望在发生冲突时使用它们的更改,因此假设您在
    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
    
    但我正确理解这不是您想要的,因为回购协议将包含文件1:

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

    和文件2:

    另一个文件-仅在侧分支中

    Git将考虑分支中包含的每个提交,优先考虑较新的提交,但cherry pick不会“撤消”分支中的提交,为此,您需要使用revert。
    第二种可能性:这是一个复杂的过程,需要以某种方式处理冲突,在恢复内容时可能会很混乱。我在commit 5中创建了一个名为“new”的新分支:

    $ git checkout 93e6ede
    $ git branch new
    $ git checkout new
    
    我从中得出:

    $ git revert c1d2916
    $ git revert 61f0cfe
    $ git revert -m 1 93e6ede
    
    最后我得到了以下树(注意新建分支):

    仅在“新”文件1中具有:

    第1行提交1
    第2行提交2只需要这两行
    然后,如果您也希望提交6而不必修改历史记录,那么您需要在提交6之后添加您的工作,这样您就可以正确处理冲突,这是最后一个冲突:

    在这里,除了处理冲突和手动选择所需内容之外,并没有其他解决方案

    第三种可能也是最好的看到了第二种有多混乱:使用不同的分支(要查看更改),比如new2,在commit 2中签出。然后简单地执行以下操作:

    $ git cherry-pick f2cb927
    
    在这里,您可能会遇到冲突,但它们应该很容易解决,正如您在commit 6的实际内容中所希望的那样
    * 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
    
    $ git rebase f2cb927
    
    $ git cherry-pick f2cb927
    
    $ 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 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