Skip to content

Instantly share code, notes, and snippets.

@wchargin
Created February 10, 2019 05:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wchargin/3934c1c09812a2ad0e4f2092391e1ac8 to your computer and use it in GitHub Desktop.
Save wchargin/3934c1c09812a2ad0e4f2092391e1ac8 to your computer and use it in GitHub Desktop.
wcgit utils for pushing a one-patch-is-one-change local repository to a one-branch-per-change remote (see https://wchargin.github.io/posts/managing-dependent-pull-requests for explanation)
https://wchargin.github.io/posts/managing-dependent-pull-requests
#!/bin/bash
#
# git-push-to-target: Push this commit to a branch specified in its
# commit description.
#
# Copyright (c) 2018 William Chargin. Released under the MIT license.
set -eu
usage() {
printf 'usage: git push-to-target [[REF...] | --since REVISION]\n'
printf ' [(--remote|-r) REMOTE]\n'
printf ' [--dry-run|-n] [--help|-h]\n'
printf '\n'
printf 'Push commits to remote branches specified in their messages.\n'
printf '\n'
printf '%s\n' '[REF...]'
printf ' If explicit revisions are specified, push those commits.\n'
printf ' If no revisions are specified, act as if HEAD was given.\n'
printf '\n'
printf '%s\n' '--since REVISION'
printf ' Push those commits reachable from HEAD but not reachable\n'
printf ' from REVISION. Incompatible with explicit revisions.\n'
printf '\n'
printf '%s\n' '(--remote|-r) REMOTE'
printf ' Push to REMOTE. Defaults to "origin".\n'
printf '\n'
printf '%s\n' '(--dry-run|-n), or environment variable DRY_RUN nonempty'
printf ' Invoke git-push with --dry-run.\n'
printf '\n'
printf '%s\n' '--help'
printf ' Show this message.\n'
printf '\n'
printf 'See: git-target-branch(1).\n'
}
# Read refs from stdin and push them. Stdin must be nonempty.
process_refs() {
remote="$1"
refspecs=( )
while read -r ref; do
sha="$(git rev-parse --short --verify "${ref}")"
target="$(git target-branch "${sha}")"
refspec="${sha}:refs/heads/${target}"
refspecs+=( "${refspec}" )
message="$(git show --no-patch --format=%s "${sha}")"
printf ' * %s:%s (%s)\n' "${sha}" "${target}" "${message}"
done
dry_run=
if [ -n "${DRY_RUN:-}" ]; then
dry_run="--dry-run"
fi
(set -x; git push $dry_run --force-with-lease "${remote}" "${refspecs[@]}")
}
main() {
since=
remote=origin
refs=( )
while [ $# -gt 0 ]; do
case "$1" in
--help|-h)
usage
exit 0
;;
--remote|-r)
shift
remote="$1"
;;
--dry-run|-n)
DRY_RUN=1
;;
--since)
shift
since="$1"
;;
*)
refs+=( "$1" )
;;
esac
shift
done
if [ -n "${since}" ]; then
if [ "${#refs[@]}" -ne 0 ]; then
printf >&2 'fatal: --since used with explicit revisions\n'
return 1
fi
ref_list="$(git rev-list --reverse HEAD "^${since}")"
printf '%s\n' "${ref_list}" | process_refs "${remote}"
elif [ "${#refs[@]}" -gt 0 ]; then
printf '%s\n' "${refs[@]}" | process_refs "${remote}"
else
printf 'HEAD\n' | process_refs "${remote}"
fi
}
main "$@"
#!/bin/sh
#
# git-target-branch: Identify the remote tracking branch specified in a
# commit message.
#
# Copyright (c) 2018 William Chargin. Released under the MIT license.
set -eu
DIRECTIVE='wchargin-branch'
BRANCH_PREFIX='wchargin-'
target_branch() {
# shellcheck disable=SC1004
awk -v FS=": " -v directive="${DIRECTIVE}" -v prefix="${BRANCH_PREFIX}" '
NR == 1 { summary = $0 }
$1 == directive {
if (NF == 2 && $2 !~ /[^A-Za-z0-9_.-]/) {
found[count++] = $2;
} else {
printf("bad \"%s\" directive (%s): %s\n", directive, $0, summary) \
>"/dev/stderr";
aborted = 1;
exit 1;
}
}
END {
if (aborted) exit;
if (count == 0) {
printf("missing \"%s\" directive: %s\n", directive, summary) \
>"/dev/stderr";
exit 1;
} else if (count > 1) {
printf("multiple \"%s\" directives: %s\n", directive, summary) \
>"/dev/stderr";
exit 1;
} else {
printf("%s%s\n", prefix, found[0]);
}
}
'
}
main() {
# TODO: --help
src="${1:-HEAD}"
if [ "${src}" = '-' ]; then
target_branch
else
git show --no-patch --format=%B "${src}" | target_branch
fi
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment