Is there a way to check that a git tag matches the content of the corresponding commit?

*爱你&永不变心* 提交于 2019-12-01 10:47:33

问题


In the company I work for, some projects have an project.info file which contains the current version of the program/library/whatever.

Actually, when someone wants to tag a version, he must first ensure that the project.info file (which is versionned) is up-to-date and contains the same version than the name of the tag he is about to create. No need to say that this is error prone.

We work on a clients-server workflow for git (all commits go to the same central repository) so I wonder: is there a way (a hook perhaps ?) to make this central repository refuse tags for which the project.info does not match ?

What should I look for to get started ?

Thank you very much.


回答1:


Thanks to all the advices here, I succeeded.

Here is my final update hook script:

#!/bin/sh
#
# An example hook script to blocks unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
#   This boolean sets whether unannotated tags will be allowed into the
#   repository.  By default they won't be.
# hooks.allowdeletetag
#   This boolean sets whether deleting tags will be allowed in the
#   repository.  By default they won't be.
# hooks.allowmodifytag
#   This boolean sets whether a tag may be modified after creation. By default
#   it won't be.
# hooks.allowdeletebranch
#   This boolean sets whether deleting branches will be allowed in the
#   repository.  By default they won't be.
# hooks.denycreatebranch
#   This boolean sets whether remotely creating branches will be denied
#   in the repository.  By default this is allowed.
#

# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"

# --- Safety check
if [ -z "$GIT_DIR" ]; then
    echo "Don't run this script from the command line." >&2
    echo " (if you want, you could supply GIT_DIR then run" >&2
    echo "  $0 <ref> <oldrev> <newrev>)" >&2
    exit 1
fi

if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
    echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
    exit 1
fi

# --- Config
allowunannotated=$(git config --bool hooks.allowunannotated)
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
denycreatebranch=$(git config --bool hooks.denycreatebranch)
allowdeletetag=$(git config --bool hooks.allowdeletetag)
allowmodifytag=$(git config --bool hooks.allowmodifytag)
allowwildtag=$(git config --bool hooks.allowwildtag)
allowunmatchedtag=$(git config --bool hooks.allowunmatchedtag)

# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
    echo "*** Project description file hasn't been set" >&2
    exit 1
    ;;
esac

# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
    newrev_type=delete
else
    newrev_type=$(git cat-file -t $newrev)
fi

case "$refname","$newrev_type" in
    refs/tags/*,commit)
        # un-annotated tag
        short_refname=${refname##refs/tags/}
        if [ "$allowunannotated" != "true" ]; then
            echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
            echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
            exit 1
        fi
        ;;
    refs/tags/*,delete)
        # delete tag
        if [ "$allowdeletetag" != "true" ]; then
            echo "*** Deleting a tag is not allowed in this repository" >&2
            exit 1
        fi
        ;;
    refs/tags/*,tag)
        # annotated tag
        if [ "$allowwildtag" != "true" ] && ./hooks/check_tag -r $refname > /dev/null 2>&1
        then
            echo "*** Tag '$refname' does not match the naming constraints." >&2
            echo "*** Tags must follow the 'x.y-z' pattern, where x, y, and z are numeric characters." >&2
            exit 1
        fi
        if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
        then
            echo "*** Tag '$refname' already exists." >&2
            echo "*** Modifying a tag is not allowed in this repository." >&2
            exit 1
        fi
        if [ "$allowunmatchedtag" != "true" ] 
        then
            project_version=`./hooks/extract_project_version $newrev 2>/dev/null`

            if [ "$project_version" == "" ]
            then
                # We dont output anything in case of success
                #echo "*** Project does not contain a project.info file. No tag match performed."
                :
            elif [ "$project_version" == "error" ]
            then
                # The project contains an invalid project.info: we accept the tag but warn about it.
                echo "*** Project contains an invalid project.info file. No tag match performed."
            else
                tag_version=${refname##refs/tags/}

                if [ "$project_version" != "$tag_version" ]
                then
                    echo "*** Tag and project version do not match: $tag_version != $project_version"
                    echo "*** Please check your project.info file."
                    exit 1
                fi
            fi
        fi
        ;;
    refs/heads/*,commit)
        # branch
        if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
            echo "*** Creating a branch is not allowed in this repository" >&2
            exit 1
        fi
        ;;
    refs/heads/*,delete)
        # delete branch
        if [ "$allowdeletebranch" != "true" ]; then
            echo "*** Deleting a branch is not allowed in this repository" >&2
            exit 1
        fi
        ;;
    refs/remotes/*,commit)
        # tracking branch
        ;;
    refs/remotes/*,delete)
        # delete tracking branch
        if [ "$allowdeletebranch" != "true" ]; then
            echo "*** Deleting a tracking branch is not allowed in this repository" >&2
            exit 1
        fi
        ;;
    *)
        # Anything else (is there anything else?)
        echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
        exit 1
        ;;
esac

# --- Finished
exit 0

And here is my extract_project_version script:

#!/bin/bash

rev=$1

if [ "$rev" == "" ]
then
    echo "Missing revision parameter." >&2
    exit 1
fi

tmpdir=/tmp/$$.extract_project_version

mkdir -p $tmpdir
git archive $rev | tar -x -C $tmpdir

if [ -e "$tmpdir/project.info" ]
then
    echo $tmpdir/project.info
fi

rm -rf $tmpdir

And now it works perfectly :)




回答2:


Server-side

If you plan on using hooks, a post-receive hook (server refs updated) could create a commit with a change on project.info if it has been forgotten, but the initial tagging commit would not have the right information in the file...

If you want to validate this before updating the refs, the problem is that pre-receive/update hooks do not actually have the information required to check the validity of project.info (they get the name of the ref being updated, the old object name stored in the ref and the new objectname to be stored in the ref).

You can find some examples of hooks here and some information there.

Some other Stack Overflow topics on this subject :

  • How do I react to new tags in git hooks?
  • Automatically run Git hook when creating a Git tag

Client-side

This would not ensure anything since clients could decide not to apply the proper method. Still, a post-commit hook could probably do the trick.

  • An example of post-commit hook : git post-commit hook - script on committed files


来源:https://stackoverflow.com/questions/8418071/is-there-a-way-to-check-that-a-git-tag-matches-the-content-of-the-corresponding

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