18

Is there a way to squash and merge master into the same branch? Effectively taking all the pending commits and putting them into 1 commit?

My original idea is a script that takes my-branch and does a git checkout master && git pull && git checkout my-branch-squashed and then git merge --squash my-branch (deal with any merge conflicts) and then finally delete my-branch and rename my-branch-squash to my-branch

This seem very round-about and possibly bad, so I am trying to see if there is any other way. The intent I am trying to solve is that when I put branches on github and they are "squashed and merged" into master, the branch that exists on the local machine doesn't match the branch that was merged into master, so when using git branch --merged ${1-master} | grep -v " ${1-master}$" | xargs -r git branch -d; it doesnt correctly delete the branches that have already been merged into master. What I want is a way to auto-delete old branches that have been merged into master

CuriousDeveloper
  • 849
  • 2
  • 8
  • 27
  • 1
    `git rebase -i master` – Alexan Jan 05 '18 at 20:19
  • Possible duplicate of: [squash-my-last-x-commits-together-using-git](https://stackoverflow.com/questions/5189560/squash-my-last-x-commits-together-using-git). – pishpish Jan 10 '18 at 15:32
  • Shamelessly [quoting myself](https://stackoverflow.com/a/11797964/212858), a squash isn't really a merge. Why not just either a) squash your branch before pushing to github, or b) use an actual merge, rather than a "squash-and-merge"? – Useless Jan 10 '18 at 15:36
  • To confirm I'm understanding your question correctly, removing all speculation on a method, does this sum things up: You work on a project and the maintainers use the merge-squash method for accepting pull requests in their workflow. You have a lot of previously merged branches you want to get rid of, but don't know how to differentiate the merged ones from the unmerged ones? – LightBender Jan 13 '18 at 17:57
  • Yes, people are constantly squash and merging pull requests for all requests, even though a simple merge is enough. The solution is definitely to make them merge when there is only 1 commit, but that requires that I squash everything before the pull request, which is what I am trying to automate more. So @Useless I am trying to accomplish a) in a more automated way. LightBender exactly. But I understand the solution is to get people to merge and not merge-squash – CuriousDeveloper Jan 15 '18 at 16:05
  • 1
    You could reset the last x local commits and then force push them back to master: `git reset --soft HEAD~numOfCommits` – Kai Jan 17 '18 at 12:34

4 Answers4

8

You can do that using git rebase, and fixup the commits you want to merge:

$ git rebase -i HEAD~5

pick c2e2c87 commit 1
f 689d474 commit 2
f aa9d9b4 commit 3
f 888a009 commit 4
f d396e75 commit 5

# Rebase 2f7f53e..d396e75 onto 2f7f53e (5 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

You can use git rebase -i --root in order to rebase from the first commit.

Maroun
  • 94,125
  • 30
  • 188
  • 241
1

If all you really want is to squash your local development history before submitting a pull-request, the simplest way is to just develop on a local feature branch which is different from whatever upstream branch you want to affect.

Then, the procedure for squashing it onto master is

git checkout master
git merge --squash feature

(replace master with integration or whatever).

I'd use rebase -i for fine control, but for this simple case we can use git's knowledge of your history to figure out the last common ancestor automatically.

Useless
  • 64,155
  • 6
  • 88
  • 132
0

Setting aside the issues with the merge-squash workflow, which have already been discussed thoroughly in many places, in light of workflows often being out of your control.

There is a method you can use and though it is not 100% effective, it does have a fairly good track record in my experience and always fails safe.

Without ancestry to rely on, you need is a way of determining if two snapshots are identical. You can use the hash of the commits' trees with this command:

git show --format="%T" <committish>

In order to check if a branch can be deleted, first merge master into your branch. If there is a conflict in this merge, or there was a conflict in the original squash, you won't be able to use this method (this is the less than 100% effective part).

Thanks to the nature of git, it does not matter what order a set of patches are applied if no conflicts occur. So if the result of this merge should be identical to the head of master if your branch has been merged. This can be confirmed by comparing the tree hashes of the two branch heads you can find out if any unmerged code exists on your branch.

This could be boiled down into an command for single use that can be easily aliased like:

if [ $(git show --format="%T" origin/master) = $(git show --format="%T" HEAD) ]; then echo Merged; else echo Unmerged; fi

Or built into a shell script that will loop through all your local branches, merge them, test them, and delete the ones that have been merged.

All that being said, using interactive rebase to flatten your branch and getting the maintainer to use a fast forward only merge strategy on the pull requests would make all this unnecessary.

LightBender
  • 4,046
  • 1
  • 15
  • 31
0

You can soft reset to last commit you want in there and re-commit.

enter image description here

enter image description here

Chris Gunawardena
  • 6,246
  • 1
  • 29
  • 45
  • What program is this? How can I soft reset all commits in the branch – CuriousDeveloper Jan 17 '18 at 16:13
  • 1
    This is Sourcetree by Atlassian which is a free GUI on top of git but every git program will have this function. If you pick the last commit in your branch, it will move all changes to working. This is the same as what Kai mentioned in the comments `git reset --soft HEAD~numOfCommits` – Chris Gunawardena Jan 18 '18 at 08:05