-
Notifications
You must be signed in to change notification settings - Fork 4
Development Workflow
The objective of this document is to outline a workflow which will allow multiple developers to develop disparate features simultaneously while keeping version control history as linear as possible and promoting cohesive change sets represented by individual branches. If followed correctly the branching and merging strategies outlined here will ease continuous integration of new feature development into the code base and allow for bugs to be tackled and pushed to production with a minimum of friction. Features can be developed and deployed independent of their development timeline.
This workflow differs significantly from the way many developers are used to using Git. While there is a learning curve for the individual developer, adhering to the process benefits for the team in terms of ease of code review, compatibility with Continuous Deployment techniques, and readability of development history. It is very important to follow this process precisely, and there are several steps that are easy to overlook when first learning the steps of the workflow. The following points are covered in detail in various sections of this document, but are listed here for emphasis due to their importance to this workflow process.
- In order for this workflow to be successful, you must execute all the commands for a given step.
- Never use
git pull
to sync your local repo with the remote. This workflow depends on the use ofgit fetch
andgit rebase –p
to update local repositories. This is critical. - You will occasionally run into merge conflicts when rebasing. Resolving conflicts is not covered in this workflow. If you are not comfortable or not experienced with resolving conflicts, contact the release manager to help walk you through them if they arise.
- Always
git fetch
andgit rebase –p
the development branch onto itself immediately before merging your feature branch. (see point #1) - Always
git fetch
andgit rebase –p
your feature branch onto the development branch immediately before merging your feature branch. (see point #1) - Always use the
--no-ff
flag when merging your feature branch into the development branch. For instance,git merge --no-ff feature-myNewFeature
- It is highly recommended that you rebase your feature branches onto the development branch daily. This prevents branches from getting stale and eases the resolution of merge conflicts.
This document shows two sets of commands for some steps. Those in grey are the standard git commands that you will need to execute for the step. If you have the aliases that are described in the [Tips] section installed, you can instead run the commands in green, as they provide a more convenient syntax to accomplish the same result as the standard result. You only need to run the commands in grey or in green, not both. See the [Tips] section of this document for additional ways to make using this workflow easier.
Main branches are permanent branches. They are never deleted and are the branches upon which the production, test, and development servers depend for deployment.
Tested, Production ready code. Successful builds from the HEAD of this branch are deployed to production.
Branches from | N/A |
Allowed branches | test, hotfixes |
Must merge into | N/A |
Naming convention | master |
Allow direct commits? | No |
Code that is a clone of production with configuration changes which are handled by the CI server. Used for QA and client review. Successful builds from the HEAD of this branch are deployed to the test server.
Branches from | master |
Allowed branches | dev |
Must merge into | master |
Naming convention | test |
Allow direct commits? | No |
Branch for integration testing of new features by the developers. Successful builds from the HEAD of this branch are deployed to the development server.
Branches from | test |
Allowed branches | features |
Must merge into | test |
Naming convention | dev |
Allow direct commits? | No |
Support branches are temporary branches upon which features and fixes are developed. They always merge upwards into the appropriate main branch. Once they are merged upwards and tested, they are deleted.
Branches in local development environments for the purpose of new feature development. Typically not pushed to remote repo except under special circumstances. These contain new functionality whether it be custom development or integration of a module, plugin, etc.
Branches from | dev |
Allowed branches | N/A |
Must merge into | dev |
Naming convention | IssueID |
Allow direct commits? | Yes |
Branches for bug fixes in production. Should be a last resort. Give preference to Feature branches where possible.
Branches from | dev (see "Hotfix branches" section later in this document) |
Allowed branches | N/A |
Must merge into | master / possibly test+dev |
Naming convention | IssueID |
Allow direct commits? | Yes |
The master branch will always contain production ready code. No commits will ever be made directly to master for any reason. Advancement of master will be through merges from test and hotfix branches only. Those processes are described shortly.
The test branch the intermediate branch between development and master. Its purpose is to be as close to a production clone as possible for the purpose of deployment to a test server for QA and client review purposes. Any necessary configuration changes are handled via the CI server. The test branch is only advanced through merges from development and, very rarely, from hotfix branches merged down. Direct commits should never be made to test, nor should branches be based off of this branch.
The development branch is the most active permanent branch. Its purpose is to integrate and test compatibility of newly developed features before merging up into test. The development branch is advanced through merges of feature branches and, very rarely, hotfix branches. Direct commits should never be made to the development branch.
Feature branches are where all development should take place, and should be named after their corresponding issue number. Feature branches should typically be kept as local branches and should only be pushed to the remote repository when there is need to collaborate on with other developers on the feature. The process for developing and integrating a feature branch is as follows:
Ensure you are on the development branch and that it is up to date with the remote repository.
git gup dev
which expands to the following:
git fetch --all --prune
git checkout dev
git rebase –p origin/dev
Create a new branch based on the development branch with a descriptive name that follows the naming convention from above.
git cob feature-[descriptor]
which expands to the following:
git checkout -b feature-[descriptor]
Commit code to your feature branch as needed during development.
git checkout feature-[descriptor]
<Make some awesome changes>
git commit –a –m “your commit message”
If you need to have someone review your work or make their own changes you can push to the remote repository.
git push -u origin feature-[descriptor]
The simplest way to keep a feature branch up to date is to keep it local and rebase it on top of the HEAD of the development branch daily. For a local feature branch, you only need to execute the commands in 4a.
If you are collaborating with multiple developers on a feature branch and you have pushed it to the remote repository, the updating/rebasing process is slightly more complex. The feature branch owner should follow the commands on 4b, and collaborators should follow the commands on 4c. It is important to follow these commands in order to keep the repository clean and readable.
If you have not pushed your feature branch to the remote repository, to integrate new work from development into your feature branch, fetch changes from the remote repository and rebase your feature branch on top of development.
git gup feature-[descriptor] dev
which expands to the following:
git fetch --all --prune
git checkout dev
git rebase -p origin/dev
git checkout feature-[descriptor]
git rebase -p dev
If you have pushed your feature branch to the remote repository and you are the feature branch owner, to integrate new work from development into the feature branch that has collaborators on the remote, fetch changes from the remote repository, rebase development on top of its respective remote, rebase the feature branch on top its respective remote, and finally rebase the feature branch onto development. Only the owner of the feature branch should rebase it onto development.
git gup --update-both feature-[descriptor] dev
which expands to the following:
git fetch --all --prune
git checkout dev
git rebase -p origin/dev
git checkout feature-[descriptor]
git rebase -p origin/feature-[descriptor]
git rebase -p dev
IMPORTANT: If you have pushed your feature branch to the remote repository, you will have to force the push of the rebased feature branch to the remote. This is the only circumstance where you will use the “--force” flag when pushing a branch. Never force pushes to the development branch, or any of the other main branches.
git push --force origin feature-[descriptor]
For the collaborators of a feature branch that has been pushed to the remote repository, to integrate new work on the feature branch, fetch changes from the remote repository and rebase the feature branch on top of the remote.
git gup feature-[descriptor]
which expands to the following:
git fetch --all --prune
git checkout feature-[descriptor]
git rebase -p origin/feature-[descriptor]
To push the new changes to the feature branch, use the following command:
git push origin feature-[descriptor]
Once you are satisfied with the quality of the code for the new feature, you will need to update the development branch with the latest code changes, rebase your feature branch on top of the HEAD of development, merge it into development, and push to the remote repository.
git gup feature-[descriptor] dev
git mnf feature-[descriptor] dev
git push
which expands to the following:
git fetch --all --prune
git checkout dev
git rebase -p origin/dev
git checkout feature-[descriptor]
git rebase -p dev
git checkout dev
git merge --no-ff feature-[descriptor]
git push
Now we can delete the feature branch locally
git branch –d feature-[descriptor]
And if the branch has been pushed, delete it remotely
git push origin :feature-[descriptor]
NOTE: The release manager will need to be consulted when creating and cherry picking a hotfix branch. This process circumvents the standard workflow and can introduce deltas in the code between branches if not done with care. As a result, this process should be used sparingly.
Hotfix branches are simply feature branches that are squash-picked directly to the production branch. This may be necessary when features are staged on development, test, or both, but a production bug warrants a fix before those features can be approved for deployment. The process follows that of any other feature branch in terms of branching and merging, however all of the branch's commits are squash-picked to prod with a commit message of "HOTFIX: feature-[descriptor]". In cases where the same areas of code have been touched by other feature branches that have yet to be deployed on dev or test, the code may need to developed directly against prod (WITHOUT deploying each commit) and cherry picked down to dev where any conflicts or incompatibilities may be addressed. The same may need to be done on test when features are staged but not yet deployed to production. It is of the utmost importance to ensure that, once the hotfix feature branch has been merged to test and test to production, all 3 branches' code is identical. After a hotfix, any merge from a lower branch to a higher branch should be diffed against the lower branch's ultimate commit and the higher branch's resultant merge commit. If differences are found, the merge should be reverted and the hotfix modified to eliminate the differences. Otherwise future issues may arise. TODO: clarify and elaborate on the hotfix process
- Configure Git to use the
rerere
command when resolving conflicts. This can prevent you from having to manually resolve the same conflict over and over again. Rungit config --global rerere.enabled true
to set this, or in your global .gitconfig like so:
[rerere]
enabled = true
-
Installing TortoiseGit/GitKraken/gitk/whatever is highly encouraged. It makes the process of committing, visualizing the commit history, and diffing file versions much, much easier.
-
When renaming files in a git repository on Windows, be sure to use git mv instead of renaming the file in Explorer. Msysgit will not pick up on the filename change otherwise and instead will list the original filename as a deleted file and the renamed filename as a new, untracked file. This results in a break in the file’s history when looking at the git log as well as inflation of the repository size as git will create new blobs for what it perceives as a new file.
-
If you should add files to your repository that you subsequently want to remove without deleting them from your filesystem use the following command, use
git rm
with the--cached
flag, for instance:
git rm --cached myfile.foo
This will remove the file from the repository, but not from your filesystem. Keep in mind that this is not a total solution since when other developers pull down your commit their version of myfile.foo will be deleted!