0

Here is my structure:

* - commit 2
|
* - commit 1
|
* - Merge commit (feature #1)
|\  
| * - Feature #1 commit 
|/
*

I messed up previously and now my history doesn't look as clean as I'd like.

I'd like to know if there is a simple way to squash 'commit 1' and 'commit 2' into 'Merge commit (feature #1)', so that I end up with something like this:

* - Merge commit (feature #1)
|\  
| * - Feature #1 commit 
|/
*

rebase -i doesn't seem to recognise the merge commit, and seems like it will squash everything into 'Feature #1 commit', which is not desirable.
Thanks

zing
  • 561
  • 3
  • 12
  • 2
    I have to question whether the result is desireable. Why would you want to have additional modifications in the merge commit that are not in the "Feature #1 commit"? – j6t Nov 25 '20 at 07:53
  • I agree with @j6t wholeheartedly. Why do you make efforts to build a lie for your future self (if not coworkers)? History will not reflect what really happened. You could have specific reasons, though. No judgement here, only trying to understand. – Romain Valeri Nov 25 '20 at 08:08
  • I do agree with you guys, it seems somewhat of a cover up. In this specific case I am the only one currently on the repo (busy preparing it for use for others) and just for my sake would like to have the history as clean as possible. The changes in the two commits are not significant or tied to any specific piece of work, so I just thought if it's not too complex I'd just squash them. – zing Nov 25 '20 at 08:25

1 Answers1

3

Make sure that you do not have anything staged (git added). Then just do

git reset --soft HEAD~2       # go back across the two commits
git commit --amend            # squash into the merge commit

git reset --soft does not change what you have in the working directory nor what you have staged. Therefore, if you have git added content after you made "commit 2", then the git commit --amend would commit those staged content as well. For this reason, you have to make sure that you have nothing staged when you begin. git status should tell you whether that is the case.

j6t
  • 9,150
  • 1
  • 15
  • 35
  • extra stress on "make sure you have nothing staged" or in any form modified since commit#2. `git stash` or `git checkout -- .` before starting are an option worth considering before beginning the squash. Also, this is not a squash in the strict sense: you are rewriting history. Now, with a squash I am [certain](https://stackoverflow.com/questions/35703556/what-does-it-mean-to-squash-commits-in-git) that the 3 "old" commits are preserved. With a git reset I am not so sure, [ref](https://git-scm.com/docs/git-reset) – Daemon Painter Nov 25 '20 at 08:12
  • @DaemonPainter Of course, squashing *always* rewrites history. That is no different in the case that you refer to. The pretty pictures just do a good job of hiding that fact. – j6t Nov 25 '20 at 08:16
  • they both rewrite history, but squash preserves knowledge in the repository of the SHA of the old commits, I'm not so sure about reset. – Daemon Painter Nov 25 '20 at 08:19
  • So, in my above example I mentioned that squashing *appeared to squash the 3 commits into one commit at the point where it was committed to the feature branch (ie. before the merge commit). Does that mean I would end up with a linear history with a single commit on the main branch? Or does it preserve the fact that the 3 commits where "on a separate branch" and then merged with a merge commit. – zing Nov 25 '20 at 08:33
  • By the way, thanks for the help @j6t. Exactly what I wanted. – zing Nov 25 '20 at 08:34
  • @Zingers If you squash all 3 commits together, you typically get a single commit. If I guess right, it would be a commit on the main branch. The new commit does not record that it is the result of a squash or what branch it originated from, except when you write it into the commit message yourself. – j6t Nov 25 '20 at 09:03
  • @DaemonPainter No, a squash operation does not record the identity of the original commits (neither when made by `commit --amend` nor when made by `rebase -i`). The original commits remain in the repository, of course, until they are garbage collected, typically after a few weeks. – j6t Nov 25 '20 at 09:07