X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=topgit.git;a=blobdiff_plain;f=tg.sh;h=a844b5ed2828a27d8018955913474bb5274040ed;hp=ee14e62860c2a34bda48aecdc7162128bf83fb48;hb=074eb3f1b432d95880fcc2c5b388bebac75046e9;hpb=58eccfa3f1f94f5b288a561bdb5fd482471737c0 diff --git a/tg.sh b/tg.sh index ee14e62..a844b5e 100644 --- a/tg.sh +++ b/tg.sh @@ -45,11 +45,11 @@ setup_hook() # setup_ours (no arguments) setup_ours() { - if [ ! -s "$git_dir/info/gitattributes" ] || ! grep -q topmsg "$git_dir/info/gitattributes"; then + if [ ! -s "$git_dir/info/attributes" ] || ! grep -q topmsg "$git_dir/info/attributes"; then { echo -e ".topmsg\tmerge=ours" echo -e ".topdeps\tmerge=ours" - } >>"$git_dir/info/gitattributes" + } >>"$git_dir/info/attributes" fi if ! git config merge.ours.driver >/dev/null; then git config merge.ours.name '"always keep ours" merge driver' @@ -60,11 +60,11 @@ setup_ours() # measure_branch NAME [BASE] measure_branch() { - _name="$1"; _base="$2" - [ -n "$_base" ] || _base="refs/top-bases/$_name" + _bname="$1"; _base="$2" + [ -n "$_base" ] || _base="refs/top-bases/$_bname" # The caller should've verified $name is valid - _commits="$(git rev-list "$_name" ^"$_base" | wc -l)" - _nmcommits="$(git rev-list --no-merges "$_name" ^"$_base" | wc -l)" + _commits="$(git rev-list "$_bname" ^"$_base" | wc -l)" + _nmcommits="$(git rev-list --no-merges "$_bname" ^"$_base" | wc -l)" if [ $_commits -gt 1 ]; then _suffix="commits" else @@ -80,45 +80,83 @@ branch_contains() [ -z "$(git rev-list ^"$1" "$2")" ] } -# needs_update NAME [BRANCHPATH...] +# recurse_deps CMD NAME [BRANCHPATH...] +# Recursively eval CMD on all dependencies of NAME. +# CMD can refer to $_name for queried branch name, +# $_dep for dependency name, +# $_depchain for space-seperated branch backtrace, +# and the $_dep_is_tgish boolean. +# It can modify $_ret to affect the return value +# of the whole function. +# If recurse_deps() hits missing dependencies, it will append +# them to space-separated $missing_deps list and skip them. +recurse_deps() +{ + _cmd="$1"; shift + _name="$1"; # no shift + _depchain="$*" + _depsfile="$(mktemp)" + git cat-file blob "$_name:.topdeps" >"$_depsfile" + _ret=0 + while read _dep; do + if ! git rev-parse --verify "$_dep" >/dev/null 2>&1; then + # All hope is lost + missing_deps="$missing_deps $_dep" + continue + fi + + _dep_is_tgish=1 + git rev-parse --verify "refs/top-bases/$_dep" >/dev/null 2>&1 || + _dep_is_tgish= + + # Shoo shoo, keep our environment alone! + [ -z "$_dep_is_tgish" ] || + (recurse_deps "$_cmd" "$_dep" "$@") || + _ret=$? + + eval "$_cmd" + done <"$_depsfile" + missing_deps="${missing_deps# }" + rm "$_depsfile" + return $_ret +} + +# branch_needs_update +# This is a helper function for determining whether given branch +# is up-to-date wrt. its dependencies. It expects input as if it +# is called as a recurse_deps() helper. +# In case the branch does need update, it will echo it together +# with the branch backtrace on the output (see needs_update() +# description for details) and set $_ret to non-zero. +branch_needs_update() +{ + _dep_base_uptodate=1 + if [ -n "$_dep_is_tgish" ]; then + branch_contains "$_dep" "refs/top-bases/$_dep" || _dep_base_uptodate= + fi + + if [ -z "$_dep_base_uptodate" ]; then + # _dep needs to be synced with its base + echo ": $_dep $_depchain" + _ret=1 + elif [ -n "$_name" ] && ! branch_contains "refs/top-bases/$_name" "$_dep"; then + # Some new commits in _dep + echo "$_dep $_depchain" + _ret=1 + fi +} + +# needs_update NAME # This function is recursive; it outputs reverse path from NAME # to the branch (e.g. B_DIRTY B1 B2 NAME), one path per line, # inner paths first. Innermost name can be ':' if the head is # not in sync with the base. +# It will also return non-zero status if NAME needs update. # If needs_update() hits missing dependencies, it will append # them to space-separated $missing_deps list and skip them. needs_update() { - { - git cat-file blob "$1:.topdeps" 2>/dev/null | - while read _dep; do - if !git rev-parse --verify "$_dep" >/dev/null 2>&1; then - # All hope is lost - missing_deps="$missing_deps $_dep" - continue - fi - - _dep_is_tgish=1 - git rev-parse --verify "refs/top-bases/$_dep" >/dev/null 2>&1 || - _dep_is_tgish= - - # Shoo shoo, keep our environment alone! - [ -z "$_dep_is_tgish" ] || (needs_update "$_dep" "$@") - - _dep_base_uptodate=1 - if [ -n "$_dep_is_tgish" ]; then - branch_contains "$_dep" "refs/top-bases/$_dep" || _dep_base_uptodate= - fi - - if [ -z "$_dep_base_uptodate" ]; then - # _dep needs to be synced with its base - echo ": $_dep $*" - elif ! branch_contains "refs/top-bases/$1" "$_dep"; then - # Some new commits in _dep - echo "$_dep $*" - fi - done - } || : # $1 is not tracked by TopGit anymore + recurse_deps branch_needs_update "$@" } # branch_empty NAME @@ -140,6 +178,32 @@ switch_to_base() git symbolic-ref HEAD "$_base" } +# Show the help messages. +do_help() +{ + if [ -z "$1" ] ; then + ## Build available commands list for help output + + cmds= + sep= + for cmd in "@cmddir@"/tg-*; do + ! [ -r "$cmd" ] && continue + # strip directory part and "tg-" prefix + cmd="$(basename "$cmd")" + cmd="${cmd#tg-}" + cmds="$cmds$sep$cmd" + sep="|" + done + + echo "TopGit v0.1 - A different patch queue manager" + echo "Usage: tg ($cmds|help) ..." + elif [ -r "@sharedir@/tg-$1.txt" ] ; then + cat "@sharedir@/tg-$1.txt" + else + echo "`basename $0`: no help for $1" 1>&2 + fi +} + ## Initial setup @@ -150,6 +214,8 @@ root_dir="$(git rev-parse --show-cdup)"; root_dir="${root_dir:-.}" setup_ours setup_hook "pre-commit" +[ -d "@cmddir@" ] || + die "No command directory: '@cmddir@'" ## Dispatch @@ -162,16 +228,16 @@ cmd="$1" shift case "$cmd" in -help) - echo "TopGit v0.1 - A different patch queue manager" - echo "Usage: tg (create|delete|info|patch|summary|update|help) ..." +help|--help|-h) + do_help "$1" exit 1;; -create|delete|info|patch|summary|update) - . "@cmddir@"/tg-$cmd;; --hooks-path) # Internal command echo "@hooksdir@";; *) - echo "Unknown subcommand: $cmd" >&2 - exit 1;; + [ -r "@cmddir@"/tg-$cmd ] || { + echo "Unknown subcommand: $cmd" >&2 + exit 1 + } + . "@cmddir@"/tg-$cmd;; esac