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
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
Store your Mercurial API credentials in Lovable Cloud Secrets
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().
Create the Mercurial proxy Edge Function
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.
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
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'23const corsHeaders = {4 'Access-Control-Allow-Origin': '*',5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',6}78serve(async (req) => {9 if (req.method === 'OPTIONS') {10 return new Response('ok', { headers: corsHeaders })11 }1213 try {14 const heptapodToken = Deno.env.get('HEPTAPOD_TOKEN')15 const heptapodBaseUrl = Deno.env.get('HEPTAPOD_BASE_URL')1617 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 }2324 const url = new URL(req.url)25 const endpoint = url.searchParams.get('endpoint')2627 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 }3334 // Only allow read-only API paths35 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 }4344 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 })5152 const data = await response.json()5354 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.
Fetch and display Mercurial changeset data in your React frontend
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.
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.
Handle pagination and branch filtering
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.
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.
Test the integration and verify security
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.
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.
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.
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.
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
GitLab is a native Lovable shared connector that works without any Edge Function bridge — choose it if your team can migrate version control to a Git-based platform.
VS Code with the Mercurial extension allows local Hg development that you export to GitHub for Lovable sync, avoiding the need for a live API bridge.
Docker lets you containerize a Mercurial-to-Git mirror service that auto-syncs your Hg repo to GitHub, giving Lovable a native Git source of truth without runtime API calls.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation