Added functionality around `git flow project` for creating projects. #272

Open
wants to merge 15 commits into from
View
1 .gitignore
@@ -2,3 +2,4 @@ debian/files
debian/*.substvars
debian/*.debhelper.log
debian/*/*
+howto_install.txt
View
14 Changes.mdown
@@ -1,3 +1,17 @@
+0.4.3:
+-----
+Release: **not yet**
+
+* Added `git flow project` based on `git flow feature` for branches that
+ typically lives longer than 1 day.
+
+* Changed `git flow feature finish` to accept a base branch in order to finish
+ features initially created from a project branch.
+
+* Changed `git flow init` to ask the user what the project branches prefixes should
+ be called.
+
+
0.4.2:
-----
Release date: **not yet**
View
1 Makefile
@@ -34,6 +34,7 @@ EXEC_FILES=git-flow
# files that need mode 644
SCRIPT_FILES =git-flow-init
+SCRIPT_FILES+=git-flow-project
SCRIPT_FILES+=git-flow-feature
SCRIPT_FILES+=git-flow-hotfix
SCRIPT_FILES+=git-flow-release
View
2 contrib/gitflow-installer.sh
@@ -21,7 +21,7 @@ if [ -z "$REPO_HOME" ] ; then
fi
EXEC_FILES="git-flow"
-SCRIPT_FILES="git-flow-init git-flow-feature git-flow-hotfix git-flow-release git-flow-support git-flow-version gitflow-common gitflow-shFlags"
+SCRIPT_FILES="git-flow-init git-flow-project git-flow-feature git-flow-hotfix git-flow-release git-flow-support git-flow-version gitflow-common gitflow-shFlags"
SUBMODULE_FILE="gitflow-shFlags"
echo "### gitflow no-make installer ###"
View
1 git-flow
@@ -54,6 +54,7 @@ usage() {
echo
echo "Available subcommands are:"
echo " init Initialize a new git repo with support for the branching model."
+ echo " project Manage your project branches."
echo " feature Manage your feature branches."
echo " release Manage your release branches."
echo " hotfix Manage your hotfix branches."
View
87 git-flow-feature
@@ -204,7 +204,7 @@ cmd_start() {
# update the local repo with remote changes, if asked
if flag fetch; then
- git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
+ git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
fi
# if the origin branch counterpart exists, assert that the local branch
@@ -214,7 +214,7 @@ cmd_start() {
fi
# create branch
- if ! git_do checkout -b "$BRANCH" "$BASE"; then
+ if ! git checkout -b "$BRANCH" "$BASE"; then
die "Could not create feature branch '$BRANCH'"
fi
@@ -225,7 +225,7 @@ cmd_start() {
echo ""
echo "Now, start committing on your feature. When done, use:"
echo ""
- echo " git flow feature finish $NAME"
+ echo " git flow feature finish $NAME $BASE"
echo
}
@@ -236,6 +236,7 @@ cmd_finish() {
DEFINE_boolean force_delete false "force delete feature branch after finish" D
DEFINE_boolean squash false "squash feature during merge" S
parse_args "$@"
+ BASE=${2:-$DEVELOP_BRANCH}
expand_nameprefix_arg_or_current
# sanity checks
@@ -287,48 +288,56 @@ cmd_finish() {
# update local repo with remote changes first, if asked
if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
if flag fetch; then
- git_do fetch -q "$ORIGIN" "$BRANCH"
- git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
+ git fetch -q "$ORIGIN" "$BRANCH"
+ #git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
+ git fetch -q "$ORIGIN" "$BASE"
fi
fi
if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
require_branches_equal "$BRANCH" "$ORIGIN/$BRANCH"
fi
- if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
- require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
+ #if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
+ if has "$ORIGIN/$BASE" $(git_remote_branches); then
+ #require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
+ require_branches_equal "$BASE" "$ORIGIN/$BASE"
fi
# if the user wants to rebase, do that first
if flag rebase; then
- if ! git flow feature rebase "$NAME" "$DEVELOP_BRANCH"; then
+ #if ! git flow feature rebase "$NAME" "$DEVELOP_BRANCH"; 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' '$DEVELOP_BRANCH'"
+ #warn " git flow feature finish '$NAME' '$DEVELOP_BRANCH'"
+ warn " git flow feature finish '$NAME' '$BASE'"
exit 1
fi
fi
# merge into BASE
- git_do checkout "$DEVELOP_BRANCH"
- if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then
- git_do merge --ff "$BRANCH"
+ #git checkout "$DEVELOP_BRANCH"
+ git checkout "$BASE"
+ #if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then
+ if [ "$(git rev-list -n2 "$BASE..$BRANCH" | wc -l)" -eq 1 ]; then
+ git merge --ff "$BRANCH"
else
if noflag squash; then
- git_do merge --no-ff "$BRANCH"
+ git merge --no-ff "$BRANCH"
else
- git_do merge --squash "$BRANCH"
- git_do commit
- git_do merge "$BRANCH"
+ git merge --squash "$BRANCH"
+ git commit
+ git merge "$BRANCH"
fi
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 "$DOT_GIT_DIR/.gitflow"
- echo "$DEVELOP_BRANCH" > "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
+ #echo "$DEVELOP_BRANCH" > "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
+ echo "$BASE" > "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
echo
echo "There were merge conflicts. To resolve the merge conflict manually, use:"
echo " git mergetool"
@@ -351,28 +360,30 @@ helper_finish_cleanup() {
# delete branch
if flag fetch; then
- git_do push "$ORIGIN" ":refs/heads/$BRANCH"
+ git push "$ORIGIN" ":refs/heads/$BRANCH"
fi
if noflag keep; then
if flag force_delete; then
- git_do branch -D "$BRANCH"
+ git branch -D "$BRANCH"
else
- git_do branch -d "$BRANCH"
+ git branch -d "$BRANCH"
fi
fi
echo
echo "Summary of actions:"
- echo "- The feature branch '$BRANCH' was merged into '$DEVELOP_BRANCH'"
+ #echo "- The feature branch '$BRANCH' was merged into '$DEVELOP_BRANCH'"
+ echo "- The feature branch '$BRANCH' was merged into '$BASE'"
#echo "- Merge conflicts were resolved" # TODO: Add this line when it's supported
if flag keep; then
echo "- Feature branch '$BRANCH' is still available"
else
echo "- Feature branch '$BRANCH' has been removed"
fi
- echo "- You are now on branch '$DEVELOP_BRANCH'"
+ #echo "- You are now on branch '$DEVELOP_BRANCH'"
+ echo "- You are now on branch '$BASE'"
echo
}
@@ -383,17 +394,17 @@ cmd_publish() {
# sanity checks
require_clean_working_tree
require_branch "$BRANCH"
- git_do fetch -q "$ORIGIN"
+ git fetch -q "$ORIGIN"
require_branch_absent "$ORIGIN/$BRANCH"
# create remote branch
- git_do push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
- git_do fetch -q "$ORIGIN"
+ git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
+ git fetch -q "$ORIGIN"
# configure remote tracking
- git_do config "branch.$BRANCH.remote" "$ORIGIN"
- git_do config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
- git_do checkout "$BRANCH"
+ git config "branch.$BRANCH.remote" "$ORIGIN"
+ git config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
+ git checkout "$BRANCH"
echo
echo "Summary of actions:"
@@ -410,11 +421,11 @@ cmd_track() {
# sanity checks
require_clean_working_tree
require_branch_absent "$BRANCH"
- git_do fetch -q "$ORIGIN"
+ git fetch -q "$ORIGIN"
require_branch "$ORIGIN/$BRANCH"
# create tracking branch
- git_do checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
+ git checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
echo
echo "Summary of actions:"
@@ -445,7 +456,7 @@ cmd_checkout() {
if [ "$NAME" != "" ]; then
expand_nameprefix_arg
- git_do checkout "$BRANCH"
+ git checkout "$BRANCH"
else
die "Name a feature branch explicitly."
fi
@@ -464,12 +475,12 @@ cmd_rebase() {
require_clean_working_tree
require_branch "$BRANCH"
- git_do checkout -q "$BRANCH"
+ git checkout -q "$BRANCH"
local OPTS=
if flag interactive; then
OPTS="$OPTS -i"
fi
- git_do rebase $OPTS "$DEVELOP_BRANCH"
+ git rebase $OPTS "$DEVELOP_BRANCH"
}
avoid_accidental_cross_branch_action() {
@@ -511,20 +522,20 @@ cmd_pull() {
# we already have a local branch called like this, so simply pull the
# remote changes in
if flag rebase; then
- if ! git_do pull --rebase -q "$REMOTE" "$BRANCH"; then
+ if ! git pull --rebase -q "$REMOTE" "$BRANCH"; then
warn "Pull was aborted. There might be conflicts during rebase or '$REMOTE' might be inaccessible."
exit 1
fi
else
- git_do pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'."
+ git pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'."
fi
echo "Pulled $REMOTE's changes into $BRANCH."
else
# setup the local branch clone for the first time
- git_do fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD
- git_do branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed."
- git_do checkout -q "$BRANCH" || die "Checking out new local branch failed."
+ git fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD
+ git branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed."
+ git checkout -q "$BRANCH" || die "Checking out new local branch failed."
echo "Created local branch $BRANCH based on $REMOTE's $BRANCH."
fi
}
View
42 git-flow-init
@@ -53,7 +53,7 @@ cmd_default() {
parse_args "$@"
if ! git rev-parse --git-dir >/dev/null 2>&1; then
- git_do init
+ git init
else
# assure that we are not working in a repo with local changes
git_repo_is_headless || require_clean_working_tree
@@ -121,14 +121,14 @@ cmd_default() {
# name exists, checkout that branch and use it for master
if ! git_local_branch_exists "$master_branch" && \
git_remote_branch_exists "origin/$master_branch"; then
- git_do branch "$master_branch" "origin/$master_branch" >/dev/null 2>&1
+ git branch "$master_branch" "origin/$master_branch" >/dev/null 2>&1
elif ! git_local_branch_exists "$master_branch"; then
die "Local branch '$master_branch' does not exist."
fi
fi
# store the name of the master branch
- git_do config gitflow.branch.master "$master_branch"
+ git config gitflow.branch.master "$master_branch"
fi
# add a develop branch if no such branch exists yet
@@ -185,7 +185,7 @@ cmd_default() {
fi
# store the name of the develop branch
- git_do config gitflow.branch.develop "$develop_branch"
+ git config gitflow.branch.develop "$develop_branch"
fi
# Creation of HEAD
@@ -194,8 +194,8 @@ cmd_default() {
# it to be able to create new branches.
local created_gitflow_branch=0
if ! git rev-parse --quiet --verify HEAD >/dev/null 2>&1; then
- git_do symbolic-ref HEAD "refs/heads/$master_branch"
- git_do commit --allow-empty --quiet -m "Initial commit"
+ git symbolic-ref HEAD "refs/heads/$master_branch"
+ git commit --allow-empty --quiet -m "Initial commit"
created_gitflow_branch=1
fi
@@ -213,9 +213,9 @@ cmd_default() {
# the develop branch now in that case (we base it on master, of course)
if ! git_local_branch_exists "$develop_branch"; then
if git_remote_branch_exists "origin/$develop_branch"; then
- git_do branch "$develop_branch" "origin/$develop_branch" >/dev/null 2>&1
+ git branch "$develop_branch" "origin/$develop_branch" >/dev/null 2>&1
else
- git_do branch --no-track "$develop_branch" "$master_branch"
+ git branch --no-track "$develop_branch" "$master_branch"
fi
created_gitflow_branch=1
fi
@@ -225,7 +225,7 @@ cmd_default() {
# switch to develop branch if its newly created
if [ $created_gitflow_branch -eq 1 ]; then
- git_do checkout -q "$develop_branch"
+ git checkout -q "$develop_branch"
fi
# finally, ask the user for naming conventions (branch and tag prefixes)
@@ -234,13 +234,27 @@ cmd_default() {
! git config --get gitflow.prefix.release >/dev/null 2>&1 ||
! git config --get gitflow.prefix.hotfix >/dev/null 2>&1 ||
! git config --get gitflow.prefix.support >/dev/null 2>&1 ||
+ ! git config --get gitflow.prefix.project >/dev/null 2>&1 ||
! git config --get gitflow.prefix.versiontag >/dev/null 2>&1; then
echo
echo "How to name your supporting branch prefixes?"
fi
local prefix
+ # Project branches
+ if ! git config --get gitflow.prefix.project >/dev/null 2>&1 || flag force; then
+ default_suggestion=$(git config --get gitflow.prefix.project || echo project/)
+ printf "Project branches? [$default_suggestion] "
+ if noflag defaults; then
+ read answer
+ else
+ printf "\n"
+ fi
+ [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
+ git config gitflow.prefix.project "$prefix"
+ fi
+
# Feature branches
if ! git config --get gitflow.prefix.feature >/dev/null 2>&1 || flag force; then
default_suggestion=$(git config --get gitflow.prefix.feature || echo feature/)
@@ -251,7 +265,7 @@ cmd_default() {
printf "\n"
fi
[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
- git_do config gitflow.prefix.feature "$prefix"
+ git config gitflow.prefix.feature "$prefix"
fi
# Release branches
@@ -264,7 +278,7 @@ cmd_default() {
printf "\n"
fi
[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
- git_do config gitflow.prefix.release "$prefix"
+ git config gitflow.prefix.release "$prefix"
fi
@@ -278,7 +292,7 @@ cmd_default() {
printf "\n"
fi
[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
- git_do config gitflow.prefix.hotfix "$prefix"
+ git config gitflow.prefix.hotfix "$prefix"
fi
@@ -292,7 +306,7 @@ cmd_default() {
printf "\n"
fi
[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
- git_do config gitflow.prefix.support "$prefix"
+ git config gitflow.prefix.support "$prefix"
fi
@@ -306,7 +320,7 @@ cmd_default() {
printf "\n"
fi
[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
- git_do config gitflow.prefix.versiontag "$prefix"
+ git config gitflow.prefix.versiontag "$prefix"
fi
View
530 git-flow-project
@@ -0,0 +1,530 @@
+#
+# 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/git-model
+#
+# Feel free to contribute to this project at:
+# http://github.com/nvie/gitflow
+#
+# Copyright 2010 Vincent Driessen. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are
+# those of the authors and should not be interpreted as representing official
+# policies, either expressed or implied, of Vincent Driessen.
+#
+
+init() {
+ require_git_repo
+ require_gitflow_initialized
+ gitflow_load_settings
+ PREFIX=$(git config --get gitflow.prefix.project)
+}
+
+usage() {
+ echo "usage: git flow project [list] [-v]"
+ echo " git flow project start [-F] <name> [<base>]"
+ echo " git flow project finish [-rFkDS] [<name|nameprefix>]"
+ echo " git flow project publish <name>"
+ echo " git flow project track <name>"
+ echo " git flow project diff [<name|nameprefix>]"
+ echo " git flow project rebase [-i] [<name|nameprefix>]"
+ echo " git flow project checkout [<name|nameprefix>]"
+ echo " git flow project pull [-r] <remote> [<name>]"
+}
+
+cmd_default() {
+ cmd_list "$@"
+}
+
+cmd_list() {
+ DEFINE_boolean verbose false 'verbose (more) output' v
+ parse_args "$@"
+
+ local project_branches
+ local current_branch
+ local short_names
+ project_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
+ if [ -z "$project_branches" ]; then
+ warn "No project branches exist."
+ warn ""
+ warn "You can start a new project branch:"
+ warn ""
+ warn " git flow project start <name> [<base>]"
+ warn ""
+ exit 0
+ fi
+ current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
+ short_names=$(echo "$project_branches" | sed "s ^$PREFIX g")
+
+ # determine column width first
+ local width=0
+ local branch
+ for branch in $short_names; do
+ local len=${#branch}
+ width=$(max $width $len)
+ done
+ width=$(($width+3))
+
+ local branch
+ for branch in $short_names; do
+ local fullname=$PREFIX$branch
+ local base=$(git merge-base "$fullname" "$DEVELOP_BRANCH")
+ local develop_sha=$(git rev-parse "$DEVELOP_BRANCH")
+ local 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
+
+ local expanded_name
+ local exitcode
+ expanded_name=$(gitflow_resolve_nameprefix "$NAME" "$PREFIX")
+ exitcode=$?
+ case $exitcode in
+ 0) NAME=$expanded_name
+ BRANCH=$PREFIX$NAME
+ ;;
+ *) exit 1 ;;
+ esac
+}
+
+use_current_project_branch_name() {
+ local current_branch=$(git_current_branch)
+ if startswith "$current_branch" "$PREFIX"; then
+ BRANCH=$current_branch
+ NAME=${BRANCH#$PREFIX}
+ else
+ warn "The current HEAD is no project branch."
+ warn "Please specify a <name> argument."
+ exit 1
+ fi
+}
+
+expand_nameprefix_arg_or_current() {
+ if [ "$NAME" != "" ]; then
+ expand_nameprefix_arg
+ require_branch "$PREFIX$NAME"
+ else
+ use_current_project_branch_name
+ fi
+}
+
+name_or_current() {
+ if [ -z "$NAME" ]; then
+ use_current_project_branch_name
+ fi
+}
+
+parse_args() {
+ # parse options
+ FLAGS "$@" || exit $?
+ eval set -- "${FLAGS_ARGV}"
+
+ # read arguments into global variables
+ NAME=$1
+ BRANCH=$PREFIX$NAME
+}
+
+parse_remote_name() {
+ # parse options
+ FLAGS "$@" || exit $?
+ eval set -- "${FLAGS_ARGV}"
+
+ # read arguments into global variables
+ REMOTE=$1
+ NAME=$2
+ BRANCH=$PREFIX$NAME
+}
+
+cmd_start() {
+ DEFINE_boolean fetch false 'fetch from origin before performing local operation' F
+ parse_args "$@"
+ BASE=${2:-$DEVELOP_BRANCH}
+ require_name_arg
+
+ # sanity checks
+ require_branch_absent "$BRANCH"
+
+ # update the local repo with remote changes, if asked
+ if flag fetch; then
+ git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
+ fi
+
+ # if the origin branch counterpart exists, assert that the local branch
+ # isn't behind it (to avoid unnecessary rebasing)
+ if git_branch_exists "$ORIGIN/$DEVELOP_BRANCH"; then
+ require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
+ fi
+
+ # create branch
+ if ! git checkout -b "$BRANCH" "$BASE"; then
+ die "Could not create project 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, use this project branch as a base for creating feature branches. When the project is done, use:"
+ echo ""
+ echo " git flow project finish $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 keep false "keep branch after performing finish" k
+ DEFINE_boolean force_delete false "force delete project branch after finish" D
+ DEFINE_boolean squash false "squash project during merge" S
+ parse_args "$@"
+ expand_nameprefix_arg_or_current
+
+ # sanity checks
+ require_branch "$BRANCH"
+
+ # detect if we're restoring from a merge conflict
+ if [ -f "$DOT_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: git_is_clean_working_tree() should provide an alternative
+ # exit code for "unmerged changes in working tree", which we should
+ # actually be testing for here
+ if git_is_clean_working_tree; then
+ FINISH_BASE=$(cat "$DOT_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 git_is_branch_merged_into()
+ if git_is_branch_merged_into "$BRANCH" "$FINISH_BASE"; then
+ rm -f "$DOT_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 "$DOT_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 project finish $NAME"
+ echo
+ exit 1
+ fi
+ fi
+
+ # sanity checks
+ require_clean_working_tree
+
+ # update local repo with remote changes first, if asked
+ if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
+ if flag fetch; then
+ git fetch -q "$ORIGIN" "$BRANCH"
+ git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
+ fi
+ fi
+
+ if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
+ require_branches_equal "$BRANCH" "$ORIGIN/$BRANCH"
+ fi
+ if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
+ 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 project 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 project 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
+ if noflag squash; then
+ git merge --no-ff "$BRANCH"
+ else
+ git merge --squash "$BRANCH"
+ git commit
+ git merge "$BRANCH"
+ fi
+ 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 "$DOT_GIT_DIR/.gitflow"
+ echo "$DEVELOP_BRANCH" > "$DOT_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 project finish $NAME"
+ echo
+ exit 1
+ fi
+
+ # when no merge conflict is detected, just clean up the project branch
+ helper_finish_cleanup
+}
+
+helper_finish_cleanup() {
+ # sanity checks
+ require_branch "$BRANCH"
+ require_clean_working_tree
+
+ # delete branch
+ if flag fetch; then
+ git push "$ORIGIN" ":refs/heads/$BRANCH"
+ fi
+
+
+ if noflag keep; then
+ if flag force_delete; then
+ git branch -D "$BRANCH"
+ else
+ git branch -d "$BRANCH"
+ fi
+ fi
+
+ echo
+ echo "Summary of actions:"
+ echo "- The project branch '$BRANCH' was merged into '$DEVELOP_BRANCH'"
+ #echo "- Merge conflicts were resolved" # TODO: Add this line when it's supported
+ if flag keep; then
+ echo "- Project branch '$BRANCH' is still available"
+ else
+ echo "- Project branch '$BRANCH' has been removed"
+ fi
+ echo "- You are now on branch '$DEVELOP_BRANCH'"
+ echo
+}
+
+cmd_publish() {
+ parse_args "$@"
+ expand_nameprefix_arg
+
+ # sanity checks
+ require_clean_working_tree
+ require_branch "$BRANCH"
+ git fetch -q "$ORIGIN"
+ 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
+ require_clean_working_tree
+ require_branch_absent "$BRANCH"
+ git fetch -q "$ORIGIN"
+ 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 ! git_current_branch | grep -q "^$PREFIX"; then
+ die "Not on a project branch. Name one explicitly."
+ fi
+
+ BASE=$(git merge-base "$DEVELOP_BRANCH" HEAD)
+ git diff "$BASE"
+ fi
+}
+
+cmd_checkout() {
+ parse_args "$@"
+
+ if [ "$NAME" != "" ]; then
+ expand_nameprefix_arg
+ git checkout "$BRANCH"
+ else
+ die "Name a project branch explicitly."
+ fi
+}
+
+cmd_co() {
+ # Alias for checkout
+ cmd_checkout "$@"
+}
+
+cmd_rebase() {
+ DEFINE_boolean interactive false 'do an interactive rebase' i
+ parse_args "$@"
+ expand_nameprefix_arg_or_current
+ warn "Will try to rebase '$NAME'..."
+ require_clean_working_tree
+ require_branch "$BRANCH"
+
+ git checkout -q "$BRANCH"
+ local OPTS=
+ if flag interactive; then
+ OPTS="$OPTS -i"
+ fi
+ git rebase $OPTS "$DEVELOP_BRANCH"
+}
+
+avoid_accidental_cross_branch_action() {
+ local current_branch=$(git_current_branch)
+ if [ "$BRANCH" != "$current_branch" ]; then
+ warn "Trying to pull from '$BRANCH' while currently on branch '$current_branch'."
+ warn "To avoid unintended merges, git-flow aborted."
+ return 1
+ fi
+ return 0
+}
+
+cmd_pull() {
+ #DEFINE_string prefix false 'alternative remote project branch name prefix' p
+ DEFINE_boolean rebase false "pull with rebase" r
+ parse_remote_name "$@"
+
+ if [ -z "$REMOTE" ]; then
+ die "Name a remote explicitly."
+ fi
+ name_or_current
+
+ # To avoid accidentally merging different project branches into each other,
+ # die if the current porject branch differs from the requested $NAME
+ # argument.
+ local current_branch=$(git_current_branch)
+ if startswith "$current_branch" "$PREFIX"; then
+ # we are on a local project branch already, so $BRANCH must be equal to
+ # the current branch
+ avoid_accidental_cross_branch_action || die
+ fi
+
+ require_clean_working_tree
+
+ if git_branch_exists "$BRANCH"; then
+ # Again, avoid accidental merges
+ avoid_accidental_cross_branch_action || die
+
+ # we already have a local branch called like this, so simply pull the
+ # remote changes in
+ if flag rebase; then
+ if ! git pull --rebase -q "$REMOTE" "$BRANCH"; then
+ warn "Pull was aborted. There might be conflicts during rebase or '$REMOTE' might be inaccessible."
+ exit 1
+ fi
+ else
+ git pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'."
+ fi
+
+ echo "Pulled $REMOTE's changes into $BRANCH."
+ else
+ # setup the local branch clone for the first time
+ git fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD
+ git branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed."
+ git checkout -q "$BRANCH" || die "Checking out new local branch failed."
+ echo "Created local branch $BRANCH based on $REMOTE's $BRANCH."
+ fi
+}