确保在git sync

时间:2018-11-09 22:07:44

标签: git makefile

我不确定这是一个git问题还是一个make问题,或者也许都是这两个问题,但这是可行的...

假设您有一个文件foo.cpp,它是由生成器程序gen.py生成的。如果修改了生成器程序,则必须再次运行它以生成foo.cpp的新版本。

编码此行为的makefile规则可以如下所示: 1

foo.cpp: gen.py
    gen.py

对于在gen.py上进行积极开发,我认为这是理想的选择。

但是,请考虑在github或其他类似站点上通过git远程访问项目的情况。用户将下载并制作您的项目。

处理已生成文件的传统方法是不全部检查它们:不要在git all中包括foo.cpp(将其添加到您的.gitignore中),并且当用户构建您的项目时,该文件将生成。

但是,另一种方法 是将生成的文件foo.cpp包含在git中。如果更改了一个gen.pyfoo.cpp而又不更新另一个,则有可能gen.pygen.py“不同步” 2

  • 只想构建项目但从不更新foo.cpp的用户不需要具有运行gen.py的先决条件(例如Python)。
  • 未修改的构建速度更快,因为生成步骤未运行。
  • 由于文件将成为diff的一部分,因此在暂存/提交期间,make暗示对foo.cpp的更改将获得直接反馈。

相对于始终在本地生成文件,我对诉讼生成文件的优点不感兴趣。您可能会认为,对于某些项目,已经决定签入生成的文件,并且该决定不值得商debate。

那么在这种情况下,我的问题是:

对于刚克隆项目或同步涉及两个文件的提交的用户,我如何确保foo.cpp不尝试生成gen.py?默认情况下,git在同步文件时将使用当前时间,因此foo.cppfoo.cpp将具有相同的时间戳,并且coplot将被重建。

我不能要求用户更改其git配置。


1 也许points会有其他依赖关系,这也将作为前提条件出现,但这是“基本情况”。

2 一种合理的方法是通过git hook强制它们同步。

2 个答案:

答案 0 :(得分:2)

您可以比较输出,并仅在目标不同时更新目标。例如,假设您的gen.py会将其输出写入stdout,因此通常情况下,您会有如下规则:

foo.cpp: gen.py
        ./gen.py > $@

您可以将其更改为:

foo.cpp: gen.py
        ./gen.py > $@.tmp
        cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@

这里的缺点是,直到您 DO 更新foo.cppgen.py配方将始终运行。但是,不会建立依赖于foo.cpp的目标 ,因为其时间戳未更改。

如果这很昂贵,并且您想解决这个问题,则必须做一些更复杂的事情:如果比较结果正确(无差异),则需要重置gen.py上的时间戳,以便与目标相同;这样可以确保以后不再将目标视为过时的目标。像这样:

foo.cpp: gen.py
        ./gen.py > $@.tmp
        if test -e $@ && cmp -s $@ $@.tmp; then \
            rm -f $@.tmp; \
            touch -r $@ gen.py; \
        else \
            mv -f $@.tmp $@; \
        fi

如果无法轻松更改gen.py的输出文件名,那么您将不得不做一些更令人作呕的事情,例如在运行{{1}之前将$@重命名为$@.old }},然后改回它或适当地使用新的。

ETA 如果您只是想避免运行./gen.py,如果它是同一版本,那么它更像是一个Git问题而不是makefile问题,因为您真正要问的是如何知道这些文件是否在同一Git提交中。这很容易,但是正如我在下面的评论中所说,我认为这实际上是不对的:

gen.py

答案 1 :(得分:0)

一个post-merge钩子可能会解决您的问题:

  1. 向您的存储库中添加一个hooks/post-merge脚本,其中包含例如(使用bash版本> 4):

    $ cat hooks/post-merge
    #!/usr/bin/env bash
    printf 'running post-merge\n'
    declare -A generated=()
    generated["foo.cpp"]="gen.py"
    # add other (generated, generator) pairs to the generated associative array
    for g in "${!generated[@]}"; do
        s="${generated[$g]}"
        if ! git diff ORIG_HEAD HEAD --exit-code -- "$g" "$s" &> /dev/null; then
            printf 'touch %s\n' "$g"
            touch "$g"
        fi
    done
    $ chmod +x hooks/post-merge
    
  2. 添加一个runme.sh脚本,并要求您的用户在克隆存储库后手动运行它(他们必须真的信任您,但是安全性超出了此答案的范围):

    $ cat runme.sh
    #!/usr/bin/env bash
    for s in hooks/*; do
        ln -sf ../../"$s" .git/hooks
    done
    

如果每对(generatedgenerator)对都被git pull修改,则post-merge钩子将触碰generated,以使其最后一次修改时间戳比generator更新。

当然,这只是一个起点,可能还有其他几件事要考虑(重新设置,...)。