diff --git a/AUTHORS b/AUTHORS index d4e7490e4..aded955fc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -16,5 +16,9 @@ Authors are (ordered by first commit date): - Kiall Mac Innes - Jon Bernard - Olivier Mengué +- Emre Berge Ergenekon +- Eric Holmes +- Vedang Manerikar +- Myke Hines Portions derived from other open source works are clearly marked. diff --git a/Changes.mdown b/Changes.mdown index 53858b6b1..2281f2307 100644 --- a/Changes.mdown +++ b/Changes.mdown @@ -2,12 +2,22 @@ ----- Release date: **not yet** +* `git flow init` now detects situations where origin already has gitflow + branches set up, and behaves accordingly (thanks Emre Berge Ergenekon). + * `git flow feature finish` can now be called without a feature branch name(prefix) argument and will finish the current branch, if on any. -* Various minor bug fixes related to internal argument passing +* `git flow feature pull` now has a `-r` flag, to support `pull --rebase` + semantics (thanks Vedang Manerikar). + +* Various minor bug fixes related to internal argument passing. + +* Improved some documentation. + +* Better support for Windows and BSD users. -* Add package installers for the Debian and Windows platforms. +* Add package installer for the Windows platform. 0.4.1: ----- diff --git a/Makefile b/Makefile index 764bd23b0..fbbfd2c00 100644 --- a/Makefile +++ b/Makefile @@ -27,12 +27,7 @@ # policies, either expressed or implied, of Vincent Driessen. # -# Determine if we're inside a debian build .. -ifdef DEB_BUILD_ARCH - prefix=$(DESTDIR)/usr/ -else - prefix=/usr/local -endif +prefix=/usr/local # files that need mode 755 EXEC_FILES=git-flow diff --git a/README.mdown b/README.mdown index b3e1df31b..b720b125e 100644 --- a/README.mdown +++ b/README.mdown @@ -1,5 +1,37 @@ -git-flow ![Project status](http://stillmaintained.com/nvie/gitflow.png) +> [!IMPORTANT] +> +> # ⚠️ git-flow has moved to git-flow-next! +> +> **This repository is no longer maintained.** The wonderful team at [Tower](https://www.git-tower.com/) has created [**git-flow-next**](https://git-flow.sh) — a modern, fully customizable evolution of git-flow, built on a generic branch dependency model. It’s fully backward compatible with git-flow, open source, and actively maintained. +> +> ### Get started with git-flow-next: +> +> - **Website:** https://git-flow.sh +> - **GitHub:** https://github.com/gittower/git-flow-next +> - **Evolution Story:** Read about the journey from git-flow → git-flow-avh → git-flow-next in [their blog post](https://git-flow.sh/blog) +> +> ### Why git-flow-next? +> +> git-flow-next builds upon the foundation laid by the original git-flow, offering: +> +> - Full customization of branch names and workflow +> - Modern implementation with active maintenance +> - Backward compatibility with existing git-flow workflows +> - Upcoming features like stacked branches and topic branch syncing +> +> ### Thank you ❤️ +> +> To everyone who has used, contributed to, and supported git-flow over the past 15+ years — thank you! Your feedback, contributions, and adoption made git-flow one of the most widely-used Git workflow tools. Special thanks to Peter van der Does for maintaining git-flow-avh, and to the folks at Tower for carrying the torch forward with git-flow-next. + +--- + +(Below, you’ll find the original documentation for historical reference.) + +--- + +git-flow ======== + A collection of Git extensions to provide high-level repository operations for Vincent Driessen's [branching model](http://nvie.com/git-model "original blog post"). @@ -14,77 +46,14 @@ Kreeftmeijer's blog post: Or have a look at one of these screen casts: +* [How to use a scalable Git branching model called git-flow](http://buildamodule.com/video/change-management-and-version-control-deploying-releases-features-and-fixes-with-git-how-to-use-a-scalable-git-branching-model-called-gitflow) (by Build a Module) * [A short introduction to git-flow](http://vimeo.com/16018419) (by Mark Derricutt) * [On the path with git-flow](http://codesherpas.com/screencasts/on_the_path_gitflow.mov) (by Dave Bock) Installing git-flow ------------------- - -### Mac OS -If you're on a Mac and use [homebrew](http://github.com/mxcl/homebrew), it's simple: - - $ brew install git-flow - -If you're on a Mac and use [MacPorts](http://macports.org/), it's simple: - - $ port install git-flow - -### Linux, etc. -Another easy way to install git-flow is using Rick Osborne's excellent git-flow -installer, which can be run using the following command: - - $ wget --no-check-certificate -q -O - https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | sudo bash - -### Windows - -For Windows users, [msysgit](http://code.google.com/p/msysgit/) is a good -starting place for installing git. - -#### Using Cygwin -For Windows users who wish to use the automated install, it is suggested that you install [Cygwin](http://www.cygwin.com/) -first to install tools like `git`, `util-linux` and `wget` (with those three being packages that can be selected -during installation). Then simply run this command from a Cygwin shell: - - $ wget -q -O - https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | bash - -#### Using [msysgit](http://code.google.com/p/msysgit/) -Download and install `getopt.exe` from the [util-linux package](http://gnuwin32.sourceforge.net/packages/util-linux-ng.htm) into `C:\Program Files\Git\bin`. (Only `getopt.exe`, the others util-linux files are not used). Also install `libintl3.dll` from the Dependencies package, into the same directory. - -Clone the git-flow sources from GitHub: - - $ git clone --recursive git://github.com/nvie/gitflow.git - $ cd gitflow - -Run the `msysgit-install` script from a command-line prompt (you may have to -run it with "Full Administrator" rights if you installed msysgit with its -installer): - - C:\gitflow> contrib\msysgit-install.cmd - -### Manual installation -If you prefer a manual installation, please use the following instructions: - - $ git clone --recursive git://github.com/nvie/gitflow.git - -Then, you can install `git-flow`, using: - - $ sudo make install - -By default, git-flow will be installed in /usr/local. To change the prefix -where git-flow will be installed, simply specify it explicitly, using: - - $ sudo make prefix=/opt/local install - -Or simply point your `PATH` environment variable to your git-flow checkout -directory. - -*Installation note:* -git-flow depends on the availability of the command line utility `getopt`, -which may not be available in your Unix/Linux environment. Please use your -favorite package manager to install `getopt`. For Cygwin, install the -`util-linux` package to get `getopt`. If you use `apt-get` as your install -manager, the package name is `opt`. +See the Wiki for up-to-date [Installation Instructions](https://github.com/nvie/gitflow/wiki/Installation). Integration with your shell @@ -102,20 +71,6 @@ See the [FAQ](http://github.com/nvie/gitflow/wiki/FAQ) section of the project Wiki. -Please help out ---------------- -This project is still under development. Feedback and suggestions are very -welcome and I encourage you to use the [Issues -list](http://github.com/nvie/gitflow/issues) on Github to provide that -feedback. - -Feel free to fork this repo and to commit your additions. For a list of all -contributors, please see the [AUTHORS](AUTHORS) file. - -Any questions, tips, or general discussion can be posted to our Google group: -[http://groups.google.com/group/gitflow-users](http://groups.google.com/group/gitflow-users) - - License terms ------------- git-flow is published under the liberal terms of the BSD License, see the @@ -129,13 +84,15 @@ in a Github fork, of course. To initialize a new repo with the basic branch structure, use: - git flow init + git flow init [-d] This will then interactively prompt you with some questions on which branches you would like to use as development and production branches, and how you would like your prefixes be named. You may simply press Return on any of those questions to accept the (sane) default suggestions. +The ``-d`` flag will accept all defaults. + ### Creating feature/release/hotfix/support branches @@ -147,6 +104,11 @@ those questions to accept the (sane) default suggestions. For feature branches, the `` arg must be a commit on `develop`. +* To push/pull a feature branch to the remote repository, use: + + git flow feature publish + git flow feature pull + * To list/start/finish release branches, use: git flow release @@ -169,18 +131,3 @@ those questions to accept the (sane) default suggestions. git flow support start For support branches, the `` arg must be a commit on `master`. - - -Showing your appreciation -========================= -A few people already requested it, so now it's here: a Flattr button. - -Of course, the best way to show your appreciation for the original -[blog post](http://nvie.com/git-model) or the git-flow tool itself remains -contributing to the community. If you'd like to show your appreciation in -another way, however, consider Flattr'ing me: - -[![Flattr this][2]][1] - -[1]: http://flattr.com/thing/53771/git-flow -[2]: http://api.flattr.com/button/button-static-50x60.png diff --git a/contrib/debian/changelog b/contrib/debian/changelog deleted file mode 100644 index f8895965e..000000000 --- a/contrib/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -gitflow (0.4.1) unstable; urgency=low - - * Initial Debian Packaging - - -- Kiall Mac Innes Mon, 25 Apr 2011 23:51:42 +0100 diff --git a/contrib/debian/compat b/contrib/debian/compat deleted file mode 100644 index 7f8f011eb..000000000 --- a/contrib/debian/compat +++ /dev/null @@ -1 +0,0 @@ -7 diff --git a/contrib/debian/control b/contrib/debian/control deleted file mode 100644 index d47b2b03d..000000000 --- a/contrib/debian/control +++ /dev/null @@ -1,14 +0,0 @@ -Source: gitflow -Section: vcs -Priority: optional -Maintainer: Kiall Mac Innes -Build-Depends: debhelper (>= 7.0.50~), git -Standards-Version: 3.9.1 -Homepage: http://nvie.com/posts/a-successful-git-branching-model/ -Vcs-Git: https://github.com/nvie/gitflow.git -Vcs-Browser: https://github.com/nvie/gitflow - -Package: gitflow -Architecture: all -Depends: ${shlibs:Depends}, ${misc:Depends}, git -Description: Git extensions to provide high-level repository operations for Vincent Driessen's branching model diff --git a/contrib/debian/copyright b/contrib/debian/copyright deleted file mode 100644 index a08359acb..000000000 --- a/contrib/debian/copyright +++ /dev/null @@ -1,40 +0,0 @@ -Format: http://dep.debian.net/deps/dep5 -Upstream-Name: gitflow -Source: - -Files: * -Copyright: - -License: BSD-3-Clause - -Files: debian/* -Copyright: 2011 Kiall -License: BSD-3-Clause - -License: BSD-3-Clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the University nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - . - THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - -# Please also look if there are files or directories which have a -# different copyright/license attached and list them here. diff --git a/contrib/debian/docs b/contrib/debian/docs deleted file mode 100644 index ceda507c1..000000000 --- a/contrib/debian/docs +++ /dev/null @@ -1 +0,0 @@ -README.mdown diff --git a/contrib/debian/rules b/contrib/debian/rules deleted file mode 100755 index 79092d6be..000000000 --- a/contrib/debian/rules +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -override_dh_testdir: - dh_testdir - git submodule init && git submodule update -%: - dh $@ diff --git a/contrib/msysgit-install.cmd b/contrib/msysgit-install.cmd index 12858397d..994235c79 100644 --- a/contrib/msysgit-install.cmd +++ b/contrib/msysgit-install.cmd @@ -22,11 +22,11 @@ echo getopt.exe... Found if not exist "%GIT_HOME%\bin\git-flow" goto :Install echo GitFlow is already installed.>&2 -choice /C YN /M "Do you want to replace it" -if errorlevel 255 goto :Abort -if errorlevel 2 goto :Abort -if not errorlevel 1 goto :Abort +set /p mychoice="Do you want to replace it [y/n]" +if "%mychoice%"=="y" goto :DeleteOldFiles +goto :Abort +:DeleteOldFiles echo Deleting old files... for /F %%i in ("%GIT_HOME%\git-flow*" "%GIT_HOME%\gitflow-*") do if exist "%%~fi" del /F /Q "%%~fi" @@ -63,6 +63,7 @@ goto :End :ChkGetopt :: %1 is getopt.exe if exist "%GIT_HOME%\bin\%1" goto :EOF +if exist "%USERPROFILE%\bin\%1" goto :EOF if exist "%~f$PATH:1" goto :EOF echo %GIT_HOME%\bin\%1 not found.>&2 echo You have to install this file manually. See the GitFlow README. diff --git a/git-flow b/git-flow index 97b1b34bb..fd16d5168 100755 --- a/git-flow +++ b/git-flow @@ -37,12 +37,17 @@ # policies, either expressed or implied, of Vincent Driessen. # +# set this to workaround expr problems in shFlags on freebsd +if uname -s | egrep -iq 'bsd'; then export EXPR_COMPAT=1; fi + # enable debug mode if [ "$DEBUG" = "yes" ]; then set -x fi -export GITFLOW_DIR=$(dirname "$0") +# The sed expression here replaces all backslashes by forward slashes. +# This helps our Windows users, while not bothering our Unix users. +export GITFLOW_DIR=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") usage() { echo "usage: git flow " @@ -75,6 +80,11 @@ main() { # use the shFlags project to parse the command line arguments . "$GITFLOW_DIR/gitflow-shFlags" FLAGS_PARENT="git flow" + + # allow user to request git action logging + DEFINE_boolean show_commands false 'show actions taken (git commands)' g + + # do actual parsing FLAGS "$@" || exit $? eval set -- "${FLAGS_ARGV}" @@ -104,7 +114,10 @@ main() { fi # run the specified action - cmd_$SUBACTION "$@" + if [ $SUBACTION != "help" ] && [ $SUBCOMMAND != "init" ] ; then + init + fi + cmd_$SUBACTION "$@" } main "$@" diff --git a/git-flow-feature b/git-flow-feature index 9da4b0bfa..55198ad82 100644 --- a/git-flow-feature +++ b/git-flow-feature @@ -36,21 +36,23 @@ # policies, either expressed or implied, of Vincent Driessen. # -require_git_repo -require_gitflow_initialized -gitflow_load_settings -PREFIX=$(git config --get gitflow.prefix.feature) +init() { + require_git_repo + require_gitflow_initialized + gitflow_load_settings + PREFIX=$(git config --get gitflow.prefix.feature) +} usage() { echo "usage: git flow feature [list] [-v]" echo " git flow feature start [-F] []" - echo " git flow feature finish [-rFk] []" + echo " git flow feature finish [-rFkDS] []" echo " git flow feature publish " echo " git flow feature track " echo " git flow feature diff []" echo " git flow feature rebase [-i] []" echo " git flow feature checkout []" - echo " git flow feature pull []" + echo " git flow feature pull [-r] []" } cmd_default() { @@ -202,7 +204,7 @@ cmd_start() { # update the local repo with remote changes, if asked if flag fetch; then - git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" + git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" fi # if the origin branch counterpart exists, assert that the local branch @@ -212,7 +214,7 @@ cmd_start() { fi # create branch - if ! git checkout -b "$BRANCH" "$BASE"; then + if ! git_do checkout -b "$BRANCH" "$BASE"; then die "Could not create feature branch '$BRANCH'" fi @@ -231,6 +233,8 @@ cmd_finish() { DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F DEFINE_boolean rebase false "rebase instead of merge" r DEFINE_boolean keep false "keep branch after performing finish" k + DEFINE_boolean force_delete false "force delete feature branch after finish" D + DEFINE_boolean squash false "squash feature during merge" S parse_args "$@" expand_nameprefix_arg_or_current @@ -283,8 +287,8 @@ cmd_finish() { # update local repo with remote changes first, if asked if has "$ORIGIN/$BRANCH" $(git_remote_branches); then if flag fetch; then - git fetch -q "$ORIGIN" "$BRANCH" - git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" + git_do fetch -q "$ORIGIN" "$BRANCH" + git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" fi fi @@ -307,11 +311,17 @@ cmd_finish() { fi # merge into BASE - git checkout "$DEVELOP_BRANCH" + git_do checkout "$DEVELOP_BRANCH" if [ "$(git rev-list -n2 "$DEVELOP_BRANCH..$BRANCH" | wc -l)" -eq 1 ]; then - git merge --ff "$BRANCH" + git_do merge --ff "$BRANCH" else - git merge --no-ff "$BRANCH" + if noflag squash; then + git_do merge --no-ff "$BRANCH" + else + git_do merge --squash "$BRANCH" + git_do commit + git_do merge "$BRANCH" + fi fi if [ $? -ne 0 ]; then @@ -341,12 +351,16 @@ helper_finish_cleanup() { # delete branch if flag fetch; then - git push "$ORIGIN" ":refs/heads/$BRANCH" + git_do push "$ORIGIN" ":refs/heads/$BRANCH" fi if noflag keep; then - git branch -d "$BRANCH" + if flag force_delete; then + git_do branch -D "$BRANCH" + else + git_do branch -d "$BRANCH" + fi fi echo @@ -369,17 +383,17 @@ cmd_publish() { # sanity checks require_clean_working_tree require_branch "$BRANCH" - git fetch -q "$ORIGIN" + git_do fetch -q "$ORIGIN" require_branch_absent "$ORIGIN/$BRANCH" # create remote branch - git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH" - git fetch -q "$ORIGIN" + git_do push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH" + git_do fetch -q "$ORIGIN" # configure remote tracking - git config "branch.$BRANCH.remote" "$ORIGIN" - git config "branch.$BRANCH.merge" "refs/heads/$BRANCH" - git checkout "$BRANCH" + git_do config "branch.$BRANCH.remote" "$ORIGIN" + git_do config "branch.$BRANCH.merge" "refs/heads/$BRANCH" + git_do checkout "$BRANCH" echo echo "Summary of actions:" @@ -396,11 +410,11 @@ cmd_track() { # sanity checks require_clean_working_tree require_branch_absent "$BRANCH" - git fetch -q "$ORIGIN" + git_do fetch -q "$ORIGIN" require_branch "$ORIGIN/$BRANCH" # create tracking branch - git checkout -b "$BRANCH" "$ORIGIN/$BRANCH" + git_do checkout -b "$BRANCH" "$ORIGIN/$BRANCH" echo echo "Summary of actions:" @@ -431,7 +445,7 @@ cmd_checkout() { if [ "$NAME" != "" ]; then expand_nameprefix_arg - git checkout "$BRANCH" + git_do checkout "$BRANCH" else die "Name a feature branch explicitly." fi @@ -450,12 +464,12 @@ cmd_rebase() { require_clean_working_tree require_branch "$BRANCH" - git checkout -q "$BRANCH" + git_do checkout -q "$BRANCH" local OPTS= if flag interactive; then OPTS="$OPTS -i" fi - git rebase $OPTS "$DEVELOP_BRANCH" + git_do rebase $OPTS "$DEVELOP_BRANCH" } avoid_accidental_cross_branch_action() { @@ -470,6 +484,7 @@ avoid_accidental_cross_branch_action() { cmd_pull() { #DEFINE_string prefix false 'alternative remote feature branch name prefix' p + DEFINE_boolean rebase false "pull with rebase" r parse_remote_name "$@" if [ -z "$REMOTE" ]; then @@ -495,13 +510,21 @@ cmd_pull() { # we already have a local branch called like this, so simply pull the # remote changes in - git pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'." + if flag rebase; then + if ! git_do pull --rebase -q "$REMOTE" "$BRANCH"; then + warn "Pull was aborted. There might be conflicts during rebase or '$REMOTE' might be inaccessible." + exit 1 + fi + else + git_do pull -q "$REMOTE" "$BRANCH" || die "Failed to pull from remote '$REMOTE'." + fi + echo "Pulled $REMOTE's changes into $BRANCH." else # setup the local branch clone for the first time - git fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD - git branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed." - git checkout -q "$BRANCH" || die "Checking out new local branch failed." + git_do fetch -q "$REMOTE" "$BRANCH" || die "Fetch failed." # stores in FETCH_HEAD + git_do branch --no-track "$BRANCH" FETCH_HEAD || die "Branch failed." + git_do checkout -q "$BRANCH" || die "Checking out new local branch failed." echo "Created local branch $BRANCH based on $REMOTE's $BRANCH." fi } diff --git a/git-flow-hotfix b/git-flow-hotfix index 261811317..ba485f6fe 100644 --- a/git-flow-hotfix +++ b/git-flow-hotfix @@ -36,16 +36,20 @@ # policies, either expressed or implied, of Vincent Driessen. # -require_git_repo -require_gitflow_initialized -gitflow_load_settings -VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`") -PREFIX=$(git config --get gitflow.prefix.hotfix) +init() { + require_git_repo + require_gitflow_initialized + gitflow_load_settings + VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`") + PREFIX=$(git config --get gitflow.prefix.hotfix) +} usage() { echo "usage: git flow hotfix [list] [-v]" echo " git flow hotfix start [-F] []" echo " git flow hotfix finish [-Fsumpk] " + echo " git flow hotfix publish " + echo " git flow hotfix track " } cmd_default() { @@ -165,14 +169,14 @@ cmd_start() { require_branch_absent "$BRANCH" require_tag_absent "$VERSION_PREFIX$VERSION" if flag fetch; then - git fetch -q "$ORIGIN" "$MASTER_BRANCH" + git_do fetch -q "$ORIGIN" "$MASTER_BRANCH" fi if has "$ORIGIN/$MASTER_BRANCH" $(git_remote_branches); then require_branches_equal "$MASTER_BRANCH" "$ORIGIN/$MASTER_BRANCH" fi # create branch - git checkout -b "$BRANCH" "$BASE" + git_do checkout -b "$BRANCH" "$BASE" echo echo "Summary of actions:" @@ -188,11 +192,59 @@ cmd_start() { echo } +cmd_publish() { + parse_args "$@" + require_version_arg + + # sanity checks + require_clean_working_tree + require_branch "$BRANCH" + git_do fetch -q "$ORIGIN" + require_branch_absent "$ORIGIN/$BRANCH" + + # create remote branch + git_do push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH" + git_do fetch -q "$ORIGIN" + + # configure remote tracking + git config "branch.$BRANCH.remote" "$ORIGIN" + git config "branch.$BRANCH.merge" "refs/heads/$BRANCH" + git_do checkout "$BRANCH" + + echo + echo "Summary of actions:" + echo "- A new remote branch '$BRANCH' was created" + echo "- The local branch '$BRANCH' was configured to track the remote branch" + echo "- You are now on branch '$BRANCH'" + echo +} + +cmd_track() { + parse_args "$@" + require_version_arg + + # sanity checks + require_clean_working_tree + require_branch_absent "$BRANCH" + git_do fetch -q "$ORIGIN" + require_branch "$ORIGIN/$BRANCH" + + # create tracking branch + git_do checkout -b "$BRANCH" "$ORIGIN/$BRANCH" + + echo + echo "Summary of actions:" + echo "- A new remote tracking branch '$BRANCH' was created" + echo "- You are now on branch '$BRANCH'" + echo +} + cmd_finish() { DEFINE_boolean fetch false "fetch from $ORIGIN before performing finish" F DEFINE_boolean sign false "sign the release tag cryptographically" s DEFINE_string signingkey "" "use the given GPG-key for the digital signature (implies -s)" u DEFINE_string message "" "use the given tag message" m + DEFINE_string messagefile "" "use the contents of the given file as tag message" f DEFINE_boolean push false "push to $ORIGIN after performing finish" p DEFINE_boolean keep false "keep branch after performing finish" k DEFINE_boolean notag false "don't tag this release" n @@ -208,9 +260,9 @@ cmd_finish() { require_branch "$BRANCH" require_clean_working_tree if flag fetch; then - git fetch -q "$ORIGIN" "$MASTER_BRANCH" || \ + git_do fetch -q "$ORIGIN" "$MASTER_BRANCH" || \ die "Could not fetch $MASTER_BRANCH from $ORIGIN." - git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" || \ + git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" || \ die "Could not fetch $DEVELOP_BRANCH from $ORIGIN." fi if has "$ORIGIN/$MASTER_BRANCH" $(git_remote_branches); then @@ -224,9 +276,9 @@ cmd_finish() { # in case a previous attempt to finish this release branch has failed, # but the merge into master was successful, we skip it now if ! git_is_branch_merged_into "$BRANCH" "$MASTER_BRANCH"; then - git checkout "$MASTER_BRANCH" || \ + git_do checkout "$MASTER_BRANCH" || \ die "Could not check out $MASTER_BRANCH." - git merge --no-ff "$BRANCH" || \ + git_do merge --no-ff "$BRANCH" || \ die "There were merge conflicts." # TODO: What do we do now? fi @@ -241,7 +293,8 @@ cmd_finish() { flag sign && opts="$opts -s" [ "$FLAGS_signingkey" != "" ] && opts="$opts -u '$FLAGS_signingkey'" [ "$FLAGS_message" != "" ] && opts="$opts -m '$FLAGS_message'" - git tag $opts "$VERSION_PREFIX$VERSION" || \ + [ "$FLAGS_messagefile" != "" ] && opts="$opts -F '$FLAGS_messagefile'" + eval git_do tag $opts "$VERSION_PREFIX$VERSION" "$BRANCH" || \ die "Tagging failed. Please run finish again to retry." fi fi @@ -250,28 +303,28 @@ cmd_finish() { # in case a previous attempt to finish this release branch has failed, # but the merge into develop was successful, we skip it now if ! git_is_branch_merged_into "$BRANCH" "$DEVELOP_BRANCH"; then - git checkout "$DEVELOP_BRANCH" || \ + git_do checkout "$DEVELOP_BRANCH" || \ die "Could not check out $DEVELOP_BRANCH." # TODO: Actually, accounting for 'git describe' pays, so we should # ideally git merge --no-ff $tagname here, instead! - git merge --no-ff "$BRANCH" || \ + git_do merge --no-ff "$BRANCH" || \ die "There were merge conflicts." # TODO: What do we do now? fi # delete branch if noflag keep; then - git branch -d "$BRANCH" + git_do branch -d "$BRANCH" fi if flag push; then - git push "$ORIGIN" "$DEVELOP_BRANCH" || \ + git_do push "$ORIGIN" "$DEVELOP_BRANCH" || \ die "Could not push to $DEVELOP_BRANCH from $ORIGIN." - git push "$ORIGIN" "$MASTER_BRANCH" || \ + git_do push "$ORIGIN" "$MASTER_BRANCH" || \ die "Could not push to $MASTER_BRANCH from $ORIGIN." if noflag notag; then - git push --tags "$ORIGIN" || \ + git_do push --tags "$ORIGIN" || \ die "Could not push tags to $ORIGIN." fi fi diff --git a/git-flow-init b/git-flow-init index 57ab2441e..5b4e7e807 100644 --- a/git-flow-init +++ b/git-flow-init @@ -53,7 +53,7 @@ cmd_default() { parse_args "$@" if ! git rev-parse --git-dir >/dev/null 2>&1; then - git init + git_do init else # assure that we are not working in a repo with local changes git_repo_is_headless || require_clean_working_tree @@ -117,12 +117,18 @@ cmd_default() { # check existence in case of an already existing repo if [ "$should_check_existence" = "YES" ]; then - git_local_branch_exists "$master_branch" || \ + # if no local branch exists and a remote branch of the same + # name exists, checkout that branch and use it for master + if ! git_local_branch_exists "$master_branch" && \ + git_remote_branch_exists "origin/$master_branch"; then + git_do branch "$master_branch" "origin/$master_branch" >/dev/null 2>&1 + elif ! git_local_branch_exists "$master_branch"; then die "Local branch '$master_branch' does not exist." + fi fi # store the name of the master branch - git config gitflow.branch.master "$master_branch" + git_do config gitflow.branch.master "$master_branch" fi # add a develop branch if no such branch exists yet @@ -147,11 +153,17 @@ cmd_default() { default_suggestion= for guess in $(git config --get gitflow.branch.develop) \ 'develop' 'int' 'integration' 'master'; do - if git_local_branch_exists "$guess"; then + if git_local_branch_exists "$guess" && [ "$guess" != "$master_branch" ]; then default_suggestion="$guess" break fi done + + if [ -z $default_suggestion ]; then + should_check_existence=NO + default_suggestion=$(git config --get gitflow.branch.develop || echo develop) + fi + fi printf "Branch name for \"next release\" development: [$default_suggestion] " @@ -173,7 +185,7 @@ cmd_default() { fi # store the name of the develop branch - git config gitflow.branch.develop "$develop_branch" + git_do config gitflow.branch.develop "$develop_branch" fi # Creation of HEAD @@ -182,8 +194,8 @@ cmd_default() { # it to be able to create new branches. local created_gitflow_branch=0 if ! git rev-parse --quiet --verify HEAD >/dev/null 2>&1; then - git symbolic-ref HEAD "refs/heads/$master_branch" - git commit --allow-empty --quiet -m "Initial commit" + git_do symbolic-ref HEAD "refs/heads/$master_branch" + git_do commit --allow-empty --quiet -m "Initial commit" created_gitflow_branch=1 fi @@ -200,7 +212,11 @@ cmd_default() { # default production branch and develop was "created". We should create # the develop branch now in that case (we base it on master, of course) if ! git_local_branch_exists "$develop_branch"; then - git branch --no-track "$develop_branch" "$master_branch" + if git_remote_branch_exists "origin/$develop_branch"; then + git_do branch "$develop_branch" "origin/$develop_branch" >/dev/null 2>&1 + else + git_do branch --no-track "$develop_branch" "$master_branch" + fi created_gitflow_branch=1 fi @@ -209,7 +225,7 @@ cmd_default() { # switch to develop branch if its newly created if [ $created_gitflow_branch -eq 1 ]; then - git checkout -q "$develop_branch" + git_do checkout -q "$develop_branch" fi # finally, ask the user for naming conventions (branch and tag prefixes) @@ -235,7 +251,7 @@ cmd_default() { printf "\n" fi [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion} - git config gitflow.prefix.feature "$prefix" + git_do config gitflow.prefix.feature "$prefix" fi # Release branches @@ -248,7 +264,7 @@ cmd_default() { printf "\n" fi [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion} - git config gitflow.prefix.release "$prefix" + git_do config gitflow.prefix.release "$prefix" fi @@ -262,7 +278,7 @@ cmd_default() { printf "\n" fi [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion} - git config gitflow.prefix.hotfix "$prefix" + git_do config gitflow.prefix.hotfix "$prefix" fi @@ -276,7 +292,7 @@ cmd_default() { printf "\n" fi [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion} - git config gitflow.prefix.support "$prefix" + git_do config gitflow.prefix.support "$prefix" fi @@ -290,7 +306,7 @@ cmd_default() { printf "\n" fi [ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion} - git config gitflow.prefix.versiontag "$prefix" + git_do config gitflow.prefix.versiontag "$prefix" fi diff --git a/git-flow-release b/git-flow-release index 08f595b2f..cb95bd486 100644 --- a/git-flow-release +++ b/git-flow-release @@ -36,16 +36,18 @@ # policies, either expressed or implied, of Vincent Driessen. # -require_git_repo -require_gitflow_initialized -gitflow_load_settings -VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`") -PREFIX=$(git config --get gitflow.prefix.release) +init() { + require_git_repo + require_gitflow_initialized + gitflow_load_settings + VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`") + PREFIX=$(git config --get gitflow.prefix.release) +} usage() { echo "usage: git flow release [list] [-v]" echo " git flow release start [-F] []" - echo " git flow release finish [-Fsumpk] " + echo " git flow release finish [-FsumpkS] " echo " git flow release publish " echo " git flow release track " } @@ -134,7 +136,7 @@ require_version_arg() { } require_base_is_on_develop() { - if ! git branch --no-color --contains "$BASE" 2>/dev/null \ + if ! git_do branch --no-color --contains "$BASE" 2>/dev/null \ | sed 's/[* ] //g' \ | grep -q "^$DEVELOP_BRANCH\$"; then die "fatal: Given base '$BASE' is not a valid commit on '$DEVELOP_BRANCH'." @@ -162,14 +164,14 @@ cmd_start() { require_branch_absent "$BRANCH" require_tag_absent "$VERSION_PREFIX$VERSION" if flag fetch; then - git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" + git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" fi if has "$ORIGIN/$DEVELOP_BRANCH" $(git_remote_branches); then require_branches_equal "$DEVELOP_BRANCH" "$ORIGIN/$DEVELOP_BRANCH" fi # create branch - git checkout -b "$BRANCH" "$BASE" + git_do checkout -b "$BRANCH" "$BASE" echo echo "Summary of actions:" @@ -190,9 +192,11 @@ cmd_finish() { DEFINE_boolean sign false "sign the release tag cryptographically" s DEFINE_string signingkey "" "use the given GPG-key for the digital signature (implies -s)" u DEFINE_string message "" "use the given tag message" m + DEFINE_string messagefile "" "use the contents of the given file as a tag message" f DEFINE_boolean push false "push to $ORIGIN after performing finish" p DEFINE_boolean keep false "keep branch after performing finish" k DEFINE_boolean notag false "don't tag this release" n + DEFINE_boolean squash false "squash release during merge" S parse_args "$@" require_version_arg @@ -206,9 +210,9 @@ cmd_finish() { require_branch "$BRANCH" require_clean_working_tree if flag fetch; then - git fetch -q "$ORIGIN" "$MASTER_BRANCH" || \ + git_do fetch -q "$ORIGIN" "$MASTER_BRANCH" || \ die "Could not fetch $MASTER_BRANCH from $ORIGIN." - git fetch -q "$ORIGIN" "$DEVELOP_BRANCH" || \ + git_do fetch -q "$ORIGIN" "$DEVELOP_BRANCH" || \ die "Could not fetch $DEVELOP_BRANCH from $ORIGIN." fi if has "$ORIGIN/$MASTER_BRANCH" $(git_remote_branches); then @@ -222,11 +226,17 @@ cmd_finish() { # in case a previous attempt to finish this release branch has failed, # but the merge into master was successful, we skip it now if ! git_is_branch_merged_into "$BRANCH" "$MASTER_BRANCH"; then - git checkout "$MASTER_BRANCH" || \ + git_do checkout "$MASTER_BRANCH" || \ die "Could not check out $MASTER_BRANCH." - git merge --no-ff "$BRANCH" || \ - die "There were merge conflicts." - # TODO: What do we do now? + if noflag squash; then + git_do merge --no-ff "$BRANCH" || \ + die "There were merge conflicts." + # TODO: What do we do now? + else + git_do merge --squash "$BRANCH" || \ + die "There were merge conflicts." + git_do commit + fi fi if noflag notag; then @@ -239,7 +249,8 @@ cmd_finish() { flag sign && opts="$opts -s" [ "$FLAGS_signingkey" != "" ] && opts="$opts -u '$FLAGS_signingkey'" [ "$FLAGS_message" != "" ] && opts="$opts -m '$FLAGS_message'" - git tag $opts "$tagname" || \ + [ "$FLAGS_messagefile" != "" ] && opts="$opts -F '$FLAGS_messagefile'" + eval git_do tag $opts "$tagname" "$BRANCH" || \ die "Tagging failed. Please run finish again to retry." fi fi @@ -248,34 +259,41 @@ cmd_finish() { # in case a previous attempt to finish this release branch has failed, # but the merge into develop was successful, we skip it now if ! git_is_branch_merged_into "$BRANCH" "$DEVELOP_BRANCH"; then - git checkout "$DEVELOP_BRANCH" || \ + git_do checkout "$DEVELOP_BRANCH" || \ die "Could not check out $DEVELOP_BRANCH." # TODO: Actually, accounting for 'git describe' pays, so we should # ideally git merge --no-ff $tagname here, instead! - git merge --no-ff "$BRANCH" || \ - die "There were merge conflicts." - # TODO: What do we do now? + if noflag squash; then + git_do merge --no-ff "$BRANCH" || \ + die "There were merge conflicts." + # TODO: What do we do now? + else + git_do merge --squash "$BRANCH" || \ + die "There were merge conflicts." + # TODO: What do we do now? + git_do commit + fi fi # delete branch if noflag keep; then if [ "$BRANCH" = "$(git_current_branch)" ]; then - git checkout "$MASTER_BRANCH" + git_do checkout "$MASTER_BRANCH" fi - git branch -d "$BRANCH" + git_do branch -d "$BRANCH" fi if flag push; then - git push "$ORIGIN" "$DEVELOP_BRANCH" || \ + git_do push "$ORIGIN" "$DEVELOP_BRANCH" || \ die "Could not push to $DEVELOP_BRANCH from $ORIGIN." - git push "$ORIGIN" "$MASTER_BRANCH" || \ + git_do push "$ORIGIN" "$MASTER_BRANCH" || \ die "Could not push to $MASTER_BRANCH from $ORIGIN." if noflag notag; then - git push --tags "$ORIGIN" || \ + git_do push --tags "$ORIGIN" || \ die "Could not push tags to $ORIGIN." fi - git push "$ORIGIN" :"$BRANCH" || \ + git_do push "$ORIGIN" :"$BRANCH" || \ die "Could not delete the remote $BRANCH in $ORIGIN." fi @@ -306,17 +324,17 @@ cmd_publish() { # sanity checks require_clean_working_tree require_branch "$BRANCH" - git fetch -q "$ORIGIN" + git_do fetch -q "$ORIGIN" require_branch_absent "$ORIGIN/$BRANCH" # create remote branch - git push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH" - git fetch -q "$ORIGIN" + git_do push "$ORIGIN" "$BRANCH:refs/heads/$BRANCH" + git_do fetch -q "$ORIGIN" # configure remote tracking - git config "branch.$BRANCH.remote" "$ORIGIN" - git config "branch.$BRANCH.merge" "refs/heads/$BRANCH" - git checkout "$BRANCH" + git_do config "branch.$BRANCH.remote" "$ORIGIN" + git_do config "branch.$BRANCH.merge" "refs/heads/$BRANCH" + git_do checkout "$BRANCH" echo echo "Summary of actions:" @@ -333,11 +351,11 @@ cmd_track() { # sanity checks require_clean_working_tree require_branch_absent "$BRANCH" - git fetch -q "$ORIGIN" + git_do fetch -q "$ORIGIN" require_branch "$ORIGIN/$BRANCH" # create tracking branch - git checkout -b "$BRANCH" "$ORIGIN/$BRANCH" + git_do checkout -b "$BRANCH" "$ORIGIN/$BRANCH" echo echo "Summary of actions:" diff --git a/git-flow-support b/git-flow-support index 605694dc9..cdbfc717c 100644 --- a/git-flow-support +++ b/git-flow-support @@ -36,11 +36,13 @@ # policies, either expressed or implied, of Vincent Driessen. # -require_git_repo -require_gitflow_initialized -gitflow_load_settings -VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`") -PREFIX=$(git config --get gitflow.prefix.support) +init() { + require_git_repo + require_gitflow_initialized + gitflow_load_settings + VERSION_PREFIX=$(eval "echo `git config --get gitflow.prefix.versiontag`") + PREFIX=$(git config --get gitflow.prefix.support) +} warn "note: The support subcommand is still very EXPERIMENTAL!" warn "note: DO NOT use it in a production situation." @@ -167,12 +169,12 @@ cmd_start() { # fetch remote changes if flag fetch; then - git fetch -q "$ORIGIN" "$BASE" + git_do fetch -q "$ORIGIN" "$BASE" fi require_branch_absent "$BRANCH" # create branch - git checkout -b "$BRANCH" "$BASE" + git_do checkout -b "$BRANCH" "$BASE" echo echo "Summary of actions:" diff --git a/gitflow-common b/gitflow-common index 252f5d0ea..332740533 100644 --- a/gitflow-common +++ b/gitflow-common @@ -45,7 +45,7 @@ warn() { echo "$@" >&2; } die() { warn "$@"; exit 1; } escape() { - echo "$1" | sed 's/\([\.\+\$\*]\)/\\\1/g' + echo "$1" | sed 's/\([\.\$\*]\)/\\\1/g' } # set logic @@ -70,6 +70,14 @@ noflag() { local FLAG; eval FLAG='$FLAGS_'$1; [ $FLAG -ne $FLAGS_TRUE ]; } # Git specific common functionality # +git_do() { + # equivalent to git, used to indicate actions that make modifications + if flag show_commands; then + echo "git $@" >&2 + fi + git "$@" +} + git_local_branches() { git branch --no-color | sed 's/^[* ] //'; } git_remote_branches() { git branch -r --no-color | sed 's/^[* ] //'; } git_all_branches() { ( git branch --no-color; git branch -r --no-color) | sed 's/^[* ] //'; } @@ -97,6 +105,10 @@ git_local_branch_exists() { has $1 $(git_local_branches) } +git_remote_branch_exists() { + has $1 $(git_remote_branches) +} + git_branch_exists() { has $1 $(git_all_branches) }