claude-dev-env 8.74.0 → 8.74.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/hooks/followup_ledger.py
CHANGED
|
@@ -25,10 +25,15 @@ if _hooks_directory not in sys.path:
|
|
|
25
25
|
from hooks_constants.followup_ledger_constants import (
|
|
26
26
|
ABSENT_ORIGIN_COMMIT,
|
|
27
27
|
ALL_FOLLOWUP_LEDGER_PATH_SEGMENTS,
|
|
28
|
-
ALL_GIT_HEAD_PATH_SEGMENTS,
|
|
29
28
|
CHECK_ID_KEY,
|
|
30
29
|
FILE_PATH_KEY,
|
|
30
|
+
GIT_COMMON_DIRECTORY_FILE_NAME,
|
|
31
|
+
GIT_DIRECTORY_FILE_PREFIX,
|
|
31
32
|
GIT_DIRECTORY_NAME,
|
|
33
|
+
GIT_HEAD_FILE_NAME,
|
|
34
|
+
GIT_PACKED_REFERENCES_COMMENT_PREFIX,
|
|
35
|
+
GIT_PACKED_REFERENCES_FILE_NAME,
|
|
36
|
+
GIT_PACKED_REFERENCES_PEELED_PREFIX,
|
|
32
37
|
GIT_REFERENCE_PREFIX,
|
|
33
38
|
LEDGER_APPEND_MODE,
|
|
34
39
|
LEDGER_ENCODING,
|
|
@@ -267,7 +272,10 @@ def head_commit(repository_root: Path) -> str:
|
|
|
267
272
|
|
|
268
273
|
.git/HEAD holding "ref: refs/heads/main" -> the sha in refs/heads/main
|
|
269
274
|
.git/HEAD holding a sha -> that sha
|
|
270
|
-
|
|
275
|
+
.git file holding "gitdir: <path>" -> the same reads in <path>,
|
|
276
|
+
branches from its commondir
|
|
277
|
+
branch only in packed-refs -> the sha packed-refs lists
|
|
278
|
+
no .git entry -> ""
|
|
271
279
|
|
|
272
280
|
The revision comes from the files git writes rather than from a git
|
|
273
281
|
process, so recording a finding starts no subprocess.
|
|
@@ -278,16 +286,73 @@ def head_commit(repository_root: Path) -> str:
|
|
|
278
286
|
Returns:
|
|
279
287
|
The checked-out revision, or an empty string when none can be read.
|
|
280
288
|
"""
|
|
281
|
-
|
|
282
|
-
head_text = _file_text(
|
|
289
|
+
git_directory = _git_directory(repository_root)
|
|
290
|
+
head_text = _file_text(git_directory / GIT_HEAD_FILE_NAME)
|
|
283
291
|
if head_text is None:
|
|
284
292
|
return ABSENT_ORIGIN_COMMIT
|
|
285
293
|
if not head_text.startswith(GIT_REFERENCE_PREFIX):
|
|
286
294
|
return head_text
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
295
|
+
reference_name = head_text[len(GIT_REFERENCE_PREFIX) :]
|
|
296
|
+
for each_reference_directory in (git_directory, _common_directory(git_directory)):
|
|
297
|
+
loose_commit = _file_text(each_reference_directory / reference_name)
|
|
298
|
+
if loose_commit:
|
|
299
|
+
return loose_commit
|
|
300
|
+
return _packed_reference_commit(_common_directory(git_directory), reference_name)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _git_directory(repository_root: Path) -> Path:
|
|
304
|
+
"""Locate the git directory, following a linked worktree's .git file.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
repository_root: The repository whose git directory to locate.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
The directory git keeps HEAD in for this checkout.
|
|
311
|
+
"""
|
|
312
|
+
git_entry = repository_root / GIT_DIRECTORY_NAME
|
|
313
|
+
if not git_entry.is_file():
|
|
314
|
+
return git_entry
|
|
315
|
+
entry_text = _file_text(git_entry) or ""
|
|
316
|
+
if not entry_text.startswith(GIT_DIRECTORY_FILE_PREFIX):
|
|
317
|
+
return git_entry
|
|
318
|
+
return repository_root / entry_text[len(GIT_DIRECTORY_FILE_PREFIX) :]
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _common_directory(git_directory: Path) -> Path:
|
|
322
|
+
"""Locate the directory holding the branches shared by every worktree.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
git_directory: The checkout's own git directory.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
The commondir target, or git_directory itself when none is named.
|
|
329
|
+
"""
|
|
330
|
+
common_text = _file_text(git_directory / GIT_COMMON_DIRECTORY_FILE_NAME)
|
|
331
|
+
if not common_text:
|
|
332
|
+
return git_directory
|
|
333
|
+
return git_directory / common_text
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _packed_reference_commit(common_directory: Path, reference_name: str) -> str:
|
|
337
|
+
"""Find one reference's sha in packed-refs.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
common_directory: The git directory holding packed-refs.
|
|
341
|
+
reference_name: The full reference name, such as refs/heads/main.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
The packed sha, or an empty string when packed-refs lacks the reference.
|
|
345
|
+
"""
|
|
346
|
+
packed_text = _file_text(common_directory / GIT_PACKED_REFERENCES_FILE_NAME) or ""
|
|
347
|
+
for each_line in packed_text.splitlines():
|
|
348
|
+
if each_line.startswith(
|
|
349
|
+
(GIT_PACKED_REFERENCES_COMMENT_PREFIX, GIT_PACKED_REFERENCES_PEELED_PREFIX)
|
|
350
|
+
):
|
|
351
|
+
continue
|
|
352
|
+
commit_and_name = each_line.split(maxsplit=1)
|
|
353
|
+
if len(commit_and_name) == 2 and commit_and_name[1] == reference_name:
|
|
354
|
+
return commit_and_name[0]
|
|
355
|
+
return ABSENT_ORIGIN_COMMIT
|
|
291
356
|
|
|
292
357
|
|
|
293
358
|
def _file_text(file_path: Path) -> str | None:
|
|
@@ -15,7 +15,12 @@ __all__ = [
|
|
|
15
15
|
"SEVERITY_KEY",
|
|
16
16
|
"ORIGIN_COMMIT_KEY",
|
|
17
17
|
"ABSENT_ORIGIN_COMMIT",
|
|
18
|
-
"
|
|
18
|
+
"GIT_HEAD_FILE_NAME",
|
|
19
|
+
"GIT_DIRECTORY_FILE_PREFIX",
|
|
20
|
+
"GIT_COMMON_DIRECTORY_FILE_NAME",
|
|
21
|
+
"GIT_PACKED_REFERENCES_FILE_NAME",
|
|
22
|
+
"GIT_PACKED_REFERENCES_COMMENT_PREFIX",
|
|
23
|
+
"GIT_PACKED_REFERENCES_PEELED_PREFIX",
|
|
19
24
|
"GIT_DIRECTORY_NAME",
|
|
20
25
|
"GIT_REFERENCE_PREFIX",
|
|
21
26
|
"SEVERITY_BREAKING",
|
|
@@ -38,7 +43,12 @@ ORIGIN_COMMIT_KEY: str = "origin_commit"
|
|
|
38
43
|
|
|
39
44
|
ABSENT_ORIGIN_COMMIT: str = ""
|
|
40
45
|
GIT_DIRECTORY_NAME: str = ".git"
|
|
41
|
-
|
|
46
|
+
GIT_HEAD_FILE_NAME: str = "HEAD"
|
|
47
|
+
GIT_DIRECTORY_FILE_PREFIX: str = "gitdir: "
|
|
48
|
+
GIT_COMMON_DIRECTORY_FILE_NAME: str = "commondir"
|
|
49
|
+
GIT_PACKED_REFERENCES_FILE_NAME: str = "packed-refs"
|
|
50
|
+
GIT_PACKED_REFERENCES_COMMENT_PREFIX: str = "#"
|
|
51
|
+
GIT_PACKED_REFERENCES_PEELED_PREFIX: str = "^"
|
|
42
52
|
GIT_REFERENCE_PREFIX: str = "ref: "
|
|
43
53
|
|
|
44
54
|
SEVERITY_BREAKING: str = "breaking"
|
|
@@ -191,6 +191,83 @@ def test_head_commit_is_empty_outside_a_repository(tmp_path: Path) -> None:
|
|
|
191
191
|
assert head_commit(tmp_path) == ""
|
|
192
192
|
|
|
193
193
|
|
|
194
|
+
def _committed_repository(repository_root: Path) -> str:
|
|
195
|
+
subprocess.run(["git", "init", "--quiet", str(repository_root)], check=True)
|
|
196
|
+
subprocess.run(
|
|
197
|
+
[
|
|
198
|
+
"git",
|
|
199
|
+
"-C",
|
|
200
|
+
str(repository_root),
|
|
201
|
+
"-c",
|
|
202
|
+
"user.name=t",
|
|
203
|
+
"-c",
|
|
204
|
+
"user.email=t@t",
|
|
205
|
+
"commit",
|
|
206
|
+
"--quiet",
|
|
207
|
+
"--allow-empty",
|
|
208
|
+
"-m",
|
|
209
|
+
"start",
|
|
210
|
+
],
|
|
211
|
+
check=True,
|
|
212
|
+
)
|
|
213
|
+
return subprocess.run(
|
|
214
|
+
["git", "-C", str(repository_root), "rev-parse", "HEAD"],
|
|
215
|
+
check=True,
|
|
216
|
+
capture_output=True,
|
|
217
|
+
text=True,
|
|
218
|
+
).stdout.strip()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_head_commit_reads_a_linked_worktree_branch(tmp_path: Path) -> None:
|
|
222
|
+
main_root = tmp_path / "main"
|
|
223
|
+
worktree_root = tmp_path / "linked"
|
|
224
|
+
expected_commit = _committed_repository(main_root)
|
|
225
|
+
subprocess.run(
|
|
226
|
+
[
|
|
227
|
+
"git",
|
|
228
|
+
"-C",
|
|
229
|
+
str(main_root),
|
|
230
|
+
"worktree",
|
|
231
|
+
"add",
|
|
232
|
+
"--quiet",
|
|
233
|
+
"-b",
|
|
234
|
+
"feature",
|
|
235
|
+
str(worktree_root),
|
|
236
|
+
],
|
|
237
|
+
check=True,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
assert head_commit(worktree_root) == expected_commit
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_head_commit_reads_a_packed_branch_from_a_linked_worktree(
|
|
244
|
+
tmp_path: Path,
|
|
245
|
+
) -> None:
|
|
246
|
+
main_root = tmp_path / "main"
|
|
247
|
+
worktree_root = tmp_path / "linked"
|
|
248
|
+
expected_commit = _committed_repository(main_root)
|
|
249
|
+
subprocess.run(
|
|
250
|
+
[
|
|
251
|
+
"git",
|
|
252
|
+
"-C",
|
|
253
|
+
str(main_root),
|
|
254
|
+
"worktree",
|
|
255
|
+
"add",
|
|
256
|
+
"--quiet",
|
|
257
|
+
"-b",
|
|
258
|
+
"feature",
|
|
259
|
+
str(worktree_root),
|
|
260
|
+
],
|
|
261
|
+
check=True,
|
|
262
|
+
)
|
|
263
|
+
subprocess.run(
|
|
264
|
+
["git", "-C", str(main_root), "pack-refs", "--all"], check=True
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
assert not (main_root / ".git" / "refs" / "heads" / "feature").exists()
|
|
268
|
+
assert head_commit(worktree_root) == expected_commit
|
|
269
|
+
|
|
270
|
+
|
|
194
271
|
def test_deduplicated_lines_keeps_the_first_of_each_exact_line_in_order() -> None:
|
|
195
272
|
assert deduplicated_lines(["a", "b", "a", "c", "b"]) == ["a", "b", "c"]
|
|
196
273
|
|