Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Mercurial

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.

What you'll learn

  • How Lovable's Git-based workflow differs from Mercurial and why a bridge is needed
  • How to create a Supabase Edge Function that proxies the Bitbucket or Heptapod REST API
  • How to securely store Mercurial API credentials in Lovable Cloud Secrets
  • How to fetch and display Mercurial changeset history in your Lovable app
  • How to troubleshoot common Hg-to-Git bridge errors in Lovable's Logs panel
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate14 min read45 minutesDevOpsMarch 2026RapidDev Engineering Team
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.

Integration method

Edge Function Integration

Because Lovable's native version control is Git-based (via GitHub), Mercurial integration requires an Edge Function that acts as a bridge layer. The Edge Function proxies calls to Bitbucket or Heptapod REST APIs, translating Mercurial changesets and branch metadata into formats your Lovable app can consume. Secrets like Bitbucket App Passwords are stored securely in Lovable Cloud's Secrets panel and accessed only server-side via Deno.env.get().

Prerequisites

  • 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.

Pro tip: Use all-caps with underscores for secret names (e.g., HEPTAPOD_TOKEN) to match the convention Lovable's AI expects when it generates Edge Function code referencing your secrets.

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.

Lovable Prompt

Create a Supabase Edge Function called 'hg-proxy' that proxies the Heptapod REST API. Read HEPTAPOD_TOKEN and HEPTAPOD_BASE_URL from environment variables using Deno.env.get(). The function should accept a GET request with a query parameter 'endpoint' that specifies which Heptapod API path to call. Forward the request to ${HEPTAPOD_BASE_URL}/api/v4/${endpoint} with the Authorization: Bearer ${HEPTAPOD_TOKEN} header. Return the JSON response to the caller. Include CORS headers so the frontend can call it. Handle errors gracefully with appropriate HTTP status codes.

Paste this in Lovable chat

supabase/functions/hg-proxy/index.ts
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
2
3const corsHeaders = {
4 'Access-Control-Allow-Origin': '*',
5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
6}
7
8serve(async (req) => {
9 if (req.method === 'OPTIONS') {
10 return new Response('ok', { headers: corsHeaders })
11 }
12
13 try {
14 const heptapodToken = Deno.env.get('HEPTAPOD_TOKEN')
15 const heptapodBaseUrl = Deno.env.get('HEPTAPOD_BASE_URL')
16
17 if (!heptapodToken || !heptapodBaseUrl) {
18 return new Response(
19 JSON.stringify({ error: 'Missing Heptapod credentials in Secrets' }),
20 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
21 )
22 }
23
24 const url = new URL(req.url)
25 const endpoint = url.searchParams.get('endpoint')
26
27 if (!endpoint) {
28 return new Response(
29 JSON.stringify({ error: 'Missing endpoint query parameter' }),
30 { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
31 )
32 }
33
34 // Only allow read-only API paths
35 const allowedPrefixes = ['projects/', 'groups/']
36 const isAllowed = allowedPrefixes.some(prefix => endpoint.startsWith(prefix))
37 if (!isAllowed) {
38 return new Response(
39 JSON.stringify({ error: 'Endpoint not permitted' }),
40 { status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
41 )
42 }
43
44 const apiUrl = `${heptapodBaseUrl}/api/v4/${endpoint}`
45 const response = await fetch(apiUrl, {
46 headers: {
47 'Authorization': `Bearer ${heptapodToken}`,
48 'Content-Type': 'application/json',
49 },
50 })
51
52 const data = await response.json()
53
54 return new Response(
55 JSON.stringify(data),
56 {
57 status: response.status,
58 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
59 }
60 )
61 } catch (error) {
62 return new Response(
63 JSON.stringify({ error: error.message }),
64 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
65 )
66 }
67})

Pro tip: The allowedPrefixes check restricts the proxy to read-only API paths. This prevents the Edge Function from being misused to make write requests to your Heptapod instance if the function URL is ever discovered by a third party.

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.

Lovable Prompt

Create a React component called 'MercurialChangeset' that fetches the last 15 changesets from my Heptapod project. Use supabase.functions.invoke('hg-proxy', { body: null }) with the query parameter endpoint set to 'projects%2FYOUR_PROJECT_PATH%2Frepository%2Fcommits?per_page=15'. Display each changeset in a card showing: the short hash in a monospace badge, the commit message as the title, the author name, and the date formatted as 'MMM D, YYYY'. Show a loading skeleton while fetching and an error state if the call fails.

Paste this in Lovable chat

Pro tip: Replace YOUR_PROJECT_PATH with your actual Heptapod project path URL-encoded. If your path is acme-corp/backend-api, encode it as acme-corp%2Fbackend-api in the query string.

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.

Lovable Prompt

Update the MercurialChangeset component to support branch filtering. Add a dropdown that lists branch names fetched from the Heptapod API (endpoint: projects%2FYOUR_PROJECT_PATH%2Frepository%2Fbranches). When a branch is selected, re-fetch changesets filtered to that branch using the ref_name parameter. Add a 'Load more' button that increments the page parameter and appends new results to the existing list.

Paste this in Lovable chat

Pro tip: In Mercurial, the primary branch is typically called 'default' rather than 'main' or 'master'. Make sure your UI labels this branch clearly so non-Mercurial users understand it is the equivalent of the main branch.

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.

Pro tip: Use the Cloud → Logs panel to tail Edge Function execution logs in real time. Set your browser to open the Lovable app in a separate tab and trigger the changeset fetch — you will see the log entry appear within seconds.

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.

Common 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.

Lovable Prompt

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.

Copy this prompt to try it in Lovable

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.

Lovable Prompt

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.

Copy this prompt to try it in Lovable

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.

Lovable Prompt

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.

Copy this prompt to try it in Lovable

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.

typescript
1if (req.method === 'OPTIONS') {
2 return new Response('ok', { headers: corsHeaders })
3}

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'.

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.

Alternatives

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.