Summary
Libra preserves file mode metadata correctly in its index/object layers, but working-tree materialization (libra clone, libra restore) writes regular files as 0644 regardless of the recorded mode, silently dropping the executable bit of 100755 entries. Additionally, libra status/diff do not treat the mode mismatch as a modification, so the corruption is invisible to libra itself.
Observed with libra 0.22.10 (Linux x86_64). Source at 0.22.12 was not re-tested.
Reproduction 1: libra clone from a git repository
$ git init -q -b main git-src && cd git-src
$ printf '#!/bin/sh\necho hi\n' > run.sh && chmod 755 run.sh
$ git add . && git -c user.name=t -c user.email=t@t commit -q -m init
$ stat -c '%a' run.sh
755
$ cd .. && libra clone git-src libra-clone
$ stat -c '%a' libra-clone/run.sh
644 # expected 755
Reproduction 2: libra-native add → restore cycle
$ libra init libra-own && cd libra-own
$ printf '#!/bin/sh\necho hi\n' > run.sh && chmod 755 run.sh
$ libra add run.sh && libra commit -m x
$ rm run.sh
$ libra restore run.sh # "Updated 1 path(s) from the index"
$ stat -c '%a' run.sh
644 # expected 755
$ libra ls-files --stage -- run.sh
100755 4163036efa65bd4a469e752267498f01ea36a55c 0 run.sh
$ libra status
On branch main # clean — mode loss is NOT reported as a change
Analysis
- The metadata layers are intact:
libra ls-files --stage (and equivalently GIT_INDEX_FILE=.libra/index git ls-files --stage, since .libra/index is git-index-v2 compatible) still records 100755 after the round-trip. Only the step that writes blobs back to the working tree loses the bit — it appears to write files without applying the mode stored in the index/tree entry (e.g. plain create/truncate + write, defaulting to 0666 & ~umask).
libra status reports the working tree as clean while the index says 100755 and disk is 0644, so libra's status/diff path does not compare the mode component.
Real-world impact
- Every working tree obtained via libra loses
+x on executable scripts (scripts/*.sh, hooks, build wrappers), so direct execution (./scripts/check-licenses.sh) fails until the user manually re-runs chmod +x.
- Git interop is polluted: running
git status in such a tree (e.g. a repo also cloned normally with git) shows phantom old mode 100755 / new mode 100644 diffs for every executable file. We hit exactly this in https://github.com/gitmono-dev/megaui — 6 scripts/check-*.sh files appeared mode-modified against origin/main (content identical), and committing from that tree would have silently stripped the executable bits in the upstream repository.
- Because
libra status treats it as clean, nothing alerts the user.
Expected behavior
- When materializing a blob into the working tree (clone/checkout/restore/merge/stash-pop), apply the mode from the index/tree entry:
0755 for S_IFREG | 0755, 0644 for S_IFREG | 0644 (and preserve symlinks as-is).
status/diff should treat a working-tree mode change vs. the index as a modification (parity with git), so the regression above is at least visible.
Summary
Libra preserves file mode metadata correctly in its index/object layers, but working-tree materialization (
libra clone,libra restore) writes regular files as0644regardless of the recorded mode, silently dropping the executable bit of100755entries. Additionally,libra status/diffdo not treat the mode mismatch as a modification, so the corruption is invisible to libra itself.Observed with libra 0.22.10 (Linux x86_64). Source at
0.22.12was not re-tested.Reproduction 1:
libra clonefrom a git repositoryReproduction 2: libra-native
add→restorecycleAnalysis
libra ls-files --stage(and equivalentlyGIT_INDEX_FILE=.libra/index git ls-files --stage, since.libra/indexis git-index-v2 compatible) still records100755after the round-trip. Only the step that writes blobs back to the working tree loses the bit — it appears to write files without applying the mode stored in the index/tree entry (e.g. plain create/truncate + write, defaulting to0666 & ~umask).libra statusreports the working tree as clean while the index says100755and disk is0644, so libra's status/diff path does not compare the mode component.Real-world impact
+xon executable scripts (scripts/*.sh, hooks, build wrappers), so direct execution (./scripts/check-licenses.sh) fails until the user manually re-runschmod +x.git statusin such a tree (e.g. a repo also cloned normally with git) shows phantomold mode 100755 / new mode 100644diffs for every executable file. We hit exactly this in https://github.com/gitmono-dev/megaui — 6scripts/check-*.shfiles appeared mode-modified againstorigin/main(content identical), and committing from that tree would have silently stripped the executable bits in the upstream repository.libra statustreats it as clean, nothing alerts the user.Expected behavior
0755forS_IFREG | 0755,0644forS_IFREG | 0644(and preserve symlinks as-is).status/diffshould treat a working-tree mode change vs. the index as a modification (parity with git), so the regression above is at least visible.