Try the following command, which I have tested:

$ cp -pv --parents $(git diff --name-only) DESTINATION-DIRECTORY
Answer from York on Stack Overflow
Top answer
1 of 2
2

Your question conceals a fundamental issue. Let's take a look at a typical commit graph fragment:

...--E--F--G--H--I--J  <-- master

You pick two commits, such as E and I, and run git diff --name-only (or git diff --name-status) to compare them:

$ git diff --name-only <hash-E> <hash-I>
myfolder/a.txt
code/snippet.c
test.txt

but then say:

... the result should be files introduced with the corresponding SHA.

The fact that these file names come out means that all three files are present in commits E and/or I, but if you had commit E in your work-tree and wanted to modify it to get commit I, those three files would need some kind of change: create, modify, or even delete. Using --name-status will give you the kind of change as well: A for "the file is to be newly created", M for "the file is to be modified", and D for "the file is to be deleted". (There are, of course, probably dozens or thousands of files in both E and I that are the same and hence are not printed here.)

But now you ask for the corresponding hash where this change is introduced. There may not be a the hash. There might be more than one. (There must be at least one of course). For instance, test.txt might be removed entirely in F by mistake (instead of being corrected), put back intact-but-wrong in H, and then corrected in I. Meanwhile code/snippet.c might be modified in both G and I.

Which commit hash(es) would you like for each file? The answer to that determines how to find them. (Of course, if there is only one such hash, the problem vanishes.)

xxfelixxx's answer gives a (slighlty faulty but easily fixed and improved) method for obtaining one commit—the first one git log prints. To fix the one bug and improve it a bit, replace the do sequence with:

do echo -n "$file "; git rev-list <starthash>..<endhash> -- "$file" | head -1

That is, we want to find one of the commit hashes printed by git rev-list when run only over the specified start/stop points and looking for changes to the one file. We need <starthash>..<endhash> to do the initial commit-limiting, and the -- $file to select only commits that add, modify, or remove that one path. Note that if there are spaces in the file name you will need to quote it (so I did), although then reading the output of git diff itself gets tricky too.

Using head -1 gets you the most recent commit that touched the file, e.g., with our example where code/snippet.c in both G and I, you get the hash for commit I. This is because Git works backwards, from newer commits to older ones. If you want the first one, use tail -1, and if you want all of them, you will need a fancier format. :-)

(There is another subtle difference here, between git log and git rev-list, involving merge commits, but that probably won't affect you.)

2 of 2
1

Not sure if there is an internal git command to do this, but you could always just loop over your files and grab the first commit:

for file in `git diff [start-sha] [end-sha] --name-only`; 
    do echo -n "$file "; git log --pretty=short [start-sha] [end-sha] -- $file | /bin/grep commit | cut -b8- | head -n 1;
done
🌐
Stack Overflow
stackoverflow.com › questions › 79802836 › optimized-git-diff-name-only
Optimized git diff --name-only - Stack Overflow
Then - `git diff --cached --name-only` will return ABC - `git diff --name-only "$1"..HEAD` will return ABC - This means `get_changed_file()` will return ABC, even thought technically the line change is 0.
🌐
Stack Overflow
stackoverflow.com › questions › 34434470 › git-diff-name-only-shows-unchanged-files
git diff --name-only shows unchanged files - Stack Overflow
git-diff supports relative paths at least since v2.5.2. To print out paths relative to current working directory: "git diff --relative --name-only". 2017-06-07T22:57:20.157Z+00:00 ...
🌐
Stack Overflow
stackoverflow.com › questions › 41010261 › add-line-break-to-git-diff-name-only
Add line break to git diff --name-only - Stack Overflow
October 30, 2019 - Explore Stack Internal ... name-only and saving the output to a file. but the changed file names are on same line. Can i put one changed file name in one line ... core.symlinks=false core.autocrlf=true core.fscache=true color.diff=auto color.status=auto color.branch=auto color.interactive=true help.format=html http.sslcainfo=C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt diff.astextplain.textconv=astextplain rebase.autosquash=true credential.helper=manager difftool.usebuiltin=true gui.recentrepo=C:/My Data/Frontend Development/my projects/ain gui.recentrepo=C:/NovCR alias.gl=git core.autocrlf=true core.repositoryformatversion=0 core.filemode=false core.bare=false core.logallrefupdates=true core.symlinks=false core.ignorecase=true svn-remote.svn.url=https://abcd.ko.com/svn/a/b svn-remote.svn.fetch=:refs/remotes/git-svn
Top answer
1 of 2
6

Besides the --name-flag that displays only the file name, the git diff command (without other arguments) will display the difference between your index and your working directory (as explained in the diagaram here).

Whereas the git status command will display the whole current status of your working tree (files that are staged, modified, deleted, untracked etc.)

However, by considering the -s flag that shorten the output of the status command (which typically display only names), we can have some situations where these two commands will give you "almost" the exact same output.. It all depends of what you already did in your working directory.

For example, if you do git status -s, you will get all the differences from your working directory against your HEAD and your index including untracked files.. (But say you don't have untracked files..)

On the other hands, and regardless of what you already staged or not, if you do git diff --name-only HEAD (see the above-mentioned diagram), there is a chance that you get almost the same output.

Also, the results are almost the same (not exactly the same) because git status -s shows pretty useful additional information:

  • a letter at the beginning of the line that summarize the status (M for modified, D for deleted, A for added, ? for untracked files)
  • a color code that shows what are already staged (green for staged, red for unstaged).

So, we can consider that diff is more suited to view differences in actual file contents. That is why not displaying untracked files by diff makes sense.. Comparing differences starting from only something we have (the tracked files)..

All that said, if you want some concise output about your actual changes (after all this is the rational for using --name-only) you should really consider git status -s.

2 of 2
2

On one hand, the git diff --name-only returns the name of the files which are ready to be staged on the next commit. As following :

git diff --name-only
/src/main/java/com/core/First.java 
/src/main/java/com/core/Second.java

On the other hand, git status provides you not only the details of the files to be staged in the current working repository, but also the comparison with the origin of your branch.

git status

On branch myBranch
Your branch is up-to-date with 'origin/myBranch'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   /src/main/java/com/core/First.java
        modified:   /src/main/java/com/core/Second.java

no changes added to commit (use "git add" and/or "git commit -a")
Find elsewhere
🌐
Stack Overflow
stackoverflow.com › questions › 28662475 › git-status-shows-between-more-git-diff-cached-name-only-and-git-diff
`git status` shows (between more) `git diff --cached --name-only` and `git diff --name-only`? - Stack Overflow
February 22, 2015 - Am I right if I state that git status shows (if we disregard untracked files) git diff --cached --name-only (with green color) and git diff --name-only (with red color) Is that true?
🌐
Git
git-scm.com › docs › git-diff
Git - git-diff Documentation
Just like --name-only the file names are often encoded in UTF-8. ... Specify how differences in submodules are shown. When specifying --submodule=short the short format is used. This format just shows the names of the commits at the beginning and end of the range.
🌐
Bassochette
bassochette.github.io › memo › 2016 › 12 › 21 › git-diff-show-only-file-names
git diff show only file names - coffee and 🚬
$ git diff --name-only · Stack overflow · ← Previous Archive Next → · 21 December 2016 · memo · git 5 ·
🌐
Explain Shell
explainshell.com › explain
explainshell.com - git diff --cached --name-only; git diff --name-only
git diff(1) distro · arch latest · ubuntu 26.04 --cached --name-only; git diff(1) distro · arch latest · ubuntu 26.04 --name-only ·