When I do commit --amend, it is unsafe commit if the commit already has been pushed to remote repository.
I want to detect unsafe commit --amend by pre-commit hook and abort.
But pre-commit hook has no arguments. I don't know how to detect --amend.
What should I do ?
Another way to check if this is an
--amend
case, in standard shell:A quick way to detect a "pure" amend in the pre-commit hook:
By "pure" I mean you're only rewriting the commit message and not any of the changes in the commit. If there are any changes in your index when you call
git commit --amend
, you're rewriting more than the commit message and this will behave as if you're doing a conventionalgit commit
.TL;DR version: there's a script below (kind of in the middle) that enforces a particular work-flow that may work for you, or may not. It doesn't exactly prevent particular
git commit --amend
s (plus you can always use--no-verify
to skip the script), and it does prevent (or at least warn about) othergit commit
s, which may or may not be what you want.To make it error-out instead of warning, change
WARNING
toERROR
and changesleep 5
toexit 1
.EDIT: erroring-out is not a good idea, because you can't tell, in this git hook, that this is an "amend" commit, so this will fail (you have to add
--no-verify
) if you're simply adding a new commit to a branch that has an upstream and is at the upstream's head.It's not necessarily unsafe, because
git commit --amend
does not actually change any commits in your repo, it just adds a new, different commit and re-points the branch tip there. For instance, if your branch looks like this:then what a successful
git commit --amend
does is this:You still have commit
F
, and commitG
is the "amended" version ofF
. However, it's true thatG
is not a "fast forward" ofF
and you probably should notgit push -f origin branch
in this case.A similar cases occurs if you're already in that kind of situation, i.e., after that successful
git commit --amend
(done without or in spite of the script below):If you now
git commit
(even without--amend
), you'll add a new commit, e.g.,G
connects toH
; but again, attempting to pushH
is a non-fast-forward.You can't specifically test for
--amend
, but you can check whether there is an "upstream", and if so, whether the currentHEAD
is an ancestor of that upstream. Here's a slightly cheesy pre-commit hook that does this (with a warning-and-sleep rather than an error-exit).The basic problem here is that this situation occurs all the time even without using
git commit --amend
. Let's say you start with the same setup as above, but commitF
does not exist yet:Now you, in your copy of the repo, decide to work on
branch
. You fix a bug andgit commit
:You're now ahead of
origin
andgit push origin branch
would do the right thing. But while you were fixing one bug, Joe fixes a different bug in his copy of the repo, and pushes his version toorigin/branch
, beating you to thepush
step. So you rungit fetch
to update and you now have this:(where
J
is Joe's commit). This is a perfectly normal state, and it would be nice to be able togit commit
to add another fix (for, say, a third bug) and then either merge or rebase to include Joe's fix too. The example pre-commit hook will object.If you always rebase-or-merge first, then add your third fix, the script won't object. Let's look at what happens when we get into the
F
-and-J
situation above and usegit merge
(or agit pull
that does a merge):You are now at commit
M
, the merge, which is "ahead of"J
. So the script's@{upstream}
finds commitJ
and checks whether theHEAD
commit (M
) is an ancestor ofJ
. It's not, and additional new commits are allowed, so your "fix third bug" commitN
gives you this:Alternatively you can
git rebase
ontoJ
, so that before you go to fix the third bug you have:(here
F'
is the cherry-picked commitF
; I put parentheses aroundF
to indicate that, while it's still in your repo, it no longer has any branch label pointing to it, so it's mostly invisible.) Now the pre-commit hook script won't object, again.Following @perror's answer, I came up with the following:
Following @Roger Dueck's answer, ended up doing: