mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(update): never offer the delete answer when there is nothing to pull; scan ignores showUntrackedFiles
- A main checkout reports in_place_update=False, so the keep/delete prompt fired even with HEAD already on origin/main; a "no" deleted edits on a no-op run. Ask only for a real branch switch or when the target is not HEAD or an ancestor of it. - status.showUntrackedFiles=no hid every untracked entry from the scan, so the stash fell back to whole-tree --include-untracked. The scan now passes --untracked-files=normal.
This commit is contained in:
1 parent
c483933325
commit
001b0eba27
3 files changed
+39
-4
No files matched your search
@@ -81,7 +81,7 @@ from hermes_cli.update_cmd_stash import (
|
||||
_git_untracked_paths, _park_stashed_changes, _print_stash_cleanup_guidance,
|
||||
_reject_unsafe_stash_restore, _resolve_stash_selector, _restore_stashed_changes,
|
||||
_restored_python_paths, _stash_apply_failed_only_on_existing_untracked,
|
||||
_stash_local_changes_if_needed, _unrestored_autostash_notice,
|
||||
_stash_local_changes_if_needed, _target_would_move_head, _unrestored_autostash_notice,
|
||||
_warn_orphaned_update_autostashes)
|
||||
from hermes_cli.update_cmd_config import (
|
||||
_LAST_SIBLING_SNAPSHOTS, _check_and_apply_config_migration, _migrate_sibling_profile_configs,
|
||||
@@ -1413,7 +1413,8 @@ def _prepare_checkout_for_update(
|
||||
# lands on a ref fetched only later.
|
||||
prompt_for_restore = not assume_yes and (gateway_mode or (sys.stdin.isatty() and sys.stdout.isatty()))
|
||||
ask_first = prompt_for_restore and not gateway_mode and (
|
||||
not in_place_update or _resolved_commit(git_cmd, target_ref) != _capture_head_sha(git_cmd, _m().PROJECT_ROOT))
|
||||
(not release_tag and not in_place_update and current_branch != branch)
|
||||
or _target_would_move_head(git_cmd, _m().PROJECT_ROOT, target_ref))
|
||||
auto_stash_ref = _m()._stash_local_changes_if_needed(
|
||||
git_cmd, _m().PROJECT_ROOT, checkout_move=_moves_for(_windows_gateway_resume), ask_first=ask_first,
|
||||
untracked_targets=() if is_fork else (target_ref, f"origin/{branch}", f"refs/heads/{branch}"))
|
||||
|
||||
@@ -150,7 +150,9 @@ def _scan_local_changes(git_cmd: list[str], cwd: Path, untracked_targets=()) ->
|
||||
blocks no pull or checkout, so with *untracked_targets* (every ref the update may land on) only the
|
||||
colliding ones are moved; without targets, or when a target tree is unreadable, all of them are."""
|
||||
from hermes_cli.update_cmd_git import _git_run
|
||||
status = _git_run(git_cmd, ["status", "--porcelain", "-z"], cwd, check=True)
|
||||
# Explicit mode: status.showUntrackedFiles=no would hide every untracked entry, and an empty
|
||||
# leave-set means the stash takes them all.
|
||||
status = _git_run(git_cmd, ["status", "--porcelain", "-z", "--untracked-files=normal"], cwd, check=True)
|
||||
if not status.stdout.strip():
|
||||
return None
|
||||
records = _porcelain_records(status.stdout)
|
||||
@@ -162,6 +164,13 @@ def _scan_local_changes(git_cmd: list[str], cwd: Path, untracked_targets=()) ->
|
||||
stash_untracked=tuple(collide), leave_untracked=tuple(u for u in untracked if u not in collide))
|
||||
|
||||
|
||||
def _target_would_move_head(git_cmd: list[str], cwd: Path, target_ref: str) -> bool:
|
||||
"""True unless *target_ref* is HEAD or an ancestor of it (nothing to pull). An unresolvable
|
||||
target answers True: the update fails on it later, and asking first is the safe side."""
|
||||
probe = _git_quiet(git_cmd, ["merge-base", "--is-ancestor", f"{target_ref}^{{commit}}", "HEAD"], cwd)
|
||||
return probe is None or probe.returncode != 0
|
||||
|
||||
|
||||
def _literal(path: str, *magic: str) -> str:
|
||||
"""Pathspec matching *path* byte-for-byte (relative to the checkout root git runs in)."""
|
||||
return f":({','.join(('literal', *magic))}){path}"
|
||||
|
||||
@@ -5,8 +5,10 @@ the autostash and deleted from disk on every update; declining the restore then
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from hermes_cli import update_cmd_stash
|
||||
import hermes_cli.main as cli_main
|
||||
from hermes_cli import update_cmd, update_cmd_stash
|
||||
|
||||
|
||||
def _git(cwd, *args):
|
||||
@@ -28,6 +30,7 @@ def _behind_clone(tmp_path):
|
||||
_git(up, "add", ".")
|
||||
_git(up, "commit", "-qm", "adds pkg/new.py")
|
||||
_git(work, "fetch", "-q", "origin")
|
||||
_git(work, "config", "status.showUntrackedFiles", "no") # the scan must not inherit display settings
|
||||
(work / "a.txt").write_text("a\nmine\n") # tracked edit
|
||||
(work / "pkg").mkdir()
|
||||
(work / "pkg" / "new.py").write_text("my copy\n") # untracked, collides with the update
|
||||
@@ -63,3 +66,25 @@ def test_declining_up_front_discards_without_a_stash_entry(tmp_path, monkeypatch
|
||||
assert not (work / "pkg" / "new.py").exists()
|
||||
assert (work / "stray" / "big.bin").exists()
|
||||
_git(work, "merge", "--ff-only", "-q", "origin/main")
|
||||
|
||||
|
||||
def test_up_to_date_checkout_never_offers_to_delete(tmp_path, monkeypatch):
|
||||
work = _behind_clone(tmp_path)
|
||||
_git(work, "stash", "push", "-q", "--include-untracked")
|
||||
_git(work, "merge", "--ff-only", "-q", "origin/main")
|
||||
(work / "a.txt").write_text("a\nmine\n") # dirty, already on the target commit
|
||||
monkeypatch.setattr(cli_main, "PROJECT_ROOT", work)
|
||||
monkeypatch.chdir(work)
|
||||
for stream in (sys.stdin, sys.stdout):
|
||||
monkeypatch.setattr(stream, "isatty", lambda: True, raising=False)
|
||||
|
||||
def refuse(*_a):
|
||||
raise AssertionError("asked keep/delete on an update with nothing to pull")
|
||||
monkeypatch.setattr("builtins.input", refuse)
|
||||
|
||||
plan = update_cmd._prepare_checkout_for_update(
|
||||
["git"], "main", "main", is_fork=False, assume_yes=False, gateway_mode=False,
|
||||
gw_input_fn=None, switch_branch=False, _windows_gateway_resume=None)
|
||||
|
||||
assert plan.commit_count == 0
|
||||
assert "a.txt" in _git(work, "stash", "show", "--name-only", plan.auto_stash_ref) # kept, not deleted
|
||||
Reference in new issue
Block a user