Skip to content

Instantly share code, notes, and snippets.

@nhoriguchi
Last active August 29, 2015 14:09
Show Gist options
  • Save nhoriguchi/6b5697d5bafd16cafd2d to your computer and use it in GitHub Desktop.
Save nhoriguchi/6b5697d5bafd16cafd2d to your computer and use it in GitHub Desktop.
stg-bundle.sh
#!/bin/bash
#
# Author: Naoya Horiguchi <nao.horiguchi@gmail.com>
#
# Usage:
# stg-bundle.sh create [-b branch] [-d directory] [-f] [-h]
# stg-bundle.sh create [-a] [-d directory] [-f] [-h]
# stg-bundle.sh import [-d directory] [-n branchname] [-f] [-h]
# stg-bundle.sh test
#
# Options for subcommand create:
# -b branch : set stgit branch to be bundled (default: current branch)
# -d dir : directory to which bundle file and patch files are exported
# -a : bundle whole local trees. If this option is set, -d option
# specifies the base directory under which directories for all
# branches are saved.
# -f : if the directory you specify with -d option already exists,
# the old contents of the directory will be overwritten.
# -h : show this message
#
# Options for subcommand import:
# -d dir : directory of the exported bundle/patch file set
# -n name : set new stgit branch name
# -f : if the branch you specify with -n option already exists,
# the imported branch overwrites the exisiting one.
# -h : show this message
usage() {
head -n $[LINENO - 3] $BASH_SOURCE
exit 0
}
error_dirty() {
echo "Working tree is not clean. Do stg refresh at first." >&2
exit 1
}
SUBCMD=$1
if [ "$SUBCMD" != create ] && [ "$SUBCMD" != import ] && [ "$SUBCMD" != test ] ; then
usage
fi
shift 1
BRANCH=$(git rev-parse --abbrev-ref HEAD)
DIR=
NAME=
ALLBRANCH=
FORCE=
if [ "$SUBCMD" = create ] ; then
while getopts b:ad:fh OPT
do
case $OPT in
b) BRANCH=$OPTARG ;;
d) DIR=$OPTARG ;;
a) ALLBRANCH=true ;;
f) FORCE=true ;;
h) usage ;;
esac
done
elif [ "$SUBCMD" = import ] ; then
while getopts d:n:fh OPT
do
case $OPT in
d) DIR=$OPTARG ;;
n) NAME=$OPTARG ;;
f) FORCE=true ;;
h) usage ;;
esac
done
fi
shift $[OPTIND - 1]
TDIR=/tmp/$(basename $BASH_SOURCE)
[ ! -d $TDIR ] && mkdir -p $TDIR
TMPF=$(mktemp --tmpdir=$TDIR -d XXXX)
stg_initialized() {
local branch=$1
git describe --tags $branch.stgit > /dev/null 2>&1
}
save_stgit_data() {
local dir=$1
local branch=$2
mkdir -p $dir
echo rsync -au .git/patches/$branch/ $dir/patches
rsync -au .git/patches/$branch/ $dir/patches
echo rsync -au .git/refs/patches/$branch/ $dir/refs_patches
rsync -au .git/refs/patches/$branch/ $dir/refs_patches
}
create_bundle() {
local dir=$1
shift
local branch="$@"
local recent_tag=
local revlist=
for br in $branch ; do
recent_tag=$(git describe --tags --abbrev=0 $br)
echo $recent_tag
revlist="$revlist $recent_tag..$br"
if stg_initialized $br ; then
refs_list="$(git show-ref | grep " refs/patches/$br" | cut -f2 -d' ' | tr '\n' ' ')"
revlist="$revlist $recent_tag..$br.stgit $refs_list"
fi
echo $br >> $dir/branch
echo $br:$recent_tag >> $dir/branch_recent_tag
done
echo git bundle create $dir/bundle $revlist
git bundle create $dir/bundle $revlist
}
do_create_branch() {
create_bundle $DIR $BRANCH
stg_initialized $BRANCH && save_stgit_data $DIR $BRANCH
}
do_create_all_branches() {
git branch | cut -c3- | grep -v "\.stgit$" > $TMPF/branches
create_bundle $DIR $(cat $TMPF/branches)
for br in $(cat $TMPF/branches) ; do
stg_initialized $br && save_stgit_data $DIR/$br $br
done
}
do_create() {
if [ ! "$DIR" ] ; then
echo "no target directory given (with -d option)." >&2
exit 1
fi
if [ -d "$DIR" ] && [ "$FORCE" != true ] ; then
echo "target directory $DIR already exists." >&2
exit 1
fi
rm -rf $DIR/ || exit 1
mkdir -p $DIR > /dev/null 2>&1
[ $? -ne 0 ] && exit 1
if [ "$ALLBRANCH" = true ] ; then
do_create_all_branches
else
do_create_branch
fi
}
do_import_single() {
if [ ! "$NAME" ] ; then
echo "no name for new branch given (with -n option)." >&2
exit 1
fi
git describe --tags $NAME > /dev/null
if [ $? -eq 0 ] && [ ! "$FORCE" ] ; then
echo "branch $NAME already exists"
exit 1
fi
git bundle verify $DIR/bundle || exit 1
git remote remove tmp_bundle 2> /dev/null
git remote add tmp_bundle $DIR/bundle || exit 1
git fetch tmp_bundle || exit 1
local branch=$(cat $DIR/branch)
# for stgit
if [ -d "$DIR/patches" ] ; then
git checkout -B $NAME.stgit tmp_bundle/$branch.stgit || exit 1
mkdir -p .git/patches/$NAME .git/refs/patches/$NAME
echo rsync -au $DIR/patches/ .git/patches/$NAME/
rsync -au $DIR/patches/ .git/patches/$NAME/
echo rsync -au $DIR/refs_patches/ .git/refs/patches/$NAME/
rsync -au $DIR/refs_patches/ .git/refs/patches/$NAME/
fi
git checkout -B $NAME tmp_bundle/$branch || exit 1
git remote remove tmp_bundle
}
do_import_all_tree() {
cat $DIR/branch
git bundle verify $DIR/bundle || exit 1
git bundle unbundle $DIR/bundle
git remote remove tmp_bundle 2> /dev/null
git remote add tmp_bundle $DIR/bundle || exit 1
git fetch tmp_bundle || exit 1
git branch -r --no-color | grep tmp_bundle | cut -f2- -d'/' > $TMPF/import_branches
local cbranch=$(git rev-parse --abbrev-ref HEAD)
echo "--------------- $cbranch"
for br in $(cat $TMPF/import_branches) ; do
# for stgit
echo "--- $br"
if [ -d "$DIR/$br/patches" ] ; then
stg branch --delete --force $br
git checkout -B $br.stgit tmp_bundle/$br.stgit || exit 1
mkdir -p .git/patches/$br .git/refs/patches/$br
echo rsync -au $DIR/$br/patches/ .git/patches/$br/
rsync -au $DIR/$br/patches/ .git/patches/$br/
echo rsync -au $DIR/$br/refs_patches/ .git/refs/patches/$br/
rsync -au $DIR/$br/refs_patches/ .git/refs/patches/$br/
fi
git branch -D $br
git checkout -B $br tmp_bundle/$br || exit 1
done
git checkout $cbranch
git remote remove tmp_bundle
}
do_import() {
if [ ! -d "$DIR" ] ; then
echo "no target directory given (with -d option)." >&2
exit 1
fi
if [ $(cat $DIR/branch | wc -l) -eq 1 ] ; then
do_import_single
else
FORCE=true
do_import_all_tree
fi
}
#############
# test code #
#############
commit_num() {
local num=$1
echo "data $num" > file
git add file
git commit -m "commit $num"
}
commit_num_stgit() {
local num=$1
echo "data $num" > file
stg new -m "commit $num"
stg refresh
}
do_test_branch_transfer() {
TMPF=$(mktemp -d)
SDIR=$TMPF/src
DDIR=$TMPF/dst
mkdir $SDIR $DDIR
pushd $SDIR
git init
commit_num 1
commit_num 2
commit_num 3
stg init
git tag -a -m "start tag" -f start master
git tag
rsync -a $SDIR/ $DDIR
commit_num_stgit 4
commit_num_stgit 5
commit_num_stgit 6
stg pop
DIR=$TMPF/saved
BRANCH=master
do_create
file $DIR/bundle
popd
pushd $DDIR
NAME=master
FORCE=true
stg series
do_import
stg series | tee $TMPF/dst_series
if [ "$(git rev-parse --abbrev-ref HEAD)" = master ] ; then
echo PASS
else
echo FAIL
NR_FAIL=$[NR_FAIL + 1]
fi
if [ "$(cat $TMPF/dst_series)" = "+ commit-4
> commit-5
- commit-6" ] ; then
echo PASS
else
echo FAIL
NR_FAIL=$[NR_FAIL + 1]
fi
popd
}
do_test_bundle_all_trees() {
TMPF=$(mktemp -d)
SDIR=$TMPF/src
DDIR=$TMPF/dst
mkdir $SDIR $DDIR
pushd $SDIR
git init
commit_num 1
commit_num 2
commit_num 3
git tag -f start master
git checkout -b dev4 start
commit_num 13
commit_num 14
git checkout -b dev5 master
stg init
commit_num_stgit 15
commit_num_stgit 16
git checkout -b dev3 master
stg init
commit_num_stgit 10
commit_num_stgit 11
rsync -a $SDIR/ $DDIR
stg pop
commit_num_stgit 12
git checkout -b dev1 master
stg init
commit_num_stgit 4
commit_num_stgit 5
commit_num_stgit 6
stg pop
git checkout -b dev2 start
commit_num 7
commit_num 8
commit_num 9
DIR=$TMPF/saved
ALLBRANCH=true
git branch
do_create
popd
pushd $DDIR
stg series
do_import
git branch
stg series | tee $TMPF/dst_series
popd
}
do_test() {
do_test_branch_transfer
do_test_bundle_all_trees
}
#################
# test code end #
#################
if [ "$SUBCMD" = test ] ; then
NR_FAIL=0
do_test
[ $NR_FAIL -eq 0 ] && echo "ALL PASS" || echo "$NR_FAIL failed."
else
[ $(stg diff | wc -l) -ne 0 ] && error_dirty
[ ! -d ".git" ] && echo "you are not on git working directory" >&2 && exit 1
if [ "$SUBCMD" = create ] ; then
do_create
elif [ "$SUBCMD" = import ] ; then
do_import
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment