Skip to content

index: write blobs via git hash-object, not gitdb's odb.store - #2209

Merged
Byron merged 1 commit into
gitpython-developers:mainfrom
caroescm:fix-index-add-chmod
Aug 10, 2026
Merged

index: write blobs via git hash-object, not gitdb's odb.store#2209
Byron merged 1 commit into
gitpython-developers:mainfrom
caroescm:fix-index-add-chmod

Conversation

@caroescm

Copy link
Copy Markdown

Problem

repo.index.add() is GitPython's own way of adding files, separate from just running git add. To do that, it writes the file's content into .git/objects itself using Python, instead of asking git to do it. As part of that, it also tries to change the new file's permissions (chmod) — and in some setups, that permission change isn't allowed, even though writing the file itself worked fine.

So index.add() can crash with PermissionError in situations where plain git add (and repo.git.add(...), which just runs git add directly) work without any problem, on the same file.

There's also a smaller side effect: because GitPython reads the file itself instead of asking git, it skips any .gitattributes rules (like converting line endings), so the content it stores can end up slightly different from what git add would store.

What I changed

I changed index.add() to use the real git hash-object -w command to write the file into the object database, instead of doing it in Python. This means:

  • No more Python-side chmod, so the crash goes away.
  • The stored content now matches what git add would store, filters included.

Symlinks needed a small extra step: git hash-object normally follows a symlink and hashes the file it points to, but Git is supposed to store the symlink's target path itself, not the pointed-to file. So for symlinks, I write the target path to a temporary file and hash that instead.

Fixes #2021

Testing

  • Ran the existing test suite (test_index.py, test_base.py, test_repo.py) — all still pass.
  • Compared index.add()'s output to real git add for a normal file, a symlink, and a file in a subfolder — identical results.
  • Recreated the original bug by blocking chmod only on .git/objects files (same as the error in repo.index.add() method attempts to chmod files which it should not #2021) — index.add() now works instead of crashing.

@caroescm
caroescm force-pushed the fix-index-add-chmod branch from 4dbefc1 to 7ab02c6 Compare August 10, 2026 04:56
@Byron
Byron force-pushed the fix-index-add-chmod branch from 7ab02c6 to 139d0b1 Compare August 10, 2026 08:28
@Byron

Byron commented Aug 10, 2026

Copy link
Copy Markdown
Member

Thanks a lot for tackling this!

I will see if an alternative solution can do the trick, as such a localized override goes against the original architecture.

@Byron
Byron force-pushed the fix-index-add-chmod branch from 139d0b1 to f202503 Compare August 10, 2026 09:11
This is done by calling into `git hash-object` for correctness, instead
of using a mostly incorrect custom implementation for this (lacks filters).

<!-- agent -->
GitCmdObjectDB inherited LooseObjectDB.store(), so despite its name, object
writes bypassed Git and used gitdb's loose-object implementation. That path
creates and chmods object files itself, which can fail during Index.add() on
filesystems where those permission changes are unsupported.

Override store() to stream new objects through `git hash-object -w --stdin`.
This lets Git manage object creation and permissions consistently with the
repository configuration. Retain the inherited implementation for pre-hashed
objects and custom output streams, whose existing semantics hash-object cannot
provide.

Assisted-by: GPT 5.6
Co-authored-by: GPT 5.6 <codex@openai.com>
@Byron
Byron force-pushed the fix-index-add-chmod branch from f202503 to 93677a0 Compare August 10, 2026 09:54
@Byron
Byron merged commit 5ff52cc into gitpython-developers:main Aug 10, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

repo.index.add() method attempts to chmod files which it should not

2 participants