blob: bfbd76ef8c91e08b15e504c7eb064c625ae62f39 [file] [log] [blame]
Benedikt Böhme2fa4912010-01-26 14:41:20 +01001#!/bin/sh
Benedikt Böhm00ccea62010-01-26 12:39:36 +01002#
Vincent Driessen6c2d30b2010-01-26 22:18:36 +01003# git-flow -- A collection of Git extensions to provide high-level
4# repository operations for Vincent Driessen's branching model.
Benedikt Böhm00ccea62010-01-26 12:39:36 +01005#
6# Original blog post presenting this model is found at:
7# http://nvie.com/archives/323
8#
9# Feel free to contribute to this project at:
10# http://github.com/nvie/gitflow
11#
12# Copyright (c) 2010 by Vincent Driessen
13# Copyright (c) 2010 by Benedikt Böhm
14#
15
Benedikt Böhme5eaff92010-01-26 12:44:55 +010016# enable debug mode
17if [ "$DEBUG" = "yes" ]; then
18 set -x
19fi
20
Vincent Driessen58995b52010-01-28 12:39:06 +010021export GIT_DIR=$(git rev-parse --git-dir)
Benedikt Böhm4a864fb2010-01-26 12:59:27 +010022export GITFLOW_DIR=$(dirname "$0")
23export MASTER_BRANCH=$(git config --get gitflow.branch.master || echo master)
24export DEVELOP_BRANCH=$(git config --get gitflow.branch.develop || echo develop)
Benedikt Böhm350e7152010-01-26 13:05:05 +010025export ORIGIN=$(git config --get gitflow.origin || echo origin)
Benedikt Böhm427c5db2010-01-26 18:23:44 +010026export README=$(git config --get gitflow.readme || echo README)
Benedikt Böhm4a864fb2010-01-26 12:59:27 +010027
Benedikt Böhm00ccea62010-01-26 12:39:36 +010028warn() { echo "$@" >&2; }
29die() { warn "$@"; exit 1; }
Benedikt Böhme2fa4912010-01-26 14:41:20 +010030
31has() {
32 local item=$1; shift
33 echo " $@ " | grep -q " $item "
34}
Benedikt Böhm00ccea62010-01-26 12:39:36 +010035
36usage() {
Vincent Driessen186d2b52010-01-27 23:48:39 +010037 echo "usage: git flow <subcommand>"
Benedikt Böhm00ccea62010-01-26 12:39:36 +010038 echo
Vincent Driessen186d2b52010-01-27 23:48:39 +010039 echo "Available subcommands are:"
40 echo " init Initialize a new git repo with support for the branching model."
41 echo " feature Manage your feature branches."
42 echo " release Manage your release branches."
43 echo " hotfix Manage your hotfix branches."
44 echo " support Manage your support branches."
Vincent Driessen3625f392010-01-28 00:13:26 +010045 echo " version Shows version information."
Benedikt Böhm7d703a82010-01-26 13:17:12 +010046 echo
Vincent Driessen186d2b52010-01-27 23:48:39 +010047 echo "Try 'git flow <subcommand> help' for details."
Benedikt Böhm00ccea62010-01-26 12:39:36 +010048}
49
50main() {
Benedikt Böhm427c5db2010-01-26 18:23:44 +010051 if [ $# -lt 1 ]; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +010052 usage
53 exit 1
54 fi
55
Vincent Driessenea58d0f2010-01-30 20:51:03 +010056 # use the shFlags project to parse the command line arguments
Vincent Driessenc3b7db92010-02-02 22:50:13 +010057 . "$GITFLOW_DIR/shFlags.sh"
Vincent Driessen44174922010-02-02 23:53:21 +010058 FLAGS_PARENT="git flow"
Vincent Driessenea58d0f2010-01-30 20:51:03 +010059 #DEFINE_boolean quiet 0 'run without output' q
60 #DEFINE_boolean verbose 0 'run verbose (more output)' v
61 FLAGS "$@" || exit $?
62 eval set -- "${FLAGS_ARGV}"
63
Benedikt Böhm00ccea62010-01-26 12:39:36 +010064 # sanity checks
Vincent Driessen186d2b52010-01-27 23:48:39 +010065 SUBCOMMAND="$1"; shift
Benedikt Böhm427c5db2010-01-26 18:23:44 +010066
Vincent Driessen186d2b52010-01-27 23:48:39 +010067 if [ ! -e "$GITFLOW_DIR/git-flow-$SUBCOMMAND" ]; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +010068 usage
69 exit 1
70 fi
71
Vincent Driessen2ba9a4d2010-01-27 12:51:07 +010072 if ! git rev-parse --git-dir >/dev/null; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +010073 die "Not a git repository"
74 fi
75
76 # get all available branches
77 LOCAL_BRANCHES=$(git branch | sed 's/^[* ] //')
78 REMOTE_BRANCHES=$(git branch -r | sed 's/^[* ] //')
79 ALL_BRANCHES="$LOCAL_BRANCHES $REMOTE_BRANCHES"
80
81 # run command
Vincent Driessen186d2b52010-01-27 23:48:39 +010082 . "$GITFLOW_DIR/git-flow-$SUBCOMMAND"
Vincent Driessen44174922010-02-02 23:53:21 +010083 FLAGS_PARENT="git flow $SUBCOMMAND"
Benedikt Böhm00ccea62010-01-26 12:39:36 +010084
Vincent Driessen1b819232010-02-01 15:51:43 +010085 # test if the first argument is a flag (i.e. starts with '-')
86 # in that case, we interpret this arg as a flag for the default
87 # command
88 SUBACTION="default"
89 if [ "$1" != "" ] && ! echo "$1" | grep -q "^-"; then
90 SUBACTION="$1"; shift
91 fi
Vincent Driessene1684872010-02-02 06:33:53 +010092 if ! typeset -f "cmd_$SUBACTION" 2>&1 >/dev/null; then
Vincent Driessen186d2b52010-01-27 23:48:39 +010093 warn "Unknown subcommand: '$1'"
94 usage
95 exit 1
Benedikt Böhm427c5db2010-01-26 18:23:44 +010096 fi
97
Vincent Driessen186d2b52010-01-27 23:48:39 +010098 # run the specified action
99 cmd_$SUBACTION "$@"
Benedikt Böhm427c5db2010-01-26 18:23:44 +0100100}
101
Vincent Driessen62345d52010-01-29 10:27:01 +0100102gitflow_test_clean_working_tree() {
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100103 if ! git diff --no-ext-diff --ignore-submodules --quiet --exit-code; then
Vincent Driessen62345d52010-01-29 10:27:01 +0100104 return 1
105 elif ! git diff-index --cached --quiet --ignore-submodules HEAD --; then
106 return 2
107 else
108 return 0
109 fi
110}
111
112gitflow_require_clean_working_tree() {
113 gitflow_test_clean_working_tree
114 result=$?
115 if [ $result -eq 1 ]; then
Vincent Driessen4c92a9d2010-02-02 09:42:51 +0100116 die "fatal: Working tree contains unstaged changes. Aborting."
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100117 fi
Vincent Driessen62345d52010-01-29 10:27:01 +0100118 if [ $result -eq 2 ]; then
Vincent Driessen4c92a9d2010-02-02 09:42:51 +0100119 die "fatal: Index contains uncommited changes. Aborting."
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100120 fi
121}
122
123gitflow_require_local_branch() {
124 if ! has $1 $LOCAL_BRANCHES; then
Vincent Driessen4c92a9d2010-02-02 09:42:51 +0100125 die "fatal: Local branch '$1' does not exist and is required."
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100126 fi
127}
128
129gitflow_require_remote_branch() {
130 if ! has $1 $REMOTE_BRANCHES; then
131 die "Remote branch '$1' does not exist and is required."
132 fi
133}
134
135gitflow_require_branch() {
136 if ! has $1 $ALL_BRANCHES; then
137 die "Branch '$1' does not exist and is required."
138 fi
139}
140
141gitflow_require_branch_absent() {
142 if has $1 $ALL_BRANCHES; then
143 die "Branch '$1' already exists. Pick another name."
144 fi
145}
146
147#
148# gitflow_test_branches_equal()
149#
150# Tests whether branches and their "origin" counterparts have diverged and need
151# merging first. It returns error codes to provide more detail, like so:
152#
153# 0 Branch heads point to the same commit
154# 1 First given branch needs fast-forwarding
155# 2 Second given branch needs fast-forwarding
156# 3 Branch needs a real merge
157#
158gitflow_test_branches_equal() {
159 commit1=$(git rev-parse "$1")
160 commit2=$(git rev-parse "$2")
161 if [ "$commit1" != "$commit2" ]; then
162 base=$(git merge-base "$commit1" "$commit2")
163 if [ "$commit1" = "$base" ]; then
164 return 1
165 elif [ "$commit2" = "$base" ]; then
166 return 2
167 else
168 return 3
169 fi
170 else
171 return 0
172 fi
173}
174
175gitflow_require_branches_equal() {
176 gitflow_require_local_branch "$1"
177 gitflow_require_remote_branch "$2"
178 gitflow_test_branches_equal "$1" "$2"
179 status=$?
180 if [ $status -gt 0 ]; then
181 warn "Branches '$1' and '$2' have diverged."
182 if [ $status -eq 1 ]; then
183 die "And branch '$1' may be fast-forwarded."
184 elif [ $status -eq 2 ]; then
185 # Warn here, since there is no harm in being ahead
186 warn "And local branch '$1' is ahead of '$2'."
187 else
188 die "Branches need merging first."
189 fi
190 fi
191}
192
Vincent Driessen58995b52010-01-28 12:39:06 +0100193#
194# gitflow_is_branch_merged_into()
195#
196# Checks whether branch $1 is succesfully merged into $2
197#
198gitflow_is_branch_merged_into() {
199 local SUBJECT=$1
200 local BASE=$2
201 ALL_MERGES=$(git branch --contains $SUBJECT | sed 's/^[* ] //')
202 has $BASE $ALL_MERGES
203}
204
Vincent Driessen3c337fb2010-02-04 11:30:18 +0100205# helper functions for common reuse
206max() { if [ "$1" -gt "$2" ]; then echo "$1"; else echo "$2"; fi; }
207
Vincent Driessen44174922010-02-02 23:53:21 +0100208# convenience functions for checking whether flags have been set or not
209flag() { eval FLAG=\$FLAGS_$1; [ $FLAG -eq $FLAGS_TRUE ]; }
210noflag() { eval FLAG=\$FLAGS_$1; [ $FLAG -ne $FLAGS_TRUE ]; }
211
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100212main "$@"