├── Assembly Versioning TeamCity Metarunner.xml
├── Configure-GitFlow.ps1
├── Dependencies
├── getopt.exe
└── libintl3.dll
├── GitFlowExtensions
├── .git-flow-release.swp
├── git-flow
├── git-flow-feature
├── git-flow-hotfix
├── git-flow-init
├── git-flow-release
├── git-flow-support
├── git-flow-version
├── gitflow-common
└── gitflow-shFlags
├── One-Page GitFlow-Cheatsheet.pdf
└── README.md
/Assembly Versioning TeamCity Metarunner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Set the version of all assemblies to the value of the Git tag (for master builds) or branch name (for Release/Hotfix builds.)
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | $TmpFile
28 |
29 | move-item $TmpFile $o.FullName -force
30 | }
31 | }
32 |
33 | function GetNextIncrement($buildId, $release)
34 | {
35 | $ht = @{}
36 | $increment = 0;
37 | $key = $buildId+$release;
38 |
39 | $autoincPath = "%autoinc.path%"
40 | Write-Host "Using versions file at $autoincPath"
41 |
42 | # if the autoinc file exists, deserialize the data into a hashtable.
43 | if(Test-Path -Path $autoincPath)
44 | {
45 | $json = Get-Content $autoincPath | ConvertFrom-Json
46 | $json.psobject.properties | Foreach { $ht[$_.Name] = $_.Value }
47 | }
48 |
49 | # if there is an existing increment for this $key, then fetch it.
50 | if ($ht.ContainsKey($key))
51 | {
52 | $increment = $ht.Get_Item($key)
53 | Write-Host "Last increment of $key is $increment"
54 | }
55 |
56 | # increment the increment.
57 | $increment = 1 + $increment;
58 | Write-Host "New increment of $key is $increment"
59 |
60 | # if the autoinc file exists, persist the hashtable back to disk.
61 | if(Test-Path -Path $autoincPath)
62 | {
63 | # update the hashtable...
64 | $ht.Set_Item($key, $increment);
65 | # ...and write it to disk.
66 | ConvertTo-Json $ht -Compress | Set-Content $autoincPath
67 | }
68 |
69 | return $increment;
70 | }
71 |
72 | $version = "%build.number%"
73 | $branch = "%teamcity.build.branch%"
74 |
75 | $beta = 0;
76 | $suffix = "";
77 |
78 |
79 | if ($branch -match "[\d]+.[\d]+(.[\d]+)?" -or $branch -match "master")
80 | {
81 | ### master
82 | if ($branch -match "master")
83 | {
84 | $version = git describe --abbrev=0
85 | $patchBase = 200;
86 | }
87 | ### release / hotfix
88 | else
89 | {
90 | $version = $branch
91 | $patchBase = 100;
92 | $beta = GetNextIncrement "%system.teamcity.buildType.id%" "$branch"
93 | $suffix = "-beta$beta"
94 | }
95 |
96 | $version = $version.Trim().TrimStart("v");
97 | $parts = $version.Split(".")
98 | $major = $parts[0]
99 | $minor = $parts[1]
100 | $patch = @{$true=$parts[2];$false=0}[$parts.Length -eq 3]
101 | $patchFull = $patchBase + 10*$patch + $beta
102 |
103 | $fileVersion = "$major.$minor.$patchFull"
104 | $infoVersion = "$major.$minor.$patch$suffix"
105 | }
106 | ### develop / feature branch
107 | else
108 | {
109 | $fileVersion = $version;
110 | $infoVersion = $version
111 | }
112 |
113 | Write-Host "Updating versions to $version / $fileVersion / $infoVersion..."
114 | Write-Host "##teamcity[buildNumber '$infoVersion']"
115 |
116 | foreach ($file in "AssemblyInfo.cs", "AssemblyInfo.vb" )
117 | {
118 | get-childitem -recurse |? {$_.Name -eq $file} | Update-SourceVersion $version $fileVersion $infoVersion;
119 | }
120 |
121 | Write-Host "Done"]]>
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/Configure-GitFlow.ps1:
--------------------------------------------------------------------------------
1 | param
2 | (
3 | [Alias("Silent")]
4 | [switch] $SilentInstallation
5 | )
6 |
7 | <#
8 | .SYNOPSIS
9 | Adds PowerShell GitFlow extensions to GitHub for Windows
10 | .DESCRIPTION
11 | This script adds support for PowerShell GitFlow to GitHub for Windows. The GitFlow scripts were developed
12 | by nvie (https://github.com/nvie/gitflow). This script is itself a modified version of a script developed
13 | by Howard van Rooijen and documented in a fantastic series of blog posts about using GitHub with TeamCity:
14 | http://blogs.endjin.com/2013/03/a-step-by-step-guide-to-using-gitflow-with-teamcity-part-1-different-branching-models/
15 | .PARAMETER SilentInstallation
16 | Optional installation with no prompts to the user.
17 |
18 | .NOTES
19 | Author : John Hoerr - jhoerr@gmail.com
20 | .LINK
21 | http://github.com/jhoerr/posh-gitflow
22 | #>
23 | Function Configure-GitFlow
24 | {
25 | $currentPath = Split-Path ${function:Configure-GitFlow}.File -Parent
26 |
27 | # Find all 'PortableGit*' folders in the GitHub for Windows application folder
28 | $gitHubPath = "C:\Users\$env:username\AppData\Local\GitHub"
29 | if (Test-Path $gitHubPath) {
30 |
31 | $portableGitFolders = Get-ChildItem $gitHubPath | ?{ $_.PSIsContainer } | ?{$_.Name -like 'PortableGit*'}
32 |
33 | # Provision the PoSH GitFlow scripts for each instance of PortableGit
34 | foreach ($portableGitFolder in $portableGitFolders){
35 | $gitPath = (Join-Path (Join-Path $gitHubPath $portableGitFolder.Name) "bin")
36 | Write-Host "Installing extensions to Git binaries in '$gitPath'" -ForegroundColor Green
37 |
38 | Write-Host "Copying Required supporting binaries"
39 | Copy-Item (Join-Path $currentPath "Dependencies\*.*") -Destination $gitPath -Verbose
40 |
41 | Write-Host "Copying GitFlow extensions"
42 | Copy-Item (Join-Path $currentPath "GitFlowExtensions\**") -Destination $gitPath -Verbose
43 |
44 | Write-Host "Copying Git components"
45 | Copy-Item (Join-Path (Join-Path (Join-Path (Join-Path $gitHubPath $portableGitFolder.Name) "mingw32") "bin") "libiconv-2.dll") -Destination (Join-Path (Join-Path (Join-Path (Join-Path $gitHubPath $portableGitFolder.Name) "mingw32") "bin") "libiconv2.dll") -Verbose
46 | }
47 | }
48 |
49 | $gitPath = "C:\Users\$env:username\AppData\Local\Programs\Git"
50 | if (Test-Path $gitPath) {
51 | $gitBinPath = (Join-Path $gitPath "bin")
52 | $gitMingW64BinPath = (Join-Path (Join-Path $gitPath "mingw64") "bin")
53 |
54 | Write-Host "Installing extensions to Git binaries in '$gitBinPath'" -ForegroundColor Green
55 |
56 | Write-Host "Copying Required supporting binaries"
57 | Copy-Item (Join-Path $currentPath "Dependencies\*.*") -Destination $gitBinPath -Verbose
58 |
59 | Write-Host "Copying GitFlow extensions"
60 | Copy-Item (Join-Path $currentPath "GitFlowExtensions\**") -Destination $gitBinPath -Verbose
61 |
62 | Write-Host "Copying Git components"
63 | Copy-Item (Join-Path $gitMingW64BinPath "libiconv-2.dll") -Destination (Join-Path $gitBinPath "libiconv2.dll") -Verbose
64 | }
65 |
66 | if(!$SilentInstallation)
67 | {
68 | Write-Host "Press Enter to finish"
69 | Read-Host
70 | }
71 | }
72 |
73 | Configure-GitFlow
74 |
--------------------------------------------------------------------------------
/Dependencies/getopt.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhoerr/posh-gitflow/d21bb6f66cbb05b9b83e03a4a422d3fb0a208194/Dependencies/getopt.exe
--------------------------------------------------------------------------------
/Dependencies/libintl3.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhoerr/posh-gitflow/d21bb6f66cbb05b9b83e03a4a422d3fb0a208194/Dependencies/libintl3.dll
--------------------------------------------------------------------------------
/GitFlowExtensions/.git-flow-release.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhoerr/posh-gitflow/d21bb6f66cbb05b9b83e03a4a422d3fb0a208194/GitFlowExtensions/.git-flow-release.swp
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # git-flow -- A collection of Git extensions to provide high-level
4 | # repository operations for Vincent Driessen's branching model.
5 | #
6 | # Original blog post presenting this model is found at:
7 | # http://nvie.com/git-model
8 | #
9 | # Feel free to contribute to this project at:
10 | # http://github.com/nvie/gitflow
11 | #
12 | # Copyright 2010 Vincent Driessen. All rights reserved.
13 | #
14 | # Redistribution and use in source and binary forms, with or without
15 | # modification, are permitted provided that the following conditions are met:
16 | #
17 | # 1. Redistributions of source code must retain the above copyright notice,
18 | # this list of conditions and the following disclaimer.
19 | #
20 | # 2. Redistributions in binary form must reproduce the above copyright
21 | # notice, this list of conditions and the following disclaimer in the
22 | # documentation and/or other materials provided with the distribution.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
25 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
27 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
31 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
33 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 | #
35 | # The views and conclusions contained in the software and documentation are
36 | # those of the authors and should not be interpreted as representing official
37 | # policies, either expressed or implied, of Vincent Driessen.
38 | #
39 |
40 | # set this to workaround expr problems in shFlags on freebsd
41 | if uname -s | egrep -iq 'bsd'; then export EXPR_COMPAT=1; fi
42 |
43 | # enable debug mode
44 | if [ "$DEBUG" = "yes" ]; then
45 | set -x
46 | fi
47 |
48 | # The sed expression here replaces all backslashes by forward slashes.
49 | # This helps our Windows users, while not bothering our Unix users.
50 | export GITFLOW_DIR=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
51 |
52 | usage() {
53 | echo "usage: git flow "
54 | echo
55 | echo "Available subcommands are:"
56 | echo " init Initialize a new git repo with support for the branching model."
57 | echo " feature Manage your feature branches."
58 | echo " release Manage your release branches."
59 | echo " hotfix Manage your hotfix branches."
60 | echo " support Manage your support branches."
61 | echo " version Shows version information."
62 | echo
63 | echo "Try 'git flow help' for details."
64 | }
65 |
66 | main() {
67 | if [ $# -lt 1 ]; then
68 | usage
69 | exit 1
70 | fi
71 |
72 | # load common functionality
73 | . "$GITFLOW_DIR/gitflow-common"
74 |
75 | # This environmental variable fixes non-POSIX getopt style argument
76 | # parsing, effectively breaking git-flow subcommand parsing on several
77 | # Linux platforms.
78 | export POSIXLY_CORRECT=1
79 |
80 | # use the shFlags project to parse the command line arguments
81 | . "$GITFLOW_DIR/gitflow-shFlags"
82 | FLAGS_PARENT="git flow"
83 | FLAGS "$@" || exit $?
84 | eval set -- "${FLAGS_ARGV}"
85 |
86 | # sanity checks
87 | SUBCOMMAND="$1"; shift
88 |
89 | if [ ! -e "$GITFLOW_DIR/git-flow-$SUBCOMMAND" ]; then
90 | usage
91 | exit 1
92 | fi
93 |
94 | # run command
95 | . "$GITFLOW_DIR/git-flow-$SUBCOMMAND"
96 | FLAGS_PARENT="git flow $SUBCOMMAND"
97 |
98 | # test if the first argument is a flag (i.e. starts with '-')
99 | # in that case, we interpret this arg as a flag for the default
100 | # command
101 | SUBACTION="default"
102 | if [ "$1" != "" ] && { ! echo "$1" | grep -q "^-"; } then
103 | SUBACTION="$1"; shift
104 | fi
105 | if ! type "cmd_$SUBACTION" >/dev/null 2>&1; then
106 | warn "Unknown subcommand: '$SUBACTION'"
107 | usage
108 | exit 1
109 | fi
110 |
111 | # run the specified action
112 | if [ $SUBACTION != "help" ] && [ $SUBCOMMAND != "init" ] ; then
113 | init
114 | fi
115 | cmd_$SUBACTION "$@"
116 | }
117 |
118 | main "$@"
119 |
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow-feature:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | init() {
40 | require_git_repo
41 | require_gitflow_initialized
42 | gitflow_load_settings
43 | PREFIX=$(git config --get gitflow.prefix.feature)
44 | }
45 |
46 | usage() {
47 | echo "usage: git flow feature [list] [-v]"
48 | echo " git flow feature start [-F] []"
49 | echo " git flow feature finish [-rFkDS] []"
50 | echo " git flow feature publish "
51 | echo " git flow feature track "
52 | echo " git flow feature diff []"
53 | echo " git flow feature rebase [-i] []"
54 | echo " git flow feature checkout []"
55 | echo " git flow feature pull [-r] []"
56 | }
57 |
58 | cmd_default() {
59 | cmd_list "$@"
60 | }
61 |
62 | cmd_list() {
63 | DEFINE_boolean verbose false 'verbose (more) output' v
64 | parse_args "$@"
65 |
66 | local feature_branches
67 | local current_branch
68 | local short_names
69 | feature_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
70 | if [ -z "$feature_branches" ]; then
71 | warn "No feature branches exist."
72 | warn ""
73 | warn "You can start a new feature branch:"
74 | warn ""
75 | warn " git flow feature start []"
76 | warn ""
77 | exit 0
78 | fi
79 | current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
80 | short_names=$(echo "$feature_branches" | sed "s ^$PREFIX g")
81 |
82 | # determine column width first
83 | local width=0
84 | local branch
85 | for branch in $short_names; do
86 | local len=${#branch}
87 | width=$(max $width $len)
88 | done
89 | width=$(($width+3))
90 |
91 | local branch
92 | for branch in $short_names; do
93 | local fullname=$PREFIX$branch
94 | local base=$(git merge-base "$fullname" "$DEVELOP_BRANCH")
95 | local develop_sha=$(git rev-parse "$DEVELOP_BRANCH")
96 | local branch_sha=$(git rev-parse "$fullname")
97 | if [ "$fullname" = "$current_branch" ]; then
98 | printf "* "
99 | else
100 | printf " "
101 | fi
102 | if flag verbose; then
103 | printf "%-${width}s" "$branch"
104 | if [ "$branch_sha" = "$develop_sha" ]; then
105 | printf "(no commits yet)"
106 | elif [ "$base" = "$branch_sha" ]; then
107 | printf "(is behind develop, may ff)"
108 | elif [ "$base" = "$develop_sha" ]; then
109 | printf "(based on latest develop)"
110 | else
111 | printf "(may be rebased)"
112 | fi
113 | else
114 | printf "%s" "$branch"
115 | fi
116 | echo
117 | done
118 | }
119 |
120 | cmd_help() {
121 | usage
122 | exit 0
123 | }
124 |
125 | require_name_arg() {
126 | if [ "$NAME" = "" ]; then
127 | warn "Missing argument "
128 | usage
129 | exit 1
130 | fi
131 | }
132 |
133 | expand_nameprefix_arg() {
134 | require_name_arg
135 |
136 | local expanded_name
137 | local exitcode
138 | expanded_name=$(gitflow_resolve_nameprefix "$NAME" "$PREFIX")
139 | exitcode=$?
140 | case $exitcode in
141 | 0) NAME=$expanded_name
142 | BRANCH=$PREFIX$NAME
143 | ;;
144 | *) exit 1 ;;
145 | esac
146 | }
147 |
148 | use_current_feature_branch_name() {
149 | local current_branch=$(git_current_branch)
150 | if startswith "$current_branch" "$PREFIX"; then
151 | BRANCH=$current_branch
152 | NAME=${BRANCH#$PREFIX}
153 | else
154 | warn "The current HEAD is no feature branch."
155 | warn "Please specify a argument."
156 | exit 1
157 | fi
158 | }
159 |
160 | expand_nameprefix_arg_or_current() {
161 | if [ "$NAME" != "" ]; then
162 | expand_nameprefix_arg
163 | require_branch "$PREFIX$NAME"
164 | else
165 | use_current_feature_branch_name
166 | fi
167 | }
168 |
169 | name_or_current() {
170 | if [ -z "$NAME" ]; then
171 | use_current_feature_branch_name
172 | fi
173 | }
174 |
175 | parse_args() {
176 | # parse options
177 | FLAGS "$@" || exit $?
178 | eval set -- "${FLAGS_ARGV}"
179 |
180 | # read arguments into global variables
181 | NAME=$1
182 | BRANCH=$PREFIX$NAME
183 | }
184 |
185 | parse_remote_name() {
186 | # parse options
187 | FLAGS "$@" || exit $?
188 | eval set -- "${FLAGS_ARGV}"
189 |
190 | # read arguments into global variables
191 | REMOTE=$1
192 | NAME=$2
193 | BRANCH=$PREFIX$NAME
194 | }
195 |
196 | cmd_start() {
197 | DEFINE_boolean fetch false 'fetch from origin before performing local operation' F
198 | parse_args "$@"
199 | BASE=${2:-$DEVELOP_BRANCH}
200 | require_name_arg
201 |
202 | # sanity checks
203 | require_branch_absent "$BRANCH"
204 |
205 | # update the local repo with remote changes, if asked
206 | if flag fetch; then
207 | git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
208 | fi
209 |
210 | # if the origin branch counterpart exists, assert that the local branch
211 | # isn't behind it (to avoid unnecessary rebasing)
212 | if git_branch_exists "$ORIGIN/$DEVELOP_BRANCH"; then
213 | require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
214 | fi
215 |
216 | # create branch
217 | if ! git checkout -b "$BRANCH" "$BASE"; then
218 | die "Could not create feature branch '$BRANCH'"
219 | fi
220 |
221 | echo
222 | echo "Summary of actions:"
223 | echo "- A new branch '$BRANCH' was created, based on '$BASE'"
224 | echo "- You are now on branch '$BRANCH'"
225 | echo ""
226 | echo "Now, start committing on your feature. When done, use:"
227 | echo ""
228 | echo " git flow feature finish $NAME"
229 | echo
230 | }
231 |
232 | cmd_finish() {
233 | DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
234 | DEFINE_boolean rebase false "rebase instead of merge" r
235 | DEFINE_boolean keep false "keep branch after performing finish" k
236 | DEFINE_boolean force_delete false "force delete feature branch after finish" D
237 | DEFINE_boolean squash false "squash feature during merge" S
238 | parse_args "$@"
239 | expand_nameprefix_arg_or_current
240 |
241 | # sanity checks
242 | require_branch "$BRANCH"
243 |
244 | # detect if we're restoring from a merge conflict
245 | if [ -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE" ]; then
246 | #
247 | # TODO: detect that we're working on the correct branch here!
248 | # The user need not necessarily have given the same $NAME twice here
249 | # (although he/she should).
250 | #
251 |
252 | # TODO: git_is_clean_working_tree() should provide an alternative
253 | # exit code for "unmerged changes in working tree", which we should
254 | # actually be testing for here
255 | if git_is_clean_working_tree; then
256 | FINISH_BASE=$(cat "$DOT_GIT_DIR/.gitflow/MERGE_BASE")
257 |
258 | # Since the working tree is now clean, either the user did a
259 | # succesfull merge manually, or the merge was cancelled.
260 | # We detect this using git_is_branch_merged_into()
261 | if git_is_branch_merged_into "$BRANCH" "$FINISH_BASE"; then
262 | rm -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
263 | helper_finish_cleanup
264 | exit 0
265 | else
266 | # If the user cancelled the merge and decided to wait until later,
267 | # that's fine. But we have to acknowledge this by removing the
268 | # MERGE_BASE file and continuing normal execution of the finish
269 | rm -f "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
270 | fi
271 | else
272 | echo
273 | echo "Merge conflicts not resolved yet, use:"
274 | echo " git mergetool"
275 | echo " git commit"
276 | echo
277 | echo "You can then complete the finish by running it again:"
278 | echo " git flow feature finish $NAME"
279 | echo
280 | exit 1
281 | fi
282 | fi
283 |
284 | # sanity checks
285 | require_clean_working_tree
286 |
287 | # update local repo with remote changes first, if asked
288 | if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
289 | if flag fetch; then
290 | git fetch -q "$ORIGIN" "$BRANCH"
291 | git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
292 | fi
293 | fi
294 |
295 | if has "$ORIGIN/$BRANCH" $(git_remote_branches); then
296 | require_branches_equal "$BRANCH" "$ORIGIN/$BRANCH"
297 | fi
298 | if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
299 | require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
300 | fi
301 |
302 | # if the user wants to rebase, do that first
303 | if flag rebase; then
304 | if ! git flow feature rebase "$NAME" "$DEVELOP_BRANCH"; then
305 | warn "Finish was aborted due to conflicts during rebase."
306 | warn "Please finish the rebase manually now."
307 | warn "When finished, re-run:"
308 | warn " git flow feature finish '$NAME' '$DEVELOP_BRANCH'"
309 | exit 1
310 | fi
311 | fi
312 |
313 | # merge into BASE
314 | git checkout "$DEVELOP_BRANCH"
315 | if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then
316 | git merge --ff "$BRANCH"
317 | else
318 | if noflag squash; then
319 | git merge --no-ff "$BRANCH"
320 | else
321 | git merge --squash "$BRANCH"
322 | git commit
323 | git merge "$BRANCH"
324 | fi
325 | fi
326 |
327 | if [ $? -ne 0 ]; then
328 | # oops.. we have a merge conflict!
329 | # write the given $DEVELOP_BRANCH to a temporary file (we need it later)
330 | mkdir -p "$DOT_GIT_DIR/.gitflow"
331 | echo "$DEVELOP_BRANCH" > "$DOT_GIT_DIR/.gitflow/MERGE_BASE"
332 | echo
333 | echo "There were merge conflicts. To resolve the merge conflict manually, use:"
334 | echo " git mergetool"
335 | echo " git commit"
336 | echo
337 | echo "You can then complete the finish by running it again:"
338 | echo " git flow feature finish $NAME"
339 | echo
340 | exit 1
341 | fi
342 |
343 | # when no merge conflict is detected, just clean up the feature branch
344 | helper_finish_cleanup
345 | }
346 |
347 | helper_finish_cleanup() {
348 | # sanity checks
349 | require_branch "$BRANCH"
350 | require_clean_working_tree
351 |
352 | # delete branch
353 | if flag fetch; then
354 | git push "$ORIGIN" ":refs/heads/$BRANCH"
355 | fi
356 |
357 |
358 | if noflag keep; then
359 | if flag force_delete; then
360 | git branch -D "$BRANCH"
361 | else
362 | git branch -d "$BRANCH"
363 | fi
364 | fi
365 |
366 | echo
367 | echo "Summary of actions:"
368 | echo "- The feature branch '$BRANCH' was merged into '$DEVELOP_BRANCH'"
369 | #echo "- Merge conflicts were resolved" # TODO: Add this line when it's supported
370 | if flag keep; then
371 | echo "- Feature branch '$BRANCH' is still available"
372 | else
373 | echo "- Feature branch '$BRANCH' has been removed"
374 | fi
375 | echo "- You are now on branch '$DEVELOP_BRANCH'"
376 | echo
377 | }
378 |
379 | cmd_publish() {
380 | parse_args "$@"
381 | expand_nameprefix_arg
382 |
383 | # sanity checks
384 | require_clean_working_tree
385 | require_branch "$BRANCH"
386 | git fetch -q "$ORIGIN"
387 | require_branch_absent "$ORIGIN/$BRANCH"
388 |
389 | # create remote branch
390 | git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
391 | git fetch -q "$ORIGIN"
392 |
393 | # configure remote tracking
394 | git config "branch.$BRANCH.remote" "$ORIGIN"
395 | git config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
396 | git checkout "$BRANCH"
397 |
398 | echo
399 | echo "Summary of actions:"
400 | echo "- A new remote branch '$BRANCH' was created"
401 | echo "- The local branch '$BRANCH' was configured to track the remote branch"
402 | echo "- You are now on branch '$BRANCH'"
403 | echo
404 | }
405 |
406 | cmd_track() {
407 | parse_args "$@"
408 | require_name_arg
409 |
410 | # sanity checks
411 | require_clean_working_tree
412 | require_branch_absent "$BRANCH"
413 | git fetch -q "$ORIGIN"
414 | require_branch "$ORIGIN/$BRANCH"
415 |
416 | # create tracking branch
417 | git checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
418 |
419 | echo
420 | echo "Summary of actions:"
421 | echo "- A new remote tracking branch '$BRANCH' was created"
422 | echo "- You are now on branch '$BRANCH'"
423 | echo
424 | }
425 |
426 | cmd_diff() {
427 | parse_args "$@"
428 |
429 | if [ "$NAME" != "" ]; then
430 | expand_nameprefix_arg
431 | BASE=$(git merge-base "$DEVELOP_BRANCH" "$BRANCH")
432 | git diff "$BASE..$BRANCH"
433 | else
434 | if ! git_current_branch | grep -q "^$PREFIX"; then
435 | die "Not on a feature branch. Name one explicitly."
436 | fi
437 |
438 | BASE=$(git merge-base "$DEVELOP_BRANCH" HEAD)
439 | git diff "$BASE"
440 | fi
441 | }
442 |
443 | cmd_checkout() {
444 | parse_args "$@"
445 |
446 | if [ "$NAME" != "" ]; then
447 | expand_nameprefix_arg
448 | git checkout "$BRANCH"
449 | else
450 | die "Name a feature branch explicitly."
451 | fi
452 | }
453 |
454 | cmd_co() {
455 | # Alias for checkout
456 | cmd_checkout "$@"
457 | }
458 |
459 | cmd_rebase() {
460 | DEFINE_boolean interactive false 'do an interactive rebase' i
461 | parse_args "$@"
462 | expand_nameprefix_arg_or_current
463 | warn "Will try to rebase '$NAME'..."
464 | require_clean_working_tree
465 | require_branch "$BRANCH"
466 |
467 | git checkout -q "$BRANCH"
468 | local OPTS=
469 | if flag interactive; then
470 | OPTS="$OPTS -i"
471 | fi
472 | git rebase $OPTS "$DEVELOP_BRANCH"
473 | }
474 |
475 | avoid_accidental_cross_branch_action() {
476 | local current_branch=$(git_current_branch)
477 | if [ "$BRANCH" != "$current_branch" ]; then
478 | warn "Trying to pull from '$BRANCH' while currently on branch '$current_branch'."
479 | warn "To avoid unintended merges, git-flow aborted."
480 | return 1
481 | fi
482 | return 0
483 | }
484 |
485 | cmd_pull() {
486 | #DEFINE_string prefix false 'alternative remote feature branch name prefix' p
487 | DEFINE_boolean rebase false "pull with rebase" r
488 | parse_remote_name "$@"
489 |
490 | if [ -z "$REMOTE" ]; then
491 | die "Name a remote explicitly."
492 | fi
493 | name_or_current
494 |
495 | # To avoid accidentally merging different feature branches into each other,
496 | # die if the current feature branch differs from the requested $NAME
497 | # argument.
498 | local current_branch=$(git_current_branch)
499 | if startswith "$current_branch" "$PREFIX"; then
500 | # we are on a local feature branch already, so $BRANCH must be equal to
501 | # the current branch
502 | avoid_accidental_cross_branch_action || die
503 | fi
504 |
505 | require_clean_working_tree
506 |
507 | if git_branch_exists "$BRANCH"; then
508 | # Again, avoid accidental merges
509 | avoid_accidental_cross_branch_action || die
510 |
511 | # we already have a local branch called like this, so simply pull the
512 | # remote changes in
513 | if flag rebase; then
514 | if ! git pull --rebase -q "$REMOTE" "$BRANCH"; then
515 | warn "Pull was aborted. There might be conflicts during rebase or '$REMOTE' might be inaccessible."
516 | exit 1
517 | fi
518 | else
519 | git pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'."
520 | fi
521 |
522 | echo "Pulled $REMOTE's changes into $BRANCH."
523 | else
524 | # setup the local branch clone for the first time
525 | git fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD
526 | git branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed."
527 | git checkout -q "$BRANCH" || die "Checking out new local branch failed."
528 | echo "Created local branch $BRANCH based on $REMOTE's $BRANCH."
529 | fi
530 | }
531 |
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow-hotfix:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | init() {
40 | require_git_repo
41 | require_gitflow_initialized
42 | gitflow_load_settings
43 | VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`")
44 | PREFIX=$(git config --get gitflow.prefix.hotfix)
45 | }
46 |
47 | usage() {
48 | echo "usage: git flow hotfix [list] [-v]"
49 | echo " git flow hotfix start [-F] []"
50 | echo " git flow hotfix finish [-Fsumpk] "
51 | echo " git flow hotfix publish "
52 | echo " git flow hotfix track "
53 | }
54 |
55 | cmd_default() {
56 | cmd_list "$@"
57 | }
58 |
59 | cmd_list() {
60 | DEFINE_boolean verbose false 'verbose (more) output' v
61 | parse_args "$@"
62 |
63 | local hotfix_branches
64 | local current_branch
65 | local short_names
66 | hotfix_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
67 | if [ -z "$hotfix_branches" ]; then
68 | warn "No hotfix branches exist."
69 | warn ""
70 | warn "You can start a new hotfix branch:"
71 | warn ""
72 | warn " git flow hotfix start []"
73 | warn ""
74 | exit 0
75 | fi
76 | current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
77 | short_names=$(echo "$hotfix_branches" | sed "s ^$PREFIX g")
78 |
79 | # determine column width first
80 | local width=0
81 | local branch
82 | for branch in $short_names; do
83 | local len=${#branch}
84 | width=$(max $width $len)
85 | done
86 | width=$(($width+3))
87 |
88 | local branch
89 | for branch in $short_names; do
90 | local fullname=$PREFIX$branch
91 | local base=$(git merge-base "$fullname" "$MASTER_BRANCH")
92 | local master_sha=$(git rev-parse "$MASTER_BRANCH")
93 | local branch_sha=$(git rev-parse "$fullname")
94 | if [ "$fullname" = "$current_branch" ]; then
95 | printf "* "
96 | else
97 | printf " "
98 | fi
99 | if flag verbose; then
100 | printf "%-${width}s" "$branch"
101 | if [ "$branch_sha" = "$master_sha" ]; then
102 | printf "(no commits yet)"
103 | else
104 | local tagname=$(git name-rev --tags --no-undefined --name-only "$base")
105 | local nicename
106 | if [ "$tagname" != "" ]; then
107 | nicename=$tagname
108 | else
109 | nicename=$(git rev-parse --short "$base")
110 | fi
111 | printf "(based on $nicename)"
112 | fi
113 | else
114 | printf "%s" "$branch"
115 | fi
116 | echo
117 | done
118 | }
119 |
120 | cmd_help() {
121 | usage
122 | exit 0
123 | }
124 |
125 | parse_args() {
126 | # parse options
127 | FLAGS "$@" || exit $?
128 | eval set -- "${FLAGS_ARGV}"
129 |
130 | # read arguments into global variables
131 | VERSION=$1
132 | BRANCH=$PREFIX$VERSION
133 | }
134 |
135 | require_version_arg() {
136 | if [ "$VERSION" = "" ]; then
137 | warn "Missing argument "
138 | usage
139 | exit 1
140 | fi
141 | }
142 |
143 | require_base_is_on_master() {
144 | if ! git branch --no-color --contains "$BASE" 2>/dev/null \
145 | | sed 's/[* ] //g' \
146 | | grep -q "^$MASTER_BRANCH\$"; then
147 | die "fatal: Given base '$BASE' is not a valid commit on '$MASTER_BRANCH'."
148 | fi
149 | }
150 |
151 | require_no_existing_hotfix_branches() {
152 | local hotfix_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
153 | local first_branch=$(echo ${hotfix_branches} | head -n1)
154 | first_branch=${first_branch#$PREFIX}
155 | [ -z "$hotfix_branches" ] || \
156 | die "There is an existing hotfix branch ($first_branch). Finish that one first."
157 | }
158 |
159 | cmd_start() {
160 | DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
161 | parse_args "$@"
162 | BASE=${2:-$MASTER_BRANCH}
163 | require_version_arg
164 | require_base_is_on_master
165 | require_no_existing_hotfix_branches
166 |
167 | # sanity checks
168 | require_clean_working_tree
169 | require_branch_absent "$BRANCH"
170 | require_tag_absent "$VERSION_PREFIX$VERSION"
171 | if flag fetch; then
172 | git fetch -q "$ORIGIN" "$MASTER_BRANCH"
173 | fi
174 | if has "$ORIGIN/$MASTER_BRANCH" $(git_remote_branches); then
175 | require_branches_equal "$MASTER_BRANCH" "$ORIGIN/$MASTER_BRANCH"
176 | fi
177 |
178 | # create branch
179 | git checkout -b "$BRANCH" "$BASE"
180 |
181 | echo
182 | echo "Summary of actions:"
183 | echo "- A new branch '$BRANCH' was created, based on '$BASE'"
184 | echo "- You are now on branch '$BRANCH'"
185 | echo
186 | echo "Follow-up actions:"
187 | echo "- Bump the version number now!"
188 | echo "- Start committing your hot fixes"
189 | echo "- When done, run:"
190 | echo
191 | echo " git flow hotfix finish '$VERSION'"
192 | echo
193 | }
194 |
195 | cmd_publish() {
196 | parse_args "$@"
197 | require_version_arg
198 |
199 | # sanity checks
200 | require_clean_working_tree
201 | require_branch "$BRANCH"
202 | git fetch -q "$ORIGIN"
203 | require_branch_absent "$ORIGIN/$BRANCH"
204 |
205 | # create remote branch
206 | git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
207 | git fetch -q "$ORIGIN"
208 |
209 | # configure remote tracking
210 | git config "branch.$BRANCH.remote" "$ORIGIN"
211 | git config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
212 | git checkout "$BRANCH"
213 |
214 | echo
215 | echo "Summary of actions:"
216 | echo "- A new remote branch '$BRANCH' was created"
217 | echo "- The local branch '$BRANCH' was configured to track the remote branch"
218 | echo "- You are now on branch '$BRANCH'"
219 | echo
220 | }
221 |
222 | cmd_track() {
223 | parse_args "$@"
224 | require_version_arg
225 |
226 | # sanity checks
227 | require_clean_working_tree
228 | require_branch_absent "$BRANCH"
229 | git fetch -q "$ORIGIN"
230 | require_branch "$ORIGIN/$BRANCH"
231 |
232 | # create tracking branch
233 | git checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
234 |
235 | echo
236 | echo "Summary of actions:"
237 | echo "- A new remote tracking branch '$BRANCH' was created"
238 | echo "- You are now on branch '$BRANCH'"
239 | echo
240 | }
241 |
242 | cmd_finish() {
243 | DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
244 | DEFINE_boolean sign false "sign the release tag cryptographically" s
245 | DEFINE_string signingkey "" "use the given GPG-key for the digital signature (implies -s)" u
246 | DEFINE_string message "" "use the given tag message" m
247 | DEFINE_string messagefile "" "use the contents of the given file as tag message" f
248 | DEFINE_boolean push false "push to $ORIGIN after performing finish" p
249 | DEFINE_boolean keep false "keep branch after performing finish" k
250 | DEFINE_boolean notag false "don't tag this release" n
251 | parse_args "$@"
252 | require_version_arg
253 |
254 | # handle flags that imply other flags
255 | if [ "$FLAGS_signingkey" != "" ]; then
256 | FLAGS_sign=$FLAGS_TRUE
257 | fi
258 |
259 | # sanity checks
260 | require_branch "$BRANCH"
261 | require_clean_working_tree
262 | if flag fetch; then
263 | git fetch -q "$ORIGIN" "$MASTER_BRANCH" || \
264 | die "Could not fetch $MASTER_BRANCH from $ORIGIN."
265 | git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" || \
266 | die "Could not fetch $DEVELOP_BRANCH from $ORIGIN."
267 | fi
268 | if has "$ORIGIN/$MASTER_BRANCH" $(git_remote_branches); then
269 | require_branches_equal "$MASTER_BRANCH" "$ORIGIN/$MASTER_BRANCH"
270 | fi
271 | if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
272 | require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
273 | fi
274 |
275 | # try to merge into master
276 | # in case a previous attempt to finish this release branch has failed,
277 | # but the merge into master was successful, we skip it now
278 | if ! git_is_branch_merged_into "$BRANCH" "$MASTER_BRANCH"; then
279 | git checkout "$MASTER_BRANCH" || \
280 | die "Could not check out $MASTER_BRANCH."
281 | git merge --no-ff "$BRANCH" || \
282 | die "There were merge conflicts."
283 | # TODO: What do we do now?
284 | fi
285 |
286 | if noflag notag; then
287 | # try to tag the release
288 | # in case a previous attempt to finish this release branch has failed,
289 | # but the tag was set successful, we skip it now
290 | local tagname=$VERSION_PREFIX$VERSION
291 | if ! git_tag_exists "$tagname"; then
292 | local opts="-a"
293 | flag sign && opts="$opts -s"
294 | [ "$FLAGS_signingkey" != "" ] && opts="$opts -u '$FLAGS_signingkey'"
295 | [ "$FLAGS_message" != "" ] && opts="$opts -m '$FLAGS_message'"
296 | [ "$FLAGS_messagefile" != "" ] && opts="$opts -F '$FLAGS_messagefile'"
297 | eval git tag $opts "$VERSION_PREFIX$VERSION" "$BRANCH" || \
298 | die "Tagging failed. Please run finish again to retry."
299 | fi
300 | fi
301 |
302 | # try to merge into develop
303 | # in case a previous attempt to finish this release branch has failed,
304 | # but the merge into develop was successful, we skip it now
305 | if ! git_is_branch_merged_into "$BRANCH" "$DEVELOP_BRANCH"; then
306 | git checkout "$DEVELOP_BRANCH" || \
307 | die "Could not check out $DEVELOP_BRANCH."
308 |
309 | # TODO: Actually, accounting for 'git describe' pays, so we should
310 | # ideally git merge --no-ff $tagname here, instead!
311 | git merge --no-ff "$BRANCH" || \
312 | die "There were merge conflicts."
313 | # TODO: What do we do now?
314 | fi
315 |
316 | # delete branch
317 | if noflag keep; then
318 | git branch -d "$BRANCH"
319 | fi
320 |
321 | if flag push; then
322 | git push "$ORIGIN" "$DEVELOP_BRANCH" || \
323 | die "Could not push to $DEVELOP_BRANCH from $ORIGIN."
324 | git push "$ORIGIN" "$MASTER_BRANCH" || \
325 | die "Could not push to $MASTER_BRANCH from $ORIGIN."
326 | if noflag notag; then
327 | git push --tags "$ORIGIN" || \
328 | die "Could not push tags to $ORIGIN."
329 | fi
330 | git push "$ORIGIN" :"$BRANCH" || \
331 | die "Could not delete the remote $BRANCH in $ORIGIN."
332 | fi
333 |
334 | echo
335 | echo "Summary of actions:"
336 | echo "- Latest objects have been fetched from '$ORIGIN'"
337 | echo "- Hotfix branch has been merged into '$MASTER_BRANCH'"
338 | if noflag notag; then
339 | echo "- The hotfix was tagged '$VERSION_PREFIX$VERSION'"
340 | fi
341 | echo "- Hotfix branch has been back-merged into '$DEVELOP_BRANCH'"
342 | if flag keep; then
343 | echo "- Hotfix branch '$BRANCH' is still available"
344 | else
345 | echo "- Hotfix branch '$BRANCH' has been deleted"
346 | fi
347 | if flag push; then
348 | echo "- '$DEVELOP_BRANCH', '$MASTER_BRANCH' and tags have been pushed to '$ORIGIN'"
349 | echo "- Hotfix branch '$BRANCH' in '$ORIGIN' has been deleted."
350 | fi
351 | echo
352 | }
353 |
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow-init:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | usage() {
40 | echo "usage: git flow init [-fd]"
41 | }
42 |
43 | parse_args() {
44 | # parse options
45 | FLAGS "$@" || exit $?
46 | eval set -- "${FLAGS_ARGV}"
47 | }
48 |
49 | # Default entry when no SUBACTION is given
50 | cmd_default() {
51 | DEFINE_boolean force false 'force setting of gitflow branches, even if already configured' f
52 | DEFINE_boolean defaults false 'use default branch naming conventions' d
53 | parse_args "$@"
54 |
55 | if ! git rev-parse --git-dir >/dev/null 2>&1; then
56 | git init
57 | else
58 | # assure that we are not working in a repo with local changes
59 | git_repo_is_headless || require_clean_working_tree
60 | fi
61 |
62 | # running git flow init on an already initialized repo is fine
63 | if gitflow_is_initialized && ! flag force; then
64 | warn "Already initialized for gitflow."
65 | warn "To force reinitialization, use: git flow init -f"
66 | exit 0
67 | fi
68 |
69 | local branch_count
70 | local answer
71 |
72 | if flag defaults; then
73 | warn "Using default branch names."
74 | fi
75 |
76 | # add a master branch if no such branch exists yet
77 | local master_branch
78 | if gitflow_has_master_configured && ! flag force; then
79 | master_branch=$(git config --get gitflow.branch.master)
80 | else
81 | # Two cases are distinguished:
82 | # 1. A fresh git repo (without any branches)
83 | # We will create a new master/develop branch for the user
84 | # 2. Some branches do already exist
85 | # We will disallow creation of new master/develop branches and
86 | # rather allow to use existing branches for git-flow.
87 | local default_suggestion
88 | local should_check_existence
89 | branch_count=$(git_local_branches | wc -l)
90 | if [ "$branch_count" -eq 0 ]; then
91 | echo "No branches exist yet. Base branches must be created now."
92 | should_check_existence=NO
93 | default_suggestion=$(git config --get gitflow.branch.master || echo master)
94 | else
95 | echo
96 | echo "Which branch should be used for bringing forth production releases?"
97 | git_local_branches | sed 's/^.*$/ - &/g'
98 |
99 | should_check_existence=YES
100 | default_suggestion=
101 | for guess in $(git config --get gitflow.branch.master) \
102 | 'production' 'main' 'master'; do
103 | if git_local_branch_exists "$guess"; then
104 | default_suggestion="$guess"
105 | break
106 | fi
107 | done
108 | fi
109 |
110 | printf "Branch name for production releases: [$default_suggestion] "
111 | if noflag defaults; then
112 | read answer
113 | else
114 | printf "\n"
115 | fi
116 | master_branch=${answer:-$default_suggestion}
117 |
118 | # check existence in case of an already existing repo
119 | if [ "$should_check_existence" = "YES" ]; then
120 | # if no local branch exists and a remote branch of the same
121 | # name exists, checkout that branch and use it for master
122 | if ! git_local_branch_exists "$master_branch" && \
123 | git_remote_branch_exists "origin/$master_branch"; then
124 | git branch "$master_branch" "origin/$master_branch" >/dev/null 2>&1
125 | elif ! git_local_branch_exists "$master_branch"; then
126 | die "Local branch '$master_branch' does not exist."
127 | fi
128 | fi
129 |
130 | # store the name of the master branch
131 | git config gitflow.branch.master "$master_branch"
132 | fi
133 |
134 | # add a develop branch if no such branch exists yet
135 | local develop_branch
136 | if gitflow_has_develop_configured && ! flag force; then
137 | develop_branch=$(git config --get gitflow.branch.develop)
138 | else
139 | # Again, the same two cases as with the master selection are
140 | # considered (fresh repo or repo that contains branches)
141 | local default_suggestion
142 | local should_check_existence
143 | branch_count=$(git_local_branches | grep -v "^${master_branch}\$" | wc -l)
144 | if [ "$branch_count" -eq 0 ]; then
145 | should_check_existence=NO
146 | default_suggestion=$(git config --get gitflow.branch.develop || echo develop)
147 | else
148 | echo
149 | echo "Which branch should be used for integration of the \"next release\"?"
150 | git_local_branches | grep -v "^${master_branch}\$" | sed 's/^.*$/ - &/g'
151 |
152 | should_check_existence=YES
153 | default_suggestion=
154 | for guess in $(git config --get gitflow.branch.develop) \
155 | 'develop' 'int' 'integration' 'master'; do
156 | if git_local_branch_exists "$guess" && [ "$guess" != "$master_branch" ]; then
157 | default_suggestion="$guess"
158 | break
159 | fi
160 | done
161 |
162 | if [ -z $default_suggestion ]; then
163 | should_check_existence=NO
164 | default_suggestion=$(git config --get gitflow.branch.develop || echo develop)
165 | fi
166 |
167 | fi
168 |
169 | printf "Branch name for \"next release\" development: [$default_suggestion] "
170 | if noflag defaults; then
171 | read answer
172 | else
173 | printf "\n"
174 | fi
175 | develop_branch=${answer:-$default_suggestion}
176 |
177 | if [ "$master_branch" = "$develop_branch" ]; then
178 | die "Production and integration branches should differ."
179 | fi
180 |
181 | # check existence in case of an already existing repo
182 | if [ "$should_check_existence" = "YES" ]; then
183 | git_local_branch_exists "$develop_branch" || \
184 | die "Local branch '$develop_branch' does not exist."
185 | fi
186 |
187 | # store the name of the develop branch
188 | git config gitflow.branch.develop "$develop_branch"
189 | fi
190 |
191 | # Creation of HEAD
192 | # ----------------
193 | # We create a HEAD now, if it does not exist yet (in a fresh repo). We need
194 | # it to be able to create new branches.
195 | local created_gitflow_branch=0
196 | if ! git rev-parse --quiet --verify HEAD >/dev/null 2>&1; then
197 | git symbolic-ref HEAD "refs/heads/$master_branch"
198 | git commit --allow-empty --quiet -m "Initial commit"
199 | created_gitflow_branch=1
200 | fi
201 |
202 | # Creation of master
203 | # ------------------
204 | # At this point, there always is a master branch: either it existed already
205 | # (and was picked interactively as the production branch) or it has just
206 | # been created in a fresh repo
207 |
208 | # Creation of develop
209 | # -------------------
210 | # The develop branch possibly does not exist yet. This is the case when,
211 | # in a git init'ed repo with one or more commits, master was picked as the
212 | # default production branch and develop was "created". We should create
213 | # the develop branch now in that case (we base it on master, of course)
214 | if ! git_local_branch_exists "$develop_branch"; then
215 | if git_remote_branch_exists "origin/$develop_branch"; then
216 | git branch "$develop_branch" "origin/$develop_branch" >/dev/null 2>&1
217 | else
218 | git branch --no-track "$develop_branch" "$master_branch"
219 | fi
220 | created_gitflow_branch=1
221 | fi
222 |
223 | # assert the gitflow repo has been correctly initialized
224 | gitflow_is_initialized
225 |
226 | # switch to develop branch if its newly created
227 | if [ $created_gitflow_branch -eq 1 ]; then
228 | git checkout -q "$develop_branch"
229 | fi
230 |
231 | # finally, ask the user for naming conventions (branch and tag prefixes)
232 | if flag force || \
233 | ! git config --get gitflow.prefix.feature >/dev/null 2>&1 ||
234 | ! git config --get gitflow.prefix.release >/dev/null 2>&1 ||
235 | ! git config --get gitflow.prefix.hotfix >/dev/null 2>&1 ||
236 | ! git config --get gitflow.prefix.support >/dev/null 2>&1 ||
237 | ! git config --get gitflow.prefix.versiontag >/dev/null 2>&1; then
238 | echo
239 | echo "How to name your supporting branch prefixes?"
240 | fi
241 |
242 | local prefix
243 |
244 | # Feature branches
245 | if ! git config --get gitflow.prefix.feature >/dev/null 2>&1 || flag force; then
246 | default_suggestion=$(git config --get gitflow.prefix.feature || echo feature/)
247 | printf "Feature branches? [$default_suggestion] "
248 | if noflag defaults; then
249 | read answer
250 | else
251 | printf "\n"
252 | fi
253 | [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
254 | git config gitflow.prefix.feature "$prefix"
255 | fi
256 |
257 | # Release branches
258 | if ! git config --get gitflow.prefix.release >/dev/null 2>&1 || flag force; then
259 | default_suggestion=$(git config --get gitflow.prefix.release || echo release/)
260 | printf "Release branches? [$default_suggestion] "
261 | if noflag defaults; then
262 | read answer
263 | else
264 | printf "\n"
265 | fi
266 | [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
267 | git config gitflow.prefix.release "$prefix"
268 | fi
269 |
270 |
271 | # Hotfix branches
272 | if ! git config --get gitflow.prefix.hotfix >/dev/null 2>&1 || flag force; then
273 | default_suggestion=$(git config --get gitflow.prefix.hotfix || echo hotfix/)
274 | printf "Hotfix branches? [$default_suggestion] "
275 | if noflag defaults; then
276 | read answer
277 | else
278 | printf "\n"
279 | fi
280 | [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
281 | git config gitflow.prefix.hotfix "$prefix"
282 | fi
283 |
284 |
285 | # Support branches
286 | if ! git config --get gitflow.prefix.support >/dev/null 2>&1 || flag force; then
287 | default_suggestion=$(git config --get gitflow.prefix.support || echo support/)
288 | printf "Support branches? [$default_suggestion] "
289 | if noflag defaults; then
290 | read answer
291 | else
292 | printf "\n"
293 | fi
294 | [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
295 | git config gitflow.prefix.support "$prefix"
296 | fi
297 |
298 |
299 | # Version tag prefix
300 | if ! git config --get gitflow.prefix.versiontag >/dev/null 2>&1 || flag force; then
301 | default_suggestion=$(git config --get gitflow.prefix.versiontag || echo "")
302 | printf "Version tag prefix? [$default_suggestion] "
303 | if noflag defaults; then
304 | read answer
305 | else
306 | printf "\n"
307 | fi
308 | [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
309 | git config gitflow.prefix.versiontag "$prefix"
310 | fi
311 |
312 |
313 | # TODO: what to do with origin?
314 | }
315 |
316 | cmd_help() {
317 | usage
318 | exit 0
319 | }
320 |
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow-release:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | init() {
40 | require_git_repo
41 | require_gitflow_initialized
42 | gitflow_load_settings
43 | VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`")
44 | PREFIX=$(git config --get gitflow.prefix.release)
45 | }
46 |
47 | usage() {
48 | echo "usage: git flow release [list] [-v]"
49 | echo " git flow release start [-F] []"
50 | echo " git flow release finish [-FsumpkS] "
51 | echo " git flow release publish "
52 | echo " git flow release track "
53 | }
54 |
55 | cmd_default() {
56 | cmd_list "$@"
57 | }
58 |
59 | cmd_list() {
60 | DEFINE_boolean verbose false 'verbose (more) output' v
61 | parse_args "$@"
62 |
63 | local release_branches
64 | local current_branch
65 | local short_names
66 | release_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
67 | if [ -z "$release_branches" ]; then
68 | warn "No release branches exist."
69 | warn ""
70 | warn "You can start a new release branch:"
71 | warn ""
72 | warn " git flow release start []"
73 | warn ""
74 | exit 0
75 | fi
76 |
77 | current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
78 | short_names=$(echo "$release_branches" | sed "s ^$PREFIX g")
79 |
80 | # determine column width first
81 | local width=0
82 | local branch
83 | for branch in $short_names; do
84 | local len=${#branch}
85 | width=$(max $width $len)
86 | done
87 | width=$(($width+3))
88 |
89 | local branch
90 | for branch in $short_names; do
91 | local fullname=$PREFIX$branch
92 | local base=$(git merge-base "$fullname" "$DEVELOP_BRANCH")
93 | local develop_sha=$(git rev-parse "$DEVELOP_BRANCH")
94 | local branch_sha=$(git rev-parse "$fullname")
95 | if [ "$fullname" = "$current_branch" ]; then
96 | printf "* "
97 | else
98 | printf " "
99 | fi
100 | if flag verbose; then
101 | printf "%-${width}s" "$branch"
102 | if [ "$branch_sha" = "$develop_sha" ]; then
103 | printf "(no commits yet)"
104 | else
105 | local nicename=$(git rev-parse --short "$base")
106 | printf "(based on $nicename)"
107 | fi
108 | else
109 | printf "%s" "$branch"
110 | fi
111 | echo
112 | done
113 | }
114 |
115 | cmd_help() {
116 | usage
117 | exit 0
118 | }
119 |
120 | parse_args() {
121 | # parse options
122 | FLAGS "$@" || exit $?
123 | eval set -- "${FLAGS_ARGV}"
124 |
125 | # read arguments into global variables
126 | VERSION=$1
127 | BRANCH=$PREFIX$VERSION
128 | }
129 |
130 | require_version_arg() {
131 | if [ "$VERSION" = "" ]; then
132 | warn "Missing argument "
133 | usage
134 | exit 1
135 | fi
136 | }
137 |
138 | require_base_is_on_develop() {
139 | if ! git branch --no-color --contains "$BASE" 2>/dev/null \
140 | | sed 's/[* ] //g' \
141 | | grep -q "^$DEVELOP_BRANCH\$"; then
142 | die "fatal: Given base '$BASE' is not a valid commit on '$DEVELOP_BRANCH'."
143 | fi
144 | }
145 |
146 | require_no_existing_release_branches() {
147 | local release_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
148 | local first_branch=$(echo ${release_branches} | head -n1)
149 | first_branch=${first_branch#$PREFIX}
150 | [ -z "$release_branches" ] || \
151 | die "There is an existing release branch ($first_branch). Finish that one first."
152 | }
153 |
154 | cmd_start() {
155 | DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
156 | parse_args "$@"
157 | BASE=${2:-$DEVELOP_BRANCH}
158 | require_version_arg
159 | require_base_is_on_develop
160 | require_no_existing_release_branches
161 |
162 | # sanity checks
163 | require_clean_working_tree
164 | require_branch_absent "$BRANCH"
165 | require_tag_absent "$VERSION_PREFIX$VERSION"
166 | if flag fetch; then
167 | git fetch -q "$ORIGIN" "$DEVELOP_BRANCH"
168 | fi
169 | if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
170 | require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
171 | fi
172 |
173 | # create branch
174 | git checkout -b "$BRANCH" "$BASE"
175 |
176 | echo
177 | echo "Summary of actions:"
178 | echo "- A new branch '$BRANCH' was created, based on '$BASE'"
179 | echo "- You are now on branch '$BRANCH'"
180 | echo
181 | echo "Follow-up actions:"
182 | echo "- Bump the version number now!"
183 | echo "- Start committing last-minute fixes in preparing your release"
184 | echo "- When done, run:"
185 | echo
186 | echo " git flow release finish '$VERSION'"
187 | echo
188 | }
189 |
190 | cmd_finish() {
191 | DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
192 | DEFINE_boolean sign false "sign the release tag cryptographically" s
193 | DEFINE_string signingkey "" "use the given GPG-key for the digital signature (implies -s)" u
194 | DEFINE_string message "" "use the given tag message" m
195 | DEFINE_string messagefile "" "use the contents of the given file as a tag message" f
196 | DEFINE_boolean push false "push to $ORIGIN after performing finish" p
197 | DEFINE_boolean keep false "keep branch after performing finish" k
198 | DEFINE_boolean notag false "don't tag this release" n
199 | DEFINE_boolean squash false "squash release during merge" S
200 |
201 | parse_args "$@"
202 | require_version_arg
203 |
204 | # handle flags that imply other flags
205 | if [ "$FLAGS_signingkey" != "" ]; then
206 | FLAGS_sign=$FLAGS_TRUE
207 | fi
208 |
209 | # sanity checks
210 | require_branch "$BRANCH"
211 | require_clean_working_tree
212 | if flag fetch; then
213 | git fetch -q "$ORIGIN" "$MASTER_BRANCH" || \
214 | die "Could not fetch $MASTER_BRANCH from $ORIGIN."
215 | git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" || \
216 | die "Could not fetch $DEVELOP_BRANCH from $ORIGIN."
217 | fi
218 | if has "$ORIGIN/$MASTER_BRANCH" $(git_remote_branches); then
219 | require_branches_equal "$MASTER_BRANCH" "$ORIGIN/$MASTER_BRANCH"
220 | fi
221 | if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then
222 | require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH"
223 | fi
224 |
225 | # try to merge into master
226 | # in case a previous attempt to finish this release branch has failed,
227 | # but the merge into master was successful, we skip it now
228 | if ! git_is_branch_merged_into "$BRANCH" "$MASTER_BRANCH"; then
229 | git checkout "$MASTER_BRANCH" || \
230 | die "Could not check out $MASTER_BRANCH."
231 | if noflag squash; then
232 | git merge --no-ff "$BRANCH" || \
233 | die "There were merge conflicts."
234 | # TODO: What do we do now?
235 | else
236 | git merge --squash "$BRANCH" || \
237 | die "There were merge conflicts."
238 | git commit
239 | fi
240 | fi
241 |
242 | if noflag notag; then
243 | # try to tag the release
244 | # in case a previous attempt to finish this release branch has failed,
245 | # but the tag was set successful, we skip it now
246 | local tagname=$VERSION_PREFIX$VERSION
247 | if ! git_tag_exists "$tagname"; then
248 | local opts="-a"
249 | flag sign && opts="$opts -s"
250 | [ "$FLAGS_signingkey" != "" ] && opts="$opts -u '$FLAGS_signingkey'"
251 | [ "$FLAGS_message" != "" ] && opts="$opts -m '$FLAGS_message'"
252 | [ "$FLAGS_messagefile" != "" ] && opts="$opts -F '$FLAGS_messagefile'"
253 | eval git tag $opts "$tagname" "$BRANCH" || \
254 | die "Tagging failed. Please run finish again to retry."
255 | fi
256 | fi
257 |
258 | # try to merge into develop
259 | # in case a previous attempt to finish this release branch has failed,
260 | # but the merge into develop was successful, we skip it now
261 | if ! git_is_branch_merged_into "$BRANCH" "$DEVELOP_BRANCH"; then
262 | git checkout "$DEVELOP_BRANCH" || \
263 | die "Could not check out $DEVELOP_BRANCH."
264 |
265 | # TODO: Actually, accounting for 'git describe' pays, so we should
266 | # ideally git merge --no-ff $tagname here, instead!
267 | if noflag squash; then
268 | git merge --no-ff "$BRANCH" || \
269 | die "There were merge conflicts."
270 | # TODO: What do we do now?
271 | else
272 | git merge --squash "$BRANCH" || \
273 | die "There were merge conflicts."
274 | # TODO: What do we do now?
275 | git commit
276 | fi
277 | fi
278 |
279 | # delete branch
280 | if noflag keep; then
281 | if [ "$BRANCH" = "$(git_current_branch)" ]; then
282 | git checkout "$MASTER_BRANCH"
283 | fi
284 | git branch -d "$BRANCH"
285 | fi
286 |
287 | if flag push; then
288 | git push "$ORIGIN" "$DEVELOP_BRANCH" || \
289 | die "Could not push to $DEVELOP_BRANCH from $ORIGIN."
290 | git push "$ORIGIN" "$MASTER_BRANCH" || \
291 | die "Could not push to $MASTER_BRANCH from $ORIGIN."
292 | if noflag notag; then
293 | git push --tags "$ORIGIN" || \
294 | die "Could not push tags to $ORIGIN."
295 | fi
296 | git push "$ORIGIN" :"$BRANCH" || \
297 | die "Could not delete the remote $BRANCH in $ORIGIN."
298 | fi
299 |
300 | echo
301 | echo "Summary of actions:"
302 | echo "- Latest objects have been fetched from '$ORIGIN'"
303 | echo "- Release branch has been merged into '$MASTER_BRANCH'"
304 | if noflag notag; then
305 | echo "- The release was tagged '$tagname'"
306 | fi
307 | echo "- Release branch has been back-merged into '$DEVELOP_BRANCH'"
308 | if flag keep; then
309 | echo "- Release branch '$BRANCH' is still available"
310 | else
311 | echo "- Release branch '$BRANCH' has been deleted"
312 | fi
313 | if flag push; then
314 | echo "- '$DEVELOP_BRANCH', '$MASTER_BRANCH' and tags have been pushed to '$ORIGIN'"
315 | echo "- Release branch '$BRANCH' in '$ORIGIN' has been deleted."
316 | fi
317 | echo
318 | }
319 |
320 | cmd_publish() {
321 | parse_args "$@"
322 | require_version_arg
323 |
324 | # sanity checks
325 | require_clean_working_tree
326 | require_branch "$BRANCH"
327 | git fetch -q "$ORIGIN"
328 | require_branch_absent "$ORIGIN/$BRANCH"
329 |
330 | # create remote branch
331 | git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH"
332 | git fetch -q "$ORIGIN"
333 |
334 | # configure remote tracking
335 | git config "branch.$BRANCH.remote" "$ORIGIN"
336 | git config "branch.$BRANCH.merge" "refs/heads/$BRANCH"
337 | git checkout "$BRANCH"
338 |
339 | echo
340 | echo "Summary of actions:"
341 | echo "- A new remote branch '$BRANCH' was created"
342 | echo "- The local branch '$BRANCH' was configured to track the remote branch"
343 | echo "- You are now on branch '$BRANCH'"
344 | echo
345 | }
346 |
347 | cmd_track() {
348 | parse_args "$@"
349 | require_version_arg
350 |
351 | # sanity checks
352 | require_clean_working_tree
353 | require_branch_absent "$BRANCH"
354 | git fetch -q "$ORIGIN"
355 | require_branch "$ORIGIN/$BRANCH"
356 |
357 | # create tracking branch
358 | git checkout -b "$BRANCH" "$ORIGIN/$BRANCH"
359 |
360 | echo
361 | echo "Summary of actions:"
362 | echo "- A new remote tracking branch '$BRANCH' was created"
363 | echo "- You are now on branch '$BRANCH'"
364 | echo
365 | }
366 |
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow-support:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | init() {
40 | require_git_repo
41 | require_gitflow_initialized
42 | gitflow_load_settings
43 | VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`")
44 | PREFIX=$(git config --get gitflow.prefix.support)
45 | }
46 |
47 | warn "note: The support subcommand is still very EXPERIMENTAL!"
48 | warn "note: DO NOT use it in a production situation."
49 |
50 | usage() {
51 | echo "usage: git flow support [list] [-v]"
52 | echo " git flow support start [-F] "
53 | }
54 |
55 | cmd_default() {
56 | cmd_list "$@"
57 | }
58 |
59 | cmd_list() {
60 | DEFINE_boolean verbose false 'verbose (more) output' v
61 | parse_args "$@"
62 |
63 | local support_branches
64 | local current_branch
65 | local short_names
66 | support_branches=$(echo "$(git_local_branches)" | grep "^$PREFIX")
67 | if [ -z "$support_branches" ]; then
68 | warn "No support branches exist."
69 | warn ""
70 | warn "You can start a new support branch:"
71 | warn ""
72 | warn " git flow support start "
73 | warn ""
74 | exit 0
75 | fi
76 | current_branch=$(git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g')
77 | short_names=$(echo "$support_branches" | sed "s ^$PREFIX g")
78 |
79 | # determine column width first
80 | local width=0
81 | local branch
82 | for branch in $short_names; do
83 | local len=${#branch}
84 | width=$(max $width $len)
85 | done
86 | width=$(($width+3))
87 |
88 | local branch
89 | for branch in $short_names; do
90 | local fullname=$PREFIX$branch
91 | local base=$(git merge-base "$fullname" "$MASTER_BRANCH")
92 | local master_sha=$(git rev-parse "$MASTER_BRANCH")
93 | local branch_sha=$(git rev-parse "$fullname")
94 | if [ "$fullname" = "$current_branch" ]; then
95 | printf "* "
96 | else
97 | printf " "
98 | fi
99 | if flag verbose; then
100 | printf "%-${width}s" "$branch"
101 | if [ "$branch_sha" = "$master_sha" ]; then
102 | printf "(no commits yet)"
103 | else
104 | local tagname=$(git name-rev --tags --no-undefined --name-only "$base")
105 | local nicename
106 | if [ "$tagname" != "" ]; then
107 | nicename=$tagname
108 | else
109 | nicename=$(git rev-parse --short "$base")
110 | fi
111 | printf "(based on $nicename)"
112 | fi
113 | else
114 | printf "%s" "$branch"
115 | fi
116 | echo
117 | done
118 | }
119 |
120 | cmd_help() {
121 | usage
122 | exit 0
123 | }
124 |
125 | parse_args() {
126 | # parse options
127 | FLAGS "$@" || exit $?
128 | eval set -- "${FLAGS_ARGV}"
129 |
130 | # read arguments into global variables
131 | VERSION=$1
132 | BASE=$2
133 | BRANCH=$PREFIX$VERSION
134 | }
135 |
136 | require_version_arg() {
137 | if [ "$VERSION" = "" ]; then
138 | warn "Missing argument "
139 | usage
140 | exit 1
141 | fi
142 | }
143 |
144 | require_base_arg() {
145 | if [ "$BASE" = "" ]; then
146 | warn "Missing argument "
147 | usage
148 | exit 1
149 | fi
150 | }
151 |
152 | require_base_is_on_master() {
153 | if ! git branch --no-color --contains "$BASE" 2>/dev/null \
154 | | sed 's/[* ] //g' \
155 | | grep -q "^$MASTER_BRANCH\$"; then
156 | die "fatal: Given base '$BASE' is not a valid commit on '$MASTER_BRANCH'."
157 | fi
158 | }
159 |
160 | cmd_start() {
161 | DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F
162 | parse_args "$@"
163 | require_version_arg
164 | require_base_arg
165 | require_base_is_on_master
166 |
167 | # sanity checks
168 | require_clean_working_tree
169 |
170 | # fetch remote changes
171 | if flag fetch; then
172 | git fetch -q "$ORIGIN" "$BASE"
173 | fi
174 | require_branch_absent "$BRANCH"
175 |
176 | # create branch
177 | git checkout -b "$BRANCH" "$BASE"
178 |
179 | echo
180 | echo "Summary of actions:"
181 | echo "- A new branch '$BRANCH' was created, based on '$BASE'"
182 | echo "- You are now on branch '$BRANCH'"
183 | echo
184 | }
185 |
--------------------------------------------------------------------------------
/GitFlowExtensions/git-flow-version:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | GITFLOW_VERSION=0.4.2-pre
40 |
41 | usage() {
42 | echo "usage: git flow version"
43 | }
44 |
45 | cmd_default() {
46 | echo "$GITFLOW_VERSION"
47 | }
48 |
49 | cmd_help() {
50 | usage
51 | exit 0
52 | }
53 |
--------------------------------------------------------------------------------
/GitFlowExtensions/gitflow-common:
--------------------------------------------------------------------------------
1 | #
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/git-model
7 | #
8 | # Feel free to contribute to this project at:
9 | # http://github.com/nvie/gitflow
10 | #
11 | # Copyright 2010 Vincent Driessen. All rights reserved.
12 | #
13 | # Redistribution and use in source and binary forms, with or without
14 | # modification, are permitted provided that the following conditions are met:
15 | #
16 | # 1. Redistributions of source code must retain the above copyright notice,
17 | # this list of conditions and the following disclaimer.
18 | #
19 | # 2. Redistributions in binary form must reproduce the above copyright
20 | # notice, this list of conditions and the following disclaimer in the
21 | # documentation and/or other materials provided with the distribution.
22 | #
23 | # THIS SOFTWARE IS PROVIDED BY VINCENT DRIESSEN ``AS IS'' AND ANY EXPRESS OR
24 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26 | # EVENT SHALL VINCENT DRIESSEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 | #
34 | # The views and conclusions contained in the software and documentation are
35 | # those of the authors and should not be interpreted as representing official
36 | # policies, either expressed or implied, of Vincent Driessen.
37 | #
38 |
39 | #
40 | # Common functionality
41 | #
42 |
43 | # shell output
44 | warn() { echo "$@" >&2; }
45 | die() { warn "$@"; exit 1; }
46 |
47 | escape() {
48 | echo "$1" | sed 's/\([\.\$\*]\)/\\\1/g'
49 | }
50 |
51 | # set logic
52 | has() {
53 | local item=$1; shift
54 | echo " $@ " | grep -q " $(escape $item) "
55 | }
56 |
57 | # basic math
58 | min() { [ "$1" -le "$2" ] && echo "$1" || echo "$2"; }
59 | max() { [ "$1" -ge "$2" ] && echo "$1" || echo "$2"; }
60 |
61 | # basic string matching
62 | startswith() { [ "$1" != "${1#$2}" ]; }
63 | endswith() { [ "$1" != "${1%$2}" ]; }
64 |
65 | # convenience functions for checking shFlags flags
66 | flag() { local FLAG; eval FLAG='$FLAGS_'$1; [ $FLAG -eq $FLAGS_TRUE ]; }
67 | noflag() { local FLAG; eval FLAG='$FLAGS_'$1; [ $FLAG -ne $FLAGS_TRUE ]; }
68 |
69 | #
70 | # Git specific common functionality
71 | #
72 |
73 | git_local_branches() { git branch --no-color | sed 's/^[* ] //'; }
74 | git_remote_branches() { git branch -r --no-color | sed 's/^[* ] //'; }
75 | git_all_branches() { ( git branch --no-color; git branch -r --no-color) | sed 's/^[* ] //'; }
76 | git_all_tags() { git tag; }
77 |
78 | git_current_branch() {
79 | git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g'
80 | }
81 |
82 | git_is_clean_working_tree() {
83 | if ! git diff --no-ext-diff --ignore-submodules --quiet --exit-code; then
84 | return 1
85 | elif ! git diff-index --cached --quiet --ignore-submodules HEAD --; then
86 | return 2
87 | else
88 | return 0
89 | fi
90 | }
91 |
92 | git_repo_is_headless() {
93 | ! git rev-parse --quiet --verify HEAD >/dev/null 2>&1
94 | }
95 |
96 | git_local_branch_exists() {
97 | has $1 $(git_local_branches)
98 | }
99 |
100 | git_remote_branch_exists() {
101 | has $1 $(git_remote_branches)
102 | }
103 |
104 | git_branch_exists() {
105 | has $1 $(git_all_branches)
106 | }
107 |
108 | git_tag_exists() {
109 | has $1 $(git_all_tags)
110 | }
111 |
112 | #
113 | # git_compare_branches()
114 | #
115 | # Tests whether branches and their "origin" counterparts have diverged and need
116 | # merging first. It returns error codes to provide more detail, like so:
117 | #
118 | # 0 Branch heads point to the same commit
119 | # 1 First given branch needs fast-forwarding
120 | # 2 Second given branch needs fast-forwarding
121 | # 3 Branch needs a real merge
122 | # 4 There is no merge base, i.e. the branches have no common ancestors
123 | #
124 | git_compare_branches() {
125 | local commit1=$(git rev-parse "$1")
126 | local commit2=$(git rev-parse "$2")
127 | if [ "$commit1" != "$commit2" ]; then
128 | local base=$(git merge-base "$commit1" "$commit2")
129 | if [ $? -ne 0 ]; then
130 | return 4
131 | elif [ "$commit1" = "$base" ]; then
132 | return 1
133 | elif [ "$commit2" = "$base" ]; then
134 | return 2
135 | else
136 | return 3
137 | fi
138 | else
139 | return 0
140 | fi
141 | }
142 |
143 | #
144 | # git_is_branch_merged_into()
145 | #
146 | # Checks whether branch $1 is succesfully merged into $2
147 | #
148 | git_is_branch_merged_into() {
149 | local subject=$1
150 | local base=$2
151 | local all_merges="$(git branch --no-color --contains $subject | sed 's/^[* ] //')"
152 | has $base $all_merges
153 | }
154 |
155 | #
156 | # gitflow specific common functionality
157 | #
158 |
159 | # check if this repo has been inited for gitflow
160 | gitflow_has_master_configured() {
161 | local master=$(git config --get gitflow.branch.master)
162 | [ "$master" != "" ] && git_local_branch_exists "$master"
163 | }
164 |
165 | gitflow_has_develop_configured() {
166 | local develop=$(git config --get gitflow.branch.develop)
167 | [ "$develop" != "" ] && git_local_branch_exists "$develop"
168 | }
169 |
170 | gitflow_has_prefixes_configured() {
171 | git config --get gitflow.prefix.feature >/dev/null 2>&1 && \
172 | git config --get gitflow.prefix.release >/dev/null 2>&1 && \
173 | git config --get gitflow.prefix.hotfix >/dev/null 2>&1 && \
174 | git config --get gitflow.prefix.support >/dev/null 2>&1 && \
175 | git config --get gitflow.prefix.versiontag >/dev/null 2>&1
176 | }
177 |
178 | gitflow_is_initialized() {
179 | gitflow_has_master_configured && \
180 | gitflow_has_develop_configured && \
181 | [ "$(git config --get gitflow.branch.master)" != \
182 | "$(git config --get gitflow.branch.develop)" ] && \
183 | gitflow_has_prefixes_configured
184 | }
185 |
186 | # loading settings that can be overridden using git config
187 | gitflow_load_settings() {
188 | export DOT_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
189 | export MASTER_BRANCH=$(git config --get gitflow.branch.master)
190 | export DEVELOP_BRANCH=$(git config --get gitflow.branch.develop)
191 | export ORIGIN=$(git config --get gitflow.origin || echo origin)
192 | }
193 |
194 | #
195 | # gitflow_resolve_nameprefix
196 | #
197 | # Inputs:
198 | # $1 = name prefix to resolve
199 | # $2 = branch prefix to use
200 | #
201 | # Searches branch names from git_local_branches() to look for a unique
202 | # branch name whose name starts with the given name prefix.
203 | #
204 | # There are multiple exit codes possible:
205 | # 0: The unambiguous full name of the branch is written to stdout
206 | # (success)
207 | # 1: No match is found.
208 | # 2: Multiple matches found. These matches are written to stderr
209 | #
210 | gitflow_resolve_nameprefix() {
211 | local name=$1
212 | local prefix=$2
213 | local matches
214 | local num_matches
215 |
216 | # first, check if there is a perfect match
217 | if git_local_branch_exists "$prefix$name"; then
218 | echo "$name"
219 | return 0
220 | fi
221 |
222 | matches=$(echo "$(git_local_branches)" | grep "^$(escape "$prefix$name")")
223 | num_matches=$(echo "$matches" | wc -l)
224 | if [ -z "$matches" ]; then
225 | # no prefix match, so take it literally
226 | warn "No branch matches prefix '$name'"
227 | return 1
228 | else
229 | if [ $num_matches -eq 1 ]; then
230 | echo "${matches#$prefix}"
231 | return 0
232 | else
233 | # multiple matches, cannot decide
234 | warn "Multiple branches match prefix '$name':"
235 | for match in $matches; do
236 | warn "- $match"
237 | done
238 | return 2
239 | fi
240 | fi
241 | }
242 |
243 | #
244 | # Assertions for use in git-flow subcommands
245 | #
246 |
247 | require_git_repo() {
248 | if ! git rev-parse --git-dir >/dev/null 2>&1; then
249 | die "fatal: Not a git repository"
250 | fi
251 | }
252 |
253 | require_gitflow_initialized() {
254 | if ! gitflow_is_initialized; then
255 | die "fatal: Not a gitflow-enabled repo yet. Please run \"git flow init\" first."
256 | fi
257 | }
258 |
259 | require_clean_working_tree() {
260 | git_is_clean_working_tree
261 | local result=$?
262 | if [ $result -eq 1 ]; then
263 | die "fatal: Working tree contains unstaged changes. Aborting."
264 | fi
265 | if [ $result -eq 2 ]; then
266 | die "fatal: Index contains uncommited changes. Aborting."
267 | fi
268 | }
269 |
270 | require_local_branch() {
271 | if ! git_local_branch_exists $1; then
272 | die "fatal: Local branch '$1' does not exist and is required."
273 | fi
274 | }
275 |
276 | require_remote_branch() {
277 | if ! has $1 $(git_remote_branches); then
278 | die "Remote branch '$1' does not exist and is required."
279 | fi
280 | }
281 |
282 | require_branch() {
283 | if ! has $1 $(git_all_branches); then
284 | die "Branch '$1' does not exist and is required."
285 | fi
286 | }
287 |
288 | require_branch_absent() {
289 | if has $1 $(git_all_branches); then
290 | die "Branch '$1' already exists. Pick another name."
291 | fi
292 | }
293 |
294 | require_tag_absent() {
295 | for tag in $(git_all_tags); do
296 | if [ "$1" = "$tag" ]; then
297 | die "Tag '$1' already exists. Pick another name."
298 | fi
299 | done
300 | }
301 |
302 | require_branches_equal() {
303 | require_local_branch "$1"
304 | require_remote_branch "$2"
305 | git_compare_branches "$1" "$2"
306 | local status=$?
307 | if [ $status -gt 0 ]; then
308 | warn "Branches '$1' and '$2' have diverged."
309 | if [ $status -eq 1 ]; then
310 | die "And branch '$1' may be fast-forwarded."
311 | elif [ $status -eq 2 ]; then
312 | # Warn here, since there is no harm in being ahead
313 | warn "And local branch '$1' is ahead of '$2'."
314 | else
315 | die "Branches need merging first."
316 | fi
317 | fi
318 | }
319 |
--------------------------------------------------------------------------------
/GitFlowExtensions/gitflow-shFlags:
--------------------------------------------------------------------------------
1 | # $Id$
2 | # vim:et:ft=sh:sts=2:sw=2
3 | #
4 | # Copyright 2008 Kate Ward. All Rights Reserved.
5 | # Released under the LGPL (GNU Lesser General Public License)
6 | #
7 | # shFlags -- Advanced command-line flag library for Unix shell scripts.
8 | # http://code.google.com/p/shflags/
9 | #
10 | # Author: kate.ward@forestent.com (Kate Ward)
11 | #
12 | # This module implements something like the google-gflags library available
13 | # from http://code.google.com/p/google-gflags/.
14 | #
15 | # FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags take
16 | # a name, default value, help-string, and optional 'short' name (one-letter
17 | # name). Some flags have other arguments, which are described with the flag.
18 | #
19 | # DEFINE_string: takes any input, and intreprets it as a string.
20 | #
21 | # DEFINE_boolean: typically does not take any argument: say --myflag to set
22 | # FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false.
23 | # Alternately, you can say
24 | # --myflag=true or --myflag=t or --myflag=0 or
25 | # --myflag=false or --myflag=f or --myflag=1
26 | # Passing an option has the same affect as passing the option once.
27 | #
28 | # DEFINE_float: takes an input and intreprets it as a floating point number. As
29 | # shell does not support floats per-se, the input is merely validated as
30 | # being a valid floating point value.
31 | #
32 | # DEFINE_integer: takes an input and intreprets it as an integer.
33 | #
34 | # SPECIAL FLAGS: There are a few flags that have special meaning:
35 | # --help (or -?) prints a list of all the flags in a human-readable fashion
36 | # --flagfile=foo read flags from foo. (not implemented yet)
37 | # -- as in getopt(), terminates flag-processing
38 | #
39 | # EXAMPLE USAGE:
40 | #
41 | # -- begin hello.sh --
42 | # #! /bin/sh
43 | # . ./shflags
44 | # DEFINE_string name 'world' "somebody's name" n
45 | # FLAGS "$@" || exit $?
46 | # eval set -- "${FLAGS_ARGV}"
47 | # echo "Hello, ${FLAGS_name}."
48 | # -- end hello.sh --
49 | #
50 | # $ ./hello.sh -n Kate
51 | # Hello, Kate.
52 | #
53 | # NOTE: Not all systems include a getopt version that supports long flags. On
54 | # these systems, only short flags are recognized.
55 |
56 | #==============================================================================
57 | # shFlags
58 | #
59 | # Shared attributes:
60 | # flags_error: last error message
61 | # flags_return: last return value
62 | #
63 | # __flags_longNames: list of long names for all flags
64 | # __flags_shortNames: list of short names for all flags
65 | # __flags_boolNames: list of boolean flag names
66 | #
67 | # __flags_opts: options parsed by getopt
68 | #
69 | # Per-flag attributes:
70 | # FLAGS_: contains value of flag named 'flag_name'
71 | # __flags__default: the default flag value
72 | # __flags__help: the flag help string
73 | # __flags__short: the flag short name
74 | # __flags__type: the flag type
75 | #
76 | # Notes:
77 | # - lists of strings are space separated, and a null value is the '~' char.
78 |
79 | # return if FLAGS already loaded
80 | [ -n "${FLAGS_VERSION:-}" ] && return 0
81 | FLAGS_VERSION='1.0.3'
82 |
83 | # return values
84 | FLAGS_TRUE=0
85 | FLAGS_FALSE=1
86 | FLAGS_ERROR=2
87 |
88 | # reserved flag names
89 | FLAGS_RESERVED='ARGC ARGV ERROR FALSE HELP PARENT RESERVED TRUE VERSION'
90 |
91 | _flags_debug() { echo "flags:DEBUG $@" >&2; }
92 | _flags_warn() { echo "flags:WARN $@" >&2; }
93 | _flags_error() { echo "flags:ERROR $@" >&2; }
94 | _flags_fatal() { echo "flags:FATAL $@" >&2; }
95 |
96 | # specific shell checks
97 | if [ -n "${ZSH_VERSION:-}" ]; then
98 | setopt |grep "^shwordsplit$" >/dev/null
99 | if [ $? -ne ${FLAGS_TRUE} ]; then
100 | _flags_fatal 'zsh shwordsplit option is required for proper zsh operation'
101 | exit ${FLAGS_ERROR}
102 | fi
103 | if [ -z "${FLAGS_PARENT:-}" ]; then
104 | _flags_fatal "zsh does not pass \$0 through properly. please declare' \
105 | \"FLAGS_PARENT=\$0\" before calling shFlags"
106 | exit ${FLAGS_ERROR}
107 | fi
108 | fi
109 |
110 | #
111 | # constants
112 | #
113 |
114 | # getopt version
115 | __FLAGS_GETOPT_VERS_STD=0
116 | __FLAGS_GETOPT_VERS_ENH=1
117 | __FLAGS_GETOPT_VERS_BSD=2
118 |
119 | getopt >/dev/null 2>&1
120 | case $? in
121 | 0) __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} ;; # bsd getopt
122 | 2)
123 | # TODO(kward): look into '-T' option to test the internal getopt() version
124 | if [ "`getopt --version`" = '-- ' ]; then
125 | __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD}
126 | else
127 | __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_ENH}
128 | fi
129 | ;;
130 | *)
131 | _flags_fatal 'unable to determine getopt version'
132 | exit ${FLAGS_ERROR}
133 | ;;
134 | esac
135 |
136 | # getopt optstring lengths
137 | __FLAGS_OPTSTR_SHORT=0
138 | __FLAGS_OPTSTR_LONG=1
139 |
140 | __FLAGS_NULL='~'
141 |
142 | # flag info strings
143 | __FLAGS_INFO_DEFAULT='default'
144 | __FLAGS_INFO_HELP='help'
145 | __FLAGS_INFO_SHORT='short'
146 | __FLAGS_INFO_TYPE='type'
147 |
148 | # flag lengths
149 | __FLAGS_LEN_SHORT=0
150 | __FLAGS_LEN_LONG=1
151 |
152 | # flag types
153 | __FLAGS_TYPE_NONE=0
154 | __FLAGS_TYPE_BOOLEAN=1
155 | __FLAGS_TYPE_FLOAT=2
156 | __FLAGS_TYPE_INTEGER=3
157 | __FLAGS_TYPE_STRING=4
158 |
159 | # set the constants readonly
160 | __flags_constants=`set |awk -F= '/^FLAGS_/ || /^__FLAGS_/ {print $1}'`
161 | for __flags_const in ${__flags_constants}; do
162 | # skip certain flags
163 | case ${__flags_const} in
164 | FLAGS_HELP) continue ;;
165 | FLAGS_PARENT) continue ;;
166 | esac
167 | # set flag readonly
168 | if [ -z "${ZSH_VERSION:-}" ]; then
169 | readonly ${__flags_const}
170 | else # handle zsh
171 | case ${ZSH_VERSION} in
172 | [123].*) readonly ${__flags_const} ;;
173 | *) readonly -g ${__flags_const} ;; # declare readonly constants globally
174 | esac
175 | fi
176 | done
177 | unset __flags_const __flags_constants
178 |
179 | #
180 | # internal variables
181 | #
182 |
183 | __flags_boolNames=' ' # space separated list of boolean flag names
184 | __flags_longNames=' ' # space separated list of long flag names
185 | __flags_shortNames=' ' # space separated list of short flag names
186 |
187 | __flags_columns='' # screen width in columns
188 | __flags_opts='' # temporary storage for parsed getopt flags
189 |
190 | #------------------------------------------------------------------------------
191 | # private functions
192 | #
193 |
194 | # Define a flag.
195 | #
196 | # Calling this function will define the following info variables for the
197 | # specified flag:
198 | # FLAGS_flagname - the name for this flag (based upon the long flag name)
199 | # __flags__default - the default value
200 | # __flags_flagname_help - the help string
201 | # __flags_flagname_short - the single letter alias
202 | # __flags_flagname_type - the type of flag (one of __FLAGS_TYPE_*)
203 | #
204 | # Args:
205 | # _flags__type: integer: internal type of flag (__FLAGS_TYPE_*)
206 | # _flags__name: string: long flag name
207 | # _flags__default: default flag value
208 | # _flags__help: string: help string
209 | # _flags__short: string: (optional) short flag name
210 | # Returns:
211 | # integer: success of operation, or error
212 | _flags_define()
213 | {
214 | if [ $# -lt 4 ]; then
215 | flags_error='DEFINE error: too few arguments'
216 | flags_return=${FLAGS_ERROR}
217 | _flags_error "${flags_error}"
218 | return ${flags_return}
219 | fi
220 |
221 | _flags_type_=$1
222 | _flags_name_=$2
223 | _flags_default_=$3
224 | _flags_help_=$4
225 | _flags_short_=${5:-${__FLAGS_NULL}}
226 |
227 | _flags_return_=${FLAGS_TRUE}
228 |
229 | # TODO(kward): check for validity of the flag name (e.g. dashes)
230 |
231 | # check whether the flag name is reserved
232 | echo " ${FLAGS_RESERVED} " |grep " ${_flags_name_} " >/dev/null
233 | if [ $? -eq 0 ]; then
234 | flags_error="flag name (${_flags_name_}) is reserved"
235 | _flags_return_=${FLAGS_ERROR}
236 | fi
237 |
238 | # require short option for getopt that don't support long options
239 | if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
240 | -a ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} \
241 | -a "${_flags_short_}" = "${__FLAGS_NULL}" ]
242 | then
243 | flags_error="short flag required for (${_flags_name_}) on this platform"
244 | _flags_return_=${FLAGS_ERROR}
245 | fi
246 |
247 | # check for existing long name definition
248 | if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
249 | if _flags_itemInList "${_flags_name_}" \
250 | ${__flags_longNames} ${__flags_boolNames}
251 | then
252 | flags_error="flag name ([no]${_flags_name_}) already defined"
253 | _flags_warn "${flags_error}"
254 | _flags_return_=${FLAGS_FALSE}
255 | fi
256 | fi
257 |
258 | # check for existing short name definition
259 | if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
260 | -a "${_flags_short_}" != "${__FLAGS_NULL}" ]
261 | then
262 | if _flags_itemInList "${_flags_short_}" ${__flags_shortNames}; then
263 | flags_error="flag short name (${_flags_short_}) already defined"
264 | _flags_warn "${flags_error}"
265 | _flags_return_=${FLAGS_FALSE}
266 | fi
267 | fi
268 |
269 | # handle default value. note, on several occasions the 'if' portion of an
270 | # if/then/else contains just a ':' which does nothing. a binary reversal via
271 | # '!' is not done because it does not work on all shells.
272 | if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
273 | case ${_flags_type_} in
274 | ${__FLAGS_TYPE_BOOLEAN})
275 | if _flags_validateBoolean "${_flags_default_}"; then
276 | case ${_flags_default_} in
277 | true|t|0) _flags_default_=${FLAGS_TRUE} ;;
278 | false|f|1) _flags_default_=${FLAGS_FALSE} ;;
279 | esac
280 | else
281 | flags_error="invalid default flag value '${_flags_default_}'"
282 | _flags_return_=${FLAGS_ERROR}
283 | fi
284 | ;;
285 |
286 | ${__FLAGS_TYPE_FLOAT})
287 | if _flags_validateFloat "${_flags_default_}"; then
288 | :
289 | else
290 | flags_error="invalid default flag value '${_flags_default_}'"
291 | _flags_return_=${FLAGS_ERROR}
292 | fi
293 | ;;
294 |
295 | ${__FLAGS_TYPE_INTEGER})
296 | if _flags_validateInteger "${_flags_default_}"; then
297 | :
298 | else
299 | flags_error="invalid default flag value '${_flags_default_}'"
300 | _flags_return_=${FLAGS_ERROR}
301 | fi
302 | ;;
303 |
304 | ${__FLAGS_TYPE_STRING}) ;; # everything in shell is a valid string
305 |
306 | *)
307 | flags_error="unrecognized flag type '${_flags_type_}'"
308 | _flags_return_=${FLAGS_ERROR}
309 | ;;
310 | esac
311 | fi
312 |
313 | if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
314 | # store flag information
315 | eval "FLAGS_${_flags_name_}='${_flags_default_}'"
316 | eval "__flags_${_flags_name_}_${__FLAGS_INFO_TYPE}=${_flags_type_}"
317 | eval "__flags_${_flags_name_}_${__FLAGS_INFO_DEFAULT}=\
318 | \"${_flags_default_}\""
319 | eval "__flags_${_flags_name_}_${__FLAGS_INFO_HELP}=\"${_flags_help_}\""
320 | eval "__flags_${_flags_name_}_${__FLAGS_INFO_SHORT}='${_flags_short_}'"
321 |
322 | # append flag name(s) to list of names
323 | __flags_longNames="${__flags_longNames}${_flags_name_} "
324 | __flags_shortNames="${__flags_shortNames}${_flags_short_} "
325 | [ ${_flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] && \
326 | __flags_boolNames="${__flags_boolNames}no${_flags_name_} "
327 | fi
328 |
329 | flags_return=${_flags_return_}
330 | unset _flags_default_ _flags_help_ _flags_name_ _flags_return_ _flags_short_ \
331 | _flags_type_
332 | [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
333 | return ${flags_return}
334 | }
335 |
336 | # Return valid getopt options using currently defined list of long options.
337 | #
338 | # This function builds a proper getopt option string for short (and long)
339 | # options, using the current list of long options for reference.
340 | #
341 | # Args:
342 | # _flags_optStr: integer: option string type (__FLAGS_OPTSTR_*)
343 | # Output:
344 | # string: generated option string for getopt
345 | # Returns:
346 | # boolean: success of operation (always returns True)
347 | _flags_genOptStr()
348 | {
349 | _flags_optStrType_=$1
350 |
351 | _flags_opts_=''
352 |
353 | for _flags_flag_ in ${__flags_longNames}; do
354 | _flags_type_=`_flags_getFlagInfo ${_flags_flag_} ${__FLAGS_INFO_TYPE}`
355 | case ${_flags_optStrType_} in
356 | ${__FLAGS_OPTSTR_SHORT})
357 | _flags_shortName_=`_flags_getFlagInfo \
358 | ${_flags_flag_} ${__FLAGS_INFO_SHORT}`
359 | if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then
360 | _flags_opts_="${_flags_opts_}${_flags_shortName_}"
361 | # getopt needs a trailing ':' to indicate a required argument
362 | [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \
363 | _flags_opts_="${_flags_opts_}:"
364 | fi
365 | ;;
366 |
367 | ${__FLAGS_OPTSTR_LONG})
368 | _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_flag_}"
369 | # getopt needs a trailing ':' to indicate a required argument
370 | [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \
371 | _flags_opts_="${_flags_opts_}:"
372 | ;;
373 | esac
374 | done
375 |
376 | echo "${_flags_opts_}"
377 | unset _flags_flag_ _flags_opts_ _flags_optStrType_ _flags_shortName_ \
378 | _flags_type_
379 | return ${FLAGS_TRUE}
380 | }
381 |
382 | # Returns flag details based on a flag name and flag info.
383 | #
384 | # Args:
385 | # string: long flag name
386 | # string: flag info (see the _flags_define function for valid info types)
387 | # Output:
388 | # string: value of dereferenced flag variable
389 | # Returns:
390 | # integer: one of FLAGS_{TRUE|FALSE|ERROR}
391 | _flags_getFlagInfo()
392 | {
393 | _flags_name_=$1
394 | _flags_info_=$2
395 |
396 | _flags_nameVar_="__flags_${_flags_name_}_${_flags_info_}"
397 | _flags_strToEval_="_flags_value_=\"\${${_flags_nameVar_}:-}\""
398 | eval "${_flags_strToEval_}"
399 | if [ -n "${_flags_value_}" ]; then
400 | flags_return=${FLAGS_TRUE}
401 | else
402 | # see if the _flags_name_ variable is a string as strings can be empty...
403 | # note: the DRY principle would say to have this function call itself for
404 | # the next three lines, but doing so results in an infinite loop as an
405 | # invalid _flags_name_ will also not have the associated _type variable.
406 | # Because it doesn't (it will evaluate to an empty string) the logic will
407 | # try to find the _type variable of the _type variable, and so on. Not so
408 | # good ;-)
409 | _flags_typeVar_="__flags_${_flags_name_}_${__FLAGS_INFO_TYPE}"
410 | _flags_strToEval_="_flags_type_=\"\${${_flags_typeVar_}:-}\""
411 | eval "${_flags_strToEval_}"
412 | if [ "${_flags_type_}" = "${__FLAGS_TYPE_STRING}" ]; then
413 | flags_return=${FLAGS_TRUE}
414 | else
415 | flags_return=${FLAGS_ERROR}
416 | flags_error="invalid flag name (${_flags_nameVar_})"
417 | fi
418 | fi
419 |
420 | echo "${_flags_value_}"
421 | unset _flags_info_ _flags_name_ _flags_strToEval_ _flags_type_ _flags_value_ \
422 | _flags_nameVar_ _flags_typeVar_
423 | [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
424 | return ${flags_return}
425 | }
426 |
427 | # check for presense of item in a list. passed a string (e.g. 'abc'), this
428 | # function will determine if the string is present in the list of strings (e.g.
429 | # ' foo bar abc ').
430 | #
431 | # Args:
432 | # _flags__str: string: string to search for in a list of strings
433 | # unnamed: list: list of strings
434 | # Returns:
435 | # boolean: true if item is in the list
436 | _flags_itemInList()
437 | {
438 | _flags_str_=$1
439 | shift
440 |
441 | echo " ${*:-} " |grep " ${_flags_str_} " >/dev/null
442 | if [ $? -eq 0 ]; then
443 | flags_return=${FLAGS_TRUE}
444 | else
445 | flags_return=${FLAGS_FALSE}
446 | fi
447 |
448 | unset _flags_str_
449 | return ${flags_return}
450 | }
451 |
452 | # Returns the width of the current screen.
453 | #
454 | # Output:
455 | # integer: width in columns of the current screen.
456 | _flags_columns()
457 | {
458 | if [ -z "${__flags_columns}" ]; then
459 | # determine the value and store it
460 | if eval stty size >/dev/null 2>&1; then
461 | # stty size worked :-)
462 | set -- `stty size`
463 | __flags_columns=$2
464 | elif eval tput cols >/dev/null 2>&1; then
465 | set -- `tput cols`
466 | __flags_columns=$1
467 | else
468 | __flags_columns=80 # default terminal width
469 | fi
470 | fi
471 | echo ${__flags_columns}
472 | }
473 |
474 | # Validate a boolean.
475 | #
476 | # Args:
477 | # _flags__bool: boolean: value to validate
478 | # Returns:
479 | # bool: true if the value is a valid boolean
480 | _flags_validateBoolean()
481 | {
482 | _flags_bool_=$1
483 |
484 | flags_return=${FLAGS_TRUE}
485 | case "${_flags_bool_}" in
486 | true|t|0) ;;
487 | false|f|1) ;;
488 | *) flags_return=${FLAGS_FALSE} ;;
489 | esac
490 |
491 | unset _flags_bool_
492 | return ${flags_return}
493 | }
494 |
495 | # Validate a float.
496 | #
497 | # Args:
498 | # _flags__float: float: value to validate
499 | # Returns:
500 | # bool: true if the value is a valid float
501 | _flags_validateFloat()
502 | {
503 | _flags_float_=$1
504 |
505 | if _flags_validateInteger ${_flags_float_}; then
506 | flags_return=${FLAGS_TRUE}
507 | else
508 | flags_return=${FLAGS_TRUE}
509 | case ${_flags_float_} in
510 | -*) # negative floats
511 | _flags_test_=`expr "${_flags_float_}" : '\(-[0-9][0-9]*\.[0-9][0-9]*\)'`
512 | ;;
513 | *) # positive floats
514 | _flags_test_=`expr "${_flags_float_}" : '\([0-9][0-9]*\.[0-9][0-9]*\)'`
515 | ;;
516 | esac
517 | [ "${_flags_test_}" != "${_flags_float_}" ] && flags_return=${FLAGS_FALSE}
518 | fi
519 |
520 | unset _flags_float_ _flags_test_
521 | return ${flags_return}
522 | }
523 |
524 | # Validate an integer.
525 | #
526 | # Args:
527 | # _flags__integer: interger: value to validate
528 | # Returns:
529 | # bool: true if the value is a valid integer
530 | _flags_validateInteger()
531 | {
532 | _flags_int_=$1
533 |
534 | flags_return=${FLAGS_TRUE}
535 | case ${_flags_int_} in
536 | -*) # negative ints
537 | _flags_test_=`expr "${_flags_int_}" : '\(-[0-9][0-9]*\)'`
538 | ;;
539 | *) # positive ints
540 | _flags_test_=`expr "${_flags_int_}" : '\([0-9][0-9]*\)'`
541 | ;;
542 | esac
543 | [ "${_flags_test_}" != "${_flags_int_}" ] && flags_return=${FLAGS_FALSE}
544 |
545 | unset _flags_int_ _flags_test_
546 | return ${flags_return}
547 | }
548 |
549 | # Parse command-line options using the standard getopt.
550 | #
551 | # Note: the flag options are passed around in the global __flags_opts so that
552 | # the formatting is not lost due to shell parsing and such.
553 | #
554 | # Args:
555 | # @: varies: command-line options to parse
556 | # Returns:
557 | # integer: a FLAGS success condition
558 | _flags_getoptStandard()
559 | {
560 | flags_return=${FLAGS_TRUE}
561 | _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
562 |
563 | # check for spaces in passed options
564 | for _flags_opt_ in "$@"; do
565 | # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
566 | _flags_match_=`echo "x${_flags_opt_}x" |sed 's/ //g'`
567 | if [ "${_flags_match_}" != "x${_flags_opt_}x" ]; then
568 | flags_error='the available getopt does not support spaces in options'
569 | flags_return=${FLAGS_ERROR}
570 | break
571 | fi
572 | done
573 |
574 | if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
575 | __flags_opts=`getopt ${_flags_shortOpts_} $@ 2>&1`
576 | _flags_rtrn_=$?
577 | if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
578 | _flags_warn "${__flags_opts}"
579 | flags_error='unable to parse provided options with getopt.'
580 | flags_return=${FLAGS_ERROR}
581 | fi
582 | fi
583 |
584 | unset _flags_match_ _flags_opt_ _flags_rtrn_ _flags_shortOpts_
585 | return ${flags_return}
586 | }
587 |
588 | # Parse command-line options using the enhanced getopt.
589 | #
590 | # Note: the flag options are passed around in the global __flags_opts so that
591 | # the formatting is not lost due to shell parsing and such.
592 | #
593 | # Args:
594 | # @: varies: command-line options to parse
595 | # Returns:
596 | # integer: a FLAGS success condition
597 | _flags_getoptEnhanced()
598 | {
599 | flags_return=${FLAGS_TRUE}
600 | _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
601 | _flags_boolOpts_=`echo "${__flags_boolNames}" \
602 | |sed 's/^ *//;s/ *$//;s/ /,/g'`
603 | _flags_longOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_LONG}`
604 |
605 | __flags_opts=`getopt \
606 | -o ${_flags_shortOpts_} \
607 | -l "${_flags_longOpts_},${_flags_boolOpts_}" \
608 | -- "$@" 2>&1`
609 | _flags_rtrn_=$?
610 | if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
611 | _flags_warn "${__flags_opts}"
612 | flags_error='unable to parse provided options with getopt.'
613 | flags_return=${FLAGS_ERROR}
614 | fi
615 |
616 | unset _flags_boolOpts_ _flags_longOpts_ _flags_rtrn_ _flags_shortOpts_
617 | return ${flags_return}
618 | }
619 |
620 | # Dynamically parse a getopt result and set appropriate variables.
621 | #
622 | # This function does the actual conversion of getopt output and runs it through
623 | # the standard case structure for parsing. The case structure is actually quite
624 | # dynamic to support any number of flags.
625 | #
626 | # Args:
627 | # argc: int: original command-line argument count
628 | # @: varies: output from getopt parsing
629 | # Returns:
630 | # integer: a FLAGS success condition
631 | _flags_parseGetopt()
632 | {
633 | _flags_argc_=$1
634 | shift
635 |
636 | flags_return=${FLAGS_TRUE}
637 |
638 | if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then
639 | set -- $@
640 | else
641 | # note the quotes around the `$@' -- they are essential!
642 | eval set -- "$@"
643 | fi
644 |
645 | # provide user with number of arguments to shift by later
646 | # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not
647 | # properly give user access to non-flag arguments mixed in between flag
648 | # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only
649 | # for backwards compatibility reasons.
650 | FLAGS_ARGC=`expr $# - 1 - ${_flags_argc_}`
651 |
652 | # handle options. note options with values must do an additional shift
653 | while true; do
654 | _flags_opt_=$1
655 | _flags_arg_=${2:-}
656 | _flags_type_=${__FLAGS_TYPE_NONE}
657 | _flags_name_=''
658 |
659 | # determine long flag name
660 | case "${_flags_opt_}" in
661 | --) shift; break ;; # discontinue option parsing
662 |
663 | --*) # long option
664 | _flags_opt_=`expr "${_flags_opt_}" : '--\(.*\)'`
665 | _flags_len_=${__FLAGS_LEN_LONG}
666 | if _flags_itemInList "${_flags_opt_}" ${__flags_longNames}; then
667 | _flags_name_=${_flags_opt_}
668 | else
669 | # check for negated long boolean version
670 | if _flags_itemInList "${_flags_opt_}" ${__flags_boolNames}; then
671 | _flags_name_=`expr "${_flags_opt_}" : 'no\(.*\)'`
672 | _flags_type_=${__FLAGS_TYPE_BOOLEAN}
673 | _flags_arg_=${__FLAGS_NULL}
674 | fi
675 | fi
676 | ;;
677 |
678 | -*) # short option
679 | _flags_opt_=`expr "${_flags_opt_}" : '-\(.*\)'`
680 | _flags_len_=${__FLAGS_LEN_SHORT}
681 | if _flags_itemInList "${_flags_opt_}" ${__flags_shortNames}; then
682 | # yes. match short name to long name. note purposeful off-by-one
683 | # (too high) with awk calculations.
684 | _flags_pos_=`echo "${__flags_shortNames}" \
685 | |awk 'BEGIN{RS=" ";rn=0}$0==e{rn=NR}END{print rn}' \
686 | e=${_flags_opt_}`
687 | _flags_name_=`echo "${__flags_longNames}" \
688 | |awk 'BEGIN{RS=" "}rn==NR{print $0}' rn="${_flags_pos_}"`
689 | fi
690 | ;;
691 | esac
692 |
693 | # die if the flag was unrecognized
694 | if [ -z "${_flags_name_}" ]; then
695 | flags_error="unrecognized option (${_flags_opt_})"
696 | flags_return=${FLAGS_ERROR}
697 | break
698 | fi
699 |
700 | # set new flag value
701 | [ ${_flags_type_} -eq ${__FLAGS_TYPE_NONE} ] && \
702 | _flags_type_=`_flags_getFlagInfo \
703 | "${_flags_name_}" ${__FLAGS_INFO_TYPE}`
704 | case ${_flags_type_} in
705 | ${__FLAGS_TYPE_BOOLEAN})
706 | if [ ${_flags_len_} -eq ${__FLAGS_LEN_LONG} ]; then
707 | if [ "${_flags_arg_}" != "${__FLAGS_NULL}" ]; then
708 | eval "FLAGS_${_flags_name_}=${FLAGS_TRUE}"
709 | else
710 | eval "FLAGS_${_flags_name_}=${FLAGS_FALSE}"
711 | fi
712 | else
713 | _flags_strToEval_="_flags_val_=\
714 | \${__flags_${_flags_name_}_${__FLAGS_INFO_DEFAULT}}"
715 | eval "${_flags_strToEval_}"
716 | if [ ${_flags_val_} -eq ${FLAGS_FALSE} ]; then
717 | eval "FLAGS_${_flags_name_}=${FLAGS_TRUE}"
718 | else
719 | eval "FLAGS_${_flags_name_}=${FLAGS_FALSE}"
720 | fi
721 | fi
722 | ;;
723 |
724 | ${__FLAGS_TYPE_FLOAT})
725 | if _flags_validateFloat "${_flags_arg_}"; then
726 | eval "FLAGS_${_flags_name_}='${_flags_arg_}'"
727 | else
728 | flags_error="invalid float value (${_flags_arg_})"
729 | flags_return=${FLAGS_ERROR}
730 | break
731 | fi
732 | ;;
733 |
734 | ${__FLAGS_TYPE_INTEGER})
735 | if _flags_validateInteger "${_flags_arg_}"; then
736 | eval "FLAGS_${_flags_name_}='${_flags_arg_}'"
737 | else
738 | flags_error="invalid integer value (${_flags_arg_})"
739 | flags_return=${FLAGS_ERROR}
740 | break
741 | fi
742 | ;;
743 |
744 | ${__FLAGS_TYPE_STRING})
745 | eval "FLAGS_${_flags_name_}='${_flags_arg_}'"
746 | ;;
747 | esac
748 |
749 | # handle special case help flag
750 | if [ "${_flags_name_}" = 'help' ]; then
751 | if [ ${FLAGS_help} -eq ${FLAGS_TRUE} ]; then
752 | flags_help
753 | flags_error='help requested'
754 | flags_return=${FLAGS_FALSE}
755 | break
756 | fi
757 | fi
758 |
759 | # shift the option and non-boolean arguements out.
760 | shift
761 | [ ${_flags_type_} != ${__FLAGS_TYPE_BOOLEAN} ] && shift
762 | done
763 |
764 | # give user back non-flag arguments
765 | FLAGS_ARGV=''
766 | while [ $# -gt 0 ]; do
767 | FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'"
768 | shift
769 | done
770 |
771 | unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \
772 | _flags_strToEval_ _flags_type_ _flags_val_
773 | return ${flags_return}
774 | }
775 |
776 | #------------------------------------------------------------------------------
777 | # public functions
778 | #
779 |
780 | # A basic boolean flag. Boolean flags do not take any arguments, and their
781 | # value is either 1 (false) or 0 (true). For long flags, the false value is
782 | # specified on the command line by prepending the word 'no'. With short flags,
783 | # the presense of the flag toggles the current value between true and false.
784 | # Specifying a short boolean flag twice on the command results in returning the
785 | # value back to the default value.
786 | #
787 | # A default value is required for boolean flags.
788 | #
789 | # For example, lets say a Boolean flag was created whose long name was 'update'
790 | # and whose short name was 'x', and the default value was 'false'. This flag
791 | # could be explicitly set to 'true' with '--update' or by '-x', and it could be
792 | # explicitly set to 'false' with '--noupdate'.
793 | DEFINE_boolean() { _flags_define ${__FLAGS_TYPE_BOOLEAN} "$@"; }
794 |
795 | # Other basic flags.
796 | DEFINE_float() { _flags_define ${__FLAGS_TYPE_FLOAT} "$@"; }
797 | DEFINE_integer() { _flags_define ${__FLAGS_TYPE_INTEGER} "$@"; }
798 | DEFINE_string() { _flags_define ${__FLAGS_TYPE_STRING} "$@"; }
799 |
800 | # Parse the flags.
801 | #
802 | # Args:
803 | # unnamed: list: command-line flags to parse
804 | # Returns:
805 | # integer: success of operation, or error
806 | FLAGS()
807 | {
808 | # define a standard 'help' flag if one isn't already defined
809 | [ -z "${__flags_help_type:-}" ] && \
810 | DEFINE_boolean 'help' false 'show this help' 'h'
811 |
812 | # parse options
813 | if [ $# -gt 0 ]; then
814 | if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then
815 | _flags_getoptStandard "$@"
816 | else
817 | _flags_getoptEnhanced "$@"
818 | fi
819 | flags_return=$?
820 | else
821 | # nothing passed; won't bother running getopt
822 | __flags_opts='--'
823 | flags_return=${FLAGS_TRUE}
824 | fi
825 |
826 | if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
827 | _flags_parseGetopt $# "${__flags_opts}"
828 | flags_return=$?
829 | fi
830 |
831 | [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_fatal "${flags_error}"
832 | return ${flags_return}
833 | }
834 |
835 | # This is a helper function for determining the `getopt` version for platforms
836 | # where the detection isn't working. It simply outputs debug information that
837 | # can be included in a bug report.
838 | #
839 | # Args:
840 | # none
841 | # Output:
842 | # debug info that can be included in a bug report
843 | # Returns:
844 | # nothing
845 | flags_getoptInfo()
846 | {
847 | # platform info
848 | _flags_debug "uname -a: `uname -a`"
849 | _flags_debug "PATH: ${PATH}"
850 |
851 | # shell info
852 | if [ -n "${BASH_VERSION:-}" ]; then
853 | _flags_debug 'shell: bash'
854 | _flags_debug "BASH_VERSION: ${BASH_VERSION}"
855 | elif [ -n "${ZSH_VERSION:-}" ]; then
856 | _flags_debug 'shell: zsh'
857 | _flags_debug "ZSH_VERSION: ${ZSH_VERSION}"
858 | fi
859 |
860 | # getopt info
861 | getopt >/dev/null
862 | _flags_getoptReturn=$?
863 | _flags_debug "getopt return: ${_flags_getoptReturn}"
864 | _flags_debug "getopt --version: `getopt --version 2>&1`"
865 |
866 | unset _flags_getoptReturn
867 | }
868 |
869 | # Returns whether the detected getopt version is the enhanced version.
870 | #
871 | # Args:
872 | # none
873 | # Output:
874 | # none
875 | # Returns:
876 | # bool: true if getopt is the enhanced version
877 | flags_getoptIsEnh()
878 | {
879 | test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH}
880 | }
881 |
882 | # Returns whether the detected getopt version is the standard version.
883 | #
884 | # Args:
885 | # none
886 | # Returns:
887 | # bool: true if getopt is the standard version
888 | flags_getoptIsStd()
889 | {
890 | test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD}
891 | }
892 |
893 | # This is effectively a 'usage()' function. It prints usage information and
894 | # exits the program with ${FLAGS_FALSE} if it is ever found in the command line
895 | # arguments. Note this function can be overridden so other apps can define
896 | # their own --help flag, replacing this one, if they want.
897 | #
898 | # Args:
899 | # none
900 | # Returns:
901 | # integer: success of operation (always returns true)
902 | flags_help()
903 | {
904 | if [ -n "${FLAGS_HELP:-}" ]; then
905 | echo "${FLAGS_HELP}" >&2
906 | else
907 | echo "USAGE: ${FLAGS_PARENT:-$0} [flags] args" >&2
908 | fi
909 | if [ -n "${__flags_longNames}" ]; then
910 | echo 'flags:' >&2
911 | for flags_name_ in ${__flags_longNames}; do
912 | flags_flagStr_=''
913 | flags_boolStr_=''
914 |
915 | flags_default_=`_flags_getFlagInfo \
916 | "${flags_name_}" ${__FLAGS_INFO_DEFAULT}`
917 | flags_help_=`_flags_getFlagInfo \
918 | "${flags_name_}" ${__FLAGS_INFO_HELP}`
919 | flags_short_=`_flags_getFlagInfo \
920 | "${flags_name_}" ${__FLAGS_INFO_SHORT}`
921 | flags_type_=`_flags_getFlagInfo \
922 | "${flags_name_}" ${__FLAGS_INFO_TYPE}`
923 |
924 | [ "${flags_short_}" != "${__FLAGS_NULL}" ] \
925 | && flags_flagStr_="-${flags_short_}"
926 |
927 | if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH} ]; then
928 | [ "${flags_short_}" != "${__FLAGS_NULL}" ] \
929 | && flags_flagStr_="${flags_flagStr_},"
930 | [ ${flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] \
931 | && flags_boolStr_='[no]'
932 | flags_flagStr_="${flags_flagStr_}--${flags_boolStr_}${flags_name_}:"
933 | fi
934 |
935 | case ${flags_type_} in
936 | ${__FLAGS_TYPE_BOOLEAN})
937 | if [ ${flags_default_} -eq ${FLAGS_TRUE} ]; then
938 | flags_defaultStr_='true'
939 | else
940 | flags_defaultStr_='false'
941 | fi
942 | ;;
943 | ${__FLAGS_TYPE_FLOAT}|${__FLAGS_TYPE_INTEGER})
944 | flags_defaultStr_=${flags_default_} ;;
945 | ${__FLAGS_TYPE_STRING}) flags_defaultStr_="'${flags_default_}'" ;;
946 | esac
947 | flags_defaultStr_="(default: ${flags_defaultStr_})"
948 |
949 | flags_helpStr_=" ${flags_flagStr_} ${flags_help_} ${flags_defaultStr_}"
950 | flags_helpStrLen_=`expr "${flags_helpStr_}" : '.*'`
951 | flags_columns_=`_flags_columns`
952 | if [ ${flags_helpStrLen_} -lt ${flags_columns_} ]; then
953 | echo "${flags_helpStr_}" >&2
954 | else
955 | echo " ${flags_flagStr_} ${flags_help_}" >&2
956 | # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
957 | # because it doesn't like empty strings when used in this manner.
958 | flags_emptyStr_="`echo \"x${flags_flagStr_}x\" \
959 | |awk '{printf "%"length($0)-2"s", ""}'`"
960 | flags_helpStr_=" ${flags_emptyStr_} ${flags_defaultStr_}"
961 | flags_helpStrLen_=`expr "${flags_helpStr_}" : '.*'`
962 | if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD} \
963 | -o ${flags_helpStrLen_} -lt ${flags_columns_} ]; then
964 | # indented to match help string
965 | echo "${flags_helpStr_}" >&2
966 | else
967 | # indented four from left to allow for longer defaults as long flag
968 | # names might be used too, making things too long
969 | echo " ${flags_defaultStr_}" >&2
970 | fi
971 | fi
972 | done
973 | fi
974 |
975 | unset flags_boolStr_ flags_default_ flags_defaultStr_ flags_emptyStr_ \
976 | flags_flagStr_ flags_help_ flags_helpStr flags_helpStrLen flags_name_ \
977 | flags_columns_ flags_short_ flags_type_
978 | return ${FLAGS_TRUE}
979 | }
980 |
981 | # Reset shflags back to an uninitialized state.
982 | #
983 | # Args:
984 | # none
985 | # Returns:
986 | # nothing
987 | flags_reset()
988 | {
989 | for flags_name_ in ${__flags_longNames}; do
990 | flags_strToEval_="unset FLAGS_${flags_name_}"
991 | for flags_type_ in \
992 | ${__FLAGS_INFO_DEFAULT} \
993 | ${__FLAGS_INFO_HELP} \
994 | ${__FLAGS_INFO_SHORT} \
995 | ${__FLAGS_INFO_TYPE}
996 | do
997 | flags_strToEval_=\
998 | "${flags_strToEval_} __flags_${flags_name_}_${flags_type_}"
999 | done
1000 | eval ${flags_strToEval_}
1001 | done
1002 |
1003 | # reset internal variables
1004 | __flags_boolNames=' '
1005 | __flags_longNames=' '
1006 | __flags_shortNames=' '
1007 |
1008 | unset flags_name_ flags_type_ flags_strToEval_
1009 | }
1010 |
--------------------------------------------------------------------------------
/One-Page GitFlow-Cheatsheet.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhoerr/posh-gitflow/d21bb6f66cbb05b9b83e03a4a422d3fb0a208194/One-Page GitFlow-Cheatsheet.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | posh-gitflow
2 | ============
3 |
4 | Adds PowerShell extensions to [posh-git](https://github.com/dahlbyk/posh-git) to enable GitFlow source code management. Posh-git is a PowerShell environment for git that can be installed by itself, or bundled with GitHub for Windows as the 'Git Shell.'
5 |
6 | **Description**
7 |
8 | This script adds GitFlow support to [posh-git](https://github.com/dahlbyk/posh-git). The GitFlow concept and core scripts were developed by nvie. The cheatsheet and script offered here is a modified version of one developed by Howard van Rooijen and documented in a fantastic series of blog posts about using GitHub with TeamCity.
9 |
10 | **Installation**
11 |
12 | 1. Open a posh-git shell. (If using the GitHub for Windows client browse to a repository and 'Open in Git Shell'. Be sure to change to an appropriate directory so you don't clone into an existing repository.)
13 | 2. Run `git clone https://github.com/jhoerr/posh-gitflow.git;cd posh-gitflow;./Configure-GitFlow.ps1;cd ..`
14 | 3. Run `git flow init` to initialize the repository for GitFlow.
15 | 4. Review the One-Page GitFlow Cheatsheet to learn the commands.
16 |
17 | **Known Issues**
18 | Depending on your computer's PowerShell execution policy, you may receive the following error during installation step #1:
19 | ```
20 | .\Configure-GitFlow.ps1 : File Configure-GitFlow.ps1 cannot be loaded. The file Configure-GitFlow.ps1 is
21 | not digitally signed. You cannot run this script on the current system. For more information about running
22 | scripts and setting execution policy, see about_Execution_Policies at
23 | http://go.microsoft.com/fwlink/?LinkID=135170.
24 | At line:1 char:1
25 | + .\Configure-GitFlow.ps1
26 | + ~~~~~~~~~~~~~~~~~~~~~~~
27 | + CategoryInfo : SecurityError: (:) [], PSSecurityException
28 | + FullyQualifiedErrorId : UnauthorizedAccess
29 | ```
30 |
31 | To proceed without adjusting the system's execution policy, navigate to the file in Explorer, right-click and choose Properties, then click the Unblock button.
32 |
--------------------------------------------------------------------------------