Frequently Asked Questions

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?

No, but we'd love it to. If you want it sooner than later, vote for it via the general interest and support form.

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:

topic/TARGET_HG_BRANCH/TOPIC_NAME

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:

[heptapod]
allow-bookmarks = yes

or use the allow_bookmarks parameter in the HGRC REST API:

Note

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:

[heptapod]
allow-multiple-heads = yes

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

Publishing is restricted to project maintainers

Starting with Heptapod 0.6.0, only project Maintainers are permitted to push a public changeset or the promotion a changeset to the public phase.

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 inconditional rights to push anything with their Mercurial client.

Note

For consistency between the Web UI (or RESTful API) and what is allowed with a hg push, at the time of this writing, it is necessary to use GitLab's protected branch feature, and to declare explicitely that branch/* is protected on all projects. We hope to make it automatic for the 0.6.0 release.

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

[hooks]
pretxnclose.heptapod_check_publish=

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:

[heptapod]
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?

Not for the time being, but we need them less than in regular GitLab.

In practice, not having forks means that all contributors to a project have to be granted the right to push topics to its main repository. In Heptapod, this is the Developer role, which does not have the right to push to named branches by default.

We are thinking about supporting personal forks, but we want to do it the right way, so that the chosen solution is transparent for users, and robust enough to serve us in the long run.

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:

REPOSITORIES_ROOT=/var/opt/gitlab/git-data/repositories

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 foss.heptapod.net:

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

Note

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:

    @hashed/f7/4e/f74efabef12ea619e30b79bddef89cffa9dda494761681ca862cff2871a85980.hg
    

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:

RELATIVE_PATH = GROUP_SLUG[[/SUBGROUP_SLUG]]/PROJECT_SLUG.hg

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.

Warning

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:

    mercurial:
      hgrc:
         - 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

Note

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.

Note

On foss.heptapod.net and heptapod.host, the system-wide settings are just the Heptapod defaults. On foss.heptapod.net, 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:

Warning

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.

Example:

curl --request PUT \
     --header "Private-Token: <your_access_token>" \
     --header "Content-Type: application/json" \
     --data '{"inherit": true, "auto_publish": "nothing"} \
     https://heptapod.example.com/api/v4/projects/my-group%2Fmy-project/hgrc

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>" \
     https://heptapod.example.com/api/v4/projects/my-group%2Fmy-project/hg_heptapod_config
{"auto-publish":"nothing"}

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 foss.heptapod.net and heptapod.host, 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>" \
     https://heptapod.example.com/api/v4/projects/my-group%2Fmy-project/hgrc
{"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?

Yes, as a temporary implementation detail, going only from the server-side Mercurial repository to an inner Git repository; there is no back conversion to Mercurial.

The Mercurial client interacts with a regular Mercurial repository on the server side, which itself pushes under the hood to a Git repository through a customized version of hg-git.

The only purpose of the Git repository is to be a read-only convenience mirror for other GitLab components, including the Git hooks that GitLab uses internally to get notified of new commits and perform subsequent actions (closing a Merge Request, launching the CI…).

Because there is no back conversion, all writes initiated from GitLab (e.g. merging a Merge Request from the web user interface) actually perform regular Mercurial commands on the server-side Mercurial repository. This guarantees that Heptapod behaves like a regular Mercurial server.

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 has many drawbacks, including performance and scalability.

During the current post-prototype, yet experimental phase, we intend to gradually get rid of it.