[git] Git diff --name-only and copy that list

I just want to get a list of changed files between two revisions, which is simple:

git diff -–name-only commit1 commit2 > /path/to/my/file

But, what should I write, if I want copy all that listed files to another place? And I need completely identical directory structure for copied files.

For example, I have modified and added files:

/protected/texts/file1.txt
/protected/scripts/index.php
/public/pics/pic1.png

I want to have in /home/changes/ all those changed and added files:

/home/changes/protected/texts/file1.txt
/home/changes/protected/scripts/index.php
/home/changes/public/pics/pic1.png

This question is related to git

The answer is


Here's a one-liner:

List changed files & pack them as *.zip:

git diff --name-only | zip patched.zip -@

List last committed changed files & pack them as *.zip:

git diff --name-only HEAD~ HEAD | zip patched.zip -@

The following should work fine:

git diff -z --name-only commit1 commit2 | xargs -0 -IREPLACE rsync -aR REPLACE /home/changes/protected/

To explain further:

  • The -z to with git diff --name-only means to output the list of files separated with NUL bytes instead of newlines, just in case your filenames have unusual characters in them.

  • The -0 to xargs says to interpret standard input as a NUL-separated list of parameters.

  • The -IREPLACE is needed since by default xargs would append the parameters to the end of the rsync command. Instead, that says to put them where the later REPLACE is. (That's a nice tip from this Server Fault answer.)

  • The -a parameter to rsync means to preserve permissions, ownership, etc. if possible. The -R means to use the full relative path when creating the files in the destination.

Update: if you have an old version of xargs, you'll need to use the -i option instead of -I. (The former is deprecated in later versions of findutils.)


#!/bin/bash
# Target directory
TARGET=/target/directory/here

for i in $(git diff --name-only)
    do
        # First create the target directory, if it doesn't exist.
        mkdir -p "$TARGET/$(dirname $i)"
        # Then copy over the file.
        cp -rf "$i" "$TARGET/$i"
    done

https://stackoverflow.com/users/79061/sebastian-paaske-t%c3%b8rholm


It works perfectly.

git diff 1526043 82a4f7d --name-only | xargs zip update.zip

git diff 1526043 82a4f7d --name-only |xargs -n 10 zip update.zip

No-one has mentioned cpio which is easy to type, creates hard links and handles spaces in filenames:

git diff --name-only $from..$to  | cpio -pld outdir

zip update.zip $(git diff --name-only commit commit)