有一种(有点)方法可以做到。我不建议这样做,但如果你真的想要,可以按照以下步骤进行。
首先,你需要将现有的两个项目分开:
- 暂存更改
- 未暂存的工作树项目
此外,你希望第一组可用于重新格式化。
可以使用
git stash
完成这个操作,就像我在
如何在提交前钩子中正确地git stash/pop以获取干净的工作树进行测试?的答案中展示的那样(注意其中关于git stash中的错误警告)。
基本上,你希望在脚本中运行测试的那个点。
status=...
一旦进入这个状态,您可以通过格式化运行工作树项目,并在预提交钩子中使用
git add
结果(就像您已经发现的那样)。这将避免格式化“another”块,因为它不在工作目录版本中。然后您可以让提交继续进行(即脚本的其余部分在此处不适用)。
问题现在是从备份中恢复工作树版本。由于您修改了索引,所以即使提交完成后也无法回到此版本:
# Restore changes
git reset --hard -q && git stash apply --index -q && git stash drop -q
相反,你想要的是找到存储的索引(
stash^1
)和存储的工作树(
stash
)之间的差异,并将其应用于新的
HEAD
提交。有至少两种方法可以做到这一点,而不使用git plumbing命令。由于提交版本的重新格式化,两者都可能导致冲突:
git diff stash^1 stash | git apply --reject
(最终要使用git stash drop
)
git stash branch tempbranch; git commit -m for-cherry-pick; git checkout prev-branch; git cherry-pick -n tempbranch; git branch -D tempbranch
方法1更简单但混乱,因为会将可能导致合并冲突的更改放入“拒绝”文件中。方法2使用合并机制,因此如果需要,更改会获得冲突标记。如果没有冲突,则
-n
会阻止提交,以便您可以使用真正的消息自己进行提交,而不是复制虚拟的
for-cherry-pick
消息。
当然我没有测试过这些方法。此外,还有一些不使用git stash
的方法来完成这个任务,例如将git add
的文件的索引版本检出到一个单独的目录中进行格式化,然后添加格式化后的版本,以便整个过程不会影响当前的工作目录。如果你真的决心要这样做,这可能更好。以下是一个脚本用于实现那种方法(也没有经过真正的测试——它需要加入一些健壮性,例如使用-z
和xargs -0
来处理包含空格的文件名,在checkout diff-index输出部分):
WORKDIR=$(mktemp -d -t reformat) || exit 1
trap "rm -rf $WORKDIR 0 1 2 3 15"
git --work-tree=$WORKDIR checkout -- \
$(git diff-index --cached --name-only --no-renames --diff-filter=AM HEAD)
(cd $WORKDIR; ...)
git --work-tree=$WORKDIR add --ignore-removal .
这里我建议的做法是:不要在 pre-commit hook 中对代码进行格式化,而是检查 它是否已经 格式化。如果已经格式化了,就允许提交。如果没有格式化,则拒绝提交。这更符合 pre-commit hook 的精神,并且可以使用 其他答案中的脚本。基本上,在它说的那个地方:
status=...
你只需运行一个检查格式化器是否会有任何更改的程序(例如,允许格式化器执行其操作,并查看结果是否与要提交的索引中的内容不同)。这样就可以获得状态。然后按照脚本中的其余部分进行操作,使用"git reset --hard -q && git stash apply --index -q && git stash drop -q"将所有内容恢复到创建存储时的状态。
indent
程序将“真实”文件进行管道传输。这是一个解决方案,但我不太喜欢它。我希望你能理解我认为缺少的东西:由indent
程序编辑的行在存储库中被更改,但在工作区中没有更改。如果我通过indent
传输真实文件,我应该得到与预期相同的结果。 - musicmatzeadd --patch
选项损坏的备份,你不能真正期望以那种方式捕获它,如果没有这个,我会和@torek一样:“而不是在pre-commit钩子中格式化代码,在那个点上只需检查它是否被格式化。如果是这样,请允许提交。” - jthill