22

我正在写一个 git pre-commit 钩子。
该脚本可以重新格式化一些代码,因此它可以修改暂存文件。

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

4

3 回答 3

19

如果没有pre-commit hook上下文,您可以使用以下命令获取暂存文件的列表:

git diff --name-only --cached

因此,如果要重新索引暂存文件,可以使用:

git diff --name-only --cached | xargs -l git add

pre-commit hook上下文中,您应该遵循David Winterbottom 的建议,并在其他任何事情之前存储未分级的更改。

这种技术使您不必担心索引或更改未暂存的更改。因此,您不必暂存所有暂存文件,而是所有更新的文件:

# Stash unstaged changes
git stash -q --keep-index

# Edit your project files here
...

# Stage updated files
git add -u

# Re-apply original unstaged changes
git stash pop -q
于 2014-11-13T14:24:15.947 回答
14

我喜欢@tzi 的回答;但是,在 David Winterbottom 引用的文章中,在评论中提出了一个边缘案例问题,您将丢失一些提交历史记录。不过,这并不像评论者所说的那样悲观,而且对于那些有问题的做法的人来说,这又是一个边缘案例。它发生在

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

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

预提交

#!/bin/sh

... # other pre-commit tasks

## Stash unstaged changes, but keep the current index
### Modified files in WD should be those of INDEX (v. A), everything else HEAD
### Stashed was the WD of the original state (v. B)

git stash save -q --keep-index "current wd"

## script for editing project files
### This is editing your original staged files version (v. A), since this is your WD 
### (call changed files v. A')

./your_script.sh

## Check for exit errors of your_script.sh; on errors revert to original state 
## (index has v. A and WD has v. 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

## Stage your_script.sh modified files (v. A')

git add -u

您还应该将 移动git stash pop到 post-commit 钩子,因为这是在提交之前用修改后的文件 (v. B) 覆盖暂存文件 (v. A) 的原因。实际上,您的脚本很可能不会失败,但即便如此,您git stash pop在 pre-commit 钩子中也会与您的脚本修改文件 (v.A') 和未暂存的修改 (v.B) 产生合并冲突。然后,这会阻止文件被提交,但是您确实修改了脚本最初的暂存文件(v.A')和未暂存的后暂存修改文件(v.B)(可以说不会丢失任何重要的历史记录,假设your_script.sh仅诸如缩进这样的东西 v. A 和 v. A' 几乎相同)。

总结:如果您使用最佳实践并在再次修改它们之前提交暂存文件,那么原始答案是最简单且很棒的。在我看来,如果你有不这样做的坏习惯,并且想要在你的历史中同时使用两个版本(分阶段和修改),你需要小心(为什么这是一种不好的做法的论据)!无论如何,这可能是一个安全网。

于 2016-09-15T22:28:38.597 回答
0

可悲的是,我认为上面@NearHuscarl 的回答并没有完全削减它。这是我见过的最接近的,但是当您在提交后挂钩中弹出存储时,您仍然会引入合并冲突。这是因为隐藏的内容(即使带有--keep-index标志)既是未暂存的更改(我们想要的),也是在您对它们运行自动格式化程序之前的暂存更改(我们不想要)。这将在已提交的自动格式化更改和原始尚未格式化的隐藏更改之间产生合并冲突。据我了解,没有简单的方法可以告诉 git 仅存储未分级的更改。这--keep-indexflag 存储未暂存的更改和暂存的更改,同时保留暂存的更改。这与默认行为的不同之处在于,分阶段的更改通常也会与未分阶段的更改一起隐藏起来。但它并没有专门存储未暂存的更改,这确实是我们需要的。

我很想在这方面犯错,但我很确定在 bash 中没有快速实施解决方案。像任何问题一样,它当然是可以解决的,但需要大量的跑腿工作。lint-staged实际上非常优雅地处理了这个问题,但并非没有投入工作。是他们介绍此功能的 PR,这有关该问题的相应讨论。即使完成了所有这些工作,仍然存在一些边缘情况,即它们明确地使钩子失败并将 WD 重置为其原始状态。他们根本不能总是保证他们不会引入冲突。

我的收获是:如果您正在处理一个 javascript 项目,请使用lint-staged. 如果您像我一样,并且真的想坚持使用简单的 bash 脚本,那么在执行任何其他操作之前检查是否有任何部分暂存的文件并通过一条消息中止告诉用户修复他们的部分暂存文件可能是值得的。在lefthook引入此功能(在此处发布)之前,您的其他选项都非常令人发指。

但我希望有人能证明我错了。

于 2021-02-21T13:09:35.630 回答