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