Remerge after reverting failed merge

前端 未结 2 819
庸人自扰
庸人自扰 2021-02-10 19:50

I have two branches in repository: feature and master. I have merged master into feature and pushed result to remote feature branch:

g         


        
相关标签:
2条回答
  • 2021-02-10 20:20

    If you execute the git revert, it will undo the merge, so I do not think the feature contains necessary changes. I will goes to the state before merge only with some additional git log. So you can freely merge it. But, if you do not want the additional git log, you can use git reset.

    1. Check which is your wanted previous state:git log

    2. git reset {commit-id-founded}

    3. resolve your conflicts

    4. merge again

    0 讨论(0)
  • 2021-02-10 20:33

    If you have not published the bad merge and its reversion, you can remove them and publish a correct merge instead.

    If you have published (pushed or otherwise given out) the bad merge, your best bet is probably to work out a correct merge by creating a new branch starting from just before the bad merge. For instance, suppose the commit graph fragment looks like this:

    ...--i--j--m--w   <-- feature
              /
    ...---k--l        <-- master
    

    where merge commit m is the one that went wrong, and w (m upside down1) is the reversion of m. (Note: if you have a more complex history, you probably should use a different strategy; see the link in the footnote.)

    Here, the idea would be to check out commit j directly:

    git checkout <sha1-of-j>
    

    You are now in "detached HEAD" mode. At this point you can run a new git merge:

    git merge master
    

    This will (based on your mention of merge conflicts) stop with a merge conflict, since it's repeating the step that got you bad-merge-m. (If it won't stop on its own, add --no-commit to the merge command.)

    Now resolve the conflicts correctly this time :-) and add and commit as needed. This makes a new merge which I'll call M, and I will draw the new graph like this:

    ...--i--j------m--w   <-- feature
             \    /
              M  /        <-- HEAD
              | /
             / /
             |/
    ...---k--l            <-- master
    

    This new commit M is not (yet) on any branch, and in fact, you don't really need it to be on any branch: what you want is the tree you obtained at this point.

    Now we'll make this a new (but temporary) branch to remember the SHA-1 of commit M:

    git checkout -b temp
    

    (we could have done this earlier; you can do it at the "check out commit j" step if you like; but I have some other, un-tested, methods in mind that I'll outline below). Now let's get back on feature and make a new commit that uses M's tree, rather than that of m or w. There are several ways to do this, but I will illustrate this one since it's pretty simple:

    git checkout feature
    git rm -r .  # assumes you're in the top level of the work dir
    git checkout temp -- .
    

    The first of these, checkout feature, simply gets us back on branch feature. The second empties out the index (the "next commit")—this step is only necessary if M is missing some file(s) that are in m and w—and then the third extracts the entire tree from commit M into the index and work-tree.

    Now we're ready to commit the result:

    git commit -m "replace everything with corrected merge"
    

    The graph now looks like this:

    ...--i--j------m--w--n   <-- HEAD=feature
             \    /
              M  /           <-- temp
              | /
             / /
             |/
    ...---k--l               <-- master
    

    The files under commit n are the same as those under commit M. We no longer need commit M and branch temp at all, so we can simply delete them (git branch -D temp), giving:

    ...--i--j--m--w--n   <-- HEAD=feature
              /
    ...---k--l           <-- master
    

    If you're comfortable with using lower level git commands, there's a simpler (?) way to copy the tree from M to a new commit we'll put on feature. In particular we just need to make a new commit whose parent is w and whose tree is that of M. We can do that in one step while still on M and the anonymous HEAD, with git commit-tree:

    id=$(git commit-tree -p feature -m "message" $(git rev-parse HEAD^{tree}))
    

    Assuming this works (I haven't tested this particular form and you might have to use git rev-parse to convert the name feature to a raw SHA-1), we can then use git update-ref to make refs/heads/feature contain id $id:

    git update-ref -m "add corrected merge" refs/heads/feature $id
    

    after which it's safe to simply git checkout feature to get back on the (updated) branch.

    This being git, there are more ways to do it, e.g., when on the anonymous branch, you could do this:

    git symbolic-ref HEAD refs/heads/feature
    git commit -m "replace everything with corrected merge"
    

    which is probably simpler than the git commit-tree method (the commit-tree method is just what I thought of first, due to having recently written a complicated shell script that used commit-tree for a fancy repo shadowing thing). The way this works is that the symbolic-ref puts you back on branch feature but does not touch the index (nor work-tree) at all, so it/they still match the tree for commit M. Then we make a new commit in the ordinary way, using the current index; and since nothing remains to point to commit M, the garbage-collector will eventually delete that commit (but not the tree itself, which is now safely saved on branch feature).


    1The m and w thing is stolen directly from Linus Torvalds and Junio Hamano.

    0 讨论(0)
提交回复
热议问题