Bitbucket is a code hosting site with unlimited public and private repositories. We're also free for small teams!

Close

Hg Guest Repo

Extension for enterprises needing to handle modules and components


Use case:

  • Many products, multiple projects within those products, multiple developers within those projects, multiple branches per developer

  • Source code shared in a multiplicity of configurations

  • Third party code (possibly patched on some branches)

  • A given module may have multiple versions going into the same product

Hg subrepos do not handle the sharing of components well, due to the recursive merge from the top (super) repo and requirement to lock at a specific version.

Guestrepo's goal is to overcome these limitations.

The guestrepo extension does not change any existing Mercurial behavior. It only adds new commands.

To enable the extension after installing on Linux, add the following to your ~/.hgrc.

[extensions]
guestrepo =

Windows users will have to specify

[extensions]
guestrepo = c:\absolute\path\to\guestrepo

Administration

Guest repositories are administered using the two files .hgguestrepo and .hggrmapping. These files are in the INI format used by Mercurial.

Remote Paths

Each guest repository must have a remote path defined in the .hggrmapping file to successfully push or pull.

Remote paths are referenced by assigning a each path a unique name (visible only within the namespace of guestrepos).

The .hggrmapping file lists remote paths in the following INI format:

name = remote-path

If a remote repository is moved, the path referenced by a name can be updated to point to the new location.

Example .hggrmapping file:

remoteid  = http://host/path/to/repo
remoteid2 = /path/to/repo2

The .hggrmapping file used for any operation is the .hggrmapping file at the most recently committed head of the current branch. Using the most recent revision of the .hggrmapping file keeps the guests of previous revisions pointed to the correct location.

The --local flag causes the .hggrmapping and .hgguestrepo file in the working directory to be used.

.hgguestrepo

The .hgguestrepo file defines guests by specifying three things:

  • location: The location of the guest relative to the root of the parent repository.
  • remote id: The name of a remote path defined in the .hggrmapping file.
  • changeset id: A Mercurial identifier e.g., a tag, bookmark, changeset hash, branch, or other future identifier resolvable to a single changeset.

The .hgguestrepo file lists guest repositories in the following INI format:

location = remote-id changeset-id

Example .hgguestrepo file:

path/to/guest = remoteid csid
path/to/other/guest = remoteid2 csid

If the the directory of the guest repository exists, it must contain a mercurial repository.

HOWTO

Setting up the shell

Guestrepos should be added to a single top-level repository that contains the .hgguestrepo and .hggrmapping files. This is known as the shell repository and is what will be cloned down by anyone wanting to work on a project using Guestrepos.

Creating a New Project Using Guestrepos

To create a new project, you first need to set up a shell repository.

cd shell
hg init
touch README

You now need to set up your .hgguestrepo and .hggrmapping files. To add an existing remote repository as a Guestrepo, it needs to be added to each of these files. The .hggrmapping file specifies the mapping between the remote repository and where it should be in relation to the shell repository.

For example:

echo "lib-python_interface = ssh://user@remote-host:/python/python_interface" > .hggrmapping

In this case, we are mapping the repository on the remote server to the local folder lib/python_interface.

Now we have set up the .hgguestrepo file with our mapping along with a branch or revision to point to:

echo "lib/python_interface = lib-python_interface default" > .hgguestrepo

A hg grpull command will now let you pull down the latest version of the lib-python_interface guestrepo that is on the server.

Converting a Project Using Subrepositories

If you are converting an existing project, then you already have a top-level repository to use as a shell. In this case, all you have to do is use the grconvert command to create the .hgguestrepo and .hggrmapping files from your existing .hgsub file.

Suppose you have the following structure for your repository

my_project/
   .hg
   .hgsub
   .hgsubstate
   README
   super_library/

In this case, super_library is a subrepository as defined in the .hgsub file.

To convert this into a subrepository, simply run:

user@test_machine:/tmp/my_project$ hg grconvert
Do not forget to add .hgguestrepo and .hggrmapping to the repository
Verify that .hgguestrepo and .hggrmapping are correct before removing .hgsub and .hgsubstate

The .hggrmapping file should have the same mapping between your guestrepo and its remote path that is in the .hgsub file. Similarly, the .hgguestrepo file should have the same mapping along with the revision that the subrepo was pointing to.

To finish converting the repo, hg add the .hgguestrepo and .hggrmapping files to the shell repository and hg rm the .hgsub and .hgsubstate files and then commit.

All of the guestrepo commands should now work.

Commands

Pull

hg grpull [guestrepo...]

Pull each guest repo from the location specified in the mapping file. If any guest repo does not exist it is cloned and updated to the specified changeset id in the .hgguestrepo file. See sync for information on how guests are updated. A pull to an existing repo will not update.

Pull is run recursively on guest repositories.

Push

hg grpush [OPTIONS] [guestrepo...]

Push each guest to the path specified in the mapping file.

Push is run recursively on guest repositories.

Passing the --local flag will use the .hggrmapping and .hgguestrepo files in the working directory even if they have been modified.

Push is normally optimized to check to see if there are any draft changesets before attempting to push to the remote repository. This can be overridden by the --all flag. The --local flag also overrides this behavior since it is might be necessary to push public changesets to a new repository.

Sync

hg grupdate [guestrepo...]

Update each guest repo to the changeset identifier in the .hgguestrepo file. The changeset identifier can be a branch, tag, changeset hex, bookmark, or any other identifier that can be given to Mercurial's update command.

A warning will be raised if a guest is updated to a branch with multiple heads.

Update is recursive. If the update operation adds a new guest, it is cloned to the newly added identifier.

Update will present an error and do nothing if there are local changes in any guest repository. This prevents the update from causing a merge with local changes.

Freeze

hg grfreeze

The freeze command outputs the state of the guest repositories to the .hgguestrepo file. It uses same format as the .hgguestrepo file, but only outputs the absolute changeset hash.

"local_dir" =  remoteid "changeset hash"

Freeze can be used to generate an .hgguestrepo file for a release:

$ hg branch Release
$ hg grfreeze
$ hg commit -m Release

Freeze can also save a .hgguestrepo to a particular file using the --file option:

$ hg grfreeze --file <output_file>

State

hg grstate

Print the exact changeset of all guests to stdout, as in .hgguestrepo.

The state command outputs the state of the guest repositories to standard out. It uses same format as the .hgguestrepo file, but only outputs the absolute changeset hash

State is essentially the freeze command directed to standard out rather than .hgguestrepo.

State takes an optional flag --json that prints the state of the guest repositories as a single JSON array. The schema is as follows:

[
    {
        "id": "<value>",
        "path": "<value>",
        "remote_path": "<value>"
    }
]

Summary

hg grsummary

Summary presents a simple description of the state of the project: a column of subdirectories, followed by the branch of the current changeset, followed by the tags and bookmarks pointing to the current changeset, followed by a * if modified.

local_directory (branch) [tags] [bookmarks] [*current bookmark] [*]

Summary takes an optional flag --json that prints the summary of the guest repositories as a single JSON array. The schema is as follows:

[
    {
        "path": "<value>",
        "remote_name": "<value>",
        "id": "<value>",
        "branch": "<value>"
    }
]

If present, the following keys are also valid in the schema

bookmarks
tags
current_bookmark
changed

The absence of these keys in the JSON indicates that the guestrepo does not have any of these attributes.

Out

hg grout

List the outgoing changes for all guestrepos.

In

hg grin

List the incoming changes for all guestrepos.

Error Handling

One of the key defects in the user experience of subrepositories is the invalid state that is occasionally maneuvered into and the requirement for a guru to fix the user's state. This wastes time. We claim it is better to have a globally inconsistent repository with the ability to return to a consistent state than a globally consistent and dysfunctional repository.


Errors parsing configuration files and resolving remote paths will cause guestrepo to abort before attempting any operations.

Errors that occur when operating on a guest repository do not cause other operations to be aborted. At the end of operation all errors are reported along with the current state of the repository and if it is 'sane' to use (i.e., not left in an an aborted operation state).

Errors will be displayed on standard error, and all errors cause a non-zero exit code.

Performance

The guestrepo extension has an option to use multiple threads to improve performance in I/O bound situations.

Unresponsive remote operations may cause guestrpo to hang indefinitely.

Issues

  • Changeset identifiers in the .hgguestrepo file cannot contain spaces
  • Convert script could be improved to allow both guestrepos and subrepos
  • No warning on modified/uncommited guestrepo files

Mailing List

Logilab hosts a Guestrepo mailing list at:

http://lists.logilab.org/mailman/listinfo/mercurial-guestrepo

LICENSE

Guestrepo: A Mercurial Extension for the managing of components

Copyright (C) 2012 Schweitzer Engineering Laboratories, Inc.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Contact information:

opensource@selinc.com

Schweitzer Engineering Laboratories, Inc. 2350 NE Hopkins Court Pullman, WA 99163 - USA

Contributors: Ben Smith

Recent activity

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.