# GitHub vs GitLab Forge Integration in tuicr: 5 Key Differences Explained

> Discover the 5 key differences between GitHub and GitLab forge integration in tuicr. Understand variations in CLI dependencies, diff retrieval, and more.

- Repository: [Almog Gavra/tuicr](https://github.com/agavra/tuicr)
- Tags: comparison
- Published: 2026-08-02

---

**tuicr implements GitHub and GitLab integration through separate `ForgeBackend` trait implementations that differ in CLI dependencies, diff retrieval, comment anchoring, and draft review semantics.**

The [tuicr](https://github.com/agavra/tuicr) terminal UI for code reviews abstracts remote forge interactions behind a common interface, yet each backend handles platform-specific quirks differently. Understanding these distinctions helps users troubleshoot authentication errors, parse target formats correctly, and know what to expect when switching between forges.

## CLI Dependencies and Backend Instantiation

Each integration relies on a different official command-line tool.

**GitHub** requires the `gh` CLI. The backend is instantiated in [`src/forge/github/gh.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/gh.rs), which emits errors such as "GitHub integration requires `gh`" and "GitHub authentication failed" when prerequisites aren't met.

**GitLab** requires the `glab` CLI. Its implementation in [`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs) produces analogous error messages: "GitLab integration requires `glab`" and "GitLab authentication failed."

Both backends check for the binary and valid authentication before executing any remote operations.

## Remote Detection Priority

The detection logic in [`src/forge/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/mod.rs) prioritizes GitHub over GitLab.

- GitHub detection matches remotes whose host contains "github" or matches a configured GitHub Enterprise host
- GitLab detection runs as a fallback, checking for "gitlab" in the host or consulting `glab`'s configuration file

Users can override automatic detection with explicit flags:

```bash
tuicr --forge github pr open 12   # force GitHub backend

tuicr --forge gitlab mr open 3    # force GitLab backend

```

## PR/MR Target Parsing and Identification

The two forges accept different target formats.

| Forge | Valid target formats |
|-------|-------------------|
| GitHub | `owner/repo#<number>`, full PR URL |
| GitLab | numeric IID, `owner/repo#iid`, full MR URL, `<host>/owner/repo#iid` |

GitHub parsing lives in [`src/forge/github/pr_info.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/pr_info.rs). GitLab parsing is embedded in [`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs) and accepts more flexible host-qualified identifiers.

## Diff Retrieval: Cumulative vs Commit-Range

The backends retrieve diffs through fundamentally different mechanisms.

**GitHub** ([`src/forge/github/gh.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/gh.rs)) uses `gh pr diff` for the cumulative diff and falls back to the GitHub compare API with `Accept: application/vnd.github.diff` when needed.

**GitLab** ([`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs)) invokes `glab mr diff`. A critical limitation: the commit-range diff is only available when the MR head is reachable locally. Otherwise, tuicr reports "Commit range diff without local checkout not yet supported for GitLab."

## Comment Anchoring and Position Payloads

Inline comment positioning requires platform-specific JSON structures.

GitHub's payload in [`src/forge/github/submit.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/submit.rs) uses:

- `side` field: `"LEFT"` or `"RIGHT"`
- Single `line` number

GitLab's payload in [`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs) requires:

- `old_path` and `new_path`
- `old_line` and `new_line`
- Computed `line_code`
- `line_range` object for multi-line comments

The GitLab backend explicitly notes: "GitLab positions need both old_path and new_path" when constructing the position hash.

## Draft Review Semantics

Draft handling diverges significantly between platforms.

**GitHub** supports native draft reviews. tuicr creates one by omitting the `event` field (`SubmitEvent::Draft`). GitHub returns a review ID that is stored on each comment for later submission.

**GitLab** has no direct draft review API. Instead, tuicr creates a draft note, and the user must publish it through the GitLab web UI. Lines 567-569 in [`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs) document this limitation: the backend creates the note but cannot transition it to published status programmatically.

## Permission and Error Handling Patterns

Both backends emit forge-specific permission errors:

- GitHub: "Cannot submit review: GitHub token lacks pull request write permission." ([`src/forge/github/gh.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/gh.rs), line 908)
- GitLab: "Cannot submit review: GitLab token lacks merge request write permission." ([`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs), line 1221)

GitHub may return HTTP 422 when the selected commit isn't part of the PR. tuicr surfaces a clarified message: "GitHub rejected the review..."

GitLab errors when the MR head has moved or when draft notes cannot be published, with similar user-facing explanations in the GitLab backend file.

## Remote Comment Fetching Architectures

The data retrieval patterns differ in protocol and parsing strategy.

| Aspect | GitHub | GitLab |
|--------|--------|--------|
| Protocol | GraphQL | REST via `glab api` |
| Thread queries | [`src/forge/github/review_threads.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/review_threads.rs) | Embedded in [`glab.rs`](https://github.com/agavra/tuicr/blob/main/glab.rs) |
| Summary queries | [`src/forge/github/review_summaries.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/review_summaries.rs) | [`src/forge/gitlab/models.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/models.rs) |
| Metadata queries | [`src/forge/github/review_metadata.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/github/review_metadata.rs) | [`src/forge/gitlab/models.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/models.rs) |

GitLab's response parsing is centralized in [`src/forge/gitlab/models.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/models.rs), while GitHub distributes GraphQL handling across specialized modules.

## Summary

- **CLI tools**: `gh` for GitHub, `glab` for GitLab — both required and checked at runtime
- **Detection order**: GitHub prioritized over GitLab in [`src/forge/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/mod.rs)
- **Target formats**: GitLab accepts more flexible host-qualified identifiers
- **Diff retrieval**: GitHub always provides cumulative diffs; GitLab commit-range diffs require local checkout
- **Comment anchoring**: GitHub uses `side`+`line`; GitLab requires `old_path`/`new_path` pairs with `line_code`
- **Draft reviews**: GitHub native and publishable; GitLab creates draft notes requiring web UI publication

## Frequently Asked Questions

### Can tuicr work with both GitHub and GitLab remotes in the same repository?

Yes. The `--forge` flag forces a specific backend when automatic detection in [`src/forge/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/mod.rs) is ambiguous. Without the flag, tuicr selects GitHub if any remote matches GitHub hosts, falling back to GitLab only when no GitHub remote exists.

### Why does GitLab diff retrieval fail without a local checkout?

GitLab's `glab mr diff` command can only compute commit-range diffs when the target branch head is present in the local Git repository. The implementation in [`src/forge/gitlab/glab.rs`](https://github.com/agavra/tuicr/blob/main/src/forge/gitlab/glab.rs) explicitly checks reachability and emits "Commit range diff without local checkout not yet supported for GitLab" when this requirement isn't met. GitHub's compare API does not have this limitation.

### What's the difference between `:submit draft` on GitHub versus GitLab?

On GitHub, `:submit draft` creates a pending review via the pull request reviews API by omitting the `event` field, returning a review ID that tuicr associates with each comment. These drafts can be published later from within tuicr. On GitLab, the same command creates individual draft notes through the notes API, but GitLab provides no programmatic way to publish them — users must complete this step in the GitLab web interface.