0

I'm looking to write a Linux script that contributors can run to automate squashing and rebasing of their own branches/Pull Requests. As maintainer, I currently perform manual squashed merges of PRs, so have a clean history tree in master but lose too much info about who committed what (e.g. for git blame).

Other similar questions have accepted solutions which still allow for too much human error, e.g.

git rebase -i HEAD~3 requires people to count their commits every time.

git fetch origin && git rebase -i origin/master requires manual changes of pick to squash.

The first solution above seems tempting to adapt, if we can reliably know the commit count based on output of another git command.

You can assume for now one author per PR and we want to squash it to one commit by that same author. The solution should also be able to work multiple times in a PR prior to merging. e.g. author may want to run it prior to raising the first PR, and then later after review and more commits, prior to merge. You can also assume that the implications of "rewriting history" with rebase are not of a concern.

rgareth
  • 3,377
  • 5
  • 23
  • 35
  • As a side note, you might be interested in the `GIT_{AUTHOR,COMMITER}_{NAME,EMAIL}` variables to influence a commit's author (and comitter) information (the author ones are the ones you should care about) - related question http://stackoverflow.com/questions/750172/change-the-author-of-a-commit-in-git – Tobias Kienzler Aug 11 '14 at 09:32
  • as for the counting, users could use a (local, non-pushed) tag such as `start_squash` and then `git rebase -i start_squash~1` - no need to start counting from `HEAD` – Tobias Kienzler Aug 11 '14 at 09:35
  • A "Plan B" could be for me to continue squashing but essentially faking the committer to be the contributor. But this ideally should also be automated so it's not prone to my human error copy/pasting names and emails correctly. – rgareth Aug 11 '14 at 11:32
  • You should rather "fake" the author, not the comitter, to quote the [pro git book](http://git-scm.com/book/ch2-3.html): "You may be wondering what the difference is between _author_ and _committer_. The _author_ is the person who originally wrote the patch, whereas the _committer_ is the person who last applied the patch." This is not faking, it _is_ what `GIT_AUTHOR_NAME` and `GIT_AUTHOR_EMAIL` _are_ intended for. If you want automation, if all commits-to-be-squashed stem from the same author you can process the output of `git shortlog` somehow to simply re-use the author name and email – Tobias Kienzler Aug 11 '14 at 11:38
  • ...or `git log --pretty=format:"GIT_AUTHOR_NAME=%an GIT_AUTHOR_EMAIL=%ae" HEAD^` wrapped in an `eval` or similar. Maybe https://help.github.com/articles/changing-author-info is helpful for you as well – Tobias Kienzler Aug 11 '14 at 11:42

1 Answers1

1

First of all, if you want commits to have different authors than yourself, simple prepend the git commands with GIT_AUTHOR_NAME="author name" GIT_AUTHOR_EMAIL="author@email", or for some automatism wrap the rest in a

(
  eval $(git log HEAD^.. --pretty=format:'export GIT_AUTHOR_NAME="%an" GIT_AUTHOR_EMAIL="%ae"')
  git stuff
)

Next, when rebasing, it is always wise to make a backup-tag (though HEAD@{1} should be helpful enough if errors are recognized soon enough).

Now to the actual question. As this post suggests, for an automated "rebase squash", you can actually simply use git reset --soft SOMEHEAD; git commit. Now let's get to your desired ability to squash multiple PRs. I suggest for that, your authors should use a local tag such as branchname/PRed re-attached to the latest PR commit (via git tag -f). Then the SOMEHEAD to soft-reset to would simply said tag.

So in summary, the workflow for an author would be

git tag -f tmp        # just in case we screw up,
                      # so git reset --hard tmp hopefully fixes things
git reset --soft PRed # or e.g. master/PRed etc.
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
                      # to start with a concatenation of the squashed
                      # commit messages
git tag -f PRed       # same tag name as in git reset --soft

To automate things more, you could use a commit message prefix for PRs and grep the output of git log to obtain the correct commit to git reset --soft to instead of having to use a tag, of course.

Community
  • 1
  • 1
Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • Sorry to throw in a potential curveball, but will this approach work if the contributor has merged from master prior? Also for simplicity sake, let's assume: (a) they branched off and PR'd to origin/master, and (b) branch name is feature/abc. – rgareth Aug 11 '14 at 12:33
  • Phew, good question, you'd best try this with a fake git repo to be sure. A temporal branch and `git merge --squash` might work better then. Or a `git stash; git merge origin/master; git stash pop` between the reset and the commit, so the merge is not considered part of the PR – Tobias Kienzler Aug 11 '14 at 12:37