有一个文件应该在我们的git存储库中,以便它可以在任何结帐中。 用户可以 更改,但通常不应该重新检入更改。 --assume_unchanged和--skip_work_tree都没有提供所需的灵活性,而且文件太麻烦,不能用涂抹/清洁过滤器合理地“修改”。
所以我编写了一个预提交挂钩,成功地询问用户是否确定要将更改提交到此文件。如果他们说是,则检入文件(钩子返回0,提交继续),否则,提交被中止。
我不想中止,而是让用户可以选择还原对文件的更改并继续提交。
要将文件还原为未更改状态,我正在使用git checkout -- file/in/question
。
鉴于该文件已被修改并暂存以进行提交,我运行以下预提交钩子:
#!/bin/bash
echo "git checkout -- file/in/question"
git checkout -- file/in/question
echo "git status"
git status
exit 1 #would be 0 if the hook worked as expected
我得到以下输出:
git checkout -- file/in/question
git status
On branch blah
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: file/in/question
为什么git status报告git checkout没有效果? (它是正确的 - 从钩子返回0导致文件被错误地提交)
答案 0 :(得分:3)
当给定路径时,默认情况下checkout
会更新索引中的工作树(即来自提交的更改)。
你想要的是更新索引(大概来自HEAD
,以便通过此提交保持文件不变)。这可以用
git reset HEAD -- file/in/question
默认情况下,这会将工作树中的更改保留为未分阶段的更改。这可能比恢复索引和工作树更安全,但如果你真的想要还原两者,你可以说
git checkout HEAD -- file/in/question
答案 1 :(得分:3)
在任何时候,每个文件都有三个(!)副本(不包括添加或删除文件的特殊情况)。这也适用于您的特定文件。为方便起见,我们将此文件称为 F
。
F
的一个副本位于当前或HEAD
提交中。此副本为只读。
F
的一个副本位于索引中,可以提交。此副本是可更改的,但我们必须小心查看谁会注意到任何更改以及何时更改。
F
的最终副本位于工作树中。此副本是可更改的,但与索引副本一样,我们必须小心查看谁将注意到任何更改以及何时更改。 (这里也值得一提,因为你评论了涂抹和干净的过滤器, F
的工作树副本是唯一一个带有污迹和行结束过滤的副本已应用;索引中的副本是干净的变体。)
如果Git会写一个提交,那么它会使用当时的索引。
如果git commit
作为git commit -a
或git commit <path1> <path2> ... <pathN>
运行,Git当前正在使用临时索引,正常或&#34;真实&#34;指数放在一边。 (这可以回过头来讨论我们:当且仅当提交完成时,Git才会更新真实的索引。)
您处于预提交挂钩状态,这意味着您已经 git commit
运行了,并且是否允许提交继续进行是/否决定
现在让我们把这些全部放在一起并观察一些问题。
如果您在预提交挂钩中进行了 no 更改,则无需担心:您返回一个go / stop状态,并且Git继续从index(在HEAD
指向新提交之后),或者不是。
如果执行进行更改,您将在索引和/或工作树中进行更改。谁会看到这些变化以及什么时候?
您实际要求的更改是git checkout -- F
。这将从索引复制到工作树。这对将要提交的内容没有影响。
您可以使用git reset HEAD -- F
或git checkout HEAD -- F
。这些将从当前提交复制到索引 - 如果是我们正在使用的那个,则为真实索引,如果我们使用临时索引,则为临时索引。 checkout
表单也将从索引复制到工作树。
如果让提交继续完成,和 Git正在使用临时索引,Git将作为最后一步复制它添加到此临时索引的任何条目(由于-a
或<path>
参数)回到真实索引;但如果它使用真实索引,则不需要更新真实索引。
在一些(非常)旧版本的Git中,Git没有注意到在预提交钩子中对索引所做的更改(因为,它从不重新读取索引)。我已经太长时间没有记住它具有什么效果,或者它影响的Git版本,但值得对此进行一些仔细的测试:我有点模糊的记忆是Git有承诺 - 在C代码中内置的树代码,并且不重新读取它使用原始索引内容而不是新内容构建树的索引,因此在预提交挂钩期间复制到索引中的文件实际上并没有进入提交。
在任何情况下,如果您更新索引中的文件,在工作树中更新它可能也是明智之举,但请考虑对经过精心设置不同<的人的影响/ em>文件的版本比工作树中的版本。在这种情况下,您将覆盖精心设计的版本和工作树版本。
在一个不同但相关的极端情况下,我们应该注意当Git将索引条目从临时索引复制回真实索引时(在commit -a
和commit <path>
情况下),这也是消除任何仔细不同的阶段文件。也就是说,如果你这样做:
git add -p somefile
并小心地暂存一个版本,然后运行git commit somefile
以提交当前工作树版本 ,您将失去精心上演的版本。这可能(或可能不)建议你如何处理这个问题。特别是,如果要对暂存的内容和工作树中的内容进行任何更改,那么让pre-commit hook flat out拒绝使用临时索引会很有用,只是为了避免意外。