17

I would like to avoid calling porcelain commands from my scripts, but is there a way to get some of the behavior of git checkout <commit> using only plumbing commands like checkout-index? I'm particularly interested in the effect on the working copy: assuming everything is clean, checkout deletes files that were tracked in the old HEAD and absent in the new one. checkout-index doesn't seem to have any concept of deleting files. The closest thing I can think of would be to call

git diff-tree -p <old> <new> | git apply

but computing the whole diff seems unnecessarily expensive. Is there a better way?

Jack O'Connor
  • 10,068
  • 4
  • 48
  • 53
  • 3
    Any reason why you don't just call `git checkout ...`? – Oliver Charlesworth Jul 23 '14 at 21:47
  • Yeah, don't make your life unnecessarily hard. – ThiefMaster Jul 23 '14 at 21:56
  • 1
    Ultimately I want to have more control over how the checkout happens. For example, `checkout` can blow away ignored files, and I want to avoid doing that (increased safety). But I do want pave over deleted files (decreased safety), without using `--force` to pave over everything. This is part of a script that's using git under the covers to store file trees, and `checkout` is very close to how I want my script to behave, but not quite. I figured if I could get `checkout` with plumbing commands I could tweak it from there, though please let me know if there's another way to get this control. – Jack O'Connor Jul 23 '14 at 22:07
  • 1
    Have you actually tested that `diff-tree` is slow? `checkout` basically diffs everything to figure out what to do in the first place. (Of course, `checkout` doesn't create patches and then apply them, and probably you shouldn't either.) – Edward Thomson Jul 23 '14 at 23:52
  • @EdwardThomson `diff-tree` is probably what I want, but I'm not sure what to pipe its output into. Is there a way to apply this diff to the working copy besides the `git apply` hack above? It would be easy enough to write a Python script to take the list of deletions and actually perform them, but I worry that would be slow. – Jack O'Connor Jul 24 '14 at 07:57

1 Answers1

12

You're looking for the two-tree git read-tree -um. It uses a base tree, (generally you feed it HEAD), a target tree, and (implicitly) the index and worktree. The table describing its behavior was hard for me to understand so I have my own cheatsheet for it, a reformatted one that makes more sense to me, anyway. At any rate, it implements git checkout.

git read-tree -um H M  # `I` is the (implicit) index, GIT_INDEX_FILE

                    Legend

        H       Original tree (usually HEAD:)
        I       Indexed tree
        M       Merge target tree

        H->I     \
        H->M      } status in second relative to first
        I->M     /

        "-"     file exists in neither
       new      exists only in second
       deleted  exists only in first
       same     exists in both, unchanged
       changed  exists in both, different
      (blank)   irrelevant or all cases not otherwise given

        keep    keep current version
        fail    whole command fails, no changes
       delete   delete worktree file


      H->I      H->M      I->M

                          same     keep

     deleted   changed             fail
     deleted   deleted             delete
     deleted    same               delete unless Index empty, else use M
                same               keep

      same     changed             worktree clean: use M; dirty: fail
      same     deleted             worktree clean: deleted; dirty: fail

      new        -                 keep
      new       new      changed   fail
     changed   changed   changed   fail
     changed   deleted             fail


note: "index empty" identifies an initial checkout, where HEAD has been
set but never loaded.  git can't currently distinguish between a
delete-everything index and an initial-checkout index.
jthill
  • 55,082
  • 5
  • 77
  • 137
  • +1. I like the cheatsheet. I have completed my own answer with an example using `git read-tree -um`. – VonC Jul 28 '14 at 05:59
  • Thank you! I never would've guessed. – Jack O'Connor Jul 29 '14 at 16:07
  • You're welcome. I wouldn't have guessed it either, it's not always easy to see what you can get git to do. I know I didn't really get this command until I got mad at the table in the doc and spent the time to make my own. – jthill Jul 29 '14 at 16:43