Git diff --name-only 以及将该列表复制

161

我只想获取两个版本之间变更文件的列表,很简单:

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

如果我想将所有列出的文件复制到另一个地方,我应该写什么?同时,我需要完全相同的目录结构来复制这些文件。

例如,我已经修改和添加了文件:

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

我想要把所有更改和添加的文件放在/home/changes/目录下:

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

2
我不太明白你想要实现什么...是将更改传播到不同的克隆吗?创建补丁?在Git存储库之外修补文件? - knittl
不是补丁,而是更改文件的精确复制结构。类似于补丁,但具有实体文件。 - BazZy
是的,对我来说,在Git存储库之外修补文件是最接近的目标 :) - BazZy
3
抱歉,什么?你想要捕获并重放文件和树的更改?那就是一个补丁。git format-patch 可以为提交范围执行此操作。 - knittl
1
git diff commit1 commit2 > my.patch 然后 cd other/path; patch -p1 < my.patch。为什么必须使用文件的完整副本进行操作?如果是因为您认为补丁可能无法应用,因此另一个目录实际上不处于 commit1 状态,那么您真的应该从 commit2 状态复制 所有内容... - Cascabel
1
有趣的事实:我从 OP 处复制粘贴了 git diff -–name-only 命令并在我的计算机上运行,但它失败了。原来这两个破折号并不是真正的破折号: "-–name-only" .charCodeAt(0) 45 "-–name-only" .charCodeAt(1) 8211。 - qbolec
7个回答

127

尝试以下命令,我已经测试过:

$ cp -pv --parents $(git diff --name-only) DESTINATION-DIRECTORY

2
谢谢你提供“cp --parents”的提示,我多年来一直想要这个选项,但从未知道它的存在!显然,我也从未费心去寻找它 =\ - Stephan Henningsen
2
请注意,此方法在 Mac 上不可行,因为 cp--parents 选项不可用。 - M. Justin
如果文件包含非常规字符,例如空格或北欧字符,你有什么建议吗? - Halvor Holsten Strand
@HalvorStrand,我没有测试过,但首先设置IFS=$'\n'可能会奏效。(最好在子shell中执行以避免破坏原始IFS值。) - Wildcard
@Shiva 你确定你使用的是字符 ` 而不是字符 ' 吗? - York
显示剩余2条评论

18

下面的代码应该可以正常工作:

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

更进一步解释:

  • -z 参数用于 git diff --name-only 命令,表示输出文件列表时使用空字节(NUL)而不是换行符进行分隔,以防文件名中包含非常规字符。

  • -0 参数用于 xargs 命令,表示将标准输入解释为以空字节分隔的参数列表。

  • -IREPLACE 是必需的,因为默认情况下 xargs 会将参数追加到 rsync 命令的末尾。相反,这个选项表示将它们放在后面的 REPLACE 处。(这是来自Server Fault答案的一个好建议。)

  • -a 参数用于 rsync 命令,表示尽可能保留权限、所有权等信息。 -R 表示在目标位置创建文件时使用完整的相对路径。

更新:如果你使用的是旧版本的 xargs,你需要使用 -i 选项代替 -I。(后来的 findutils 版本中已弃用前者。)


这很奇怪 - 你使用的是什么操作系统?如果是Linux发行版,是哪一个?xargs --version显示什么? - Mark Longair
1
啊,在那个版本的xargs中,你需要使用-i,但这在后来的版本中已经被弃用了——4.1现在相当老了。我会更新我的答案。 - Mark Longair
@Mark:我记得在某个版本中,你需要在“-I”后面加一个空格,所以也许可以尝试使用“-I REPLACE”。 - Mikel
没关系。那也不行。"-I" 只支持 findutils 4.2.10 及以上版本,该版本于2004年12月发布。 - Mikel
你应该添加 --diff-filter=d 来排除已删除的文件。否则,在尝试将它们复制到更改文件夹时,你会遇到错误。不过,回答很好。谢谢! - taymless

14

这是一个一行代码:

列出更改的文件并将它们打包成 *.zip 文件:

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

列出最后提交过更改的文件并将它们打包成*.zip:

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

如果您正在使用Windows操作系统,您可以从https://sourceforge.net/projects/gnuwin32/files/zip/下载命令行Zip。 - Radon8472
@Radon8472 这会安装 sed.exe 吗?我在 Windows 上仍然得到 zip 未找到的错误。 - Shiva
不,该链接指向的是zip.exe的下载链接,而不是sed。 如果您无法在命令行中使用zip,则应将zip.exe所在的文件夹添加到您的“PATH”环境变量中。 - Radon8472

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

4

谢谢;我正在使用这段代码将修改后的文件复制到另一个目录。 - Deidrei

2

它工作得非常完美。

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

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

1
没有人提到 cpio,它容易打字,可以创建硬链接,并处理文件名中的空格:
git diff --name-only $from..$to  | cpio -pld outdir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接