可以在预提交git钩子中重新暂存文件吗?

时间:2017-10-23 22:35:08

标签: git bash powershell github githooks

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

问题在于,当我在预提交git hook中修改文件时,这些更改不会被重新进行,所以我基本上必须暂存,提交然后再次提交以提交标头。

有没有一种好方法可以解决这个问题,或者将powershell脚本的执行与git流分开是更好的做法吗?

2 个答案:

答案 0 :(得分:2)

TL; DR

主题中问题的答案是"是的,但是。" :-)我在这里稍微改写一下,以便我们可以讨论所有的技术细节。 "但是"如果你使用git commit --only,那将是一个很大的惊喜。我似乎记得有一段时间它也严重影响git commit -a,但现在情况好一点。 (我不确定哪个Git版本与-a表现不好。)

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

然后,问题涉及这三个部分:

  1. 准确地哪个索引用于构建新提交?
  2. 可以修改该索引吗?如果是这样,新提交会发生什么?
  3. 这对 索引有何影响?
  4. 这里, 索引是与工作树一起使用的特殊的,可分辨的索引。每个工作树都有一个这样的索引:如果你使用git worktree add,你会获得更多的工作树,每个工作树都有自己的私有索引,但你只能 in 一次只有一个工作树,所以"这个工作树的索引"是 索引。

    但是,当您运行git commit时,您可以指示它(使用--only和/或其他命令行参数)来构建自己的私有索引,与<分开< em> 索引。如果您已经这样做了,它将使用环境变量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。这没什么大不了的,因为复制到此提交的索引 - 索引 - 已经是相同的基本版本。

    但是,现在让我们修改sneakygit add的当前内容,并修改othergit add的当前内容,以便我们在 索引中为这两个文件添加新内容......

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

    此时,索引和工作树都有&#34;版本2&#34;。现在让我们将工作树文件更新为&#34;版本3&#34; 没有 git add他们:

    $ 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的索引版本匹配HEADother,所以:

    $ 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的答案:预提交钩子中的git add更新用于构建下一个提交的索引,但是如果那个临时的 index,它更新将成为真正的索引。这可以说是一个错误:预提交钩子中的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所做的是将建议的新索引创建为 index.lock文件。 git add正常进行,以通常方式添加文件,然后当提交成功时,Git index.lock重命名为index。这将解锁索引文件,同时将修改后的索引放在适当位置,以便git commit -a到Q3的答案是临时索引成为索引。这与git commit --only的不同。

答案 1 :(得分:0)

这与this post非常相似。

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

希望有所帮助!