【问题标题】:rsync exclude according to .gitignore & .hgignore & svn:ignore like --filter=:Crsync 排除根据 .gitignore & .hgignore & svn:ignore like --filter=:C
【发布时间】:2012-12-04 22:30:17
【问题描述】:

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;例如.hgignore 包含类似 ^target$ 的行(Git /target/ 的 Mercurial 等效项)不会被 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(过滤器也可以包括)。

标签: git svn mercurial rsync


【解决方案1】:

正如 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 的完整路径才能保存?
【解决方案2】:

经过数小时的研究,我找到了我真正需要的东西:将目标文件夹与源文件夹同步(如果它们在源中被删除,也会删除目标中的文件),而不是将文件复制到目标.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-files。 但是如果你会有这些文件,.gitignore中描述的文件不会被删除,它们会被忽略,只是意料之中的。

【讨论】:

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

您可以使用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 &gt; /tmp/excludes; echo /tmp/excludes)" SRC DEST
  • 如果您在 .gitignore 中同时排除 和包含 行(即以 ! 开头的行),这是唯一有效的方法。它还会同步您 --force 添加到您的存储库中的文件,这通常是一件好事。
  • 确实这个答案不起作用,所以我最终写了一个有效的答案:stackoverflow.com/a/50059607/99834
【解决方案4】:

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。
【解决方案5】:

2018年解决方案confirmed

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 存储库作为不同深度的子目录,其中许多具有特殊的 .gitignores。
  • 如果您使用的 shell 支持进程替换(bash、zsh 等),您可以使用--exclude-from=&lt;(git -C SRC ls-files --exclude-standard -oi --directory)
【解决方案6】:

对于mercurial,您可以使用

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

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

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

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

【讨论】:

    【解决方案7】:

    试试这个:

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

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

    【讨论】:

      【解决方案8】:

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

      【讨论】:

      • 通过locate -0e .gitignore | (while read -d '' x; do process_git_ignore "$x"; done) 尝试这个,但有很多问题。与.gitignore 位于同一目录中的文件未正确与/ 的目录名称分开。空行和 cmets 被误解。在带有空格的路径中阻塞.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 调用一次。
      【解决方案9】:

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

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

      【讨论】:

        【解决方案10】:

        根据 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 中似乎是多余的。
        【解决方案11】:

        替代方案:

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

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

        (rsync 只能部分理解.gitignore)

        【讨论】:

          【解决方案12】:

          您可以使用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 返回换行分隔的路径,这仍然有效。如果您的文件名中有空格,则可能无法使用。

          【讨论】:

            【解决方案13】:

            简答

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

            参数含义:

            -r: 递归

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-05-19
              • 1970-01-01
              • 1970-01-01
              • 2019-09-19
              • 2015-06-11
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多