ChatGPT解决这个技术问题 Extra ChatGPT

rsync 排除根据 .gitignore & .hgignore & svn:ignore like --filter=:C

Rsync 包含一个漂亮的选项 --cvs-exclude 以“以与 CVS 相同的方式忽略文件”,但 CVS 已过时多年。有没有办法让它也排除现代版本控制系统(Git、Mercurial、Subversion)会忽略的文件?

例如,我有很多从 GitHub 签出的 Maven 项目。通常,它们包括一个 .gitignore,至少列出 target,即默认的 Maven 构建目录(可能存在于顶层或子模块中)。由于这些目录的内容完全是一次性的,而且它们可能比源代码大得多,所以我想在使用 rsync 进行备份时将它们排除在外。

当然,我可以明确地 --exclude=target/ 但这会意外地抑制恰好被命名为 target 并且不应该被忽略的不相关目录。

我可以为磁盘上任何 .gitignore.hgignoresvn:ignore 属性中提到的所有文件名和模式提供绝对路径的完整列表,但这将是一个巨大的列表,必须由某种脚本。

由于 rsync 除了 CVS 之外没有对 VCS 检出的内置支持,有没有什么好的技巧可以给它提供忽略模式?或者某种回调系统,可以询问用户脚本是否应该包含给定的文件/目录?

更新:LordJavac 建议的 --filter=':- .gitignore' 似乎对 Git 和 --filter=:C 对 CVS 一样有效,至少在我找到的示例中,但不清楚语法是否准确匹配。 --filter=':- .hgignore' 不适用于 Mercurial;例如,包含类似 ^target$ 的行(Git /target/ 的 Mercurial 等效项)的 .hgignore 不会被 rsync 识别为正则表达式。 Subversion 似乎没有任何效果,您必须解析 .svn/dir-prop-base 以获得 1.6 或更早的工作副本,并沮丧地举起手来获得 1.7 或更高版本的工作副本。

听起来有点像为 rsync 提交一个补丁来增加对 .gitignore、.hgignore 等的支持是个好主意。
@ThiefMaster:我将 bugzilla.samba.org/show_bug.cgi?id=9744 作为起点。
只是给其他人的注释,.gitignore 需要位于被 rysnc'd 的文件夹层次结构中,而不是在执行命令的目录中
:- 究竟是什么意思?冒号是什么意思?什么破折号?
@David : 代表 dir-merge(如果文件夹树中有 .gitignore 个文件很有用),- 代表 exclude(过滤器也可以包含)。

R
Ram

正如 luksan 所提到的,您可以通过将 --filter 切换到 rsync 来执行此操作。我通过 --filter=':- .gitignore'(“.gitignore”之前有一个空格)实现了这一点,它告诉 rsync.gitignore 文件进行目录合并,并根据 git 的规则将它们排除在外。如果有的话,您可能还想添加全局忽略文件。为了使其更易于使用,我为 rsync 创建了一个别名,其中包含过滤器。


一个更详细的版本,它也排除了 .git 文件:--exclude='/.git' --filter="dir-merge,- .gitignore"
我现在有这样的东西:rsync -rvv --exclude='.git*' --exclude='/rsync-to-dev.sh' --filter='dir-merge,-n /.gitignore' $DIR/ development.foobar.com:~/test/ .. 但虽然它说 [sender] hiding file .gitignore because of pattern .git*,文件仍然被发送到目的地
如果您还想使用 --delete 选项,这里是工作命令行:rsync --delete-after --filter=":e- .gitignore" --filter "- .git/" -v -a ...。这花了我一段时间...过滤器中的 e--delete-after 都很重要。我建议阅读 rsync 手册页的“PER-DIRECTORY RULES AND DELETE”一章。
同步删除以及添加 &更新,您可以简单地将 --delete-after 添加到 @VasiliNovikov 的命令版本。 (这似乎等同于@dboliton 的命令版本,除了@db 使用 :e 我认为它排除了 .gitignore 文件被复制,这不是我想要的。)
这是否假定从包含 .gitignore 的目录运行 rsync?或者它是否从 dir syncs 中提取 ot?我想我必须输入 .gitignore 的完整路径才能保存?
J
Jared Deckard

您可以使用 git ls-files 构建存储库的 .gitignore 文件排除的文件列表。 https://git-scm.com/docs/git-ls-files

选项:

--exclude-standard 考虑所有 .gitignore 文件。

-o 不要忽略未暂存的更改。

-i 只输出被忽略的文件。

--directory 如果忽略整个目录,则仅输出目录路径。

我唯一要忽略的是.git

rsync -azP --exclude=.git --exclude=`git -C <SRC> ls-files --exclude-standard -oi --directory` <SRC> <DEST>

这不起作用。它从 git 子命令中排除第一个文件,然后将其余文件视为 SRC 列表的一部分。这有效:rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
如果您在 .gitignore 中同时排除 和包含 行(即以 ! 开头的行),这是唯一有效的方法。它还会同步您 --force 添加到您的存储库中的文件,这通常是一件好事。
事实上,这个答案不起作用,所以我最终写了一个有效的答案:stackoverflow.com/a/50059607/99834
J
James Bond

经过数小时的研究,我找到了我所需要的:将目标文件夹与源文件夹同步(如果它们在源中被删除,也会删除目标中的文件),而不是将被忽略的文件复制到目标通过 .gitignore但也不要删除目标中的这些文件

rsync -vhra /source/project/ /destination/project/ --include='**.gitignore' --exclude='/.git' --filter=':- .gitignore' --delete-after

换句话说,这个命令完全忽略来自 .gitignore 的文件,无论是源文件还是目标文件。如果还想复制 .git 文件夹,您可以省略 --exclude='/.git' 部分。

必须从源复制 .gitignore 个文件。如果您将使用 LordJavac 的命令,则不会复制 .gitignore。如果您在目标文件夹中创建了一个文件,该文件应被 .gitignore 忽略,那么尽管有 .gitignore,该文件仍将被删除。这是因为目标中没有 .gitignore 文件。但是如果你有这些文件,.gitignore中描述的文件不会被删除,它们会被忽略,只是意料之中的。


此解决方案特别适合在其目录周围使用多个 .gitignore 分散的项目,这是大多数现代 git 结构。很高兴我向下滚动到这里
抱歉,但这不能按预期工作,因为 rsync 无法正确准备好 gitignore 文件,并且对在那里找到的内容感到困惑。例如,如果您在 .gitigore 中有 foo/*,则 rsync 无法同步 src/foo/.*,即使这不是 git 忽略模式的一部分。
我省略了 .git 的排除,但它仍然没有复制该目录.. 编辑:由 --include '.git' 解决
由于不明显,可能值得注意的是 ':- .gitignore' 表示目录合并 (:),从文件 .gitignore 中排除模式 (-)。 “dir-merge”是“per-directory merge”的缩写,这意味着“rsync 将扫描它遍历的每个目录以查找命名文件,当文件存在时将其内容合并到当前继承规则列表中。”就我而言,我只有一个 .gitignore,它位于父目录中,所以对我来说正确的选项是:--filter='.- ../.gitignore',这是一个“单实例”(.) 合并。
再三考虑,我可以从父目录运行原始命令并调整 。留下评论作为其他人的指南。
s
sorin

2018年解决方案确认

rsync -ah --delete 
    --include .git --exclude-from="$(git -C SRC ls-files \
        --exclude-standard -oi --directory >.git/ignores.tmp && \
        echo .git/ignores.tmp')" \
    SRC DST 

详细信息:--exclude-from 是强制性的,而不是 --exclude,因为排除列表的可能情况不会被解析为参数。从需要文件中排除并且不能使用管道。

当前解决方案将排除文件保存在 .git 文件夹中,以确保它不会影响 git status,同时保持其独立性。如果你愿意,欢迎使用 /tmp。


如果您有一个想要同步的特定 Git 存储库(此处的SRC),这看起来会起作用,但不适用于我所说的原始问题,这是一个包含数千个 Git 存储库的庞大目录作为不同深度的子目录,其中许多具有特殊的 .gitignore
如果您使用的 shell 支持进程替换(bash、zsh 等),您可以使用 --exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
e
ericn

rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination 怎么样?
它对我有用。
我相信你也可以有更多的 --exclude-from 参数。


只要您的 .gitignore 文件碰巧使用与 rsync 兼容的语法,这将起作用。
@JesseGlick 是对的,rsync 无法解析 .gitignore 文件,请参阅 stackoverflow.com/a/50059607/99834 workround。
c
cobbzilla

我有许多非常大的 .gitignore 文件,但没有一个“纯 rsync”解决方案对我有用。我写了这个 rsync wrapper script,它完全遵守 .gitignore 规则(包括 ! 样式的异常和子目录中的 .gitignore 文件),并且对我来说就像一个魅力。


通过 locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done) 尝试此操作,但有很多问题。与 .gitignore 位于同一目录中的文件未正确与带有 / 的目录名称分开。空行和注释被误解。在带有空格的路径中阻塞 .gitignore 文件(不要介意 Ubuntu 的 vagrant 包中的恶魔 /opt/vagrant/embedded/gems/gems/rb-fsevent-0.9.4/spec/fixtures/custom 'path/.gitignore)。也许作为 Perl 脚本做得更好。
@JesseGlick 我不确定您为什么要在脚本中调用该函数。它旨在用作 rsync 的直接替代品,因为处理引号/空格是如此痛苦的特定原因。如果您有一个失败的 gsync 命令行示例以及与之关联的 .gitignore 文件,我很乐意仔细查看。
我需要rsync 整个文件系统,周围散布着各种 Git 存储库。也许您的脚本在同步 single 存储库的情况下运行良好。
当然是。抱歉,我没有说清楚。使用此脚本,您必须从 repo 目录中的每个 git repo 调用它一次。
f
ffeast

对于 mercurial,您可以使用

hg status -i | sed 's/^I //' > /tmp/tmpfile.txt

收集由于 .hgignore 限制而不受 mercurial 控制的文件列表,然后运行

rsync -avm --exclude-from=/tmp/tmpfile.txt --delete source_dir/ target_dir/

rsync 除了忽略的文件之外的所有文件。注意 rsync 中的 -m 标志,它将从同步中排除空目录,因为 hg status -i 只会列出排除的文件,而不是目录


S
Shawn Wang

尝试这个:

rsync -azP --delete --filter=":- .gitignore" <SRC> <DEST>

它可以将所有文件复制到远程目录,不包括“.gitignore”中的文件,并删除不在当前目录中的文件。


l
luksan

查看 rsync(1) 中的 MERGE-FILES FILTER RULES 部分。

看起来可以创建一个 rsync --filter 规则,该规则将包含 .gitignore 文件作为遍历目录结构。


D
Doug Harris

根据 rsync 手册页,除了文件模式的标准列表:

$HOME/.cvsignore 中列出的文件将添加到列表中,并且 CVSIGNORE 环境变量中列出的所有文件

因此,我的 $HOME/.cvsignore 文件如下所示:

.git/
.sass-cache/

排除 .git 和 Sass 生成的文件。


相反,我肯定想要包含 .git/ 目录,甚至可能比工作副本更强大。我要排除的是构建产品。
此外,此设置不可移植。它是每个用户,而不是每个项目。
@JesseGlick 我同意您保留 .git/ 目录。 Git 作为分布式 SCM,备份整个本地存储库很重要。
1/ 此答案中引用的 rsync 手册页中的句子描述了 --cvs-exclude 选项,因此您必须明确使用它。 2/ 您可以在任何目录中创建 .cvsignore 文件以忽略项目特定的内容,这些文件也会被读取。 3/ 根据手册,当您使用 --cvs-exclude 时,.git 已经被忽略,因此将它放在 $HOME/.cvsignore 中似乎是多余的。
d
druid62

备择方案:

git ls-files -zi --exclude-standard |rsync -0 --exclude-from=- ...

git ls-files -zi --exclude-per-directory=".gitignore" |...

(rsync 只能部分理解 .gitignore)


佚名

您可以使用 git ls-files 选择要 rsync 的每个文件,而不是创建排除过滤器:

#!/usr/bin/env bash

if [[ ! $# -eq 2 ]] ; then
    echo "Usage: $(basename $0) <local source> <rsync destination>"
    exit 1
fi

cd $1
versioned=$(git ls-files --exclude-standard)
rsync --verbose --links --times --relative --protect-args ${versioned} $2

即使 git ls-files 返回换行符分隔的路径,这仍然有效。如果您有文件名中带有空格的版本化文件,则可能无法正常工作。


A
Adrian

简短的回答

rsync -r --info=progress2 --filter=':- .gitignore' SOURCE DEST/

参数含义:

-r:递归

--info=...:显示进度

--filter=...:按 .gitignore 文件中列出的规则排除


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅