How the GitHub CLI Enables Triangular Workflows for Advanced Git Collaboration
Tyler McGoffin discusses the GitHub CLI’s new support for triangular workflows, providing advanced collaboration capabilities for developers. Explore Git fundamentals, workflow configuration, and how the updated CLI simplifies pull request and branch management.
How the GitHub CLI Enables Triangular Workflows for Advanced Git Collaboration
By Tyler McGoffin
Most developers are familiar with the standard Git workflow, where a branch is created, changes are made, and those changes are pushed back to the same branch on the main repository—a process known as the centralized workflow. This method is straightforward and widely adopted across projects.
However, certain collaborative scenarios require pulling changes from different branches or repositories directly into your feature branch to stay updated. Simultaneously, you may want to push your changes to your own branch. This two-directional process is known as a triangular workflow.
What Are Triangular Workflows?
Triangular workflows typically occur when you fork a repository, work on your fork, and then open a pull request back to the original source repository. While this process works seamlessly on GitHub’s web interface, it previously required extra manual configuration in the GitHub CLI (gh
).
With the release of GitHub CLI v2.71.2, the CLI now natively supports triangular workflows, allowing gh pr
commands to work in conjunction with your Git configurations. This improvement streamlines the development experience for both centralized and complex fork-based workflows.
Git Fundamentals Overview
To understand how these workflows operate, it is helpful to review several core Git concepts:
- Ref (Reference): A pointer to a repository and branch, formed by a remote name (like
origin
orupstream
) and a branch name.origin/branch
refers to a branch on the remote namedorigin
. - Push and Pull: Terms that describe sending (
push
) or receiving (pull
) changes. In context, a headRef is the source remote/branch pushing changes, and a baseRef is the destination remote/branch pulling those changes. - Pull Requests: Proposals to integrate changes from one ref to another, acting as a pause for code or automated review before merging.
Illustration Examples
Several diagrams (see original post) showcase the flow between local, origin, and upstream branches, differentiating between typical and triangular workflows.
Common Git Workflow Patterns
- Centralized Workflow: The default for most users, pushing and pulling to/from the same remote/branch (e.g.,
origin/branch
) - Triangular Workflow: Pushing and pulling from different refs. A common scenario is updating your local feature branch with changes from the upstream default branch, while pushing changes to your fork.
Triangular Workflows and Forks
When working on a fork, remote names like origin
(fork) and upstream
(source project) are typically used. In this context:
- PushRef (push remote/ref): Where your changes are sent (commonly your fork).
- PullRef (pull remote/ref): Where you get updates from (commonly the upstream default branch).
Git Configuration for Triangular Workflows
Customizing Git config files (.git/config
or .gitconfig
) enables triangular setups. Key configurations include:
-
Basic Centralized Example
[remote "origin"] url = https://github.com/OWNER/REPO.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "default"] remote = origin merge = refs/heads/default [branch "branch"] remote = origin merge = refs/heads/branch
-
Triangular Branch Example
[branch "branch"] remote = origin merge = refs/heads/default
This setup pulls from
origin/default
but pushes toorigin/branch
. -
Triangular Fork Example (Multiple Remotes)
[remote "upstream"] url = https://github.com/ORIGINALOWNER/REPO.git [remote "origin"] url = https://github.com/FORKOWNER/REPO.git [branch "branch"] remote = upstream merge = refs/heads/default pushremote = origin
This configuration pulls from
upstream/default
and pushes toorigin/branch
. -
Repository-wide Setting with
remote.pushDefault
[remote] pushDefault = origin [branch "branch"] remote = upstream merge = refs/heads/default
All branches default pushing to
origin
by default.
GitHub CLI Enhancements
Previously, the GitHub CLI’s gh pr
commands did not resolve push and pull refs in the same way as Git. With the new update:
gh pr
now respects the branch’s pushRef (via@{push}
revision syntax), falling back topushremote
, thenremote.pushDefault
in the config.- This means
gh pr
commands will “just work” if your local Git push/pull commands work as desired.
Community Contributions
This improvement took years and significant community input. Shout-outs are given to users such as @Frederick888, @benknoble, @phil-blain, @neutrinoceros, @rd-yan-farba, @pdunnavant, and @cs278 for their contributions to bug reports, feedback, and reviews.
CLI native support for triangular workflows was 4.5 years in the making, and we’re proud to have been able to provide this update for the community.
The GitHub CLI Team—@andyfeller, @babakks, @bagtoad, @jtmcg, @mxie, @RyanHecht, and @williammartin
Pro Tips:
- The ordering of recognized remotes in the CLI (
upstream
,github
,origin
, then others) - Use
gh repo set-default [<repository>]
to override default remote resolution - For problems, open issues in the GitHub CLI open source repository
This post appeared first on “GitHub Engineering Blog”. Read the entire article here