Rsync 包含一个漂亮的选项 --cvs-exclude
以“以与 CVS 相同的方式忽略文件”,但 CVS 已过时多年。有没有办法让它也排除现代版本控制系统(Git、Mercurial、Subversion)会忽略的文件?
例如,我有很多从 GitHub 签出的 Maven 项目。通常,它们包括一个 .gitignore
,至少列出 target
,即默认的 Maven 构建目录(可能存在于顶层或子模块中)。由于这些目录的内容完全是一次性的,而且它们可能比源代码大得多,所以我想在使用 rsync 进行备份时将它们排除在外。
当然,我可以明确地 --exclude=target/
但这会意外地抑制恰好被命名为 target
并且不应该被忽略的不相关目录。
我可以为磁盘上任何 .gitignore
、.hgignore
或 svn: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 或更高版本的工作副本。
:-
究竟是什么意思?冒号是什么意思?什么破折号?
:
代表 dir-merge
(如果文件夹树中有 .gitignore
个文件很有用),-
代表 exclude
(过滤器也可以包含)。
正如 luksan 所提到的,您可以通过将 --filter
切换到 rsync
来执行此操作。我通过 --filter=':- .gitignore'
(“.gitignore”之前有一个空格)实现了这一点,它告诉 rsync
与 .gitignore
文件进行目录合并,并根据 git 的规则将它们排除在外。如果有的话,您可能还想添加全局忽略文件。为了使其更易于使用,我为 rsync
创建了一个别名,其中包含过滤器。
您可以使用 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>
rsync -azP --exclude-from="$(git -C SRC ls-files --exclude-standard -oi --directory > /tmp/excludes; echo /tmp/excludes)" SRC DEST
.gitignore
中同时排除 和包含 行(即以 !
开头的行),这是唯一有效的方法。它还会同步您 --force
添加到您的存储库中的文件,这通常是一件好事。
经过数小时的研究,我找到了我所需要的:将目标文件夹与源文件夹同步(如果它们在源中被删除,也会删除目标中的文件),而不是将被忽略的文件复制到目标通过 .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 结构。很高兴我向下滚动到这里
.gitigore
中有 foo/*
,则 rsync 无法同步 src/foo/.*
,即使这不是 git 忽略模式的一部分。
--include '.git'
解决
':- .gitignore'
表示目录合并 (:
),从文件 .gitignore
中排除模式 (-
)。 “dir-merge”是“per-directory merge”的缩写,这意味着“rsync 将扫描它遍历的每个目录以查找命名文件,当文件存在时将其内容合并到当前继承规则列表中。”就我而言,我只有一个 .gitignore
,它位于父目录中,所以对我来说正确的选项是:--filter='.- ../.gitignore'
,这是一个“单实例”(.
) 合并。
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。
SRC
),这看起来会起作用,但不适用于我所说的原始问题,这是一个包含数千个 Git 存储库的庞大目录作为不同深度的子目录,其中许多具有特殊的 .gitignore
。
--exclude-from=<(git -C SRC ls-files --exclude-standard -oi --directory)
rsync --exclude-from='path/.gitignore' --exclude-from='path/myignore.txt' source destination
怎么样?
它对我有用。
我相信你也可以有更多的 --exclude-from
参数。
.gitignore
文件碰巧使用与 rsync
兼容的语法,这将起作用。
我有许多非常大的 .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 脚本做得更好。
rsync
的直接替代品,因为处理引号/空格是如此痛苦的特定原因。如果您有一个失败的 gsync
命令行示例以及与之关联的 .gitignore
文件,我很乐意仔细查看。
rsync
整个文件系统,周围散布着各种 Git 存储库。也许您的脚本在同步 single 存储库的情况下运行良好。
对于 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 只会列出排除的文件,而不是目录
尝试这个:
rsync -azP --delete --filter=":- .gitignore" <SRC> <DEST>
它可以将所有文件复制到远程目录,不包括“.gitignore”中的文件,并删除不在当前目录中的文件。
查看 rsync(1) 中的 MERGE-FILES FILTER RULES 部分。
看起来可以创建一个 rsync --filter 规则,该规则将包含 .gitignore 文件作为遍历目录结构。
根据 rsync
手册页,除了文件模式的标准列表:
$HOME/.cvsignore 中列出的文件将添加到列表中,并且 CVSIGNORE 环境变量中列出的所有文件
因此,我的 $HOME/.cvsignore 文件如下所示:
.git/
.sass-cache/
排除 .git 和 Sass 生成的文件。
.git/
目录,甚至可能比工作副本更强大。我要排除的是构建产品。
rsync
手册页中的句子描述了 --cvs-exclude
选项,因此您必须明确使用它。 2/ 您可以在任何目录中创建 .cvsignore
文件以忽略项目特定的内容,这些文件也会被读取。 3/ 根据手册,当您使用 --cvs-exclude
时,.git
已经被忽略,因此将它放在 $HOME/.cvsignore
中似乎是多余的。
备择方案:
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
返回换行符分隔的路径,这仍然有效。如果您有文件名中带有空格的版本化文件,则可能无法正常工作。
简短的回答
rsync -r --info=progress2 --filter=':- .gitignore' SOURCE DEST/
参数含义:
-r
:递归
--info=...
:显示进度
--filter=...
:按 .gitignore 文件中列出的规则排除
--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 的完整路径才能保存?