Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 4417492346
Fetching contributors…

Cannot retrieve contributors at this time

382 lines (331 sloc) 9.946 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> [<base>]"
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 "$@"
}
max() { if [ "$1" -gt "$2" ]; then echo "$1"; else echo "$2"; fi; }
cmd_list() {
DEFINE_boolean verbose false 'verbose (more) output' v
parse_args "$@"
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
width=0
for branch in $SHORT_NAMES; do
len=$(($(echo "$branch" | wc -c) - 1))
width=$(max $width $len)
done
width=$(($width + 3))
for branch in $SHORT_NAMES; do
fullname="$PREFIX$branch"
base=$(git merge-base "$fullname" "$DEVELOP_BRANCH")
develop_sha=$(git rev-parse "$DEVELOP_BRANCH")
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
}
resolve_name_by_prefix() {
# first, check if there is a perfect match
if has "$LOCAL_BRANCHES" "$PREFIX$1"; then
echo "$1"
return 0
fi
MATCHES="$(echo "$LOCAL_BRANCHES" | grep "^$PREFIX$1")"
NUM_MATCHES=$(echo "$MATCHES" | wc -l)
if [ -z "$MATCHES" ]; then
# no prefix match, so take it literally
echo "$1"
else
if [ $NUM_MATCHES -eq 1 ]; then
# sed arg looks a bit weird, but $PREFIX should not contain spaces,
# so this one is safe
echo "$MATCHES" | sed "s $PREFIX g"
else
# multiple matches, cannot decide
warn "Multiple branches match for prefix '$1':"
for match in $MATCHES; do
warn "- $match"
done
die "Aborting. Use an unambiguous prefix."
fi
fi
}
require_name() {
if [ "$NAME" = "" ]; then
echo "Missing argument <name>"
usage
exit 1
fi
}
expand_name_arg_prefix() {
require_name
NAME=$(resolve_name_by_prefix "$NAME")
if echo "$NAME" | grep -q "^[ \t\n\r]+$"; then
exit 1
fi
BRANCH=$PREFIX$NAME
}
expand_name_arg_prefix_or_current() {
CURRENT_BRANCH=$(git branch | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
if [ "$NAME" != "" ]; then
expand_name_arg_prefix
elif [ "$CURRENT_BRANCH" != "" ]; then
BRANCH="$CURRENT_BRANCH"
NAME=$(echo "$BRANCH" | sed "s?$PREFIX??g")
else
warn "The current HEAD is no feature branch."
warn "To diff a feature, specify a <name> argument."
usage
exit 1
fi
}
parse_args() {
# parse options
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
# read arguments into global variables
NAME="$1"
BASE="${2:-$DEVELOP_BRANCH}"
BRANCH=$PREFIX$NAME
}
cmd_start() {
DEFINE_boolean fetch false 'fetch from origin before performing local operation' F
parse_args "$@"
require_name
# sanity checks
gitflow_require_clean_working_tree
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
git checkout -b $BRANCH $BASE
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 local operation' 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_name_arg_prefix
# 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
if [ "$BASE" = "$DEVELOP_BRANCH" ]; then
gitflow_require_branches_equal $DEVELOP_BRANCH $ORIGIN/$DEVELOP_BRANCH
fi
# if the user wants to rebase, do that first
if flag rebase; then
if ! git flow feature rebase "$NAME" "$BASE"; 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' '$BASE'"
exit 1
fi
fi
# merge into BASE
git checkout $BASE
if [ "$(git rev-list -n2 $BASE..$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 $BASE to a temporary file (we need it later)
mkdir -p "$GIT_DIR/.gitflow"
echo "$BASE" > "$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 '$BASE'"
#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 '$BASE'"
echo
}
cmd_publish() {
parse_args "$@"
expand_name_arg_prefix
# 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
# 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 "$@"
expand_name_arg_prefix_or_current
# TODO: if this feature has been based on a non-develop branch, we really
# should not be comparing to $DEVELOP. How to deal with this?
git diff $DEVELOP_BRANCH..$BRANCH
}
cmd_rebase() {
DEFINE_boolean interactive false 'do an interactive rebase' i
parse_args "$@"
expand_name_arg_prefix_or_current
warn "Will try to rebase '$NAME'..."
gitflow_require_clean_working_tree
gitflow_require_branch "$BRANCH"
git checkout -q "$BRANCH"
OPTS=
if flag interactive; then
OPTS="$OPTS -i"
fi
git rebase $OPTS "$BASE"
}
Jump to Line
Something went wrong with that request. Please try again.