Docs/CLI reference

CLI reference

tama hook commit-lock

Git keeps one message file per repository (COMMIT_EDITMSG), whichever index a commit is made from, a private GIT_INDEX_FILE included. It writes the message after the pre-commit hook and reads it back after commit-msg. Two commits running at once in one checkout therefore each record whichever message was written last: one commit can land with the other's message over its own staged files, after each review passed the pair it was shown. The managed Git dispatchers allow one commit at a time per checkout.

Git keeps one message file per repository (COMMIT_EDITMSG), whichever index a commit is made from, a private GIT_INDEX_FILE included. It writes the message after the pre-commit hook and reads it back after commit-msg. Two commits running at once in one checkout therefore each record whichever message was written last: one commit can land with the other's message over its own staged files, after each review passed the pair it was shown. The managed Git dispatchers allow one commit at a time per checkout.

#Invocation

tama hook commit-lock take <git pid>
tama hook commit-lock release <git pid>

The managed pre-commit dispatcher runs take "$PPID" before Git writes the message, for the git commit process that runs the hook. It creates tama-commit.lock in the repository's common Git directory (git rev-parse --git-common-dir), holding that process id. The managed reference-transaction dispatcher of the same process runs release "$PPID" at committed or aborted. Release removes only a lock that names that process.

Each take or release first acquires a nonblocking filesystem lock on tama-commit.transaction in the same Git directory. This file remains in place after release: removing its inode would allow competing transactions to own different locks. The transaction covers owner reads, creation and stale-owner removal, so another invocation cannot observe a partially written owner or remove a replacement owner's lock.

#Refusals

  • commit-lock: another git commit (process <pid>) is running in this checkout and holds <lock>; …; commit again when it ends, followed by tama: another commit is running in <checkout>; no commit created. Another running process holds the lock. Nothing was written and no message file was touched; commit again once that commit ends.
  • commit-lock: the commit lock <lock> holds "<text>", not a process id. The lock was written by something other than this program. Remove it once no commit runs in the checkout.
  • commit-lock: cannot create|read|remove|release the commit lock <lock>: <cause>. The Git directory cannot be written or read; the cause is the filesystem's.
  • cannot open|acquire commit lock transaction <path>: <cause>. The transaction file is unavailable or another transition owns it; the owner record remains unchanged.
  • <source> must name a positive process id, got <pid>. Process-group and all-process selectors are not valid commit owners.
  • cannot inspect commit lock owner process <pid>: <cause>. An unexpected process inspection failure does not authorize stale-owner removal. Only an operating-system no-such-process result establishes that the owner ended; permission denial preserves the lock.
  • commit-lock: git's lock <lock> stands while process <pid> (<command>), … has this repository open, and any of them may hold it; commit again when they end. A process other than this commit has the lock, a pack file or the common Git directory open, or is a git process working in the checkout; the lock stays.
  • commit-lock: cannot run lsof to find a ref lock's holder: <cause>, commit-lock: lsof cannot say who holds the repository: <cause>, commit-lock: cannot read the pack files in <dir>: <cause>, commit-lock: cannot read git's lock <lock>: <cause>, commit-lock: cannot remove git's abandoned lock <lock>: <cause> and commit-lock: git rev-parse … failed: <cause>. Whether git's lock on HEAD or the checked-out branch was abandoned cannot be read, so it is not removed and the commit stops.

#A commit that ended early

A commit that ends before any ref transaction, for example one refused by a hook or one with nothing to commit, leaves a lock naming a process that no longer runs. The next commit takes it over and prints commit-lock: process <pid>, which held <lock>, no longer runs; this commit takes the lock. git commit --no-verify runs no hook and takes no lock.

#A git command that ended holding a ref lock

Git takes HEAD.lock and refs/heads/<branch>.lock while it moves HEAD and the branch, records no owner in them, and closes their files while its reference-transaction hook runs. A git command that ends between taking and committing them leaves them behind, so every later commit in the checkout is refused with Another git process seems to be running in this repository. Once take holds the commit lock, so no other commit runs in the checkout, it reads both and, for one it finds, asks lsof who has the repository open: the lock itself, any pack file or the common Git directory (a git in another worktree, one run with --git-dir from elsewhere, a libgit2 writer reading objects), and every git process whose directory is in the checkout. The commit and the processes it runs in do not count, and a program that only sits in the checkout holds nothing. With none, the lock was left by a command that ended: it is removed and named, commit-lock: <lock> was left by a git command that ended holding it, as no other process has this repository open; this commit removed it. With any, the commit is refused naming them and the lock stays. A writer that has nothing of the repository open while it holds a lock cannot be seen this way. A detached HEAD moves no branch, so only HEAD.lock is read.