title: Git: How can I list the changed files in a commit, including the SHA-1 hash for each file (blob)?
body: <h1>What I Want, Summarized:</h1>
  
  <p>I have a commit, such as <code>HEAD</code> or <code>111abc111</code>, and I want an elegant way to print all of the modified files and <em>only</em> the modified files along with their SHA-1 hashes. How can I do that?</p>
  
  <p>Below is an idea using <code>git-cat-file</code> that <em>almost</em> works, but it either lists all files (including files that didn't change) or you must use it in batch mode. Using it in batch mode initially seems promising, but I cannot make it work. See below for things I have tried with <code>git-ls-tree</code> and so on.</p>
  
  <p>For a note about my priorities, see below in this question, or see the answer I wrote myself (which I am not going to accept, but maybe you can refactor it).</p>
  
  <h1>Concrete Example:</h1>
  
  <p><strong>Setting up the example:</strong></p>
  
  <p>For background, let's see what my Git working tree looks like:</p>
  
  <pre><code>$ ls
  
  alice.txt
  bob.c
  carol.h
  main.c
  
  $ git status -s
  
  # Nothing prints, the working copy is clean and untouched.
  </code></pre>
  
  <p>I will now change <em>only</em> two files:</p>
  
  <pre><code>$ echo "Add one line." &gt;&gt; bob.c
  
  $ echo "Add one line." &gt;&gt; carol.h
  
  $ git add .    # Add (stage) both changed files.
  
  $ git status -s
  
  M  bob.c
  M  carol.h
  
  $ git commit -m "Two changed files."
  
  [master 111abc111] Two changed files.
   2 files changed, 2 insertions(+), 0 deletions(-)
  </code></pre>
  
  <hr>
  
  <p><strong>This does ALMOST what I want:</strong></p>
  
  <pre><code>$ git cat-file -p 111abc111:./
  
  100644 blob 99c2e88ad312f1eac63afc908f64c370fac9d947    .gitignore
  100644 blob 607f8ea764981fb3f92a8d91abc2b154d99bc39c    alice.txt
  100755 blob 5a297bd6931c1a70abbcab919815324258c08b0f    bob.c
  100644 blob c6c2dfd18d26c1cf71b21e9d4c0892157dd6ec33    carol.h
  100755 blob d0802cd238a3e83f186bc5c24be7e23dfc69205f    main.c
  </code></pre>
  
  <p>The problem with the command above is that it lists everything at the specified path, which was <code>./</code> in this example, which is the current directory. It lists <strong>every file</strong>, not only the modified files. I only want it to show <code>bob.c</code> and <code>carol.h</code>.</p>
  
  <p>A second problem is that using <code>111abc111:./</code> to specify the tree-ish object will only show files (blobs) in that one directory, it will not show files in subdirectories. Subdirectories will show up like this:</p>
  
  <pre><code>040000 tree b98f38763b689e8197c6129726d41169fceeaaa0    subdir
  </code></pre>
  
  <p><strong>Possible Ideas:</strong></p>
  
  <p>I just deleted a few paragraphs with some things that I have tried.</p>
  
  <p>I suspect the key will be using <code>git-diff</code> to make a list of "git objects" (including blobs) that have changed in the specified commit, and then pass that list of "git objects" in some format to <code>git-cat-file</code>. So a magic command like this might work:</p>
  
  <pre><code>$ git diff 111abc111^ 111abc111 --magic-options-go-here | git cat-file --batch-check='%(objectname) %(objectsize)'
  </code></pre>
  
  <p>The key is to find the value for <code>--magic-options-go-here</code>. I am also not convinced <code>git-cat-file</code> is what I want on the right side of the pipe character, I might want something else.</p>
  
  <p>Thank you.</p>
  
  <h2>Edit: My Priorities</h2>
  
  <p>I am more interested in "git objects", the entities stored within Git and identified by SHA-1 hashes, such as trees, blobs, commits, and maybe other things I have not thought of such as tags. I am far less interested in filenames and how things actually look in the filesystem if you do a checkout of the commit.</p>
  
  <p>I want to see the SHA-1 hashes so that I can see "oh, this merge commit is pointing to the tree from <em>way over there</em> in a different branch." With branching and merging and rebasing in a large repository, each commit object contains a huge number of trees and blobs that have not changed and are just pointers (references), and the things they refer to may get very far away in a conceptual sense. This can become apparent when you change just one line, do a git-commit, then do a git-push and the amount of data pushed is 50 MiB. Internally, Git just had to dereference a ton of pointers and make new deltas and packfiles and stuff. Things that feel like small changes in the working directory (the filesystem) may actually represent a lot of data in the Git repository binary format.</p>