Mercurial Manual for Openbravo Developers
This manual explains the usage of Mercurial for community developers of Openbravo ERP and Openbravo POS. It details the particular configuration and procedures recommended to work in an Openbravo project.
This is not a complete Mercurial manual. If you are looking for general Mercurial resources, there is excellent documentation:
- The hg help <command> integrated help.
- The Mercurial wiki.
- The hgbook: Mercurial: The Definitive Guide. If you plan to work with Mercurial, reading this is a must.
- SCM: Source Code Management. More
- DSCM: Distributed Source Code Management. More
- Repository: A remote copy of a source tree with revision history. More
- Working Copy: Also known as working directory. It's the top-level directory in a repository, in which the plain versions of files are available to read, edit and build. More
- Changeset: It's a collection of all the changes that lead to a new revision of the repository. More
- clone: a complete copy of a repository. More
- pull: a pull propagates changesets from a "remote" repository (the source) into a local repository (the destination). More
- update: it updates the working directory from the repository. More
- commit: commit changes to the given files into the repository. More
- push: to send all differing revisions to another repository. More
- tag: to name a particular revision. More
- branch: a diverging point in time in the revision history. More
- head: repository heads are changesets that don't have child changesets. They are where development generally takes place and are the usual targets for update and merge operations.
- Development branch: a branch of the main repository, usually used to add a new feature to the product.
- Stable branch: a stabilization or maintenance branch of a major release.
- Release stabilization branch: a branch used by Openbravo in Subversion to stabilize an upcoming release and to avoid freezing trunk.
- trunk: name used in Subversion to indicate the branch where the main developer work is done.
- main: name used by Openbravo in Mercurial to describe the repository where the main developer work is done.
- tip: The tip revision (usually just called the tip) is the most recently added changeset in the repository, the most recently changed head.
Introduction to distributed SCMs
Before starting with Mercurial, is a good idea to have a general view of what distributed SCMs are and how they work. First of all, read the Understanding Mercurial and Intro to Distributed Version Control (Illustrated) documents. They illustrate the basic concepts.
In a centralized SCM there is only one central repository. Developers have working copies of this repository, and changes are committed to this central site. It is not possible for developer A to share changes with developer B without passing the changes by the central repository. This problem is solved by the distributed SCMs. Instead of checking out a working copy, you get a full clone copy of the repository.
This means that once you have the repository locally, all the operations are local and therefore very fast. Everyday command like diffs, merges, commits and reverts are all done locally. There's no server to ask for old revisions from a year ago.
Additionally, every cloned repository can be considered as a new branch. Merging is an every day operation in distributed SCMs and it is much faster compared to centralized systems. You merge locally and then optionally push the results to another repository.
Finally, a distributed SCM offers working with different collaboration models, even in a centralized manner.
If you want a quick and nice tutorial, follow the Mercurial Kick Start.
Use Mercurial 1.1 or later. Use the hg version command to check it.
If you are are on testing (squeeze) or unstable (sid):
apt-get install mercurial
The current stable version of Debian (Lenny) includes Mercurial 1.0.1, which is old for Openbravo's requirements. If you are running Lenny thenbuild Mercurial from sources
Install Mercurial from the Mercurial Releases PPA. If you are using Lucid (10.04) or Karmic (9.10):
sudo add-apt-repository ppa:mercurial-ppa/releases sudo apt-get update sudo apt-get install mercurial
If you are using Jaunty (9.04) or Hardy (8.04):
sudo sh -c 'echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu YOUR_UBUNTU_VERSION_HERE main" >> /etc/apt/sources.list' sudo sh -c 'echo "deb-src http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu YOUR_UBUNTU_VERSION_HERE main" >> /etc/apt/sources.list' sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key 323293EE sudo apt-get update sudo apt-get install mercurial
Install the latest Mercurial release:
echo dev-vcs/mercurial >> /etc/portage/package.keywords emerge -av mercurial
There are 2 options:
- Use the command line version and install it using a prepackaged binary installer. This is the easiest method.
- Install using MacPorts. This method is a little more involved, because you need to first download and install MacPorts, which involves making sure you have the latest Xcode installed. Once you have MacPorts set up:
sudo port -v install mercurial
There are 2 options:
- Use the command line version and install it using a prepackaged binary installer.
- TortoiseHg: an all-inclusive Mercurial binary installer package for Windows, which provides a windows explorer extension (shell extension), so that Mercurial commands can be executed from the context menu of the explorer.
You should configure Mercurial just after installing it. On UNIX-like systems, this should be done in your $HOME/.hgrc file. On Windows, these settings can be added to C:\Program Files\Mercurial\Mercurial.ini, or, if you'd like per-user settings, $HOME\.hgrc or $HOME\Mercurial.ini. (if the file does not exist you should create it manually).
Your Mercurial configuration file should have at least the following settings:
[ui] username = Your Real Name <email@example.com> merge = your-merge-program [diff] git = 1
The username is used to identify the commit author.
It is very important to choose a merge program. There are many popular options to choose from, like Meld, KDiff3 or TortoiseMerge.
The git = 1 diff option specifies to use the extended git format. This allows including file permission changes in the diffs.
You can configure the editor to use for commit messages using the editor option in the [ui] section or by setting the EDITOR environment variable.
Extensions allow the creation of new features and using them directly from the main hg command line as if they were built-in commands.
- alias: define your own command aliases.
- color: color output for the diff, status and qseries commands.
- fetch: conveniently pull, merge and update in one step.
- graphlog: adds a new command glog that behaves like (a subset of) the normal log command except that it also prints a graph representing the revision history using ASCII characters to the left of the log.
- hgk: allows you browsing the history of a repository in a graphical way.
- histpush: allows you to locally track which revisions of a repository have been pushed to a remote repository.
- rebase: rebase uses repeated merging to graft changesets from one part of history onto another. This can be useful for linearizing local changes relative to a master development tree.
- transplant: allows you to transplant patches from another branch or repository (aka backport).
To enable an extension, add it to the [extensions] section of your $HOME/.hgrc. For example to enable the color, rebase and transplant extensions:
[ui] username = Your Real Name <firstname.lastname@example.org> merge = your-merge-program [extensions] color = rebase = transplant =
Some extensions are shipped along with Mercurial. Others can be added externally. Check the official web page for a list of extensions.
Custom diff program
It is usually be very useful to view a diff using an external program like meld, kdiff3.
Activation of the extension:
[extensions] extdiff =
Specify the desired program:
[extdiff] cmd.vdiff = meld
Example of use:
hg vdiff modified_file.java
For more information about extdiff check the official guide.
From the book Distributed revision control with Mercurial:
"...it's often good practice to keep a 'pristine' copy of a remote repository around, which you can then make temporary clones of to create sandboxes for each task you want to work on. This lets you work on multiple tasks in parallel, each isolated from the others until it's complete and you're ready to integrate it back. Because local clones are so cheap, there's almost no overhead to cloning and destroying repositories whenever you want."
We recommend this process of using cheap local clones. The general process is:
- Have a pristine copy of the remote repository.
- To work on a repository, make a temporary local clone.
- Do as many atomic commits as needed in that temporary clone.
- Once finished, push directly to the master repository (the remote one).
A developer could have several temporary clones of the same repository to isolate different developments. For example, if a developer is fixing two bugs simultaneously but wants the commits and tests to be isolated, he would use two separate clones.
We propose having a topology were $HOME/src contains all the pristine copies of the projects you are working on. And $HOME/workspaces contain the clones of these pristine copies. The repositories from $HOME/src are updated from the central repositories and the ones from $HOME/workspaces are updated from the local $HOME/src repositories.
$HOME/src | |--- openbravo/ |-----| |--- erp | |---devel |--- main |--- pi |--- project1 |--- project2 |---stable |--- 2.3x |--- 2.40
$HOME/workspaces | |--- openbravo/ |-----| |--- erp | |---devel |--- main |--- pi |--- project1 |--- project2 |---stable |--- 2.3x |--- 2.40
This document will assume that the pristine copies are stored under $HOME/src and the local clones under $HOME/workspaces. If you choose different locations, take it into account when following the examples.
Creating the local pristine clones
While in Subversion a checkout updates your working copy from a repository, in Mercurial you first update your local repository and then the working copy.
In the Mercurial terminology this is called a clone. A clone is a copy of a repository in a new directory. To do a clone:
mkdir -p $HOME/src/openbravo/erp/devel cd $HOME/src/openbravo/erp/devel hg clone https://code.openbravo.com/erp/devel/pi
Do as many clones as projects you are going to work on.
Remember that these are pristine clones you will keep unmodified.
Keeping the local pristines clones up to date
Check what you're going to pull:
cd $HOME/src/openbravo/erp/devel/pi hg incoming
To keep your clone up to date, update the repository with these changes:
Then, to update your working copy:
Note that it is possible to combine these two steps in one:
hg pull -u
Since this clone is a pristine copy of the remote repository you may want to maintain it up to date with a cron job.
Creating local working clones
To start working on a project, you should create a local clone of the pristine clone:
mkdir -p $HOME/workspaces/openbravo/erp/devel cd $HOME/workspaces/openbravo/erp/devel hg clone $HOME/src/openbravo/erp/devel/pi
Periodically you will want to update the local clone from the pristine clone:
cd $HOME/workspaces/openbravo/erp/devel/pi hg incoming hg pull -u
These are the clones where you develop your code.
Check in to the central repository
Firstly make sure you have configured your $HOME/.hgrc with your username.
Secondly, to push to the Openbravo repositories you need push access (or someone with push access needs to pull from your repository).
To check if you're really committing what you want to commit:
hg status hg diff
Note that while Subversion applies the command to the specific directory you are in, Mercurial applies it to the entire tree. To see the status or diff of a specific directory specify it as an argument:
hg status web/js
hg diff web/js/message.js
As the next step, commit your changes to your local repository:
hg commit -m "message"
You can make as many atomic commits as you need. This commits won't be seen in other repositories until you push them.
Push to the central repository
To check what you are going to push:
To actually send the commits to the central repository:
If you receive the message "abort: push creates new remote heads", do not do what the message suggests (hg push -f). What this message means is that some changes have been done to the central repository since the last time you updated your local repository. If you do it the server will reject it anyway. We recommend following one of these two approaches:
In case of doubt use the first approach: rebase
Check that the rebase extension is enabled in your .hgrc file:
[extensions] rebase =
hg pull --rebase
This will pull and rebase your local changesets at the same time. More details and options can be found at the Mercurial wiki.
So now you are ready to push your commits:
In the process of the rebase, it may discover conflicts. In that case it will stop and allow you to fix the conflicts. After fixing the conflicts, use hg resolve to mark the files as resolved, and then, instead of running hg commit just run hg rebase --continue. Example:
$ hg pull --rebase pulling from /home/johndoe/src/openbravo/erp/devel/pi searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) merging src/build.xml warning: conflicts during merge. merging srv/build.xml failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue
Now we edit src/build.xml and fix the conflicts. Then, we mark the files as resolved:
$ hg resolve -l U src/build.xml hg resolve -m src/build.xml hg resolve -l R src/build.xml
And we continue the rebase process:
$ hg rebase --continue saving bundle to /home/johndoe/src/openbravo/erp/devel/pi/.hg/strip-backup/05a5fc850b1f-temp adding branch adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 1 files rebase completed
Pull the latest changes from the repository:
This will create an extra head locally. Merge the two heads:
Commit the result:
hg commit -m "Merge heads A and B"
Push to the central repository:
Fixing a bug
Example: the developer has to fix issue 6265 in main.
The developer starts going to the local work clone of the main repository. Then the developer can start fixing and testing the issue in that clone, doing as many commits as needed. Each commit should be an atomic piece.
cd $HOME/workspaces/openbravo/erp/devel/pi (edit fixme.java) hg ci -m "Fixes issue 6265."
When the developer is happy with the fix, the changesets can be pushed to Openbravo repository.
Before pushing it is a good idea to see what's going to be pushed with hg outgoing
Backporting a bug fix
Example: the developer has to backport the fix of issue 6265 from devel/pi to stable/2.40.
The developer has already fixed the bug in one branch and wants to repeat the fix in another branch. Then he can use transplant to apply the same fix in this branch (4544 is the revision of the commit done to fix this bug in main):
cd $HOME/workspaces/erp/stable/2.40 hg transplant -s $HOME/workspaces/erp/devel/pi 4544
After testing the fix, he can push the update to Openbravo repository and delete the temporary clone:
hg outgoing hg push
The transplant extension is distributed along with Mercurial. Another possibility to backport changes is to use hg export and hg import.
Developing a new feature
New features can be developed in local repositories. But if other core developers or community members are also contributing to this feature, you want a public repository to share your developments.
Example: the developer wants to start a community project to develop a new feature based on main.
- Create a shared repository based on main.
- Create a local clone as a reference:
hg clone https://code.openbravo.com/erp/devel/modularity $HOME/src/openbravo/erp/devel/modularity
- Create a local working clone:
hg clone $HOME/src/openbravo/erp/devel/modularity $HOME/workspaces/openbravo/erp/devel/modularity
- Edit code, committing in atomic pieces. Push to the shared repository to make the changes available to developers and the community.
- Once the feature is developed, merge the feature branch into main.
- The developer already has local copies of both repositories in $HOME/src/openbravo/erp/devel/pi $HOME/workspaces/openbravo/erp/devel/modularity. So we create a new clone were the merge will be done:
hg clone $HOME/src/erp/devel/pi $HOME/src/erp/devel/merge_modularity
- Then, we pull changes from modularity branch:
cd $HOME/src/erp/devel/merge_modularity hg pull $HOME/src/erp/devel/modularity hg merge (conflict resolution) hg ci -m "Merged modularity."
Instead of pull+merge+commit, the fetch extension could be used.
Once the merge is done, the changesets can be pushed into the centralized main repository and the merge repository can be deleted:
cd $HOME/src/erp/devel/merge_modularity hg outgoing hg push https://code.openbravo.com/erp/devel/pi rm -rf $HOME/src/erp/devel/merge_modularity
Finally, the modularity repository can be deleted.
Backing out changes
Generally if you make a mistake and want to fix it, you should just edit sources to fix it and commit a new revision. This preserves history of the mistake and its correction and is suitable for routine programming errors.
I've committed one changeset locally, it's not pushed
If you commit something that you really cannot tolerate being in history (e.g. legally forbidden files, passwords) it is possible to discard the commit only if you have not yet pushed it (Once it is on the server, it is permanent). If you have just committed the change and nothing else, just run hg rollback. Your working copy will still be modified; you can hg revert if you need to.
Alternatively you can also make a version of the repository that does not include the latest changes. Use hg clone -r and pass in the "last known good" changeset ID.
I've committed one changeset locally, it's pushed
Just back it out and push it:
hg backout -r REV hg push
I've committed more than one changeset locally, it's not pushed
If you commit several changesets locally but you haven't pushed them, it is possible to backout the changes in 2 different ways:
- Preserving history: run "hg rever -a -r REV", so that REV is the revision you want to have as the new tip of the repository. Then commit change changes and push them.
- Modifying the local history: in case you absolutely do not want the commits to go to the central repository, it is still possible to kill off bad changesets. The simplest way of doing this is to use the hg strip command, added by the MQ extension:
[extensions] mq =
If you strip out a changeset from your local repository, it will be removed along with all descendant changesets. Be careful. (Do backups)
Alternatively you can also make a version of the repository that does not include the latest changes. Use hg clone -r and pass in the "last known good" changeset ID.
I've committed more than one changeset locally and it's pushed
The easiest way is to revert your local working directory to the REV you want to revert to and then commit the changes:
hg revert -a -r REV hg commit -m "Back our changesets A, B and C"
Triggering actions in the issue tracker
As a general rule your commit must be related to an issue reported in Mantis. You can benefit from the integration between Mercurial and Mantis. Depending on the comment you write for the commit, a note can be automatically added to the issue. Moreover, the status of the issue can be automatically set to resolved.
- Adding a note: To add the commit comment as a note of the issue, you should add to the comment one of the words bug or issue (case insensitive) followed by the number of the issue. In the middle you can put none or more spaces. For example, the following messages would add a note to the issue:
- Issue 107.
- Related to the problem in bug107.
- Solving an issue: To mark the issue as resolved, you should add one of the words fixed or fixes before the text needed to add a note. If we wanted to resolve the issues, the previous examples could be:
- This patch fixes issue 107.
- Solves the login problem. Fixes bug 107.
Take into account that writing issue A and issue B in the same commit message will add comments to both issues. So, for example, solve issue 321, related to issue 223 would add a comment to issues 321 and 223 and solve issue 321. If you want to reference another issue in the same comment, use issue #B instead. In our example: solve issue 321, related to issue #223.
New repository for new feature
Sharing changes with other developers is an easy task with SSH or using the built-in web server. However, there are situations where developers want to share the work with other developers or with the community in a centralized manner.
There is no built-in support in Mercurial for server-side copy of a branch (i.e. remote to remote cloning). However, an interactive script could be added to the server to provide this feature. An example session could look like this:
$ ssh $USERNAME@code.openbravo.com 'create erp feature-1' Clone another ERP branch? Answering no will create an empty repository [Y/N]: Y Name of the branch to clone: erp/devel/pi ... Your new branch is at http://code.openbravo.com/erp/devel/feature-1
Core developers are allowed to create remote clones of the main repository.
An alternative to SSH is to create your own web interface to manage these requests.
New developer & access controls
Every repository will have one or more developers in charge of them (manager). The Project Managers decide who can pull/push and who cannot.
To add a new developer, an interactive script could be added to the server to provide this feature. An example session could look like this:
$ ssh $USERNAME@code.openbravo.com 'addhguser' Create a new account? [Y/N]: Y Developer user name: johndoe Developer e-mail: email@example.com ... New user created. An e-mail has been sent to firstname.lastname@example.org with the developer credentials.
Equally, to delete a developer account:
$ ssh $USERNAME@code.openbravo.com 'delhguser' Delete an existing account? [Y/N]: Y Developer user name: johndoe This is permanent. Are you absolutely sure? [Y/N]: Y ... User johndoe deleted.
Finally, the developer in charge of a repository is capable of managing the access controls. An example session could look like this:
$ ssh $USERNAME@code.openbravo.com 'hgacl' Manage access controls? [Y/N]: Y You are the manager of the following repositories: * erp/devel/pi * erp/devel/localization * erp/devel/my-feature-1 Select a repository: erp/devel/my-feature-1 Developer user name (use 'all' for anonymous): johndoe Current permissions of johndoe in erp/devel/my-feature-1: +pull -push Select action: +pull ... Permissions modified for user johndoe in erp/devel/my-feature-1: +pull +push
Sharing repositories with local developers
The easiest way to share a repository with a developer in the same LAN is to use hg serve, it includes a mini-web server:
~/workspaces/openbravo/erp/devel/pi $ hg serve -v listening at http://192.168.102.123:8000 (bound to *:8000)
Alternatively you can clone/push/pull directly using SSH:
hg clone ssh://email@example.com/workspaces/erp/devel/pi
In this case the repo would be located in /home/john/workspaces/erp/devel/pi. To specify absolute paths use double slashes (//):
hg clone ssh://firstname.lastname@example.org//home/john/workspaces/erp/devel/pi
Write meaningful commit messages
Both the hg log command and the hgweb web interface print only the first line of a commit message by default. So it's best to write a commit message whose first line stands alone. Our recommendation is to write short but informative messages that tells us something we can't quickly figure out from the diff.
Template for good commit messages
Short (72 chars or less) summary of changes. More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. Leave a blank line between the summary and the detailed explanation. If needed, further paragraphs can come after: - Bullet points are fine. - Second bullet point.
Example of a good commit message
Fixed issue 27183: make postcreate and postupdate stop on build errors This ensures that importing masterdata and sampledata will not fail because of this past error.
Example of a bad commit message
Fix issue 27183: make the postcreate and postupdate ant tasks stop the build process when errors are found. This is useful to ensure that importing masterdata and sampledata will not fail later on in the process. So changed the "onerror" paramater accordingly. Now that I'm writing, I'd also like to say hello to my friend John Doe.
Changing push password
To change your password to push to code.openbravo.com, use the hgpasswd utility.
Mercurial for Subversion refugees
|svn co URL|| hg clone URL
|svn diff||hg diff|
|svn status||hg status|
|svn add file1 file2||hg add file1 file2|
|svn del file1 file2||hg del file1 file2|
|svn commit -m "message"|| hg commit -m "message"
|svn update||hg pull -u / hg pull --rebase|
|svn log||hg log|
|svn log --limit 5||hg log --limit 5|
|svn diff -c 6445||hg log -r 6455 -p|
|svn info|| hg identify
|svn revert file1 file2||hg revert file1 file2|
|svn revert -R .||hg revert -a|
There is mailing list called openbravo-commits. For every push to the Openbravo ERP repository an e-mail is generated and sent to the list showing all the changesets of that push, their authors, dates and diffs.
There is one read only mirror in SourceForge:
How can I store my push/pull login and password?
This information is saved per repository. So edit <hg-clone>/.hg/hgrc of the repository and edit the URL:
If you're using Mercurial 1.3 or later, you can specify the credentials globally in your main hgrc file. As an example, edit $HOME/.hgrc ($HOME\mercurial.ini in Windows) and add the following section:
[auth] ob.prefix = code.openbravo.com ob.username = myusername ob.password = mypassword
For more information about this check the hgrc(5) man page.
I'm running hg push -f and it fails! What do I do?
Running this command is forbidden. In fact it intentionally fails with an error similar to:
transaction abort! rollback completed abort: pretxncchangegroup.forbid_2heads hook exited with status 1
To fix this situation follow these steps.
How do I add a file to the ignore list, to prevent it from being committed?
If its of general interest edit the <hg-clone>/.hgignore file and add your list of files there. Take into account this file is version controlled as a common file and that it has to be committed. For example to ignore config/Openbravo.properties:
echo config/Openbravo.properties >> .hgignore hg ci -m "Ignore config/Openbravo.properties" hg push
This is the equivalent to the svn:ignore property in Subversion.
How do ignore files *only* in my local repository?
You may want do ignore files locally, without having to modify the general .hgignore file. To achieve this, edit <hg-clone>/.hg/hgrc and add:
[ui] ignore = /absolute/path/to/repo/.hg/hgignore
And use /absolute/path/to/repo/.hg/hgignore as the local ignore file.
How do I go to revision XY in my working directory?
Use hg update to achieve this. For example to go to revision 2345
hg up -C 2345
How do I create a new repository for testing?
mkdir my-new-repo cd my-new-repo hg init (add some files) hg addremove hg commit -m 'Initial commit'
Can I edit the last commit message?
Yes you can, as long as it's not pushed. First, enable the mq extension in your $HOME/.hgrc file. Then:
hg qimport -r tip hg qrefresh -m "Enter here the new message" hg qfinish -a
For more information about Mercurial:
- The Mercurial wiki.
- The hgbook: Mercurial: The Definitive Guide.
- Cheat sheets: http://www.selenic.com/mercurial/wiki/index.cgi/QuickReferenceCardsAndCheatSheets
- The Mercurial FAQ.
- HgMigrationDocs from NetBeans Wiki.