From: martin f. krafft Date: Fri, 26 Sep 2008 18:06:01 +0000 (+0200) Subject: Merge branch 'master' into refs/top-bases/debian/locations X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=41ad0fd31729461c5c9c3926c28011770ccfc9e7;hp=643328644f50c39690341b8bc2b27aea329576f9;p=topgit.git Merge branch 'master' into refs/top-bases/debian/locations --- diff --git a/.gitignore b/.gitignore index 8f1d3a7..1e90e82 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ tg-delete tg-delete.txt tg-info tg-info.txt +tg-mail +tg-mail.txt tg-patch tg-patch.txt tg-summary @@ -13,4 +15,9 @@ tg-update tg-update.txt tg-export tg-export.txt +tg-import +tg-import.txt +tg-remote +tg-remote.txt tg +.*.swp diff --git a/Makefile b/Makefile index b73a1f1..2c627af 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ help_out = $(patsubst %.sh,%.txt,$(commands_in)) all:: tg $(commands_out) $(hooks_out) $(help_out) -tg $(commands_out) $(hooks_out): % : %.sh +tg $(commands_out) $(hooks_out): % : %.sh Makefile @echo "[SED] $@" @sed -e 's#@cmddir@#$(cmddir)#g;' \ -e 's#@hooksdir@#$(hooksdir)#g' \ diff --git a/README b/README index b99b036..1893dc0 100644 --- a/README +++ b/README @@ -253,6 +253,19 @@ tg delete TODO: '-a' to delete all empty branches, depfix, revert +tg depend +~~~~~~~~~ + Change dependencies of a TopGit-controlled topic branch. + This should have several subcommands, but only 'add' is + supported right now. + + The 'add' subcommand takes an argument of a topic branch + to be added, adds it to '.topdeps', performs a commit and + then updates your topic branch accordingly. If you want to + do other things related to the dependency addition, like + adjusting '.topmsg', prepare them in the index before + calling 'tg depend add'. + tg info ~~~~~~~ Show a summary information about the current or specified @@ -272,11 +285,31 @@ tg patch TODO: tg patch -i to base at index instead of branch, -w for working tree +tg mail +~~~~~~~ + Send a patch from the current or specified topic branch as + email. + + Takes the patch given on the command line and emails it out. + Destination addresses such as To, Cc and Bcc are taken from the + patch header. + + Since it actually boils down to `git send-email` please refer to + 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. + + TODO: 'tg mail patchfile' to mail an already exported patch + TODO: mailing patch series + TODO: specifying additional options and addresses on command + line + tg remote ~~~~~~~~~ Register given remote as TopGit-controlled. This will create the namespace for the remote branch bases and teach 'git fetch' - and 'git push' to operate on them. + and 'git push' to operate on them. (Do NOT use 'git push --all' + for your pushes - plain 'git push' will do the right thing.) It takes a mandatory remote name argument, and optional '--populate' switch - use that for your origin-style remote, @@ -362,6 +395,10 @@ tg export and an argument specifying the directory where the quilt series should be saved. + With '--quilt', you can also pass '-b' parameter followed by + a comma-separated explicit list of branches to export. This + mode of operation is currently not supported with collapse. + Usage: tg export ([--collapse] BRANCH | --quilt DIR) TODO: Make stripping of non-essential headers configurable @@ -369,8 +406,6 @@ tg export TODO: --mbox option for other mode of operation TODO: -n option to prevent exporting of empty patches TODO: -a option to export all branches - TODO: Allow branches to be exported to be passed as arguments, default - to the current branch if none are specified TODO: For quilt exporting, use a temporary branch and remove it when done - this would allow producing conflict-less series diff --git a/debian/README.source b/debian/README.source index 732e5d4..6fafbb4 100644 --- a/debian/README.source +++ b/debian/README.source @@ -63,30 +63,29 @@ The upstream tarball for $VERSION can be obtained using pristine-tar: To build the package, you check out the build branch, recreate debian/patches, commit, build, test, upload, tag: - 1. tg create stage-0.3-1 debian/locations - 2. git commit -m'staging 0.3-1' + 1. tg create stage-0.4-1 debian/locations + 2. git commit -m'staging 0.4-1' 3. tg export --quilt debian/patches.new 4. rm debian/patches.new/stage-* 5. sed -i '/^stage-/d' debian/patches.new/series 6. git checkout build && git rm -r debian/patches 7. mv debian/patches.new debian/patches && git add debian/patches - 8. git commit -m'preparing 0.3-1' - 9. build, test, upload, tag ('debian/topgit-0.3-1') -10. tg delete stage-0.3-1 + 8. git commit -m'preparing 0.4-1' + 9. build, test, upload, tag ('debian/topgit-0.4-1') +10. tg delete stage-0.4-1 This process is still very cumbersome and needs to be improved, ideally within TopGit. 5. Importing a new upstream version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To import a new upstream, pull it into the upstream branch, merge upstream +To import a new upstream, update the remote, merge the tag you want to merge into the master branch, ideally together with an update to debian/changelog, then update all TopGit branches: -1. git checkout upstream -2. git pull -3. git checkout master -4. git merge upstream +1. git remote update +2. git checkout master +3. git merge topgit-0.4 Now proceed as in the next step. @@ -105,10 +104,12 @@ this procedure: Until upstream provides official tarballs, the following can be used to create them for Debian: -1. git archive --prefix=$(git describe upstream)/ --verbose upstream \ - | gzip -9 > ../$(git describe upstream | sed s,-,_,).orig.tar.gz -2. pristine-tar commit ../$(git describe upstream | sed s,-,_,).orig.tar.gz \ - upstream +1. git checkout topgit-0.4 +2. git archive --prefix=$(git describe HEAD)/ --verbose HEAD \ + | gzip -9 > ../$(git describe HEAD | sed s,-,_,).orig.tar.gz +3. pristine-tar commit ../$(git describe HEAD | sed s,-,_,).orig.tar.gz \ + HEAD +4. git checkout master All comments and suggestions are welcome, especially those pertaining to auto-generating debian/changelog from commit logs. diff --git a/debian/changelog b/debian/changelog index c61ba3e..40e7e14 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +topgit (0.4-1) unstable; urgency=low + + * New upstream release. + + -- martin f. krafft Fri, 26 Sep 2008 19:55:58 +0200 + topgit (0.3-1) unstable; urgency=low * New upstream release (closes: #498559). diff --git a/tg-delete.sh b/tg-delete.sh index 075d15c..7f7ede7 100644 --- a/tg-delete.sh +++ b/tg-delete.sh @@ -31,7 +31,7 @@ branchrev="$(git rev-parse --verify "$name" 2>/dev/null)" || die "invalid branch name: $name" baserev="$(git rev-parse --verify "refs/top-bases/$name" 2>/dev/null)" || die "not a TopGit topic branch: $name" -[ "$(git symbolic-ref HEAD)" != "refs/heads/$name" ] || +! git symbolic-ref HEAD >/dev/null || [ "$(git symbolic-ref HEAD)" != "refs/heads/$name" ] || die "cannot delete your current branch" nonempty= diff --git a/tg-depend.sh b/tg-depend.sh new file mode 100644 index 0000000..af78808 --- /dev/null +++ b/tg-depend.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# TopGit - A different patch queue manager +# (c) Petr Baudis 2008 +# GPLv2 + +name= + + +## Parse options + +subcmd="$1"; shift +[ "$subcmd" = "add" ] || die "unknown subcommand ($subcmd)" + +while [ -n "$1" ]; do + arg="$1"; shift + case "$arg" in + -*) + echo "Usage: tg [...] depend add NAME" >&2 + exit 1;; + *) + [ -z "$name" ] || die "name already specified ($name)" + name="$arg";; + esac +done + + +## Sanity checks + +[ -n "$name" ] || die "no branch name specified" +branchrev="$(git rev-parse --verify "$name" 2>/dev/null)" || + die "invalid branch name: $name" +baserev="$(git rev-parse --verify "refs/top-bases/$name" 2>/dev/null)" || + die "not a TopGit topic branch: $name" + + +## Record new dependency + +echo "$name" >>.topdeps +git add .topdeps +git commit -m"New TopGit dependency: $name" +$tg update diff --git a/tg-export.sh b/tg-export.sh index 654b38b..a7c459a 100644 --- a/tg-export.sh +++ b/tg-export.sh @@ -4,6 +4,7 @@ # GPLv2 name= +branches= output= driver=collapse @@ -13,12 +14,14 @@ driver=collapse while [ -n "$1" ]; do arg="$1"; shift case "$arg" in + -b) + branches="$1"; shift;; --quilt) driver=quilt;; --collapse) driver=collapse;; -*) - echo "Usage: tg [...] export ([--collapse] NEWBRANCH | --quilt DIRECTORY)" >&2 + echo "Usage: tg [...] export [-b BRANCH1,BRANCH2...] ([--collapse] NEWBRANCH | --quilt DIRECTORY)" >&2 exit 1;; *) [ -z "$output" ] || die "output already specified ($output)" @@ -31,6 +34,9 @@ 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" + playground="$(mktemp -d -t tg-export.XXXXXX)" trap 'rm -rf "$playground"' EXIT @@ -38,17 +44,6 @@ trap 'rm -rf "$playground"' EXIT ## Collapse driver -# Trusty Cogito code: -load_author() -{ - if [ -z "$GIT_AUTHOR_NAME" ] && echo "$1" | grep -q '^[^< ]'; then - export GIT_AUTHOR_NAME="$(echo "$1" | sed 's/ *<.*//')" - fi - if [ -z "$GIT_AUTHOR_EMAIL" ] && echo "$1" | grep -q '<.*>'; then - export GIT_AUTHOR_EMAIL="$(echo "$1" | sed 's/.*<\(.*\)>.*/\1/')" - fi -} - # pretty_tree NAME # Output tree ID of a cleaned-up tree without tg's artifacts. pretty_tree() @@ -69,19 +64,16 @@ collapsed_commit() >"$playground/^body" # Get commit message and authorship information - git cat-file blob "$name:.topmsg" >"$playground/^msg" - while read line; do - if [ -z "$line" ]; then - # end of header - cat >"$playground/^body" - break - fi - case "$line" in - From:*) load_author "${line#From: }";; - Subject:*) echo "${line#Subject: }" >>"$playground/^pre";; - *) echo "$line" >>"$playground/^post";; - esac - done <"$playground/^msg" + git cat-file blob "$name:.topmsg" | git mailinfo "$playground/^msg" /dev/null > "$playground/^info" + + GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$playground/^info")" + GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$playground/^info")" + GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$playground/^info")" + SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$playground/^info")" + + test -n "$GIT_AUTHOR_NAME" && export GIT_AUTHOR_NAME + test -n "$GIT_AUTHOR_EMAIL" && export GIT_AUTHOR_EMAIL + test -n "$GIT_AUTHOR_DATE" && export GIT_AUTHOR_DATE # Determine parent parent="$(cut -f 1 "$playground/$name^parents")" @@ -95,14 +87,9 @@ collapsed_commit() $(for p in $parent; do echo -p $p; done))" fi - { - if [ -s "$playground/^pre" ]; then - cat "$playground/^pre" - echo - fi - cat "$playground/^body" - [ ! -s "$playground/^post" ] || cat "$playground/^post" - } | git commit-tree "$(pretty_tree "$name")" -p "$parent" + (printf '%s\n\n' "$SUBJECT"; cat "$playground/^msg") | + git stripspace | + git commit-tree "$(pretty_tree "$name")" -p "$parent" echo "$name" >>"$playground/^ticker" } @@ -176,6 +163,7 @@ fi driver() { + case $_dep in refs/remotes/*) return;; esac branch_needs_update >/dev/null [ "$_ret" -eq 0 ] || die "cancelling export of $_dep (-> $_name): branch not up-to-date" @@ -185,8 +173,16 @@ driver() # Call driver on all the branches - this will happen # in topological order. -recurse_deps driver "$name" -(_ret=0; _dep="$name"; _name=""; _dep_is_tgish=1; driver) +if [ -z "$branches" ]; then + recurse_deps driver "$name" + (_ret=0; _dep="$name"; _name=""; _dep_is_tgish=1; driver) +else + echo "$branches" | tr ',' '\n' | while read _dep; do + _dep_is_tgish=1 + $driver + done + name="$(echo "$branches" | sed 's/.*,//')" +fi if [ "$driver" = "collapse" ]; then diff --git a/tg-import.sh b/tg-import.sh index 6a4f79e..799efc9 100644 --- a/tg-import.sh +++ b/tg-import.sh @@ -24,6 +24,15 @@ while [ -n "$1" ]; do done +## Make sure our tree is clean + +git update-index --ignore-submodules --refresh || exit +[ -z "$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)" ] || + die "the index is not clean" + + +## Perform import + get_commit_msg() { commit="$1" @@ -52,7 +61,7 @@ process_commit() branch_name=$(get_branch_name "$commit") info "---- Importing $commit to $branch_prefix$branch_name" tg create "$branch_prefix""$branch_name" - git read-tree "$commit" + git cherry-pick --no-commit "$commit" get_commit_msg "$commit" > .topmsg git add -f .topmsg .topdeps git commit -C "$commit" diff --git a/tg-info.sh b/tg-info.sh index b5b92f6..9076247 100644 --- a/tg-info.sh +++ b/tg-info.sh @@ -20,7 +20,7 @@ while [ -n "$1" ]; do esac done -[ -n "$name" ] || name="$(git symbolic-ref HEAD | sed 's#^refs/heads/##')" +[ -n "$name" ] || name="$(git symbolic-ref HEAD | sed 's#^refs/\(heads\|top-bases\)/##')" base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" || die "not a TopGit-controlled branch" @@ -32,7 +32,7 @@ if [ "$(git rev-parse --short "$name")" = "$base_rev" ]; then exit 0 fi -git cat-file blob "$name:.topmsg" | grep ^Subject: +git cat-file blob "$name:.topmsg" | grep ^Subject: || : echo "Base: $base_rev" branch_contains "$name" "$base_rev" || diff --git a/tg-mail.sh b/tg-mail.sh new file mode 100644 index 0000000..24e5f67 --- /dev/null +++ b/tg-mail.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# TopGit - A different patch queue manager +# GPLv2 + +name= +send_email_args= + + +## Parse options + +while [ -n "$1" ]; do + arg="$1"; shift + case "$arg" in + -s) + send_email_args="$1"; shift;; + -*) + echo "Usage: tg [...] mail [-s SEND_EMAIL_ARGS] [NAME]" >&2 + exit 1;; + *) + [ -z "$name" ] || die "name already specified ($name)" + name="$arg";; + esac +done + +[ -n "$name" ] || 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 a TopGit-controlled branch" + + +patchfile="$(mktemp -t tg-mail.XXXXXX)" + +$tg patch $name >"$patchfile" + +hlines=$(grep -n -m 1 '^---' "$patchfile" | sed 's/:---//') +header=$(head -n $(($hlines - 1)) "$patchfile") + + + +from="$(echo "$header" | grep '^From:' | sed 's/From:\s*//')" +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") +# FIXME: there could be multimple To +[ -n "$to" ] && people=("${people[@]}" --to "$to") + + +# NOTE: git-send-email handles cc itself +git send-email $send_email_args "${people[@]}" "$patchfile" + +rm "$patchfile" diff --git a/tg-patch.sh b/tg-patch.sh index 7a24718..97338ab 100644 --- a/tg-patch.sh +++ b/tg-patch.sh @@ -20,7 +20,7 @@ while [ -n "$1" ]; do esac done -[ -n "$name" ] || name="$(git symbolic-ref HEAD | sed 's#^refs/heads/##')" +[ -n "$name" ] || name="$(git symbolic-ref HEAD | sed 's#^refs/\(heads\|top-bases\)/##')" base_rev="$(git rev-parse --short --verify "refs/top-bases/$name" 2>/dev/null)" || die "not a TopGit-controlled branch" diff --git a/tg-update.sh b/tg-update.sh index 39769be..f36624f 100644 --- a/tg-update.sh +++ b/tg-update.sh @@ -75,7 +75,7 @@ if [ -s "$depcheck" ]; then info "Updating base with $dep changes..." if ! git merge "$dep"; then if [ -z "$TG_RECURSIVE" ]; then - resume='`$tg update` again' + resume="\`$tg update\` again" else # subshell resume='exit' fi diff --git a/tg.sh b/tg.sh index b493399..7005f98 100644 --- a/tg.sh +++ b/tg.sh @@ -26,7 +26,7 @@ setup_hook() # Another job well done! return fi - # Prepare incanation + # Prepare incantation if [ -x "$git_dir/hooks/$1" ]; then hook_call="$hook_call"' || exit $?' else @@ -222,10 +222,14 @@ do_help() sep="|" done - echo "TopGit v0.3 - A different patch queue manager" + echo "TopGit v0.4 - A different patch queue manager" echo "Usage: tg [-r REMOTE] ($cmds|help) ..." - elif [ -r "@sharedir@/tg-$1.txt" ] ; then - cat "@sharedir@/tg-$1.txt" + 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 fi