contrib / git-resurrect.shon commit Merge commit 'v1.7.0' into jc/checkout-reflog-fix (71ee7fd)
   1#!/bin/sh
   2
   3USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
   4LONG_USAGE="git-resurrect attempts to find traces of a branch tip
   5called <name>, and tries to resurrect it.  Currently, the reflog is
   6searched for checkout messages, and with -r also merge messages.  With
   7-m and -t, the history of all refs is scanned for Merge <name> into
   8other/Merge <other> into <name> (respectively) commit subjects, which
   9is rather slow but allows you to resurrect other people's topic
  10branches."
  11
  12OPTIONS_SPEC="\
  13git resurrect $USAGE
  14--
  15b,branch=            save branch as <newname> instead of <name>
  16a,all                same as -l -r -m -t
  17k,keep-going         full rev-list scan (instead of first match)
  18l,reflog             scan reflog for checkouts (enabled by default)
  19r,reflog-merges      scan for merges recorded in reflog
  20m,merges             scan for merges into other branches (slow)
  21t,merge-targets      scan for merges of other branches into <name>
  22n,dry-run            don't recreate the branch"
  23
  24. git-sh-setup
  25
  26search_reflog () {
  27        sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
  28                < "$GIT_DIR"/logs/HEAD
  29}
  30
  31search_reflog_merges () {
  32        git rev-parse $(
  33                sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
  34                        < "$GIT_DIR"/logs/HEAD
  35        )
  36}
  37
  38_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
  39_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
  40
  41search_merges () {
  42        git rev-list --all --grep="Merge branch '$1'" \
  43                --pretty=tformat:"%P %s" |
  44        sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}"
  45}
  46
  47search_merge_targets () {
  48        git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
  49                --pretty=tformat:"%H %s" --all |
  50        sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} "
  51}
  52
  53dry_run=
  54early_exit=q
  55scan_reflog=t
  56scan_reflog_merges=
  57scan_merges=
  58scan_merge_targets=
  59new_name=
  60
  61while test "$#" != 0; do
  62        case "$1" in
  63            -b|--branch)
  64                shift
  65                new_name="$1"
  66                ;;
  67            -n|--dry-run)
  68                dry_run=t
  69                ;;
  70            --no-dry-run)
  71                dry_run=
  72                ;;
  73            -k|--keep-going)
  74                early_exit=
  75                ;;
  76            --no-keep-going)
  77                early_exit=q
  78                ;;
  79            -m|--merges)
  80                scan_merges=t
  81                ;;
  82            --no-merges)
  83                scan_merges=
  84                ;;
  85            -l|--reflog)
  86                scan_reflog=t
  87                ;;
  88            --no-reflog)
  89                scan_reflog=
  90                ;;
  91            -r|--reflog_merges)
  92                scan_reflog_merges=t
  93                ;;
  94            --no-reflog_merges)
  95                scan_reflog_merges=
  96                ;;
  97            -t|--merge-targets)
  98                scan_merge_targets=t
  99                ;;
 100            --no-merge-targets)
 101                scan_merge_targets=
 102                ;;
 103            -a|--all)
 104                scan_reflog=t
 105                scan_reflog_merges=t
 106                scan_merges=t
 107                scan_merge_targets=t
 108                ;;
 109            --)
 110                shift
 111                break
 112                ;;
 113            *)
 114                usage
 115                ;;
 116        esac
 117        shift
 118done
 119
 120test "$#" = 1 || usage
 121
 122all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
 123if test -z "$all_strategies"; then
 124        die "must enable at least one of -lrmt"
 125fi
 126
 127branch="$1"
 128test -z "$new_name" && new_name="$branch"
 129
 130if test ! -z "$scan_reflog"; then
 131        if test -r "$GIT_DIR"/logs/HEAD; then
 132                candidates="$(search_reflog $branch)"
 133        else
 134                die 'reflog scanning requested, but' \
 135                        '$GIT_DIR/logs/HEAD not readable'
 136        fi
 137fi
 138if test ! -z "$scan_reflog_merges"; then
 139        if test -r "$GIT_DIR"/logs/HEAD; then
 140                candidates="$candidates $(search_reflog_merges $branch)"
 141        else
 142                die 'reflog scanning requested, but' \
 143                        '$GIT_DIR/logs/HEAD not readable'
 144        fi
 145fi
 146if test ! -z "$scan_merges"; then
 147        candidates="$candidates $(search_merges $branch)"
 148fi
 149if test ! -z "$scan_merge_targets"; then
 150        candidates="$candidates $(search_merge_targets $branch)"
 151fi
 152
 153candidates="$(git rev-parse $candidates | sort -u)"
 154
 155if test -z "$candidates"; then
 156        hint=
 157        test "z$all_strategies" != "ztttt" \
 158                && hint=" (maybe try again with -a)"
 159        die "no candidates for $branch found$hint"
 160fi
 161
 162echo "** Candidates for $branch **"
 163for cmt in $candidates; do
 164        git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
 165done \
 166| sort -n | cut -d: -f2-
 167
 168newest="$(git rev-list -1 $candidates)"
 169if test ! -z "$dry_run"; then
 170        printf "** Most recent: "
 171        git --no-pager log -1 --pretty=tformat:"%h %s" $newest
 172elif ! git rev-parse --verify --quiet $new_name >/dev/null; then
 173        printf "** Restoring $new_name to "
 174        git --no-pager log -1 --pretty=tformat:"%h %s" $newest
 175        git branch $new_name $newest
 176else
 177        printf "Most recent: "
 178        git --no-pager log -1 --pretty=tformat:"%h %s" $newest
 179        echo "** $new_name already exists, doing nothing"
 180fi