Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 106 additions & 19 deletions shape-doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3601,9 +3601,10 @@
puts back is what the row then reads.

THE ROWS, NOT A YES, for the shape pin (review of PR #358): `rev` may
be an index stage, `:2` or `:3`, and the rows of the side of a merge
`pin_restore` takes whole are the copies that side's tool wrote, so
they are the conflicted files its `why` gives a person to merge (#485).
be an index stage, `:2` or `:3`, and the rows of each side of a merge
are the copies that side's tool wrote, so they are the conflicted files
`pin_restore`'s `why` takes with the pin or gives a person to merge
(#485, #687).
"""
blob = git_answer(["cat-file", "blob", f"{rev}:./{pin}"],
ctx.root, binary=True)
Expand Down Expand Up @@ -3644,11 +3645,12 @@
edit of the project, kept by `apply --accept-local` on one
side, and taken whole the other side's copy dropped it,
the hunks git merged cleanly with it, and the doctor then
said COMPLIANT. The copies are the files the taken side's
said COMPLIANT. The copies are the files EITHER side's
pin has `files:` rows for, their collision copies
included -- those rows and not this standard's copy
lists, since a file the pin has no row for is not a shape
copy, though the lists name its path (review of PR #358).
lists, since a file neither pin has a row for is not a
shape copy, though the lists name its path (review of PR
#358). Only the taken side's can be taken (#687).
PROVE AND TAKE (Brett Heap, 2026-10-14T23:54:30Z, #547):
a conflicted copy whose two stages are each, byte for
byte, the standard's own file at the commit THAT side's
Expand Down Expand Up @@ -3680,6 +3682,29 @@
is the person's word: AGENTS.md's rule 3 is never to
pass the flag to make a refusal go away, and nothing
here tells a copy merged by hand from one nobody merged.
A COPY ONLY THE OTHER SIDE'S PIN HAS A ROW FOR (#687) --
a path that side's `apply --add` took from the standard
and the side taken here wrote as its own, say -- is
never taken either: `as_the_standard_wrote_it` proves
nothing of a side whose pin has no row for it. Nor is it
left out of the `why`, though the pin taken here makes it
the project's own file once the merge is finished: its
conflict holds the other side's `apply` output, and the
`merge` row lists it unmerged all the same. So the `why`
says that the other side's pin calls it a copy and the
pin taken here does not, and that a person merges it by
hand, keeping the hunks git merged. Where the pin taken
here does not read, it says that instead, and nothing of
whose file the copy becomes; where one side deleted the
copy, it says a person keeps or deletes it, as there are
no hunks to keep (`other_sides_copies`, review of PR
#736). The `--accept-local`
sentence is not for it, and is said only where a copy the
kept pin has a row for is left: `apply` refuses the flag
on a path the pin has no row for. In the `manifest kinds`
row such a copy under `contracts/` is `unmerged` while
the pin is conflicted, as every file the `why` gives a
person is, and `project` once the pin taken here reads.
The manifest and README.md are never named either, though
`apply` writes lines of them: every other line is the
project's own, and one side taken whole drops the other
Expand All @@ -3688,7 +3713,10 @@
says a person merges it.
`detail["left_for_a_person"]` lists every file the `why`
gives a person. A file of the project's own is never
named: its merge is theirs.
named: its merge is theirs. Both pins decide which file
that is, not the pin taken here alone: one NEITHER
side's pin has a row for. A file only the other side's
pin calls a copy is named, as above (#687).
uncommitted HEAD holds a pin that reads, and the tree or the index
differs from it. `checkout HEAD`, which drops a change no
tool of this standard leaves uncommitted.
Expand Down Expand Up @@ -3795,13 +3823,20 @@
f"{what} whole" if b"3" in pin_stages and side == "--ours"
else f"only that side of the merge holds a {what}. Take "
"it whole")
lines, copies, taken, partial = [], [], [], False
lines, copies, other_side, taken = [], [], [], []
partial, kept_reads = False, True
if pin == SHAPE_PIN:
lines = sorted(name for name in (kind.manifest, us.README)
if name in stages)
copies = sorted(name for name in set(
pin_at(ctx, f":{stage.decode()}") or ())
if name in stages)
# Both pins say what is a copy, and the kept one what can be
# taken: a copy only the other side's is never taken (#687).
held = pin_at(ctx, f":{stage.decode()}")
kept_reads, kept = held is not None, set(held or ())
other = set(pin_at(ctx, ":3" if stage == b"2" else ":2")
or ())
copies = sorted(name for name in kept if name in stages)
other_side = sorted(name for name in other - kept
if name in stages)
# A read of a partial clone can fetch: prove nothing there.
partial = bool(copies) and partial_clone(ctx.shape)
if not partial:
Expand All @@ -3821,9 +3856,10 @@
+ ("them" if len(taken) > 1 else "it") + " at the "
"commit that side's pin names, so neither side holds an "
"edit of the project")
if lines or copies:
if lines or copies or other_side:
if detail is not None:
detail["left_for_a_person"] = sorted(lines + copies)
detail["left_for_a_person"] = sorted(lines + copies
+ other_side)
if lines:
said.append(
f"{' and '.join(lines)} "
Expand Down Expand Up @@ -3854,6 +3890,7 @@
"check calls it both, as update-conflict, and "
"either refusal asks for --accept-local on that "
"path")
said += other_sides_copies(other_side, stages, kept_reads)
if said:
then = ". " + ". ".join(said) + ". Then finish"
return "unmerged", (
Expand Down Expand Up @@ -3912,6 +3949,52 @@
return "none", None, None


def other_sides_copies(names: list, stages: dict, kept_reads: bool) -> list:

Check failure on line 3952 in shape-doctor.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 20 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=opensoft_openRepoShape&issues=AaFHB8wsCBoOIVIXblzO&open=AaFHB8wsCBoOIVIXblzO&pullRequest=736
"""The sentences of a conflicted shape pin's `why` for `names`, the
files conflicted beside it that only the side NOT taken has `files:`
rows for (#687): never taken, and a person's to resolve. `stages` is
`{path: {stage: mode}}`, as `pin_restore` reads `ls-files -u`, and
`kept_reads` whether the pin taken here reads as one.

WHAT IS KNOWN, AND NOTHING ELSE (review of PR #736). Where the pin
taken here reads, it has no row for them, so once the merge is finished
each is the project's own file, and the sentence says so. Where it does
not read, nothing says whose file each becomes, so the sentence says
only that it does not read.

ONE SENTENCE FOR THE FILES BOTH SIDES HOLD, ANOTHER FOR THOSE ONE SIDE
DELETED. Both sides' stages are there for a content conflict, an
add/add included, and a person merges the file by hand, keeping the
hunks git merged. A delete/modify conflict holds one of them alone, and
has no hunks to keep: a person keeps the file or deletes it.
"""
said = []
for deleted in (False, True):
group = [name for name in names
if deleted != ({b"2", b"3"} <= set(stages.get(name, ())))]
if not group:
continue
many = len(group) > 1
each = "each" if many else "it"
sentence = (
f"{' and '.join(group)} {'are' if many else 'is'} conflicted "
"too, and the other side's pin calls "
+ ("them copies" if many else "it a copy")
+ (" where the pin taken here does not" if kept_reads
else " while the pin taken here does not read as a pin")
+ (f", and one side deleted {each}" if deleted else "")
+ ", so neither side is taken whole: ")
if kept_reads:
sentence += (f"once the merge is finished {each} is the "
"project's own file"
+ (" if kept" if deleted else "") + ", and ")
said.append(sentence + (
f"a person keeps or deletes {each} by hand" if deleted
else f"a person merges {each} by hand, keeping the hunks git "
"merged"))
return said


def taken_with_the_pin(ctx: Context, kind, copies: list,
stages: dict) -> list:
"""Which of `copies`, conflicted beside the shape pin, `pin_restore`
Expand Down Expand Up @@ -4227,12 +4310,16 @@

WHAT THE SHAPE PIN'S MERGE LEAVES TO A PERSON IS SAID HERE TOO (#485).
While the shape pin does not read, no row says what is a copy, so a
copy conflicted beside it is not `tool_written`. The side of the merge
`pin_restore` takes does say, and its `why` gives each such copy to a
person. That copy's case is `unmerged`, the one it has once the pin
reads, and not `project`, and `detail` gets the `left_for_a_person`
the `shape currency` row's has, so both rows say the same of the same
command. A copy that command takes whole with the pin, proved tool
copy conflicted beside it is not `tool_written`. The two sides of the
merge do say, and `pin_restore`'s `why` gives each such copy to a
person. That copy's case is `unmerged`, and not `project`, and
`detail` gets the `left_for_a_person` the `shape currency` row's has,
so both rows say the same of the same command. Where the pin taken
here has a row for the copy, `unmerged` is the case it has once the
pin reads. A copy under `contracts/` that only the other side's pin
lists (#687) is `project` then, as the pin taken here has no row for
it: `unmerged` is its case only while the pin is conflicted and the
`why` names it. A copy the NEXT takes whole with the pin, proved tool
output on both sides (#547), is `unmerged` too, and `detail` gets the
`taken_with_the_pin` that row's has: the command overwrites it, and
`project` would say git was not asked about a file the NEXT puts back.
Expand Down
Loading
Loading