-prefix = $(HOME)
-bindir = $(prefix)/bin
-cmddir = $(prefix)/share/topgit
-docdir = $(prefix)/share/doc/topgit
-hooksdir = $(cmddir)/hooks
+prefix ?= $(HOME)
+bindir := $(prefix)/bin
+cmddir := $(prefix)/share/topgit
+docdir := $(prefix)/share/doc/topgit
+hooksdir := $(cmddir)/hooks
-commands_in = $(wildcard tg-*.sh)
+commands_in := $(wildcard tg-*.sh)
hooks_in = hooks/pre-commit.sh
-commands_out = $(patsubst %.sh,%,$(commands_in))
-hooks_out = $(patsubst %.sh,%,$(hooks_in))
-help_out = $(patsubst %.sh,%.txt,$(commands_in))
+commands_out := $(patsubst %.sh,%,$(commands_in))
+hooks_out := $(patsubst %.sh,%,$(hooks_in))
+help_out := $(patsubst %.sh,%.txt,$(commands_in))
all:: tg $(commands_out) $(hooks_out) $(help_out)
you track many patches where many are independent but some depend
on others, TopGit ignores the ancient Quilt heritage of patch series
and instead allows the patches to freely form graphs (DAGs just like
-Git history itself, only "one lever higher"). For now, you have
+Git history itself, only "one level higher"). For now, you have
to manually specify which patches does the current one depend
on, but TopGit might help you with that in the future in a darcs-like
fashion.
adjusting '.topmsg', prepare them in the index before
calling 'tg depend add'.
+ TODO: Subcommand for removing dependencies, obviously
+
tg info
~~~~~~~
Show a summary information about the current or specified
its documentation for details on how to setup email for git.
You can pass arbitrary options to this command through the
'-s' parameter, but you must double-quote everything.
+ The '-r' parameter with msgid can be used to generate in-reply-to
+ and reference headers to an earlier mail.
TODO: 'tg mail patchfile' to mail an already exported patch
TODO: mailing patch series
'!' marks that it has missing dependencies (even recursively),
'B' marks that it is out-of-date wrt. its base).
+ This can take long time to accurately determine all the relevant
+ information about each branch; you can pass '-t' to get just
+ terse list of topic branch names quickly. Alternately, you can
+ pass '--graphviz' to get a dot-suitable output to draw a dependency
+ graph between the topic branches.
+
TODO: Speed up by an order of magnitude
- TODO: Graph view
+ TODO: Text graph view
tg export
~~~~~~~~~
~~~~~~~~~
Import commits within the given revision range into TopGit,
creating one topic branch per commit, the dependencies forming
- a linear sequence starting on your current branch.
+ a linear sequence starting on your current branch (or a branch
+ specified by the '-d' parameter).
The branch names are auto-guessed from the commit messages
and prefixed by t/ by default; use '-p PREFIX' to specify
an alternative prefix (even an empty one).
+ Alternatively, you can use the '-s NAME' parameter to specify
+ the name of target branch; the command will then take one more
+ argument describing a single commit to import.
+
tg update
~~~~~~~~~
Update the current topic branch wrt. changes in the branches
.topdeps: Contains the one-per-line list of branches
your patch depends on, pre-seeded with `tg create`. (Continuously
updated) merge of these branches will be the "base" of your topic
-branch.
+branch. DO NOT EDIT THIS FILE MANUALLY!!! If you do so, you need
+to know exactly what are you doing, since this file must stay in
+sync with the Git history information, otherwise very bad things
+will happen.
TopGit also automagically installs a bunch of custom commit-related
hooks that will verify if you are committing the .top* files in sane
--- /dev/null
+#
+# bash completion support for TopGit.
+#
+# Copyright (C) 2008 Jonas Fonseca <fonseca@diku.dk>
+# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
+# Based git's git-completion.sh: http://repo.or.cz/w/git/fastimport.git
+# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
+# Distributed under the GNU General Public License, version 2.0.
+#
+# To use these routines:
+#
+# 1) Copy this file to somewhere (e.g. ~/.tg-completion.sh).
+# 2) Source it from your ~/.bashrc.
+#
+# Note: Make sure the tg script is in your PATH before you source this
+# script, so it can properly setup cached values.
+
+case "$COMP_WORDBREAKS" in
+*:*) : great ;;
+*) COMP_WORDBREAKS="$COMP_WORDBREAKS:"
+esac
+
+### {{{ Utilities
+
+__tgdir ()
+{
+ if [ -z "$1" ]; then
+ if [ -n "$__tg_dir" ]; then
+ echo "$__tg_dir"
+ elif [ -d .git ]; then
+ echo .git
+ else
+ git rev-parse --git-dir 2>/dev/null
+ fi
+ elif [ -d "$1/.git" ]; then
+ echo "$1/.git"
+ else
+ echo "$1"
+ fi
+}
+
+__tgcomp_1 ()
+{
+ local c IFS=' '$'\t'$'\n'
+ for c in $1; do
+ case "$c$2" in
+ --*=*) printf %s$'\n' "$c$2" ;;
+ *.) printf %s$'\n' "$c$2" ;;
+ *) printf %s$'\n' "$c$2 " ;;
+ esac
+ done
+}
+
+__tgcomp ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ if [ $# -gt 2 ]; then
+ cur="$3"
+ fi
+ case "$cur" in
+ --*=)
+ COMPREPLY=()
+ ;;
+ *)
+ local IFS=$'\n'
+ COMPREPLY=($(compgen -P "$2" \
+ -W "$(__tgcomp_1 "$1" "$4")" \
+ -- "$cur"))
+ ;;
+ esac
+}
+
+__tg_heads ()
+{
+ local cmd i is_hash=y dir="$(__tgdir "$1")"
+ if [ -d "$dir" ]; then
+ git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+ refs/heads
+ return
+ fi
+ for i in $(git ls-remote "$1" 2>/dev/null); do
+ case "$is_hash,$i" in
+ y,*) is_hash=n ;;
+ n,*^{}) is_hash=y ;;
+ n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+ n,*) is_hash=y; echo "$i" ;;
+ esac
+ done
+}
+
+__tg_refs ()
+{
+ local cmd i is_hash=y dir="$(__tgdir "$1")"
+ if [ -d "$dir" ]; then
+ if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+ git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+ refs/tags refs/heads refs/remotes
+ return
+ fi
+ for i in $(git ls-remote "$dir" 2>/dev/null); do
+ case "$is_hash,$i" in
+ y,*) is_hash=n ;;
+ n,*^{}) is_hash=y ;;
+ n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
+ n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+ n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
+ n,*) is_hash=y; echo "$i" ;;
+ esac
+ done
+}
+
+__tg_refs2 ()
+{
+ local i
+ for i in $(__tg_refs "$1"); do
+ echo "$i:$i"
+ done
+}
+
+__tg_refs_remotes ()
+{
+ local cmd i is_hash=y
+ for i in $(git ls-remote "$1" 2>/dev/null); do
+ case "$is_hash,$i" in
+ n,refs/heads/*)
+ is_hash=y
+ echo "$i:refs/remotes/$1/${i#refs/heads/}"
+ ;;
+ y,*) is_hash=n ;;
+ n,*^{}) is_hash=y ;;
+ n,refs/tags/*) is_hash=y;;
+ n,*) is_hash=y; ;;
+ esac
+ done
+}
+
+__tg_remotes ()
+{
+ local i ngoff IFS=$'\n' d="$(__tgdir)"
+ shopt -q nullglob || ngoff=1
+ shopt -s nullglob
+ for i in "$d/remotes"/*; do
+ echo ${i#$d/remotes/}
+ done
+ [ "$ngoff" ] && shopt -u nullglob
+ for i in $(git --git-dir="$d" config --list); do
+ case "$i" in
+ remote.*.url=*)
+ i="${i#remote.}"
+ echo "${i/.url=*/}"
+ ;;
+ esac
+ done
+}
+
+__tg_find_subcommand ()
+{
+ local word subcommand c=1
+
+ while [ $c -lt $COMP_CWORD ]; do
+ word="${COMP_WORDS[c]}"
+ for subcommand in $1; do
+ if [ "$subcommand" = "$word" ]; then
+ echo "$subcommand"
+ return
+ fi
+ done
+ c=$((++c))
+ done
+}
+
+__tg_complete_revlist ()
+{
+ local pfx cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ *...*)
+ pfx="${cur%...*}..."
+ cur="${cur#*...}"
+ __tgcomp "$(__tg_refs)" "$pfx" "$cur"
+ ;;
+ *..*)
+ pfx="${cur%..*}.."
+ cur="${cur#*..}"
+ __tgcomp "$(__tg_refs)" "$pfx" "$cur"
+ ;;
+ *)
+ __tgcomp "$(__tg_refs)"
+ ;;
+ esac
+}
+
+__tg_topics ()
+{
+ tg summary -t
+}
+
+__tg_commands ()
+{
+ if [ -n "$__tg_all_commandlist" ]; then
+ echo "$__tg_all_commandlist"
+ return
+ fi
+ local i IFS=" "$'\n'
+ for i in $(tg help | sed -n 's/^Usage:.*(\(.*\)).*/\1/p' | tr '|' ' ')
+ do
+ case $i in
+ *--*) : helper pattern;;
+ *) echo $i;;
+ esac
+ done
+}
+__tg_all_commandlist=
+__tg_all_commandlist="$(__tg_commands 2>/dev/null)"
+
+__tg_complete_arg ()
+{
+ if [ $COMP_CWORD -gt 2 ] && [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "$1" ]; then
+ return 0
+ fi
+ return 1
+}
+
+### }}}
+### {{{ Commands
+
+_tg_create ()
+{
+ local cmd="$1"
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ # Name must be the first arg after the create command
+ if [ $((cmd + 1)) = $COMP_CWORD ]; then
+ __tgcomp "$(__tg_topics)"
+ return
+ fi
+
+ __tg_complete_arg "-r" && {
+ __tgcomp "$(__tg_remotes)"
+ return
+ }
+
+ case "$cur" in
+ -*)
+ __tgcomp "
+ -r
+ "
+ ;;
+ *)
+ __tgcomp "$(__tg_refs)"
+ esac
+}
+
+_tg_delete ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "$cur" in
+ -*)
+ __tgcomp "
+ -f
+ "
+ ;;
+ *)
+ __tgcomp "$(__tg_topics)"
+ esac
+}
+
+_tg_depend ()
+{
+ local subcommands="add"
+ local subcommand="$(__git_find_subcommand "$subcommands")"
+ if [ -z "$subcommand" ]; then
+ __tgcomp "$subcommands"
+ return
+ fi
+
+ case "$subcommand" in
+ add)
+ __tgcomp "$(__tg_refs)"
+ esac
+}
+
+_tg_export ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ __tg_complete_arg "--collapse" && {
+ __tgcomp "$(__tg_heads)"
+ return
+ }
+
+ __tg_complete_arg "--quilt" && {
+ return
+ }
+
+ case "$cur" in
+ *)
+ __tgcomp "
+ --collapse
+ --quilt
+ "
+ esac
+}
+
+_tg_help ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ -*)
+ COMPREPLY=()
+ return
+ ;;
+ esac
+ __tgcomp "$(__tg_commands)"
+}
+
+_tg_import ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ __tg_complete_arg "-p" && {
+ COMPREPLY=()
+ return
+ }
+
+ case "$cur" in
+ -*)
+ __tgcomp "
+ -p
+ "
+ ;;
+ *)
+ __tg_complete_revlist
+ esac
+}
+
+_tg_info ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "$cur" in
+ *)
+ __tgcomp "$(__tg_topics)"
+ esac
+}
+
+_tg_mail ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "$cur" in
+ *)
+ __tgcomp "$(__tg_topics)"
+ esac
+}
+
+_tg_patch ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "$cur" in
+ *)
+ __tgcomp "$(__tg_topics)"
+ esac
+}
+
+_tg_remote ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ case "$cur" in
+ *)
+ __tgcomp "$(__tg_remotes)"
+ esac
+}
+
+_tg_summary ()
+{
+ COMPREPLY=()
+}
+
+_tg_update ()
+{
+ COMPREPLY=()
+}
+
+### }}}
+### {{{ tg completion
+
+_tg ()
+{
+ local i c=1 command __tg_dir
+
+ while [ $c -lt $COMP_CWORD ]; do
+ i="${COMP_WORDS[c]}"
+ case "$i" in
+ -r)
+ c=$((++c))
+ if [ $c -lt $COMP_CWORD ]; then
+ __tgcomp "$(__tg_remotes)"
+ return
+ fi
+ ;;
+ -h|--help) command="help"; break ;;
+ *) command="$i"; break ;;
+ esac
+ c=$((++c))
+ done
+
+ if [ -z "$command" ]; then
+ case "${COMP_WORDS[COMP_CWORD]}" in
+ -*) __tgcomp "
+ -r
+ -h
+ --help
+ "
+ ;;
+ *) __tgcomp "$(__tg_commands)" ;;
+ esac
+ return
+ fi
+
+ case "$command" in
+ create) _tg_create "$c" ;;
+ delete) _tg_delete ;;
+ depend) _tg_depend ;;
+ export) _tg_export ;;
+ help) _tg_help ;;
+ import) _tg_import ;;
+ info) _tg_info ;;
+ mail) _tg_mail ;;
+ patch) _tg_patch ;;
+ remote) _tg_remote ;;
+ summary) _tg_summary ;;
+ update) _tg_update ;;
+ *) COMPREPLY=() ;;
+ esac
+}
+
+### }}}
+
+complete -o default -o nospace -F _tg tg
+
+# The following are necessary only for Cygwin, and only are needed
+# when the user has tab-completed the executable name and consequently
+# included the '.exe' suffix.
+#
+if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
+complete -o default -o nospace -F _tg tg.exe
+fi
Building topgit for Debian
--------------------------
-The topgit source package uses quilt to apply and remove its patches. Please
+The topgit source package uses quilt to apply and remove its patches. Please
refer to /usr/share/doc/quilt/README.source for information about how to use
quilt for source packages.
2. cd topgit
3. tg remote --populate origin
+TODO: debcheckout support
+
Branches in use
'''''''''''''''
The following branches are in use in the package:
This process is still very cumbersome and needs to be improved, ideally within
TopGit.
+TODO: provide Makefile snippet for the above to prevent useless duplication (#501991)
+TODO: rewrite to use tg-export -b, which will be fixed in TopGit 0.5 (#500273)
+
5. Importing a new upstream version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To import a new upstream, update the remote, merge the tag you want to merge
-topgit (0.4-1) unstable; urgency=low
+topgit (0.5-1) unstable; urgency=low
* New upstream release.
## Parse options
-subcmd="$1"; shift
-[ "$subcmd" = "add" ] || die "unknown subcommand ($subcmd)"
+subcmd="$1"; shift || :
+case "$subcmd" in
+ -h|"")
+ echo "Usage: tg [...] depend add NAME" >&2
+ exit 1;;
+ add)
+ ;;
+ *)
+ die "unknown subcommand ($subcmd)";;
+esac
while [ -n "$1" ]; do
arg="$1"; shift
## Record new dependency
-echo "$name" >>.topdeps
-git add .topdeps
+echo "$name" >>"$root_dir/.topdeps"
+git add -f "$root_dir/.topdeps"
git commit -m"New TopGit dependency: $name"
$tg update
--collapse)
driver=collapse;;
-*)
- echo "Usage: tg [...] export [-b BRANCH1,BRANCH2...] ([--collapse] NEWBRANCH | --quilt DIRECTORY)" >&2
+ echo "Usage: tg [...] export ([--collapse] NEWBRANCH | [-b BRANCH1,BRANCH2...] --quilt DIRECTORY)" >&2
exit 1;;
*)
[ -z "$output" ] || die "output already specified ($output)"
done
-name="$(git symbolic-ref HEAD | sed 's#^refs/heads/##')"
-base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" ||
- die "not on a TopGit-controlled branch"
[ -z "$branches" -o "$driver" = "quilt" ] ||
die "-b works only with the quilt driver"
+if [ -z "$branches" ]; then
+ # this check is only needed when no branches have been passed
+ name="$(git symbolic-ref HEAD | sed 's#^refs/heads/##')"
+ base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" ||
+ die "not on a TopGit-controlled branch"
+fi
+
playground="$(mktemp -d -t tg-export.XXXXXX)"
trap 'rm -rf "$playground"' EXIT
# GPLv2
branch_prefix=t/
+single=
ranges=
+basedep=
## Parse options
while [ -n "$1" ]; do
arg="$1"; shift
case "$arg" in
+ -d)
+ basedep="$1"; shift;;
-p)
branch_prefix="$1"; shift;;
+ -s)
+ single="$1"; shift;;
-*)
- echo "Usage: tg [...] import [-p PREFIX] RANGE..." >&2
+ echo "Usage: tg [...] import [-d BASE_BRANCH] {[-p PREFIX] RANGE...|-s NAME COMMIT}" >&2
exit 1;;
*)
ranges="$ranges $arg";;
get_commit_msg()
{
commit="$1"
- git log -1 --pretty=format:"From: %an <%ae>%n%n%s%n%n%b" "$commit"
+ headers=""
+ ! header="$(git config topgit.to)" || headers="$headers%nTo: $header"
+ ! header="$(git config topgit.cc)" || headers="$headers%nCc: $header"
+ ! header="$(git config topgit.bcc)" || headers="$headers%nBcc: $header"
+ git log -1 --pretty=format:"From: %an <%ae>$headers%nSubject: %s%n%n%b" "$commit"
}
get_branch_name()
process_commit()
{
commit="$1"
- branch_name=$(get_branch_name "$commit")
- info "---- Importing $commit to $branch_prefix$branch_name"
- tg create "$branch_prefix""$branch_name"
- git cherry-pick --no-commit "$commit"
+ branch_name="$2"
+ info "---- Importing $commit to $branch_name"
+ tg create "$branch_name" $basedep
+ basedep=
get_commit_msg "$commit" > .topmsg
git add -f .topmsg .topdeps
+ if ! git cherry-pick --no-commit "$commit"; then
+ info "The commit will also finish the import of this patch."
+ exit 2
+ fi
git commit -C "$commit"
info "++++ Importing $commit finished"
}
+if [ -n "$single" ]; then
+ process_commit $ranges "$single"
+ exit
+fi
+
# nice arg verification stolen from git-format-patch.sh
for revpair in $ranges
do
info "Merged already: $comment"
;;
*)
- process_commit "$rev"
+ process_commit "$rev" "$branch_prefix$(get_branch_name "$rev")"
;;
esac
done
name=
send_email_args=
+in_reply_to=
## Parse options
case "$arg" in
-s)
send_email_args="$1"; shift;;
+ -r)
+ in_reply_to="$1"; shift;;
-*)
- echo "Usage: tg [...] mail [-s SEND_EMAIL_ARGS] [NAME]" >&2
+ echo "Usage: tg [...] mail [-s SEND_EMAIL_ARGS] [-r REFERENCE_MSGID] [NAME]" >&2
exit 1;;
*)
[ -z "$name" ] || die "name already specified ($name)"
base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" ||
die "not a TopGit-controlled branch"
+if [ -n "$in_reply_to" ]; then
+ send_email_args="$send_email_args --in-reply-to=$in_reply_to"
+fi
+
patchfile="$(mktemp -t tg-mail.XXXXXX)"
-$tg patch $name >"$patchfile"
+$tg patch "$name" >"$patchfile"
-hlines=$(grep -n -m 1 '^---' "$patchfile" | sed 's/:---//')
-header=$(head -n $(($hlines - 1)) "$patchfile")
+header="$(sed -e '/^$/,$d' "$patchfile")"
to="$(echo "$header" | grep '^To:' | sed 's/To:\s*//')"
-# XXX: I can't get quoting right without arrays
-people=()
-[ -n "$from" ] && people=("${people[@]}" --from "$from")
+people=
+[ -n "$from" ] && people="$people --from '$from'"
# FIXME: there could be multimple To
-[ -n "$to" ] && people=("${people[@]}" --to "$to")
-
+[ -n "$to" ] && people="$people --to '$to'"
# NOTE: git-send-email handles cc itself
-git send-email $send_email_args "${people[@]}" "$patchfile"
+eval git send-email $send_email_args "$people" "$patchfile"
rm "$patchfile"
# (c) Petr Baudis <pasky@suse.cz> 2008
# GPLv2
+terse=
+graphviz=
+
## Parse options
-if [ -n "$1" ]; then
- echo "Usage: tg [...] summary" >&2
- exit 1
-fi
+while [ -n "$1" ]; do
+ arg="$1"; shift
+ case "$arg" in
+ -t)
+ terse=1;;
+ --graphviz)
+ graphviz=1;;
+ *)
+ echo "Usage: tg [...] summary [-t | --graphviz]" >&2
+ exit 1;;
+ esac
+done
curname="$(git symbolic-ref HEAD | sed 's#^refs/\(heads\|top-bases\)/##')"
+if [ -n "$graphviz" ]; then
+ cat <<EOT
+# GraphViz output; pipe to:
+# | dot -Tpng -o <ouput>
+# or
+# | dot -Txlib
+
+digraph G {
+
+graph [
+ rankdir = "TB"
+ label="TopGit Layout\n\n\n"
+ fontsize = 14
+ labelloc=top
+ pad = "0.5,0.5"
+];
+
+EOT
+fi
+
## List branches
git for-each-ref refs/top-bases |
while read rev type ref; do
name="${ref#refs/top-bases/}"
+ if [ -n "$terse" ]; then
+ echo "$name"
+ continue
+ fi
+ if [ -n "$graphviz" ]; then
+ git cat-file blob "$name:.topdeps" | while read dep; do
+ echo "\"$name\" -> \"$dep\";"
+ done
+ continue
+ fi
+
missing_deps=
current=' '
printf '%s\t%-31s\t%s\n' "$current$nonempty$remote$rem_update$deps_update$deps_missing$base_update" \
"$name" "$subject"
done
+
+if [ -n "$graphviz" ]; then
+ echo '}'
+fi
info "Updating base with $dep changes..."
if ! git merge "$dep"; then
if [ -z "$TG_RECURSIVE" ]; then
- resume="\`$tg update\` again"
+ resume="\`git checkout $name && $tg update\` again"
else # subshell
resume='exit'
fi
exit 2
fi
done
-
- # Home, sweet home...
- git checkout -q "$name"
else
info "The base is up-to-date."
fi
rm "$depcheck"
+# Home, sweet home...
+# (We want to always switch back, in case we were on the base from failed
+# previous merge.)
+git checkout -q "$name"
+
merge_with="refs/top-bases/$name"
sep="|"
done
- echo "TopGit v0.4 - A different patch queue manager"
+ 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 || :
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