【问题标题】:get the git commit hash of the last commit which affected files not listed in a .gitignore-like file获取最后一次提交的 git 提交哈希,该哈希影响了 .gitignore-like 文件中未列出的文件
【发布时间】:2019-07-12 11:25:29
【问题描述】:

我正在使用 CI/CD 系统从 git 存储库自动构建 Docker 映像。图像的 Image Tag 对应于相应 git commit 的短(即 8 个字符)哈希,例如myimage:123456ab.

存储库包含打包在 Docker 映像中的源代码以及使用.dockerignore 文件(类似于.gitignore)排除的文档和部署配置等内容。

虽然该过程通常有效,但它会导致重建和重新部署完全相同的 Docker 映像,因为仅对未成为映像一部分的文件进行了更改(例如存储库自述文件)。

仅使用 shell(在本例中为 bash)、git 和标准 *nix 工具,有没有办法获取更改文件的最新提交的短哈希,该文件被忽略.dockerignore 文件?这也应该包括删除一个未被忽略的文件。

【问题讨论】:

    标签: bash git shell


    【解决方案1】:

    您可以通过git loggit show 的组合来做到这一点。

    下面的脚本将回溯修订历史并找到第一个提交有一个不会被.dockerignore忽略的更改

    for commit in $(git log --pretty=%H)
    do
      # Get the changed file names for the commit. 
      # Use `sed 1d` to remove the first line, which is the commit description
      files=$(git show $commit --oneline --name-only | sed 1d)
      if docker-check-ignore $files
      then
         echo $commit
         exit 0
      fi
    done
    exit 1
    

    然后您可以将docker-check-ignore 定义为如下脚本:

    #!/bin/sh
    DIR=$(mktemp -d)
    pushd $DIR
    # Set up a temporary git repository so we can use 
    # git check-ignore with .dockerignore
    git init
    popd
    cp .dockerignore $DIR/.gitignore
    pushd $DIR
    git check-ignore $@
    # Store the error code
    ERROR=$? 
    popd
    rm -rf $DIR
    exit $ERROR
    

    我将减少文件系统操作的数量,而不是为每次提交创建/删除目录。

    【讨论】:

    • 感谢您的回答!您的git log 调用让我很难检查文件列表的结束位置和下一次提交的开始位置,知道吗?此外,awk 脚本在两个左大括号中都抱怨语法错误。你知道问题可能是什么吗?
    • @muffel 我重写了答案以使其更简单。您可以使用 git show 获取每次提交的更改列表。这可能效率稍低,但实现起来更容易/更具可读性。
    【解决方案2】:
    #!/usr/bin/env bash
    
    declare -a ign_table=()
    
    # Populates ign_table with patterns from .dockerignore
    while IFS= read -r line || [[ ${line} ]]; do
      ign_table+=("${line}")
    done < <(sed '/^#/d;/^$/d' .dockerignore)
    
    is_docker_ignored() {
    
      locale -i ignore=1 # false, default not ignored
    
      for ign_patt in "${ign_table[@]}"; do
    
        # If pattern starts with ! it is an exception rule
        # when filename match !pattern, do not ignore it
        # shellcheck disable=SC2053 # $ign_patt must not use quotes to match wildcards
        if [[ ${ign_patt} =~ ^\!(.*) ]] && [[ ${1} == ${BASH_REMATCH[1]} ]]; then
          return 1 # false: no need to check further patterns, file not ignored
        fi
    
        # Normal exclusion pattern, if file match,
        # shellcheck disable=SC2053 # $ign_patt must not use quotes to match wildcards
        if [[ ${1} == $ign_patt ]]; then
          ignore=0 # true: it match an ignore pattern, file may not be ignored if it later matches an exception pattern
        fi
      done
    
      return "${ignore}"
    }
    
    while IFS= read -r file
    do
      is_docker_ignored "${file}" && continue # File is in .dockerignore
      commit_hash="$(git rev-list --all -1 "${file}")"
      printf '%s\n' "${commit_hash:0:8}"
    done < <(git ls-files)
    

    【讨论】:

    • 感谢您的回答,我喜欢这里不依赖 git 的想法。不幸的是,它似乎不适用于重新包含(*!test.txt)。
    • @muffel 我添加了对!do-not-igore-me! 异常规则的支持,如documented here
    猜你喜欢
    • 2016-04-03
    • 1970-01-01
    • 2020-09-06
    • 2016-12-11
    • 2013-09-11
    • 1970-01-01
    • 2013-04-28
    • 2021-06-13
    • 1970-01-01
    相关资源
    最近更新 更多