11

I wish to create a .patch file based on the diff of an old stash that I can view with git stash show -p stash@{2}

I tried this, but no luck:

git format-patch stash@{2} --stdout > file.patch

I assumed it would work like a normal commit? Apologies for being most likely uber-thick.

Thanks!

Matt Fletcher
  • 8,182
  • 8
  • 41
  • 60

2 Answers2

19

The output of git stash show -p is itself a valid patch. You can use it directly:

git stash show -p stash@{2} > file.patch
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
1

Per the git format-patch documentation, if you specify a single commit,1 then:

  1. A single commit, since, specifies that the commits leading to the tip of the current branch that are not in the history that leads to the since to be output.

Thus, format-patch tries to find "commits leading to the tip of the current branch" (HEAD) that are neither themselves stash@{2} nor ancestors of stash@{2}. It's hard to say precisely which commits these will be without knowing the actual commit graph, but if the graph looks something like this:

... - o - o - * - *   <-- HEAD=branch
          |\
          i-w   <-- stash@{2}

then format-patch would make a patch containing the two commits marked *: they are the only ancestors of HEAD that are not removed by starting at the w commit and working backwards.

If the graph looks more like this:

              *   <-- HEAD=branch
            /
... - o - o - o - o   <-- anotherbranch
              |\
              i-w   <-- stash@{2}

then once again you would get the commit marked * (one commit this time, just to be a bit different).

(In fact, you get precisely the same commits as for stash@{2}..HEAD, since this gitrevisions syntax means what's in item 1 of the format-patch documentation).

One solution is to proceed to item 2 in the format-patch documentation:

  1. Generic revision range expression (see "SPECIFYING REVISIONS" section in gitrevisions(7)) means the commits in the specified range.

Here you need only specify the commits from "just before the w commit in the stash-bag" to "the w commit itself", which is simply stash@{2}^..stash@{2}:

git format-patch [additional options like --stdout here] stash@{2}^..stash@{2}

Since this is a single commit, the only difference between this and just using git show (as John Zwinck suggested) is the precise formatting of the patch (format-patch makes mailbox style patches by default).


Alternatively, you can always turn a stash into a "real branch" using git stash branch. This turns the index commit i of the stash into a real commit if needed, and restores the work directory state (and the untracked or all-files state as well, if one of those was included in the stash) after creating a new branch starting at the parent commit, i.e., the one the stash-bag is attached to. Commit the resulting work-tree and you have an ordinary branch, which you can manipulate with all the ordinary branch operations (including format-patch).


1See some of my other descriptions about git "stash bags" to see that a stash is a small clump of commits, but note that the name stash@{2} identifies a single commit, specifically the work-tree commit in the "stash bag".

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks for the info, but John actually answered the question directly so his is the one I've accepted! Cheers – Matt Fletcher Oct 02 '14 at 07:59
  • You might consider replicating the commands you need and explain why you might want a mailbox patch (or where it goes) to replicate John's answer - using your answer produces two blank patch files whereas @john-zwinck 's "just works". – smaudet Mar 23 '23 at 17:48