(如何)我可以从预提交挂钩中 运行 git 签出?
(How) can I run git checkout from within the pre-commit hook?
有一个文件应该在我们的 git 存储库中,以便它在任何检出中。
它可能 由用户更改,但通常不应重新签入更改。
--assume_unchanged 和 --skip_work_tree 都没有提供所需的灵活性,而且文件太笨重,无法合理地 'modified' 使用 smudge/clean 过滤器。
所以我编写了一个预提交挂钩,成功询问用户是否确定要提交对此文件的更改。如果他们说是,则文件被签入(挂钩 returns 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 状态报告 git 结帐没有效果? (正确 - 从挂钩返回 0 会导致文件被错误提交)
当给定路径时,默认情况下 checkout
从索引更新工作树(即从为提交准备的更改)。
你想要的是更新索引(大概来自 HEAD
,以便在这次提交时保持文件不变)。这可以用
来完成
git reset HEAD -- file/in/question
默认情况下,这会将工作树中的更改保留为未暂存的更改。这可能比同时还原索引和工作树更安全,但如果你真的想还原两者,你可以说
git checkout HEAD -- file/in/question
在任何时候,每个文件都有三个(!)副本(不包括添加或删除文件的特殊情况)。这也适用于您的特定文件。为了方便起见,我们称这个文件为 F
.
F
的一个副本在当前或 HEAD
提交中。此副本是只读的。
F
的一个副本在索引中,可以提交。此副本是可以更改的,但我们必须仔细查看谁会注意到任何更改以及何时。
F
的最终副本在工作树中。这个副本是可以更改的,尽管与索引副本一样,我们必须仔细查看谁会注意到任何更改以及何时。 (这里也值得一提,因为你评论过污迹和干净的过滤器,F
的工作树副本是唯一应用了污迹和行尾过滤的副本; 索引中的副本是干净的变体。)
如果 Git 将写入一个提交,它会使用当时的索引。
如果 git commit
是 运行 作为 git commit -a
或 git commit <path1> <path2> ... <pathN>
,Git 当前正在使用 临时 索引,将正常或“真实”索引放在一边。 (这可以稍后回来咬我们:Git 将在且仅当提交完成时更新 real 索引。)
您处于预提交挂钩中,这意味着您已经 运行 git commit
并且正在制作一个 yes/no 决定是否允许提交继续进行。
现在让我们把这些放在一起,观察一些问题。
如果您在预提交挂钩中进行 no 更改,则无需担心:您 return a go/stop 状态,并且 Git 继续从索引进行新提交(之后 HEAD
指向新提交),否则它不会。
如果您 进行更改,您将在索引and/or 工作树中进行更改。谁将在何时看到这些变化?
您实际要求的更改是 git checkout -- <em>F</em>
。这从索引复制到工作树。这对将要提交的内容没有影响。
您可以改用 git 重置 HEAD -- <em>F</em>
或 git 结帐 HEAD -- <em>F</em>
。这些将从当前提交复制到索引——如果我们使用的是真实索引,或者如果我们使用临时索引,则为临时索引。 checkout
表单也将从索引复制到工作树。
如果让提交继续完成,和 Git 正在使用临时索引,Git 将作为最后一步,将它添加到此临时索引(由于 -a
或 <path>
参数)的任何条目复制回真实索引;但如果它使用的是真实索引,则不需要更新真实索引。
在 Git 的某些(非常)旧版本中,Git 无法注意到预提交挂钩中对索引所做的更改(例如,它从不重新读取索引) .我已经太久无法准确记住这有什么影响,或者它影响了哪些 Git 版本,但是值得围绕这个做一些仔细的测试:我有点模糊的记忆是 Git 有提交-树代码内置在 C 代码中,通过不重新读取索引,它使用原始索引内容而不是新内容构建树,因此在预提交挂钩期间复制到索引中的文件实际上并没有进入提交。
在任何情况下,如果您更新索引中的文件,在工作树中也更新它可能是明智的,但请考虑对仔细安排不同[的人的影响=96=] 文件的版本比工作树中的版本。在这种情况下,您将覆盖精心准备的版本 和 工作树版本。
在一个不同但相关的极端情况下,我们应该注意何时 Git 确实将索引条目从临时索引复制回真实索引(在 commit -a
和 commit <path>
情况下),这也会清除所有精心设计的不同阶段的文件。也就是说,如果您这样做:
git add -p somefile
并小心地暂存一个版本,然后 运行 git commit somefile
提交当前工作树版本 首先 ,你失去了精心暂存的版本。这可能(或可能不会)建议您如何处理这个问题。特别是,如果要对暂存的内容和工作树中的内容进行任何更改,则让预提交挂钩完全拒绝使用临时索引可能很有用,只是为了避免意外。
有一个文件应该在我们的 git 存储库中,以便它在任何检出中。 它可能 由用户更改,但通常不应重新签入更改。 --assume_unchanged 和 --skip_work_tree 都没有提供所需的灵活性,而且文件太笨重,无法合理地 'modified' 使用 smudge/clean 过滤器。
所以我编写了一个预提交挂钩,成功询问用户是否确定要提交对此文件的更改。如果他们说是,则文件被签入(挂钩 returns 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 状态报告 git 结帐没有效果? (正确 - 从挂钩返回 0 会导致文件被错误提交)
当给定路径时,默认情况下 checkout
从索引更新工作树(即从为提交准备的更改)。
你想要的是更新索引(大概来自 HEAD
,以便在这次提交时保持文件不变)。这可以用
git reset HEAD -- file/in/question
默认情况下,这会将工作树中的更改保留为未暂存的更改。这可能比同时还原索引和工作树更安全,但如果你真的想还原两者,你可以说
git checkout HEAD -- file/in/question
在任何时候,每个文件都有三个(!)副本(不包括添加或删除文件的特殊情况)。这也适用于您的特定文件。为了方便起见,我们称这个文件为 F
.
F
的一个副本在当前或HEAD
提交中。此副本是只读的。F
的一个副本在索引中,可以提交。此副本是可以更改的,但我们必须仔细查看谁会注意到任何更改以及何时。F
的最终副本在工作树中。这个副本是可以更改的,尽管与索引副本一样,我们必须仔细查看谁会注意到任何更改以及何时。 (这里也值得一提,因为你评论过污迹和干净的过滤器,F
的工作树副本是唯一应用了污迹和行尾过滤的副本; 索引中的副本是干净的变体。)如果 Git 将写入一个提交,它会使用当时的索引。
如果
git commit
是 运行 作为git commit -a
或git commit <path1> <path2> ... <pathN>
,Git 当前正在使用 临时 索引,将正常或“真实”索引放在一边。 (这可以稍后回来咬我们:Git 将在且仅当提交完成时更新 real 索引。)您处于预提交挂钩中,这意味着您已经 运行
git commit
并且正在制作一个 yes/no 决定是否允许提交继续进行。
现在让我们把这些放在一起,观察一些问题。
如果您在预提交挂钩中进行 no 更改,则无需担心:您 return a go/stop 状态,并且 Git 继续从索引进行新提交(之后
HEAD
指向新提交),否则它不会。如果您 进行更改,您将在索引and/or 工作树中进行更改。谁将在何时看到这些变化?
您实际要求的更改是
git checkout -- <em>F</em>
。这从索引复制到工作树。这对将要提交的内容没有影响。您可以改用
git 重置 HEAD -- <em>F</em>
或git 结帐 HEAD -- <em>F</em>
。这些将从当前提交复制到索引——如果我们使用的是真实索引,或者如果我们使用临时索引,则为临时索引。checkout
表单也将从索引复制到工作树。如果让提交继续完成,和 Git 正在使用临时索引,Git 将作为最后一步,将它添加到此临时索引(由于
-a
或<path>
参数)的任何条目复制回真实索引;但如果它使用的是真实索引,则不需要更新真实索引。在 Git 的某些(非常)旧版本中,Git 无法注意到预提交挂钩中对索引所做的更改(例如,它从不重新读取索引) .我已经太久无法准确记住这有什么影响,或者它影响了哪些 Git 版本,但是值得围绕这个做一些仔细的测试:我有点模糊的记忆是 Git 有提交-树代码内置在 C 代码中,通过不重新读取索引,它使用原始索引内容而不是新内容构建树,因此在预提交挂钩期间复制到索引中的文件实际上并没有进入提交。
在任何情况下,如果您更新索引中的文件,在工作树中也更新它可能是明智的,但请考虑对仔细安排不同[的人的影响=96=] 文件的版本比工作树中的版本。在这种情况下,您将覆盖精心准备的版本 和 工作树版本。
在一个不同但相关的极端情况下,我们应该注意何时 Git 确实将索引条目从临时索引复制回真实索引(在 commit -a
和 commit <path>
情况下),这也会清除所有精心设计的不同阶段的文件。也就是说,如果您这样做:
git add -p somefile
并小心地暂存一个版本,然后 运行 git commit somefile
提交当前工作树版本 首先 ,你失去了精心暂存的版本。这可能(或可能不会)建议您如何处理这个问题。特别是,如果要对暂存的内容和工作树中的内容进行任何更改,则让预提交挂钩完全拒绝使用临时索引可能很有用,只是为了避免意外。