Add a tool 'git resurrect <branch>...' that tries to find traces of each <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@xxxxxxxxxxxxxxx> --- So here's a slightly more polished version so gmane can keep it forever. Thanks for the sed trick! I was too lazy to add more options, but at least there's a "fast" and a "complete" mode. Makefile | 1 + git-resurrect.sh | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 0 deletions(-) create mode 100755 git-resurrect.sh diff --git a/Makefile b/Makefile index 2b873fa..87cb539 100644 --- a/Makefile +++ b/Makefile @@ -260,6 +260,7 @@ SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-parse-remote.sh SCRIPT_SH += git-pull.sh +SCRIPT_SH += git-resurrect.sh SCRIPT_SH += git-quiltimport.sh SCRIPT_SH += git-rebase--interactive.sh SCRIPT_SH += git-rebase.sh diff --git a/git-resurrect.sh b/git-resurrect.sh new file mode 100755 index 0000000..6d5a0c7 --- /dev/null +++ b/git-resurrect.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +USAGE="git resurrect [-m | --merges] [-n | --dry-run] <name>..." +LONG_USAGE="git-resurrect attempts to find traces of a branch tip called <name>, +and tries to resurrect it. Currently, the reflog is searched for +checkout and merge messages. With --merges, the history of all refs +is scanned for merge commit subjects, which is rather slow but allows +you to resurrect other people's topic branches." + +. git-sh-setup +cd_to_toplevel + +OPTIONS_SPEC="\ +git resurrect [-m | --merges] [-n | --dry-run] <name>... +-- +m,merges also scan merges (slow) +n,dry-run don't recreate the branch" + +test "$#" = 0 && usage + +eval "$(echo "$OPTIONS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" + +search_reflog () { + sed -n 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \ + < .git/logs/HEAD +} + +search_reflog_merges () { + sed -n 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':~\1~p' \ + < .git/logs/HEAD +} + +search_merges () { + git rev-list --pretty=tformat:"%h %p:%s" --all | + grep "Merge branch.*'$branch'.*into" | + while read sha rest; do + parents="$(echo "$rest" | cut -d: -f1)" + case "$parents" in + *' '*' '*) + warn "$branch took part in octopus merge $sha" + warn "check manually!" + ;; + *' '*) + echo "$parents" | cut -d' ' -f2 + ;; + esac + done +} + +search_merge_targets () { + git rev-list --pretty=tformat:"%h %s" --all | + grep "Merge branch '[^']*' into $branch$" | + cut -d' ' -f1 +} + +dry_run= +scan_merges= + +while test "$#" != 0; do + case "$1" in + -n|--dry-run) + dry_run=t + ;; + -m|--merges) + scan_merges=t + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done + +for branch in "$@"; do + candidates="$(search_reflog $1; search_reflog_merges $1)" + if test ! -z "$scan_merges"; then + candidates="$candidates $(search_merges $1; search_merge_targets $1)" + fi + + candidates="$(git rev-parse $candidates | sort -u)" + + if test -z "$candidates"; then + echo "** No candidates for $branch found **" + test -z "$scan_merges" && echo "(maybe try again with -m)" + else + echo "** Candidates for $branch **" + for cmt in $candidates; do + git --no-pager log --pretty=oneline --abbrev-commit -1 $cmt + done + + newest="$(git rev-list -1 $candidates)" + if test ! -z "$dry_run"; then + printf "Most recent: " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + elif ! git rev-parse --verify --quiet $branch >/dev/null; then + printf "** Restoring $branch to " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + git branch $branch $newest + else + printf "Most recent: " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + echo "** $branch already exists, doing nothing" + fi + fi +done -- 1.6.1.320.gd5dca.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html