Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      May 13, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      May 13, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      May 13, 2025

      How To Prevent WordPress SQL Injection Attacks

      May 13, 2025

      This $4 Steam Deck game includes the most-played classics from my childhood — and it will save you paper

      May 13, 2025

      Microsoft shares rare look at radical Windows 11 Start menu designs it explored before settling on the least interesting one of the bunch

      May 13, 2025

      NVIDIA’s new GPU driver adds DOOM: The Dark Ages support and improves DLSS in Microsoft Flight Simulator 2024

      May 13, 2025

      How to install and use Ollama to run AI LLMs on your Windows 11 PC

      May 13, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Community News: Latest PECL Releases (05.13.2025)

      May 13, 2025
      Recent

      Community News: Latest PECL Releases (05.13.2025)

      May 13, 2025

      How We Use Epic Branches. Without Breaking Our Flow.

      May 13, 2025

      I think the ergonomics of generators is growing on me.

      May 13, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      This $4 Steam Deck game includes the most-played classics from my childhood — and it will save you paper

      May 13, 2025
      Recent

      This $4 Steam Deck game includes the most-played classics from my childhood — and it will save you paper

      May 13, 2025

      Microsoft shares rare look at radical Windows 11 Start menu designs it explored before settling on the least interesting one of the bunch

      May 13, 2025

      NVIDIA’s new GPU driver adds DOOM: The Dark Ages support and improves DLSS in Microsoft Flight Simulator 2024

      May 13, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»How the GitHub CLI can now enable triangular workflows

    How the GitHub CLI can now enable triangular workflows

    April 25, 2025

    Most developers are familiar with the standard Git workflow. You create a branch, make changes, and push those changes back to the same branch on the main repository. Git calls this a centralized workflow. It’s straightforward and works well for many projects.

    However, sometimes you might want to pull changes from a different branch directly into your feature branch to help you keep your branch updated without constantly needing to merge or rebase. However, you’ll still want to push local changes to your own branch. This is where triangular workflows come in.

    It’s possible that some of you have already used triangular workflows, even without knowing it. When you fork a repo, contribute to your fork, then open a pull request back to the original repo, you’re working in a triangular workflow. While this can work seamlessly on github.com, the process hasn’t always been seamless with the GitHub CLI.

    The GitHub CLI team has recently made improvements (released in v2.71.2) to better support these triangular workflows, ensuring that the gh pr commands work smoothly with your Git configurations. So, whether you’re working on a centralized workflow or a more complex triangular one, the GitHub CLI will be better equipped to handle your needs.

    If you’re already familiar with how Git handles triangular workflows, feel free to skip ahead to learn about how to use gh pr commands with triangular workflows. Otherwise, let’s get into the details of how Git and the GitHub CLI have historically differed, and how four-and-a-half years after it was first requested, we have finally unlocked managing pull requests using triangular workflows in the GitHub CLI.

    First, a lesson in Git fundamentals

    To provide a framework for what we set out to do, it’s important to first understand some Git basics. Git, at its core, is a way to store and catalog changes on a repository and communicate those changes between copies of that repository. This workflow typically looks like the diagram below:

    Figure 1: A typical git branch setup
    Figure 1: A typical git branch setup

    The building blocks of this diagram illustrate two important Git concepts you likely use every day, a ref and push/pull.

    Refs

    A ref is a reference to a repository and branch. It has two parts: the remote, usually a name like origin or upstream, and the branch. If the remote is the local repository, it is blank. So, in the example above, origin/branch in the purple box is a remote ref, referring to a branch named branch on the repository name origin, while branch in the green box is a local ref, referring to a branch named branch on the local machine.

    While working with GitHub, the remote ref is usually the repository you are hosting on GitHub. In the diagram above, you can consider the purple box GitHub and the green box your local machine.

    Pushing and pulling

    A push and a pull refer to the same action, but from two different perspectives. Whether you are pushing or pulling is determined by whether you are sending or receiving the changes. I can push a commit to your repo, or you can pull that commit from my repo, and the references to that action would be the same.

    To disambiguate this, we will refer to different refs as the headRef or baseRef, where the headRef is sending the changes (pushing them) and the baseRef is receiving the changes (pulling them).

    Figure 2: Disambiguating headRef and baseRef for push/pull operations.
    Figure 2: Disambiguating headRef and baseRef for push/pull operations

    When dealing with a branch, we’ll often refer to the headRef of its pull operations as its pullRef and the baseRef of its push operations as its pushRef. That’s because, in these instances, the working branch is the pull’s baseRef and the push’s headRef, so they’re already disambiguated.

    The @{push} revision syntax

    Turns out, Git has a handy built-in tool for referring to the pushRef for a branch: the @{push} revision syntax. You can usually determine a branch’s pushRef by running the following command:

    git rev-parse --abbrev-ref @{push}

    This will result in a human-readable ref, like origin/branch, if one can be determined.

    Pull Requests

    On GitHub, a pull request is a proposal to integrate changes from one ref to another. In particular, they act as a simple “pause” before performing the actual integration operation, often called a merge, when changes are being pushed from ref to another. This pause allows for humans (code reviews) and robots (GitHub Copilot reviews and GitHub Actions workflows) to check the code before the changes are integrated. The name pull request came from this language specifically: You are requesting that a ref pulls your changes into itself.

    Figure 3: Demonstrating how GitHub Pull Requests correspond to pushing and pulling.
    Figure 3: Demonstrating how GitHub Pull Requests correspond to pushing and pulling

    Common Git workflows

    Now that you understand the basics, let’s talk about the workflows we typically use with Git every day.

    A centralized workflow is how most folks interact with Git and GitHub. In this configuration, any given branch is pushing and pulling from a remote ref with the same branch name. For most of us, this type of configuration is set up by default when we clone a repo and push a branch. It is the situation shown in Figure 1.

    In contrast, a triangular workflow pushes to and pulls from different refs. A common use case for this configuration is to pull directly from a remote repository’s default branch into your local feature branch, eliminating the need to run commands like git rebase <default> or git merge <default> on your feature branch to ensure the branch you’re working on is always up to date with the default branch. However, when pushing changes, this configuration will typically push to a remote ref with the same branch name as the feature branch.

    Figure 4: juxtaposing centralized workflows from triangular workflows.
    Figure 4: juxtaposing centralized workflows from triangular workflows.

    We complete the triangle when considering pull requests: the headRef is the pushRef for the local ref and the baseRef is the pullRef for the local branch:

    Figure 5: a triangular workflow
    Figure 5: a triangular workflow

    We can go one step further and set up triangular workflows using different remotes as well. This most commonly occurs when you’re developing on a fork. In this situation, you usually give the fork and source remotes different names. I’ll use origin for the fork and upstream for the source, as these are common names used in these setups. This functions exactly the same as the triangular workflows above, but the remotes and branches on the pushRef and pullRef are different:

    Figure 6: juxtaposing triangular workflows and centralized workflows with different remotes such as with forks
    Figure 6: juxtaposing triangular workflows and centralized workflows with different remotes such as with forks

    Using a Git configuration file for triangular workflows

    There are two primary ways that you can set up a triangular workflow using the Git configuration – typically defined in a `.git/config` or `.gitconfig` file. Before explaining these, let’s take a look at what the relevant bits of a typical configuration look like in a repo’s `.git/config` file for a centralized workflow:

    [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
    

    Figure 7: A typical Git configuration setup found in .git/config

    The [remote “origin”] part is naming the Git repository located at github.com/OWNER/REPO.git to origin, so we can reference it elsewhere by that name. We can see that reference being used in the specific [branch] configurations for both the default and branch branches in their remote keys. This key, in conjunction with the branch name, typically makes up the branch’s pushRef: in this example, it is origin/branch.

    The remote and merge keys are combined to make up the branch’s pullRef: in this example, it is origin/branch.

    Setting up a triangular branch workflow

    The simplest way to assemble a triangular workflow is to set the branch’s merge key to a different branch name, like so:

    [branch “branch”]
        remote = origin
        merge = refs/heads/default
    

    Figure 8: a triangular branch’s Git configuration found in .git/config

    This will result in the branch pullRef as origin/default, but pushRef as origin/branch, as shown in Figure 9.

    Figure 9: A triangular branch workflow
    Figure 9: A triangular branch workflow

    Setting up a triangular fork workflow

    Working with triangular forks requires a bit more customization than triangular branches because we are dealing with multiple remotes. Thus, our remotes in the Git config will look different than the one shown previously in Figure 7:

    [remote “upstream”]
        url = https://github.com/ORIGINALOWNER/REPO.git 
        fetch = +refs/heads/*:refs/remotes/upstream/* 
    [remote “origin”]
        url = https://github.com/FORKOWNER/REPO.git  
        fetch = +refs/heads/*:refs/remotes/origin/*
    

    Figure 10: a Git configuration for a multi-remote Git setup found in .git/config

    Upstream and origin are the most common names used in this construction, so I’ve used them here, but they can be named anything you want1.

    However, toggling a branch’s remote key between upstream and origin won’t actually set up a triangular fork workflow—it will just set up a centralized workflow with either of those remotes, like the centralized workflow shown in Figure 6. Luckily, there are two common Git configuration options to change this behavior.

    Setting a branch’s pushremote

    A branch’s configuration has a key called pushremote that does exactly what the name suggests: configures the remote that the branch will push to. A triangular fork workflow config using pushremote may look like this:

    [branch “branch”]
        remote = upstream  
        merge = refs/heads/default  
        pushremote = origin
    

    Figure 11: a triangular fork’s Git config using pushremote found in .git/config

    This assembles the triangular fork repo we see in Figure 12. The pullRef is upstream/default, as determined by combining the remote and merge keys, while the pushRef is origin/branch, as determined by combining the pushremote key and the branch name.

    Figure 12: A triangular fork workflow
    Figure 12: A triangular fork workflow

    Setting a repo’s remote.pushDefault

    To configure all branches in a repository to have the same behavior as what you’re seeing in Figure 12, you can instead set the repository’s pushDefault. The config for this is below:

    [remote] 
        pushDefault = origin 
    [branch “branch”]
        remote = upstream 
        merge = refs/heads/default
    

    Figure 13: a triangular fork’s Git config using remote.pushDefault found in .git/config

    This assembles the same triangular fork repo as shown in Figure 12 above, however this time the pushRef is determined by combining the remote.pushDefault key and the branch name, resulting in origin/branch.

    When using the branch’s pushremote and the repo’s remote.pushDefault keys together, Git will preferentially resolve the branch’s configuration over the repo’s, so the remote set on pushremote supersedes the remote set on remote.pushDefault.

    Updating the gh pr command set to reflect Git

    Previously, the gh pr command set did not resolve pushRefs and pullRefs in the same way that Git does. This was due to technical design decisions that made this change both difficult and complex. Instead of discussing that complexity—a big enough topic for a whole article in itself—I’m going to focus here on what you can now do with the updated gh pr command set.

    If you set up triangular Git workflows in the manner described above, we will automatically resolve gh pr commands in accordance with your Git configuration.

    To be slightly more specific, when trying to resolve a pull request for a branch, the GitHub CLI will respect whatever @{push} resolves to first, if it resolves at all. Then it will fall back to respect a branch’s pushremote, and if that isn’t set, finally look for a repo’s remote.pushDefault config settings.

    What this means is that the CLI is assuming your branch’s pullRef is the pull request’s baseRef and the branch’s pushRef is the pull requests headRef. In other words, if you’ve configured git pull and git push to work, then gh pr commands should just work.2 The diagram below, a general version of Figure 5, demonstrates this nicely:

    Figure 14: the triangular workflow supported by the GitHub CLI with respect to a branch’s pullRef and pushRef. This is the generalized version of Figure 5
    Figure 14: the triangular workflow supported by the GitHub CLI with respect to a branch’s pullRef and pushRef. This is the generalized version of Figure 5

    Conclusion

    We’re constantly working to improve the GitHub CLI, and we’d like the behavior of the GitHub CLI to reasonably reflect the behavior of Git. This was a team effort—everyone contributed to understanding, reviewing, and testing the code to enable this enhanced gh pr command set functionality.

    It also couldn’t have happened without the support of our contributors, so we extend our thanks to them:

    • @Frederick888 for opening the original pull request
    • @benknoble for his support with pull request review and feedback
    • @phil-blain for highlighting the configurations we’ve talked about here on the original issue
    • @neutrinoceros and @rd-yan-farba for reporting a couple of bugs that the team fixed in v2.66.1
    • @pdunnavant for reporting the bug that we fixed in v2.71.1
    • @cs278 for reporting the bug that we fixed in v2.71.2.

    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


    1. Some commands in gh are opinionated about remote names and will resolve remotes in this order: upstream, github, origin, <other remotes unstably sorted>. There is a convenience command you can run to supersede this:* gh repo set-default [<repository>] to override the default behavior above and preferentially resolve <repository> as the default remote repo. ↩
    2. If you find a git configuration that doesn’t work, please open an issue in the OSS repo so we can fix it. ↩

    The post How the GitHub CLI can now enable triangular workflows appeared first on The GitHub Blog.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous Article159 CVEs Exploited in The Wild in Q1 2025, 8.3% of Vulnerabilities Exploited Within 1-Day
    Next Article Rilasciata ONLYOFFICE DocSpace 3.1: con circa 40 novità e miglioramenti in tutta la piattaforma

    Related Posts

    News & Updates

    This $4 Steam Deck game includes the most-played classics from my childhood — and it will save you paper

    May 13, 2025
    News & Updates

    Microsoft shares rare look at radical Windows 11 Start menu designs it explored before settling on the least interesting one of the bunch

    May 13, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    The Missing Circus Alien

    Artificial Intelligence

    LG and Will.i.am bring the boom with a new line of Bluetooth AI speakers at CES 2025

    News & Updates

    Effective ways of Exception Handling in Salesforce Apex

    Development

    Flight – fast, simple, extensible framework for PHP

    Linux

    Highlights

    API with NestJS #187. Rate limiting using Throttler

    March 16, 2025

    Comments Source: Read More 

    BlackLock ransomware: What you need to know

    March 20, 2025

    Honouring Republic Day at Perficient Hyderabad

    February 4, 2025

    UEFI Secure Boot: Not so secure?

    January 17, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.