Triangular workflows extend the classic centralized Git model by allowing a branch to pull from one remote reference while pushing to another. This arrangement keeps feature branches up‑to‑date with upstream defaults without repetitive merges, and it aligns naturally with fork‑based contribution patterns used on platforms like GitHub.
Fundamental Git Concepts for Triangular Workflows
Understanding refs, push and pull operations is essential. A ref combines a remote name (e.g., origin or upstream) with a branch name. The headRef denotes the source of changes (push side) while the baseRef denotes the destination (pull side). Git supplies the @{push} syntax to reveal a branch's configured push reference, enabling scripts to discover where to send updates automatically.
Centralized versus Triangular Configuration
In a centralized workflow, the same remote‑branch pair serves for both pulling and pushing, simplifying setup but requiring manual rebases or merges to stay current with the upstream default branch. A triangular setup decouples these actions the feature branch pulls directly from the upstream default (e.g., upstream/main) while pushing to its own fork (e.g., origin/feature), reducing maintenance overhead.
Configuring a Triangular Workflow
Modify .git/config to assign distinct remote and merge entries for each branch. For a feature branch my‑feature, set remote = origin and merge = refs/heads/my‑feature to define the pushRef, and add a pullRef entry such as upstream/main via the branch.my‑feature.remote and branch.my‑feature.merge keys. The resulting configuration creates a clear triangle between local, fork, and upstream repositories.
Using the GitHub CLI with Triangular Workflows
Version 2.71.2 of the GitHub CLI adds native support for triangular patterns. Commands like gh pr create automatically infer the correct headRef (the pushRef) and baseRef (the pullRef), even when they belong to different remotes. This eliminates manual flag specifications and ensures pull‑request metadata matches the underlying Git configuration.
Practical Example with Git
After cloning a fork, run git config branch.my‑feature.remote origin and git config branch.my‑feature.merge refs/heads/my‑feature. Then set the upstream pull reference with git config branch.my‑feature.pullRemote upstream and git config branch.my‑feature.pullMerge refs/heads/main. With this in place, git pull updates the feature branch from upstream/main, while git push sends commits to origin/my‑feature. Finally, invoking gh pr create opens a pull request from origin/my‑feature into upstream/main without extra arguments.