bigywater.tech
常见报错 · 25

Git Unable to create '.git/index.lock': File exists:14 个命令只有 5 个被挡,commit -a 的钩子期间握着锁,手工删锁会让 commit 报错

“fatal: Unable to create ‘.git/index.lock’: File exists.” 的标准答案是 rm .git/index.lock。这篇在本机复现它,看哪些命令真的被挡、什么情况下是真有进程握着锁(不能删)、手工删了会发生什么,以及并发时撞锁的概率有多高。

5 / 14
锁文件存在时被挡住的命令个数(写暂存区的 add / commit / reset / stash / rm),其余 9 个不受影响
128
钩子运行期间手工删掉 index.lock 后,那个 git commit 的退出码(repository has been updated, but unable to write new index file)
80 / 200
两个进程各自循环 git add 100 个文件时,因为锁而失败的次数

哪些命令被挡

实测 · 本机执行输出
    $ touch .git/index.lock && ls -l .git/index.lock | awk '{print $5, $NF}'
      0 .git/index.lock
    $ git add b.txt    (退出码 128)
      fatal: Unable to create '<tmp>/repo/.git/index.lock': File exists.
      
      Another git process seems to be running in this repository, or the lock file may be stale
  逐个命令(只看是否因为 index.lock 失败):
      git add b.txt                                退出码 128 被 index.lock 挡住
      git commit -m x --allow-empty                退出码 128 被 index.lock 挡住
      git checkout -b newbr                        退出码 0   不受影响
      git switch -c newbr2                         退出码 0   不受影响
      git reset                                    退出码 128 被 index.lock 挡住
      git stash -u                                 退出码 1   被 index.lock 挡住
      git merge --ff-only HEAD                     退出码 0   不受影响
      git rm --cached a.txt                        退出码 128 被 index.lock 挡住
      git status                                   退出码 0   不受影响
      git log --oneline                            退出码 0   不受影响
      git diff                                     退出码 0   不受影响
      git branch                                   退出码 0   不受影响
      git show HEAD --stat                         退出码 0   不受影响
      git tag t1                                   退出码 0   不受影响

写暂存区的命令被挡,读操作和新建分支不受影响。

真有进程握着锁

实测 · 本机执行输出
  钩子运行到第 1 秒时 git commit(先 add 好):.git/index.lock 不存在
  钩子运行到第 1 秒时 git commit -a:.git/index.lock 存在
  钩子运行到第 1 秒时 git commit -m x a.txt(指定文件):.git/index.lock 存在
    $ ls .git/index.lock && git add b.txt    (退出码 128)
      .git/index.lock
      fatal: Unable to create '<tmp>/repo/.git/index.lock': File exists.
      
      Another git process seems to be running in this repository, or the lock file may be stale
  此时那个 git commit 还在运行:True
    $ ls .git/index.lock 2>&1 | tail -1; git log --oneline | head -3
      ls: .git/index.lock: No such file or directory
      42711f0 x
      2fd4b32 c1

commit -a 或指定文件的 commit 在钩子运行期间握着锁;普通 commit 不握。

能不能删

实测 · 本机执行输出
    $ rm .git/index.lock && echo "已删除锁文件(commit 还在跑钩子)" && git add b.txt && git status --short
      已删除锁文件(commit 还在跑钩子)
       M a.txt
      A  b.txt
    被删锁的那个 git commit(退出码 128):
      fatal: repository has been updated, but unable to write
      new index file. Check that disk is not full and quota is
      not exceeded, and then "git restore --staged :/" to recover.
    $ git log --oneline | head -3; git status --short
      47315ae x
      2fd4b32 c1
      AM a.txt
      A  b.txt

没有 git 进程时删掉就行;进程在跑时删锁会让它报 repository has been updated, but unable to write new index file,暂存区状态错乱。

别的锁文件

实测 · 本机执行输出
  .git/HEAD.lock 存在时 git commit -qam x --allow-empty(退出码 128):
      fatal: cannot lock ref 'HEAD': Unable to create '<tmp>/repo/.git/HEAD.lock': File exists.
      
      Another git process seems to be running in this repository, or the lock file may be stale
  .git/refs/heads/main.lock 存在时 git commit -qm x --allow-empty(退出码 128):
      fatal: cannot lock ref 'HEAD': Unable to create '<tmp>/repo/.git/refs/heads/main.lock': File exists.
      
      Another git process seems to be running in this repository, or the lock file may be stale
  .git/config.lock 存在时 git config user.name Bob(退出码 255):
      error: could not lock config file .git/config: File exists
  .git/packed-refs.lock 存在时 git pack-refs --all(退出码 128):
      fatal: Unable to create '<tmp>/repo/.git/packed-refs.lock': File exists.
      
      Another git process seems to be running in this repository, or the lock file may be stale
  .git/shallow.lock 存在时 git fetch --depth 1 ../repo 2>&1 | head -1(退出码 0):
      fatal: Unable to create '<tmp>/repo/.git/shallow.lock': File exists.

HEAD.lock、config.lock、packed-refs.lock 的报错措辞各不相同,看路径删对应的。

并发撞锁

实测 · 本机执行输出
    两个并行进程各 add 100 个文件:失败 40 次 + 40 次,共 200 次里 80 次;最后真正进入暂存区的 f*.txt 文件 120 个(用时 1.49 s)

两个进程同时 add 一半会失败,锁不会等待,直接报错。

分步演示

锁文件
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64;报错文本都是真实复现时原样捕获的(命令行工具统一用 LC_ALL=C 取英文原文,路径换成 );所有服务都是本机上自己起的、只监听 127.0.0.1,不访问外网;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用 git 2.54.0(Apple Git-157);仓库都在临时目录里,配置隔离;“残留的锁”是手工 touch 出来的空文件,没有真的让 git 崩溃;第 5 步的数字每次运行会有差别
    • 没有验证:真正崩溃(kill -9)后留下的锁文件内容与是否和手工创建的一致;git 老版本里 “e.g. an editor opened by git commit” 的旧提示文字;Windows 上杀毒软件或文件同步工具占用 .git 的情形;gc.auto 触发的后台 gc 是否也会握 gc.pid 等锁;IDE 自动 fetch / 状态刷新是否会占锁
    京ICP备2026067502号-1京公网安备11010502063487号