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