Create new Pull Request from a branch on a reverted changes to develop branch

Viewed 32

I have a weird issue or I just cannot understand it at the moment . I had some 5 PRs merged into develop (from feature branches) but needed to be merged in another branch (ex: release/release1 - branched from develop) .

So I reverted the develop branch (created a new commit) with the last known commit pre-MERGE by doing this :

# Create a backup of master branch
git branch backup_master

# Point master to '56e05fce' and
# make working directory the same with '56e05fce'
git reset --hard 56e05fce

# Point master back to 'backup_master' and
# leave working directory the same with '56e05fce'.
git reset --soft backup_master

# Now working directory is the same '56e05fce' and
# master points to the original revision. Then we create a commit.
git commit -a -m "Revert to 56e05fce"

# Delete unused branch
git branch -d backup_master

Now I need to recreate those PRs but it seems commits are both in the feature branches and develop one due to merges I guess , so basically there is no change , even though files are totally different.

How can I address this , maybe removing commits from history .. I dont know what to do .

Thanks

PS : I can think of creating new branches from develop and cherry-pick commits of every PR one by one and recreate PRs since cherry-pick changes SHAs due to parent change . Is this a good approach ?

1 Answers

Now I need to recreate those PRs but it seems [all the] commits [I want to merge] are [already] both in the feature branches and develop one due to merges ...

That's correct: merge is about combining work since a common starting point and there is no new work since the common starting point.

You can view it like this. Remember that in the drawing below, older commits are to the left and newer commits are towards the right, and the set of commits that is "in" any given branch name is all the commits Git can find by starting from the branch name and working backwards (i.e., leftwards):

                 E--F--G   <-- feature1
                /       \
 ...--o--o--o--D---M1----M2--U   <-- mainline
          \       /
           A--B--C   <-- feature2

When Git made merge M1, the picture looked like this:

 ...--o--●--o--D   <-- mainline
          \
           A--B--C   <-- feature2

The commit I filled in to make a solid bullet ● character is the merge base: the common starting point. Git compares the snapshot in ● to that in D to see what changed on the main line, and compares the snapshot in ● to that in C to see what changed on the feature branch. Git then combines these changes, applies the combined changes to the ● snapshot, and uses that to make merge commit M1 to add to the main line (which you first switch to before running git merge, even if you do this using the GitHub clicky web interface):

 ...--o--●--o--D---M1   <-- mainline
          \       /
           A--B--C   <-- feature2

Since commit C is now "on" the main line branch—Git can get to it by starting at M1 and working backwards (leftwards) and down, in the same way that Git can work backwards across to D—the common starting point for feature2 and mainline is now commit C. There is no new work here.

Adding feature1 back into the drawing, though, we see that the common starting point for commits M1 and G is commit D, so Git can merge commit G into commit M1 and update the name mainline to produce new merge commit M2:

                 E--F--G   <-- feature1
                /       \
 ...--o--o--o--D---M1----M2   <-- mainline
          \       /
           A--B--C   <-- feature2

You then used the somewhat tricky reset-and-then-reset-soft sequence to copy some old commit's snapshot—perhaps, e.g., the leftmost o snapshot, or perhaps D's snapshot—into a new commit U that "undoes" the two merges, at least in terms of the set of files stored in each commit:

                 E--F--G   <-- feature1
                /       \
 ...--o--o--o--D---M1----M2--U   <-- mainline
          \       /
           A--B--C   <-- feature2

But the merge base of mainline and feature1 is still G, and the merge base of mainline and feature2 is still C, i.e., those two features are still merged.

Note that if you picked the leftmost o snapshot to use in U, you also undid two more os and D, in effect. As far as Git is concerned, whatever is in any given commit's snapshot is the right snapshot for that commit. So whatever you put in commit U's snapshot, that's the right snapshot. If you want a different snapshot, you must construct it in some way.

How can I address this , maybe removing commits from history ...

You can force the name mainline back to point to commit D, or M1, or M2. All the existing commits continue to exist while you do this and if they have been copied to other Git repositories, those other Git repositories can send them back to your own Git repository. As Git is generally greedy for new commits, it's far too easy to wind up with these commits getting back in. That's why we don't "remove commits from history" like this very often: they're not really gone unless they're gone from every clone, and that tends to be very hard to achieve unless you have exquisite control over all clones.

I can think of creating new branches from develop and cherry-pick commits of every PR one by one and recreate PRs since cherry-pick changes SHAs due to parent change . Is this a good approach ?

In fact, git rebase—which works by repeatedly cherry-picking commits—has an explicit option to do exactly this. This option has several spellings:

--no-ff
--force-rebase
-f
      Individually replay all rebased commits instead of fast-forwarding over the unchanged ones. This ensures that the entire history of the rebased branch is composed of new commits.

You may find this helpful after reverting a topic branch merge, as this option recreates the topic branch with fresh commits so it can be remerged successfully without needing to "revert the reversion" (see the revert-a-faulty-merge How-To for details).

Follow the link and read the "how-to" document. Think about what git merge does and what you want people to see when they view the history (i.e., the commits that they can find by having Git start at the end and work backwards). This will lead you to the answer you prefer, which may not be the same as answers others prefer. Git provides tools, not solutions; you must choose which tools you like, and how to use them, to achieve the results you want to get.

Related