git-bpush: Pushing to a bundle
- From
- Santi Béjar <santi@agolina.net>
- Date
- Dec 9, 2008, 09:49 UTC
- Message-ID
- <adf1fd3d0812090149m158fcb9as15bacce58c61a1a3@mail.gmail.com>
Hi *,
I've made a script to push to a bundle that tries to behave as the normal push.
It has some limitations, but for the normal cases it works fine.
The basic idea is:
- Easily create bundles with the current branch. - Be able to push to defined bundles in remote.<remote>.url - Only add new objects by default (do not lose objects) - Reuse existing bundles (keep the basis and the branches) - Check that the branches fast-forward - Keep the pushed branches in refs/remotes/<remote>/*
But it also has some limitations:
- Do not allow refspec (git-bundle do not support them), only branch/tags names. - Push all branches or none (consequence of the above) - ...
Hope this helps, Santi
The scripts follows, but also attatched. #!/bin/sh
OPTIONS_KEEPDASHDASH= OPTIONS_SPEC="\ git bpush [options] [<remote> [<refs>...]] -- f,force force updates full create a full bundle v be verbose " SUBDIRECTORY_OK=Yes . git-sh-setup . git-parse-remote
cd_to_toplevel
LF=' ' IFS="$LF"
bases=
bbases=
changed=
force=
nonff=
remote=
refs=
while [ $# != 0 ] ; do
case "$1" in
-v) verbose=t;;
--full) full=t;;
-f|--force) force=t;;
--) shift; break;;
*) usage;;
esac
shift
done[ -n "$1" ] && remote=$1 && shift
while [ $# != 0 ] ; do
refs="$refs$LF$1" && shift
done[ -z "$remote" ] && remote=$(get_default_remote) remoteurl=$(git config remote.${remote}.url) [ -z "$remoteurl" ] && remoteurl=$remote [ -d "$remoteurl" ] && die "$remoteurl is a directory"
# Default bases in bundle.base
# Default {refs,base} can be specified in remote.<remote>.{push,bundlebase}
if [ "$remote" != "$remoteurl" ] ; then
[ -z "$refs" ] &&
refs=$(git config --get-all remote.${remote}.push)
bases=$(git config --get-all remote.${remote}.bundlebase ||
git config --get-all bundle.base)
else
bases=$(git config --get-all bundle.base)
fi# git rev-parse --symbolic-full-name resolves symlinks
# Keep at least HEAD
head=
for ref in $refs ; do
[ "$ref" = HEAD ] && head=t && break
done[ -n "$bases" ] && bases=$(git rev-parse --revs-only $bases | sort -u)
# Full symbolic refs to be uniq
[ -n "$refs" ] && \
refs=$(git-rev-parse --symbolic-full-name --revs-only $refs | sort -u) && \
[ -n "$head" ] && refs="HEAD$LF$refs"if [ -e "$remoteurl" ] ; then
# Find the bundle's bases
refs="$refs$LF$(git bundle list-heads $remoteurl | cut -d " " -f 2)"
requires=
for line in $(git bundle verify "$remoteurl" 2>/dev/null) ; do
case "$line" in "The bundle requires"*) requires=t && continue; esac
[ -z "$requires" ] && continue
bbase=$(echo $line | cut -d " " -f 1)
[ -z "$bbases" ] && bbases=$bbase && continue
bbases="$bbases$LF$bbase"
done
bbases=$(echo "$bbases" | sort -u)
[ -z "$bases" ] && bases="$bbases" || bases="$bases$LF$bbases"
elif [ -z "$refs" ] ; then
# Push current branch
refs="HEAD$LF$(git symbolic-ref -q HEAD)"
fi[ -z "$refs" ] && die "No refs to push"
refs=$(echo "$refs" | sort -u)
for ref in $bases $refs ; do
[ "$(git cat-file -t $ref^{})" != commit ] && \
die "$(basename $0): $ref is not a commit"
doneheader="To $remoteurl" [ -n "$verbose" ] && echo "Pushing to $remoteurl" && echo $header && header=
# Find what is/is not a fast-forward, up to date or new
# As "git bundle" does not support refspecs we must push all matching branches
for ref in $refs ; do
case $ref in
refs/tags/*) type=tags; newtext="new tag";;
refs/heads/*|HEAD) type=heads ; newtext="new branch" ;;
esac
newhash=$(git rev-parse $ref)
newshort=$(git rev-parse --short $ref)
bshort=$(echo $ref | sed -e "s|^refs/$type/||")
if [ -e "$remoteurl" ] ; then
bheads="$(git bundle list-heads $remoteurl)"
for bhead in $bheads ; do
bhash=$(echo $bhead | cut -d " " -f 1)
bref=$(echo $bhead | cut -d " " -f 2)
[ "$bref" != "$ref" ] && continue
oldshort=$(git-rev-parse --short $bhash)
case $type in
tags)
base=$newhash;;
heads)
base=$(git merge-base $bref $bhash);;
esac
if [ "$base" != $bhash ] ; then
[ -n "$header" ] && echo $header && header=
if [ -z "$force" ] ; then
nonff=t
echo " ! [rejected] $bshort -> $bshort (non-fast forward)"
else
changed=t
echo " + $oldshort...$newshort $bshort -> $bshort (forced update)"
fi
continue 2
fi
if [ "$newhash" != "$bhash" ] ; then
changed=t
[ -n "$header" ] && echo $header && header=
echo " $oldshort..$newshort $bshort -> $bshort"
elif [ -n "$verbose" ] ; then
[ -n "$header" ] && echo $header && header=
echo " = [up to date] $bshort -> $bshort"
fi
continue 2
done
fi
[ -n "$header" ] && echo $header && header=
echo " * [$newtext] $bshort -> $bshort"
changed=t
done[ -n "$full" ] && bases= && [ -n "$bbases" ] && changed=t
[ -n "$nonff" ] && die "error: failed to push some refs to $remoteurl" [ -z "$changed" ] && die "Everything up-to-date" [ -n "$bases" ] && bases="--not$LF$bases"
git bundle create $remoteurl $refs $bases [ "$remote" != "$remoteurl" ] && git fetch -q "$remote" exit 0