0

So let's say we have a master branch and a feature branch.

(master) M1 -> M2 -> M3 -> M4
                \
(feature)       M2 -> F1

Now what I want to do is merge all the changes in F1 into commit M2 (not after M4). I know rebasing F1 to master or merging into master will add F1 after M4. However, I want to amend the M2 commit with changes from F1.

Here is what I have tried already: rebasing to edit M2, cherry-picking F1 into M2, soft-reset to M1, commit, then continue rebase. This more or less worked but if there are conflicts between M3 /M4 and F1, then the behavior was a little weird (can't explain because I didn't understand it fully).

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • Has the `M2` commit been pushed to any remotes, or does it only exist locally on your machine? – Greg Burghardt Mar 25 '21 at 16:53
  • It will already be in remote (although changing history is not an issue in this case) – Gurpreet Singh Mar 25 '21 at 16:55
  • To give some more background, in my organization every project has only one commit and those commits are constantly changed. The edit history, however, is tracked in another tool called gerrit. I don't exactly why they opted for this but this is what I am working with. I was wondering if there is a clean way for me to branch out, then merge back all the changes in a nicer manner. Ideally, there shouldn't be any conflicts between the commit F1 and M3, but I would like to know how to do this assuming there could potentially be some conflicts. – Gurpreet Singh Mar 25 '21 at 16:59
  • Does this answer your question? [How do I squash two non-consecutive commits?](https://stackoverflow.com/questions/3921708/how-do-i-squash-two-non-consecutive-commits) – Joe Mar 26 '21 at 10:32

2 Answers2

0

You can check out M2, and amend that commit:

# Assuming you are on the tip of 'feature'

git tag feature_before_amending # Tag the F1 commit in case things go badly
git reset --hard M2             # Reset current branch pointer to M2
git checkout F1 -- .            # Restore changes from F1 as staged files
git commit --amend              # Amend the M2 commit with changes from F1
git push origin HEAD --force    # Push the new, amended commit

This assumes the F1 commit was good. If the F1 commit was bad, and you basically need to remerge it into M2, then creating a new branch followed by resetting the 'feature' branch should do the trick:

git checkout -b amended_feature M2 # Create a new local branch from M2
git merge --no-commit F1           # Merge the F1, but stage changes instead of committing
git commit --amend                 # Amend the M2 commit in 'amended_feature'
git checkout feature               # Check out the 'feature' branch again
git reset --hard amended_feature   # Reset the branch pointer of 'feature' to amended M2 commit
git push origin HEAD --force       # Finish by force-pushing the changes
Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92
  • Before trying this out, I am trying to understand how this would affect the commits M3 and M4? What if there are conflicts between F1 and, say, M3? Wouldn't this not allow us to resolve the conflict? – Gurpreet Singh Mar 25 '21 at 17:26
  • 1
    But don't do it with the `git checkout F1 -- .`, do `git cherry-pick -n F1` edit: or to get the same effect but quicker,`git read-tree -um F1`. The checkout . will miss deletions. – jthill Mar 25 '21 at 17:32
  • Oh. That means just adding the -n flag to the cherry-pick command in the method mentioned in my question would work. I will edit the response I posted. I think that's a cleaner way to achieve this. Thanks @jthill Edit: I will also look into read-tree. I haven't seen that command before – Gurpreet Singh Mar 25 '21 at 17:36
-1

I was able to achieve this in two steps rather than the one weird thing I mentioned in my question. Outlining the steps below -

  1. git rebase in order to edit M2
  2. git cherry-pick F1 within rebase
  3. git rebase --continue. If there are conflicts between F1 and M3/M4, you can solve them here and then continue rebase
  4. Once back to head, rebase again and squash the F1 commit onto M2

This requires two rebases but this works. Is there a way to achieve this in one clean rebase?

Edit: Based on the comments in the response by Greg Burghardt, I think the following way is an intuitive and simpler thing to do -

Assuming we are on head of master (M4), we do the following

git rebase -i M2^ (and edit M2)
git cherry-pick -n F1
git add .
git commit --amend
git rebase --continue (fix any conflicts and then run this again)
E_net4
  • 27,810
  • 13
  • 101
  • 139