chiark / gitweb /
dgit.1: drop remark about sbuild not building arch-independent
[dgit.git] / git-debpush
index 39ff4103150a504bc73971d155f7e2f29ceb51d7..2790560cf21658b5275bcd7d180db9909d696c59 100755 (executable)
@@ -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
 
 #   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)"
 
 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
 
 cleanup() {
     if [ -d "$temp" ]; then
@@ -57,22 +59,35 @@ badusage () {
 get_file_from_ref () {
     local path=$1
 
 get_file_from_ref () {
     local path=$1
 
+    # redirect to /dev/null instead of using `grep -Eq` to avoid grep
+    # SIGPIPEing git-ls-tree
     if git ls-tree --name-only -r "$branch" \
     if git ls-tree --name-only -r "$branch" \
-            | grep -Eq "^$path$"; then
+            | grep -E "^$path$" >/dev/null; then
         git cat-file blob $branch:$path
     fi
 }
 
 failed_check=false
 fail_check () {
         git cat-file blob $branch:$path
     fi
 }
 
 failed_check=false
 fail_check () {
-    if $force; then
-        echo >&2 "$us: warning: $*"
+    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
     else
-        echo >&2 "$us: $*"
+        echo >&2 "$us: $* ('$check' check)"
         failed_check=true
     fi
 }
 
         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
 
 find_last_tag () {
     local prefix=$1
 
@@ -86,10 +101,77 @@ find_last_tag () {
     set -o pipefail
 }
 
     set -o pipefail
 }
 
-# ---- Parse command line
+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:' \
 
 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"
 baredebian,baredebian+git,baredebian+tarball' \
               -n "$us" -- "$@")
 eval "set - $getopt"
@@ -97,7 +179,8 @@ set -e$DGIT_TEST_DEBPUSH_DEBUG
 
 git_tag_opts=()
 pushing=true
 
 git_tag_opts=()
 pushing=true
-force=false
+force_all=false
+force=""
 distro=debian
 quilt_mode=""
 branch="HEAD"
 distro=debian
 quilt_mode=""
 branch="HEAD"
@@ -106,7 +189,7 @@ while true; do
     case "$1" in
         '-n'|'--no-push') pushing=false;           shift;   continue ;;
        '-u')             git_tag_opts+=(-u "$2"); shift 2; continue ;;
     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 ;;
         '--gbp')          quilt_mode='gbp';        shift;   continue ;;
         '--dpm')          quilt_mode='dpm';        shift;   continue ;;
         '--branch')       branch=$2;               shift 2; continue ;;
@@ -121,6 +204,18 @@ while true; do
             fail "--baredebian+tarball quilt mode not supported"
             ;;
 
             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
         '--') shift; break ;;
        *) badusage "unknown option $1" ;;
     esac
@@ -137,10 +232,18 @@ case "$quilt_mode" in
     *) badusage "invalid quilt mode: $quilt_mode" ;;
 esac
 
     *) 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=()
 
 remoteconfigs=()
-push_branch=()
+to_push=()
 
 # Maybe $branch is a symbolic ref.  If so, resolve it
 branchref="$(git symbolic-ref -q $branch || test $? = 1)"
 
 # Maybe $branch is a symbolic ref.  If so, resolve it
 branchref="$(git symbolic-ref -q $branch || test $? = 1)"
@@ -167,11 +270,14 @@ esac
 case "$branch" in
     refs/heads/*)
         b=${branch#refs/heads/}
 case "$branch" in
     refs/heads/*)
         b=${branch#refs/heads/}
-        push_branch+=("$b")
+        to_push+=("$b")
         remoteconfigs+=( branch.$b.pushRemote branch.$b.remote )
         ;;
 esac
 
         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)
 # also check, if the branch does not have its own pushRemote or
 # remote, whether there's a default push remote configured
 remoteconfigs+=(remote.pushDefault)
@@ -186,7 +292,7 @@ if $pushing && [ "x$remote" = "x" ]; then
     fi
 fi
 
     fi
 fi
 
-# ---- Gather source package information
+# **** Gather source package information ****
 
 temp=$(mktemp -d)
 trap cleanup EXIT
 
 temp=$(mktemp -d)
 trap cleanup EXIT
@@ -203,9 +309,9 @@ case "$format" in
     '3.0 (quilt)')  upstream=true ;;
     '3.0 (native)') upstream=false ;;
     '1.0'|'')
     '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
            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)'
            upstream=true
        else
            fail 'please see "SOURCE FORMAT 1.0" in git-debpush(1)'
@@ -216,7 +322,7 @@ case "$format" in
        ;;
 esac
 
        ;;
 esac
 
-# ---- Gather git history information
+# **** Gather git history information ****
 
 last_debian_tag=$(find_last_tag "debian/")
 last_archive_tag=$(find_last_tag "archive/debian/")
 
 last_debian_tag=$(find_last_tag "debian/")
 last_archive_tag=$(find_last_tag "archive/debian/")
@@ -244,25 +350,32 @@ if $upstream; then
     fi
     upstream_committish=$(git rev-parse "refs/tags/${upstream_tag}"^{})
     upstream_info=" upstream-tag=$upstream_tag upstream=$upstream_committish"
     fi
     upstream_committish=$(git rev-parse "refs/tags/${upstream_tag}"^{})
     upstream_info=" upstream-tag=$upstream_tag upstream=$upstream_committish"
+    to_push+=("$upstream_tag")
 fi
 
 fi
 
-# ---- Useful sanity checks
+# **** Useful sanity checks ****
+
+# ---- UNRELEASED suite
 
 if [ "$target" = "UNRELEASED" ]; then
 
 if [ "$target" = "UNRELEASED" ]; then
-    fail_check "UNRELEASED changelog"
+    fail_check unreleased "UNRELEASED changelog"
 fi
 
 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
 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 \
+        fail_check dgit-view \
 "looks like you might be trying to push the dgit view to the maintainer branch?"
     fi
 fi
 
 "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
 if ! [ "x$last_debian_tag" = "x" ]; then
     temp=$(mktemp -d)
     trap cleanup EXIT
@@ -273,29 +386,73 @@ if ! [ "x$last_debian_tag" = "x" ]; then
     trap - EXIT
 
     if ! [ "$prev_target" = "$target" ] && ! [ "$target" = "UNRELEASED" ]; then
     trap - EXIT
 
     if ! [ "$prev_target" = "$target" ] && ! [ "$target" = "UNRELEASED" ]; then
-        fail_check \
+        fail_check suite \
 "last upload targeted $prev_target, now targeting $target; might be a mistake?"
     fi
 fi
 
 "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
 if ! [ "x$upstream_tag" = "x" ] \
         && ! git merge-base --is-ancestor "$upstream_tag" "$branch" \
         && ! [ "$quilt_mode" = "baredebian" ]; then
-    fail_check \
+    fail_check upstream-nonancestor \
  "upstream tag $upstream_tag is not an ancestor of $branch; probably a mistake"
 fi
 
  "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
 
 
-if ! $force && $failed_check; then
-    fail "some checks failed; you can override with --force"
+# ---- 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
 
 fi
 
-# ---- Create the git tag
+# **** Create the git tag ****
 
 # convert according to DEP-14 rules
 git_version=$(echo $version | tr ':~' '%_' | sed 's/\.(?=\.|$|lock$)/.#/g')
 
 debian_tag="$distro/$git_version"
 
 # 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 the user didn't supply a quilt mode, look for it in a previous
 # tag made by this script
@@ -321,19 +478,16 @@ if [ "$format" = "3.0 (quilt)" ]; then
     fi
 fi
 
     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]
 
 [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
 
 if $pushing; then
-    if [ "x$upstream_tag" = "x" ]; then
-        git push "$remote" "${push_branch[@]}" "$debian_tag"
-    else
-        git push "$remote" "${push_branch[@]}" "$debian_tag" "$upstream_tag"
-    fi
+    git push "$remote" "${to_push[@]}"
 fi
 fi