【问题标题】:PHP filtering files and paths according .gitignorePHP 根据 .gitignore 过滤文件和路径
【发布时间】:2013-11-27 16:41:21
【问题描述】:

我想使用 PHP 读取所有被 .gitignore 配置忽略的文件和路径。就像 git 一样。

可以重复读取目录并使用正则表达式对每个文件进行过滤。但是如果路径文件太多,那就太没用了。

读取.gitignore忽略的目标文件和路径有什么好的和最有效的方法吗?

【问题讨论】:

  • Git 提供了check-ignore 子命令,它可以告诉您哪些路径被忽略(并且,可选地,不被忽略),这当然可以处理各种忽略文件和列表的全部复杂性。 My answer to another question 提供了如何执行此操作的详细信息。

标签: php git path gitignore


【解决方案1】:

.gitignore 中的条目大多是 glob 模式。您可以使用 php 的 file 函数读取 .gitignore 的每一行,忽略以 # 开头的空行和行,然后使用 php glob 函数 (http://php.net/manual/en/function.glob.php) 读取模式

【讨论】:

    【解决方案2】:

    您可以从.gitignore 文件中获取要忽略的文件数组并检查。为此,您需要使用 glob 函数读取文件并匹配文件。

    首先,获取文件的内容:

    $contents = file_get_contents($pathToGitIgnoreFile);
    $path = dirname(realpath($pathToGitIgnoreFile));
    

    也可以使用.gitignore文件所在目录来匹配与gitignore同目录的文件。

    接下来,我们需要将内容拆分为单独的规则。规则从文件中自己的行开始。以井号 (#) 开头的行是 cmets,因此我们可以使用正则表达式来查找不是 cmets 的非空行:

    $rules = array();
    preg_match_all('/[\\A\\v]([^#\\v]\\V*)[\\z\\v]?/', $contents, $rules);
    $rules = $rules[1];
    

    那么你所要做的就是遍历规则并使用glob创建一个文件名数组来忽略:

    $files = array();
    foreach ($rules as $rule)
    {
        if (strpos($rule, '!') === 0) // negative rule
            $files = array_diff($files, glob($path . DIRECTORY_SEPARATOR . substr($rule, 1)));
        else
            $files = array_merge($files, glob($path . DIRECTORY_SEPARATOR . $rule));
    }
    $files = array_unique($files);
    

    我没有测试这个代码,如果它不适合你,请在下面评论。

    【讨论】:

    • 谢谢,但它不能正常工作。 1. glob 不包含子文件夹,结果为空。 2、按每个规则加载所有文件好不好?如果我们有 10,000 个文件和 50 条规则,代码必须加载 10,000 * 50 次文件。
    • @anlai 我编辑了正则表达式并对其进行了测试——它有效。 Glob 确实匹配子目录和文件。如果要解析多个 .gitignore 文件,则需要递归循环目录并首先找到任何 .gitignore 文件。我无法想出 10,000 多个文件。
    【解决方案3】:

    我用这个功能读取整条路径,效果不错

    function read_dir($dir)
        {
            $files = array();
            $dir = preg_replace('~\/+~','/',$dir . '/');
            $all  = scandir($dir);
            foreach($all as $path):
                if($path !== '.' && $path !== '..'):
                    $path = $dir . '/' . $path;
                    $path = preg_replace('~\/+~','/',$path);
                    $path = realpath($path);
                    if(is_dir($path)):
                        $files = array_merge($files, read_dir($path));
                    endif;
                    $files[] = preg_replace('~/+~i','/',$path);
                endif;
            endforeach;
            return $files;
    }
    

    更新:您可以在上述函数上使用 preg_grep,如下所示

    $files = preg_grep('~\.gitignore\b~i', array_values(read_dir($path)));
    

    【讨论】:

      【解决方案4】:

      SPL(标准 PHP 库)包含一些用于该作业的迭代器。我将示例限制为过滤掉所有以“。”开头的目录或文件。以他们的名义。

      .gitignore 的规则非常复杂,解析条目和构建一组规则将超出示例的范围。

      $directory = __DIR__;
      
      $filtered = new RecursiveIteratorIterator(
        new RecursiveCallbackFilterIterator(
          new RecursiveDirectoryIterator($directory),
          function ($fileInfo, $key, $iterator) {
            // only accept entries that do not start with an . 
            return substr($fileInfo->getFilename(), 0, 1) != '.';
          }
        )
      );
      
      
      foreach ($filtered as $fileInfo) {
        echo (string)$fileInfo, "\n";
      }
      

      【讨论】:

        【解决方案5】:

        只是一个疯狂的想法:如果您依靠 Git 为您提供忽略文件的模式,为什么不依靠它来提供包含/忽略文件的列表?只需发出如下命令:

        • git ls-files 用于所有跟踪的文件
        • git clean -ndXgit ls-files -i --exclude-from=[Path_To_Your_Global].gitignore 用于所有被忽略的文件

        查看哪个 Git 命令为您提供最佳输出,然后遍历路径文件。

        请注意:执行外部命令时采取所有必要的预防措施!

        来源:

        【讨论】:

        • 问题是我需要在没有Git环境的情况下用PHP处理.gitignore文件。
        • 如果情况确实如此,这意味着您无法发出 shell 命令,而不是使用其他解决方案。
        【解决方案6】:

        您需要分几个步骤进行:

        1 - 查找 .gitignore 文件

        每个文件夹可以有一个,所以不要假设只有一个。

        子模块有一个指向主 .git 文件夹的 .git 链接,所以也要小心不要过早停止。

        它会像这样:

        function find_gitignore_files($dir) {
          $files = array();
          while (true) {
            $file = "$dir/.gitignore";
            if (is_file($file)) $files[] = $file;
            if (is_dir("$dir/.git") && !is_link("$dir/.git")) break;  # stop here
            if (dirname($dir) === '.') break;                         # and here
            $dir = dirname($dir);
          }
          return $files;
        }
        

        2 - 解析每个 .gitignore 文件

        您需要忽略 cmets,注意否定运算符 (!),并注意 glob。

        这个是,给予或接受,将会是这样的:

        function parse_git_ignore_file($file) { # $file = '/absolute/path/to/.gitignore'
          $dir = dirname($file);
          $matches = array();
          $lines = file($file);
          foreach ($lines as $line) {
            $line = trim($line);
            if ($line === '') continue;                 # empty line
            if (substr($line, 0, 1) == '#') continue;   # a comment
            if (substr($line, 0, 1) == '!') {           # negated glob
              $line = substr($line, 1);
              $files = array_diff(glob("$dir/*"), glob("$dir/$line"));
            } else {                                    # normal glob
              $files = glob("$dir/$line");
            }
            $matches = array_merge($matches, $files);
          }
          return $matches;
        }
        

        (注意:以上都没有经过测试,但它们应该会让你朝着正确的方向前进。)

        【讨论】:

        • 此答案不完整,不应标记为已接受的答案。详细而言,PHP glob 在功能上与 .gitignore patterns 相差甚远。此外,在应用过滤器后,您也没有涵盖文件列表。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-13
        • 2017-09-13
        • 1970-01-01
        • 2018-12-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多