In this article, we'll look at the Git branch model called git flow. The model was proposed by Vincent Driessen in his article “A successful Git branching model” and is used in various variations. The Git flow defines a strict branching model designebd around the project release. This provides a robust framework for managing larger projects. The general diagram of the git branching model is as follows:
Everything You Need To Know About Git Flow Model.
Thank you for Subscription!

Gitflow uses its own git-flow toolkit that integrates seamlessly with Git, adding new Git commands.
Gitflow is ideally suited for projects that have a scheduled release cycle. This workflow doesn’t add any new concepts or commands beyond what’s required for the Feature Branch Workflow. Instead, it assigns very specific roles to different branches and defines how and when they should interact. In addition to feature branches, it uses individual branches for preparing, maintaining, and recording releases. Of course, you also get to leverage all the benefits of the Feature Branch Workflow: pull requests, isolated experiments, and more efficient collaboration.
We want to make your Git development process easier and so we’re excited to announce a new support tutorial for your team’s branching workflow.
What is git flow model Initialization
Instead of a single master branch, this workflow uses two branches to record the history of the project. The master branch stores the official release history, and the develop branch serves as an integration branch for features. It's also convenient to tag all commits in the master branch with a version number.
1. Open terminal or command line and go to your project directory (It must be initialized with git).
2. Run git flow init
It will ask some questions about the different branches’ naming structure. Please write answers like below.
- Branch name for production releases: master
- Branch name for development: develop/stage/sandbox
- Feature branches?: <TaskId>-short-description
- Bugfix branches?: <TaskId>-fux-bug/-fix-issue-short-description
- Hotfix branches?: <TaskId>-hotfix-short-description
Let us dwell on the names of branches.

At the core, the develop git flow branching model is greatly inspired by existing git models out there. The central repo holds two main git flow branches with an infinite lifetime:
- master
- develop
The master branch at origin should be familiar to every Git user. Parallel to the master branch, another branch exists called develop.
We consider origin/master to be the main branch where the source code of HEAD always reflects a production-ready state.
We consider origin/develop to be the main branch where the source code of HEAD always reflects a state with the latest delivered develop changes for the next release. Some would call this the “integration branch”. This is where any automatic nightly builds are built from.
When the source code in the develop branch reaches a stable point and is ready to be released, all of the changes should be merged back into master somehow and then tagged with a release number. How this is done in detail will be discussed further on.
Therefore, each time when changes are merged back into master, this is a new production release by definition. We tend to be very strict at this so that theoretically, we could use a Git hook script to automatically build and roll-out our software to our production servers every s time there waa commit on master.
Supporting branches
Next to the main branches master and develop, our develop git flow model uses a variety of supporting branches to aid parallel develop between team members, ease git flow tracking of features, prepare for production releases and to assist in quickly fixing live production problems. Unlike the main branches, these git flow support branches always have a limited lifetime, since they will be removed eventually.
The different types of branches we may use are:
- Feature branches
- Release branches
- Hotfix branches
Each of these branches has a specific purpose and are bound to strict rules as to which branches may be their originating branch and which branches must be their merge targets. We will walk through them in a minute.
By no means are these branches “special” from a technical perspective. The branch types are categorized by how we use them. They are of course plain old Git branches.
Feature branches

May branch off from:
develop
Must merge back into:
develop
Branch naming convention:
anything except master, develop, release-*, or hotfix-*
Feature branches (or sometimes called topic branches) are used to develop new features for the upcoming or a distant future release. When starting a develop of a feature, the target release in which this feature will be incorporated may well be unknown at that point. The essence of a feature branch is that it exists as long as the feature is in develop, but will eventually be merged back into develop (to definitely add the new feature to the upcoming release) or discarded (in case of a disappointing experiment).
Feature branches typically exist in developer repos only, not in origin.
Creating a feature branch
When starting work on a new feature, branch off from the develop branch.
$ git checkout develop
$ git pull
$ git checkout -b <TaskId>-short-description
Switched to a new branch <TaskId>-short-description
Incorporating a finished feature on develop
Finished features may be merged into the develop branch to definitely add them to the upcoming release:
$ git checkout develop
Switched to branch 'develop'
$ git checkout pull
Update your local codebase from branch 'develop'
$ git merge --no-ff <TaskId>-short-description
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d <TaskId>-short-description
Deleted branch myfeature (was 05e9557).
$ git push origin develop
The --no-ff flag 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. Compare:

In the latter case, it is impossible to see from the Git history which of the commit objects together have implemented a feature—you would have to manually read all the log messages. Reverting a whole feature (i.e. a group of commits), is a true headache in the latter situation, whereas it is easily done if the --no-ff flag was used.
Yes, it will create a few more (empty) commit objects, but the gain is much bigger than the cost.
Release branches
May branch off from:
develop
Must merge back into:
develop and master
Branch naming convention:
<TaskId>-short-description-release/merge/release-[number]
Release branches support the preparation of a new production release. They allow for last-minute dotting of i’s and crossing t’s. Furthermore, they allow for minor bug fixes and preparing meta-data for a release (version number, build dates, etc.). By doing all of this work on a release branch, the develop branch is cleared to receive features for the next big release.
The key moment to branch off a new release branch from develop is when develop (almost) reflects the desired state of the new release. At least all features that are targeted for the release-to-be-built must be merged in to develop at this point in time. All features targeted at future releases may not—they must wait until after the release branch is branched off.
It is exactly at the start of a release branch that the upcoming release gets assigned a version number—not any earlier. Up until that moment, the develop branch reflected changes for the “next release”, but it is unclear whether that “next release” will eventually become 0.3 or 1.0 until the release branch is started. That decision is made at the start of the release branch and is carried out by the project’s rules on version number bumping.
Creating a release branch
Release branches are created from the develop branch. For example, say version 1.1.5 is the current production release and we have a big release coming up. The state of develop is ready for the “next release” and we have decided that this will become version 1.2 (rather than 1.1.6 or 2.0). So we branch off and give the release branch a name reflecting the new version number:
$ git checkout develop
$ git pull
$ 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(-)
After creating a new branch and switching to it, we bump the version number. Here, bump-version.sh is a fictional shell script that changes some files in the working copy to reflect the new version. (This can, of course, be a manual change—the point being that some files change.) Then, the bumped version number is committed.
This new branch may exist there for a while, until the release may be rolled out definitely. During that time, bug fixes may be applied in this branch (rather than on the develop branch). Adding large new features here is strictly prohibited. They must be merged into develop, and therefore, wait for the next big release.
Finishing a release branch
When the state of the release branch is ready to become a real release, some actions need to be carried out. First, the release branch is merged into master (since every commit on master is a new release by definition, remember). Next, that commit on master must be tagged for easy future reference to this historical version. Finally, the changes made on the release branch need to be merged back into develop, so that future releases also contain these bug fixes.
The first two steps in Git:
$ git checkout master
$ git pull
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2
The release is now done and tagged for future reference.
Edit: You might as well want to use the -s or -u "key" flags to sign your tag cryptographically.
To keep the changes made in the release branch, we need to merge those back into develop, though. In Git:
$ git checkout develop
$ git pull
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
This step may well lead to a merge conflict (probably even, since we have changed the version number). If so, fix it and commit.
Now we are really done and the release branch may be removed since we don’t need it anymore:
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
Hotfix branches

May branch off from:
master
Must merge back into:
develop and master
Branch naming convention:
hotfix-*
Hotfix branches are very much like release branches in that they are also meant to prepare for a new production release, albeit unplanned. They arise from the necessity to act immediately upon an undesired state of a live production version. When a critical bug in a production version must be resolved immediately, a hotfix branch may be branched off from the corresponding tag on the master branch that marks the production version.
The essence is that the work of team members (on the develop branch) can continue, while another person is preparing a quick production fix.
Creating the hotfix branch
Hotfix branches are created from the master branch. For example, say version 1.2 is the current production release running live and causing troubles due to a severe bug. But changes in development are yet unstable. We may then branch off a hotfix branch and start fixing the problem:
$ git checkout master
$ git pull
$ git checkout -b hotfix-1.2.1
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(-)
Don’t forget to bump the version number after branching off!
Then, fix the bug and commit the fix in one or more separate commits.
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)
Finishing a hotfix branch
When finished, the bugfix needs to be merged back into master, but also needs to be merged back into develop, in order to safeguard that the bugfix is included in the next release as well. This is completely similar to how release branches are finished.
First, update master and tag the release.
$ git checkout master
$ git checkout pull
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
Edit: You might as well want to use the -s or -u <key> flags to sign your tag cryptographically.
Next, include the bugfix in develop, too:
$ git checkout develop
$ git checkout pull
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
The one exception to the rule here is that, when a release branch currently exists, the hotfix changes need to be merged into that release branch, instead of develop. Back-merging the bugfix into the release branch will eventually result in the bugfix being merged into develop too when the release branch is finished. (If work in develop immediately requires this bugfix and cannot wait for the release branch to be finished, you may safely merge the bugfix into develop now already as well.)
Finally, remove the temporary branch:
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
.gitignore file
git add git commit git diff git stash .gitignore
Git sees every file in your working copy as one of three things:
1. tracked - a file which has been previously staged or committed;
2. untracked - a file which has not been staged or committed; or
3. ignored - a file that Git has been explicitly told to ignore.
Ignored files are usually built artifacts and machine-generated files that can be derived from your repository source or should otherwise not be committed. Some common git flow examples are:
- dependency caches, such as the contents of /node_modules or /packages
- compiled code, such as .o, .pyc, and .class files
- build output directories, such as /bin, /out, or /target
- files generated at runtime, such as .log, .lock, or .tmp
- hidden system files, such as .DS_Store or Thumbs.db
- personal IDE config files, such as .idea/workspace.xml
Ignored files are tracked in a special file named .gitignore that is checked in at the root of your repository. There is no explicit git ignore command: instead, the .gitignore file must be edited and committed by hand when you have new files that you wish to ignore. .gitignore files contain git flow patterns that are matched against file names in your repository to determine whether or not they should be ignored.
Git ignore patterns
.gitignore uses globbing patterns to match against file names. You can construct your patterns using various symbols:
Pattern | Example matches | Explanation* |
---|---|---|
**/logs |
logs/debug.log logs/monday/foo.bar build/logs/debug.log |
You can prepend a pattern with a double asterisk to match directories anywhere in the repository. |
**/logs/debug.log | logs/debug.log build/logs/debug.log but not logs/build/debug.log |
You can also use a double asterisk to match files based on their name and the name of their parent directory. |
*.log | debug.log foo.log .log logs/debug.log |
An asterisk is a wildcard that matches zero or more characters. |
*.log !important.log |
debug.log trace.log but not important.log logs/important.log |
Prepending an exclamation mark to a pattern negates it. If a file matches a pattern, but also matches a negating pattern defined later in the file, it will not be ignored. |
*.log !important/*.log trace.* |
debug.log important/trace.log but not important/debug.log |
Patterns defined after a negating pattern will re-ignore any previously negated files. |
/debug.log | debug.log but not logs/debug.log |
Prepending a slash matches files only in the repository root. |
debug.log | debug.log logs/debug.log |
By default, patterns match files in any directory |
debug?.log | debug0.log debugg.log but not debug10.log |
A question mark matches exactly one character. |
debug[0-9].log | debug0.log debug1.log but not debug10.log |
Square brackets can also be used to match a single character from a specified range. |
debug[01].log | debug0.log debug1.log but not debug2.log debug01.log |
Square brackets match a single character form the specified set. |
debug[!01].log | debug2.log but not debug0.log debug1.log debug01.log |
An exclamation mark can be used to match any character except one from the specified set. |
debug[a-z].log | debuga.log debugb.log but not debug1.log |
Ranges can be numeric or alphabetic. |
logs | logs logs/debug.log logs/latest/foo.bar build/logs build/logs/debug.log |
If you don't append a slash, the pattern will match both files and the contents of directories with that name. In the example matches on the left, both directories and files named logs are ignored. |
logs/ | logs/debug.log logs/latest/foo.bar build/logs/foo.bar build/logs/latest/debug.log |
Appending a slash indicates the pattern is a directory. The entire contents of any directory in the repository matching that name – including all of its files and subdirectories – will be ignored. |
logs/ !logs/important.log |
logs/debug.log logs/important.log |
Wait a minute! Shouldn't logs/important.log be negated in the example on the left Nope! Due to a performance-related quirk in Git, you can not negate a file that is ignored due to a pattern matching a directory |
logs/**/debug.log | logs/debug.log logs/monday/debug.log logs/monday/pm/debug.log |
A double asterisk matches zero or more directories. |
logs/*day/debug.log | logs/monday/debug.log logs/tuesday/debug.log but not logs/latest/debug.log |
Wildcards can be used in directory names as well. |
logs/debug.log | logs/debug.log but not debug.log build/logs/debug.log |
Patterns specifying a file in a particular directory are relative to the repository root. (You can prepend a slash if you like, but it doesn't do anything special.) |
** these explanations assume your .gitignore file is in the top level directory of your repository, as is the convention. If your repository has multiple .gitignore files, simply mentally replace "repository root" with "directory containing the .gitignore file" (and consider unifying them, for the sanity of your team).*
In addition to these characters, you can use # to include comments in your .gitignore file:
# ignore all logs
*.log
You can use \ to escape .gitignore pattern characters if you have files or directories containing them:
# ignore the file literally named foo[01].txt
foo\[01\].txt
Our Experience
To organize team work on a project, we use the Git file version control system. Using Git has several advantages over other ways of organizing collaboration:
- saving a complete history of file changes with the ability to revert to previous versions
- synchronization of changes between the team and automatic merging of changes
- the ability to work with large binary files
Git is a distributed system and each developer or designer has their own local repository (repository). Synchronization between local repositories is carried out through a central "shared" repository, which we place on a dedicated machine (server). The server can be accessed via the SSH protocol. We suggest using this type of cooperation between team members.
Typical workflow:
1. As you work, files are created, modified, or deleted in local repositories.
2. Upon completion of a certain logical stage of work, it becomes necessary to commit changes (commit) and / or synchronize with colleagues.
3. Preparation of files for commit is carried out - accounting of changed, new and deleted files, as well as reset of changes.
4. The commit is in progress.
5. Local changes are uploaded to the shared repository and made available to colleagues.
Summary
Your local code bases should always be relevant at the time of creating the merge or development branch, as well as creating a pull request for the admin.