This page summarizes the git-based development process we are using for Bro and its associated subprojects.
Contents
The available git repositories are listed at http://git.bro.org. All of them are publically accessible and can be cloned from git://git.bro.org/<repo>. If you are a developer with SSH-based write access, use ssh://git@git.bro.org/<repo> instead.
Some of the repositories, in particular bro, include others as submodules. To clone the repository, including the submodules, use —recursive. For example, to clone bro with all its submodules:
git clone --recursive git://git.bro.org/bro
The submodule directory tree that results from the recursive clone looks like:
bro/
cmake/
aux/binpac/
aux/bro-aux/
aux/broccoli/
bindings/broccoli-python/
bindings/broccoli-ruby/
aux/broctl/
aux/capstats/
aux/pysubnettree/
aux/trace-summary/
If the public URLs are used in the original cloning of repositories, but later write access is needed, the following command can be used to reset what URL is used for fetch/push operations (e.g. for the bro repository):
git remote set-url origin ssh://git@git.bro.org/bro
As an alternative, a global git config option can be set to automatically rewrite any of the public URLs into the private/write URLs:
git config --global url.ssh://git@git.bro.org.pushInsteadOf git://git.bro.org
The following diagram illustrates how we structure the development process:
The automatic nightly testing is not yet fully in place.
We use the following standard branches:
Topic branches are created by developers for working on a specific feature/bugfix/etc. They must be branched off of master. If there’s a developer primarily in charge of the branch, include the name (e.g., topic/robin/exciting-new-feature); otherwise just give it a descriptive name topic/new-documentation. All development relevant to the particular topic must be committed to the branch.
All developers can generally create/modify all topic branches, but please coordinate before working on somebody else’s topic branch.
This is a special, long-lived topic branch that allows for fast approval of small changes. All developers may commit into fastpath. However, each commit must be self-contained (i.e., not depend on earlier or later changes, and not be changed/amended afterwards), and it must be immediately ready for integration into master. fastpath should be regularly merged with master, any developer can do that.
Note that in case there is later a change necessary to a commit that has already been pushed into this branch, manual coordination with the maintainer will be necessary to make sure the right thing happens.
When committing something to fastpath in response to working on a tracker ticket, do not close the ticket immediately but change it’s type to FastPath.
See our separate page for contributing patches and functionality.
The following is primarily for developers with write access to the central repositories.
One note ahead of everything else: Never use git rebase if you’re not sure what you’re doing. In particular, never use rebase with anything that has already been pushed out to the origin repository. You would be “changing history” this way, create a huge mess, and have to pay for lots of beer at Jupiter.
Create a new topic branch locally and remotely, and make sure the local one tracks the remote one.
> git checkout master
> git checkout -b topic/robin/foo
> git push -u origin HEAD
Edit and commit locally:
> vi foo.txt [...] > git commit -a # Commits everything changed.
Often it’s helpful to stage things individually before committing everything:
> vi foo.txt # Edit. > git add foo.txt # Stage. > vi bar.txt # Edit. > git add bar.txt # Stage. > git commit # Commit everything staged.
See Using the Index For Checkpoints for more.
See Writing Commit Messages for some thoughts on what to put into a commit message.
Push local changes upstream:
> git push origin HEAD
This will generate commit notifications to the mailing list. Note that you can do just git push as well, but that will push your changes from all branches upstream.
Regularly pull in changes from upstream in case somebody is working on the same branch:
> git pull # This combines "git fetch && git merge"
If this needs to merge upstream changes with local ones, the pull will automatically commit the merge.
Likewise, regularly merge with master to incorporate any changes:
> git fetch # Ensure local repository is current. > git merge origin/master # Merge in master.
Once a topic branch is ready for integration into master, you can create a merge request, as follows:
Make sure you have merged the current master into your topic branch.
Create a tracker ticket, setting the component to the one corresponding to the repository you’re working on (e.g., Bro), and the type to Merge Request. Include the name of the topic branch into the ticket’s subject.
Note that your commit messages should already have sufficient information for the maintainer to create a CHANGES entry. If that’s not the case, include additional context in the ticket’s body.
If the maintainer finds that further work is needed before the branch can be merged, he’ll add comments to the ticket and reassign it to you. If so, address the comments by committing further to the topic branch and update the ticket once done; assign the ticket back to the maintainer when you’re ready for another attempt.
If the maintainer finds the topic branch suitable for merging, he will do so, close the ticket, and delete the topic branch.
Topic branches should be deleted once they have been merged into master (and, naturally, also whenever else you don’t need them anymore). Note that you need to delete a branch separately from both your local and the remote central repository:
Delete the branch from the remote repository:
> git push origin :topic/robin/foo
Remove the branch from the local repository by first switching to master and then deleting it:
> git checkout master > git branch -d topic/robin/foo
Deleting a branch will warn you if it has not been fully merged into the current branch (i.e., here, if there are changes in topic/robin/foo that aren’t in master). To override, you can do:
> git branch -D topic/robin/foo
Note that branches are just symbolic names pointing to a particular commit. Git does not keep a history of previous branch names.
If something goes wrong, you can usually revert to the previous state, even after a commit. Every “large” git operation sets ORIG_HEAD to the point before it started:
> git reset --hard ORIG_HEAD
This will revert all changes in the working tree and set the current HEAD accordingly. Instead of ORIG_HEAD you can also directly specify a revision you want to revert to.
Otherwise git status will generally give you hints on how to revert specific changes in the working copy or the index.
This section summarizes typical tasks for the repository maintainers.
This needs updating once the dust settles.
Once a developer files a merge request with the tracker, the branch can be merged into master. Merging involves the following steps (or variations of those, generally there are a number of ways some things can be done):
Make sure your repository has everything needed:
> git fetch
You should be able to see the topic branch:
> git branch -a
Switch to master and make sure it’s up to date:
> git checkout master > git merge origin/master
Check what revisions the topic branch has that will be included by the merge:
> git log --no-merges origin/topic/cmake-port ^master
If you also want to see diffs, add -p.
If that all looks fine (or mostly fine), merge the branch into master, but don’t commit yet:
> git merge --no-ff --no-commit --log origin/topic/cmake-port
Explaining the options:
Always records the merge as a separate commit, even if it could be fast-forwarded.
Does not commit the merge immediately, but just stages it.
Include the first sentence of each topic-branch commit in the commit log.
See useful macros for tips on how to make this easier.
Resolve conflicts, if any.
You can double-check the staged changes:
> git diff --cached
Edit anything you need to change and stage the changes:
> vi foo.txt > git add foo.txt
Verify that the test-suite passes.
Edit VERSION and CHANGES as appropriate and stage.
Note that there’s now a script that helps with updating these. Need to clean that up and add to the repository, then document here. -Robin
Commit the merge:
> git commit
Delete the topic branch:
> git branch -d > git push origin :topic/cmake-port # Delete remote.
If you have tried a merge that resulted in complex conflicts and want to start over, you can recover with:
> git reset --merge
See the section on reverting changes above for other options.
Generally, this works similar to merging in a topic branch, except for the assumption that each commit on fastpath is an independent change. There are two ways to handle this:
A hybrid approach is of course also possible: pick some for merging individually, and others for merging wholesale. The best way depends on how confident you are that the patches are reasonable and work as advertised.
The important thing to remember is that submodules reference a specific commit (i.e. repository snapshot) in the foreign repository. Because of this, the foreign repository can be developed completely independently of whatever includes it as a submodule. However, the maintainer may wish to update submodules to point to newer versions (presumably “stable” releases) from time to time. That is done as follows:
Checkout the master branch of your repository and make sure it is up-to-date:
> git checkout master > git pull
Checkout the version you want to set the submodule to, like a specific branch:
> cd path/to/submodule/foo > git pull > git checkout release/v1.1
Double-check that the parent repository shows submodule changes:
> cd path/to/parent/repo > git status
Stage the updated submodule (the lack of a trailing slash is important!):
> git add path/to/submodule/foo
Commit and push the updated submodule:
> git commit -m "Updated submodule foo to release 1.1" > git push origin master
There’s a script that does all this automagically. Need to clean up the script and add to the repository, then document here. -Robin
TODO
Update Bro’s NEWS with release notes. The file should contain the most important information about the new version, including a summary of major changes, especially incompatible ones. It should also point out the major changes in submodules.
Double-check these:
- Ensure tests succeed in testing/btest, testing/external/bro-testing, and testing/external/bro-testing-private.
- Make sure to also run all these tests on a Bro compiled with perftools support. That activates additional leak checking.
Finalize submodules before parent modules. For each module you want to release:
Make sure your checkout is up to date:
> git pullIf the module has submodules, make sure to reference their current versions:
> git add <module> > git commit -m "Updating submodule." <module>Make sure everything else is committed as well:
> git statusUpdate README as necessary and commit.
Do the final CHANGES update, set the new version, and tag as release:
> cd <into/module> > update-changes -R v0.31Note
If you rerun the update-changes script for a version already set in an earlier run, it will do the right thing: delete the old tags and create a new ones. But you should do that only if you haven’t published that version yet.
git describe should now show the new version.
Push all modules:
> git submodule foreach --recursive push > git push
Run check-release to check if everything is in the right state. It should output something like this:
Branch CHANGES Pending Modif Sub VERSION Tags
+ bro master ok 0 ok ok 2.0 v2.0,release
+ binpac master ok 0 ok ok 0.31 v0.31,release
+ bro-aux master ok 0 ok ok 0.22 v0.22,release
+ broccoli master ok 0 ok ok 1.8 v1.8,release
+ broccoli-python master ok 0 ok ok 0.52 v0.52,release
+ broccoli-ruby master ok 0 ok ok 1.52 v1.52,release
+ broctl master ok 0 ok ok 1.0 v1.0,release
+ capstats master ok 0 ok ok 0.16 v0.16,release
+ pysubnettree master ok 0 ok ok 0.17 v0.17,release
+ trace-summary master ok 0 ok ok 0.73 v0.73,release
+ btest master ok 0 ok ok 0.31 v0.31,release
If there’s a line starting with a minus instead of a plus, there may be something wrong (not necessarily a hard stop if you know what you’re doing, but something to check out).
Time to build tar-balls. Make sure you have a gpg-agent running. This will then build tgz for all modules and sign them:
> make-release --recursive
The output will show the new files with their signatures:
--- All distributions in /home/robin/bro/master/build/dist: -rw-r--r-- 1 robin robin 16619 Jan 10 18:39 /home/robin/bro/master/build/dist/release/trace-summary-0.73.tar.gz.asc -rw-r--r-- 1 robin robin 11601 Jan 10 18:37 /home/robin/bro/master/build/dist/release/trace-summary-0.73.tar.gz -rw-r--r-- 1 robin robin 53628 Jan 10 18:39 /home/robin/bro/master/build/dist/release/pysubnettree-0.17.tar.gz.asc [...]
Copy them over to the web server:
> scp build/dist/release/* www.bro.org:~www/public_html/downloads/release
Push the tags now. This will reflect the new versions on the web server:
> git submodule foreach --recursive push --tags > git push --tags
On the web server, move the old versions to the archive. This moves all release/ files older than 5 days into archive/:
> find ~www/public_html/downloads/release -ctime +5 -exec mv '{}' ~www/public_html/downloads/archive ';'
Create a maintenance branch:
> git checkout master > git checkout -v release/2.0 > git push
Do that for all modules you want to maintain separate release versions for.
Adapt Trac to reflect new milestones and canned queries on front page.
Adapt mail-merge-status script on www to now send out reports for new milestone.
While there is a git for SVN users crash course on the git homepage, it can be more confusing than helpful, especially since it doesn’t deal much with the local vs. remote relationships.
However, two very good and quick to read introductions to git are here:
A more comprehensive book on Git is available online:
A great comprehensive book / guide to git is:
Since Bro uses an optional Git “superproject” organization for the repositories, developers and maintainers may find it helpful to review Git Submodules.
Generally, it’s worth reading (and understanding :) the principles behind git (e.g., by reading the first few chapters in the O’Reilly book) as this will greatly help understanding what’s going on.
Merging upstream changes into the current branch:
> git pull
Actually, this command is shorthand for fetching all changes from remote and then merging the remote of the current branch into the current branch (i.e., a pull fetches all remote but only merges/applies changes to the current local branch).
List all available local branches (the one you’re currently on is marked with an asterisk):
> git branch
List all branches, remote and local with remote tracking and latest commit information:
> git branch -avv
To switch the working copy over to a specific branch, say fastpath:
> git checkout fastpath
Push (only) the current local branch to its remote counterpart:
> git push origin HEAD
Using only git push will push all local branches to their remote counterpart.
In order to commit changes to git, one has to git add FILE to stage the file and then git commit to actually commit the file. (Or use the shorthand git commit -a.) However, the staging area can also be used for checkpoints.
Whenever a change is staged with git add FILE git remembers the state of the file at this time. If you then further edit the FILE, you can either stage the new version by using git add FILE again, or revert to the previously staged version by using git checkout — FILE. In order to unstage the file, you can use git reset HEAD FILE. (This will not change the file’s contents in the working copy. It only removes the staged content. In order to revert the FILE to the last commit version, you have to do another git checkout — FILE.) If in doubt, git status is your friend.
Thus, if you want checkpoints, every git add can be considered a checkpoint and a git checkout gets you back to the last checkpoint.
If a branch is pushed upstream, each local commit will result in a commit to the upstream branch, thus triggering a commit message. However, sometimes it may be useful to squash several local commits (e.g., a bunch of checkpoint commits) into one upstream commit using git rebase -i. See here for more information.
But keep the Golden Rule in mind: when rebasing, never change anything that has already been pushed. One way to rebase only what has not yet been merged with origin/master is:
git rebase -i origin/master
Alternately, you might consider using the index instead of many local commits, see Using the Index for Checkpoints.
Normally, a fast-forward merge occurs by default when the current branch HEAD is directly upstream from the HEAD of the branch being merged. The —no-ff flag will, in this case, force the generation of a merge commit instead of silently moving the branch HEAD forward (a fast-forward). This avoids losing historical information about the existence of a topic branch because, although it doesn’t contain any content changes itself, the “merge commit” will have parent pointers corresponding to each of the old branch HEADs, thus allowing one to see exactly what commits were involved in a topic branch.
Commit messages should follow the following style as some of the git tools rely on that structure for picking out the right information:
Short (50 chars or less) summary of changes. More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Further paragraphs come after blank lines. — Bullet points are okay, too — Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here — Use a hanging indent
(This is stolen and adapted from here.)
In addition, make sure to include sufficient context with your changes that the repository maintainer can easily create a CHANGES entry for your work.
Tickets in the ticket tracker can be updated through commit messages as well. The basic syntax is command ticket, where command is typically Addresses, Closes, Fixes, or References, and ticket the ticket number prefixed with a # sign. For example, if you fix a bug in ticket 42, you would write the following at the end of your commit message:
Repair Bro. Fixes #42.
This automatically adds the commit message as a comment to ticket 42. More details about formatting the commit messages can be found in Trac’s wiki.
This section only applies to those who have checked out the bro repository recursively in order to get a “superproject” containing all (or most) the Repositories:
git clone --recursive git://git.bro.org/bro
This command will checkout the Bro master branch along with whatever versions of all submodules that repository maintainers currently deem stable.
If one wants to make changes to anything in the “superproject” source tree, it’s the same process as outlined in either the For Developers or For External Contributors sections. However, developers with write access to repositories will notice that all the submodule repositories are initialized using the public URLs, so they may want to add a global git option to automatically rewrite them into the private URLs for write access during push commands:
git config --global url.ssh://git@git.bro.org.pushInsteadOf git://git.bro.org
Developers that only plan to make changes to the top-level bro repository may not need to worry about the submodule repositories at all — the local copy will remain at whatever version was originally cloned even after performing git pull, however if the pulled version of the parent repository changes what commit the submodule points to, the opportunity can be taken to update the local copy. See the fix-submodules macro below for how to update the local copies of submodule after the parent repository’s maintainer has changed them.
git allows to define custom commands in ~/.gitconfig that then turn into more complex ones. You have to put them into a section [alias]. Here are some that might be helpful for the workflow above:
Like git merge but always add the options —log —no-commit —no-ff (see merging).
merge-branch = merge --log --no-commit --no-ff
Like git push, but pushes only the current branch upstream.
push-current = push origin HEAD
Executes git <git cmd> recursively for all submodules.
recursive = "!sh -c 'for i in . `git submodule foreach -q --recursive pwd`; do cd $i && git $@; done' -"
Executes the given shell command recursively for all submodules.
recursive-sh = "!sh -c 'for i in . `git submodule foreach -q --recursive pwd`; do cd $i && $@; done' -"
Makes sure all submodules are checked out at the revisions specified by their parent module.
fix-submodules = submodule update --recursive --init
Like git log, but compresses the output format into one-line summaries.
log1 = log --format=oneline
© 2013 The Bro Project. Logo design by DigiP.
