├── 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 | --------------------------------------------------------------------------------