ElyTree: an unread stop says why; git am skips and aborts (G-02)
A failed read of what Git is doing shows as a danger alert in Git's words. git am's Skip and Abort had no operation name in either catalog and panicked; both catalogs now hold it. Tests cover the unread stop and both buttons through their confirm to Git. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
818491ad9f
commit
6d1373fe16
5 files changed
+56
-9
No files matched your search
@@ -263,15 +263,9 @@ fn a_git_am_stopped_on_a_conflict_says_so() {
|
||||
let (root, _) = conflicted(dir.path());
|
||||
let patch = sh(&root, &["format-patch", "-1", "side", "--stdout"]);
|
||||
std::fs::write(dir.path().join("p.patch"), patch).expect("writes");
|
||||
let patch_at = dir.path().join("p.patch").to_str().expect("utf-8").to_owned();
|
||||
// `git am` stops on the conflict, as in a terminal.
|
||||
let stopped = super::git().run(
|
||||
&root,
|
||||
&[
|
||||
"am",
|
||||
"--3way",
|
||||
dir.path().join("p.patch").to_str().expect("utf-8"),
|
||||
],
|
||||
);
|
||||
let stopped = super::git().run(&root, &["am", "--3way", patch_at.as_str()]);
|
||||
assert!(stopped.is_err(), "the patch conflicts");
|
||||
let mut cx = TestAppContext::single();
|
||||
let (tree, cx) = window(&mut cx, dir.path());
|
||||
@@ -285,6 +279,32 @@ fn a_git_am_stopped_on_a_conflict_says_so() {
|
||||
cx.update(|_, app| op(app)),
|
||||
Some((crate::ops::Op::Am, "1/1".to_string()))
|
||||
);
|
||||
let applying = || root.join(".git/rebase-apply").exists();
|
||||
// Skip, then abort a second stop: each asks first.
|
||||
for skip in [true, false] {
|
||||
if !skip {
|
||||
assert!(super::git().run(&root, &["am", "--3way", patch_at.as_str()]).is_err());
|
||||
settle(cx, |app| op(app).is_some());
|
||||
}
|
||||
cx.update(|_, app| {
|
||||
tree.update(app, |tree, cx| match skip {
|
||||
true => tree.op_skip(crate::ops::Op::Am, cx),
|
||||
false => tree.op_abort(crate::ops::Op::Am, cx),
|
||||
})
|
||||
});
|
||||
let write = cx.update(|_, app| match &tree.read(app).ask {
|
||||
Some(Ask::Confirm { write, .. }) => write.clone(),
|
||||
_ => panic!("a confirm"),
|
||||
});
|
||||
assert!(applying(), "nothing runs before the yes");
|
||||
cx.update(|_, app| {
|
||||
tree.update(app, |tree, cx| {
|
||||
tree.ask = None;
|
||||
tree.write(1, write, cx)
|
||||
})
|
||||
});
|
||||
settle(cx, |_| !applying());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -320,3 +340,24 @@ fn a_settled_conflict_says_so_when_picked_again() {
|
||||
let toasts = cx.update(|_, app| shell.read(app).toasts(app));
|
||||
assert!(toasts.iter().any(|(text, _)| *text == want), "{toasts:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_stop_that_does_not_read_says_why() {
|
||||
let dir = tempfile::tempdir().expect("a temp dir");
|
||||
let root = super::committed(dir.path());
|
||||
// A merge whose other side no longer exists.
|
||||
let gone = "0123456789abcdef0123456789abcdef01234567";
|
||||
std::fs::write(root.join(".git/MERGE_HEAD"), format!("{gone}\n")).expect("writes");
|
||||
let mut cx = TestAppContext::single();
|
||||
let (tree, cx) = window(&mut cx, dir.path());
|
||||
cx.update(|_, app| tree.update(app, |tree, cx| tree.open_path(root.clone(), cx)));
|
||||
let unread = |app: &App| match tree.read(app).active().and_then(|o| o.doing.clone()) {
|
||||
Some(Err(error)) => Some(error),
|
||||
_ => None,
|
||||
};
|
||||
settle(cx, |app| unread(app).is_some());
|
||||
let error = cx.update(|_, app| unread(app)).expect("an error");
|
||||
assert!(error.contains("bad object MERGE_HEAD"), "Git's own words: {error}");
|
||||
let alert = cx.update(|_, app| tree.update(app, |tree, cx| tree.op_alert(cx).is_some()));
|
||||
assert!(alert, "the failure shows above the work tree");
|
||||
}
|
||||
@@ -149,6 +149,7 @@ pub(super) const EN: &[(&str, &str)] = &[
|
||||
("tree.opname.rebase", "rebase"),
|
||||
("tree.opname.cherry-pick", "cherry-pick"),
|
||||
("tree.opname.revert", "revert"),
|
||||
("tree.opname.am", "patch application"),
|
||||
];
|
||||
|
||||
pub(super) const ZH_CN: &[(&str, &str)] = &[
|
||||
@@ -273,4 +274,5 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[
|
||||
("tree.opname.rebase", "变基"),
|
||||
("tree.opname.cherry-pick", "拣选"),
|
||||
("tree.opname.revert", "撤销"),
|
||||
("tree.opname.am", "补丁应用"),
|
||||
];
|
||||
+1
-1
@@ -77,7 +77,7 @@
|
||||
- [x] M-14 `render_pdf` 在界面线程内嵌图片并写文件 — 打印页的内嵌、读样式与写入经 paper::stage 在后台执行,GPUI 单测证明执行器运行前未写(审查 2 轮)
|
||||
- [x] M-15 打印与导出 PDF 的暂存页执行文档里的脚本 — 打印与导出 PDF 的暂存页以 WebView::open_inert 打开,脚本关;webprobe 以同页开脚本为对照(审查 2 轮)
|
||||
- [x] G-01 打开“文件历史”或“逐行追溯”后,中间区不再响应任何别的选择 — 每个新选择(文件、提交、侧栏、分支、位置)都清掉文件历史与逐行追溯;测试窗口先装菜单(审查 2 轮)
|
||||
- [ ] G-02 进行中操作读取失败被吞掉;`git am` 冲突无提示条无错误
|
||||
- [x] G-02 进行中操作读取失败被吞掉;`git am` 冲突无提示条无错误 — 读不出进行中的操作即以 Git 原话的红色提示与日志说明;无 onto 的 rebase-apply 视为 git am,跳过、中止有文案(审查 2 轮)
|
||||
- [ ] G-03 新建的空仓库在“仓库”页显示两段 Git 致命错误(引用日志、LFS)
|
||||
- [ ] G-04 停止取回/拉取/推送失败的提示写成“克隆没有停下”
|
||||
- [ ] G-05 合并结果保存失败报成“Git 失败”,`tree.error.save` 是死文案
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
apps/ely-tree/src/moves.rs:211 — Abort and Skip both request `tree.opname.am` for the new `Op::Am`, but neither language catalog defines that key. Open a repository where `git am --3way` stopped on a conflict, then press Abort or Skip: both handlers panic with `no message "tree.opname.am" in en-US` (also `zh-CN`) before opening their confirmation, breaking operation recovery. Native Windows and WSL probes reproduce all four action/locale combinations; logs: `.work/G-02-r1/windows-probe-actions.log` and `.work/G-02-r1/linux-probe-actions.log`. Add the operation name to both catalogs and cover both actions through their confirmation and Git command paths; the existing am regression only checks detection and misses these broken buttons.
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
Reference in new issue
Block a user