19

I'm trying to build a set of git hook scripts for my organization, and one I would like to use (for multiple project just for myself) would be to check upon a git rebase --continue that I don't have any conflicts markers leftover in my code (<<<<<, =====, or >>>>>).

I already have such a script for my pre-commit, but what script applies on a rebase --continue ?

Martín Schonaker
  • 7,191
  • 4
  • 30
  • 55
Yeraze
  • 3,119
  • 3
  • 27
  • 42

2 Answers2

14

The manpage githooks has a list of the available git hooks. There is no hook for git rebase --continue (the list is exhaustive).

There is a hook "post-rewrite", which "is invoked by commands that rewrite commits", such as git rebase. However, it only runs once the command is done (i.e. when the rebase is finished).

It will give you the list of new commits created by the rewrite, so you could check if the commits introduce any conflicts markers and complain, but at that point it is too late to abort the rebase. You can still revert the rebase using the reflog, of course.

All in all, it is probably easier to write some wrapper for git rebase, or a separate checking tool to invoke manually. At any rate, you should (IMHO) always review the changes you made before invoking git rebase --continue. If you stick to doing that, you will not accidentally have conflict markers checked in.

sleske
  • 77,633
  • 33
  • 182
  • 219
1

TL;DR: git rebase --exec --reschedule-failed-exec "run-your-tests" <sha> will run tests after every commit after <sha>: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---execltcmdgt

=======

It would be nice if we could somehow have git rebase --continue simply run all our normal commit hooks.

In leu of this, "post-rewrite" and "pre-push" may be useful to verify quality of code that was just rebased, and/or check it before pushing.

But - how do we verify each intermediate commit has passing tests/working build/no lint errors?

Git rebase authors/maintainers have an answer:

git rebase --exec "run-your-tests" <base-commit-ish>

This is better than a "wrapper around git rebase" or "separate checking tool" that @sleske referred too, it's a native git feature!

When a commit has an issue, you'll see:

... output from failed tests ...
warning: execution failed: run-your-tests
You can fix the problem, and then run

  git rebase --continue

Here's some "pseudo" code you could run post-rewrite or pre-push:

rm -rf node_modules
git rebase
  --exec "no-conflict-markers"
  --exec "npm install"
  --exec "ensure-clean-git-status"
  --exec "npm run lint"
  --exec "npm run test"
  --exec "npm run type-check"
 master

(To run lint, test, and type-check in parallel for npm/node, see run-p)

When you yourself are doing an interactive rebase, you'll want to do something like:

git rebase -i --exec "run-your-tests" --reschedule-failed-exec <sha>

You could alias this to something shorter:

git quality-rebase <sha>

Using --resheculed-failed-exec does exactly what it says it does. If you fail a quality check, fix it, and run git rebase --continue, it will run your quality checks again (which OP obviously wants).

Thanks to git worktree, you can make your quality checks as HEAVY as you want. As you are waiting for your quality checks to finish, you can open new terminal, run cd myapp; git worktree add ../myapp2 master; git checkout <whatever> and keep on coding in a new working directory, unaffected by the status of the rebase-exec quality checks.

I think whenever you have good working code it's good to back it up to your git remote, which also allows others to discover it, and you can also have other webhook thingys "broadcast" that a new branch was published. Also, if your quality checks take some time, you won't want to sit and watch it. Therefore, I believe it's best to use any heavier checks as a pre-push hook. Simply attempt to git push and then when and if the rebase-exec quality checks finish, it'll be pushed up. You could use say command to tell you if they failed: git push && say "successfully pushed changed" || say "quality checks failed"

Here's my specific command I'd recommend in the interim, you'll need to edit the --exec command for your specific project:

CURRENT_BRANCH=`git branch --show-current`
git checkout master
git pull # this step should probably be automated via post-checkout hook. Or maybe warning should be generated when you create branch off an old local master branch.
git checkout $CURRENT_BRANCH
git fetch origin master # ensure latest origin/master, even if checkout commands fail
git rebase -i --reapply-cherry-picks --empty=ask --reschedule-failed-exec --rerere-autoupdate --exec "yarn install && yarn test -u && yarn run-p --print-label lint type-check" origin/master
Devin Rhode
  • 20,940
  • 6
  • 51
  • 67
  • If you have a `commit-msg` hook which adds metadata to a commit message (I have this npm package called git-guilt-staged which adds "Suggested reviewers:" to each commit message), maybe we'd be able to "reword" all commits and see this hook run - idk. – Devin Rhode Jan 03 '22 at 16:39
  • I am very diligent about breaking up changes to make a clean, easy to follow commit history. As such, I like to breakup config changes/git hooks/quality checks, apart from the actual application source code changes. This presents a dilemma: do you first make your quality checks stricter, and commit with `--no-verify`, - which reflects the chronological order you are working, and is easier to follow - OR, do you reverse the commits, to avoid using `--no-verify`? – Devin Rhode Jan 24 '22 at 17:04
  • I'd say the solution to this would be: commits which make quality checks stricter should have a special "flag" in the commit message. Maybe it has #no-verify, or `NO-VERIFY: add new lint rule". This way you get easier-to-follow commit history. – Devin Rhode Jan 24 '22 at 17:06
  • What may be _IDEAL_ is if somehow we could permit one commit to have failing tests, as long as the next commit does not. This allows you to correctly portray the history. First, you encountered an issue, then, you fixed it. This is not only for TDD. You change types, you upgrade something, rewrite some method, new lint rule, stricter type checking, etc. – Devin Rhode Jan 28 '22 at 01:51
  • Different interesting usage for this today: vscode on-save formatting was on the "fritz" and to stay in the zone, I just commited them as `"vscode formatting is on the fritz"`. Later, I was cleaning up commit log. I wanted to FIX the history of shitty formatting mishaps. See: https://stackoverflow.com/questions/71812872/how-to-rebase-when-prettier-formatting-was-applied-to-whole-project/71812873#71812873 – Devin Rhode Apr 10 '22 at 00:20