# π’ `@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)
[](https://nodejs.org)
[](https://www.typescriptlang.org)
[](#-license)
[Status](#-status) β’ [Install](#-install) β’ [Quick Start](#-quick-start) β’ [CLI](#-cli) β’ [How it works](#-how-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
```bash
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
```bash
export GITPORT_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 `
Port one GitLab merge request from a source repo into a destination repo.
```bash
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 ` | **Required.** GitLab source merge request URL, such as `https://gitlab.example.com/repo-a/-/merge_requests/123` |
| `--destination-repo-url ` | **Required.** GitLab repo URL that receives the ported commits. The `.git` suffix is optional |
| `--base-branch ` | **Required.** Destination branch to create the port branch from |
| `--port-branch ` | **Required.** Destination branch that receives the cherry-picks. Existing branches are reused |
| `--title ` | **Required.** Destination Draft MR title |
| `--token ` | GitLab token. Falls back to `GITPORT_GITLAB_TOKEN` |
| `--keep-workdir` | Keep the isolated run folder after a successful port |
---
## π How it works
```
ββββββββββββββββββββββββββββ
β gitport β
β --source-mr-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//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 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:
```bash
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!