Git Workflow
Terminology
For the purposes of this tutorial I'll use some abbreviations short names to refer to particular git repositories.
local - your local repository that exists on your machine
origin - your forked version of the Sage Bionetworks repository (you did fork the repo on github right?)
upstream - the "official" repository that is owned by Sage Bionetworks on github.
Bringing in Upstream Changes
There are two basic paths for the git workflow:
- You have local changes committed to your local develop branch
- You have local changes committed to a local "feature" branch
Now, you see that the upstream repository has changes in it from one of your teammates checking in code (via code review!). In order for your changes to make it into upstream with a minimum of fuss, you need to bring in those changes before you can submit a pull request.
Changes to local develop branch
This is probably basically what your branch history looks like in this scenario:
A---B---C develop \ D---E upstream/develop
The problem here, is that if you simply do a git pull to merge in the upstream changes, you'll get an extra commit, and if git resolves merge conflicts the wrong way you could potentially end up committing changes that undo whatever work occurred upstream. So instead what you want to do is this:
git fetch upstream # make sure you have all the latest info on your upstream repo
git checkout develop # be on your develop branch
git rebase upstream/develop
This should result in your history looking like this:
A---B---D---E upstream/develop \ C' develop
Now move on to Sharing Your Changes
Changes to local "feature" branch
This is a little simpler. For it to work smoothly though, your local develop branch must be able to fast-forward to where upstream develop is. Put another way, your local develop branch must point at a commit that is a direct or indirect parent of the commit that upstream develop is pointing at.
That is if we have commits:
A---B---C---D---E---F
and upstream/master points to F, then develop points to any of A through E. Similarly, your feature branch can be based off of any commit on the develop branch (before or after your local develop).
Now you do
# You can skip this if you're already on the develop branch
git checkout develop
git pull upstream develop
Now assuming that you are done with the feature you're working on and want to bring your changes into the main develop branch so that you can issue a pull request. Now:
git merge my-feature
git branch -d myfeature # remove the now useless branch
Git should ask you to enter a commit message. Now you're ready to Share Your Changes
Sharing Your Changes
Squash feature commits into one
Git allows you to rewrite your history and squash several commits into a single commit. While Git encourages (and often requires) many commits for a single feature, all these commits make the upstream repository's history confusing. To squash commits you do an interactive rebase:
- Make sure you don't have any uncommited changes
- Find the commit just before your work started
- An easy way to do this is using the command line git graph specified on our Git page
In this example, if I wanted to squash feature 123's commits, I need to use commit 4a25eee for the rebase
* 073c346 (develop) feature work 123 - all done (David Burdick) * 23e1f7b feature work 123 - small commit (David Burdick) * f4f4f99 feature work 123 - initial feature code (David Burdick) * 4a25eee Some commit from someone else prior to your work (Jay Hodgson) |\ | * 0437009 blah2 (David Burdick) | * ee3fc4e blah1 (David Burdick)
git rebase -i <commit>
- For the example above, <commit> = 4a25eee
Git will open a shell editor and show you the commits
pick 073c346 feature work 123 - all done pick 23e1f7b feature work 123 - small commit pick f4f4f99 feature work 123 - initial feature code # Rebase 073c346..f4f4f99 onto 4a25eee # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
- change "pick" to "squash" (or just "s") to squash commits up into the previous commit. Don't worry about changing the comments, that comes later
Example Feature squash
pick 073c346 feature work 123 - all done s 23e1f7b feature work 123 - small commit s f4f4f99 feature work 123 - initial feature code # Rebase 073c346..f4f4f99 onto 4a25eee # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message ...
- Save and exit the editor.
- Git now brings up a comments editor.
- Delete all non commented lines and write a message like "Feature PLFM-123 work", which will be the commit message for the single commit
- Save and exit
The git graph history will now look like this
* b0f5732 Feature PLFM-123 work (David Burdick) * 4a25eee Some commit from someone else prior to your work (Jay Hodgson) |\ | * 0437009 blah2 (David Burdick) | * ee3fc4e blah1 (David Burdick)
- Two things to remember
- If something goes wrong or you chose the wrong range, you can delete everything in the editors to abort the rebase
- As with any rebase, DO NOT rebase commits that have been pushed to public repositories (i.e. GitHub). This will mess up anyone who has forked the public repository. However, it is okay to push this type of rebase change up to your own GitHub fork as no one should be forking your forked origin repo.
Send changes to GitHub and Pull Request
Now to share your changes with the rest of the team (starting from your local develop branch)
git push origin develop
Now go to your GitHub repository page and click the "Pull Request" button, mark the changeset as
Sage-Bionetworks/<repo-name>@develop to <your-name>/<repo-name>@develop
Then send someone an email, and schedule your code review!