git: match native agent workflows

This commit is contained in:
2026-11-06 19:07:28 +00:00
parent b5462cc366
commit 6daa53cca4
11 changed files with 1641 additions and 75 deletions
+189 -2
View File
@@ -16,15 +16,29 @@ def main [component: path] {
with-env $envs {
for side in [native wasi] { mkdir ($root | path join $side repo) }
let steps = [
{args: [init -b main], compare: false}
{args: [init -q -b main], compare: true}
{args: [config user.name Agent], compare: true}
{args: [config user.email agent@example.test], compare: true}
{args: [-c user.name=Override config --get user.name], compare: true}
{args: [config --get user.name], compare: true}
{write: 'base\n', file: 'file.txt'}
{args: [add .], compare: true}
{args: [commit -m initial], compare: true}
{args: [status --short], compare: true}
{args: [status -sb], compare: true}
{args: [status --porcelain=v1], compare: true}
{args: [status], compare: true}
{args: [ls-files], compare: true}
{args: [branch --show-current], compare: true}
{args: [branch -a], compare: true}
{args: [log -n 1], compare: true}
{args: [log -n 1 '--format=%H %h %s %an %ae %ad %d'], compare: true}
{args: [log -n 1 '--pretty=format:%H %h %s %an %ae %ad %d'], compare: true}
{args: [log -n 1 -p], compare: true}
{args: [log -n 1 --stat], compare: true}
{args: [log --oneline -- file.txt], compare: true}
{args: [show --stat], compare: true}
{args: [rev-parse --short HEAD], compare: true}
{args: [show HEAD], compare: true}
{args: [show HEAD:file.txt], compare: true}
{args: [branch topic], compare: true}
@@ -32,6 +46,18 @@ def main [component: path] {
{write: 'topic\n', file: 'topic.txt'}
{args: [add .], compare: true}
{args: [commit -m topic], compare: true}
{args: [rev-parse HEAD HEAD~1], compare: true}
{args: [diff HEAD~1], compare: true}
{args: [diff HEAD~1..HEAD], compare: true}
{args: [diff --name-only HEAD~1..HEAD], compare: true}
{args: [diff --name-status HEAD~1..HEAD], compare: true}
{args: [diff HEAD~1 -- topic.txt], compare: true}
{args: [branch disposable], compare: true}
{args: [branch -d disposable], compare: true}
{args: [branch disposable], compare: true}
{args: [branch -D disposable], compare: true}
{args: [branch disposable], compare: true}
{args: [branch -q -d disposable], compare: true}
{args: [switch main], compare: true}
{args: [merge topic], compare: true}
{write: 'changed\n', file: 'file.txt'}
@@ -48,6 +74,8 @@ def main [component: path] {
{args: [merge topic], compare: true, exit: 1}
{args: [status --short], compare: true}
{args: [merge --abort], compare: true}
{args: [merge -q topic], compare: true, exit: 1}
{args: [merge --abort], compare: true}
{args: [worktree add -b linked ../linked], compare: true}
{args: [worktree list], compare: false}
{args: [worktree remove ../linked], compare: true}
@@ -56,6 +84,7 @@ def main [component: path] {
{write: 'resolved\n', file: 'file.txt'}
{args: [add file.txt], compare: true}
{args: [rebase --continue], compare: true}
{args: [rebase -q main], compare: true}
{args: [log --oneline], compare: true}
{args: [switch main], compare: true}
{write: 'left\n', file: 'left.txt'}
@@ -67,11 +96,137 @@ def main [component: path] {
{args: [commit -m right], compare: true}
{args: [switch main], compare: true}
{args: [merge topic], compare: true}
{args: [show HEAD], compare: true}
{args: [branch], compare: true}
{args: [rm right.txt], compare: true}
{args: [diff --cached], compare: true}
{args: [status --porcelain], compare: true}
{args: [commit -m removed], compare: true}
{args: [checkout HEAD~1], compare: true}
{args: [status], compare: true}
{args: [status -sb], compare: true}
{args: [switch main], compare: true}
{args: [checkout -q -b agent-checkout], compare: true}
{args: [switch -q main], compare: true}
{args: [switch -q -c agent-switch], compare: true}
{write: 'agent\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{args: [commit -q -m agent], compare: true}
{args: [switch main], compare: true}
{args: [merge --no-ff -q agent-switch], compare: true}
{args: [show --stat], compare: true}
{args: [commit --quiet --allow-empty -m quiet], compare: true}
{args: [init], compare: true, normalize_path: true, state: true}
{args: [init -q], compare: true, state: true}
{args: [log -n 1 --oneline -- file.txt], compare: true}
{args: [log -n 2 --oneline -- file.txt], compare: true}
{args: [config branch.main.description agent-branch], compare: true}
{args: [branch -m rework], compare: true, state: true}
{args: [config --get branch.rework.description], compare: true}
{args: [branch -m agent-checkout renamed], compare: true, state: true}
{args: [branch], compare: true}
{args: [remote add example https://example.test/repo.git], compare: true, state: true}
{args: [remote -v], compare: true}
{args: [remote set-url example https://example.test/new.git], compare: true, state: true}
{args: [remote get-url example], compare: true}
{args: [remote remove example], compare: true, state: true}
{args: [remote -v], compare: true}
{args: [tag lightweight], compare: true, state: true}
{args: [tag -a annotated -m annotation], compare: true, state: true}
{args: [tag], compare: true}
{args: [tag --list], compare: true}
{args: [tag -d annotated], compare: true, state: true}
{args: [tag -d lightweight], compare: true, state: true}
{write: 'amended\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{args: [commit --amend -m amended], compare: true, state: true}
{args: [commit --amend --no-edit], compare: true, state: true}
{write: 'file message\n\nbody\n', file: 'message.txt'}
{write: 'from-file\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{args: [commit -F message.txt], compare: true, state: true}
{write: 'dirty\n', file: 'agent.txt'}
{args: [checkout agent.txt], compare: true, state: true}
{write: 'dirty\n', file: 'agent.txt'}
{args: [checkout -- agent.txt], compare: true, state: true}
{write: 'dirty\n', file: 'agent.txt'}
{args: [restore agent.txt], compare: true, state: true}
{write: 'staged\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{args: [restore --staged agent.txt], compare: true, state: true}
{args: [status --short], compare: true}
{args: [restore agent.txt], compare: true, state: true}
{write: 'staged\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{args: [reset HEAD agent.txt], compare: true, state: true}
{args: [restore agent.txt], compare: true, state: true}
{args: [branch saved], compare: true}
{args: [reset --soft HEAD~1], compare: true, state: true}
{args: [status --short], compare: true}
{args: [log --oneline -n 2], compare: true}
{args: [reset --hard saved], compare: true, state: true}
{args: [reset HEAD~1], compare: true, state: true}
{args: [status --short], compare: true}
{args: [reset --hard saved], compare: true, state: true}
{args: [reset --hard HEAD~1], compare: true, state: true}
{args: [reset --hard saved], compare: true, state: true}
{write: 'stash one\n', file: 'agent.txt'}
{args: [stash push], compare: true, state: true}
{args: [stash list], compare: true}
{args: [stash bogus], compare: true, exit: 128, state: true}
{args: [stash apply], compare: true, state: true}
{args: [reset --hard], compare: true, state: true}
{args: [stash drop], compare: true, state: true}
{write: 'stash two\n', file: 'agent.txt'}
{args: [stash -m custom], compare: true, state: true}
{args: [stash list], compare: true}
{write: 'stash three\n', file: 'agent.txt'}
{args: [stash], compare: true, state: true}
{args: [stash list], compare: true}
{args: [stash drop], compare: true, state: true}
{args: [stash pop], compare: true, state: true}
{args: [status --short], compare: true}
{args: [reset --hard], compare: true, state: true}
{args: [stash push], compare: true, state: true}
{write: 'added stash\n', file: 'stash-added.txt'}
{args: [add stash-added.txt], compare: true}
{write: 'stage stash\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{write: 'unstaged stash\n', file: 'agent.txt'}
{args: [stash push -m staged], compare: true, state: true}
{args: [stash apply], compare: true, state: true}
{args: [status --short], compare: true}
{args: [reset --hard], compare: true, state: true}
{args: [stash drop], compare: true, state: true}
{write: 'quiet stash\n', file: 'agent.txt'}
{args: [stash push -q -m quiet], compare: true, state: true}
{args: [stash pop -q], compare: true, state: true}
{args: [stash push -q], compare: true, state: true}
{args: [stash drop -q], compare: true, state: true}
{args: [stash push -q], compare: true, state: true}
{args: [switch -c picks], compare: true}
{write: 'picked\n', file: 'pick.txt'}
{args: [add pick.txt], compare: true}
{args: [commit -m picked], compare: true}
{args: [switch rework], compare: true}
{args: [cherry-pick picks], compare: true, state: true}
{args: [log --oneline -n 2], compare: true}
{args: [switch picks], compare: true}
{write: 'theirs\n', file: 'agent.txt'}
{args: [commit -am theirs], compare: true}
{args: [switch rework], compare: true}
{write: 'ours\n', file: 'agent.txt'}
{args: [commit -am ours], compare: true}
{args: [cherry-pick picks], compare: true, exit: 1, state: true}
{args: [status --short], compare: true}
{args: [cherry-pick --abort], compare: true, state: true}
{args: [cherry-pick picks], compare: true, exit: 1, state: true}
{write: 'resolved\n', file: 'agent.txt'}
{args: [add agent.txt], compare: true}
{args: [cherry-pick --continue], compare: true, state: true}
{args: [log --oneline -n 3], compare: true}
{args: [status --short], compare: true}
{args: [cherry-pick --abort], compare: true, exit: 128, state: true}
{args: [status --bad-option], compare: true, exit: 129}
{args: [not-a-command], compare: true, exit: 1}
]
@@ -85,13 +240,45 @@ def main [component: path] {
let native = (^git -C ($root | path join native repo) ...$args | complete)
let wasi = (^wasmtime run -S http=y -S cli-exit-with-code=y --dir $'($root)/wasi::/' --env GIT_AUTHOR_NAME --env GIT_AUTHOR_EMAIL --env GIT_COMMITTER_NAME --env GIT_COMMITTER_EMAIL --env GIT_AUTHOR_DATE --env GIT_COMMITTER_DATE $component -C /repo ...$args | complete)
let expected = ($step.exit? | default 0)
let equal = ($native.exit_code == $expected and $wasi.exit_code == $expected and (not $step.compare or $native.stdout == $wasi.stdout))
let native_stdout = if ($step.normalize_path? | default false) { $native.stdout | str replace --all ($root | path join native repo) '/repo' } else { $native.stdout }
let equal = ($native.exit_code == $expected and $wasi.exit_code == $expected and (not $step.compare or $native_stdout == $wasi.stdout))
print $'($args | str join " "): (if $equal {"PASS"} else {"FAIL"}) native=($native.exit_code) wasi=($wasi.exit_code)'
if not $equal {
$failures += 1
print ({native: $native, wasi: $wasi} | to json)
}
if ($step.state? | default false) {
for check in [[status --short] [log '--format=%H %P %an %ae %s' --all] [diff] [diff --cached] [show-ref] [config --get-regexp '^(branch|remote)\.']] {
let native_state = (^git -C ($root | path join native repo) ...$check | complete)
let wasi_state = (^git -C ($root | path join wasi repo) ...$check | complete)
let same = ($native_state.exit_code == $wasi_state.exit_code and $native_state.stdout == $wasi_state.stdout)
print $' state ($check | str join " "): (if $same {"PASS"} else {"FAIL"})'
if not $same { $failures += 1; print ({native: $native_state, wasi: $wasi_state} | to json) }
}
}
}
# Explicit usage checks for documented forms that this CLI does not implement.
let unsupported = [
[log --graph --oneline --all]
]
for args in $unsupported {
let result = (^wasmtime run -S http=y -S cli-exit-with-code=y --dir $'($root)/wasi::/' $component -C /repo ...$args | complete)
let expected = 129
let equal = ($result.exit_code == $expected and $result.stdout == '' and ($result.stderr | str lowercase | str contains 'usage'))
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) }
}
# 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)
^git -C $native add file.txt
let tree = (^git -C $native write-tree | str trim)
let merge = (^git -C $native commit-tree $tree -p HEAD -p HEAD~1 -m combined | str trim)
let result = (^wasmtime run -S http=y -S cli-exit-with-code=y --dir $'($root)/native/repo::/combined' $component -C /combined show $merge | complete)
let native_result = (^git -C $native show $merge | complete)
let equal = ($native_result.exit_code == 0 and $result.exit_code == 129 and $result.stdout == '' and ($result.stderr | str contains 'usage'))
print $'show combined merge: (if $equal {"PASS documented usage"} else {"FAIL documented usage"}) native=($native_result.exit_code) wasi=($result.exit_code)'
if not $equal { $failures += 1; print ($result | to json) }
if $failures > 0 {error make {msg: $'($failures) workflow comparisons failed'}}
}
}