X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=topgit.git;a=blobdiff_plain;f=tg.sh;h=5bb2d0cd123f09b1b038b04ed3b9a17bc4495a5d;hp=e5766fe8cc5b460a09a6ea4d5e9e105dc64d74a9;hb=4fe0a8e009776b8c66357f08ef8171bdc2c0f468;hpb=11a91e470992973293cc18e701baa8ce0da83453 diff --git a/tg.sh b/tg.sh index e5766fe..5bb2d0c 100644 --- a/tg.sh +++ b/tg.sh @@ -20,13 +20,13 @@ die() # setup_hook NAME setup_hook() { - hook_call="\"\$(tg --hooks-path)\"/$1 \"\$@\"" + hook_call="\"\$($tg --hooks-path)\"/$1 \"\$@\"" if [ -f "$git_dir/hooks/$1" ] && fgrep -q "$hook_call" "$git_dir/hooks/$1"; then # Another job well done! return fi - # Prepare incanation + # Prepare incantation if [ -x "$git_dir/hooks/$1" ]; then hook_call="$hook_call"' || exit $?' else @@ -63,8 +63,8 @@ measure_branch() _bname="$1"; _base="$2" [ -n "$_base" ] || _base="refs/top-bases/$_bname" # The caller should've verified $name is valid - _commits="$(git rev-list "$_bname" ^"$_base" | wc -l)" - _nmcommits="$(git rev-list --no-merges "$_bname" ^"$_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 @@ -77,7 +77,31 @@ measure_branch() # Whether B1 is a superset of B2. branch_contains() { - [ -z "$(git rev-list ^"$1" "$2")" ] + [ -z "$(git rev-list --max-count=1 ^"$1" "$2" --)" ] +} + +# ref_exists REF +# Whether REF is a valid ref name +ref_exists() +{ + git rev-parse --verify "$@" >/dev/null 2>&1 +} + +# has_remote BRANCH +# Whether BRANCH has a remote equivalent (accepts top-bases/ too) +has_remote() +{ + [ -n "$base_remote" ] && ref_exists "remotes/$base_remote/$1" +} + +branch_annihilated() +{ + _name="$1"; + + # use the merge base in case the base is ahead. + mb="$(git merge-base "refs/top-bases/$_name" "$_name")"; + + test "$(git rev-parse "$mb^{tree}")" = "$(git rev-parse "$_name^{tree}")"; } # recurse_deps CMD NAME [BRANCHPATH...] @@ -95,18 +119,30 @@ recurse_deps() _cmd="$1"; shift _name="$1"; # no shift _depchain="$*" - _depsfile="$(mktemp)" - git cat-file blob "$_name:.topdeps" >"$_depsfile" + + _depsfile="$(mktemp -t tg-depsfile.XXXXXX)" + # Check also our base against remote base. Checking our head + # against remote head has to be done in the helper. + if has_remote "top-bases/$_name"; then + echo "refs/remotes/$base_remote/top-bases/$_name" >>"$_depsfile" + fi + + # if the branch was annihilated, there exists no .topdeps file + if ! branch_annihilated "$_name"; then + #TODO: handle nonexisting .topdeps? + git cat-file blob "$_name:.topdeps" >>"$_depsfile"; + fi; + _ret=0 while read _dep; do - if ! git rev-parse --verify "$_dep" >/dev/null 2>&1; then + if ! ref_exists "$_dep" ; 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 || + ref_exists "refs/top-bases/$_dep" || _dep_is_tgish= # Shoo shoo, keep our environment alone! @@ -130,14 +166,19 @@ recurse_deps() # description for details) and set $_ret to non-zero. branch_needs_update() { - _dep_base_uptodate=1 + _dep_base_update= if [ -n "$_dep_is_tgish" ]; then - branch_contains "$_dep" "refs/top-bases/$_dep" || _dep_base_uptodate= + if has_remote "$_dep"; then + branch_contains "$_dep" "refs/remotes/$base_remote/$_dep" || _dep_base_update=% + fi + # This can possibly override the remote check result; + # we want to sync with our base first + branch_contains "$_dep" "refs/top-bases/$_dep" || _dep_base_update=: fi - if [ -z "$_dep_base_uptodate" ]; then - # _dep needs to be synced with its base - echo ": $_dep $_depchain" + if [ -n "$_dep_base_update" ]; then + # _dep needs to be synced with its base/remote + echo "$_dep_base_update $_dep $_depchain" _ret=1 elif [ -n "$_name" ] && ! branch_contains "refs/top-bases/$_name" "$_dep"; then # Some new commits in _dep @@ -150,7 +191,8 @@ branch_needs_update() # 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. +# not in sync with the base or '%' if the head is not in sync +# with the remote (in this order of priority). # 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. @@ -182,6 +224,12 @@ switch_to_base() do_help() { if [ -z "$1" ] ; then + # This is currently invoked in all kinds of circumstances, + # including when the user made a usage error. Should we end up + # providing more than a short help message, then we should + # differentiate. + # Petr's comment: http://marc.info/?l=git&m=122718711327376&w=2 + ## Build available commands list for help output cmds= @@ -195,49 +243,75 @@ do_help() sep="|" done - echo "TopGit v0.2 - A different patch queue manager" - echo "Usage: tg ($cmds|help) ..." - elif [ -r "@sharedir@/tg-$1.txt" ] ; then - cat "@sharedir@/tg-$1.txt" + echo "TopGit v0.5 - A different patch queue manager" + echo "Usage: tg [-r REMOTE] ($cmds|help) ..." + elif [ -r "@cmddir@"/tg-$1 ] ; then + @cmddir@/tg-$1 -h || : + echo + if [ -r "@sharedir@/tg-$1.txt" ] ; then + cat "@sharedir@/tg-$1.txt" + fi else echo "`basename $0`: no help for $1" 1>&2 + do_help + exit 1 fi } +## Startup + +[ -d "@cmddir@" ] || + die "No command directory: '@cmddir@'" + ## Initial setup set -e git_dir="$(git rev-parse --git-dir)" root_dir="$(git rev-parse --show-cdup)"; root_dir="${root_dir:-.}" +# Make sure root_dir doesn't end with a trailing slash. +root_dir="${root_dir%/}" +base_remote="$(git config topgit.remote 2>/dev/null)" || : +tg="tg" # make sure merging the .top* files will always behave sanely setup_ours setup_hook "pre-commit" -[ -d "@cmddir@" ] || - die "No command directory: '@cmddir@'" - ## Dispatch # We were sourced from another script for our utility functions; # this is set by hooks. [ -z "$tg__include" ] || return 0 +if [ "$1" = "-r" ]; then + shift + if [ -z "$1" ]; then + echo "Option -r requires an argument." >&2 + do_help + exit 1 + fi + base_remote="$1"; shift + tg="$tg -r $base_remote" +fi + cmd="$1" -[ -n "$cmd" ] || die "He took a duck in the face at two hundred and fifty knots" +[ -n "$cmd" ] || { do_help; exit 1; } shift case "$cmd" in help|--help|-h) do_help "$1" - exit 1;; + exit 0;; --hooks-path) # Internal command echo "@hooksdir@";; *) [ -r "@cmddir@"/tg-$cmd ] || { echo "Unknown subcommand: $cmd" >&2 + do_help exit 1 } . "@cmddir@"/tg-$cmd;; esac + +# vim:noet