0

Sometimes I squash a bunch of commits together. Usually my commands follow this pattern:

git reset --hard HEAD~4
git merge --squash HEAD@{1}
git commit

Today, though, at step 2, I got distracted by something (squirrel? Slack? Facebook?) and I forgot to end the git merge --squash command with HEAD@{1}. Yet it magically did what I wanted.

I can't find any documentation that omitting the target of the merge will default to HEAD@{1}, but it was nice that it worked out.

Is there documentation on this somewhere?

I see the following from git help merge, but it doesn't seem to answer the question:

--squash, --no-squash
           Produce the working tree and index state as if a real merge happened (except for the merge information), but do not actually make a commit, move the HEAD, or record $GIT_DIR/MERGE_HEAD (to cause the next git commit command to create a merge commit). This
           allows you to create a single commit on top of the current branch whose effect is the same as merging another branch (or more in case of an octopus).

           With --no-squash perform the merge and commit the result. This option can be used to override --squash.

           With --squash, --commit is not allowed, and will fail.
Kirby
  • 15,127
  • 10
  • 89
  • 104
  • 1
    Side note: `git reset --soft HEAD~4; git commit` will do it, you're just changing ancestry of your current checkout. – jthill Feb 02 '22 at 20:28
  • I agree with jthill; see my essay https://stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard/59675191#59675191 – matt Feb 03 '22 at 02:36
  • Thank you! `--soft` it is for me from now on. my mind is blown. I was a little leery of asking this question as I thought there must be a clear answer somewhere else, but I couldn't find it. – Kirby Feb 03 '22 at 16:38
  • i guess the difference is that `git merge --squash` creates a nice commit message about what you included – Kirby Feb 10 '22 at 23:19

1 Answers1

1

Running:

git merge

(with no options and no arguments) "means":

git merge @{upstream}

Adding options, such as --squash, does not change this, so:

git merge --squash

is a synonym for:

git merge --squash @{upstream}

The @{upstream} syntax means locate the commit pointed-to by the upstream of the current branch. The upstream of master is pretty typically origin/master. The upstream of dev is pretty typically origin/dev. This pattern repeats. The actual upstream can be found with git rev-parse:

git rev-parse --symbolic-full-name @{upstream}

which for instance in my Git clone of the Git repository for Git produces:

$ git rev-parse --symbolic-full-name @{u}
refs/remotes/origin/master

i.e., I'm on master and its upstream is origin/master. (This particular clone is primarily a reference clone for me, so I just keep its master in sync with the other various duplicates of the Git repository for Git. @{u} is a shorter way to write @{upstream}: less mnemonic, but easier to type in.) Whatever commit @{u} meant, that's the one that Git squash-merged with.

As jthill noted in a comment, you should consider git reset --soft for your particular goal. The soft reset moves the current branch, without touching the index and working tree, which avoids doing the work that git merge --squash would have to perform. The resulting index-and-work-tree will be the same either way, and since --squash does not create .git/MERGE_HEAD (and does imply --no-commit) the final git commit will behave the same way as well.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Cool, thanks! `--soft` for the win. my mind is blown. I was a little leery of asking this question as I thought there must be a clear answer somewhere else, but I couldn't find it. And I also learned that you can hyperlink comments in stackoverflow! Double win. – Kirby Feb 03 '22 at 16:37