Git pre-commit hook:使用 -a 标志提交时如何获取添加/修改的文件

2023-12-11

当我使用 git commit -a 提交我的工作时,预提交挂钩中的“git diff --diff-filter=ACM --name-only --cached”无法获取 git 将添加的文件。那么对于这种情况,正确的解决方案是什么。


这里的问题是git commit -a本身。你最好的选择是不要使用-a选项。单独添加文件,然后运行git commit.如果您想修复挂钩,请继续阅读。

提交是如何运作的

编写 Git 钩子(嗯,至少是其中一些钩子)的人需要意识到 Git 从索引构建提交的事实。但是这个说法——Git 构建提交来自the索引——有点谎言,或者至少是不完整的。 Git 构建提交来自an index.

如果你跑git commit without使用三个特定选项中的任何一个,都只有一个索引。该指数是the索引,所以任何假设 Git 正在工作的人the索引得到了他们期望的行为,并且完全幼稚的预提交挂钩表现良好:有时甚至不需要知道 Git 将提交索引中的内容,而不是用户工作树中的内容。但有三个提交选项可以更改此行为:

  • git commit -a:这很像git add -u && git commit,除了为了确保git add -u没有效果if提交fails(被预提交钩子拒绝,或者被用户中止),Git 必须创建一个临时索引.

  • git commit --include paths: 这类似于git commit -a除了添加的文件不是由git add -u而是指定的路径。

  • git commit --only paths: 这是最糟糕的情况。注意git commit paths,两者都没有--include nor --only,与使用具有相同的效果--only。对于这种特殊情况,Git 不仅必须创建一个,而且还必须创建一个two临时索引文件。

All of this follows from the basic idea of the index. Git's index holds, at all times,1 your proposed next commit. That is, what's in the index is the set of files that should be included in the next commit. When you run git commit with no options, you're asking Git to commit the proposed next commit. So the index has the right stuff in it.

但当你跑步时git commit -a, git commit --include (-i简称),或git commit --only (-o简而言之),你是在说:采取当前建议的下一次提交,对其进行一些更改,然后尝试提交。如果这个动作succeeds,新索引(添加了额外更改的索引)应该是更新后的索引。但如果这个动作fails, Git 希望将索引恢复到原来的样子,使用no所做的改变。

To achieve this, Git keeps the original index file intact and creates one or two new index files.2 If you're using git commit -a or git commit -i, we need one extra index: Git copies the main index to the temporary index, then uses git add, or the internal equivalent, to update the temporary index. This temporary index is named index.lock and this file is used to prevent an additional git commit command from running while this git commit command runs, so even a plain git commit with no options creates an index.lock file: it's just that with the plain commit, the index.lock file contents will match the index file contents.

So, for git commit or git commit -a or git commit -i,可以只使用index.lock文件作为“the”索引,并从中获取正确的内容。当然,如果您要在编写的预提交挂钩中执行此操作,您需要弄清楚 Git 是否正在使用standard首先是索引:如脚注2所示,添加的工作树使用不同的标准索引,因此它具有不同的标准index.lock.


1This isn't quite right, because sometimes the index gets expanded to hold entries that are not "stage zero". This is the case during conflicted merges. ("Merge" here includes cherry-pick and revert as well: anything that invokes Git's internal merge engine.) However, even during this expanded operation, the index does still hold the proposed next commit. It's just that there are parts of the index that can't be committed—that require resolving—and these get in the way of doing the commit. Resolving the conflicted entries removes the nonzero stage entries, either replacing them with a single resolved stage-zero entry, or just removing them if the file shouldn't be committed after all.

索引内容可见git ls-files --stage:有一个包含正斜杠的路径名,例如src/somefile.ext, a mode—one of 100644 or 100755对于普通文件;另外两种模式是为符号链接和 gitlinks 以及哈希 ID 保留的。还有一个阶段数,它必须为零才能使索引可提交。任何阶段 1、2 或 3 条目都表明存在合并冲突,通过读取该槽即可获取冲突文件:请参阅the git checkout-index command.

2The index is, or mostly-is, just an ordinary file: the usual file is .git/index. In secondary work-trees resulting from git worktree add, the usual file is in a subdirectory within the .git top level directory. You can, however, override this index with your own temporary index, using the environment variable GIT_INDEX_FILE. Various Git shell scripts use this technique. For instance, when git stash was a shell script, it did this. And of course, git commit uses this same concept to create these other extra temporary index files.


git commit --only是最难的情况

For git commit --only,两个索引文件是不够的。我们会需要three这样的文件。原因如下。的功能git commit --only is to:

  1. 阅读当前提交 (HEAD) 到临时索引中。
  2. 更新该临时索引以添加指定的文件。
  3. 尝试将此索引转换为新的提交。

步骤3有成功案例和失败案例。失败案例比较简单,我们先看一下:

  • 失败时,Git 应该返回到当前建议的下一次提交作为建议的下一次提交。所以这意味着我们需要保留现有的索引。

  • On success然而,Git 应该提出一个新的提议下一次提交。这个新提议的下一次提交应包括当前提议的下一次提交(即当前索引)更新就像git add指定文件的.

为了为步骤 3 的成功做好准备,步骤 1 和 2 应改为这样:

  1. 准备两个临时索引文件:通过复制建立索引AHEAD到索引文件中,并通过复制现有索引文件来设置索引 B。
  2. 更新索引 Agit add-ing 指定的文件,并更新索引 Bgit add-ing 指定的文件。

步骤 3 现在已简化:

  1. 使用索引 A 进行提交。如果成功,则用索引 B 替换标准索引。如果失败,则删除两个临时索引文件。

Git 如何使用锁定文件

Git has a bunch of code paths that would like to make some sort of atomic change to a single file.3 That's where this index.lock stuff comes from. On a POSIX system, there's no particularly good way to lock a file for a particular transaction, but we can approximate it in various ways.

一种方法非常简单:使用原子文件创建 (O_CREAT|O_EXCL in open系统调用)以确保只有一个进程可以create名称结尾为的文件.lock。例如,如果我们想锁定名为index,我们自动创建一个名为index.lock。如果创建成功,我们现在就拥有了锁,并且可以复制现有的index文件到新的index.lock文件,对文件进行任何必要的更改,然后将其写出。

我们现在可以:

  • 原子地update索引文件,释放锁定文件,带有rename系统调用:rename("index.lock", "index")将完全取代旧的index文件与当前index.lock文件并成功,删除index.lock文件在此过程中,否则将失败并留下两者index and index.lock不受打扰。 (如果失败,我们将继续中止事务;见下文。)

  • 或者,我们可以release文件上的锁定,通过简单地删除锁定文件来故意中止我们的事务(unlink("index.lock"))。现有的index文件保持不变。

请注意该技术如何无缝地完成这两个任务git commit and git commit -a / git commit -i。这两个操作之间的关键区别完全由我们放入什么内容index.lock。对于一个普通的git commit, both index and index.lock包含相同内容. For git commit -a or git commit -i, index包含旧内容,并且index.lock包含新的更新内容。

We can create the lock file, update it if appropriate, attempt the commit, and then either finish the transaction by renaming, or roll back the transaction by unlinking the lock file. This is all very straightforward and easy.4

硬壳是git commit -o: the --only选项需要two临时索引文件。我们离开index独自一人,创造index.lock使用一组内容(索引 B,因为它是我们希望通过重命名操作实现的内容)并创建我们的内容third索引,索引 A,用于提交过程的持续时间。我们读HEAD进入索引 A,更新索引文件 A 和 B,尝试使用索引 A 提交,remove索引 A,然后要么完成索引 B 的事务,要么像以前一样回滚。这不太简单,但显然它确实有效。


3I linked here to the Wikipedia page on atomicity in databases, as that's the concept Git is attempting to achieve here: an atomic transaction. Real database software might benefit Git here; the stuff it does is kind of crude. However, real database software is (a) hard and (b) slow. Git attempts a sort of have-your-cake-and-eat-it-too here. It mostly succeeds: there are real tradeoffs here and Git manages most of them pretty well. They're breaking down now in various cases, though, and work here is ongoing.

4"Easy" here means only multiple dozens of lines of C code. If Git were written in a higher level language, it really would be relatively easy, though.


编写一个处理所有这些情况的预提交钩子

在这里,你简直有麻烦了。随着git commit --only在这种情况下,将要提交的内容位于索引 A 中。但是您可以获取其路径的两个文件know是原始索引($GIT_INDEX_FILE,如果已设置,或者.git/index或适当的工作树索引)和索引 B(与之前相同的文件加上.lock后缀)。

You can判断是否存在至少两个不同的索引文件。如果是这样的话,我们正在做git commit -a, git commit -i, or git commit -o。这将告诉您无法可靠地处理此问题,并且您可以让预提交挂钩中止并告诉用户不要这样做。

由于这些都没有记录在案,因此没有official方法来做到这一点,但一些现有的预提交挂钩使用这种技术:

if [ $GIT_INDEX_FILE != ".git/index" ]; then
    echo "Error: non-default index file is being used (GIT_INDEX_FILE is set)." >&2
    ...
    exit 1
fi

不过,这有一个恼人的副作用,即拒绝添加的工作树的提交。要修复它,如果你的 Git 足够新,可以git rev-parse --git-path,替换任何硬编码的.git/index字符串:

git rev-parse --git-path index

正如您所观察到的,某些版本的 Git 不会创建index.lock当没有必要时。这就是依赖未记录行为的问题:它可能在您现在安装的 Git 版本中工作,然后在升级到较新版本的 Git 时中断。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Git pre-commit hook:使用 -a 标志提交时如何获取添加/修改的文件 的相关文章

  • git,Heroku:预接收挂钩被拒绝

    我正在设置 git 存储库并尝试将其链接到 Heroku 当我运行命令时 git push heroku master 我收到 Counting objects 7 done Delta compression using up to 2
  • 哪种 git commit 实践更好?

    我坚信 对一个问题做出一次承诺是一种很好的做法 我确信我在 最佳实践 之类的文章中读过它 因此 我的工作流程如下 对于新问题 我创建了一个新的本地分支git checkout b new issue 将所有更改提交到其中 有时这涉及到lot
  • Git 提交到公共子模块(主分支)

    我有两个或更多项目 我们称它们为Foo项目 and 项目栏 有一些通用代码我放入了一个子模块 我的理解是 如果我从内部提交对子模块的更改Foo项目它将在一个超然的头脑中 只有所有Foo项目克隆人可以看到 master cd ProjectF
  • 全局 Git 忽略

    我想将 Git 设置为全局忽略某些文件 我添加了一个 gitignore文件到我的主目录 Users me 并且我添加了以下行 tmproj 但它并没有忽略这种类型的文件 知道我做错了什么吗 您需要设置您的全局core excludesfi
  • 如何让 Git diff 忽略版本号更改?

    有没有办法从 Git diff 中消除版本号变化噪音像这个 https github com keith Xcode app strings commit 9502c601cad31ef452c4cd6e8ece3982345fba1d 具
  • 如何配置“git diff”以使用 emacs diff [重复]

    这个问题在这里已经有答案了 是否可以配置 git diff 以使用 emacs diff 如果是的话 你能告诉我怎么做吗 谢谢 看这个问题 如何使用视觉差异程序查看 git diff 输出 https stackoverflow com q
  • 版本控制 rpm 规范文件

    我正在配置一台构建机器来为大量非常相似的项目生成 rpm 每个项目的规范文件在名称上以及有时在依赖项和其他属性上都略有不同 每个项目都有自己的 git 存储库 其中包含项目文件 这些规范文件仅存在并且实际上仅对构建系统本身有用 每个项目都可
  • 改进 IntelliJ 注释(git Blame)

    我想改进 注释选项 右键单击代码行号冒号 gt 注释 它类似于 gitblame 但集成到 IntelliJ 1 这可能吗 通过插件或 IntelliJ 文件的原始修改 还有其他什么吗 2 我使用的是 Android Studio 基于 I
  • git pull:无法快进,

    你能帮我解决我的问题吗 当我尝试从 dev 分支拉取时发生错误 我浏览了解决方案 它说我需要 rebase 但它对我来说没有成功 CT aohc MP1GYWQA MINGW64 c TCO source RAPMD Web Fronten
  • 仅当除 master 之外的所有分支都有推送的 TAG 时,Gitlab 才会触发 Pipeline

    我的 YAML 文件必须如何配置 以便仅在推送标签时触发一种情况 作业 构建 的管道 该标签可能位于除 master 之外的所有分支中 对于主案例 我有一个单独的工作 build master yaml 文件 问题 如果主分支获得标签 管道
  • Git fetch 在 for-each-ref 中不显示远程分支

    我有一个远程分支feature test2 我想把它拿过来 我跑 git fetch origin feature test2 我得到 From
  • Emacs shell:保存提交消息

    我几天前开始使用 emacs 在 emacs shell M x shell 中使用 git 时遇到问题 当我 git commit 或 git commit amend 时 它会打开 vim 来编辑并保存提交消息 我对此表示同意 但我找不
  • Git 粒度——解决一行内的差异

    git 基于行的粒度或 diff 粒度是否可以增加到单词 字母分辨率 每行多条语句或使用 git 编写纯文本是值得的 根据评论重新阅读问题时 我想我明白了您最初的意思 所以我将给出一个真正的答案 与伊斯梅尔 巴达维的一行评论 https s
  • Git 和外部备份。子目录是空的,我宁愿它不是空的。

    我想使用 Git 创建外部备份系统 这部分是受到答案的启发在外部磁盘上备份整个 git 存储库的最佳方法是什么 https stackoverflow com a 4372855 2533127 其中建议如下 您还可以通过启动一个新的存储库
  • dulwich - 从远程仓库身份验证克隆

    我找不到有关此主题的任何资源 我需要通过提供用户名和密码从私有存储库进行克隆 然而 当它们作为关键字参数提供给 dulwich get client from path 时 会出现错误 提示 未知参数 用户名 这似乎是一件简单的事情 但我找
  • git 列出所有可用命令

    有没有命令可以显示 GIT 中所有可用命令的列表 有git help但它显示 usage git version exec path
  • Egit 拒绝接受 id_rsa

    我是第一次尝试在 Eclipse 中设置 egit 的 git 用户 这样我就可以继续通过 Eclipse 轻松编码 问题是 每次我尝试通过 egit 克隆存储库时 都会出现错误 无法列出可用分支 原因 ssh 电子邮件受保护 cdn cg
  • AngularJs:显示来自 git 或 Bower 的版本号

    我创建了一个 Angular 应用程序 我想在屏幕上显示应用程序的当前版本号 目前我已将其实现为常量 application constant constants VERSION 1 1 2 但这需要我在每个新版本上更新常量 我使用 Bow
  • 获得干净的 git 沙箱的最有效方法是什么?

    对于我的持续集成构建 我想确保我的 git 沙箱中没有存放任何杂散文件 并且没有任何文件被无意中更改 我知道关于git reset hard HEAD 这解决了部分问题 但我想我想做的是删除所有未跟踪和忽略的文件 我可以用蛮力的方式做到这一
  • 如何在 git merge 提交中列出冲突的文件(父级都有更改的文件)?

    qgit 有一个很好的选择 可以在合并提交中查看 有趣 的文件 其中有趣的文件被定义为在两个父文件中都有更改的文件 查看此类文件的相应命令行是什么 git show name status SHA1 of merge 将向您显示提交消息和在

随机推荐

  • Java 同步和可重入锁定

    当我们同步一个对象时 这是一个可重入锁吗 同步锁和可重入锁之间有真正的区别吗 亲切的问候 是的 锁定synchronized关键字是可重入的 但它们之间的实现可能有所不同 例如 在 JVM 的早期版本中 ReentrantLock的实现比s
  • 如果 knit root.dir 更改,knitr::include_graphics() 无法找到文件

    knitr允许您通过更改来更改评估代码块的目录root dir option r setup include FALSE knitr opts knit set root dir Project 这也可以在 RStudio 的全局选项中更改
  • 文本视图行 - 建议

  • 如何更改控制器中的 $model->attributes 值 - Yii

    用户主控制器代码 public function actionUpdate id model this gt loadModel id if isset POST UserMaster model gt attributes POST Us
  • arm-linux-androideabi-g++:-fuse-linker-plugin,但找不到 liblto_plugin.so

    我在ubuntu 12 04下编译Chrome V8时遇到一个问题是 arm linux androideabi g 致命错误 fuse linker plugin 但找不到 liblto plugin so ndk版本是r8b 我怎么解决
  • 了解使用 Photoshop 生成的 24 位 PNG

    具有透明度的 24 位 png 文件 可以使用以下命令生成Photoshop 真的有 24 位分布在每种颜色加上 alpha 上吗 或者 24 位仅指颜色并忽略 alpha RGBA 8888 有没有工具可以检查 PNG 文件并验证此类信息
  • 具有多个图像的 Pod

    创建一个名为 xyz 的 pod 其中包含一个容器 用于在其中运行以下每个映像 指定的映像可能在 1 到 4 个之间 nginx redis Memcached consul 问题不太清楚 但假设您希望一个 Pod 具有多个容器 下面是可以
  • 错误:结果不是以下位置的数据框:

    我正在尝试在相当大的数据框上运行拟合函数 该数据框由名为的变量分组 big group and small group 特别是 我试图获得每个的预测和 coefs 值small group代替big group 也就是说 我试图将这些新列添
  • 有没有什么好的方法来加密C#桌面应用程序[重复]

    这个问题在这里已经有答案了 可能的重复 保护 NET 代码免遭逆向工程 我们只是用C winforms开发一个应用程序 有什么好的加密方法可以帮助我们防止盗版吗 我看到有些软件可能需要硬件支持来保护其软件 如何实现 提前致谢 好吧 你在这里
  • 不读取模型[关闭]

    Closed 这个问题需要调试细节 目前不接受答案 我正在用Python编写一个程序 我想连接GPT4ALL 以便该程序像GPT聊天一样工作 仅在我的编程环境中本地运行 为此 我已经安装了 GPT4All 13B snoozy ggmlv3
  • 在 javascript 警报中编写 php

    我用以下方式在JS中编写PHP alert echo Error login 关联一个xml 用symfony翻译成两种语言 但现在不起作用 我该如何解决 您缺少引号alert call alert
  • Ruby on Rails - 将模型中的字段添加到另一个模型的表单上

    我有两个型号Contract and Addendum 合同has many addendums和附录belongs to contract 创建新合同时 将自动创建新的附录 但需要一些额外的元素来创建新的附录 如何添加字段value 这是
  • Pandas 中的顺序组内枚举

    假设我有以下数据框 date A B C D 0 2014 03 20 1 561714 0 979202 0 454935 0 629215 1 2014 03 20 0 390851 0 045697 1 683257 0 771027
  • 将引用(工具>引用)与 VBA 代码(宏)连接

    我想使用 VBA 代码以编程方式将一些引用连接到我的 VBA 项目 即无需使用 工具 gt 引用 手动设置引用 这可能吗 例如 Microsoft Office 12 0 对象库 您没有提到 Office 应用程序 在 MS Access
  • 使用 malloc 时出错

    I pass char input from main to processInExp 函数 然后我再次传递它processInExp 功能为getInput 函数在读取文件时动态分配它 Inside getInput 功能input检查时
  • 为什么 ASP.NET 网站没有 Designer.cs?

    如果我们在 Visual Studio 中创建 ASP NET Web 应用程序 我们可以看到每个 aspx 文件都会有一个关联的自动生成的 aspx designer cs 文件 但对于 ASP NET 网站 每个 aspx 文件只有一个
  • 如何屏蔽具有 RepeatVector() 层的 LSTM 自动编码器中的输入?

    我一直在尝试使用 LSTM 自动编码器获取向量序列的向量表示 以便我可以使用 SVM 或其他此类监督算法对序列进行分类 数据量阻止我使用完全连接的密集层进行分类 我的输入的最短大小是 7 个时间步长 最长的序列是 356 个时间步长 因此
  • _vscprintf 在 Android 上等效吗?

    vscprintf在 Android 上不可用 还vsprintf NULL fmt ap 不起作用 产生段错误 因此似乎无法计算 vsnprintf 成功所需的缓冲区大小 Android sources表明 android log pri
  • “304 Not Modified”到底是如何工作的?

    304 Not Modified 响应是如何生成的 浏览器如何判断HTTP请求的响应是否为304 是浏览器设置的还是服务器发送的 如果由服务器发送 服务器如何知道缓存中可用的数据 以及如何将304设置为图像 我的猜测 如果它是由浏览器生成的
  • Git pre-commit hook:使用 -a 标志提交时如何获取添加/修改的文件

    当我使用 git commit a 提交我的工作时 预提交挂钩中的 git diff diff filter ACM name only cached 无法获取 git 将添加的文件 那么对于这种情况 正确的解决方案是什么 这里的问题是gi