OpenCog

Development standards

From OpenCog

Notes taken from the opencog/HACKING file. See also: BuildingOpenCog

Contents

Bazaar HOWTO

Bazaar is a distributed version control system (DVCS) used by OpenCog to handle source code versioning. Bazaar is more powerful than traditional centralized VCSs such as CVS or SVN. On the other hand, it may be easily misused by those who are not familiar with its native workflows.

We recommend that OpenCog developers read the Bazaar User's Guide. It is rather comprehensive and covers the basics of all the most important operations one generally uses on a VCS, such as checking out, commiting, updating, branching, merging, resolving conflicts, etc. We specially recommend reading section "Team Collaboration, Distributed Style" which describes some of the peculiarities of working with a distributed VCS.

Inspite of the comprehensiveness of Bazaar's documentation, there are many workflows that a developer may follow when using Bazaar. Thus, we compiled a list of guidelines and recommendations on how to use Bazaar to develop and contribute code to the OpenCog project in particular.

bzrtools & bzr-rebase

The first recommendation is to install the bzrtools and bzr-rebase plugins. Some of the guildelines we will show below will use functionality provided by these two plugins.

Use standalone trees, not checkouts (aka: use branch+rebase+push, not checkout+update+commit)

One may choose to use Bazaar just the way he/she works with a traditionally centralized VCS:

prompt$ bzr checkout lp:opencog #retrieves the tree from the central repository
prompt$ bzr update #retrieve updates since the last checkout/update
<hack><hack><hack>
prompt$ bzr commit #sends the local changes to the central repository

However, we do not recomment this workflow (heck, it defeats the whole purpose of using a decentralized system!). We recommend using local branches, commiting locally and pushing the changes back to the central repository:

prompt$ bzr branch lp:opencog <localdir> #retrieves the tree from the central repository and create a *standalone branch* on directory <localdir>
prompt$ cd <localdir>
<hack> <hack> <hack>
prompt$ bzr commit # commit your changes locally
prompt$ bzr rebase # retrieve the latest updates from the central repository and then apply your local changes on top of the updated tree
prompt$ bzr push <url> #sends the local changes to the central repository

Keep a local mirror of the remote branches

It is often desireable to have a working copy of the "official" centralized tree readily available on your local disk. It speeds up braching, diffing, resolving conflicts, etc. So, instead of hacking on top of the tree created when you branched from the central repository, branch from the local tree to a different directory and use the new one for development:

prompt$ bzr branch lp:opencog main
prompt$ cd <localdir>
prompt$ bzr branch . ../dev
prompt$ cd ../dev
<hack> <hack> <hack>

"Thee shall not merge FROM the central repository"

Since "rebase" is not part of the offical bazaar set of tools, a common problem arrises when one tries to update a development local tree:

prompt$ bzr branch lp:opencog
<hack><hack><commit><hack><hack><hack><commit><etc>

Now, while we were hacking, someone else pushed one more changes to the central repository; so when I try push my changes I get:

prompt$ bzr push lp:opencog
prompt$ bzr: ERROR: These branches have diverged. Try using "merge" and then "push".

So, your first instinct is to run:

prompt$ bzr merge lp:opencog
prompt$ bzr commit -m "merge from central repository"
prompt$ bzr push lp:opencog

And everything is fine. Right? Wrong!

Suppose that your first bzr branch retrieved versions 1, 2 and 3 from the centralized repository. Then, you hacked locally and commited versions 4 and 5.

While you were hacking locally, the other developer pushed *his* changes to the central repository and they were labeled, say, 4, 5, 6 and 7. Now, when you merged your changes to the main tree, his changes will be removed from the history and added as 3.1.1, 3.1.2 and 3.1.3. When you push your changes to the central repository, the new official history will be "1 2 3 (3.1.1 3.1.2 3.1.3) 4 5".

There a few cases when this type of revision hierarchy is useful, but for local changes it should be avoided. It prevents other developers from properly tracking merges (for instance, what it a third developer branched from the repository starting in revision 6?), rebasing from the central repository doesn't work anymore and it will confuse the users/testers (hey, wasn't the last revision #7? how come we are now at #5). Also, a few bzr interfaces (notably, Launchpad) do not display the history hierarchy -- only the first level. So suddenly, the changes 4, 5, 6 and 7 from the other developer will be collapsed into your revision 4. It becomes almost impossible to use WWW diff interface to review changes.

The question now is: if I can't merge from the central repository, how do I *properly* pull the changes? We have two suggestions: 1) use "bzr rebase"; 2) merge *to* the local tree that mirrors the central repository.

bzr rebase

With bzr rebase, you simply replace "merge" with "rebase":

prompt$ bzr rebase lp:opencog
prompt$ bzr commit
prompt$ bzr push lp:opencog

What rebase does it to apply each of your local changes (in the previous example , the local revisions 4 and 5) on top of updated tree (so that they would become revisions 8 and 9).

Just like a normal merge, there may be conflicts when applying your changes to the updated tree. You deal with them just like a regular merge conflict, but with one cavet: you should have in mind the scope of the change that generated the conflict. For instance, if there was a conflict when applying your local revision 4 on top of the updated tree, you have to make sure that you won't accidentally add a change that was part of your local revision 5 (because once the conflict is resolved, the changes from revision 5 will be applied on top of the resulting tree). This is a common issue because we tend to remember the state of code after the latest revision, not after an intermediate one.

Once all conflicts from a specific revision are resolved, just issue

prompt$ bzr rebase-continue

to proceed with the rebase process. If, at some point, you decide for some reason that the rebasing process won't work, you may cancel it with

prompt$ bzr rebase-abort

and your tree will be reset to the state it was before your started rebasing.

Merging TO the central repository

If you had trouble with rebase or for some reason can't install the plugin, another option to push your changes to the central repository is merging TO the central repository (instead of FROM).

If you followed our previous recommendation, you should have a clean branch of the main repository readily available on your local disk ("clean" as in not having any local changes, so that bzr pull lp:opencog will succeed). If you do, make sure it's up to date; if not, create one now:

prompt$ bzr branch lp:opencog cleanbranch

Or, to save some time & network traffic:

prompt$ cd <root-of-dev-branch>
prompt$ bzr branch . ../cleanbranch
prompt$ cd ../cleanbranch
prompt$ bzr pull --overwrite lp:opencog

Now we may merge our changes from the development tree to the cleanbranch tree:

prompt$ cd <root-of-dev-branch></code
<code>prompt$ bzr log --forward -r ancestor:../cleanbranch..-1
# view which revisions we have commited locally since our tree diverted from the central repository
prompt$ bzr merge -r ancestor:../cleanbranch..-1 -d ../cleanbranch .
# merge these changes to the cleanbranch tree
prompt$ cd ../cleanbranch
prompt$ bzr commit # commit the merge
prompt$ bzr push lp:opencog # and finally push to the central repository

Depending on the nature of the changes on your development branch, it might make sense to break the merge into separate commits. To do so, you must change the revision spec supplied through the parameter "-r". Check bzr help revisionspec for more information.

Patches & Merges

Patches should generally follow LKML (Linux Kernel Mailing List) standards and acceptance criteria. Large or significant changes from new developers should be broken into small revisions, uploaded to a user branch on Launchpad, and discussed on IRC and/or opencog-dev (the Launchpad merge proposal workflow may also be used). Small patches may be emailed (ask for an email address to send patches at #opencog on IRC.freenode.net).

Whitespace

Required for code consistency and patch acceptance:

  • use 4 spaces for indentation
  • use 80 columns max
  • use unix-style end-of-lines (all decent win32 editors support it)
  • use definition-block brackets on a new line and command-block brackets on the same line
  • use spaces between expression operators ("i + 1" instead of "i+1")

Checking with astyle

To check whether your code complies with our standard, you may use the tool "astyle". Make sure you use the following options:

prompt-$ astyle --indent=spaces=4 --brackets=linux --indent-labels \
          --pad=oper --one-line=keep-statements --convert-tabs \
          --indent-preprocessor file.cc
prompt-$ diff file.cc file.cc.orig

Vim

If you use the VIM editor, you may add the following line to you ".vimrc" configuration file to automatically setup your editor to use opencog's style when editing a source file from OpenCog's tree:

autocmd BufNewFile,BufReadPost * if match(expand("%:p:h"), "/opencog") >= 0 && &filetype == "cpp" | set ts=4 sw=4 tw=80 ff=unix cindent expandtab | endif