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" done header="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
Attachment:
git-bpush
Description: Binary data