npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@saptools/gitport

v0.1.11

Published

Port GitLab merge requests from repo A to repo B with sequential cherry-picks, Draft MRs, and incoming conflict capture.

Readme

🚢 @saptools/gitport

Port GitLab merge requests from repo A to repo B with real sequential git cherry-pick -x, preserving original commit authors and source traceability.

Built for teams that need to move a whole MR from one related repository to another without hand-adding remotes, copying patches, or touching the current working directory.

status node typescript license

StatusInstallQuick StartCLIAPIHow it works


✨ Features

  • 🔁 Whole-MR porting — fetches every commit from a source GitLab MR and replays them into a destination repo in order
  • 🧬 Real Git history — uses actual git cherry-pick -x per commit, preserving the original author while recording source traceability
  • 📝 Draft MR by default — opens a Draft MR for new port branches and only updates the branch when the port branch already exists
  • ⚖️ Incoming conflict strategy — when a cherry-pick conflicts, captures the old destination-side code, chooses incoming, and records the conflict in the Draft MR

📌 Status

@saptools/gitport is published on npm and maintained in the saptools monorepo.


📦 Install

npm install -g @saptools/gitport

# Or use it as a library
pnpm add @saptools/gitport

[!NOTE] Requires Node.js ≥ 20, Git on PATH, and a GitLab token that can read the source repo, push to the destination repo, and create merge requests.


🚀 Quick Start

export GITPORT_GITLAB_TOKEN="<gitlab-token>"

gitport \
  --source-mr-url https://gitlab.example.com/repo-a/-/merge_requests/123 \
  --destination-repo-url https://gitlab.example.com/repo-b \
  --base-branch main \
  --port-branch gitport/repo-a-mr-123 \
  --title "JIR-112 carry feature"

Gitport will clone the destination repo into an isolated run folder, fetch the source MR commits, replay them one by one with git cherry-pick -x, push the destination branch, and create a Draft GitLab MR assigned to the token account. If --port-branch already exists in the destination repo, Gitport checks out that branch, cherry-picks onto it, pushes it, and skips Draft MR creation.

If a conflict happens, Gitport captures the destination-side and incoming-side conflict hunks, resolves the file with incoming by default, completes the cherry-pick automatically, and records the conflict details in the run report plus the Draft MR description when a new MR is created. The MR diff also keeps the overwritten destination lines visible during review.


🧰 CLI

🔁 gitport --source-mr-url <url>

Port one GitLab merge request from a source repo into a destination repo.

gitport \
  --source-mr-url https://gitlab.example.com/repo-a/-/merge_requests/123 \
  --destination-repo-url https://gitlab.example.com/repo-b \
  --base-branch main \
  --port-branch gitport/repo-a-mr-123 \
  --title "JIR-112 carry feature"

| Flag | Description | | --- | --- | | --source-mr-url <url> | Required. GitLab source merge request URL, such as https://gitlab.example.com/repo-a/-/merge_requests/123 | | --destination-repo-url <url> | Required. GitLab repo URL that receives the ported commits. The .git suffix is optional | | --base-branch <name> | Required. Destination branch to create the port branch from | | --port-branch <name> | Required. Destination branch that receives the cherry-picks. Existing branches are reused | | --title <title> | Required. Destination Draft MR title | | --token <token> | GitLab token. Falls back to GITPORT_GITLAB_TOKEN | | --keep-workdir | Keep the isolated run folder after a successful port |


🧑‍💻 Programmatic Usage

import { parseSourceMergeRequestRef, portGitLabMergeRequest } from "@saptools/gitport";

const source = parseSourceMergeRequestRef(
  "https://gitlab.example.com/repo-a/-/merge_requests/123",
);

const result = await portGitLabMergeRequest({
  sourceRepo: source.sourceRepo.original,
  destRepo: "https://gitlab.example.com/repo-b",
  sourceMergeRequestIid: source.sourceMergeRequestIid,
  baseBranch: "main",
  portBranch: "gitport/repo-a-mr-123",
  title: "JIR-112 carry feature",
  token: process.env.GITPORT_GITLAB_TOKEN,
});

if (result.mergeRequestCreated) {
  console.log(result.mergeRequestUrl);
} else {
  console.log(`Updated ${result.portBranch}`);
}

The CLI and library use the same porting engine. Library consumers can build custom review flows, batch jobs, internal dashboards, or agent workflows without shelling out to the CLI.


🔭 How it works

┌──────────────────────────┐
│ gitport                  │
│   --source-mr-url <url>  │
└─────────────┬────────────┘
              │
              ▼
  1. Resolve token from --token or GITPORT_GITLAB_TOKEN
  2. Read source MR metadata and commits from GitLab
  3. Clone destination repo into ~/.saptools/gitport/runs/<run-id>/dest
  4. Fetch the source repo as a temporary remote
  5. Check out --port-branch from its existing remote branch or from --base-branch
  6. Run git cherry-pick -x <sha> once per source MR commit
  7. On conflict, capture ours/theirs hunks, choose incoming, and complete the cherry-pick
  8. Push the destination branch
  9. Create a Draft MR unless --port-branch already existed
 10. Write every auto-resolved conflict into the run report and Draft MR description when an MR is created

Commit identity

Git cherry-pick preserves the original commit author. The person or automation running Gitport becomes the committer, which is standard Git behavior and keeps the audit trail honest.

Duplicate detection

Gitport compares patches, not only SHAs, because related repositories often have different commit IDs for the same change. It uses git cherry to skip already-ported changes safely. GitLab commit lists are read with pagination, so large MRs are not truncated at the first 100 commits.


🛡️ Safety model

  • Never modifies the current working repository
  • Creates the destination MR as Draft by default for new port branches
  • Skips patch-equivalent commits that already exist in the destination history
  • Never writes GitLab tokens to reports, config files, command previews, or errors
  • Auto-resolves cherry-pick conflicts with incoming by default, after capturing the old destination-side code for review
  • Cleans successful run folders unless --keep-workdir is set
  • Blocks publishing unless typecheck, lint, unit tests, e2e tests, and build all pass

🛠️ Development

From the monorepo root:

pnpm install
pnpm --filter @saptools/gitport typecheck
pnpm --filter @saptools/gitport lint
pnpm --filter @saptools/gitport test:unit
pnpm --filter @saptools/gitport test:e2e
pnpm --filter @saptools/gitport build

The e2e suite should use local fixture Git repositories and a mocked GitLab HTTP server. CI must not call real GitLab projects.


🗺️ Roadmap

  • MVP: one source GitLab MR to one destination Draft MR with sequential cherry-picks
  • Conflict flow: capture conflict hunks, choose incoming by default, and report conflicts in the Draft MR
  • Batch mode: port one MR to multiple destination repos
  • Saved conflict rules: reuse known resolutions only when explicitly configured

👨‍💻 Author

dongtran

📄 License

MIT


Made with ❤️ to make your work life easier!