# How to Integrate Lovable with Mercurial

- Tool: Lovable
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: March 2026

## TL;DR

Lovable uses Git natively, so Mercurial repositories require a bridge layer. Connect Mercurial (Hg) repos hosted on Bitbucket or Heptapod to Lovable by using an Edge Function to proxy the Hg REST API, converting changesets into Git-compatible references. This lets your Lovable project read commit history and trigger builds from Mercurial-based workflows without abandoning your existing Hg codebase.

## Bridging Mercurial to Lovable's Git Workflow

Lovable is built on a Git-first architecture. Every project is backed by a GitHub repository, and the platform's AI agent natively understands Git concepts like commits, branches, and pull requests. Mercurial, while a powerful distributed version control system in its own right, speaks a different protocol and uses different terminology — changesets instead of commits, bookmarks instead of branches, and revsets instead of Git log syntax. This architectural gap means you cannot point Lovable directly at a Mercurial repository.

The practical bridge for most teams is Bitbucket's REST API (for Hg repos hosted there before Bitbucket dropped native Mercurial support in 2020, many of which are now migrated to Heptapod) or the Heptapod API for teams still actively using Mercurial. By creating an Edge Function in Lovable, you can proxy these APIs securely — authenticating with an App Password or access token stored in Cloud Secrets — and surface changeset data, author information, and branch status inside your Lovable app.

This integration is particularly useful for enterprise teams that maintain Mercurial monorepos for compliance reasons while building new customer-facing apps on Lovable. Rather than forcing a full VCS migration, the Edge Function bridge lets both systems coexist: Mercurial handles your legacy codebase, and Lovable handles your new product layer, with the Edge Function passing relevant metadata between them.

## Before you start

- A Lovable project with Lovable Cloud enabled (Cloud tab visible in the editor)
- A Mercurial repository hosted on Heptapod (heptapod.net) or a legacy Bitbucket Mercurial repo
- A Heptapod Personal Access Token or Bitbucket App Password with repository read permissions
- Basic familiarity with Lovable's Cloud tab and Secrets panel
- Your Mercurial repository's full path (e.g., heptapod.net/your-org/your-repo)

## Step-by-step guide

### 1. Store your Mercurial API credentials in Lovable Cloud Secrets

Before writing any code, you need to securely store your Heptapod or Bitbucket credentials. Never paste API tokens into Lovable's chat or directly into your code — on the free tier, chat history is publicly visible, and tokens embedded in code end up in your Git history where they can be recovered.

To add your credentials: open your Lovable project, click the Cloud tab (the icon that looks like a database or cluster, found by clicking the '+' panel button in the top area of the editor). Inside the Cloud tab, locate the Secrets section. Click 'Add Secret'. For Heptapod, create a secret named HEPTAPOD_TOKEN and paste your Personal Access Token as the value. Also add HEPTAPOD_BASE_URL with the value set to your Heptapod instance URL (e.g., https://foss.heptapod.net). If you are using Bitbucket with an App Password, create secrets named BITBUCKET_USERNAME and BITBUCKET_APP_PASSWORD.

Lovable encrypts these values immediately upon saving. They will only be accessible from Edge Functions via Deno.env.get() — never from client-side React code. If you see a 'Secret [NAME] not found' error later, the most common cause is a typo between the secret name you stored and the name you reference in code, so double-check for exact case matching.

**Expected result:** Your Heptapod or Bitbucket credentials appear as named secrets in the Cloud tab Secrets panel, with values masked. The secrets are now available to any Edge Function in this project via Deno.env.get().

### 2. Create the Mercurial proxy Edge Function

Now you'll create the Edge Function that acts as your Hg-to-Lovable bridge. This function will receive requests from your Lovable frontend, authenticate with the Heptapod API using your stored credentials, and return changeset data in a format your React components can consume.

In Lovable's chat, describe what you want and the AI will generate and deploy the Edge Function automatically. The function is written in TypeScript and runs on the Deno runtime, so it uses Deno.env.get() for secrets and native fetch for HTTP requests. It lives at supabase/functions/hg-proxy/index.ts in your project's file structure.

The function should handle CORS so your frontend can call it from the browser, validate that requests come from your own app (not arbitrary external callers), and proxy the Heptapod REST API endpoint for listing changesets. Heptapod's API follows GitLab's REST conventions — for example, GET /api/v4/projects/:id/repository/commits returns commit (changeset) data. You'll pass your project path URL-encoded as the project ID parameter.

```
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}

serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  try {
    const heptapodToken = Deno.env.get('HEPTAPOD_TOKEN')
    const heptapodBaseUrl = Deno.env.get('HEPTAPOD_BASE_URL')

    if (!heptapodToken || !heptapodBaseUrl) {
      return new Response(
        JSON.stringify({ error: 'Missing Heptapod credentials in Secrets' }),
        { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      )
    }

    const url = new URL(req.url)
    const endpoint = url.searchParams.get('endpoint')

    if (!endpoint) {
      return new Response(
        JSON.stringify({ error: 'Missing endpoint query parameter' }),
        { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      )
    }

    // Only allow read-only API paths
    const allowedPrefixes = ['projects/', 'groups/']
    const isAllowed = allowedPrefixes.some(prefix => endpoint.startsWith(prefix))
    if (!isAllowed) {
      return new Response(
        JSON.stringify({ error: 'Endpoint not permitted' }),
        { status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      )
    }

    const apiUrl = `${heptapodBaseUrl}/api/v4/${endpoint}`
    const response = await fetch(apiUrl, {
      headers: {
        'Authorization': `Bearer ${heptapodToken}`,
        'Content-Type': 'application/json',
      },
    })

    const data = await response.json()

    return new Response(
      JSON.stringify(data),
      {
        status: response.status,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      }
    )
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  }
})
```

**Expected result:** Lovable deploys the Edge Function and shows it as active in the Cloud tab. You can see the function URL (ending in /functions/v1/hg-proxy) in the Edge Functions section. The function returns JSON from the Heptapod API when called with an appropriate endpoint parameter.

### 3. Fetch and display Mercurial changeset data in your React frontend

With the Edge Function deployed, you can now build the React component that calls it and renders Mercurial changeset data. The component will call your hg-proxy Edge Function with the appropriate Heptapod API path for your repository's commits endpoint. In Heptapod's GitLab-compatible API, the path follows the pattern projects/:encoded_project_path/repository/commits where the project path is URL-encoded (forward slashes become %2F).

For example, if your Heptapod project path is my-org/my-repo, you'd call: /functions/v1/hg-proxy?endpoint=projects%2Fmy-org%2Fmy-repo%2Frepository%2Fcommits&per_page=20

The Lovable AI can generate this component for you if you describe the data shape you want to display. Be specific about which fields from the changeset you want to show: the short hash (the first 8 characters of the full SHA), the author name and email, the commit message, and the timestamp. Heptapod returns commits in GitLab format with fields like id (full hash), short_id, author_name, authored_date, and message.

You'll use the Supabase client's functions.invoke() method to call the Edge Function, or a standard fetch() to the function URL. The supabase.functions.invoke() approach is slightly cleaner as it automatically includes the required apikey header.

**Expected result:** A list or card grid of recent Mercurial changesets appears in your Lovable app preview, showing changeset hashes, messages, authors, and dates pulled live from your Heptapod repository.

### 4. Handle pagination and branch filtering

Mercurial repositories on Heptapod can have thousands of changesets across dozens of named branches. Returning all of them at once would be slow and wasteful. The Heptapod API supports cursor-based pagination via the page and per_page query parameters, and you can filter by branch (ref_name in the commits endpoint) to show only changesets from a specific named branch or bookmark.

Update your Edge Function or your frontend call to support a branch parameter. When the user selects a branch from a dropdown, your component rebuilds the endpoint string to include ref_name=<branch_name>. For example: projects%2Fmy-org%2Fmy-repo%2Frepository%2Fcommits?ref_name=default&per_page=20&page=2 — where default is Mercurial's default branch name (equivalent to Git's main).

For pagination, Heptapod returns X-Next-Page and X-Total-Pages headers in the response. Your Edge Function currently returns only the JSON body — update it to also forward these headers so the frontend knows whether to show a 'Load more' button. Alternatively, you can store fetched changesets in a Supabase table and build pagination against that local cache, which reduces live API calls and makes the UI feel faster.

For complex cases, RapidDev's team can help configure a scheduled Edge Function that syncs Mercurial changeset data into a Supabase table on a regular interval, giving you fast query performance without hitting the Heptapod API on every page load.

**Expected result:** The changeset view now includes a branch selector dropdown. Switching branches refetches and displays the correct changesets. A 'Load more' button appends additional changesets from the next page of API results.

### 5. Test the integration and verify security

Before considering this integration production-ready, run through a security and correctness checklist. First, verify that your Edge Function is not publicly callable with arbitrary Heptapod endpoints — the allowedPrefixes check in the code should block attempts to call admin or write endpoints. Test this by calling the function URL directly with an endpoint like users/admin and confirming it returns a 403 status.

Second, verify your secrets are properly isolated. Open your browser's developer tools on the deployed Lovable app and inspect the network requests. You should see calls to your Edge Function URL (/functions/v1/hg-proxy), but the Heptapod token should never appear in any request from the browser — it should only appear in the server-side Edge Function logs, which you can view in Cloud → Logs.

Third, check Cloud → Logs to confirm the Edge Function is running without errors. Each invocation appears as a log entry. If you see 'Missing Heptapod credentials in Secrets', the secret names in your code do not exactly match the names you stored in the Secrets panel — this is a case-sensitive comparison, so HEPTAPOD_TOKEN and heptapod_token are treated as different names.

Finally, test the component in Lovable's preview with your actual repository path. If the changeset list renders correctly, your Hg-to-Lovable bridge is working. Note that Lovable's preview runs inside an iframe, and some CORS configurations may behave differently in preview versus the deployed app — test on the published URL if you see unexpected CORS errors in preview.

**Expected result:** The Edge Function returns changeset data correctly from Heptapod. Security checks confirm the token is server-side only. Cloud Logs show successful invocations with 200 status codes. The deployed app displays Mercurial changesets without errors.

## Best practices

- Store all Mercurial API credentials (tokens, passwords) exclusively in Lovable Cloud Secrets — never in source code, chat messages, or environment files committed to Git.
- Restrict your Edge Function proxy to read-only Heptapod API paths using an allowlist check, preventing the function from being misused to make write or admin API calls.
- Cache Mercurial changeset data in a Supabase table using a scheduled Edge Function rather than fetching live on every page load — this dramatically improves UI performance and reduces Heptapod API quota consumption.
- Use URL-encoded project paths consistently: encode the forward slash in my-org/my-repo as my-org%2Fmy-repo before passing it to the Heptapod API to avoid 404 errors.
- Monitor Edge Function invocations through Cloud → Logs to catch authentication failures or rate limit errors early, before users report them.
- Handle paginated responses properly — never assume all changesets fit in a single API response. Always implement page-based or cursor-based loading for changeset history views.
- For teams planning a full VCS migration, consider the hg-fast-export or git-remote-hg toolchain to permanently mirror your Mercurial history into GitHub, eliminating the need for the bridge entirely once migration is complete.
- Test your integration on the published Lovable URL (not just the preview iframe) since CORS and auth behavior can differ between the two environments.

## Use cases

### Display Mercurial changeset history in an internal dashboard

Enterprise teams with Mercurial monorepos often need to surface recent changeset activity in internal tools. By proxying the Heptapod or Bitbucket API through a Lovable Edge Function, you can build a dashboard that shows the latest changesets, authors, branch names, and commit messages alongside other operational data.

Prompt example:

```
Create an Edge Function that fetches the last 20 changesets from my Heptapod repository using the REST API. Store my Heptapod access token as HEPTAPOD_TOKEN in Secrets. Return changeset hash, author, date, and description. Then build a React component that displays this as a table with branch badges.
```

### Trigger Lovable webhook on Mercurial push events

If your team pushes to a Mercurial repository on Heptapod and wants Lovable to react — for example, by updating a build status badge or triggering a data sync — you can expose an Edge Function as a webhook receiver. Heptapod supports outbound webhooks for push events that your Edge Function can validate and process.

Prompt example:

```
Create an Edge Function at /functions/hg-webhook that receives POST requests from Heptapod push webhooks. Validate the request using the HEPTAPOD_WEBHOOK_SECRET stored in Secrets. Parse the changeset payload and insert a record into a 'deployments' table in Supabase with branch name, changeset hash, author, and timestamp.
```

### Compare Mercurial branch status against app feature flags

Teams that use Mercurial branches to gate feature work can sync branch status to Lovable's database. An Edge Function polls the Mercurial API for open branches and writes their status to a Supabase table, which the frontend reads to conditionally show or hide features in the app.

Prompt example:

```
Build an Edge Function that calls the Heptapod API to list all open branches in my repo. Store the results in a Supabase table called 'hg_branches' with columns: name, node, is_closed, last_updated. Schedule this Edge Function to run every 10 minutes using pg_cron.
```

## Troubleshooting

### Edge Function returns 500 with 'Missing Heptapod credentials in Secrets'

Cause: The secret names referenced in Deno.env.get() do not exactly match the names stored in the Cloud Secrets panel. This is a case-sensitive, exact-string comparison — a single character difference causes the lookup to return undefined.

Solution: Open Cloud tab → Secrets and verify the exact names you used when creating the secrets (e.g., HEPTAPOD_TOKEN). Then open the Edge Function code and confirm the Deno.env.get() calls use identical names. Edit either the secret name or the code to make them match, then redeploy the Edge Function by saving the file in Lovable.

### API call returns 401 Unauthorized from Heptapod

Cause: The Personal Access Token stored in HEPTAPOD_TOKEN has expired, was revoked, or does not have the 'read_repository' scope required to access the commits API.

Solution: Log in to your Heptapod account and navigate to User Settings → Access Tokens. Check that the token is still active and has the 'read_repository' (or 'api') scope. If expired, create a new token and update the HEPTAPOD_TOKEN secret in Lovable's Cloud tab → Secrets panel.

### CORS error when calling the Edge Function from the Lovable preview

Cause: Lovable's preview runs in an iframe at a different origin than the Edge Function URL. If the Edge Function is missing the Access-Control-Allow-Origin header or is not handling the preflight OPTIONS request, the browser blocks the call.

Solution: Ensure the Edge Function includes the corsHeaders object with Access-Control-Allow-Origin: '*' and returns a 200 response to OPTIONS preflight requests before any other logic. The code template above includes this pattern — verify it is present in your deployed function via Cloud tab → Edge Functions.

```
if (req.method === 'OPTIONS') {
  return new Response('ok', { headers: corsHeaders })
}
```

### Project path returns 404 — Heptapod says the project does not exist

Cause: The project path in the API call is either incorrect, not URL-encoded, or the token does not have access to that project (private project with insufficient permissions).

Solution: Verify your project path in Heptapod's web UI (it appears in the browser URL after heptapod.net/). Encode any forward slashes as %2F in the endpoint parameter. Confirm the access token belongs to a user with at least Reporter-level access to the project. For private projects, the token scope must include 'read_repository'.

## Frequently asked questions

### Does Lovable support Mercurial natively?

No. Lovable's version control is built entirely on Git and syncs with GitHub as the source of truth. Mercurial is not a natively supported VCS. You need an Edge Function bridge to proxy the Mercurial API (via Bitbucket or Heptapod) if you want to surface Hg data inside a Lovable app.

### Can I push code from Lovable back to a Mercurial repository?

Not directly. Lovable writes changes to GitHub via its native Git sync. To write those changes back to Mercurial, you would need a separate CI/CD step — for example, a GitHub Action that uses hg-fast-export to mirror commits from GitHub into your Hg repository. This is a complex pipeline outside Lovable's scope.

### Is Heptapod the same as Bitbucket for this integration?

Functionally similar for this use case, but they have different base URLs and authentication methods. Heptapod uses Personal Access Tokens with a GitLab-compatible API. Bitbucket dropped native Mercurial support in 2020, so most actively maintained Hg repositories have migrated to Heptapod. Use the appropriate HEPTAPOD_BASE_URL or BITBUCKET_BASE_URL in your secrets based on where your repository lives.

### Will this integration work if my Mercurial repo is private?

Yes, as long as your access token has the correct permissions. For Heptapod, the token needs at least 'read_repository' scope and the token owner must have Reporter or higher access to the project. Store the token in Cloud Secrets and it will authenticate all proxy requests from the Edge Function.

### How do I handle rate limits from the Heptapod API?

Heptapod enforces rate limits on API requests. The best mitigation is to cache changeset data in a Supabase table via a scheduled Edge Function that syncs periodically (e.g., every 10 minutes using pg_cron), rather than hitting the API on every user page load. This reduces live API calls to a predictable low volume regardless of how many users view the changeset history.

---

Source: https://www.rapidevelopers.com/lovable-integration/mercurial
© RapidDev — https://www.rapidevelopers.com/lovable-integration/mercurial
