summaryrefslogtreecommitdiff
path: root/git-debpush
diff options
context:
space:
mode:
Diffstat (limited to 'git-debpush')
-rwxr-xr-xgit-debpush297
1 files changed, 249 insertions, 48 deletions
diff --git a/git-debpush b/git-debpush
index 63057ed..c3b067d 100755
--- a/git-debpush
+++ b/git-debpush
@@ -35,9 +35,11 @@ set -o pipefail
# mode; if there is a previous tag, and no quilt mode provided, assume
# same quilt mode as in previous tag created by this script
-# ---- Helper functions and variables
+# **** Helper functions and variables ****
us="$(basename $0)"
+git_playtree_setup=git-playtree-setup ###substituted###
+git_playtree_setup=${DEBPUSH_GIT_PLAYTREE_SETUP-$git_playtree_setup}
cleanup() {
if [ -d "$temp" ]; then
@@ -63,10 +65,111 @@ get_file_from_ref () {
fi
}
-# ---- Parse command line
+failed_check=false
+fail_check () {
+ local check=$1; shift
+ local check_is_forced=false
+
+ case ",$force," in
+ *",$check,"*) check_is_forced=true ;;
+ esac
+ if $force_all || $check_is_forced; then
+ echo >&2 "$us: warning: $* ('$check' check)"
+ else
+ echo >&2 "$us: $* ('$check' check)"
+ failed_check=true
+ fi
+}
+
+fail_check_upstream_nonidentical () {
+ fail_check upstream-nonidentical \
+ "the upstream source in tag $upstream_tag is not identical to the upstream source in $branch"
+}
+
+find_last_tag () {
+ local prefix=$1
+
+ set +o pipefail # perl will SIGPIPE git-log(1) here
+ git log --pretty=format:'%D' --decorate=full "$branch" \
+ | perl -wne 'use Dpkg::Version;
+ @pieces = split /, /, $_;
+ @debian_tag_vs = sort { version_compare($b, $a) }
+ map { m|tag: refs/tags/'"$prefix"'(.+)| ? $1 : () } @pieces;
+ if (@debian_tag_vs) { print "'"$prefix"'$debian_tag_vs[0]\n"; exit }'
+ set -o pipefail
+}
+
+check_treesame () {
+ local first=$1
+ local second=$2
+ shift 2
+
+ set +e
+ git diff --quiet --exit-code "$first".."$second" -- . "$@"
+ git_diff_rc=$?
+ set -e
+
+ # show the user what the difference was
+ if [ $git_diff_rc = 1 ]; then
+ git diff --compact-summary "$first".."$second" -- . "$@"
+ fi
+
+ if [ $git_diff_rc -le 1 ]; then
+ return $git_diff_rc
+ else
+ fail "'git diff' exited with unexpected code $git_diff_rc"
+ fi
+}
+
+check_patches_apply () {
+ local should_match_branch="$1"
+
+ local playground="$(git rev-parse --git-dir)/gdp"
+ local playtree="$playground/apply-patches"
+ local git_apply_rc=0
+
+ rm -rf "$playground"
+ mkdir -p "$playtree"
+ local pwd="$(pwd)"
+ cd "$playtree"
+ "$git_playtree_setup" .
+
+ # checking out the upstream source and then d/patches on top
+ # ensures this check will work for a variety of quilt modes
+ git checkout -b upstream "$upstream_committish"
+ git checkout "$branch_commit" -- debian
+
+ if [ -s "debian/patches/series" ]; then
+ while read patch; do
+ shopt -s extglob; patch="${patch%%?( )#*}"; shopt -u extglob
+ if [ -z "$patch" ]; then continue; fi
+ set +e
+ git apply --index "debian/patches/$patch"
+ git_apply_rc=$?
+ set -e
+ if ! [ $git_apply_rc = 0 ]; then
+ fail_check patches-applicable \
+ "'git apply' failed to apply patch $patch"
+ break
+ fi
+ done <debian/patches/series
+
+ if $should_match_branch && [ $git_apply_rc = 0 ]; then
+ git commit -q -a -m"commit result of applying all patches"
+ check_treesame HEAD "$branch_commit" ':!debian' \
+ || fail_check patches-applicable \
+ "applying all patches does not yield $branch"
+ fi
+ fi
+
+ cd "$pwd"
+ rm -rf "$playground"
+}
+
+# **** Parse command line ****
getopt=$(getopt -s bash -o 'nfu:' \
- -l 'no-push,force,branch:,remote:,distro:,upstream:,quilt:,gbp,dpm,\
+ -l 'no-push,force::,branch:,remote:,distro:,upstream:,quilt:,gbp,dpm,\
baredebian,baredebian+git,baredebian+tarball' \
-n "$us" -- "$@")
eval "set - $getopt"
@@ -74,7 +177,8 @@ set -e$DGIT_TEST_DEBPUSH_DEBUG
git_tag_opts=()
pushing=true
-force=false
+force_all=false
+force=""
distro=debian
quilt_mode=""
branch="HEAD"
@@ -83,7 +187,7 @@ while true; do
case "$1" in
'-n'|'--no-push') pushing=false; shift; continue ;;
'-u') git_tag_opts+=(-u "$2"); shift 2; continue ;;
- '-f'|'--force') force=true; shift; continue ;;
+ '-f') force_all=true; shift; continue ;;
'--gbp') quilt_mode='gbp'; shift; continue ;;
'--dpm') quilt_mode='dpm'; shift; continue ;;
'--branch') branch=$2; shift 2; continue ;;
@@ -98,6 +202,18 @@ while true; do
fail "--baredebian+tarball quilt mode not supported"
;;
+ # we require the long form of the option to skip individual
+ # checks, not permitting `-f check`, to avoid problems if we
+ # later want to introduce positional args
+ '--force')
+ case "$2" in
+ '')
+ force_all=true ;;
+ *)
+ force="$force,$2" ;;
+ esac
+ shift 2; continue ;;
+
'--') shift; break ;;
*) badusage "unknown option $1" ;;
esac
@@ -114,10 +230,18 @@ case "$quilt_mode" in
*) badusage "invalid quilt mode: $quilt_mode" ;;
esac
-# ---- Gather git information
+# **** Early sanity check ****
+
+if [ "$branch" = "HEAD" ] \
+ && ! git symbolic-ref --quiet HEAD >/dev/null; then
+ fail_check detached \
+ "HEAD is detached; you probably don't want to debpush it"
+fi
+
+# **** Gather git information ****
remoteconfigs=()
-push_branch=()
+to_push=()
# Maybe $branch is a symbolic ref. If so, resolve it
branchref="$(git symbolic-ref -q $branch || test $? = 1)"
@@ -144,11 +268,14 @@ esac
case "$branch" in
refs/heads/*)
b=${branch#refs/heads/}
- push_branch+=("$b")
+ to_push+=("$b")
remoteconfigs+=( branch.$b.pushRemote branch.$b.remote )
;;
esac
+# resolve $branch to a commit
+branch_commit="$(git rev-parse --verify ${branch}^{commit})"
+
# also check, if the branch does not have its own pushRemote or
# remote, whether there's a default push remote configured
remoteconfigs+=(remote.pushDefault)
@@ -163,7 +290,7 @@ if $pushing && [ "x$remote" = "x" ]; then
fi
fi
-# ---- Gather source package information
+# **** Gather source package information ****
temp=$(mktemp -d)
trap cleanup EXIT
@@ -175,36 +302,14 @@ target=$(cd $temp; dpkg-parsechangelog -SDistribution)
rm -rf "$temp"
trap - EXIT
-# ---- Useful sanity checks
-
-if ! $force; then
-
- if [ "$target" = "UNRELEASED" ]; then
- fail "UNRELEASED changelog"
- fi
-
- # TODO additional checks we might do:
- #
- # - are we uploading to a different suite from the last tag
- # (e.g. unstable after experimental)? user should pass option to
- # confirm
- #
- # - walking backwards from $branch, if there is an archive/ strictly
- # before we reach most recent debian/ tag, error, this might be a
- # push of the dgit view to the maintainer branch
-
-fi
-
-# ---- Create the git tag
-
format="$(get_file_from_ref debian/source/format)"
case "$format" in
'3.0 (quilt)') upstream=true ;;
'3.0 (native)') upstream=false ;;
'1.0'|'')
- if get_file_from_ref debian/source/options | grep '^-sn *$'; then
+ if get_file_from_ref debian/source/options | grep -q '^-sn *$'; then
upstream=false
- elif get_file_from_ref debian/source/options | grep '^-sk *$'; then
+ elif get_file_from_ref debian/source/options | grep -q '^-sk *$'; then
upstream=true
else
fail 'please see "SOURCE FORMAT 1.0" in git-debpush(1)'
@@ -215,6 +320,11 @@ case "$format" in
;;
esac
+# **** Gather git history information ****
+
+last_debian_tag=$(find_last_tag "debian/")
+last_archive_tag=$(find_last_tag "archive/debian/")
+
upstream_info=""
if $upstream; then
if [ "x$upstream_tag" = x ]; then
@@ -238,25 +348,116 @@ if $upstream; then
fi
upstream_committish=$(git rev-parse "refs/tags/${upstream_tag}"^{})
upstream_info=" upstream-tag=$upstream_tag upstream=$upstream_committish"
+ to_push+=("$upstream_tag")
+fi
+
+# **** Useful sanity checks ****
+
+# ---- UNRELEASED suite
+
+if [ "$target" = "UNRELEASED" ]; then
+ fail_check unreleased "UNRELEASED changelog"
+fi
+
+# ---- Pushing dgit view to maintainer view
+
+if ! [ "x$last_debian_tag" = "x" ] && ! [ "x$last_archive_tag" = "x" ]; then
+ last_debian_tag_c=$(git rev-parse "$last_debian_tag"^{})
+ last_archive_tag_c=$(git rev-parse "$last_archive_tag"^{})
+ if ! [ "$last_debian_tag_c" = "$last_archive_tag_c" ] \
+ && git merge-base --is-ancestor \
+ "$last_debian_tag" "$last_archive_tag"; then
+ fail_check dgit-view \
+"looks like you might be trying to push the dgit view to the maintainer branch?"
+ fi
fi
+# ---- Targeting different suite
+
+if ! [ "x$last_debian_tag" = "x" ]; then
+ temp=$(mktemp -d)
+ trap cleanup EXIT
+ mkdir "$temp/debian"
+ git cat-file blob "$last_debian_tag":debian/changelog >"$temp/debian/changelog"
+ prev_target=$(cd $temp; dpkg-parsechangelog -SDistribution)
+ rm -rf "$temp"
+ trap - EXIT
+
+ if ! [ "$prev_target" = "$target" ] && ! [ "$target" = "UNRELEASED" ]; then
+ fail_check suite \
+"last upload targeted $prev_target, now targeting $target; might be a mistake?"
+ fi
+fi
+
+# ---- Upstream tag is not ancestor of $branch
+
+if ! [ "x$upstream_tag" = "x" ] \
+ && ! git merge-base --is-ancestor "$upstream_tag" "$branch" \
+ && ! [ "$quilt_mode" = "baredebian" ]; then
+ fail_check upstream-nonancestor \
+ "upstream tag $upstream_tag is not an ancestor of $branch; probably a mistake"
+fi
+
+# ---- Quilt mode-specific checks
+
+case "$quilt_mode" in
+ gbp)
+ check_treesame "$upstream_tag" "$branch" ':!debian' ':!**.gitignore' \
+ || fail_check_upstream_nonidentical
+ check_patches_apply false
+ ;;
+ unapplied)
+ check_treesame "$upstream_tag" "$branch" ':!debian' \
+ || fail_check_upstream_nonidentical
+ check_patches_apply false
+ ;;
+ baredebian)
+ check_patches_apply false
+ ;;
+ dpm|nofix)
+ check_patches_apply true
+ ;;
+esac
+
+# ---- git-debrebase branch format checks
+
+# only check branches, since you can't run `git debrebase conclude` on
+# non-branches
+case "$branch" in
+ refs/heads/*)
+ # see "STITCHING, PSEUDO-MERGES, FFQ RECORD" in git-debrebase(5)
+ ffq_prev_ref="refs/ffq-prev/${branch#refs/}"
+ if git show-ref --quiet --verify "$ffq_prev_ref"; then
+ fail_check unstitched \
+ "this looks like an unstitched git-debrebase branch, which should not be pushed"
+ fi
+esac
+
+# ---- Summary
+
+if $failed_check; then
+ # We don't mention the --force=check options here as those are
+ # mainly for use by scripts, or when you already know what check
+ # is going to fail before you invoke git-debpush. Keep the
+ # script's terminal output as simple as possible. No "see the
+ # manpage"!
+ fail "some check(s) failed; you can pass --force to ignore them"
+fi
+
+# **** Create the git tag ****
+
# convert according to DEP-14 rules
git_version=$(echo $version | tr ':~' '%_' | sed 's/\.(?=\.|$|lock$)/.#/g')
debian_tag="$distro/$git_version"
+to_push+=("$debian_tag")
# If the user didn't supply a quilt mode, look for it in a previous
# tag made by this script
if [ "x$quilt_mode" = "x" ] && [ "$format" = "3.0 (quilt)" ]; then
- set +o pipefail # perl will SIGPIPE git-log(1) here
- tag=$(git log --pretty=format:'%D' --decorate=full "$branch" \
- | perl -wne 'use Dpkg::Version;
- @pieces = split /, /, $_;
- @debian_tag_vs = sort {version_compare($b, $a)}
- map { m|tag: refs/tags/debian/(.+)| ? $1 : () } @pieces;
- if (@debian_tag_vs) { print "debian/$debian_tag_vs[0]\n"; exit }')
- if [ "x$tag" != "x" ]; then
- quilt_mode=$(git cat-file -p $(git rev-parse "$tag") \
+ set +o pipefail # perl will SIGPIPE git-cat-file(1) here
+ if [ "x$last_debian_tag" != "x" ]; then
+ quilt_mode=$(git cat-file -p $(git rev-parse "$last_debian_tag") \
| perl -wne \
'm/^\[dgit.*--quilt=([a-z+]+).*\]$/;
if ($1) { print "$1\n"; exit }')
@@ -275,16 +476,16 @@ if [ "$format" = "3.0 (quilt)" ]; then
fi
fi
-git tag "${git_tag_opts[@]}" -s -F- "$debian_tag" "$branch" <<EOF
-$source release $version for $target
+tagmessage="$source release $version for $target
[dgit distro=$distro split$quilt_mode_text]
[dgit please-upload$upstream_info]
-EOF
+"
+
+git tag "${git_tag_opts[@]}" -s -m "$tagmessage" "$debian_tag" "$branch"
-# ---- Do a git push
+# **** Do a git push ****
if $pushing; then
- # xxx when user can specify upstream_tag, must cope with spaces
- git push "$remote" "${push_branch[@]}" $upstream_tag "$debian_tag"
+ git push "$remote" "${to_push[@]}"
fi