你能在git提交时更改文件内容吗?

48

我在GitHub上的开放小说中保存了一个单词列表,我希望自动设置第一行,即字典中的单词数。我的首选是编写一个pre-commit钩子,读取文件,计算单词数,重写第一行并将其重新写回文件。以下是代码:

PRE_COMMIT {
  my ($git) = @_;
  my $branch =  $git->command(qw/rev-parse --abbrev-ref HEAD/);
  say "Pre-commit hook in $branch";
  if ( $branch =~ /master/ ) {
     my $changed = $git->command(qw/show --name-status/);
     my @changed_files = ($changed =~ /\s\w\s+(\S+)/g);
     if ( $words ~~ @changed_files ) {
       my @words_content = read_file( $words );
       say "I have $#words_content words";
       $words_content[0] = "$#words_content\n";
       write_file( $words, @words_content );
     }
   }
};

然而,由于文件已经被暂存,我遇到了这个错误:
错误:您对以下文件的本地更改将被检出覆盖:text/words.dic。请在切换分支之前提交您的更改或将它们隐藏起来。中止操作。
也许把它作为一个 post-commit 钩子,并在下一次提交时进行更改会更好?或者完全采取不同的方法?总的问题是:如果您想在提交过程中处理和更改文件内容,正确的方法是什么?

1
请参见提交日期:https://dev59.com/eo_ea4cB1Zd3GeqPM1VO - Ciro Santilli OurBigBook.com
2个回答

42
< p >通过 git commit 提交的实际更改是在 pre-commit 钩子完成后索引中的内容。这意味着您可以在 pre-commit 钩子中更改文件,只要您也将它们添加到 git add 中。

以下是我修改过的示例 pre-commit 钩子,从 .sample 修改而来:

#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# [snipped much of what used to be in it, added this --
#  make sure you take out the exec of git diff-index!]

num=$(cat zorg)
num=$(expr 0$num + 1)
echo $num > zorg
git add zorg
echo "updated zorg to $num"
exit 0

然后:

$ git commit -m dink
updated zorg to 3
[master 76eeefc] dink
 1 file changed, 1 insertion(+), 1 deletion(-)

但请注意一个小缺陷(不适用于您的情况):

$ git commit
git commit
updated zorg to 4
# On branch master
# Untracked files:
[snip]
nothing added to commit but untracked files present (use "git add" to track)
$ git commit
updated zorg to 5
# Please enter the commit message for your changes. Lines starting
[snip - I quit editor without changing anything]
Aborting commit due to empty commit message.
$ git commit
updated zorg to 6
# Please enter the commit message for your changes. Lines starting

基本上,因为预提交钩子会更新和添加代码(git add),即使我不进行实际提交,在这里文件仍然会不断增加。

[编辑 2021年8月:我需要强调我不建议使用这种方法。请注意,在使用 git commit -agit commit --includegit commit --only时,可能会出现一些奇怪的情况,包括在命令行中命名文件时插入的隐含--only。这是由于此类git commit创建第二个甚至第三个内部 Git 索引的原因。你在钩子中执行的任何git add操作只能影响其中之一的两个或三个索引文件中的一个。]


所以基本上这是一种不同的黑客方式,对吧?用我的方法进行修改后,文件已改变,但要等到下一个才提交。 - jjmerelo
1
是的。我不确定我会真的喜欢任何一种方法;我更愿意有像Makefile这样的东西来根据需要更新事物,以及更加手动化的东西。但它应该能够工作。 - torek
2
你可以在实际提交后,让你的脚本执行 commit -a --amend - Yawar
2
这对我来说似乎不是非常安全的。工作树与索引之间可能会发生变化,这并不罕见。如果您使用git add,您将向索引添加工作树中的任何状态,这是不可取的。 - tksfz
1
@toolforger:我在多个stackoverflow回答中详细地写了这个问题:这里这里,以及这里。这段代码在git commit内部已经发生了一些变化,而且细节没有被记录下来,因此可能会有所改变,所以不要过于依赖任何特定的行为。 - torek
显示剩余4条评论

19
原来在git add时间,你可以运行“hooks”(钩子) - 它们实际上由另一种机制处理:

https://git-scm.com/book/zh/v2/Git-属性定制-Git-属性#_关键字展开

(向下滚动一点,看看“smudge”和“clean”的图解)

以下是我的理解:

  1. 编辑 .gitattributes 文件,并为触发词典更新的文件创建规则:

    novel.txt filter=updateDict

  2. 然后,告诉 Git 在smudge(git checkout)和clean(git add)时应该如何处理 updateDict 过滤器:

    $ git config --global filter.updateDict.clean countWords.script

    $ git config --global filter.updateDict.smudge cat


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接