Paul
Paul

Reputation: 4450

How to keep a PR up to date in a project with continuous commits

There is this repository on GitHub, let's call it X.

I created a fork of this repository, called FORK_X, also on GitHub.
Then, I cloned FORK_X to my local machine and added a remote pointing to the original repository (X).

So, in my local clone, my remotes are configured as follows:

origin          [email protected]:ME/FORK_X.git (fetch)
origin          [email protected]:ME/FORK_X.git (push)
upstream        [email protected]:THEM/X.git (fetch)
upstream        [email protected]:THEM/X.git (push)

My current workflow:

When I need to create a pull request (PR):

  1. I make sure my fork's main branch is up to date with upstream/main.
  2. I create a new branch from main.
  3. I make my changes and push them to my fork.
  4. I open a PR to X.

The problem:

In the X repository, multiple PRs from other contributors are frequently opened and updated.
I need an efficient way to test and keep a specific PR from X up to date, without manually repeating commands every time the author pushes new commits.

Currently, I use the following workflow to test a PR:

git remote set-url upstream [email protected]:THEM/X.git
git fetch upstream pull/ID_PR/head:NAME
git checkout NAME

However, whenever new commits are pushed to the PR, I have to manually repeat these steps, deleting and recreating the local branch.

Is there a way to easily test a PR while the author continuously commits without manually running fetch and checkout each time?

Upvotes: -3

Views: 75

Answers (1)

Schwern
Schwern

Reputation: 165396

It should not be necessary to create any intermediate branches. Let's say you've branched from main...

  1. Fetch the latest upstream changes to your local repository.
    • git fetch upstream
  2. Update your local PR branch from its now updated upstream base.
    • git switch my-pr
    • git rebase upstream/main
    • Fix conflicts
    • Run local tests.
  3. Push your updated PR branch to your origin.
    • git push --force-with-lease.

You could write a script to do this, but it's four trivial commands; I personally don't bother.


Now let's do that with illustrations.

Let's say you branched from upstream's main and have pushed your changes. Since then some new commits have been made upstream (D and E). Your repository and the upstream repository look like this.

upstream
A - B - C - D - E [main]

origin
A - B - C
         \
          1 - 2 - 3 [my-pr]

local
A - B - C [upstream/main]
         \
          1 - 2 - 3 [my-pr] [origin/my-pr]

(Note: I did not include a local main nor origin main. I don't ever commit to main in a fork, all work is done in branches and PRs, so main, origin/main, and upstream/main would all point at the same commit. It's simpler to not have main nor origin/main and use upstream/main exclusively. Less to keep up-to-date, less to go wrong.)

Your fork is out is out of date, so it doesn't have D and E yet. We first need to get those locally.

  1. Fetch the latest upstream changes to your local repository.
$ git fetch upstream

upstream
A - B - C - D - E [main]

origin
A - B - C
         \
          1 - 2 - 3 [my-pr]

local
A - B - C - D - E [upstream/main]
         \
          1 - 2 - 3 [my-pr] [origin/my-pr]

Fetching updated your local upstream/main. It does not touch your local branches. Now we update your local PR branch by rebasing it onto upstream/main.

  1. Update your local PR branch from its now updated upstream base.
$ git switch my-pr
$ git rebase upstream/main

upstream
A - B - C - D - E [main]

origin
A - B - C
         \
          1 - 2 - 3 [my-pr]

local
A - B - C - D - E [upstream/main]
         \       \
          \       1A - 2A - 3A [my-pr]
           \
            1 - 2 - 3 [origin/my-pr]

You can also merge if you like, the procedure is the same. I prefer rebasing for updates, it avoids cluttering up the history with merges that are only about updating the branch.

Fix any conflicts, run your local tests. Once you're satisfied, push your new branch.

  1. Push your updated PR branch to your origin.
$ git push --force-with-lease

upstream
A - B - C - D - E [main]

origin
A - B - C - D - E
                 \
                  1A - 2A - 3A [my-pr]

local
A - B - C - D - E [upstream/main]
         \       \
          \       1A - 2A - 3A [my-pr] [origin/my-pr]
           \
            1 - 2 - 3 

Your old local commits will be garbage collected in a few weeks.

Because the rebase changed the commit IDs in your branch it's necessary to use some sort of --force. --force-with-lease is the safer option.

Upvotes: 2

Related Questions