【问题标题】:Can one re-stage files in a pre-commit git hook?可以在预提交 git 挂钩中重新暂存文件吗?
【发布时间】:2017-10-23 22:35:07
【问题描述】:

我制作了一个 powershell 脚本,为我的项目中的某些文件添加了标题。我想在代码被推送到 GitHub 的过程中运行它,以便 GitHub 存储库中的所有此类文件都附加了标头,从而无需手动运行脚本。

问题是,当我在我的预提交 git 挂钩中修改文件时,这些更改不会重新暂存,因此我基本上必须暂存、提交然后再次暂存以提交标题。

有没有好的方法来处理这个问题,或者将 powershell 脚本的执行与 git 流分开是否更好?

【问题讨论】:

    标签: git bash powershell github githooks


    【解决方案1】:

    TL;DR

    主题行中问题的答案是“是的,但是”。 :-) 我会在这里改写一下,这样我们就可以讨论所有的技术细节。 “但是”是如果你使用git commit --only 会有很大的惊喜。我似乎记得曾经有一段时间它也严重影响了git commit -a,但现在更好了。 (我不确定哪个 Git 版本对 -a 表现不好。)

    长

    Git 从一个——或 ,在大多数情况下——索引进行每个新的提交。索引或任何索引本质上是一个提议的提交:它由一种扁平化的树组成,带有 blob 哈希号和文件名。它的形式特别方便git mktree,它将索引变成一系列tree对象,并返回顶级树对象的哈希ID,然后进入提交对象.

    那么,这个问题涉及到这三个部分:

    1. 究竟哪个索引被用来构建新的提交?
    2. 可以修改该索引吗?如果是这样,新的提交会发生什么?
    3. 这对索引有何影响?

    这里, 索引是与工作树一起使用的特殊的、可区分的索引。每个工作树都有一个这样的索引:如果您使用git worktree add,您将获得更多工作树,并且每个工作树都有自己的私有索引,但您只能在一个工作树中一次,所以“这个工作树的索引”是 索引。

    但是,当您运行 git commit 时,您可以指示它(使用 --only 和/或其他命令行参数)构建自己的 private 索引,与 the 索引。如果你这样做了,它将运行带有环境变量GIT_INDEX_FILE 的各种钩子,该变量设置为临时索引的路径名。 (如果不是,GIT_INDEX_FILE 将包含 .git/index,这是 索引文件的路径。)

    所以 Q1 的答案是:$GIT_INDEX_FILE。

    现在,您实际上可以修改 Git 用于构建提交的任何索引,因为 Git 将在运行您的钩子后重新读取该索引。所以 Q2 的答案是:是的,这使得下一次提交使用正在使用的任何索引中的任何内容。

    第三季度是最难的。如果你使用git commit --only <paths>,Git 必须创建一个临时索引:

    • 是HEAD的副本
    • 除了指定的<paths>

    完全不影响 索引。但是,如果提交继续并成功,Git 现在必须修改 索引,以说明这些 <paths> 在新的 HEAD 提交中包含新的 blob。

    事实上,Git 需要创建 两个 临时索引(索引?),一个用于建议的提交及其 --only 文件,另一个用于建议的提交结果。提议的提交结果变为index.lock,作为新的索引。

    如果您在运行时git add 文件,它们将进入临时索引。但是 索引呢?让我们找出答案:

    $ cat .git/hooks/pre-commit
    #! /bin/sh
    echo pre-commit hook, GIT_INDEX_FILE = $GIT_INDEX_FILE
    git add sneaky
    $ echo this is the base version of sneaky > sneaky
    $ echo this is the base version of other > other
    $ git add other sneaky
    $ git commit -m 'create two files'
    pre-commit hook, GIT_INDEX_FILE = .git/index
    [master 5131b63] create two files
     2 files changed, 2 insertions(+)
     create mode 100644 other
     create mode 100644 sneaky
    

    如您所见,预提交钩子在这里触发,并添加了sneaky。这没什么大不了的,因为复制到此提交的索引中的内容(即 the 索引)已经是相同的基本版本。

    然而,现在让我们修改sneaky、git add它的当前内容,同时修改other和git add它的当前内容,这样我们在 两个文件的索引...

    $ echo version 2 of other > other
    $ echo version 2 of sneaky > sneaky
    $ git add other sneaky
    

    此时,索引和工作树都有“版本 2”。现在让我们将两个工作树文件更新为“版本 3”git adding 他们:

    $ echo version 3 of other > other
    $ echo version 3 of sneaky > sneaky
    $ git status --short
    MM other
    MM sneaky
    

    这告诉我们HEAD、索引和工作树版本都不同:HEAD 版本是基础版本,索引版本是版本 2,工作树版本是版本 3。

    现在我们运行git commit --only other:

    $ git commit --only other -m 'jump other straight to v3'
    pre-commit hook, GIT_INDEX_FILE = [path]/.git/next-index-72393.lock
    [master 91ec03b] jump other straight to v3
     2 files changed, 2 insertions(+), 2 deletions(-)
    

    现在让我们看看提交、索引和工作树中有什么:

    $ git status --short
    MM sneaky
    

    好的,other 的工作树版本与other 的索引版本匹配,HEAD 的other 版本匹配,所以:

    $ cat other
    version 3 of other
    

    它们都是第 3 版。不过,sneaky 呢? HEAD 和索引不同,索引和工作树不同。让我们先看看HEAD 中的内容:

    $ git show HEAD:sneaky
    version 3 of sneaky
    

    现在让我们看看工作树中有什么:

    $ cat sneaky
    version 3 of sneaky
    

    啊哈,这些匹配!棘手的部分是查看索引版本,但我们也可以使用git show:

    $ git show :0:sneaky
    version 2 of sneaky
    

    哇,看那个! 索引版本是旧版本!

    那么,这就是 Q3 的答案:pre-commit 挂钩中的 git add 更新用于构建下一次提交的任何索引,但如果这是一个 临时 索引,它不更新将成为真正索引的内容。这可以说是一个错误:预提交钩子中的git add 也应该添加到将成为 索引的索引中。实现这一点有点棘手(git commit 可能会在钩子之前和之后读取临时索引并将任何更新复制到 index.lock 文件中)。

    请注意,如果您在没有--only 的情况下提交会发生什么,我跳过了详细信息。幸运的是,在这种情况下,您添加的索引是 索引,所以一切都按您的预期进行:

    $ git reset --hard 5131b63
    HEAD is now at 5131b63 create two files
    $ echo v2 > other && echo v2 > sneaky && git add other
    $ git commit -m 'regular commit'
    pre-commit hook, GIT_INDEX_FILE = .git/index
    [master 10a8b20] regular commit
     2 files changed, 2 insertions(+), 2 deletions(-)
    $ git status --short
    $ 
    

    预提交git add 替换了sneaky 的索引版本,并且它一直被替换...即使提交失败,它也会被替换。

    还要注意,当使用git commit -a 时,我们会得到一个不同的临时索引:

    $ git commit -a -m test
    pre-commit hook, GIT_INDEX_FILE = [path]/.git/index.lock
    

    在这里,Git 为 git commit -a 所做的就是创建建议的新索引 as index.lock 文件。 git add 正常进行,以通常的方式添加文件,然后当提交成功时,Git renames index.lock 到 index。这将解锁索引文件,同时将修改后的索引放置到位,因此git commit -a 到 Q3 的答案是临时索引成为索引。这与git commit --only 不同。

    【讨论】:

      【解决方案2】:

      这与this post非常相似。

      您想使用.gitattributes 并指定在添加时运行的脚本(即git add)。这样,每次添加文件时,脚本都会运行并进行更改,然后提交。

      希望有帮助!

      【讨论】:

        猜你喜欢
        • 2015-01-09
        • 1970-01-01
        • 2015-10-10
        • 2015-11-28
        • 2015-12-31
        • 2019-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多