This article introduces you to a standardized multi-user workflow for the Oracle Developer Cloud Service. The intent is to showcase a how a team starting on the Oracle developer Cloud Service would collaborate on code to address various requirements and use-cases in a typical SDLC model. We present a popular branching and merging model that lets project teams collaborate effectively for maintaining and moving forward on multiple software releases and code-lines. This is, of course, not the only workflow possible, but hopefully will introduce you to a model that you can use right away or provide ideas for formulating your own workflow that works best for the individual team.
Standardizing on a workflow softens the learning curve for new members as well providing a common set of rules for actions that are commonly performed in a typical software development scenario so that all developers take the same approach. In this article we will cover the following :
A basic knowledge of git is assumed for this article. If you are unfamiliar with basic branching and merging in git please take a quick look at the excellent in-depth information at git-scm.com . A typical project intended for production deployment goes through a development phase where all developers collaborate on various features for the application being built, a simultaneous testing phase where code is integrated and tested, then a release phase where the code that has passed tests is considered production ready and frozen for a release. Usually, towards the end of the first cycle where integration testing is happening a bunch of (or all) developers will split their time between fixing bugs that came from the testing and working on new features for the next release phase. Finally, once a release has been made and deployed to production, there may be cases where a new bug is discovered in production which will require a hot-fix. The released version of the project will of course require back-ports for upstream features, one-off patches and so on as well.
If you are an experienced user of git, you will notice that this workflow is similar to git-flow.You are right, and it is in-fact based on git-flow. For those new to git-flow, its a standardized workflow for using git. The goal is that everyone uses a common set of rules for branching and merging actions that are commonly performed in a development cycle like the one above. The next step in standardization is to have a common vocabulary so that people can easily find feature branches, release branches , bug fixes and so on.
With these requirements, we can setup a simple process for source control as follows.
Well, that was quite a bit of processes, lets make sure we go through the process in a visual manner so that its easy to better understand what to do under various circumstances. we will assume you are working on a project which you develop for your organization (as opposed to a product with multiple customers).
The figure above is simplified for easy explanation, and to show the hierarchy. In reality, branches are just labels or pointers that point at commit objects, they do not have a physical manifestation other than making it easier to refer to a commit by a name. This also means that processes like branching-off and merging two branches just realigns or moves around the labels. Its is therefor common to have multiple labels like 'bug#2359' and 'develop' on the same commit. We however depict these in the figures as a hierarchical structure, and when two branches point at the same commit, we have colored them the same with the same commit hash to identify that they are in-fact the same commit.
We have 5 branches here. The master brach is on commit '5645b2' (the short form of the SHA-1 for that commit) and since the master is on this commit, we can assume that this commit has been tested and deployed to production. In other words, if we needed to get a copy of whats on production at this very instant, we would create a branch off of master as the next release branch.
We can also see that the the release branch 'Rel2.3' was delivered to master.
At commit 'ab3456', people started diverging and creating new branches from develop. The first was bug#2359, and the second was the branch named 'OAuth Support'. These are examples of bug fix and feature branches. The developers working on these fixes or features would be committing and collaborating by contributing code to these branches. In the case of the bugfix, there were two commits 5a23e3 and 98b2e5 which the developer(s) committed to this branch. At this time we can assume that the the developers have unit tested their code and consider this fix ready to be delivered to the develop branch. They go ahead and merge their bug fix branch in to the develop branch , which simply moved the develop branch ahead to that commit. This is a example of a fast forward merge, where the the bug fix initially branched off from develop and then later was being delivered back to develop. No one else had delivered anything to develop in the meantime, which means that the changes made on the bug fix was just incremental to the code in develop. Git calls this a fast forward merge, and just involves move the develop branch or pointer to a new commit, 98b2e5 in this case. In reality, now the commit 98b2e5 should have two labels or branches pointing ta it, the bug fix branch, and the develop branch. In the figure above, we have simplified this by representing the commit twice in a hierarchical structure.
Similarly at a later time, the developers who were working on 'OAuth Support' consider their feature complete at the point where they committed '34598c2' and decided to merge the feature code in to develop branch. This merge created the commit '12c3e5'.
This example was meant to simply demonstrate one of the possible workflows with git. Workflows can be more simplistic than this, where everyone works directly on master branch (a useful model when a small team works on a short lived project like a PoC or sample application - i.e. no real production use and maintenance), or could be considerably more complex with multiple distributed repositories where multiple remotes are managed and synced between each other.
To begin with lets assume that John is on master and he wants to start working on bug fix #5678. John switches to the develop branch and then creates a new branch for his bug fix off of the develop branch.
git checkout develop git checkout -b bugfix#5678
This creates the bugfix#5678 branch and switches to it. John works on the bug fix. In the most simplistic case, John is the only developer working on the bug fix, and once he tests his fix, and believes its ready to be moved up to the develop branch, he can initiates a merge request. Which then causes the merge to be code reviewed and delivered on to the develop branch. A slightly more realistic version of this scenario is when John gets pulled in to work on a higher priority issue/feature while hes in the middle of his bug-fix.
Lets assume that John's bugfix #5678 was not a trivial one and requires him to refactor code in two files. He finishes working on the first one and is abut to start on the second when Alice requests his help with a higher priority task. He now needs to save his work so that he can get back to it later. John commits his work (5675ab) and pushes it to the remote.
git commit -m "Bug-Fix 5678 - partially done. Not yet complete." git push origin bugfix#5678
The repository looks like this now:
He could have stashed his work instead of committing as well. Stashing is a core git feature that pushes your current changes on to a stack so that you no uncommitted changes in your working copy so that you safely switch branches. Alice wants his help to work on a feature to support a JSON payloads in the application. Alice has already created the 'JSONSupport' branch off of develop and checked in some code. She needs John to make a few changes to support JSON in the module he's responsible for. Alice had been working on this in parallel. John sees Alice's branch which she just pushed to the remote so that she can collaborate with John on it :
git fetch origin git checkout JSONSupport <start work>
When John fetches the repository from the server, John is fetching all the changes people have pushed to that server/remote including new branches people have started and pushed to the remote. This brings in the JSONSupport branch that Alice has been working on. John can now switch to this branch, which changes the files in his working directory to be in the state that Alice had committed to create af4572 (the tip of Alice's branch). At this point, John can start working on this branch, add JSON support to the files/modules he's responsible for and commit back to the branch.
git add <my_file.js> git commit -m "Added JSON support for the project" git push origin JSONSupport
With John's commit, the tip of the JSONSupport branch tip moved forward. When John pushes his changes, the commit that he just created is pushed in to the remote and Alice can see it when she fetches from the remote. The repository now looks like this :
The first thing that comes mind when integrating code , especially code that resides on multiple branches, is to make sure that we review the changes and make sure that everything is okay. Different git hosts have different approaches to this, but the developer cloud makes this process simple though Review Requests. Review requests are requests you open so that someone can review the changes thats about to be made, and comment on these changes before the code is pulled in to the target branch.
Lets see how this works for John and Alice. The JSON support branch (or the feature) is complete, and Alice, who was the feature lead on this goes to the developer cloud and opens a new Review Request, this is what it looks like :
Alice, picks the review branch as the JSONSupport Branch which she wants to merge (or deliver ) from and the target branch as the develop branch, the branch she wants to merge in to or deliver to. This merge however needs to be reviewed by Scott who is one of the lead developers. Alice creates the merge requests which automatically sends an email to Scott that he needs to review a merge.
Scott can come and take a look at the review request which shows him the branch which is being requested to be merged in to develop, including all the code changes that will be introduced as part of this merge. Scott can review them and provide his comments through the review request created in Oracle Developer Cloud.
Scott can now see the added and changed files online, and also see the color coded diffs, for the changes that were made. If Scott is not happy with some implementation aspect, he can give that feedback and Alice can work on the feedback and get back to Scott. Once Scott is happy with the changes, he can approve the request. Its should be pointed out that even-though review requests provide the fabric to facilitate collaboration and approval process, the platform does not prevent a rogue merge - when some one decides to merge to develop branch on their own and push those changes without creating a review request in the first place. This is because, git naturally allows for this, and provides mechanisms like server hooks to implement access restrictions on who can commit to what branch. The developer cloud platform does not hinder or feature-limit the git source control system.
Scott approves the changes and when alice does the merge and pushes her changes, the merge moves the develop branch forward, with the new JSON Support changes. In this example, there will be no conflicts when Alice merges the code to develop branch, since the develop branch is still exactly where it was when she began working on the her feature. Since there were no changes contributed to the develop branch since Alice started working, when Alice merges her feature branch, the develop branch should receive only the incremental changes made by Alice, which means its essentially the same code that Alice has on the tip of her branch. Git realizes this and this type of a merge is called a fast forward merge, since git essentially just moves a pointer.
The develop branch now has the the code changes introduced as part of the JSONSupport feature, or rather the develop branch now points to a commit that has these changes. Notice that the commits colored the same are in-fact the same commit, they are duplicated on the figure to easily visualize the brach structure.
One common aspect we did not account for, in the merge scenario above was conflict resolution. This is because it needs to a closer discussion and there are multiple approaches to do this that suit different work styles. We discuss one that we feel is most efficient in terms of the control it allows and the ownership it enforces.
When there are conflicts during a merge, the Developer cloud platform will indicate that there are conflicts, and an automatic merge cannot be done on the web ui. Conflicts are usually caused by code that changed on develop branch since the contributor started working on his feature. At this point, the reviewer and the contributor can collaborate using the merge request facility to discuss how to resolve the conflicts. However, we feel that a more efficient way to work is for the contributor to resolve the conflicts before creating the merge request. The feature developer does this by merging the latest develop in to his feature branch first, and resolving the conflicts on this feature branch. Then after testing and ensuring that he has caused no regression or unexpected side effects, he merges his code to develop, which will now be a fast forward merge.
This guideline for merging, ensures that merge requests do not lead to unexpected bugs or side effects since the possible conflicts will be resolved and tested in the feature branch, before the develop branch gets it.
The reviewer can still see how how the contributor chose to resolve the conflicts and provide feedback if he does not agree with the changes. This approach has the advantage that it limits the back and forth during the review process and presents the reviewer with a possible conflict resolution strategy. This also moves the responsibility of delivering code that is in sync with the develop branch to the feature developer. The feature developer now needs to make sure that he accounts for the latest changes on the code line rather than being isolated in his own feature code. This also encourages the feature developers to frequently pull in the latest code line changes so that the merge process is smoother and gradual.
Lets look at at this process in detail. John has now gone back to his bug fix, after helping Alice with the JSONSupport feature and finishes the bug fix. He is now ready to check-in his bug fix. First, the develop branch has now moved forward from when he started work on the bug-fix. So he now has to merge the changes and perhaps resolve conflicts. There will be conflicts only if there were changes to the same line of code in the two branches being merged. For our illustration, lets say Johns has to resolve conflicts before he can merge. Once John creates the Merge request, and Scott tries to review it, Scott will be alerted by the developer cloud service that an automatic merge cannot be done from the Developer cloud console, and will indicate the files that have merge conflicts.
One possible workflow is for Scott to look at the merge conflicts, and then suggest a resolution in the Merge request, that John can implement. Although this a simple workflow, a better way is for John to resolve the conflicts before he starts the Merge request. This way, John can resolve the conflicts, or re-work his code so that it works with the latest on develop and then run tests to make sure that there are no regressions before he creates a merge request for Scott to approve. To resolve conflicts before creating a merge request, John can pull in the latest code from develop to check if the code is going to have merge conflicts, and if so resolve them. This is what the repository would look like when john pulls in the develop branch. The merge (automatic or otherwise) creates a new commit that merges the develop branch in to the bug-fix branch moving the bug fix branch forward (Note that the develop branch stays where it is, since the target of the merge was the bug-fix branch).
The tip of the bug-fix branch now contains the merged code- the latest develop branch with John's bug-fix and any conflict resolutions. John can deliver his bug-fix to develop which would be come another Fast forward merge since he already resolved conflits.
Major releases to the code also get their own release branch. This branch is created off of the develop branch. Depending on the release model, this branch os either created once planned feature branches have been merged in to the develop branch (effectively all featured planned for the release are delivered), or in the case of a timed release plan, once the release window is upon the team (whatever features are delivered will make the release). Once created, this branch used to fix issues related to the release, or evolve the release through a release candidate process. Once this branch is ready for release, it is merged wit master and the final build is created. Once this is done, this branch can be used for long term maintenance or feature back-ports. The merge from the release branch tot he master is tagged with the release number, and the master branch will always have the latest released code line, this is important when we need to fix production issues for the project (as opposed to a product where hot fixes are best done on the release branches since multiple customers will be on multiple releases).
Lets assume that with the feature branches and bug fixes we have delivered to develop branch so far, we are ready for a release. Scott prepares for the release by creating the release branch. When created, the release branch tip points tot he same commit as the tip of the develop branch. But Scott then makes a change that is exclusive to this release in the Rel2.4 branch, like changing the version number.
Once QA has finished testing the build and the team is satisfied with the quality of this branch, it can be released by merging it with the master or the production branch.
We hope that gives you a solid foundation on setting up a standardized process which you can tailor to your needs and development style, while jumpstarting your development efforts on the Oracle Developer Cloud Service.