Skip to content

Instantly share code, notes, and snippets.

@ferdnyc
Last active September 27, 2023 21:13
Show Gist options
  • Save ferdnyc/79210523a50ef16ec6bdcb7beacce5b9 to your computer and use it in GitHub Desktop.
Save ferdnyc/79210523a50ef16ec6bdcb7beacce5b9 to your computer and use it in GitHub Desktop.
My GitHub branch-management workflow

GitHub branch management for upstream remote projects using git

My previous GitHub branch-management process, first attempted with OpenShot/openshot-qt, and used on all projects until I came to appreciate the hub method (described below). Note that what's described here is also what the gh command will set up automatically, if the user first clones an upstream and then runs gh repo fork in that directory.

  1. After creating my fork in the web interface

    $ git clone <my fork SSH identifier>; cd $PROJECT
    $ git remote add upstream <upstream HTTPS URL>
    $ git fetch upstream
    From https://github.com/...
    $ # $DEFAULT is the project default branch name, usually 'master' but sometimes 'develop' or whatever
    $ git branch -u upstream/$DEFAULT $DEFAULT
    Branch '$DEFAULT' set up to track remote branch '$DEFAULT' from 'upstream'.

    So now, the default branch tracks the upstream. I never touch that branch in my own fork, in fact I don't even have a local copy of it. I just allow it to fall wildly out of date, since it never updates past the point where I forked the upstream project.

  2. When I want to write code for a PR...

    $ git checkout $DEFAULT
    $ git pull # Sync with upstream/$DEFAULT
    $ # Create a new topic branch based on local $DEFAULT, which tracks upstream/$DEFAULT
    $ git checkout -b newtopic $DEFAULT
    $ git push -u origin newtopic # I do this immediately, before writing any code, to configure it
    Total 0 (delta 0), reused 0 (delta 0)
    remote: 
    remote: Create a pull request for 'newtopic' on GitHub by visiting:
    remote:      https://github.com/ferdnyc/$PROJECT/pull/new/newtopic
    remote: 
    To github.com:ferdnyc/$PROJECT.git
     * [new branch]      newtopic -> newtopic
    Branch 'newtopic' set up to track remote branch 'newtopic' from 'origin'.
  3. Then after committing any changes, I can just...

    $ git push
    remote: Resolving deltas: ...
    To github.com:ferdnyc/$PROJECT.git
       $GITREFA..$GITREFB  newtopic -> newtopic
  4. After I finish development on newtopic...

    $ git checkout $DEFAULT # Return to upstream/$DEFAULT as my base branch
    $ git pull # Sync with upstream/$DEFAULT
  5. And if I need to return to newtopic...

    $ git checkout newtopic
    $ git merge $DEFAULT   # This can cause conflicts

    If there's already a PR, it can be easier/cleaner to do this on the web and pull. Alternatively, this form may help to lessen the conflicts encountered:

    $ git checkout newtopic
    $ git merge --rebase $DEFAULT

    A force-push will then be required to update the PR branch.

GitHub branch management for forked projects using hub

As an alternative, a different model is possible using the older hub command-line client from GitHub. (hub has now been replaced by gh, which unfortunately does not work this way. It operates on a model more like what's described above, with upstream being the parent repo name.)

I've come to appreciate, and even prefer, the hub style. Mostly for how it centers the parent project (making it the origin, and the default remote for the local repo).

When working as a contributor to another repo, that makes sense to me. The upstream is "the repo", and remains the origin. Whereas my fork is rightly treated as secondary, since it's little more than a staging area for PRs to be opened at origin. If any PR branches from other contributors are checked out (see Using hub for pull-request management, below), those submitters' forks will be added as remotes using their username. Which means that all forks — my own very much included — are handled exactly the same.

  1. hub assumes you've checked out the parent repo first (with or without hub), and don't yet have your own fork (or haven't configured it locally).

    $ git clone <upstream HTTPS identifier>
    $ # OR
    $ hub clone $GHUSER/$PROJECT
    Cloning into '$PROJECT'...
    $ cd $PROJECT

    Done this way, origin is already the upstream (there's no separate upstream remote), so the default branch already tracks origin/$DEFAULT. There's no need to reconfigure the local default branch.

  2. You then use hub fork to create and check out your own fork.

    $ hub fork
    Updating ferdnyc
    From git://github.com/$GHUSER/$PROJECT
     * [new branch]      $DEFAULT             -> ferdnyc/$DEFAULT
    new remote: ferdnyc

    In the hub model, your fork's remote name is your username. Other forks by other users can also be checked out, and will be named along the same pattern.

  3. Everything else is largely the same. You'd use git push -u $USERNAME newtopic to configure a topic branch to track your fork.

Using hub for pull-request management

Whether you use this repo-management model or my own, hub still has several other subcommands that can be very useful when contributing to projects. By far the most useful of them is hub pr, the command to "List or checkout GitHub pull requests".

If you want to examine the code for an UN-merged PR, for instance to compile or run the code in the state it will be after merge, simply use hub pr checkout $PR_NUM to check out the PR's branch in the submitter's fork.

$ hub pr checkout $PR_NUM
remote: Enumerating objects: 2, done.
remote: Counting objects: 100% (2/2), done.
remote: Total 3 (delta 2), reused 2 (delta 2), pack-reused 1
Unpacking objects: 100% (3/3), done.
From git://github.com/$GHUSER/$PROJECT
 * [new ref]         refs/pull/$PR_NUM/head -> $SUBMITTER-$PR_BRANCH
Switched to branch '$SUBMITTER-$PR_BRANCH'

A new remote-tracking branch is created to track the submitter's fork, and that branch is then automatically checked out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment