@matheusbsbs / matheus-bagliano / commits / e1ff064e1b
Fork 0

contrib git-resurrect: find traces of a branch name and resurrect it

Add a tool 'git-resurrect.sh <branch>' that tries to find traces of the <branch> in the HEAD reflog and, optionally, all merge commits in the repository. It can then resurrect the branch, pointing it at the most recent of all candidate commits found. Signed-off-by: Thomas Rast <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Thomas Rast committed Feb 4, 2009 at 11:04 UTC e1ff064e1bffedb917c491681b7bfd4d66633ef5
1 file changed +180
contrib/git-resurrect.sh new
+180
@@ -0,0 +1,180 @@
1 +#!/bin/sh
2 +
3 +USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
4 +LONG_USAGE="git-resurrect attempts to find traces of a branch tip
5 +called <name>, and tries to resurrect it. Currently, the reflog is
6 +searched 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
8 +other/Merge <other> into <name> (respectively) commit subjects, which
9 +is rather slow but allows you to resurrect other people's topic
10 +branches."
11 +
12 +OPTIONS_SPEC="\
13 +git resurrect $USAGE
14 +--
15 +b,branch= save branch as <newname> instead of <name>
16 +a,all same as -l -r -m -t
17 +k,keep-going full rev-list scan (instead of first match)
18 +l,reflog scan reflog for checkouts (enabled by default)
19 +r,reflog-merges scan for merges recorded in reflog
20 +m,merges scan for merges into other branches (slow)
21 +t,merge-targets scan for merges of other branches into <name>
22 +n,dry-run don't recreate the branch"
23 +
24 +. git-sh-setup
25 +
26 +search_reflog () {
27 + sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
28 + < "$GIT_DIR"/logs/HEAD
29 +}
30 +
31 +search_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 +
41 +search_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 +
47 +search_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 +
53 +dry_run=
54 +early_exit=q
55 +scan_reflog=t
56 +scan_reflog_merges=
57 +scan_merges=
58 +scan_merge_targets=
59 +new_name=
60 +
61 +while 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
118 +done
119 +
120 +test "$#" = 1 || usage
121 +
122 +all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
123 +if test -z "$all_strategies"; then
124 + die "must enable at least one of -lrmt"
125 +fi
126 +
127 +branch="$1"
128 +test -z "$new_name" && new_name="$branch"
129 +
130 +if 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
137 +fi
138 +if 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
145 +fi
146 +if test ! -z "$scan_merges"; then
147 + candidates="$candidates $(search_merges $branch)"
148 +fi
149 +if test ! -z "$scan_merge_targets"; then
150 + candidates="$candidates $(search_merge_targets $branch)"
151 +fi
152 +
153 +candidates="$(git rev-parse $candidates | sort -u)"
154 +
155 +if 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"
160 +fi
161 +
162 +echo "** Candidates for $branch **"
163 +for cmt in $candidates; do
164 + git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
165 +done \
166 +| sort -n | cut -d: -f2-
167 +
168 +newest="$(git rev-list -1 $candidates)"
169 +if test ! -z "$dry_run"; then
170 + printf "** Most recent: "
171 + git --no-pager log -1 --pretty=tformat:"%h %s" $newest
172 +elif ! 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
176 +else
177 + printf "Most recent: "
178 + git --no-pager log -1 --pretty=tformat:"%h %s" $newest
179 + echo "** $new_name already exists, doing nothing"
180 +fi