Skip to content

Instantly share code, notes, and snippets.

@kergoth
Created November 24, 2009 21:39
Show Gist options
  • Save kergoth/242252 to your computer and use it in GitHub Desktop.
Save kergoth/242252 to your computer and use it in GitHub Desktop.
"origin" scripts for tracking git cherry picks.
These scripts are intended to make it easier to keep track of cherry picks
between long lived branches/forks.
The available scripts:
git-origin - associates an origin for a commit.. where it was cherry picked from
git-origin-blacklist - blacklists a commit from the output of the tools,
useful for bits you know are local-only
git-origins-from-patchid - uses patchids (like git-cherry) to generate an
initial set of origin data
git-cherry-origins - command like git-cherry, but which obeys the stored
origin information rather than patch-ids. this command
also, unlike git-cherry, supports some rev-list arguments
to limit the output by path (i.e. -- foo/bar).
git-log-origins - wrapper around git-cherry-origins which gives git-log-like
output. It will automatically use 'tig' as the pager if
available, otherwise falls back to less, then more. Takes
the same arguments as cherry-origins, as it just passes them
to it.
#!/bin/sh
set -u
. $(dirname $0)/git-origins-setup
if [ $# -gt 0 ]; then
upstream="$1"
shift
else
upstream="$(git for-each-ref --format="%(upstream)" $(git rev-parse --symbolic-full-name HEAD))"
if [ -z "$upstream" ]; then
echo >&2 "No remote tracking branch for this branch, please supply upstream."
exit 1
fi
fi
if [ $# -gt 0 ]; then
local="$1"
shift
else
local="HEAD"
fi
originfile="`mktemp`" || exit 1
GIT_DIR="$(cd $(git rev-parse --git-dir) && pwd)"
blacklistfile="$GIT_DIR/origins-blacklist"
indexfile="$GIT_DIR/origins-index"
codir="$GIT_DIR/origins-co"
trap "rm -f $indexfile $originfile; exit" INT TERM EXIT
(
export GIT_INDEX_FILE="$indexfile"
export GIT_WORK_TREE="$codir"
mkdir -p $GIT_WORK_TREE && \
git read-tree $GIT_NOTES_REF 2>/dev/null && \
git checkout-index -af
)
git rev-list "$upstream" | while read hash; do
if [ -e "$codir/$hash" ]; then
cat "$codir/$hash"
fi
done >> $originfile
git rev-list --reverse "$upstream..$local" "$@" | while read hash; do
sym="+"
if grep -q "^$hash$" $originfile $blacklistfile 2>/dev/null; then
sym="-"
fi
if [ -e "$codir/$hash" ]; then
origins="$(cat $codir/$hash)"
for origin in $origins; do
if grep -q "^$origin$" $originfile 2>/dev/null || \
[ "$(git merge-base $origin $upstream)" = "$origin" ]; then
sym="-"
break
fi
done
fi
echo "$sym $hash"
done
#!/bin/sh
have () {
which "$1" >/dev/null 2>&1
}
diffargs="-p --pretty --color"
if [ -z "$PAGER" ]; then
if [ -t 1 ] && have tig; then
PAGER=tig
diffargs="-v -s"
elif have less; then
PAGER="less -Xr"
elif have more; then
PAGER=more
else
PAGER=cat
fi
fi
git cherry-origins "$@" | \
sed -n -e's/^+ //p' | \
tac | \
git diff-tree --stdin $diffargs | \
eval $PAGER
#!/bin/sh
set -ue
if [ $# -lt 1 ]; then
echo >&2 "Usage: $0 ORIGIN [CURRENT]"
exit 1
elif [ $# -eq 1 ]; then
current="HEAD"
else
current="$2"
fi
origin="$(git rev-parse --verify $1)"
current="$(git rev-parse --verify $current)"
GIT_NOTES_REF=refs/notes/origins
export GIT_NOTES_REF
_reorder_by_date () {
# Return the commits in date order, first by author date, then by commit
# date, oldest to newest
git rev-parse "$@" | \
git diff-tree -s -v --pretty=format:"%ai %ci %H%n" --stdin --root | \
sort | \
cut -d" " -f7
}
reordered="$(_reorder_by_date $current $origin)"
origin="$(echo $reordered | cut -d" " -f1)"
current="$(echo $reordered | cut -d" " -f2)"
originorigin="$(git notes show $origin 2>/dev/null)" || true
if [ -n "$originorigin" ]; then
origin="$originorigin"
fi
prevdata="`mktemp`" || exit 1
trap "rm -f $prevdata" INT TERM EXIT
git notes show $current 2>/dev/null | sort -u >>$prevdata || true
for hash in $origin; do
if [ "$hash" != "$current" ] && \
! grep -q "^$hash$" $prevdata 2>/dev/null; then
echo $hash >> $prevdata
fi
done
if [ -z "$(cat $prevdata)" ]; then
echo >&2 "Empty prevdata, invalid commit/origin."
exit 1
fi
git notes edit -F $prevdata $current
echo Set origin of $current to:
cat $prevdata
#!/bin/sh
for hash in "$@"; do
echo "$hash" >> $(git rev-parse --git-dir)/origins-blacklist
done
#!/bin/sh
. $(dirname $0)/git-origins-setup
if [ $# -gt 0 ]; then
upstream="$1"
shift
else
upstream="$(git for-each-ref --format="%(upstream)" $(git rev-parse --symbolic-full-name HEAD))"
if [ -z "$upstream" ]; then
echo >&2 "No remote tracking branch for this branch, please supply upstream."
exit 1
fi
fi
if [ $# -gt 0 ]; then
local="$1"
shift
else
local="HEAD"
fi
patchidfile=`mktemp` || exit 1
trap "rm -f $patchidfile; exit" INT TERM EXIT
_patchids "$upstream" >> $patchidfile
_patchids "$upstream..$local" | while read patchid commit; do
patchidline="$(grep "^$patchid " $patchidfile | head -n 1)"
if [ -n "$patchidline" ]; then
anorigin="$(echo $patchidline | cut -d" " -f2)"
git origin $anorigin $commit
fi
done
GIT_NOTES_REF=refs/notes/origins
export GIT_NOTES_REF
_origins () {
git rev-list "$@" | while read hash; do
if git rev-parse -q --verify "$GIT_NOTES_REF":$hash >/dev/null; then
hashorigins="$(git show "$GIT_NOTES_REF":$hash)"
for origin in $hashorigins; do
echo $origin
done
fi
done
}
_patchids () {
git rev-list "$@" | git diff-tree -p --stdin | git patch-id
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment