Pull & Merge Requests
Create, read, review, and merge pull requests on GitHub and merge requests on GitLab without leaving Codemux.
The Review pane in the right panel follows the change request for the branch you're on — its description, checks, comments, and diff — and lets you review and merge it in place.
Codemux supports GitHub and GitLab. It never stores hosting credentials of its own — each product is driven through its own command-line tool, using the credentials that tool already holds.
For the review workflow itself — leaving a verdict, line notes, replying to threads, and merging — see Reviewing & Merging. For the triage list across every repo you have open, see The Pull Requests Page.
Requirements
Check both from Settings → Source Control, which shows whether each CLI is installed, which account it's signed in as, and the exact command to fix it if not.
Bitbucket and Azure DevOps are recognized but have no integration; a checkout on one is named, marked read-only, and offered an Open in browser button.
Host Detection
Codemux works out which product a checkout talks to by reading its git remotes — no network call, no CLI, no auth. It prefers the remote your branch tracks, then origin, then the first one listed.
github.comandgitlab.com(including their SSH aliases) are matched exactly.- Any hostname containing
githuborgitlabis classified as that product — so self-hostedgitlab.acme.comand GitHub Enterprise ongithub.acme.internalboth work with no configuration. - A neutral domain like
git.acme.internalcan't be guessed. Map it once under Settings → Source Control → Self-hosted servers; that mapping wins over everything else and syncs across your devices. - Anything unclassified is a normal state, not an error — the Review pane simply reports that the checkout isn't a supported repository.
Detection results are cached briefly, but failures aren't, so adding a remote or a host mapping takes effect immediately.
Terminology
Codemux follows the host's own vocabulary. On GitLab the Review pane reads Merge Request, the create button is Create MR, and references render as !12; on GitHub they read Pull Request, Create PR, and #172. Issue references stay # on both.
What Works Per Host
Every action is drawn from a capability the host declares, so a button you can see is a button that works. An operation a host doesn't declare is not drawn at all rather than shown and disabled.
| GitHub | GitLab | |
|---|---|---|
| List, view, create change requests | Yes | Yes |
| CI checks | Yes | Yes (pipeline jobs) |
| Issues | Yes | Yes |
| Read review threads and inline comments | Yes | Yes |
| Reply to a review thread | Yes | Yes |
| Resolve / unresolve a thread | Yes | Diff discussions only |
| Approve | Yes | Yes |
| Request changes | Yes | No — GitLab has no such verdict |
| Line-level review notes | Yes | No — the Code tab is read-only |
| Merge (squash / merge / rebase) | Yes | Yes |
| Draft ↔ ready, close, reopen | Yes | Yes |
| Timeline | Yes | Yes |
| Check out a request into a worktree | Yes | Yes |
| Deployment previews | Yes | No |
Outdated thread labels | Yes | No |
If a backend call is attempted for an undeclared operation it refuses in plain language — "GitLab does not declare this operation — Codemux cannot resolve threads here. Open it in the browser instead."
Where To Open It
- Right panel — click the Review pane in the pane deck. Its tab badges the comment count plus a rollup glyph for the checks: spinner, green check, or red ✕.
- Pull Requests page — the sidebar footer's pull-request icon, or the command palette. See The Pull Requests Page.
- Sidebar PR chip — every workspace with a request shows a chip like
#372on its sidebar row, colored by state; clicking it opens that request. A workspace with several requests reads#372 +8. When every request in the set builds on another one in it (one's base branch is another's head), the chip's icon becomes a stack icon, the workspace hover card heads the list Stack of 9, and a thin line joins the stacked requests — standalone requests in the same workspace stay unjoined. - Agent Chat Context Row — a clickable chip, colored by state.
Requests opened from a side branch
An agent that runs git checkout -b side-branch, commits, pushes, opens a PR, and then checks the worktree back used to leave the sidebar with no PR badge at all — association was strictly by checked-out branch, and the checked-out branch had none.
When the current branch resolves to no PR, Codemux falls back to branches this worktree checked out recently — up to five distinct names, newest first, read from the worktree's own reflog, excluding the current branch, the repository default branch, and detached-HEAD commits. The fallback never runs while you are on the default branch, and it badges open PRs only (the current-branch path still shows merged and closed state). Admitting history there would let a gh pr checkout of someone else's merged PR donate its badge for the whole reflog window.
A side-branch association is a badge and nothing more. It deliberately does not drive:
- PR-completion auto-settle — that branch merging says nothing about a checkout that may still hold uncommitted work. Such a workspace still ages out through the ordinary idle rule.
- The Wrapping up demotion.
- The Review pane, which stays strictly current-branch so "Create Pull Request" is never hidden for the branch you are actually on.
The workspace hover card gains a PR branch row, shown only when the request's branch differs from the checked-out one. Missing information on either side reads as a match — a detached HEAD during a rebase or bisect keeps its badge — so only two known, differing branch names ever un-associate a workspace from its request.
This reflog fallback is a GitHub-path behavior; GitLab workspaces badge from the current branch only.
The Review Pane
With a request open, the pane is a header, three tabs, and an action bar.
The header carries the repo slug, #123 / !123, a state pill (Open, Draft, Merged, Closed), the title, author · age · N files · +adds −dels, and base ← head marked "checked out here". An overflow menu holds Open in browser, Copy URL, and Refresh.
| Tab | What's on it |
|---|---|
| Summary | The checks rail, reviewers, the description, and the comment threads |
| Timeline | Everything that happened to the request, oldest first |
| Code (N) | The full diff with existing review threads under their lines, and — on GitHub — line-level review |
The tab strip is keyboard-navigable: ← / → move between tabs, Home / End jump to the first or last.
Below the body sits the pending-review footer (when you have unsubmitted notes), at most one drift notice, and the action bar. All three are covered in Reviewing & Merging.
Request data refreshes on a poll: the request and its checks every 30 seconds while checks are running, slowing to every 2 minutes once they've all reported; reviews, comments, threads, and the timeline every 2 minutes. Polling pauses while the window is hidden or after 6 minutes without activity, and picks up again the moment you're back. A failed poll never blanks the content — you get a "Showing data from 4m ago" notice with Retry now instead.
Summary
- Checks rail — a compact rollup, with rows for only the checks that are failing or still running.
- Reviewers — who's been asked and where they landed.
- Description — rendered markdown, foldable past 40 lines, editable in place if you're the author.
- Comments — the review threads. See Review threads.
Timeline
A vertical rail, oldest first, with a filter reading Everything / Host only.
Host events cover opened, reviewed (approved / requested changes / dismissed, with the comment quoted), commented, pushed (abc1234 plus subject, marked "· checks re-ran" on the newest push), force-pushed, merged, closed, reopened, review requested, and renamed. An event Codemux doesn't recognize renders as one plain line rather than being dropped.
Agent runs are the local half of the rail, marked in ember: "Agent run · addressed this thread", what it was pointed at, an optional files · +adds · −dels line, and Open thread to jump to the conversation that did the work. These are local to your install, capped at the 200 most recent, and never pushed to the host — switch the filter to Host only to hide them.
The last row is always live: "12 checks passed, 1 running", dated now.
Creating a Request
When none exists for the current branch, the pane offers Open a pull request / Open a merge request (or Commit and push first, if you haven't pushed).
The form arrives drafted from your commits:
- Title and Description are prefilled from the commits ahead of base — an ember note reads "drafted from your commits", and disappears the moment you type. Nothing is invented: the note never appears when the title was merely humanized from the branch name.
- Base branch picker —
your-branch → mainunder the heading; changing it re-reads the commit set. - N commits — the count beside the heading expands to list the commits going in.
- Write with agent — a chip under the description that hands the commits, a diff stat, a truncated diff, and your repo template to an agent, which writes both the title and the description. While it works the fields are read-only and the description reads "Reading the commits and the diff…"; when it answers, the note reads "drafted by agent" until you edit either field. It runs on the same agent as AI commit messages — the Agent override under Settings → Git → AI Tools if you set one, otherwise the Utility agent. If it fails, your title and description are left as they were.
- Use repo template — a chip that prepends your repository's PR template when it has one (GitHub's known paths; GitLab's
.gitlab/merge_request_templates/). - + reviewer — collect reviewers as chips; they're requested after creation. If a request fails, the PR still exists and you're told which handle didn't take.
- Uncommitted work is called out — "3 files have uncommitted changes" with a Review button.
- Draft or Create. The footer notes that it pushes the branch first.
Enter in the Title field moves to the description rather than submitting — submitting pushes the branch and publishes the request. Ctrl+Enter (Cmd+Enter on macOS) in either field creates it.
On GitLab a draft is created by prefixing the title with Draft:, which is how GitLab itself marks them.
Labels are deliberately absent — the backend can't carry them, so they aren't drawn. The agent's draft arrives all at once when it finishes; it doesn't stream into the field.
CI Checks
The Checks section shows GitHub Actions runs or GitLab pipeline jobs, summarised as a rail with only the failing and running rows expanded. Each row has a status icon, a clickable name, and elapsed time.
Expanding a failing check gives you Full log and Fix with agent. See Handing work to an agent.
On GitLab, pipelines reported by an external system fall back to commit statuses so they still appear.
There is no per-check re-run button. Re-running a job happens on the host.
Incoming Requests
On the default branch, the pane lists incoming requests targeting that base — including from forks. Click a row to open it on the Pull Requests page; use Checkout to create a worktree from the request's branch, or Switch if a workspace for it already exists.
When Something Is Unavailable
The Review pane reports the most fundamental problem first, so you're never told to install a CLI you don't need:
| Situation | What you see |
|---|---|
| Host has no integration | "<Product>, read-only" with Open in browser |
| No remote at all | "This repository has no remote yet" with Review changes (opens Changes), Check again, and a git remote add origin <url> hint |
| CLI missing | "GitHub CLI (gh) isn't installed" with Install gh and Check again |
| CLI not signed in | "Sign in to GitHub" — Sign in copies gh auth login to your clipboard; Check again re-reads it |
| Repo unreachable | "Can't reach this repository" with Retry and the gh auth status command |
| Branch pushed, no request | Open a pull request and View commits |
| Nothing pushed | Commit and push (or Push branch when clean) and Changes |
A repository with no remote is a normal local state, not an unsupported host. While the pane shows the no-remote, CLI-missing, or signed-out state, it checks again whenever the window regains focus and every 5 seconds for 2 minutes — so after running gh auth login or git remote add in a terminal, it usually recovers on its own.
The same gating applies to the composer's + attach menu and @issue: / @pr: mentions — those rows are disabled with the matching reason rather than failing when you click them. See Attachments.
Limitations
- GitHub and GitLab only. Bitbucket and Azure DevOps are detected and named, but read-only with a browser link.
- No request-changes verdict on GitLab, and no line-level review on GitLab — the Code tab there is a readable diff with no composer.
- No deployments on GitLab.
- Sign-in is per instance. A self-hosted GitLab on a non-default port is a separate
glabentry fromgitlab.com; sign in to each one you use. - No in-app login. Authentication is entirely
gh auth login/glab auth loginin a terminal. - No per-check re-run and no label editing on the create form.
Related
- Reviewing & Merging — verdicts, line notes, threads, and the merge sheet
- The Pull Requests Page — triage across every repo you have open
- Changes — stage and commit before you open a request
- Right Panel — the deck the Review pane lives in