【问题标题】:Git: How to re-stage the staged files in a pre-commit hookGit:如何在预提交挂钩中重新暂存暂存文件
【发布时间】:2015-01-09 06:27:39
【问题描述】:

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

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

【问题讨论】:

标签: git githooks git-stage


【解决方案1】:

没有pre-commit hook 上下文,您可以使用以下命令get a list of the staged files

git diff --name-only --cached

所以如果你想重新索引暂存文件,你可以使用:

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

pre-commit hook 上下文中,您应该遵循 the advices of 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

【讨论】:

  • 最好使用git checkout-index 并避免git stash 的问题。
  • @CS 你能说得更具体点吗?究竟会出现哪些问题?
  • 对我不起作用...当我 stash 它还添加暂存文件。所以它正确地添加了更新的文件来提交,但是当我做stash pop时,它把以前的版本放回我的工作目录
【解决方案2】:

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

  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 移动到提交后挂钩,因为这是在提交之前用修改后的文件 (v. B) 覆盖暂存文件 (v. A) 的原因。实际上,您的脚本很可能不会失败,但即便如此,您在 pre-commit 挂钩中的 git stash pop 也会与您的脚本修改文件 (v . A') 和未暂存的修改 (v. B) 产生合并冲突。然后,这会阻止文件被提交,但是您确实修改了脚本最初暂存的文件(v.A')和未暂存的后暂存修改文件(v.B)(假设@987654325 可能不会丢失任何重要的历史记录@ 只做缩进之类的东西,所以 v.A 和 v.A' 几乎相同)。

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

【讨论】:

  • 这是对我的回答的一个很好的改进!感谢分享;)
【解决方案3】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 2019-05-06
    • 2019-01-17
    • 2016-07-05
    • 1970-01-01
    • 2011-04-12
    相关资源
    最近更新 更多