【问题标题】:Shell script to check git for changes and then loop through changed files?用于检查 git 的更改然后循环通过更改的文件的 shell 脚本?
【发布时间】:2022-01-13 03:51:34
【问题描述】:

我正在尝试编写一个执行以下操作的 shell 脚本:

  1. 检查远程 git 存储库是否有任何要拉取的更改。
  2. 如果远程 git 存储库中有更改,请拉取这些更改。
  3. 循环浏览新文件或已修改的文件。

通过我的研究,我发现了一些必要的命令来执行这些操作,但我无法让它们在 shell 脚本中协同工作。

这是一个包含我拥有的一些命令的脚本:

#!/bin/sh

#Check if there are any changed files
git --git-dir="/dir/.git" fetch origin

if git --git-dir="/dir/.git" log HEAD..origin/master --oneline
then

#Output the modified files from the last pull
        git --git-dir="/dir/.git" diff --name-status ORIG_HEAD..

fi

我无法使用此脚本中的命令处理的事情是:

  1. 检查是否有更改的 if 语句并不总是正确的。我尝试过使用其他 git 命令的 if 语句,它们也总是正确的。似乎 git 不像正常的 shell 命令那样工作,你会得到 0 或 1 响应。如何获得这样的 git 命令或其他 git 命令以在 if 语句中返回正确的响应?
  2. 如何将命令输出的变量分配给数组以查看更改的文件,以便使用 for 循环遍历它们?

如果我的脚本中的命令在这种情况下不会真正起作用,有什么更好的方法来做到这一点?

编辑:抱歉,当我遍历更改的文件时应该更清楚,我需要在遍历文件之前从远程存储库中提取更改,以便在处理这些文件时获得最新的更改。

【问题讨论】:

    标签: git bash shell pull


    【解决方案1】:

    对于您的第一个问题,您可以使用git diff --quiet(或git diff --exit-code,但通常当您使用它作为其退出代码时,您希望它不打印输出,而git diff --quiet 暗示--exit-code)以确定是否有任何变化。如果有变化,这将为您提供 1 值,如果没有,则为 0。因此,如果您想拥有仅在发生更改时才运行的代码:

    if ! git --git-dir="/dir/.git" diff --quiet
    then
        # do stuff...
    fi
    

    对于第二个问题,我建议使用while read ... 循环来读取来自git diff-tree 的行:

    git --git-dir="/dir/.git" diff-tree ORIG_HEAD.. | \
        while read srcmode dstmode srcsha dstsha status srcfile dstfile
        do
            # do something with $srcfile and $dstfile
        done
    

    请注意,$srcmode 在开头会有一个额外的:,而$dstfile 只有在文件被重命名时才会有一个值。如果您不想担心重命名,请传入--no-renames,您将看到的只是添加和删除,而不是重命名。

    【讨论】:

      【解决方案2】:

      您可以简单地使用git diff --name-only 列出所有已更改文件的文件名。

      例如,这里有一个简单的脚本来列出所有编辑的 PHP 文件并测试语法。

      #!/bin/bash
      files=`git diff --name-only | grep -E '.php$' `
      for file in $files; do
        php -l $file
      done
      

      【讨论】:

      • 为我工作:git diff HEAD --name-only
      【解决方案3】:

      你说得对,git 通常不会以非零状态退出,除非出现错误。我发现检测未提交更改的最简单方法是这样的:

       let changes=0
       while read status filename; do
          let changes=1
          # do something with this filename/status
       done < <(git status --porcelain)
       if (( ! changes )); then
          echo "No changes."
       fi
      

      一般来说,如果你要尝试从代码中驱动 git,你应该在那些支持它的子命令上使用--porcelain,让它以更自动化的方式运行。您可能还想研究其他语言的库以与 git 交互,而无需构建自己的 shell 命令;例如,在 Ruby 中有 grit

      现在,就您而言,您想检测上游发生了什么变化。为此,您可能想使用git diff-tree。与上面类似的逻辑:

       git fetch   # not pull
       while read filename; do
         # do something with filename
       done < <(git diff-tree --name-only origin/master master)  # or whatever branch you're using
      

      【讨论】:

      • 嗨,马克。感谢您的回复。我尝试运行您的脚本,但出现以下错误:意外标记附近的语法错误&lt;' done
      • 你确定你使用的是 bash?
      • 嗨,马克。你是对的,我没有使用 bash ......它现在可以工作了。我需要进一步研究这一点,但据我所知,该脚本将在更改的文件从远程存储库中提取之前循环遍历它们?
      • 抱歉,这做错了 - 它循环遍历工作副本中尚未提交的更改的文件。您正在寻找来自上游的更改,因此 git diff-tree 可能是您想要的。请参阅我编辑的答案和@BrianCampbell 的。
      • 你应该可以从脚本中git pull,当然。合并更改后,两个 master 分支之间将不会报告任何差异,因此您必须区分 HEAD^HEAD
      【解决方案4】:

      如果您正在编写脚本并希望确保 repo 处于已提交状态(例如,没有新的、修改的或暂存的文件),请尝试以下操作:

      # Get to code.  Exit if unsaved changes in repo
      if `git status | grep -q "nothing to commit"`; then
        git checkout --quiet $BRANCH || exit 1
      else
        echo "ERROR: repo has unsaved changes"
        exit 1
      fi
      

      这是我能找到的最简单的检查新文件、更改文件和暂存文件的方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-08
        • 1970-01-01
        • 1970-01-01
        • 2013-06-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多