Git:如何在预提交钩子中重新暂存已暂存的文件

18 浏览
0 Comments

Git:如何在预提交钩子中重新暂存已暂存的文件

我正在编写一个Git pre-commit钩子。

这个脚本可以重新格式化一些代码,因此它可能会修改已经暂存的文件。

我该如何重新暂存所有已经暂存的文件?

0
0 Comments

问题的出现原因是在pre-commit hook上下文中,使用git stash命令无法正确处理已经暂存的更改。具体解决方法是使用git checkout-index命令替代git stash命令。

在pre-commit hook上下文中,应该按照David Winterbottom的建议,在进行任何其他操作之前,先暂存未暂存的更改。这样可以避免对未暂存的更改进行索引或更改的问题。因此,不需要对所有已暂存的文件进行重新暂存,只需要对所有已更新的文件进行重新暂存。

具体的操作步骤如下:

1. 暂存未暂存的更改:

git stash -q --keep-index

2. 在此处编辑项目文件:

...

3. 对已更新的文件进行重新暂存:

git add -u

4. 重新应用原始的未暂存的更改:

git stash pop -q

更好的做法是使用git checkout-index命令,避免使用git stash命令时遇到的问题。

某些情况下使用git stash命令时会将已暂存的更改也添加到stash中,即使使用了--keep-index选项。还有一些相关问题中也提到了这个问题。目前还没有找到解决这个问题的方法。

0
0 Comments

Git:如何在预提交钩子中重新暂存已暂存的文件

在David Winterbottom的引用文章中,我喜欢's的答案。然而,有一个 边缘 情况的关注在评论中提到,其中你将丢失一些提交历史。尽管如此,评论者所说的并不像他所说的那么糟糕,而且只是对于有问题的实践的一种边缘情况。它发生在以下情况下:

  1. 你暂存了一个文件(版本A)
  2. 在提交之前编辑了同一个文件(版本B)
  3. 希望提交最初暂存的文件(版本A),而不是修改后的文件(版本B)

如果你的提交失败,或者成功并在提交之前弹出存储区,你将丢失你最初暂存的文件(版本A),因为它从未被提交并被覆盖(使用版本B)。显然并不是灾难性的,你仍然拥有最新的编辑(版本B),但它可能会妨碍一些人的工作流程和(次优的)提交实践。为了避免这种情况,你只需要检查脚本的退出状态,并使用一些存储技巧恢复到原始状态(索引具有版本A,工作目录具有版本B)。

pre-commit

#!/bin/sh
... # 其他预提交任务
## 存储未暂存的更改,但保留当前索引
### 工作目录中的修改文件应该是索引的文件(版本A),其他文件为HEAD
### 存储区保存了原始状态的工作目录(版本B)
git stash save -q --keep-index "current wd"
## 编辑项目文件的脚本
### 这是编辑你最初暂存的文件版本(版本A),因为这是你的工作目录
###(将更改的文件称为版本A')
./your_script.sh
## 检查your_script.sh的退出错误;如果出现错误,则恢复到原始状态
##(索引具有版本A,工作目录具有版本B)
RESULT=$?
if [ $RESULT -ne 0 ]; then
git stash save -q "original index"
git stash apply -q --index stash@{1}
git stash drop -q; git stash drop -q
fi
[ $RESULT -ne 0 ] && exit 1
## 暂存your_script.sh修改后的文件(版本A')
git add -u

你还应该将 git stash pop 移动到后提交钩子中,因为这会在提交之前用修改的文件(版本B)覆盖暂存的文件(版本A)。在实践中,你的脚本很可能不会失败,但即使如此,在预提交钩子中使用git stash pop会在你的脚本修改文件(版本A')和你的未暂存修改(版本B)之间创建合并冲突。这将阻止文件被完全提交,但你仍然拥有你的脚本修改的最初暂存文件(版本A')和你的暂存后修改的文件(版本B)(假设your_script.sh只做一些诸如缩进之类的操作,所以版本A和版本A'基本上是相同的)。

总结:如果你遵循最佳实践,在修改之前提交已暂存的文件,原始答案是最简单和最好的。如果你有我认为不好的习惯,不这样做并且希望在你的历史记录中同时保留两个版本(已暂存和修改后的),你需要小心(这是为什么这是一个不好的实践的论点)!无论如何,这可能是一个可能的安全网。

这是对我的答案的一个很好的改进!谢谢分享 😉

0
0 Comments

问题的原因是:在提交前的钩子中重新暂存已暂存的文件,会导致合并冲突。

解决方法是:判断是否存在部分暂存的文件,如果存在,则中止操作并提示用户修复部分暂存的文件。另外,可以使用"lint-staged"工具来处理这个问题。

以下是整理后的文章:

很遗憾,上面的答案似乎还不太够。这是我看到的最接近的答案,但是当你在提交后的钩子中弹出储藏时,仍然会引入合并冲突。这是因为储藏的内容(即使使用了--keep-index标记)既包括未暂存的更改(我们想要的),也包括在运行自动格式化程序之前已经暂存的更改(我们不想要的)。这将在已提交的自动格式化更改与原始的尚未格式化的储藏更改之间创建合并冲突。据我所知,没有简单的方法告诉git仅储藏未暂存的更改。--keep-index标记会储藏未暂存的更改和已暂存的更改,同时保留已暂存的更改。这与默认行为不同,因为通常已暂存的更改也会与未暂存的更改一起储藏。但它并不仅仅储藏未暂存的更改,而这实际上是我们所需要的。

我很希望我对此是错误的,但我相当确定在bash中没有快速实现的解决方案。像任何问题一样,当然是可以解决的,但需要相当多的工作。实际上,lint-staged可以很好地处理这个问题,但需要付出一些努力。他们在这里介绍了这个功能,并在这里讨论了相关问题。即使在这个工作中,仍然存在一些极端情况,他们明确失败并重置工作目录到其原始状态。他们不能始终保证不引入冲突。

我的结论是:如果你在处理JavaScript项目,请使用lint-staged。如果你像我一样,真的想使用一个简单的bash脚本,那么在执行任何其他操作之前,检查是否存在部分暂存的文件,并中止操作并提示用户修复部分暂存的文件。在像lefthook引入这个功能之前(问题在这里),你的其他选项都相当麻烦。

但我很希望有人能证明我是错的。

0