blob: 9702818660a95906e4e33dbe9a5fac3487683b32 [file] [log] [blame]
Vincent Driessenc3607ac2010-02-05 19:53:45 +01001#
2# git-flow -- A collection of Git extensions to provide high-level
3# repository operations for Vincent Driessen's branching model.
4#
5# Original blog post presenting this model is found at:
6# http://nvie.com/archives/323
7#
8# Feel free to contribute to this project at:
9# http://github.com/nvie/gitflow
10#
11# Copyright (c) 2010 by Vincent Driessen
12# Copyright (c) 2010 by Benedikt Böhm
13#
14
15# shell output
16warn() { echo "$@" >&2; }
17die() { warn "$@"; exit 1; }
18
19# set logic
20has() {
Vincent Driessenf46e2902010-02-15 23:01:52 +010021 local item=$1; shift
Vincent Driessenc3607ac2010-02-05 19:53:45 +010022 echo " $@ " | grep -q " $item "
23}
24
25# basic math
26min() { [ "$1" -le "$2" ] && echo "$1" || echo "$2"; }
27max() { [ "$1" -ge "$2" ] && echo "$1" || echo "$2"; }
28
29# basic string matching
30startswith() { [ "$1" != "${1#$2}" ]; }
31endswith() { [ "$1" != "${1#$2}" ]; }
32
33# convenience functions for checking shFlags flags
Vincent Driessenf46e2902010-02-15 23:01:52 +010034flag() { local FLAG; eval FLAG='$FLAGS_'$1; [ $FLAG -eq $FLAGS_TRUE ]; }
35noflag() { local FLAG; eval FLAG='$FLAGS_'$1; [ $FLAG -ne $FLAGS_TRUE ]; }
Vincent Driessenc3607ac2010-02-05 19:53:45 +010036
37#
38# Git specific common functionality
39#
40
Vincent Driessenb25ab832010-02-20 11:21:23 +010041# check if this repo has been inited for gitflow
42gitflow_has_master_configured() {
43 local master=$(git config --get gitflow.branch.master)
44 [ "$master" != "" ] && gitflow_local_branch_exists "$master"
45}
46
47gitflow_has_develop_configured() {
48 local develop=$(git config --get gitflow.branch.develop)
49 [ "$develop" != "" ] && gitflow_local_branch_exists "$develop"
50}
51
Vincent Driessen1d8bb0d2010-02-20 16:46:38 +010052gitflow_has_prefixes_configured() {
53 git config --get gitflow.prefix.feature >/dev/null 2>&1 && \
54 git config --get gitflow.prefix.release >/dev/null 2>&1 && \
55 git config --get gitflow.prefix.hotfix >/dev/null 2>&1 && \
56 git config --get gitflow.prefix.support >/dev/null 2>&1 && \
57 git config --get gitflow.prefix.versiontag >/dev/null 2>&1
58}
59
Vincent Driessenb25ab832010-02-20 11:21:23 +010060gitflow_is_initialized() {
Vincent Driessen1d8bb0d2010-02-20 16:46:38 +010061 gitflow_has_master_configured && \
62 gitflow_has_develop_configured && \
63 [ "$(git config --get gitflow.branch.master)" != \
64 "$(git config --get gitflow.branch.develop)" ] && \
65 gitflow_has_prefixes_configured
Vincent Driessenb25ab832010-02-20 11:21:23 +010066}
67
Vincent Driessenc3607ac2010-02-05 19:53:45 +010068# get all available branches
Vincent Driessen21c7aa92010-02-16 20:57:35 +010069gitflow_local_branches() { git branch | sed 's/^[* ] //'; }
70gitflow_remote_branches() { git branch -r | sed 's/^[* ] //'; }
Vincent Driessen4e11dd62010-02-19 19:48:23 +010071gitflow_all_branches() { ( git branch; git branch -r) | sed 's/^[* ] //'; }
Vincent Driessen21c7aa92010-02-16 20:57:35 +010072gitflow_all_tags() { git tag; }
Vincent Driessenc3607ac2010-02-05 19:53:45 +010073
Vincent Driessend72e4ac2010-02-16 21:33:51 +010074# loading settings that can be overridden using git config
75gitflow_load_settings() {
Vincent Driessencf6e92a2010-02-19 19:44:48 +010076 export DOT_GIT_DIR=$(git rev-parse --git-dir >/dev/null 2>&1)
Vincent Driessenc1598bf2010-02-20 16:52:48 +010077 export MASTER_BRANCH=$(git config --get gitflow.branch.master)
78 export DEVELOP_BRANCH=$(git config --get gitflow.branch.develop)
Vincent Driessend72e4ac2010-02-16 21:33:51 +010079 export ORIGIN=$(git config --get gitflow.origin || echo origin)
Vincent Driessend72e4ac2010-02-16 21:33:51 +010080}
81
Vincent Driessend0991262010-02-06 21:19:07 +010082#
83# resolve_nameprefix
84#
85# Inputs:
86# $1 = name prefix to resolve
87# $2 = branch prefix to use
88#
Vincent Driessen21c7aa92010-02-16 20:57:35 +010089# Searches branch names from gitflow_local_branches() to look for a unique
90# branch name whose name starts with the given name prefix.
Vincent Driessend0991262010-02-06 21:19:07 +010091#
92# There are multiple exit codes possible:
93# 0: The unambiguous full name of the branch is written to stdout
94# (success)
95# 1: No match is found.
96# 2: Multiple matches found. These matches are written to stderr
97#
98resolve_nameprefix() {
Vincent Driessenf46e2902010-02-15 23:01:52 +010099 local name=$1
100 local prefix=$2
101 local matches
102 local num_matches
Vincent Driessend0991262010-02-06 21:19:07 +0100103
104 # first, check if there is a perfect match
Vincent Driessen21c7aa92010-02-16 20:57:35 +0100105 if has "$(gitflow_local_branches)" "$prefix$name"; then
Vincent Driessend0991262010-02-06 21:19:07 +0100106 echo "$name"
107 return 0
108 fi
109
Vincent Driessen21c7aa92010-02-16 20:57:35 +0100110 matches=$(echo "$(gitflow_local_branches)" | grep "^$prefix$name")
Vincent Driessend0991262010-02-06 21:19:07 +0100111 num_matches=$(echo "$matches" | wc -l)
112 if [ -z "$matches" ]; then
113 # no prefix match, so take it literally
114 warn "No branch matches prefix '$name'"
115 return 1
116 else
117 if [ $num_matches -eq 1 ]; then
118 echo "${matches#$prefix}"
119 return 0
120 else
121 # multiple matches, cannot decide
122 warn "Multiple branches match prefix '$name':"
123 for match in $matches; do
124 warn "- $match"
125 done
126 return 2
127 fi
128 fi
129}
130
131gitflow_current_branch() {
132 git branch | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g'
133}
134
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100135gitflow_test_clean_working_tree() {
136 if ! git diff --no-ext-diff --ignore-submodules --quiet --exit-code; then
137 return 1
138 elif ! git diff-index --cached --quiet --ignore-submodules HEAD --; then
139 return 2
140 else
141 return 0
142 fi
143}
144
Vincent Driessend72e4ac2010-02-16 21:33:51 +0100145gitflow_require_git_repo() {
146 if ! git rev-parse --git-dir >/dev/null 2>&1; then
Vincent Driessenc1598bf2010-02-20 16:52:48 +0100147 die "fatal: Not a git repository"
148 fi
149}
150
151gitflow_require_initialized() {
152 if ! gitflow_is_initialized; then
153 die "fatal: Not a gitflow-enabled repo yet. Please run \"git flow init\" first."
Vincent Driessend72e4ac2010-02-16 21:33:51 +0100154 fi
155}
156
Vincent Driessenef43cbd2010-02-21 11:20:05 +0100157git_repo_is_headless() {
158 ! git rev-parse --quiet --verify HEAD >/dev/null 2>&1
159}
160
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100161gitflow_require_clean_working_tree() {
162 gitflow_test_clean_working_tree
Vincent Driessenf46e2902010-02-15 23:01:52 +0100163 local result=$?
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100164 if [ $result -eq 1 ]; then
165 die "fatal: Working tree contains unstaged changes. Aborting."
166 fi
167 if [ $result -eq 2 ]; then
168 die "fatal: Index contains uncommited changes. Aborting."
169 fi
170}
171
Vincent Driessenb25ab832010-02-20 11:21:23 +0100172gitflow_local_branch_exists() {
173 has $1 $(gitflow_local_branches)
174}
175
Vincent Driessen49094bd2010-02-18 12:05:01 +0100176gitflow_branch_exists() {
177 has $1 $(gitflow_all_branches)
178}
179
180gitflow_tag_exists() {
181 has $1 $(gitflow_all_tags)
182}
183
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100184gitflow_require_local_branch() {
Vincent Driessenb25ab832010-02-20 11:21:23 +0100185 if ! gitflow_local_branch_exists; then
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100186 die "fatal: Local branch '$1' does not exist and is required."
187 fi
188}
189
190gitflow_require_remote_branch() {
Vincent Driessen21c7aa92010-02-16 20:57:35 +0100191 if ! has $1 $(gitflow_remote_branches); then
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100192 die "Remote branch '$1' does not exist and is required."
193 fi
194}
195
196gitflow_require_branch() {
Vincent Driessen21c7aa92010-02-16 20:57:35 +0100197 if ! has $1 $(gitflow_all_branches); then
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100198 die "Branch '$1' does not exist and is required."
199 fi
200}
201
202gitflow_require_branch_absent() {
Vincent Driessen21c7aa92010-02-16 20:57:35 +0100203 if has $1 $(gitflow_all_branches); then
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100204 die "Branch '$1' already exists. Pick another name."
205 fi
206}
207
Vincent Driessen1a2868b2010-02-07 23:23:16 +0100208gitflow_require_tag_absent() {
Vincent Driessen21c7aa92010-02-16 20:57:35 +0100209 if has $1 $(gitflow_all_tags); then
Vincent Driessen1a2868b2010-02-07 23:23:16 +0100210 die "Tag '$1' already exists. Pick another name."
211 fi
212}
213
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100214#
215# gitflow_test_branches_equal()
216#
217# Tests whether branches and their "origin" counterparts have diverged and need
218# merging first. It returns error codes to provide more detail, like so:
219#
220# 0 Branch heads point to the same commit
221# 1 First given branch needs fast-forwarding
222# 2 Second given branch needs fast-forwarding
223# 3 Branch needs a real merge
Vincent Driessen49094bd2010-02-18 12:05:01 +0100224# 4 There is no merge base, i.e. the branches have no common ancestors
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100225#
226gitflow_test_branches_equal() {
Vincent Driessenf46e2902010-02-15 23:01:52 +0100227 local commit1=$(git rev-parse "$1")
228 local commit2=$(git rev-parse "$2")
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100229 if [ "$commit1" != "$commit2" ]; then
Vincent Driessenf46e2902010-02-15 23:01:52 +0100230 local base=$(git merge-base "$commit1" "$commit2")
Vincent Driessen49094bd2010-02-18 12:05:01 +0100231 if [ $? -ne 0 ]; then
232 return 4
233 elif [ "$commit1" = "$base" ]; then
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100234 return 1
235 elif [ "$commit2" = "$base" ]; then
236 return 2
237 else
238 return 3
239 fi
240 else
241 return 0
242 fi
243}
244
245gitflow_require_branches_equal() {
246 gitflow_require_local_branch "$1"
247 gitflow_require_remote_branch "$2"
248 gitflow_test_branches_equal "$1" "$2"
Vincent Driessenf46e2902010-02-15 23:01:52 +0100249 local status=$?
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100250 if [ $status -gt 0 ]; then
251 warn "Branches '$1' and '$2' have diverged."
252 if [ $status -eq 1 ]; then
253 die "And branch '$1' may be fast-forwarded."
254 elif [ $status -eq 2 ]; then
255 # Warn here, since there is no harm in being ahead
256 warn "And local branch '$1' is ahead of '$2'."
257 else
258 die "Branches need merging first."
259 fi
260 fi
261}
262
263#
264# gitflow_is_branch_merged_into()
265#
266# Checks whether branch $1 is succesfully merged into $2
267#
268gitflow_is_branch_merged_into() {
Vincent Driessenf46e2902010-02-15 23:01:52 +0100269 local subject=$1
270 local base=$2
271 local all_merges=$(git branch --contains $subject | sed 's/^[* ] //')
Vincent Driessen27592dd2010-02-06 14:45:39 +0100272 has $base $all_merges
Vincent Driessenc3607ac2010-02-05 19:53:45 +0100273}