blob: 0cc6802d10040ebd58d344523531467494154d6e [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() {
21 local item=$1; shift
22 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
34flag() { eval FLAG='$FLAGS_'$1; [ $FLAG -eq $FLAGS_TRUE ]; }
35noflag() { eval FLAG='$FLAGS_'$1; [ $FLAG -ne $FLAGS_TRUE ]; }
36
37#
38# Git specific common functionality
39#
40
41# get all available branches
42LOCAL_BRANCHES=$(git branch | sed 's/^[* ] //')
43REMOTE_BRANCHES=$(git branch -r | sed 's/^[* ] //')
44ALL_BRANCHES="$LOCAL_BRANCHES $REMOTE_BRANCHES"
45
46gitflow_test_clean_working_tree() {
47 if ! git diff --no-ext-diff --ignore-submodules --quiet --exit-code; then
48 return 1
49 elif ! git diff-index --cached --quiet --ignore-submodules HEAD --; then
50 return 2
51 else
52 return 0
53 fi
54}
55
56gitflow_require_clean_working_tree() {
57 gitflow_test_clean_working_tree
58 result=$?
59 if [ $result -eq 1 ]; then
60 die "fatal: Working tree contains unstaged changes. Aborting."
61 fi
62 if [ $result -eq 2 ]; then
63 die "fatal: Index contains uncommited changes. Aborting."
64 fi
65}
66
67gitflow_require_local_branch() {
68 if ! has $1 $LOCAL_BRANCHES; then
69 die "fatal: Local branch '$1' does not exist and is required."
70 fi
71}
72
73gitflow_require_remote_branch() {
74 if ! has $1 $REMOTE_BRANCHES; then
75 die "Remote branch '$1' does not exist and is required."
76 fi
77}
78
79gitflow_require_branch() {
80 if ! has $1 $ALL_BRANCHES; then
81 die "Branch '$1' does not exist and is required."
82 fi
83}
84
85gitflow_require_branch_absent() {
86 if has $1 $ALL_BRANCHES; then
87 die "Branch '$1' already exists. Pick another name."
88 fi
89}
90
91#
92# gitflow_test_branches_equal()
93#
94# Tests whether branches and their "origin" counterparts have diverged and need
95# merging first. It returns error codes to provide more detail, like so:
96#
97# 0 Branch heads point to the same commit
98# 1 First given branch needs fast-forwarding
99# 2 Second given branch needs fast-forwarding
100# 3 Branch needs a real merge
101#
102gitflow_test_branches_equal() {
103 commit1=$(git rev-parse "$1")
104 commit2=$(git rev-parse "$2")
105 if [ "$commit1" != "$commit2" ]; then
106 base=$(git merge-base "$commit1" "$commit2")
107 if [ "$commit1" = "$base" ]; then
108 return 1
109 elif [ "$commit2" = "$base" ]; then
110 return 2
111 else
112 return 3
113 fi
114 else
115 return 0
116 fi
117}
118
119gitflow_require_branches_equal() {
120 gitflow_require_local_branch "$1"
121 gitflow_require_remote_branch "$2"
122 gitflow_test_branches_equal "$1" "$2"
123 status=$?
124 if [ $status -gt 0 ]; then
125 warn "Branches '$1' and '$2' have diverged."
126 if [ $status -eq 1 ]; then
127 die "And branch '$1' may be fast-forwarded."
128 elif [ $status -eq 2 ]; then
129 # Warn here, since there is no harm in being ahead
130 warn "And local branch '$1' is ahead of '$2'."
131 else
132 die "Branches need merging first."
133 fi
134 fi
135}
136
137#
138# gitflow_is_branch_merged_into()
139#
140# Checks whether branch $1 is succesfully merged into $2
141#
142gitflow_is_branch_merged_into() {
143 local SUBJECT=$1
144 local BASE=$2
145 ALL_MERGES=$(git branch --contains $SUBJECT | sed 's/^[* ] //')
146 has $BASE $ALL_MERGES
147}
148