Preserve indexed executable modes in WASI git

This commit is contained in:
2026-11-06 20:04:06 +00:00
parent 3e33a85a6c
commit cd324678fe
3 changed files with 37 additions and 3 deletions
+17 -1
View File
@@ -238,6 +238,22 @@ pub fn status(repo: &gix::Repository) -> Result<Status> {
.map_err(|e| error("status", e))?;
for item in iter {
let item = item.map_err(|e| error("status", e))?;
#[cfg(target_os = "wasi")]
if matches!(
&item,
Item::Modification {
status: gix::status::plumbing::index_as_worktree::EntryStatus::Change(
gix::status::plumbing::index_as_worktree::Change::Modification {
executable_bit_changed: true,
content_change: None,
..
}
),
..
}
) {
continue;
}
let path = item.rela_path().to_str_lossy().into_owned();
let Some(summary) = item.summary() else {
continue;
@@ -332,7 +348,7 @@ fn worktree_file(
} else {
entry.mode
};
#[cfg(unix)]
#[cfg(all(unix, not(target_os = "wasi")))]
let mode = if mode != 0o120000 && caps.executable_bit {
use std::os::unix::fs::PermissionsExt;
if meta.permissions().mode() & 0o111 != 0 {
+2 -2
View File
@@ -229,7 +229,7 @@ fn work_entry(
Mode::SYMLINK,
)
} else if m.is_file() {
#[cfg(unix)]
#[cfg(all(unix, not(target_os = "wasi")))]
let mode = {
use std::os::unix::fs::PermissionsExt;
if m.permissions().mode() & 0o111 != 0 {
@@ -238,7 +238,7 @@ fn work_entry(
Mode::FILE
}
};
#[cfg(not(unix))]
#[cfg(any(not(unix), target_os = "wasi"))]
let mode = if previous == Some(Mode::FILE_EXECUTABLE) {
Mode::FILE_EXECUTABLE
} else {
+18
View File
@@ -268,6 +268,24 @@ def main [component: path] {
print $'($args | str join " "): (if $equal {"PASS documented usage"} else {"FAIL documented usage"}) wasi=($result.exit_code)'
if not $equal { $failures += 1; print ($result | to json) }
}
# WASI checkout and staging retain an existing executable file's indexed mode.
let executable = ($root | path join executable)
mkdir $executable
^git -C $executable init -q -b main
"#!/bin/sh\necho executable\n" | save ($executable | path join run.sh)
^chmod +x ($executable | path join run.sh)
^git -C $executable add run.sh
^git -C $executable commit --no-gpg-sign -q -m executable
rm ($executable | path join run.sh)
mut executable_ok = true
for args in [[checkout -q HEAD -- run.sh] [status --short] [diff] [add run.sh] [diff --cached] [status --short]] {
let result = (^wasmtime run -S http=y -S cli-exit-with-code=y --dir $'($executable)::/mode' $component -C /mode ...$args | complete)
if $result.exit_code != 0 or $result.stdout != '' { $executable_ok = false; print ($result | to json) }
}
let indexed = (^git -C $executable ls-files --stage run.sh | complete)
$executable_ok = ($executable_ok and $indexed.exit_code == 0 and ($indexed.stdout | str starts-with '100755 '))
print $'executable checkout stays clean: (if $executable_ok {"PASS"} else {"FAIL"})'
if not $executable_ok { $failures += 1 }
# A merge tree that differs from both parents requires a combined diff.
'combined resolution\n' | save --force ($root | path join native/repo/file.txt)
let native = ($root | path join native/repo)