Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: a4dd223d5b
Fetching contributors…

Cannot retrieve contributors at this time

377 lines (327 sloc) 9.821 kB
#
# git-flow -- A collection of Git extensions to provide high-level
# repository operations for Vincent Driessen's branching model.
#
# Original blog post presenting this model is found at:
# http://nvie.com/archives/323
#
# Feel free to contribute to this project at:
# http://github.com/nvie/gitflow
#
# Copyright (c) 2010 by Vincent Driessen
# Copyright (c) 2010 by Benedikt Böhm
#
PREFIX=$(git config --get gitflow.prefix.feature || echo feature/)
usage() {
echo "usage: git flow feature [list] [-v]"
echo " git flow feature start [-Fv] <name> [<base>]"
echo " git flow feature finish [-rsFv] <name|nameprefix>"
echo " git flow feature publish <name>"
echo " git flow feature track <name>"
echo " git flow feature diff <name|nameprefix>"
echo " git flow feature rebase [-i] <name|nameprefix>"
}
cmd_default() {
cmd_list "$@"
}
cmd_list() {
DEFINE_boolean verbose false 'verbose (more) output' v
parse_args "$@"
typeset feature_branches
typeset current_branch
typeset short_names
feature_branches=$(echo "$LOCAL_BRANCHES" | grep "^$PREFIX")
if [ -z "$feature_branches" ]; then
warn "No feature branches exist."
exit 0
fi
current_branch=$(git branch | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
short_names=$(echo "$feature_branches" | sed "s ^$PREFIX g")
# determine column width first
typeset -i width=0
typeset branch
for branch in $short_names; do
typeset -i len=${#branch}
width=$(max $width $len)
done
width=width+3
typeset branch
for branch in $short_names; do
typeset fullname=$PREFIX$branch
typeset base=$(git merge-base "$fullname" "$DEVELOP_BRANCH")
typeset develop_sha=$(git rev-parse "$DEVELOP_BRANCH")
typeset branch_sha=$(git rev-parse "$fullname")
if [ "$fullname" = "$current_branch" ]; then
printf "* "
else
printf " "
fi
if flag verbose; then
printf "%-${width}s" "$branch"
if [ "$branch_sha" = "$develop_sha" ]; then
printf "(no commits yet)"
elif [ "$base" = "$branch_sha" ]; then
printf "(is behind develop, may ff)"
elif [ "$base" = "$develop_sha" ]; then
printf "(based on latest develop)"
else
printf "(may be rebased)"
fi
else
printf "%s" "$branch"
fi
echo
done
}
cmd_help() {
usage
exit 0
}
require_name_arg() {
if [ "$NAME" = "" ]; then
warn "Missing argument <name>"
usage
exit 1
fi
}
expand_nameprefix_arg() {
require_name_arg
typeset expanded_name
typeset -i exitcode
expanded_name=$(resolve_nameprefix "$NAME" "$PREFIX")
exitcode=$?
case $exitcode in
0) NAME=$expanded_name
BRANCH=$PREFIX$NAME
;;
*) exit 1 ;;
esac
}
expand_nameprefix_arg_or_current() {
if [ "$NAME" != "" ]; then
expand_nameprefix_arg
gitflow_require_branch "$PREFIX$NAME"
else
typeset current_branch=$(gitflow_current_branch)
if startswith "$current_branch" "$PREFIX"; then
BRANCH=$current_branch
NAME=${BRANCH#$PREFIX}
else
warn "The current HEAD is no feature branch."
warn "To diff a feature, specify a <name> argument."
usage
exit 1
fi
fi
}
parse_args() {
# parse options
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
# read arguments into global variables
NAME=$1
BRANCH=$PREFIX$NAME
}
cmd_start() {
DEFINE_boolean fetch false 'fetch from origin before performing local operation' F
DEFINE_boolean force false 'force creation of feature branch (ignores dirty working tree)' f
parse_args "$@"
BASE=${2:-$DEVELOP_BRANCH}
require_name_arg
# sanity checks
if noflag force; then
gitflow_require_clean_working_tree
fi
gitflow_require_branch_absent "$BRANCH"
# update the local repo with remote changes, if asked
if flag fetch; then
git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
fi
gitflow_require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
# create branch
if ! git checkout -b "$BRANCH" "$BASE"; then
die "Could not create feature branch '$BRANCH'"
fi
echo
echo "Summary of actions:"
echo "- A new branch '$BRANCH' was created, based on '$BASE'"
echo "- You are now on branch '$BRANCH'"
echo ""
echo "Now, start committing on your feature. When done, use:"
echo ""
echo " git flow finish feature $NAME"
echo
}
cmd_finish() {
DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
DEFINE_boolean rebase false 'rebase instead of merge' r
DEFINE_boolean squash false 'squash all commits when rebasing (implies --rebase)' s
parse_args "$@"
expand_nameprefix_arg
# sanity checks
gitflow_require_branch "$BRANCH"
# detect if we're restoring from a merge conflict
if [ -f "$GIT_DIR/.gitflow/MERGE_BASE" ]; then
#
# TODO: detect that we're working on the correct branch here!
# The user need not necessarily have given the same $NAME twice here
# (although he/she should).
#
# TODO: gitflow_test_clean_working_tree() should provide an alternative
# exit code for "unmerged changes in working tree", which we should
# actually be testing for here
if gitflow_test_clean_working_tree; then
FINISH_BASE=$(cat "$GIT_DIR/.gitflow/MERGE_BASE")
# Since the working tree is now clean, either the user did a
# succesfull merge manually, or the merge was cancelled.
# We detect this using gitflow_is_branch_merged_into()
if gitflow_is_branch_merged_into "$BRANCH" "$FINISH_BASE"; then
rm -f "$GIT_DIR/.gitflow/MERGE_BASE"
helper_finish_cleanup
exit 0
else
# If the user cancelled the merge and decided to wait until later,
# that's fine. But we have to acknowledge this by removing the
# MERGE_BASE file and continuing normal execution of the finish
rm -f "$GIT_DIR/.gitflow/MERGE_BASE"
fi
else
echo
echo "Merge conflicts not resolved yet, use:"
echo " git mergetool"
echo " git commit"
echo
echo "You can then complete the finish by running it again:"
echo " git flow feature finish $NAME"
echo
exit 1
fi
fi
# sanity checks
gitflow_require_clean_working_tree
# update local repo with remote changes first, if asked
if flag fetch; then
git fetch -q "$ORIGIN" "$BRANCH"
fi
if has "$ORIGIN/$BRANCH" "$REMOTE_BRANCHES"; then
gitflow_require_branches_equal "$BRANCH" "$ORIGIN/$BRANCH"
fi
gitflow_require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
# if the user wants to rebase, do that first
if flag rebase; then
if ! git flow feature rebase "$NAME" "$DEVELOP_BRANCH"; then
warn "Finish was aborted due to conflicts during rebase."
warn "Please finish the rebase manually now."
warn "When finished, re-run:"
warn " git flow feature finish '$NAME' '$DEVELOP_BRANCH'"
exit 1
fi
fi
# merge into BASE
git checkout "$DEVELOP_BRANCH"
if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then
git merge --ff "$BRANCH"
else
git merge --no-ff "$BRANCH"
fi
if [ $? -ne 0 ]; then
# oops.. we have a merge conflict!
# write the given $DEVELOP_BRANCH to a temporary file (we need it later)
mkdir -p "$GIT_DIR/.gitflow"
echo "$DEVELOP_BRANCH" > "$GIT_DIR/.gitflow/MERGE_BASE"
echo
echo "There were merge conflicts. To resolve the merge conflict manually, use:"
echo " git mergetool"
echo " git commit"
echo
echo "You can then complete the finish by running it again:"
echo " git flow feature finish $NAME"
echo
exit 1
fi
# when no merge conflict is detected, just clean up the feature branch
helper_finish_cleanup
}
helper_finish_cleanup() {
# sanity checks
gitflow_require_branch "$BRANCH"
gitflow_require_clean_working_tree
# delete branch
if flag fetch; then
git push "$ORIGIN" ":refs/heads/$BRANCH"
fi
git branch -d "$BRANCH"
echo
echo "Summary of actions:"
echo "- The feature branch '$BRANCH' was merged into '$DEVELOP_BRANCH'"
#echo "- Merge conflicts were resolved" # TODO: Add this line when it's supported
echo "- Feature branch '$BRANCH' has been removed"
echo "- You are now on branch '$DEVELOP_BRANCH'"
echo
}
cmd_publish() {
parse_args "$@"
expand_nameprefix_arg
# sanity checks
gitflow_require_clean_working_tree
gitflow_require_branch "$BRANCH"
git fetch -q "$ORIGIN"
gitflow_require_branch_absent "$ORIGIN/$BRANCH"
# create remote branch
git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
git fetch -q "$ORIGIN"
# configure remote tracking
git config "branch.$BRANCH.remote" "$ORIGIN"
git config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
git checkout "$BRANCH"
echo
echo "Summary of actions:"
echo "- A new remote branch '$BRANCH' was created"
echo "- The local branch '$BRANCH' was configured to track the remote branch"
echo "- You are now on branch '$BRANCH'"
echo
}
cmd_track() {
parse_args "$@"
require_name_arg
# sanity checks
gitflow_require_clean_working_tree
gitflow_require_branch_absent "$BRANCH"
git fetch -q "$ORIGIN"
gitflow_require_branch "$ORIGIN/$BRANCH"
# create tracking branch
git checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
echo
echo "Summary of actions:"
echo "- A new remote tracking branch '$BRANCH' was created"
echo "- You are now on branch '$BRANCH'"
echo
}
cmd_diff() {
parse_args "$@"
if [ "$NAME" != "" ]; then
expand_nameprefix_arg
BASE=$(git merge-base "$DEVELOP_BRANCH" "$BRANCH")
git diff "$BASE..$BRANCH"
else
if ! gitflow_current_branch | grep -q "^$PREFIX"; then
die "Not on a feature branch. Name one explicitly."
fi
BASE=$(git merge-base "$DEVELOP_BRANCH" HEAD)
git diff "$BASE"
fi
}
cmd_rebase() {
DEFINE_boolean interactive false 'do an interactive rebase' i
parse_args "$@"
expand_nameprefix_arg_or_current
warn "Will try to rebase '$NAME'..."
gitflow_require_clean_working_tree
gitflow_require_branch "$BRANCH"
git checkout -q "$BRANCH"
typeset OPTS=
if flag interactive; then
OPTS="$OPTS -i"
fi
git rebase $OPTS "$DEVELOP_BRANCH"
}
Jump to Line
Something went wrong with that request. Please try again.