Skip to content

Commit bdd27ec

Browse files
codexByron
authored andcommitted
docs(tree): clarify supported cache editing behavior (#1771)
`Tree.cache` and `TreeModifier` were announced as removed in an old changelog, but remain exported and supported. Their documentation also suggested sorting alone would make edits persistent, leaving their status and practical limits unclear. Document that cache edits affect one instance and do not update its object ID, stored tree, parents, index, or commits. Correct the historical removal claim and recommend `IndexFile.write_tree()` for staged path changes. Serialization already delegates construction to Git or Gix; no deprecation or replacement implementation is needed. Git's `Documentation/git-mktree.adoc` confirms that `mktree` normalizes entry order without pre-sorted input. Add a regression covering local subtree edits, serialization into a new object, and unchanged parent references. The focused tree tests passed with both CLI and Gix backends (3 tests each), using Git 2.54.0 and GixPython 0.3.0. The HTML build rendered the updated documentation; its strict check reported one existing field-list warning in `git.objects.submodule.util.is_relative_url`.
1 parent f1fb0bd commit bdd27ec

4 files changed

Lines changed: 51 additions & 11 deletions

File tree

‎doc/source/changes.rst‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ Git executable and safety
5151
API changes
5252
-----------
5353

54+
* ``Tree.cache`` and ``TreeModifier`` remain supported. They edit one tree's
55+
in-memory entries; object IDs and parent trees do not change automatically.
56+
Tree serialization now delegates construction to Git or the supported Gix
57+
backend. Prefer ``IndexFile.write_tree()`` for changes to staged paths.
5458
* ``Commit.stats`` preserves Unicode line separators in filenames when
5559
``core.quotePath=false``. Git's LF-delimited output remains intact for root,
5660
subsequent and empty commits, including the Gix backend's existing filename
@@ -1135,7 +1139,10 @@ Please note that due to breaking changes, we have to increase the major version.
11351139
* CRITICAL: fixed incorrect `Commit` object serialization when authored or commit date had timezones which were not
11361140
divisiblej by 3600 seconds. This would happen if the timezone was something like `+0530` for instance.
11371141
* A list of all additional fixes can be found `on GitHub <https://github.com/gitpython-developers/GitPython/issues?q=milestone%3A%22v1.0.2+-+Fixes%22+is%3Aclosed>`__
1138-
* CRITICAL: `Tree.cache` was removed without replacement. It is technically impossible to change individual trees and expect their serialization results to be consistent with what *git* expects. Instead, use the `IndexFile` facilities to adjust the content of the staging area, and write it out to the respective tree objects using `IndexFile.write_tree()` instead.
1142+
* Historical correction: ``Tree.cache`` was announced as removed, but the interface
1143+
remained available. Its current behavior and limitations are documented in
1144+
:class:`~git.objects.tree.TreeModifier`. Prefer ``IndexFile.write_tree()`` when
1145+
building trees from the staging area.
11391146

11401147
1.0.1 - Fixes
11411148
=============

‎git/objects/tree.py‎

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,17 @@ def cmp(a: str, b: str) -> int:
5555

5656

5757
class TreeModifier:
58-
"""A utility class providing methods to alter the underlying cache in a list-like
59-
fashion.
58+
"""Edit the entries cached by one :class:`Tree` instance.
6059
61-
Once all adjustments are complete, the :attr:`_cache`, which really is a reference
62-
to the cache of a tree, will be sorted. This ensures it will be in a serializable
63-
state.
60+
This interface is supported; it is not deprecated. Changes affect only that
61+
instance's in-memory entries. They do not update its object ID, the stored tree,
62+
parent trees, the index, or any commit. Call :meth:`set_done` to restore entry
63+
order for iteration after editing.
64+
65+
Tree serialization delegates construction and ordering to Git or the supported
66+
Gix backend. Prefer :class:`~git.index.base.IndexFile` and
67+
:meth:`~git.index.base.IndexFile.write_tree` when building a new tree from staged
68+
paths, especially when changes must propagate through nested trees.
6469
"""
6570

6671
__slots__ = ("_cache",)
@@ -303,10 +308,10 @@ def blobs(self) -> List[Blob]:
303308
def cache(self) -> TreeModifier:
304309
"""
305310
:return:
306-
An object allowing modification of the internal cache. This can be used to
307-
change the tree's contents. When done, make sure you call
308-
:meth:`~TreeModifier.set_done` on the tree modifier, or serialization
309-
behaviour will be incorrect.
311+
A supported editor for this instance's cached entries. Call
312+
:meth:`~TreeModifier.set_done` after editing to restore iteration order.
313+
Editing does not change this tree's object ID or persist changes to its
314+
stored object, parents, or commits.
310315
311316
:note:
312317
See :class:`TreeModifier` for more information on how to alter the cache.

‎test/test_tree.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,32 @@
1515
from git.objects import Blob, Tree
1616
from git.objects.tree import TreeModifier
1717
from test.lib import TestBase, with_rw_repo
18-
from test.lib.fixtures import read_only_fixture
18+
from test.lib.fixtures import fixture_repo, read_only_fixture
1919

2020
from .lib.helper import PathLikeMock
2121

2222

23+
def test_tree_cache_edits_do_not_update_object_ids_or_parents():
24+
with fixture_repo("tree-cache.sh", write=True) as repo:
25+
root = repo.head.commit.tree
26+
subtree = root["sub"]
27+
original_oid = subtree.hexsha
28+
del subtree.cache["file"]
29+
subtree.cache.set_done()
30+
assert not list(subtree)
31+
assert subtree.hexsha == original_oid
32+
assert root["sub"].hexsha == original_oid
33+
assert [item.name for item in repo.head.commit.tree["sub"]] == ["file"]
34+
35+
data = BytesIO()
36+
subtree._serialize(data)
37+
data.seek(0)
38+
stored = repo.odb.store(IStream("tree", len(data.getvalue()), data))
39+
assert stored.binsha != subtree.binsha
40+
assert not list(Tree(repo, stored.binsha, path=""))
41+
assert repo.head.commit.tree["sub"].hexsha == original_oid
42+
43+
2344
@ddt.ddt
2445
class TestTree(TestBase):
2546
@with_rw_repo("HEAD")

‎tests/fixtures/tree-cache.sh‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
git init --initial-branch=main repo
4+
mkdir repo/sub
5+
printf 'original\n' >repo/sub/file
6+
git -C repo add sub/file
7+
git -C repo commit -m initial

0 commit comments

Comments
 (0)