Skip to content
zit Artifact [821dd715d9]
Login

Artifact [821dd715d9]

Artifact 821dd715d950228ef1aa06a1214b3e456ae60160b0fb14a7bc2415b9f39a6789:


# Erratum: draft-fossil-repo-state-01 §6.2 and §5.3

| | |
|---|---|
| **Document** | `draft-fossil-repo-state-01` |
| **Section** | 6.2, Branch creation (a sym tag both applied and cancelled on the root); 5.3, Direct Application Procedure, step 1 |
| **Type** | Technical (a combined effect left undefined that Fossil determines) |
| **Status** | Applied in `draft-fossil-repo-state-01` (2026-10-14) |
| **Reported** | 2026-10-14 |
| **Submitter** | Dan Mestas |
| **Found via** | A review of the drafts from libfossil; confirmed by running Fossil 2.28 and reading its source |

## 1. Summary

§6.2 records that `fossil branch new` can write both `T *sym-NAME *` and
`T -sym-NAME *` on a branch root, when the parent already carries a
propagating `sym-NAME`, and then says "this document does not define their
combined effect". Fossil determines it. Crosslink applies an artifact's tag
cards one at a time in card order, all with the artifact's own mtime, so the
§5.3 skip rule decides: the `*` card sorts first and writes the row, and the
`-` card meets a row with an equal mtime and is skipped. The root keeps the
propagating tag.

§5.3 calls the equal-mtime case implementation-defined because the winner
depends on write order. For two cards of one artifact the order is fixed by
the artifact, so the case is defined once the draft says the cards are
applied in card order.

## 2. The text in error

Page 18 (§6.2, informative paragraph):

> If the parent itself carries a propagating sym-* tag of the new branch's
> own name, branch new emits both the propagating card and a cancel card for
> that name; this document does not define their combined effect.

Page 15 (§5.3 step 1):

> If two competing applications for the same (tag, artifact) pair carry
> exactly equal mtime, the >= skip rule makes the winner depend on write
> order, so that equal-mtime case is implementation-defined rather than
> crosslink-order-independent.

## 3. What Fossil implements

`manifest_crosslink()` in `src/manifest.c`:

```
for(i=0; i<p->nTag; i++){
  ...
  switch( p->aTag[i].zName[0] ){
    case '-':  type = 0;  break;
    case '+':  type = 1;  break;
    case '*':  type = 2;  break;
  }
  tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue,
             rid, p->rDate, tid);
}
```

Every card of the artifact goes to `tag_insert()` with the same mtime,
`p->rDate`, in the order the cards appear. The artifact format sorts `T`
cards by their full name token, sign included ([FossilArtifact] §4.7.16),
and `*` (0x2A) and `+` (0x2B) sort before `-` (0x2D). `tag_insert()` skips a
write when the existing row's mtime is greater than or equal to the
incoming one (§5.3 step 1).

Checked with Fossil 2.28 [52445a27f1]: commit c1 on trunk; commit c2 with
`--branch feat`; add a `closed` tag to c2; `fossil branch new feat c2`. The
new root's manifest carries

```
T *branch * feat
T *sym-feat *
T -sym-feat *
```

and its tagxref rows after crosslink, and again after `fossil rebuild`, are

| tag | tagtype | srcid |
|---|---|---|
| `branch` | 2 (`feat`) | the root |
| `sym-feat` | 2 | the root |

The cancel left nothing behind. zit trunk, given the same repository
through `import-fossil`, lists `branch feat` and `sym-feat` as active on the
root.

## 4. Corrected text

§5.3 step 1, replace the sentence quoted in Section 2 with:

> If two competing applications for the same (tag, artifact) pair carry
> exactly equal mtime, the >= skip rule makes the winner depend on write
> order. The tag cards of one artifact MUST be applied in the order they
> appear in the artifact, all with that artifact's mtime, so between two
> cards of one artifact the first in card order wins. Between applications
> from different artifacts with equal mtime the case is
> implementation-defined rather than crosslink-order-independent.

§6.2, replace the sentence quoted in Section 2 with:

> If the parent itself carries a propagating sym-* tag of the new branch's
> own name, branch new emits both the propagating card and a cancel card for
> that name. The card order decides their combined effect (Section 5.3,
> step 1): the sign is part of the ordering key ([FossilArtifact],
> Section 4.7.16), so the * card precedes the - card, and the cancel meets
> the row the * card wrote with an equal mtime and is skipped. The root
> carries the propagating sym-* tag as if the cancel card were absent.

## 5. Observable consequence of the uncorrected text

An implementation that applies a cancel last regardless of card order, or
that treats the case as undefined and picks the cancel, leaves the root
without its own `sym-NAME`. Branch listings and name resolution for that
branch then differ from Fossil's for the same artifacts.

## 6. Implementations

zit already applies an artifact's tag cards in card order and matches
Fossil. No change is needed.