blob: 303e8a7be61900d90c89c1ea65c36698b75c8c3d [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
57 . "$GITFLOW_DIR/shFlags/src/shFlags"
58 #DEFINE_boolean quiet 0 'run without output' q
59 #DEFINE_boolean verbose 0 'run verbose (more output)' v
60 FLAGS "$@" || exit $?
61 eval set -- "${FLAGS_ARGV}"
62
Benedikt Böhm00ccea62010-01-26 12:39:36 +010063 # sanity checks
Vincent Driessen186d2b52010-01-27 23:48:39 +010064 SUBCOMMAND="$1"; shift
Benedikt Böhm427c5db2010-01-26 18:23:44 +010065
Vincent Driessen186d2b52010-01-27 23:48:39 +010066 if [ ! -e "$GITFLOW_DIR/git-flow-$SUBCOMMAND" ]; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +010067 usage
68 exit 1
69 fi
70
Vincent Driessen2ba9a4d2010-01-27 12:51:07 +010071 if ! git rev-parse --git-dir >/dev/null; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +010072 die "Not a git repository"
73 fi
74
75 # get all available branches
76 LOCAL_BRANCHES=$(git branch | sed 's/^[* ] //')
77 REMOTE_BRANCHES=$(git branch -r | sed 's/^[* ] //')
78 ALL_BRANCHES="$LOCAL_BRANCHES $REMOTE_BRANCHES"
79
80 # run command
Vincent Driessen186d2b52010-01-27 23:48:39 +010081 . "$GITFLOW_DIR/git-flow-$SUBCOMMAND"
Benedikt Böhm00ccea62010-01-26 12:39:36 +010082
Vincent Driessen1b819232010-02-01 15:51:43 +010083 # test if the first argument is a flag (i.e. starts with '-')
84 # in that case, we interpret this arg as a flag for the default
85 # command
86 SUBACTION="default"
87 if [ "$1" != "" ] && ! echo "$1" | grep -q "^-"; then
88 SUBACTION="$1"; shift
89 fi
Vincent Driessen186d2b52010-01-27 23:48:39 +010090 if ! typeset -f cmd_$SUBACTION 2>&1 >/dev/null; then
91 warn "Unknown subcommand: '$1'"
92 usage
93 exit 1
Benedikt Böhm427c5db2010-01-26 18:23:44 +010094 fi
95
Vincent Driessen186d2b52010-01-27 23:48:39 +010096 # run the specified action
97 cmd_$SUBACTION "$@"
Benedikt Böhm427c5db2010-01-26 18:23:44 +010098}
99
Vincent Driessen62345d52010-01-29 10:27:01 +0100100gitflow_test_clean_working_tree() {
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100101 if ! git diff --no-ext-diff --ignore-submodules --quiet --exit-code; then
Vincent Driessen62345d52010-01-29 10:27:01 +0100102 return 1
103 elif ! git diff-index --cached --quiet --ignore-submodules HEAD --; then
104 return 2
105 else
106 return 0
107 fi
108}
109
110gitflow_require_clean_working_tree() {
111 gitflow_test_clean_working_tree
112 result=$?
113 if [ $result -eq 1 ]; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100114 die "Working tree contains unstaged changes. Aborting ..."
115 fi
Vincent Driessen62345d52010-01-29 10:27:01 +0100116 if [ $result -eq 2 ]; then
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100117 die "Index contains uncommited changes. Aborting ..."
118 fi
119}
120
121gitflow_require_local_branch() {
122 if ! has $1 $LOCAL_BRANCHES; then
123 die "Local branch '$1' does not exist and is required."
124 fi
125}
126
127gitflow_require_remote_branch() {
128 if ! has $1 $REMOTE_BRANCHES; then
129 die "Remote branch '$1' does not exist and is required."
130 fi
131}
132
133gitflow_require_branch() {
134 if ! has $1 $ALL_BRANCHES; then
135 die "Branch '$1' does not exist and is required."
136 fi
137}
138
139gitflow_require_branch_absent() {
140 if has $1 $ALL_BRANCHES; then
141 die "Branch '$1' already exists. Pick another name."
142 fi
143}
144
145#
146# gitflow_test_branches_equal()
147#
148# Tests whether branches and their "origin" counterparts have diverged and need
149# merging first. It returns error codes to provide more detail, like so:
150#
151# 0 Branch heads point to the same commit
152# 1 First given branch needs fast-forwarding
153# 2 Second given branch needs fast-forwarding
154# 3 Branch needs a real merge
155#
156gitflow_test_branches_equal() {
157 commit1=$(git rev-parse "$1")
158 commit2=$(git rev-parse "$2")
159 if [ "$commit1" != "$commit2" ]; then
160 base=$(git merge-base "$commit1" "$commit2")
161 if [ "$commit1" = "$base" ]; then
162 return 1
163 elif [ "$commit2" = "$base" ]; then
164 return 2
165 else
166 return 3
167 fi
168 else
169 return 0
170 fi
171}
172
173gitflow_require_branches_equal() {
174 gitflow_require_local_branch "$1"
175 gitflow_require_remote_branch "$2"
176 gitflow_test_branches_equal "$1" "$2"
177 status=$?
178 if [ $status -gt 0 ]; then
179 warn "Branches '$1' and '$2' have diverged."
180 if [ $status -eq 1 ]; then
181 die "And branch '$1' may be fast-forwarded."
182 elif [ $status -eq 2 ]; then
183 # Warn here, since there is no harm in being ahead
184 warn "And local branch '$1' is ahead of '$2'."
185 else
186 die "Branches need merging first."
187 fi
188 fi
189}
190
Vincent Driessen58995b52010-01-28 12:39:06 +0100191#
192# gitflow_is_branch_merged_into()
193#
194# Checks whether branch $1 is succesfully merged into $2
195#
196gitflow_is_branch_merged_into() {
197 local SUBJECT=$1
198 local BASE=$2
199 ALL_MERGES=$(git branch --contains $SUBJECT | sed 's/^[* ] //')
200 has $BASE $ALL_MERGES
201}
202
Benedikt Böhm00ccea62010-01-26 12:39:36 +0100203main "$@"