Skip to content

Instantly share code, notes, and snippets.

@chrismo
Last active April 9, 2021 18:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chrismo/505b55aca2991cbf36ea to your computer and use it in GitHub Desktop.
Save chrismo/505b55aca2991cbf36ea to your computer and use it in GitHub Desktop.
Git: Checkout PR

Greg Vaughn posted this cool alias the other day:

copr = "!f() { git fetch -fu origin refs/pull/$1/head:pr-$1; git checkout pr-$1; } ; f"

Preferring to be a stock-tools person, I wanted to deconstruct this to see how I'd use it off-the-shelf without the alias.

git fetch     -- I'm already familiar with this command
-fu           -- these two flags I'm not sure are necessary, esp. -u since the help says, 
                 "unless you are implementing your own Porcelain you are not supposed to use 
                 it." - and since I don't know what Porcelain is, I presume I'm in the general camp. 
origin        -- I also know this is the repo (the remote name from `git remote`)

Now to some new stuff for my brain:

refs/pull/$1/head:pr-$1

I know the $1 is just a variable for the PR number. This whole thing is the refspec according to git help fetch:

   <refspec>
       The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>, 
       followed by a colon :, followed by the destination ref <dst>.

       The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref 
       that matches it is fast-forwarded using <src>. If the optional plus + is used, the local ref is 
       updated even if it does not result in a fast-forward update.

Since I have a git completion script installed, I realize I can see the list of available refspecs with:

git fetch origin [<tab><tab>]

[chrismo@momac foo (master)]$ git fetch origin
HEAD:HEAD                                         refs/pull/2/merge:refs/pull/2/merge
master:master                                     refs/pull/3/head:refs/pull/3/head
refs/pull/2/head:refs/pull/2/head                 refs/pull/3/merge:refs/pull/3/merge

These can be explicitly listed (without auto-completion magic) with git ls-remote origin

[chrismo@momac foo (master)]$ git ls-remote origin
f2720fe21c9e6b92ede4f9f99598f60d3251b0b1	HEAD
f2720fe21c9e6b92ede4f9f99598f60d3251b0b1	refs/heads/master
594fe52884ca771c26a70a45402f9cfedf58c3ae	refs/pull/2/head
3dd41b7ac6db8160e61ee89d920894ab20ea0b28	refs/pull/2/merge
00d5916224d47fa99cf76ceb5e2b81ca76cd1f62	refs/pull/3/head
07f43e1ce5ae8abc3afb2d27b5a845de67819029	refs/pull/3/merge

Back to the refspec help from the fetch command, I see that refs/pull/2/head is the src and whatever is on the right side of the colon is the dst. So ...

git fetch origin refs/pull/2/head:pr-2

... simply means, fetch the contents of pull request 2 and put it into a new local branch called pr-2 -- the destination local branch could be named whatever I want.

@gvaughn
Copy link

gvaughn commented Jul 16, 2014

What the -fu does is allows you to re-run "copr" even if the submitter has forced pushed to their PR. And, yeah, this is sorta Porcelain-ish.

@chrismo
Copy link
Author

chrismo commented Jul 16, 2014

Is Porcelain a technical term in git land? Or a conceptual thing?

@gvaughn
Copy link

gvaughn commented Jul 16, 2014

My understanding is that there's a low-level of git "plumbing" commands. The higher level ones that users actually interact with most of the time are "porcelain"

@chrismo
Copy link
Author

chrismo commented Jul 16, 2014

@gvaughn - I wonder if the + in the refspec does the same thing? "If the optional plus + is used, the local ref is updated even if it does not result in a fast-forward update." e.g. +refs/pull/2/head:pr-2

@gvaughn
Copy link

gvaughn commented Jul 16, 2014

Thanks to your thorough analysis, I re-read things and wondered that too. I haven't had time to test it out though.

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