Skip to content

Coding workflow

jhou-pro edited this page Dec 11, 2014 · 60 revisions

Coding workflow

The proposed workflow is here to liberate developers from fear of making changes!

  1. Not just the master branch
  2. From master to develop
  3. Need a feature?
  4. But what if there is a conflict?
  5. Would you like to help in developing/testing some other feature?
  6. Ready for a release?
  7. But what if the release has a bug?

The complexity and the number of current and future features require a more robust approach to managing code versioning. Currently a central server model is used where all changes are made to a single master branch. In order to better control platform changes to avoid incompatibilities and broken state, a more comprehensive workflow is introduced, which is based on gitflow. The referenced tutorial should be used to get familiarised with the gitflow workflow.

This post provides more of a step-by-step instruction on how to apply the gitflow to the platform development, listing the relevant git commands for each of the steps.

Not just the master branch

Instead of coding against the master branch, each new feature or even a change to an existing feature should have its own branch. All of such branches should be short-lived copies of the develop branch that would integrate all features and fixes over the development life cycle. The feature branches are short-lived simply because they get merged into the develop branch once corresponding features are completed, and then get deleted in order not to pollute the code tree.

There are some other branches, which will be introduced in this post as we've need them.

From master to develop

Originally any repository has only a single branch -- the master branch. The same is true for TG repo, and all existing changes to the platform as of this writing are present on the master branch.

Once the gitflow workflow is adopted, there should be no direct commits to the master branch. Instead, it would only receive new commits by getting merged with either some hotfix or release-x.x branches.

Branch develop gets created as a copy of branch master and is used to accumulate all future changes. But, this also does not happen by committing changes to develop directly.

Thus far, we know that branch develop is a copy of master, which is illustrated in ASCII art below.

master ---------------->
       \
develop -------------------->

Need a feature?

Before starting the development of any new feature and assuming that the TG project is already cloned, one should make the develop branch to be the tracking branch for development (i.e. the current one) by executing the following commands:

git pull
git checkout -b develop origin/develop

The above command need to be executed the very first time where there was no branch develop checkout before. In case it was, simply switch to it and pull changes:

git checkout develop
git pull

Next, it is needed to create a new feature branch that would accumulate all relevant changes for a specific feature. It is important to understand that a feature branch should exist not only locally, but shared with other developers by pushing it to the central repository. This makes it possible for several developers to work on the same feature. Also, there should be as many feature branches as necessary at the same time. These branches should be named uniquely, of course. It is strongly suggested that each feature be specified in a separate issue (could be an umbrella issue for smaller tasks), and thus a corresponding branch should be named Issue-#?.

For example, the following command creates a new feature branch to cover changes for issue #30:

git checkout -b Issue-#30 develop

The token develop at the end of the command indicates that a newly created branch is branched out from branch develop, but this is still a local branch. The following command should be used in order to push it to the central repository:

git push -u origin Issue-#30

After the above changes, the code tree would look something like the ASCII art below:

master ----------->
       \
develop ---------->
           \
        Issue-#30 -->

Once a feature branch is created and pushed to the central repository, several developers may start working on a corresponding feature if needed. The drill with a future branch is the same as before with the master branch -- do your changes, commit them and push to the central repository to share your changes with everyone who is concerned with that specific feature. No one else would be effected by these changes simply because they exists on a separate, specially designated branch.

What to do when feature is ready for prime time?

master ----------->
       \
develop ---------->
           \
        Issue-#30 --*---*-*->

Commit all changes, push to the central repository, make the final maven build to be sure that the feature does not break other functionality and make a pull request. The pull request is a feature of github where the author basically request others to review and comment on the implemented changes. At this stage it's a good time for other developers to review changes, and even checkout the relevant feature branch to test the feature (refer the section below).

There would be a need to specify the base and compare branches upon creation of the pull request. In order correctly create a pull request for merging of a new feature, the value for base should be the develop branch, and the value for compare should be the name of the relevant feature branch (Issue-#30 in case of our example).

Once the future is accepted by others, the corresponding branch needs to be merged into the develop branch (NOT the master branch!). This can be done by either using github pull request's merge feature (preferred) or by executing the relevant command line commands:

git checkout develop
git pull
git pull origin Issue-#30
git push

The result should be like in the diagram below:

master ----------->
       \
develop ------------*---*-*->
           \
        Issue-#30 --*---*-*->

Once the merge is done, the feature branch should be deleted in order not to pollute to code tree or to be accidentally used for further development. There would be two branches with name Issue-#30 -- one local and one in the central repository (the origin). Deleting local branch does not effect the remote branch and the other way around. Therefore, both branches need to be deleted explicitly.

git branch --delete Issue-#30
git push origin --delete Issue-#30

Please note that origin branch can also be deleted via a github interface -- a feature provided as part of the merged pull request.

The deletion of the merged feature branch leads to a cleaner code tree and prevents accidental use of discontinued branches:

master ----------->
       \
develop ------------*---*-*->

Please note also that additional git command can be used to clean up the references to the deleted origin branches (gitg --all or git branch -r will show correct branches):

git remote prune origin

But what if there is a conflict?

There could be a situation where the merge would not be possible due to conflicting changes in the feature branch and the develop branch. For example, some feature branched was merged into develop earlier, and the feature branch that you would like to merge conflicts with some of those changes.

No worries as there is nothing different to the conflict resolution process comparing to the ordinary centralised workflow with a single master branch.

In order to make things concrete, let's consider that there is a problem merging feature branch Issue-#39 due to conflicting changes. Resolving the conflict takes the following steps.

  • Switch to the develop branch and synch it with the origin by pulling the changes in.

    git checkout develop
    git pull
    
  • Try merging automatically branch develop with branch Issue-#39.

    git merge --no-ff Issue-#39
    

    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.

    In case the conflict could not be resolved automatically, git would report something like this:

    Auto-merging platform-dao/pom.xml
    CONFLICT (content): Merge conflict in platform-dao/pom.xml
    Automatic merge failed; fix conflicts and then commit the result.
    

    Running git status would reveal conflicting files than need to be edited manually. Make the necessary changes, commit and push them.

    git add .
    git commit -m "#39 Resolved conflict to merge Issue-#39."
    git push
    

Github recognised the case where a conflict resolution is associated with a pull request and marks such pull request as merged and closed.

Would you like to help in developing/testing some other feature?

Assuming that there is already a feature branch like Issue-#30 from the above example, which is pushed to the central repository, anyone can checkout that branch for code review or even to contribute some changes. Switching between branches locally is simple, but requires some discipline from a developer. There are several possible use cases.

  • You have finished all the current work, all changes pushed to the central repository and merged with branch develop, and you can help with some other feature that is being develop on its own branch.

    This is an easy case -- simply checkout a corresponding branch from origin (central repository) and start coding as usual (commands are discussed below).

  • Your're developing some feature (work in progress), but need to switch to help with the development of some other feature.

    This is a tricky situation, which has two possible options.

    • You may commit all your changes to the current branch, and even push it to the origin (not required strictly speaking, but preferable). Then, checkout the branch of the feature that needs your input, and start working on it as per usual -- modify, commit, push to that branch. Once completed, checkout the branch of the feature you were working originally.

      This approach is fine, but has one annoyance -- during the development of your current feature, your Eclipse workspace would have multiple files open and positioned specifically, some debug session may be in progress etc. All of that would be lost upon switching to a different branch! Hence, an alternative approach could be more preferable.

    • Instead of switching to a different branch in you current workspace, simply clone the project repository into a different workspace, checkout a new branch you need to help with there, and make all the changes in an isolated manner without touching your current workspace setup. That additional workspace could be specifically preserved for such "need some help" occasions that is not directly related to your primary development responsibilities.


Alright, now we can discuss how to get an existing feature branch that was created by someone else into your local repository copy.

As usual make sure that the local copy is up to date:

git pull

Then create (yes -- create) a local copy of the required feature branch from origin:

git checkout -b Issue-#10 origin/Issue-#10

The above command creates a local branch (flag -b) with the same name as the required remote branch, and associates it with the remote branch -- branch origin/Issue-#10 in this case.

This needs to be done just once, and the branch becomes the current one. The rest as usual -- make changes, commit, push (to share your changes with others).

Ready for a release?

At some stage there would be a number of features merged into the develop branch worth releasing to the client. The release process might take some times (hours or even days), and it is necessary to make sure that the release would include only the currently present in the develop branch features, and would not be mixed up with some of the futures develop after the release features are agreed upon, but before the actual release takes place.

In order to achieve this, a new release branch needs to be created as a copy of the current develop branch. This ways all future changes to the develop branch would not effect the release at all and the develop branch get cleared for incorporation of future features!

The release branch may include the version of the product to be released in order to make this information more evident during the release preparation.

For example:

git checkout -b release-1.2 develop

And don't forget to push the release branch to the origin in case some other developers need to be involved or if you're going to continue working on it from some other computer:

git push -u origin release-1.2

The following diagram outlines the discussed situation.

master ----------->
       \                         release ----->
        \                               /
develop ------------*---*-*---*--*---*------*---*-->
                                          

The release branch belongs to short lived branches like feature branches, which should be deleted immediately after being merged with the master branch and potentially even the develop branch.

Changes to branch release should be relatively minor -- minor correction to the code and documentation that might be required to polish the application before releasing it. Making changes to the release branch is no different as to any feature branch.

Once all changes are completed and the actual release should take place, the release branch should be merged with the master branch via a separate pull request, and the with the develop branch also via yet another pull request, but only if required.

The merge with the develop branch should be performed carefully, if at all, as the release would contain an updated release related project version. So, make sure that after the merge, the project version on the develop branch is update to the next SNAPSHOT!

The merge commit with the master branch should be tagged, indicating the release version number. Afterwards, the release branch needs to be deleted like any feature branch that is completed.

master -------------------------------------------->
                                                 /
       \                         release -----*-->
        \                               /        \
develop ------------*---*-*---*--*---*------*---*-->
                                          

The tag in git represents an immutable object, which is by nature is very similar to a branch, but cannot be changed (i.e. no commits can be taken out or put into it), and, thus, represent a proper way of marking release changes. So, after the merge of the release branch with master, checkout master, pull changes from the central repository (that would be the merge changes if the merge was performed via github), and then tag it.

git tag -a 1.2 -m "Release 1.2."

The tag is created locally and needs to be pushed to the central repository:

git push --tags
                                                    1.2 
master -------------*---*-*---*--*---*-------------*-|->
        \                               
develop ------------*---*-*---*--*---*------*---*--*--->
                                          

But what if the release has a bug?

Then fix it! This should be done on a new branch hotfix that should be created based on the master branch. Once all changes to hotfix are committed and pushed, make a pull request to merge that branch with master, and most likely develop to promote fixes into the main development branch.

                                                    1.2
master -------------*---*-*---*--*---*-------------*-|----->
                                                     \    /
       \                                        hotfix -*->
        \                                                 \
develop ------------*---*-*---*--*---*------*---*--*------->
                                          

Then, perform the next release and tag the master branch with appropriate tag. Branch hotfix should be deleted afterwards as any other completed feature branch.

                                                    1.2  1.2.1
master -------------*---*-*---*--*---*-------------*-|---*-|->
        \                               
develop ------------*---*-*---*--*---*------*---*--*-----*--->
                                          

Please note that the only difference with the hotfix branch comparing to other short lived branches is that it gets created as a copy of the master rather than develop branch. But, similarly to the release branch it gets merged with both master and develop branches.

Clone this wiki locally