Frequently Asked Questions

SSH server keys for and

See our signature checking instructions.

What is Heptapod license?

Same as GitLab Community Edition, for simplicity and to preserve the possibility to merge back.

Are you planning on contributing Heptapod features back to GitLab?

We (Octobus) would even be happy if GitLab would simply provide the Mercurial option in some future, as this is in line with our primary business being professional services around Mercurial.

That being said, this question in practice depends on where the Heptapod community wants to go once it's mature enough, and if there are clear benefits for GitLab upstream.

For the time being, our "friendly fork" status with respect to upstream GitLab is comfortable enough for us and GitLab upstream.

Is there an Enterprise Edition for Heptapod?

Currently no, but if you're interested, tell us, and we'll reach out to GitLab about that.

Does Heptapod support both Git and Mercurial?

Yes, Git is fully supported since Heptapod 0.19.0 (January 2021).

Users can choose the Version Control System (VCS) type in Project creation forms.

Admissible and default VCS types can be changed in instance-wide and Group settings. By default, Git is allowed while the default VCS type is Mercurial.

Does Heptapod support evolve and topics?

Yes, one of the primary goals in launching the Heptapod project was to provide full native support for changeset evolution and topics-based workflows.

On the GitLab side of things (Web user interface, RESTful API), topics are exposed as "GitLab branches" with the following naming convention:


Here is an example from Heptapod's own development repository:

screenshot of Heptapod branch selector, showing some topics

Thanks to this, most GitLab features meant for Git branches translate transparently to topics. It is in particular perfectly standard to file Merge Requests for topics.

Does Heptapod support bookmarks?

By default, no.

With topics being the prominent way to provide short-lived feature branches with perceived mutability, we believe that it would be redundant and very error prone to simultaneously support bookmarks.

Also, supporting bookmarks alongside topics is a source of potential technical problems that we can't afford for the time being.

That being said, as of version 0.6.0, Heptapod will have some optional limited support for bookmarks, intended for cases where a whole Mercurial named branch is dedicated to tracking an external Git upstream with hg-git. In that case, upstream Git branches are translated to Mercurial bookmarks, and are readily visible under the same name in the GitLab interface.

To enable bookmarks on non-topic changesets, use the following HGRC server-side snippet:

allow-bookmarks = yes

or use the allow_bookmarks parameter in the HGRC REST API:


Up to Heptapod 0.17, this had the side effect to also allow multiple heads per branch or topic. As of Heptapod 0.18, the two settings are fully independent.

Bookmarks on topics are forbidden in all cases.

Pushing several bookmarks on a given branch has to be done with --force.

Does Heptapod support subrepositories?

Short answer: yes, with restrictions on server side operations.

A Mercurial repository can contain the special .hgsub file to declare that some subdirectories are to be independently versioned (with Mercurial, Git or Subversion) and have their own remote peers.

Reference: Subrepository on the Mercurial Wiki.

In what follows, we'll call the repository with the .hgsub file the main repository.

Hosting the subrepositories is nothing special, assuming they don't have subrepositories themselves. Mercurial ones can be on Heptapod.

It is possible to host the main repository on Heptapod, but some operations are not possible on the server side as of Heptapod 0.14.0. Of course you can perform them with your Mercurial client and push the result to Heptapod.

  • Merge Requests for the main repository that would give rise to an explicit merge changeset can't be merged from the Web application.

  • Online editing of repository files is not possible for the main repository.

    In Heptapod 0.14.0, these will give you a hard error, we'll make a proper user feedback later on.

  • The .hgsub and .hgsubstate files are not visible in the Web interface.

If your subrepository structure follows the thin shell recommendation, these restrictions shouldn't be a problem in practice, as the overwhelming majority of the work takes place in the subrepositories – which have nothing special to them.

What are Heptapod's workflows and Mercurial flavour?

The short answer would be that it's adapted to the Merge Request process of GitLab, but let's elaborate a bit on top of that.

Mercurial is highly configurable and even customizable through extensions. This means that there are different ways of using it. Furthermore, because of its strong backwards compatibility promise over more that 10 years of development, the default settings aren't aligned with MR-centric workflows.

We especially want the picture to be simple for newcomers with little or even no prior Mercurial experience. This is effectively a subset of what's possible with Mercurial, that amounts to say that by default in Heptapod, starting from version 0.6.0:

  • named branches have public changesets only and are writeable only by the Maintainer role members.
  • topics are writeable by all members of the Developer role and of course can be fully amended, rebased, etc.

More high-level explanations and motivations for this are given in the Octobus blog post

For experienced Mercurial users, we provide below detailed explanations for all settings that enforce these rules, with the relevant Heptapod version information.

One head per branch or topic

This has been true from Heptapod's inception.

Even if you force push, multiple heads on a given branch or branch/topic combination will be rejected by the server.

Multiple draft heads aren't needed when topics are available: just label them with topics, and you're set, you can do anything that you would have done with anonymous heads.

Functionally, multiple heads and especially multiple public heads have been a major source of confusion among users and have tremendous drawbacks for automatic processing, causing continuous integration systems to be stuck at the point before the ramification or building the wrong revision.

From Heptapod 0.6.0 onwards, there won't be any technical limitation to accept multiple heads. We still advise strongly against them, and it should be done in special cases only. The only valid use-case we can think of are related to importing repositories from other hostings systems:

  • older repositories often have some old multiple heads lurking around, especially in closed branches, and it can be cumbersome or hard to understand why they should be merged and closed. Heptapod's import features will therefore lift that restriction temporarily.
  • if an import has to be done by direct pushes because other means can't work in that precise case, an incremental approach is usually necessary because of various timeouts (see issue heptapod#25 for more details). Arbitrary selection of successive points in history tends to produce multiple heads even if the whole repository hasn't any.

To allow multiple heads, use the following HGRC server-side snippet:

allow-multiple-heads = yes

or use the allow-multiple-heads parameter in the HGRC REST API.

Publishing changesets is not allowed for Project Developers

Starting with Heptapod 0.6.0, Developers are not permitted to push a public changeset nor to perform actions leading to changeset publications.

Heptapod 0.26.0 (November 2021) introduced the Mercurial Publisher role to grant this permission without full maintainership. Before that, only Maintainers had publication rights.

This is an important design choice, and it is related to the fact that we don't support personal forks yet. Indeed, without personal forks, one must assign Developer roles to all contributors on the main project, so that they can push their changes for review. Without the publishing permission, this would mean that all contributors would gain unconditional rights to push anything on non-protected branches with their Mercurial client.

If you still want to lift this permission restriction, you may disable the check_publish hook, with the following HGRC server-side snippet:


Heptapod is auto-publishing except for topics

Also starting with Heptapod 0.6.0, this goes with the restriction of the publication permission: all non-topic changesets are published automatically by Heptapod.

This means in particular that only repository Maintainers can actually push a non topic changeset.

To switch to a full non publishing behaviour, use the following HGRC server-side snippet:

auto-publish = nothing

or set the auto_publish parameter to nothing via the HGRC REST API.

Before Heptapod 0.6.0, all repos were non publishing: all draft changesets pushed to Heptapod would have stayed drafts.

This finishes the set of features that leave us in a quite simple state with respect to named branches, topics and phases.

Does Heptapod have personal forks?

Personal forks are currently available only for Git projects, but we plan to improve on that in the future. Please read below for more about making contributions easier for Mercurial projects.

We acknowledge the importance of personal forks, especially for the purpose of so-called "drive-by contributions," and it's on our roadmap, but we haven't gotten to it yet. Our current plan is to take an incremental approach, and as the first step, support drive-by contributions without requiring a grant of permission. We plan to do this before we introduce personal forks proper.

Not having personal forks is a bit less problematic with Mercurial projects than one could fear: on one hand, all contributors to a project have to be granted the right to push topics to its main repository. On the other hand, this corresponds in Heptapod to the Developer role, which does not have the right to push to named branches by default. We realize that the necessity for contributors to request a grant of permission is unwanted friction that we need to resolve.

Our nearest term plans to foster easy contribution consist of implementing user namespaces for topics. Users with enough rights to open an Issue on the Project would also be allowed to push changesets with topics in their own namespace. More specifically, the required role will be Reporter. This role is granted by default on public projects to all authenticated users.

At that point, personal forks wouldn't really be necessary, but they would still be the basic UX expectation, especially among new users. We will therefore proceed to implement them, but that is more difficult to get right and will probably rely under the hood on topic user namespaces again.

How to find a repository on the server file system

Hashed or legacy ?

Since version 10.0, GitLab has been slowly transitioning its repository storage from a simple structure that matches the Groups and Projects URIs (the legacy storage) to one that is totally independent of them and better distributed (the hashed storage).

A given server can have projects on both storages at the same time.

The hashed storage is enabled by default for new Heptapod installations since Heptapod 0.12 (GitLab 12). This means that new projects are created in the hashed storage.

All new projects will be on the hashed storage as of GitLab 13 (should be Heptapod 0.15), and all projects will have to be migrated to the hashed storage for GitLab 14 (end of May, 2021).

The repositories root

In Docker containers, the repositories are below:


In source installations, they are where specified in the repositories section of config/gitlab.yml.

In theory Docker containers can be reconfigured for a different root, or even several storages. In practice, this is very uncommon.

Relative path: the hashed storage case

In the hashed case, the relative path from REPOSITORIES_ROOT is based on the Project numeric ID, which is visible on the "Project overview" page. Here's an example taken on

screenshot of the "Project overview" page, displaying the Project ID with the selected left menu item being also visible.


the ID can also be found by performing an API GET request on the project. This can be done with the Project URI path.

The repository is under @hashed/, which is a 3-level structure whose leaf names are based on SHA256 of the Project ID.

Here's an example for ID=76 as in the screenshot above:

  • compute the hash:

    $ echo -n 76 | sha256sum
    f74efabef12ea619e30b79bddef89cffa9dda494761681ca862cff2871a85980  -

    The -n flag is necessary: without it, the terminating new line produced by echo would make the displayed hash value wrong.

  • the first two levels in the hierarchy are the four hexadecimal digits of the hash, grouped by two. The third level is the full hash, with an additional .hg. In our example, the full relative path is:


Another way to find the relative path is to lookup the project in Admin Area > Overview > Projects: just replace the .git extension in the value of "Gitaly relative path" by .hg.

Reference: Hashed storage in GitLab documentation.

Relative path: the legacy storage case

In the legacy storage, the relative path on the file system matches the URI path, only with the added .hg extension:


In other words, for https://heptapod.example/demo/subgrp/awesome-proj, the relative path would just be demo/subgrp/awesome-proj.hg.

How to run a Mercurial command on the server

Ideally, you shouldn't have to do it, but some administrative and troubleshooting actions would still require running hg on the server.


be sure to run the proper hg with the proper configuration. Otherwise, you could get very bad surprises.

  1. Find out the path to the Mercurial executable

    For Docker containers, this is simply hg.

    In source installations, this is the value of bin_path in mercurial section of config/gitlab.yml

  2. Set the HGRCPATH environment variable as appropriate.

    For Docker containers:

    export HGRCPATH=/opt/gitlab/etc/docker.hgrc:/etc/gitlab/heptapod.hgrc

    For source installations, set HGRCPATH to the values of hgrc, in the mercurial section, using the : (colon) separator and respecting the ordering. For example, this configuration:

         - first/path/hgrc
         - second/hgrc

    corresponds to:

    export HGRCPATH=first/path/hgrc:second/hgrc
  3. You'll need to find out the repository path.

  4. Double check that the path to the hg executable and the HGRCPATH are correct:

    • run /path/to/hg version --debug. You should see heptapod among the listed extensions.
    • if you have special configuration settings, you may want to check they are listed in /path/to/hg config.
  5. Consider doing a backup of the repository, especially if you're about to perform a destructive action.

  6. Run the command:

    /path/to/hg -R /path/to/repo …

    If you're about to modify repository content, it is a good idea to do simple readonly actions before hand (log, branches, topics), to improve confidence that the configuration is all right.

  7. Don't expect any console output, check the exit code and the logs.

    Usually the command won't produce any console output: Heptapod standard configuration redirects everything to the usual Mercurial logs. In Docker context, that would be /var/log/gitlab/hgserve/mercurial.log.

    To check the exit code, do echo $? right away (it's also displayed in the logs).

    In Mercurial, exit code 1 can just mean "nothing was to be done", while errors have exit code 255.

How can one change the settings of a server-side repository?

As of this writing we don't have a proper Web User Interface, but

  • Heptapod systems administrators can do it in the Docker container directly.
  • regular users with Maintainer rights can do it at the Project level with the HGRC REST API.

Changing Mercurial configuration of a Project with the REST API


this is subject to significant changes before Heptapod 1.0

Since Heptapod 0.8, it's possible to change a small subset of high level Mercurial Project settings through a dedicated REST API endpoint. Full management rights on the project are mandatory.

As of Heptapod 0.13, this is still the only way to adjust Mercurial settings for a given Project for regular users.

These settings are kept in a separate file, and are applied on top of the settings that the systems administrator may have put in the other HGRC files (system-wide, group level or project level), perhaps using lower level knobs.


On and, the system-wide settings are just the Heptapod defaults. On, some groups may have dedicated settings.

To get started, read first how to use GitLab REST API and create a suitable access token.

The endpoint is PUT /api/v4/projects/:id/hgrc, where :id is the numeric identifier of the project (visible on the "Project overview" page) or its URL-encoded path.

The payload has to be transmitted as a JSON object, with the following fields:


fields not transmitted are erased by the call, hence existing values have to be repeated to be kept. See the GET requests below to inspect existing values.

  • inherit (boolean, required): whether to inherit configuration from the namespace (group). By default, this is true, but you can use it so that a given project does not inherit configuration set at the group level. It is necessary to resend it even if you don't want to change it.
  • allow_bookmarks (boolean): see Does Heptapod support bookmarks?.
  • allow_multiple_heads (boolean): see One head per branch or topic.
  • auto_publish (string). The admissible values are nothing, non-topic and all. The Heptapod default behaviour is equivalent to non-topic. See Heptapod is auto-publishing except for topics.


curl --request PUT \
     --header "Private-Token: <your_access_token>" \
     --header "Content-Type: application/json" \
     --data '{"inherit": true, "auto_publish": "nothing"} \

You can inspect the current configuration with two different GET endpoints.

The first GET endpoint gives you only the high level settings for the project:

$ curl --header "Private-Token: <your_access_token>" \

This doesn't really mean that, for instance, bookmarks are not allowed. They might be permitted by the system-wide HGRC file, or by some group HGRC, perhaps using lower level settings (see the note above about and, though).

and the second one gives you the raw separate HGRC file together with inheritance information. The file includes an audit log for last previous update:

$ curl --header "Private-Token: <your_access_token>" \
{"inherit":true,"content":"# This file is entirely managed through the GitLab Rails app\n# last update by user testgr (id=123) at 2020-06-16 17:42:11 +0000\n[heptapod]\nauto-publish = nothing\n\n"}

Needed actions to change HGRC files

  1. Edit the appropriate HGRC files in the instance (see Mercurial configuration files below). Currently, for Docker instances, that means either using vi under docker exec or directly within the Docker volume if that makes sense to you.

  2. In case of change of the system-wide settings, reload the configuration of the hgserve service. For Docker instances, that would be:

    docker exec -t CONTAINER_NAME gitlab-ctl hup hgserve

    If in doubt whether the changes have been applied, you may also restart the whole instance, but that causes a few minutes of downtime.

    You can watch the hgserve workers restarting in gunicorn.log (under /var/log/gitlab/hgserve for Docker instances)

Mercurial configuration files

The settings of Heptapod Mercurial repositories rely on the standard HGRC system, and have been cut in several parts by a cascade of %include directives.

  • /etc/gitlab/heptapod.hgrc: these are the global settings that apply to all Heptapod repositories. This is where you would, e.g., fine tune the Mercurial logging, and in particular set the Sentry DSN. This file is initialized with instructions on how to safely edit it.
  • REPOSITORIES_ROOT/GROUP[/SUBGROUP]/hgrc: specific settings for the given group (see about REPOSITORIES_ROOT above).

There are two Project specific HGRC files. You need to find out the repository path first:

  • REPO_PATH/.hg/hgrc: if you want to change it, be extra careful not to interfere with inclusions.
  • REPO_PATH/.hg/hgrc.managed: don't change it. This file is entirely managed by the GitLab Rails application, and gets fully overwritten at each HGRC REST API call. Furthermore, it is designed not to contain sensitive information: Project Maintainers can read it in its entirety with API calls.

Does Heptapod use Git under the hood to handle Mercurial repositories?

Short answer as of Heptapod 0.40: no, by default it does not or rather, not any more.

This was a temporary implementation detail that is actually still in place for some projects created before Heptapod 0.25. Namely, those whose VCS type read as "Mercurial (legacy with hg-git conversion)". The conversion was going only from the server-side Mercurial repository to an inner Git repository; there have never been any no back conversion to Mercurial.

In this legacy mode, the Mercurial client was interacting with a regular Mercurial repository on the server side, as it still does, but the server-side Mercurial repository was itself synchronously pushing under the hood to a Git repository using hg-git.

The only purpose of the Git repository was to be a read-only convenience mirror for other GitLab components, so it was best to think of it as a side view.

Because there was no back conversion, all writes initiated from GitLab (e.g. merging a Merge Request from the web user interface) were actually performed with regular Mercurial commands on the server-side Mercurial repository, and only then pushed to the Git repository. This guarantees that Heptapod behaves like a regular Mercurial server.

When the Heptapod project was initiated, this mirror Git repository trick helped keeping the size of our diff from regular GitLab to a minimum and get to a working prototype very quickly, but it had many drawbacks, including performance and scalability.

We are at the final stage of getting rid of it. See The road to fully native Mercurial in Heptapod for an in-depth overview of the topic.

The newer "native" Mercurial projects do not convert anything to Git. Instead, they are exposed when needed to the Web application and other GitLab components as if they were Git repositories, so that much of the logic and presentation stay unchanged from upstream GitLab.

State as of Heptapod 0.40

  • New Mercurial projects are native by default (since Heptapod 0.25)
  • Native Mercurial projects are not converted internally to Git by default, but the conversion can be reinstated by feature flags in Heptapod <0.41 or kept by Project settings if convenient, e.g., to push to Git remotes.
  • Creation of new legacy (hg-git based) Mercurial projects is disabled by default.
  • Existing legacy (hg-git based) Mercurial projects are still working as they always have.
  • Making a legacy Mercurial project native involves a one shot data migration currently under testing.
  • Current native Mercurial projects will not need any further data migration.