Artifact 6014ba71a74f6f9d73cbd415745e2541ffbb4089c78842e6570dcd5b449337c1:
- File
internal/checkout/perm_test.go
— part of check-in
[2d2e62d420]
at
2026-10-18 22:16:06
on branch feat/submanifest-reader
— fix(checkout): scan missing files and mode changes as fossil does
Port the remaining branches of fossil's vfile_check_signature. A missing file is marked changed with mtime -1, a removed file is marked changed, and the permission half sets chnged 6-9 for exec-bit and symlink changes (returning to 0 when undone), honouring the allow-symlinks setting: off, a symlink is seen as what it points to; on, as itself, its content its target's name.
Status reports the mode changes as fossil's changes command labels them (executable, symlink, unexec, unlink). Commit records the permission it finds on disk, l included, with a symlink's target as its content. Where the storage has no file modes (MemStorage) or on Windows, the recorded permissions are kept, as fossil on Windows keeps them. ScanENotFile now refuses a non-file where a tracked file belongs, and merge scans with it as fossil's does. Extract still writes a missing file without Force.
Interop tests run the same edits through fossil changes and go-libfossil status on one checkout, in both orders and with allow-symlinks on and off, and compare a commit's F cards with fossil's.
Tracked in #449 (user: [email protected], size: 2111) [annotate] [blame] [check-ins using] [more...]
package checkout import ( "testing" "github.com/danmestas/go-libfossil/internal/vfile" ) // fossil's vfile_check_signature, the permission half: the chnged value it // stores given the content verdict and the file's recorded and current // permission. func TestNextPermState(t *testing.T) { cases := []struct { name string chnged int64 orig, current filePerm want int64 }{ {"unchanged", 0, permReg, permReg, 0}, {"became exec", 0, permReg, permExe, 6}, {"lost exec", 0, permExe, permReg, 8}, {"became link", 0, permReg, permLnk, 7}, {"exec became link", 0, permExe, permLnk, 7}, {"link became file", 0, permLnk, permReg, 9}, {"link became exec", 0, permLnk, permExe, 9}, {"edited and became link", 1, permReg, permLnk, 7}, {"edited link became file", 1, permLnk, permReg, 9}, {"edited and became exec", 1, permReg, permExe, 1}, {"edited and lost exec", 1, permExe, permReg, 1}, {"merged and became exec", 2, permReg, permExe, 2}, {"exec put back", 6, permReg, permReg, 0}, {"unexec put back", 8, permExe, permExe, 0}, {"exec, now unexec", 6, permExe, permReg, 8}, {"link put back", 7, permLnk, permLnk, 0}, {"unlink put back", 9, permLnk, permLnk, 0}, {"link, still link", 7, permReg, permLnk, 7}, } for _, tc := range cases { if got := nextPermState(tc.chnged, tc.orig, tc.current); got != tc.want { t.Errorf("%s: chnged %d, want %d", tc.name, got, tc.want) } } } // The mode changes classify as fossil's changes command labels them, after a // rename (kept, as for an edit) and before an edit. func TestClassifyModeChanges(t *testing.T) { want := map[int64]FileChange{ 6: ChangeExecutable, 7: ChangeSymlink, 8: ChangeUnexec, 9: ChangeUnlink, } for chnged, change := range want { r := vfile.Row{Pathname: "f", RID: 2, MRID: 2, Chnged: chnged} if got := vfile.Classify(r, false); got != change { t.Errorf("chnged %d: Classify = %v, want %v", chnged, got, change) } if got := vfile.Classify(r, true); got != ChangeMissing { t.Errorf("chnged %d, missing: Classify = %v, want missing", chnged, got) } } }