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 ·
Top answer
1 of 3
35

It's an "imaginary diff option", used to indicate to the reader that it's not just the output of running the diff command. For example, in git's own git repo:

$ git diff HEAD~1..HEAD | head
diff --git Documentation/git.txt Documentation/git.txt
index bd659c4..7913fc2 100644
--- Documentation/git.txt
+++ Documentation/git.txt
@@ -43,6 +43,11 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
+* link:v2.10.0/git.html[documentation for release 2.10]
+
$ 

The diff command itself, if you invoked it with the same file name twice, would show no differences. git presumably creates temporary files corresponding to two different versions of Documentation/git.txt and feeds them to diff -- but the names of those temporary files would not be useful. I think git diff massages the output of diff to make it more meaningful to the reader. (This speculation was not entirely correct. See below.)

Diving into the git source code, diff.c has the string "diff --git" hardwired as a string literal:

strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);

And looking into the history of diff.c for the earliest version that contains that string:

$ git log -n 1 b58f23b3
commit b58f23b38a9a9f28d751311353819d3cdf6a86da
Author: Junio C Hamano <[email protected]>
Date:   2005-05-18 09:10:47 -0700

    [PATCH] Fix diff output take #4.
    
    This implements the output format suggested by Linus in
    <[email protected]>, except the
    imaginary diff option is spelled "diff --git" with double dashes as
    suggested by Matthias Urlichs.
    
    Signed-off-by: Junio C Hamano <[email protected]>
    Signed-off-by: Linus Torvalds <[email protected]>
$ 

Presumably <Pine.LNX...> is the message-id of some email message in a mailing list somewhere. In any case, this commit message makes it clear that diff --git is an "imaginary diff option".

This email message, cited by nos in a comment, appears to be part of the discussion that led to this. Update: That link is now dead, and I don't know whether the archive is available elsewhere.

UPDATE: I speculated above that git diff massages the output of diff, adding this information. I just tried running git diff under strace. It doesn't actually invoke the diff command, or any other command. Rather, all the output is printed by the git process itself, and apparently it computes the differences internally. This behavior might also depend on the version of git (I'm using 2.23.0), the diff command(s) available, and the arguments used.

I'll note that GNU diff has a --label=LABEL option that could be used for this kind of thing:

'-L LABEL'
'--label=LABEL'
     Use LABEL instead of the file name in the context format (*note
     Context Format::) and unified format (*note Unified Format::)
     headers.  *Note RCS::.

but git diff doesn't use it, at least in the one case I tried, and I don't see a reference to it in the git sources.

2 of 3
3

The --git is to mean that diff is in the "git" diff format. It doesn't refers to and option of the /usr/bin/diff command You can find the list of diff format documentation. Other formats are:

  • diff --combined
  • diff --cc
  • diff --summary
🌐
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 ·