Git Flow
|
---|
This page summarizes the gitflow source control process loosely based on the work of Vincent Driessen. The summary is intended as a reminder of how this process is applied on a day-to-day basis.
Model
This model employs the following types of branches:
master: Production ready and released (there is only one master branch, which is also permanent);
develop: Centralized repo for integrating current feature development – when ready for release, it will be merged to master (there is only one develop branch, which is also permanent);
feature: there are multiple branches under feature at any given time (e.g., /feature/GP1-403-2 and /feature/GP1-407-9); these branches are removed once merged to develop;
release: there is one release branch for each release; it is used to isolate the development and master branches from the activities necessary to prepare a release (release version meta data and last minute bug fixes); these branches are removed once merged to master and develop;
hotfix: like a release branch, but unplanned: the intention is to make only the bare minimum change(s) to fix an issue found with a released version, and to merge those changes back to master and develop.
From a GIT point of view, there is no technical difference between these branch types. Maintaining the branch structure and the flow between branches is a discipline that is enforced by the development team.
Roles & Responsibilities
Role | Responsibilities | GOBii Personnel | Testing Environment |
---|---|---|---|
Process owner |
| @Yaw Nti-Addae @Vishnu Govindaraj | N/A |
Process admin |
| @Roy Petrie @Joshua Lamos-Sweeney | N/A |
CI owner |
| @KevinPalis | N/A |
CD owner |
| @Roy Petrie | CG_Test VMs |
QA |
| @Elizabeth Jones @Yaw Nti-Addae | QA_test App_test |
Repo owner(s) |
| Gobiiproject - @Joshua Lamos-Sweeney DataWarehouse - @KevinPalis Loader UI - @Angel Manica Raquel Web loader UI - @Vishnu Govindaraj Pedver - @Vishnu Govindaraj Timescope - @Angel Manica Raquel Marker Portal - @Roy Petrie KDC - @KevinPalis |
|
Developer |
|
| FXN_test CBSUGOBiiXVM[X] |
Release Cycle
Process
Type | Branch From | Merges To | Lifecycle | Naming Convention | Definition |
---|---|---|---|---|---|
master | n/a | n/a | one permanent branch | master |
|
develop | master | n/a | one permanent branch | develop |
|
feature | develop | develop | multiple branches that are deleted after merge | feature/<jira_id> ex: feature/GDM-487 feature/GDM-660 OR feature/<jira_id>-<num or label> ex: feature/GDM-588-1 feature/GDM-588-normalize_names |
|
bugfix | release | develop | multiple branches that are deleted after merge | bugfix/<jira_id> ex: bugfix/GSD-487 bugfix/GSD-660-2 bugfix/GSD-588-normalize_names |
|
release | develop | develop and master | multiple branches that are deleted after merge | release/GDM_<release_number> ex: release/1.0-Cascadilla
|
|
hotfix | master | develop and master | multiple branches that are deleted after merge | hotfix/<major.minor_version>-<jira_id_for_bug> ex: hotfix/1.0.1-GP1-832 |
|
Procedures
In all cases, when merging use the --no-ff flag. Doing so causes the merge to always create a new commit object, even if the merge could be performed with a fast-forward. This avoids losing information about the historical existence of a feature branch and groups together all commits that together added the feature
Feature Branch
Create the branch:
$ git checkout -b myfeature develop
Switched to a new branch "myfeature" |
Merge the branch back to develop:
--no-commit
'–no-commit' added because of an issue with merging old changes overwriting new changes. Once you've done a --no-commit merge, check your files for differences, then commit, then merge. If you see a file that's not yours in the merge, something is likely wrong. Talk to the owner of the file!
JDLS-Note: This is superstitious cargo-culting, but still good practice to check differences before pushing, either through recommitting yourself, or just a git log -N.
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff --no-commit myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git commit
On branch develop
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop |
Release Branch
Create the release branch:
$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-) |
Merge the release branch to master and tag:
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2
$ git push origin --tags |
Merge the release to develop:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes) |
JDLS-Note: --no-ff is also cargo-culting.
Remove the release branch:
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe). |
Hotfixes
Create the hotfix branch:
$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)
|
Merge to master:
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1
$ git push origin --tags |
Typically, you would merge the hotfix to develop:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes) |
However, if a release branch currently exists, the hotfix changes need to be merged into that release branch, instead of to develop. When the release branch is merged to develop, the hotfix will be back-merged along with it.
Remove the hotfix:
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6). |
Useful Git Commands
Tagging
Oddly, tags don't get pushed to the server automatically along with your other commits. In order to push your tags, execute
$ git push origin --tags |
To show a list of tags in the current branch:
$ git tag --list |
Repo Status
View uncommitted changes
git status |
Show which branch your local repo is tracking
$ git branch -vv |
Show the last N commits
$ git log -5 |
And there are loads of options for the log command.
Diff Commits
# your working copy and staging area:
% git diff
# staging area and the latest commit:
% git diff --staged
# your working copy and commit 4ac0a6733:
% git diff 4ac0a6733
# commit 4ac0a6733 and the latest commit:
% git diff 4ac0a6733 HEAD
# commit 4ac0a6733 and commit 826793951
% git diff 4ac0a6733 826793951 |
Branch Management
Create A New Branch
$ git checkout develop
$ git pull origin develop
$ git branch GP1 451-5
$ git checkout GP1-451-5 |
Switch To A Different Branch
Say you cloned a repo from master. By default, your local repository is tracking master. We don't commit working code directory to master. Therefore, you will want to switch to a feature branch to do your develop. To switch to a branch that has already been created:
$ git checkout GP1_451-5 |
JDLS Note - Don’t do this without a fetch! You’ll get the old version, and then have a sad.
Create a branch from a specific commit
Typically, you want to want to branch from the HEAD of the current branch (i.e., from the latest commit). However, there may be cases that you want to make a branch from an earlier point.
$ git checkout dev
$ git branch test 07aeec983bfc17c25f0b0a7c1d47da8e35df7af8 |
That is, create a branch called "test" from the branch "dev" as of the commit referenced by the specified UUID.
JDLS Note - if you are doing this, you will very shortly be having a bad day.
Merge Specific Files from A Branch
Suppose you created a branch in which you unintentionally made changes that should thematically have been in another branch. You now want to bring those changes into the branch they were intended for.
$ git checkout feature/GP1-653-01
Branch feature/GP1-653-01 set up to track remote branch feature/GP1-653-01 from origin.
Switched to a new branch 'feature/GP1-653-01'
$ git checkout feature/GP1-601-02 gobii-client/src/main/java/org/gobiiproject/gobiiclient/core/HttpCore.java gobii-client/src/main/java/org/gobiiproject/gobiiclient/core/restmethods/RestResource.java
$ git status
On branch feature/GP1-653-01
Your branch is up-to-date with 'origin/feature/GP1-653-01'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: gobii-client/src/main/java/org/gobiiproject/gobiiclient/core/HttpCore.java
modified: gobii-client/src/main/java/org/gobiiproject/gobiiclient/core/restmethods/RestResource.java |
The key thing to notice is that the second checkout command specifies the branch from which you want to get the modified files, which is followed by a space-separated list of the files. Doing so does not change the current branch. But it does stage the files you want in the current branch.
JDLS Note:
git checkout -- <filename>
is the same as ‘remove all my changes from branch head to this file’
Whoops -- I Made Changes On the Wrong Branch (but didn't commit yet)
Example: You created a new branch for a user story in bitbucket, and began to modify code without realising that you had not checked out the new branch locally. You will:
Stage your current changes (git add.);
Stash your current changes (git stash);
This records the current state of your working directory and then resets the working directory to the most recent commit in the repo being tracked by your working directory;Check out the branch on which you had intended to make the changes;
Unstash the changes to working directory (git stash apply)
This effectively takes the changes you recorded with git stash and applies them to the working directory.
$ git status
On branch develop
Your branch is up-to-date with 'origin/develop'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: gobii-process/src/main/java/org/gobiiproject/gobiiprocess/web/ConfigCheck.java -> gobii-process/src/main/java/org/gobiiproject/gobiiprocess/GobiiConfig.java
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: gobii-process/pom.xml
modified: gobii-process/src/main/java/org/gobiiproject/gobiiprocess/GobiiConfig.java
Phil@GOBII-1 MINGW64 /c/phil-source/IntelliJ/gobiiproject (develop)
#############################################
###### OMG I'm still tracking develop ######
$ git add .
Phil@GOBII-1 MINGW64 /c/phil-source/IntelliJ/gobiiproject (develop)
#############################################
### Stage (but not commit!!!) my changes ####
$ git stash
Saved working directory and index state WIP on develop: e3f8912 Merge branch 'feature/GP1-654-02' into develop
HEAD is now at e3f8912 Merge branch 'feature/GP1-654-02' into develop
Phil@GOBII-1 MINGW64 /c/phil-source/IntelliJ/gobiiproject (develop)
#############################################
######### Stash my changes ##################
$ git checkout feature/GP1-555-04
Branch feature/GP1-555-04 set up to track remote branch feature/GP1-555-04 from origin.
Switched to a new branch 'feature/GP1-555-04'
Phil@GOBII-1 MINGW64 /c/phil-source/IntelliJ/gobiiproject (feature/GP1-555-04)
#############################################
### Checkout the intended branch ####
$ git stash apply
Auto-merging gobii-process/src/main/java/org/gobiiproject/gobiiprocess/GobiiConfig.java
On branch feature/GP1-555-04
Your branch is up-to-date with 'origin/feature/GP1-555-04'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: gobii-process/src/main/java/org/gobiiproject/gobiiprocess/GobiiConfig.java
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: gobii-process/pom.xml
deleted: gobii-process/src/main/java/org/gobiiproject/gobiiprocess/web/ConfigCheck.java
#############################################
### Now my working directory looks just ####
### like it did before I staged and stashed ####
Phil@GOBII-1 MINGW64 /c/phil-source/IntelliJ/gobiiproject (feature/GP1-555-04)
$ git add .
#############################################
### Proceed as usual ####
Phil@GOBII-1 MINGW64 /c/phil-source/IntelliJ/gobiiproject (feature/GP1-555-04)
$ git commit -m 'create help for new configuration options'
[feature/GP1-555-04 f381de7] create help for new configuration options
2 files changed, 1 insertion(+), 473 deletions(-)
delete mode 100644 gobii-process/src/main/java/org/gobiiproject/gobiiprocess/web/ConfigCheck.java |
JDLS - git stash, git checkout <right branch>, git stash pop.
Whoops – I didn't mean to commit that
https://sethrobertson.github.io/GitFixUm/fixup.html.
JDLS Note- Or, you know, git revert.
Whoops - Not only did I commit, but I also pushed
In other words, we need to revert HEAD to a previous version and blow out all history after that point. Changes like this can only be done with the full consent of the senior devs.
Don't do this:
http://stackoverflow.com/questions/5816688/resetting-remote-to-a-certain-commit
git reset --hard <commit-hash>
git push -f origin master
JDLS Note: Doesn’t actually remove info from the chain. If you pushed passwords, look up BFG Cleaner or some other repo cleaner. You’re in for a loong day.
Rename a branch
Apparently you cannot do this in bit bucket. [JDLS Note - clone it to the new name, then delete the old one.] So go to your GIT bash and:
git branch -m old_branch new_branch # Rename branch locally
git push origin :old_branch # Delete the old branch
git push --set-upstream origin new_branch |
More Information
Atlassian Gitflow - https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow
Caveat Emport - http://stevelosh.com/blog/2013/04/git-koans/
JDLS - Even invoking the help menu teaches you how to use git!
The novice, wanting to partake of Master Git’s vast knowledge, said: “git branch --help
”.
Master Git sat down and lectured her on the seven forms of git branch
, and their many options.
They resumed walking. A few minutes later they encountered an experienced developer traveling in the opposite direction. He bowed to Master Git and said “git branch -h
”. Master Git tersely informed him of the most common git branch
options. The developer thanked him and continued on his way.
“Master,” said the novice, “what is the nature of long and short options for commands? I thought they were equivalent, but when that developer used -h
you said something different than when I said --help
.”
“Perspective is everything,” answered the Master.
The novice was puzzled. She decided to experiment and said “git -h branch
”.
Master Git turned and threw himself off the railing, falling to his death on the rocks below.