Last active
August 29, 2015 14:09
-
-
Save nhoriguchi/6b5697d5bafd16cafd2d to your computer and use it in GitHub Desktop.
stg-bundle.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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