检测文件或目录在最近一次提交中是否发生更改的可靠方法是什么?

What is a reliable way to detect whether a file or directory has changed in the most recent commit?

我有一个 Web 项目,只需推送新提交即可将其部署到 ec2 实例。我使用 post-recieve git 挂钩远程执行一个 shell 脚本,其中 'deploys' 通过将项目检出到生产目录中。步骤是,在 Express 应用程序上 运行 npm install,在前端(create-react-app 应用程序)npm install,然后 运行 npm run build(基本上利用 web-pack 从我的节点源代码构建一个优化的分发文件夹)。

这些步骤很昂贵,而且在许多情况下不需要。例如。如果我所做的只是更新 srcs/components/ 中的节点组件,那么 npm run build 应该 运行,但服务器和前端上的 npm install 不应该。如果我所做的只是向我的 express 应用程序添加评论,那么任何脚本都不应该 运行。

我当前的服务器端部署脚本如下所示:

#!/usr/bin/env bash

GIT_WORK_TREE=/home/ec2-user/absiteProd git checkout -f

### TODO: conditional NPM work

pm2 restart index

我的问题是如何使用 git(或 grep、sed、awk 等)可靠地告诉我 /home/ec2-user/absiteProd/frontend/package.json/home/ec2-user/absiteProd/server/package.json 或“home/ec2-user/absiteProd/frontend/sources”中的任何内容已更改?

目前我在以下方面取得了一些成功:

if `git log --stat -n 1` | grep --quite frontend/src/* ; then
   cd home/ec2-user/frontend
   npm run build
fi

但既然这似乎是应用程序部署中的常见要求,我觉得一定有更简单的方法吗?

您可以在 this thread 中找到类似的需求:

How do I find a last commit for the given directory inside the repository?

I want to avoid rebuilding the specific part of the project if there were no changes in it since the last build, so I need to find the sha of the last time the directory was changed.

您可以比较修改元素的最后一次提交,使用 git rev-list:

git rev-list -1 HEAD -- frontend/package.json
git rev-list -1 HEAD -- absiteProd/server/package.json
git rev-list -1 HEAD -- frontend/src

使用当前的 HEAD SHA1(git rev-parse, the --verify 是可选的):

git rev-parse --verify HEAD

即:

h=$(git rev-parse --verify HEAD)
b=false
if [[ "$(git rev-list -1 HEAD -- frontend/package.json)" == "${h}" ]]; then b=true; fi
if [[ "$(git rev-list -1 HEAD -- frontend/package.json)" == "${h}" ]]; then b=true; fi
if [[ "$(git rev-list -1 HEAD -- frontend/package.json)" == "${h}" ]]; then b=true; fi
if !b; then exit 0; fi
cd home/ec2-user/frontend
npm run build

Git 不会以任何有用的方式 store 目录,因此您必须自己定义“任何内容”的含义(这有其优点,因为您可以定义的意思,而不是被别人对你无用的定义所困,而是意味着你必须做更多的工作。

也就是说,Git 将每个 文件 存储为每个提交中的路径名。您的部署脚本需要一些工作树——在本例中为 /home/ec2-user/absiteProd——从一种状态到另一种状态。由于它使用 git checkout 来执行此操作,并且 git checkout 对时间戳没有任何特殊作用,因此您现在有许多选项,其中包含许多不同的低级细节和后续结果。这里有两个明显且相当简单的起点:

  • /home/ec2-user/absiteProd 是否与之前的提交完全相同?如果是,哪个提交? (提交具有唯一的哈希 ID,这些通常是在脚本中使用的东西。)然后您可以 Git 将以前的提交与新的提交进行比较,例如使用 git diff --name-status。这与您现在所做的类似,但更好。

    如果您的部署脚本是 post-接收脚本,则您已经拥有从标准输入中读取的引用的旧哈希 ID 和新哈希 ID。因此,在这两次提交之间更改的文件集及其状态是:

     git diff-tree -r --name-status $oldhash $newhash
    
  • 如果 git checkout 写入任何文件,这些文件将以“现在”作为它们的修改时间戳,因为 git checkout 只是让系统的时间适用于更新的文件。你能用这个吗?只要您从不在一秒钟内部署超过两次,您就可以将其与 make 构建系统结合使用,该系统基于时间戳构建文件。

如果 make 适合这里,它可能是最好的选择,除了它的最大每秒部署一次(或者你的底层 OS 对文件的时间戳解析).您可以只声明无论输出文件 is/are,它们取决于相应的输入文件,并提供从输入和 [=53 构建输出的方法=] make.