问题
I'm not sure if I'm going about this the correct way. Sorta new to Git and GitKraken. I want to force others to squash their feature branch commits before pushing and creating a pull request. So I've tried the following pre-push git hook but even when I squash first it still thinks there are multiple commits. What am I missing? Thanks!
#!/bin/sh
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local sha1> <remote ref> <remote sha1>
remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha
do
if [ "$local_sha" = $z40 ]
then
# Handle delete
:
else
# Check for number of commits
commits=`git rev-list --count "$local_sha"`
if [ "$commits" -ne "1" ]
then
echo "Multiple commits detected, push aborted. Please squash your local branch before pushing."
exit 1
#git reset --soft HEAD~"$commits"
#git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
fi
fi
done
exit 0
回答1:
TL;DR
You want git rev-list --count $local_sha ^$remote_sha
(but see caveat below).
Long
git rev-list
is the big brother (sister?) plumbing variant of git log
: both will, by default, start from the commit(s) you give them, and find every commit reachable from those commits.
That is, given a commit graph like:
... <-E <-F <-G <-- branchname
you tell Git to start from branchname
, which identifies commit G
(G
here stands in for the actual 40-character hash ID), so git log
shows you that commit. Then, since G
has F
as its parent (G
points to F
), git log
shows you F
. Since F
has E
as its parent, git log
shows you E
next, and so on.
The rev-list code does the same thing: you tell it where to start—$local_sha
—and it lists that commit's hash, and its parent(s) hash(es), and more hashes of their parents, and so on. Adding --count
makes it count them up, and there are probably many.
You need to tell the Git commands that walk the graph where to stop. Given:
E--F--G <-- branchname
you can tell git rev-list
to start at G
(by hash ID, or by name), but to stop (and not include) E
(by hash ID, or by name if there's a name that points to E
). You can name stopping-point commits in many ways, but the most direct is the hat-prefix: ^E
means stop at E
, or any commit reachable from E
.
This reachability notion is very important in more complex graphs:
B--C
/ \
A F--G <-- branchname
\ /
D--E
The arrows connecting commits (which I've been drawing as lines here) are all one-way: they all point backwards in time. So F
points back to both E
and C
here, because F
here is a merge commit. C
points back to B
, which points back to A
; E
points back to D
, which also points back to A
.
If you name E
as a stopping point and G
as the starting point, git rev-list
will skip commits E
, D
, and A
, but will still include commits B
and C
, because those are reachable from F
. To exclude B
and C
as well, you must add ^C
to your list of stopping points.
In general, in a pre-push hook, you can just list the remote hash as the (single) stopping point, but there's a more general problem: you might not have the commit that the remote-hash points-to. You get commits from the remote by running git fetch
. They—the other repository to which you are git push
ing—get commits by someone committing there, or by other people also git push
ing to that repository. You might not have their commit(s) yet. If so, you'll have to run git fetch
first, so that your own Git has the commit, so that your Git can walk the commit graph starting from their hash ID.
来源:https://stackoverflow.com/questions/48714137/git-hook-pre-push-prompt-to-squash-commits-first