Git hook pre-push prompt to squash commits first

自闭症网瘾萝莉.ら 提交于 2019-12-11 14:36:32

问题


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 pushing—get commits by someone committing there, or by other people also git pushing 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!