Reputation: 3289
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
?
Upvotes: 19
Views: 6641
Reputation: 25267
I kinda discovered another trick. This really should be a comment, but I need the formatting available when posting an answer.
If you are doing an interactive rebase and using the edit
command to edit a commit during a rebase, you are actually forced to use git commit
instead of git rebase --continue
. This means commit hooks will run (pre-commit, commit-msg, post-commit, etc). There are other situations when rebasing where you can opt to explicitly use git commit
followed by git rebase --continue
.
During an interactive rebase, you may have some commits which are purely the result of running some command.
pick 12fo233jj Run `yarn add eslint prettier --dev`
pick 2y98tafkk Setup eslint+prettier
pick 19047ed9a Run `yarn run lint:fix:all`
A trick to avoid merge conflicts here, is to simply drop those original commits, and re-run the commands:
drop 12fo233jj Run `yarn add eslint prettier --dev`
# pardon the verbosity:
# Stash $MSG, undo commit, toss yarn.lock changes, `yarn add`, commit.
exec MSG=$(git log -1 --format=%B HEAD); git reset --soft HEAD~ && git restore --staged yarn.lock && git restore yarn.lock && yarn add eslint prettier --dev && git add yarn.lock package.json && git commit -m "$MSG" --no-verify
pick 2y98tafkk Setup eslint+prettier
drop 19047ed9a Run `yarn run lint:fix:all`
exec MSG=$(git log -1 --format=%B HEAD); git reset --hard HEAD~ && yarn run lint:fix:all && git commit -am "$MSG" --no-verify
If you want to reword
a commit, you can use this same recipe but tack on --reedit-message=
when calling git commit
.
Squashing, you'd have to create a new recipe here, undo twice, maybe grab both commit messages somehow? The rebase break
command could be useful if you like using a git gui:
pick 2y98tafkk Setup eslint+prettier
pick 19047ed9a Setup new eslint rules+hooks plugin
pick 18047ed34 Setup new prettier config
break
# Use gui tool to undo last two commits, `git commit` as "Setup new eslint rules+prettier config"
Upvotes: 0
Reputation: 25267
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 creators/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
Upvotes: 3
Reputation: 83577
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.
Upvotes: 14