This page describes the Net-SNMP Git Workflow. Using the contents of this page, users should be able to learn how to create patches and developers can learn how to manage changes to the Net-SNMP Git Tree.
These will likely make more sense after you've read the sections and examples below this.
- Patches should always be applied to the earliest branch it's expected to be included into.
- Branches should be merged upward into the branch above it. IE, V5-4-patches would be occasionally merged into V5-5-patches.
- Changes in V5-4-patches that shouldn't go into V5-5-patches may need to be reverted by hand, but they should be rare.
- New features or complex bug fixes should go into their own branch, derived off of the earliest branch they should be applied against.
- If a decision changes with respect to which branch it should eventually be merged into, use git rebase to move it's root.
- Once stable itself, the completed feature or bug fix branch should be merged into the appropriate tree.
- New feature branches should always be based off of the master branch
- features that require other new features that haven't been merged themselves may need to root off of the other feature until they get merged into the master.
This is mostly automated!
Much of the work below is actually automated and is here mostly for your education. See the Git Branch Management page for a page that describes the shell functions you should really consider using.
Managing patches in the Git Tree
First off, lets start by looking at what the SVN tree looks like at the time it changed to Git. In SVN, patches were applied to all the branches at once so the tree is very "branchey", but with no merges.
# git clone net-snmp-git-url-here net-snmp.git # cd net-snmp.git
Applying Patch #1
In SVN developers were encouraged to commit fixes to all the branches at once. This will be a major change when switching to Git. Patches should be applied to the earliest branch they're relevant for. Note that git makes it easy to switch branches with a current change still in place, see below in section #Figuring_out_which_branch_to_apply_to of this page.
In this example, we're applying patch p1 to an early branch:
# git checkout V5-3-patches # patch < p1 # git commit -m "p1" -a
Note: I switched to the 5.3 patches code in the same directory as the master; ie I didn't change directories anywhere
Note that, unlike SVN, we are only applying it manually to the earliest tree. This is a difference from how we dealt with things in SVN.
Merging the patch upward
To get the patch to move into the later releases, you merge the full branch into the later-versioned tree:
# git checkout V5-4-patches # git merge --log V5-3-patches
This creates a new commit point in the later tree with the merged code contained into the later versioned tree. This is done automatically when running 'git merge' and is very easy to do. Unless, of course, there are conflicts. But that's for a later discussion.
Merging forward again
Then, of course, that change will continue to propagate up through the patch trees:
# git checkout V5-5-patches # git merge --log V5-4-patches
Adding in pain: patches and commits everywhere
With many active developers working on a code base, you may end up with multiple commits and patches all over the place. That's just fine. There is no requirement that these patches get merged immediately upward. In fact, most of the time there probably isn't a good reason to do so.
# git checkout V5-3-patches # patch < p2 # git commit -m "applied p2" -a # git checkout V5-4-patches # # edit code to produce changes resulting in p3 # git commit -m "fixed something" -a # ... # git commit -m "done messing around after applying p5 to 5.4 tree" -a
Worry not about the pain
Git does a good job of not letting you have to think too much of the pain. It carefully merges the branches in a way that is generally perceived to be much better than SVN or CVS, if you're used to merge philosophies from those tools. It's even ok to merge some things before others. They'll all get upstream eventually.
Merging V5-4 forward to V5-6 *before* V5-3 gets upward merged:
# git checkout V5-5-patches # git merge --log V5-4-patches # git checkout V5-6-patches # git merge --log V5-5-patches
The complete merge many points later
Eventually, everything will end up moving forward properly. Sometimes this will be in groups, sometimes as single patches.
# git checkout V5-4-patches # git merge --log V5-3-patches # git checkout V5-5-patches # git merge --log V5-4-patches # git checkout V5-6-patches # git merge --log V5-5-patches # git checkout master # git merge --log V5-6-patches
Of course, git has real tags (unlike SVN), so tagging is simple and adding a new 5.3.4 should leave the desired results. A new tag:
# git checkout V5-3-patches # git tag Ext-5-3-4
Features and complex patches should generally be developed in their own trees. If they're worked on independently, they may never even be seen by other people. Or if people or working together on something, it may be critical that feature trees are pushed to the public repo. In the end, branches may or may not exist beyond your local git repo.
Lets assume we're working on two new features. They're complex enough that they deserve they're own tracking branch. Why? Because it lets you carefully track you work and make incremental improvements, including bugs, and know you haven't affected other people's critical work (the main branches). Branches in git are very very cheap. So use them. A lot. Cause if you don't like them later, they're just as easy to delete!
Reducing the diagram a bit
Lets take the V5.6 and master branches from the previous diagram and limit ourselves to just thinking about that part of the tree.
Creating a branch
The first thing you should do, of course, is create the branch(es). We'll assume they're created at the same time, but that's admittedly unusual.
# git checkout master # git branch feature1 # git branch feature2
Work will continue along these branches and patches will be applied to those trees:
# git checkout feature1 # # edit stuff to create pf1.1 # git commit -m "step one toward working on feature 1; may not work yet" # # edit stuff to create pf1.2 # git commit -m "Yay, it works now after some bugs were squashed" # git checkout feature2 # # edit stuff to create pf2.1 # git commit -m "minimally functioning feature2"
Master will of course move along too
Of course, someone may put small changes in the master trunk (or big changes compressed from private branches):
# git checkout master # # edit something that is now completely different # git commit -m "small feature description"
Eventually, we'll merge our feature in
Starting with merging feature1 back into the trunk is easy! Git takes care of the complexities.
# git merge --log feature1
Options for merging feature2
There are multiple ways we can do merge operations to get the features into the master branch. (these could have been done with feature1 as well.)
Option1: merge from where you were
If you do a merge just like with feature1, it should work and pull in only the new changes:
# git merge --log feature2
Option2: rebase first and then merge
I think of rebasing as "take my entire patch set from where I started this branch from and apply them incrementally somewhere else". It's a very useful operation, and many people prefer to rebase their trees before merging. It's not required, but it tends to make the tree more linear and readable.
# git checkout feature2 # git rebase master # git checkout master # git merge --log feature2
In many cases you may need to rebase in order to pick up the changes from other work that you suddenly decided you wanted to depend on.
More work on existing features
Many times a feature was simply at a good stable point to merge to the master branch and a merge is done. But the feature isn't done (or is a long work in progress), so work can simple continue on it and another merge later will take place to pull in the latest incremental stable improvements to the feature.
# git checkout feature2 # # more editing # git commit -m "Feature2 is now super super cool" # git checkout master # git merge --log feature2
Complex Patches on Other Branches
Of course, patch branches shouldn't have features merged in. But complex patches and fixes can be handled in the same way as the above, but using the patch branch in question instead of using the master branch.
And what's really cool? If you realize that you need to apply the patch to an earlier release (or later) than you were thinking, git rebase can actually move the entire branch to a different part of the tree!!!
Other Questions, Answers and HowTos
Who's in charge of merging upward?
Generally speaking, anyone can merge branches upward. EG, anyone can perform a merge from V5-3-patches to V5-4-patches. Generally, we'll need to assign someone to make sure these upward merges happen on a regular basis. A discussion about this will need to happen in an admin meeting.
Exception: When a branch is frozen during a release cycle for it, only the branch manager should be merging upward into that branch from the lower branches. A branch manager may not have received enough positive votes for some operations and thus may need to git cherry-pick only certain patches to be merged until after the release.
Generally, we shouldn't be merging from V5-2-patches all the way straight to V5-4-patches, but in theory it should actually work just fine if it needs to be done at some point.
If a merge fails miserably, it is always possible run git merge --abort to reset your working tree back into an unmerged state in order to get help from the people that have conflicting patches between the trees.
Figuring out which branch to apply to
git provides the ability to quickly take a working directory, with active changes, and switch the changes to a different branch. This is really helpful when working on patches or code. It lets you actually try your patch on master, for example, and then realize it actually needs to be applied to V5-6-patches. IE, this works just fine:
# git checkout master # patch < something.patch # git checkout V5-6-patches # git commit -m "applying a patch to fix Wes' broken code" # git checkout master # git merge --log V5-6-patches
Note that the code was changed while master was checked out and we switched the current directory to the 5.6 tree and committed the patch to that instead! In SVN, we would have had to change to a different directory instead and applied the patch there. With Git, it basically reverted the code in the current directory to the 5.6 tree and reapplied the patch leaving you with a patched 5.6 instead!
If you need to work on multiple lines of code at once (we all do frequently), simply git clone your repository into another directory so you can have multiple copies. You can have both of these directories working on master at the same time if you like or simple use them as needed. Do try and work under a single directory and switch back and forth a few times to see how powerful this concept is though.
Shouldn't we create scripts to automate much of this?
I mean, come on... merging that many branches upward is a lot of typing!