Commit 7031de7e authored by cmosh's avatar cmosh

Small bug with finish commit

parent 171cdf0e
# #
# git-flow -- A collection of Git extensions to provide high-level # git-flow -- A collection of Git extensions to provide high-level
# repository operations for Vincent Driessen's branching model. # repository operations for Vincent Driessen's branching model.
# #
# Original blog post presenting this model is found at: # Original blog post presenting this model is found at:
# http://nvie.com/git-model # http://nvie.com/git-model
# #
# Feel free to contribute to this project at: # Feel free to contribute to this project at:
# http://github.com/nvie/gitflow # http://github.com/nvie/gitflow
# #
# Copyright 2010 Vincent Driessen. All rights reserved. # Copyright 2010 Vincent Driessen. All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
# #
# 1. Redistributions of source code must retain the above copyright notice, # 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. # this list of conditions and the following disclaimer.
# #
# 2. Redistributions in binary form must reproduce the above copyright # 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the # notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution. # documentation and/or other materials provided with the distribution.
# #
# THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # 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 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
# The views and conclusions contained in the software and documentation are # The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official # those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of Vincent Driessen. # policies, either expressed or implied, of Vincent Driessen.
# #
init() { init() {
require_git_repo require_git_repo
require_gitflow_initialized require_gitflow_initialized
gitflow_load_settings gitflow_load_settings
parse_args "$@" parse_args "$@"
PREFIX=$(git config --get gitflow.prefix.feature) PREFIX=$(git config --get gitflow.prefix.feature)
} }
usage() { usage() {
echo "usage: git flow feature [list] [-v] s" echo "usage: git flow feature [list] [-v] s"
echo " git flow feature start [-F] <name> [<base>]" echo " git flow feature start [-F] <name> [<base>]"
echo " git flow feature finish [-rFkDS] [<name|nameprefix>]" echo " git flow feature finish [-rFkDS] [<name|nameprefix>]"
echo " git flow feature publish <name>" echo " git flow feature publish <name>"
echo " git flow feature track <name>" echo " git flow feature track <name>"
echo " git flow feature diff [<name|nameprefix>]" echo " git flow feature diff [<name|nameprefix>]"
echo " git flow feature rebase [-i] [<name|nameprefix>]" echo " git flow feature rebase [-i] [<name|nameprefix>]"
echo " git flow feature checkout [<name|nameprefix>]" echo " git flow feature checkout [<name|nameprefix>]"
echo " git flow feature pull [-r] <remote> [<name>]" echo " git flow feature pull [-r] <remote> [<name>]"
echo " git flow feature pause <name>" echo " git flow feature pause <name>"
} }
cmd_default() { cmd_default() {
cmd_list "$@" cmd_list "$@"
} }
cmd_list() { cmd_list() {
DEFINE_boolean verbose false 'verbose (more) output' v DEFINE_boolean verbose false 'verbose (more) output' v
parse_args "$@" parse_args "$@"
local feature_branches local feature_branches
local current_branch local current_branch
local short_names local short_names
feature_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX") feature_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
if [ -z "$feature_branches" ]; then if [ -z "$feature_branches" ]; then
warn "No feature branches exist." warn "No feature branches exist."
warn "" warn ""
warn "You can start a new feature branch:" warn "You can start a new feature branch:"
warn "" warn ""
warn " git flow feature start <name> [<base>]" warn " git flow feature start <name> [<base>]"
warn "" warn ""
exit 0 exit 0
fi fi
current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g') current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
short_names=$(echo "$feature_branches" | sed "s ^$PREFIX g") short_names=$(echo "$feature_branches" | sed "s ^$PREFIX g")
# determine column width first # determine column width first
local width=0 local width=0
local branch local branch
for branch in $short_names; do for branch in $short_names; do
local len=${#branch} local len=${#branch}
width=$(max $width $len) width=$(max $width $len)
done done
width=$(($width+3)) width=$(($width+3))
local branch local branch
for branch in $short_names; do for branch in $short_names; do
local fullname=$PREFIX$branch local fullname=$PREFIX$branch
local base=$(git merge-base "$fullname" "$DEVELOP_BRANCH") local base=$(git merge-base "$fullname" "$DEVELOP_BRANCH")
local develop_sha=$(git rev-parse "$DEVELOP_BRANCH") local develop_sha=$(git rev-parse "$DEVELOP_BRANCH")
local branch_sha=$(git rev-parse "$fullname") local branch_sha=$(git rev-parse "$fullname")
if [ "$fullname" = "$current_branch" ]; then if [ "$fullname" = "$current_branch" ]; then
printf "* " printf "* "
else else
printf " " printf " "
fi fi
if flag verbose; then if flag verbose; then
printf "%-${width}s" "$branch" printf "%-${width}s" "$branch"
if [ "$branch_sha" = "$develop_sha" ]; then if [ "$branch_sha" = "$develop_sha" ]; then
printf "(no commits yet)" printf "(no commits yet)"
elif [ "$base" = "$branch_sha" ]; then elif [ "$base" = "$branch_sha" ]; then
printf "(is behind develop, may ff)" printf "(is behind develop, may ff)"
elif [ "$base" = "$develop_sha" ]; then elif [ "$base" = "$develop_sha" ]; then
printf "(based on latest develop)" printf "(based on latest develop)"
else else
printf "(may be rebased)" printf "(may be rebased)"
fi fi
else else
printf "%s" "$branch" printf "%s" "$branch"
fi fi
echo echo
done done
} }
cmd_help() { cmd_help() {
usage usage
exit 0 exit 0
} }
require_name_arg() { require_name_arg() {
if [ "$NAME" = "" ]; then if [ "$NAME" = "" ]; then
warn "Missing argument <name>" warn "Missing argument <name>"
usage usage
exit 1 exit 1
fi fi
} }
expand_nameprefix_arg() { expand_nameprefix_arg() {
require_name_arg require_name_arg
local expanded_name local expanded_name
local exitcode local exitcode
expanded_name=$(gitflow_resolve_nameprefix "$NAME" "$PREFIX") expanded_name=$(gitflow_resolve_nameprefix "$NAME" "$PREFIX")
exitcode=$? exitcode=$?
case $exitcode in case $exitcode in
0) NAME=$expanded_name 0) NAME=$expanded_name
BRANCH=$PREFIX$NAME BRANCH=$PREFIX$NAME
;; ;;
*) exit 1 ;; *) exit 1 ;;
esac esac
} }
use_current_feature_branch_name() { use_current_feature_branch_name() {
local current_branch=$(git_current_branch) local current_branch=$(git_current_branch)
if startswith "$current_branch" "$PREFIX"; then if startswith "$current_branch" "$PREFIX"; then
BRANCH=$current_branch BRANCH=$current_branch
NAME=${BRANCH#$PREFIX} NAME=${BRANCH#$PREFIX}
else else
warn "The current HEAD is no feature branch." warn "The current HEAD is no feature branch."
warn "Please specify a <name> argument." warn "Please specify a <name> argument."
exit 1 exit 1
fi fi
} }
expand_nameprefix_arg_or_current() { expand_nameprefix_arg_or_current() {
if [ "$NAME" != "" ]; then if [ "$NAME" != "" ]; then
expand_nameprefix_arg expand_nameprefix_arg
require_branch "$PREFIX$NAME" require_branch "$PREFIX$NAME"
else else
use_current_feature_branch_name use_current_feature_branch_name
fi fi
} }
name_or_current() { name_or_current() {
if [ -z "$NAME" ]; then if [ -z "$NAME" ]; then
use_current_feature_branch_name use_current_feature_branch_name
fi fi
} }
parse_args() { parse_args() {
# parse options # parse options
FLAGS "$@" || exit $? FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}" eval set -- "${FLAGS_ARGV}"
# read arguments into global variables # read arguments into global variables
NAME=$1 NAME=$1
BRANCH=$PREFIX$NAME BRANCH=$PREFIX$NAME
} }
parse_remote_name() { parse_remote_name() {
# parse options # parse options
FLAGS "$@" || exit $? FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}" eval set -- "${FLAGS_ARGV}"
# read arguments into global variables # read arguments into global variables
REMOTE=$1 REMOTE=$1
NAME=$2 NAME=$2
BRANCH=$PREFIX$NAME BRANCH=$PREFIX$NAME
} }
cmd_start() { cmd_start() {
DEFINE_boolean fetch false 'fetch from origin before performing local operation' F DEFINE_boolean fetch false 'fetch from origin before performing local operation' F
parse_args "$@" parse_args "$@"
BASE=${2:-$DEVELOP_BRANCH} BASE=${2:-$DEVELOP_BRANCH}
require_name_arg require_name_arg
echo ".seconds-feature-$NAME\n.timelog-feature-$NAME" >> .gitignore echo ".seconds-feature-$NAME\n.timelog-feature-$NAME" >> .gitignore
# sanity checks # sanity checks
require_branch_absent "$BRANCH" require_branch_absent "$BRANCH"
# update the local repo with remote changes, if asked # update the local repo with remote changes, if asked
if flag fetch; then if flag fetch; then
git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
fi fi
# if the origin branch counterpart exists, assert that the local branch # if the origin branch counterpart exists, assert that the local branch
# isn't behind it (to avoid unnecessary rebasing) # isn't behind it (to avoid unnecessary rebasing)
if git_branch_exists "$ORIGIN/$DEVELOP_BRANCH"; then if git_branch_exists "$ORIGIN/$DEVELOP_BRANCH"; then
require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH" require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
fi fi
# create branch # create branch
if ! git_do checkout -b "$BRANCH" "$BASE"; then if ! git_do checkout -b "$BRANCH" "$BASE"; then
die "Could not create feature branch '$BRANCH'" die "Could not create feature branch '$BRANCH'"
fi fi
echo "How long do you think this feature will take(in hours)?" echo "How long do you think this feature will take(in hours)?"
read TIME_REQUIRED read TIME_REQUIRED
echo $TIME_REQUIRED >> ".timelog-feature-$NAME" echo $TIME_REQUIRED >> ".timelog-feature-$NAME"
git add .gitignore git add .gitignore
git commit --allow-empty -m "Started working on $BRANCH on $(date) estimated Time $TIME_REQUIRED" git commit --allow-empty -m "Started working on $BRANCH on $(date) estimated Time $TIME_REQUIRED"
echo $(date +%s) >> ".timelog-feature-$NAME" echo $(date +%s) >> ".timelog-feature-$NAME"
echo echo
echo "Summary of actions:" echo "Summary of actions:"
echo "- A new branch '$BRANCH' was created, based on '$BASE'" echo "- A new branch '$BRANCH' was created, based on '$BASE'"
echo "- You are now on branch '$BRANCH'" echo "- You are now on branch '$BRANCH'"
echo "" echo ""
echo "Now, start committing on your feature. When done, use:" echo "Now, start committing on your feature. When done, use:"
echo "" echo ""
echo " git flow feature finish $NAME" echo " git flow feature finish $NAME"
echo echo
} }
cmd_finish() { cmd_finish() {
DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
DEFINE_boolean rebase false "rebase instead of merge" r DEFINE_boolean rebase false "rebase instead of merge" r
DEFINE_boolean keep false "keep branch after performing finish" k DEFINE_boolean keep false "keep branch after performing finish" k
DEFINE_boolean force_delete false "force delete feature branch after finish" D DEFINE_boolean force_delete false "force delete feature branch after finish" D
DEFINE_boolean squash false "squash feature during merge" S DEFINE_boolean squash false "squash feature during merge" S
parse_args "$@" parse_args "$@"
expand_nameprefix_arg_or_current expand_nameprefix_arg_or_current
# sanity checks # sanity checks
require_branch "$BRANCH" require_branch "$BRANCH"
# detect if we're restoring from a merge conflict # detect if we're restoring from a merge conflict
if [ -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE" ]; then if [ -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE" ]; then
# #
# TODO: detect that we're working on the correct branch here! # TODO: detect that we're working on the correct branch here!
# The user need not necessarily have given the same $NAME twice here # The user need not necessarily have given the same $NAME twice here
# (although he/she should). # (although he/she should).
# #
# TODO: git_is_clean_working_tree() should provide an alternative # TODO: git_is_clean_working_tree() should provide an alternative
# exit code for "unmerged changes in working tree", which we should # exit code for "unmerged changes in working tree", which we should
# actually be testing for # actually be testing for
if git_is_clean_working_tree; then if git_is_clean_working_tree; then
FINISH_BASE=$(cat "$DOT_GIT_DIR/.gitflow/MERGE_BASE") FINISH_BASE=$(cat "$DOT_GIT_DIR/.gitflow/MERGE_BASE")
# Since the working tree is now clean, either the user did a # Since the working tree is now clean, either the user did a
# succesfull merge manually, or the merge was cancelled. # succesfull merge manually, or the merge was cancelled.
# We detect this using git_is_branch_merged_into() # We detect this using git_is_branch_merged_into()
if git_is_branch_merged_into "$BRANCH" "$FINISH_BASE"; then if git_is_branch_merged_into "$BRANCH" "$FINISH_BASE"; then
rm -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE" rm -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
helper_finish_cleanup helper_finish_cleanup
exit 0 exit 0
else else
# If the user cancelled the merge and decided to wait until later, # If the user cancelled the merge and decided to wait until later,
# that's fine. But we have to acknowledge this by removing the # that's fine. But we have to acknowledge this by removing the
# MERGE_BASE file and continuing normal execution of the finish # MERGE_BASE file and continuing normal execution of the finish
rm -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE" rm -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
fi fi
else else
echo echo
echo "Merge conflicts not resolved yet, use:" echo "Merge conflicts not resolved yet, use:"
echo " git mergetool" echo " git mergetool"
echo " git commit" echo " git commit"
echo echo
echo "You can then complete the finish by running it again:" echo "You can then complete the finish by running it again:"
echo " git flow feature finish $NAME" echo " git flow feature finish $NAME"
echo echo
exit 1 exit 1
fi fi
fi fi
# sanity checks # sanity checks
require_clean_working_tree require_clean_working_tree
# update local repo with remote changes first, if asked # update local repo with remote changes first, if asked
if has "$ORIGIN/$BRANCH" $(git_remote_branches); then if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
if flag fetch; then if flag fetch; then
git_do fetch -q "$ORIGIN" "$BRANCH" git_do fetch -q "$ORIGIN" "$BRANCH"
git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
fi fi
fi fi
if has "$ORIGIN/$BRANCH" $(git_remote_branches); then if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
require_branches_equal "$BRANCH" "$ORIGIN/$BRANCH" require_branches_equal "$BRANCH" "$ORIGIN/$BRANCH"
fi fi
if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH" require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
fi fi
# if the user wants to rebase, do that first # if the user wants to rebase, do that first
if flag rebase; then if flag rebase; then
if ! git flow feature rebase "$NAME" "$DEVELOP_BRANCH"; then if ! git flow feature rebase "$NAME" "$DEVELOP_BRANCH"; then
warn "Finish was aborted due to conflicts during rebase." warn "Finish was aborted due to conflicts during rebase."
warn "Please finish the rebase manually now." warn "Please finish the rebase manually now."
warn "When finished, re-run:" warn "When finished, re-run:"
warn " git flow feature finish '$NAME' '$DEVELOP_BRANCH'" warn " git flow feature finish '$NAME' '$DEVELOP_BRANCH'"
exit 1 exit 1
fi fi
fi fi
# merge into BASE # merge into BASE
git_do checkout "$DEVELOP_BRANCH" git_do checkout "$DEVELOP_BRANCH"
if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then
git_do merge --ff "$BRANCH" git_do merge --ff "$BRANCH"
else else
if noflag squash; then if noflag squash; then
git_do merge --no-ff "$BRANCH" git_do merge --no-ff "$BRANCH"
else else
git_do merge --squash "$BRANCH" git_do merge --squash "$BRANCH"
git_do commit git_do commit
git_do merge "$BRANCH" git_do merge "$BRANCH"
fi fi
fi fi
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
# oops.. we have a merge conflict! # oops.. we have a merge conflict!
# write the given $DEVELOP_BRANCH to a temporary file (we need it later) # write the given $DEVELOP_BRANCH to a temporary file (we need it later)
mkdir -p "$DOT_GIT_DIR/.gitflow" 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 echo
echo "There were merge conflicts. To resolve the merge conflict manually, use:" echo "There were merge conflicts. To resolve the merge conflict manually, use:"
echo " git mergetool" echo " git mergetool"
echo " git commit" echo " git commit"
echo echo
echo "You can then complete the finish by running it again:" echo "You can then complete the finish by running it again:"
echo " git flow feature finish $NAME" echo " git flow feature finish $NAME"
echo echo
exit 1 exit 1
fi fi
TOTAL_TIME=awk '{ sum += $1 } END { print sum }' .seconds-feature-$NAME TOTAL_TIME=$(awk '{ sum += $1 } END { print sum }' .seconds-feature-$NAME)
TIME_EXPECTED=line=$(head -n 1 .timelog-feature-$NAME) TIME_EXPECTED=line=$(head -n 1 .timelog-feature-$NAME)
seconds=$TOTAL_TIME; FINALTIME=$((seconds/86400))" days "$(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds") seconds=$TOTAL_TIME; FINALTIME=$((seconds/86400))" days "$(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds")
git commit --allow-empty -m "$BRANCH was completed on $(date), total time taken:$FINALTIME expectde time was $TIME_EXPECTED" git commit --allow-empty -m "$BRANCH was completed on $(date), total time taken:$FINALTIME expectde time was $TIME_EXPECTED"
# when no merge conflict is detected, just clean up the feature branch # when no merge conflict is detected, just clean up the feature branch
helper_finish_cleanup helper_finish_cleanup
} }
helper_finish_cleanup() { helper_finish_cleanup() {
# sanity checks # sanity checks
require_branch "$BRANCH" require_branch "$BRANCH"
require_clean_working_tree require_clean_working_tree
# delete branch # delete branch
if flag fetch; then if flag fetch; then
git_do push "$ORIGIN" ":refs/heads/$BRANCH" git_do push "$ORIGIN" ":refs/heads/$BRANCH"
fi fi
if noflag keep; then if noflag keep; then
if flag force_delete; then if flag force_delete; then
git_do branch -D "$BRANCH" git_do branch -D "$BRANCH"
else else
git_do branch -d "$BRANCH" git_do branch -d "$BRANCH"
fi fi
fi fi
echo echo
echo "Summary of actions:" 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 "- Merge conflicts were resolved" # TODO: Add this line when it's supported #echo "- Merge conflicts were resolved" # TODO: Add this line when it's supported
if flag keep; then if flag keep; then
echo "- Feature branch '$BRANCH' is still available" echo "- Feature branch '$BRANCH' is still available"
else else
echo "- Feature branch '$BRANCH' has been removed" echo "- Feature branch '$BRANCH' has been removed"
fi fi
echo "- You are now on branch '$DEVELOP_BRANCH'" echo "- You are now on branch '$DEVELOP_BRANCH'"
echo echo
} }
cmd_publish() { cmd_publish() {
parse_args "$@" parse_args "$@"
expand_nameprefix_arg expand_nameprefix_arg
# sanity checks # sanity checks
require_clean_working_tree require_clean_working_tree
require_branch "$BRANCH" require_branch "$BRANCH"
git_do fetch -q "$ORIGIN" git_do fetch -q "$ORIGIN"
require_branch_absent "$ORIGIN/$BRANCH" require_branch_absent "$ORIGIN/$BRANCH"
# create remote branch # create remote branch
git_do push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH" git_do push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
git_do fetch -q "$ORIGIN" git_do fetch -q "$ORIGIN"
# configure remote tracking # configure remote tracking
git_do config "branch.$BRANCH.remote" "$ORIGIN" git_do config "branch.$BRANCH.remote" "$ORIGIN"
git_do config "branch.$BRANCH.merge" "refs/heads/$BRANCH" git_do config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
git_do checkout "$BRANCH" git_do checkout "$BRANCH"
echo echo
echo "Summary of actions:" echo "Summary of actions:"
echo "- A new remote branch '$BRANCH' was created" echo "- A new remote branch '$BRANCH' was created"
echo "- The local branch '$BRANCH' was configured to track the remote branch" echo "- The local branch '$BRANCH' was configured to track the remote branch"
echo "- You are now on branch '$BRANCH'" echo "- You are now on branch '$BRANCH'"
echo echo
} }
cmd_track() { cmd_track() {
parse_args "$@" parse_args "$@"
require_name_arg require_name_arg
# sanity checks # sanity checks
require_clean_working_tree require_clean_working_tree
require_branch_absent "$BRANCH" require_branch_absent "$BRANCH"
git_do fetch -q "$ORIGIN" git_do fetch -q "$ORIGIN"
require_branch "$ORIGIN/$BRANCH" require_branch "$ORIGIN/$BRANCH"
# create tracking branch # create tracking branch
git_do checkout -b "$BRANCH" "$ORIGIN/$BRANCH" git_do checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
echo echo
echo "Summary of actions:" echo "Summary of actions:"
echo "- A new remote tracking branch '$BRANCH' was created" echo "- A new remote tracking branch '$BRANCH' was created"
echo "- You are now on branch '$BRANCH'" echo "- You are now on branch '$BRANCH'"
echo echo
} }
cmd_diff() { cmd_diff() {
parse_args "$@" parse_args "$@"
if [ "$NAME" != "" ]; then if [ "$NAME" != "" ]; then
expand_nameprefix_arg expand_nameprefix_arg
BASE=$(git merge-base "$DEVELOP_BRANCH" "$BRANCH") BASE=$(git merge-base "$DEVELOP_BRANCH" "$BRANCH")
git diff "$BASE..$BRANCH" git diff "$BASE..$BRANCH"
else else
if ! git_current_branch | grep -q "^$PREFIX"; then if ! git_current_branch | grep -q "^$PREFIX"; then
die "Not on a feature branch. Name one explicitly." die "Not on a feature branch. Name one explicitly."
fi fi
BASE=$(git merge-base "$DEVELOP_BRANCH" HEAD) BASE=$(git merge-base "$DEVELOP_BRANCH" HEAD)
git diff "$BASE" git diff "$BASE"
fi fi
} }
cmd_checkout() { cmd_checkout() {
parse_args "$@" parse_args "$@"
if [ "$NAME" != "" ]; then if [ "$NAME" != "" ]; then
expand_nameprefix_arg expand_nameprefix_arg
git_do checkout "$BRANCH" git_do checkout "$BRANCH"
else else
die "Name a feature branch explicitly." die "Name a feature branch explicitly."
fi fi
} }
cmd_co() { cmd_co() {
# Alias for checkout # Alias for checkout
cmd_checkout "$@" cmd_checkout "$@"
} }
cmd_rebase() { cmd_rebase() {
DEFINE_boolean interactive false 'do an interactive rebase' i DEFINE_boolean interactive false 'do an interactive rebase' i
parse_args "$@" parse_args "$@"
expand_nameprefix_arg_or_current expand_nameprefix_arg_or_current
warn "Will try to rebase '$NAME'..." warn "Will try to rebase '$NAME'..."
require_clean_working_tree require_clean_working_tree
require_branch "$BRANCH" require_branch "$BRANCH"
git_do checkout -q "$BRANCH" git_do checkout -q "$BRANCH"
local OPTS= local OPTS=
if flag interactive; then if flag interactive; then
OPTS="$OPTS -i" OPTS="$OPTS -i"
fi fi
git_do rebase $OPTS "$DEVELOP_BRANCH" git_do rebase $OPTS "$DEVELOP_BRANCH"
} }
avoid_accidental_cross_branch_action() { avoid_accidental_cross_branch_action() {
local current_branch=$(git_current_branch) local current_branch=$(git_current_branch)
if [ "$BRANCH" != "$current_branch" ]; then if [ "$BRANCH" != "$current_branch" ]; then
warn "Trying to pull from '$BRANCH' while currently on branch '$current_branch'." warn "Trying to pull from '$BRANCH' while currently on branch '$current_branch'."
warn "To avoid unintended merges, git-flow aborted." warn "To avoid unintended merges, git-flow aborted."
return 1 return 1
fi fi
return 0 return 0
} }
cmd_pull() { cmd_pull() {
#DEFINE_string prefix false 'alternative remote feature branch name prefix' p #DEFINE_string prefix false 'alternative remote feature branch name prefix' p
DEFINE_boolean rebase false "pull with rebase" r DEFINE_boolean rebase false "pull with rebase" r
parse_remote_name "$@" parse_remote_name "$@"
if [ -z "$REMOTE" ]; then if [ -z "$REMOTE" ]; then
die "Name a remote explicitly." die "Name a remote explicitly."
fi fi
name_or_current name_or_current
# To avoid accidentally merging different feature branches into each other, # To avoid accidentally merging different feature branches into each other,
# die if the current feature branch differs from the requested $NAME # die if the current feature branch differs from the requested $NAME
# argument. # argument.
local current_branch=$(git_current_branch) local current_branch=$(git_current_branch)
if startswith "$current_branch" "$PREFIX"; then if startswith "$current_branch" "$PREFIX"; then
# we are on a local feature branch already, so $BRANCH must be equal to # we are on a local feature branch already, so $BRANCH must be equal to
# the current branch # the current branch
avoid_accidental_cross_branch_action || die avoid_accidental_cross_branch_action || die
fi fi
require_clean_working_tree require_clean_working_tree
if git_branch_exists "$BRANCH"; then if git_branch_exists "$BRANCH"; then
# Again, avoid accidental merges # Again, avoid accidental merges
avoid_accidental_cross_branch_action || die avoid_accidental_cross_branch_action || die
# we already have a local branch called like this, so simply pull the # we already have a local branch called like this, so simply pull the
# remote changes in # remote changes in
if flag rebase; then if flag rebase; then
if ! git_do pull --rebase -q "$REMOTE" "$BRANCH"; then if ! git_do pull --rebase -q "$REMOTE" "$BRANCH"; then
warn "Pull was aborted. There might be conflicts during rebase or '$REMOTE' might be inaccessible." warn "Pull was aborted. There might be conflicts during rebase or '$REMOTE' might be inaccessible."
exit 1 exit 1
fi fi
else else
git_do pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'." git_do pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'."
fi fi
echo "Pulled $REMOTE's changes into $BRANCH." echo "Pulled $REMOTE's changes into $BRANCH."
else else
# setup the local branch clone for the first time # setup the local branch clone for the first time
git_do fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD 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 branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed."
git_do checkout -q "$BRANCH" || die "Checking out new local branch failed." git_do checkout -q "$BRANCH" || die "Checking out new local branch failed."
echo "Created local branch $BRANCH based on $REMOTE's $BRANCH." echo "Created local branch $BRANCH based on $REMOTE's $BRANCH."
fi fi
} }
cmd_pause() { cmd_pause() {
parse_args "$@" parse_args "$@"
require_name_arg require_name_arg
# sanity checks # sanity checks
require_branch "$BRANCH" require_branch "$BRANCH"
require_branch_absent "$BRANCH-paused" require_branch_absent "$BRANCH-paused"
echo $(date +%s) >> ".timelog-feature-$NAME" echo $(date +%s) >> ".timelog-feature-$NAME"
LAST_LINE="$(wc -l < .timelog-feature-$NAME)" LAST_LINE="$(wc -l < .timelog-feature-$NAME)"
PREV_LINE=$((LAST_LINE-1)) PREV_LINE=$((LAST_LINE-1))
LAST_TIME=$(sed "${LAST_LINE}q;d" .timelog-feature-$NAME) LAST_TIME=$(sed "${LAST_LINE}q;d" .timelog-feature-$NAME)
PREV_TIME=$(sed "${PREV_LINE}q;d" .timelog-feature-$NAME) PREV_TIME=$(sed "${PREV_LINE}q;d" .timelog-feature-$NAME)
TIME_USED=$((LAST_TIME-PREV_TIME)) TIME_USED=$((LAST_TIME-PREV_TIME))
echo "$TIME_USED" >> ".seconds-feature-$NAME" echo "$TIME_USED" >> ".seconds-feature-$NAME"
seconds=$TIME_USED; TIMESTAMP=$((seconds/86400))" days "$(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds") seconds=$TIME_USED; TIMESTAMP=$((seconds/86400))" days "$(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds")
git add --all . git add --all .
git commit -m "$BRANCH/WIP" git commit -m "$BRANCH/WIP"
git commit --allow-empty -m "$BRANCH/WIP time-paused:$(date), time taken thus far:$TIMESTAMP" git commit --allow-empty -m "$BRANCH/WIP time-paused:$(date), time taken thus far:$TIMESTAMP"
git checkout --orphan "$BRANCH-paused" git checkout --orphan "$BRANCH-paused"
echo echo
echo "Summary of actions:" echo "Summary of actions:"
echo "- Created orphan branch called '$BRANCH-paused'" echo "- Created orphan branch called '$BRANCH-paused'"
echo "- You are now free to take a break, you have spent $TIMESTAMP in this session" echo "- You are now free to take a break, you have spent $TIMESTAMP in this session"
echo "" echo ""
echo "Now, have some coffee and when you are done, use:" echo "Now, have some coffee and when you are done, use:"
echo "" echo ""
echo " git flow feature resume $BRANCH" echo " git flow feature resume $BRANCH"
echo echo
} }
cmd_resume() { cmd_resume() {
parse_args "$@" parse_args "$@"
require_name_arg require_name_arg
# sanity checks # sanity checks
require_branch "$BRANCH" require_branch "$BRANCH"
git checkout "$BRANCH" git checkout "$BRANCH"
git branch -D "$BRANCH-paused" git branch -D "$BRANCH-paused"
git commit --allow-empty -m "$BRANCH/Back to Work at:$(date)" git commit --allow-empty -m "$BRANCH/Back to Work at:$(date)"
echo $(date +%s) >> ".timelog-feature-$NAME" echo $(date +%s) >> ".timelog-feature-$NAME"
echo echo
echo "Summary of actions:" echo "Summary of actions:"
echo "Switches you back to the '$BRANCH' branch" echo "Switches you back to the '$BRANCH' branch"
echo "- You are now on branch '$BRANCH'" echo "- You are now on branch '$BRANCH'"
echo "" echo ""
echo "Remmember when you need a break use:" echo "Remmember when you need a break use:"
echo "" echo ""
echo " git flow feature pause $NAME" echo " git flow feature pause $NAME"
echo echo
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment