feat(update): stash/restore by default + settable discard for non-interactive updates (reverts #38542, #39568) (#39645)
* Revert "fix(update): require managed marker before destructive clean" This reverts commitc8e80cd0bf. * Revert "fix(update): stop stash/restore from clobbering desktop source on managed clones (#38542)" This reverts commit8a19884bf3. * chore(install): keep npm ci desktop-build fix after stash revert The destructive-clean reverts (#38542/#39568) pulled the desktop workspace install back to bare `npm install`. The npm ci -> npm install fallback is orthogonal build-correctness (avoids the Windows workspace-hoisting flake where install reports up-to-date against a stale marker while node_modules is empty, breaking tsc -b). Preserve it. * feat(update): settable stash-or-discard for non-interactive local changes Adds updates.non_interactive_local_changes (stash | discard, default stash). Governs ONLY non-interactive updates (desktop/chat app, gateway, --yes) — interactive terminal updates always stash-and-ask, unchanged. - config.py: new key under existing updates section; _config_version 26->27. - main.py: _cmd_update_impl detects non-interactive (gateway/--yes/no-TTY), reads the setting; new _discard_stashed_changes() drops the stash (stash-and-drop, never reset --hard/clean -fd, so ignored paths survive). Post-pull restore site branches on it; the bail-out and up-to-date restores always preserve work. - web_server.py + apps/desktop settings: exposes it as a stash/discard select (Advanced section, In-App Update Local Changes). - docs + tests (discard drops, stash restores, interactive ignores setting, missing section defaults to stash). * fix(install.ps1): stash/restore instead of reset --hard on Windows update The PR reverted the destructive update path to stash/restore everywhere except scripts/install.ps1, whose managed-clone update path still ran `git reset --hard HEAD` before checkout — silently destroying agent-edited tracked source on Windows (the same #38542 data-loss class the PR fixes). - Replace `git reset --hard HEAD` with stash-before-checkout + restore-after-checkout, mirroring install.sh. Untracked files are included so agent-created dirs (e.g. tinker-atropos/) survive. - Keep `core.autocrlf false` (it prevents the phantom CRLF dirt that made the stash necessary; it's also load-bearing for a clean restore). - Wrap all three checkout modes (Commit/Tag/Branch); Branch case now uses `git pull --ff-only` so local commits are never clobbered. - Only prompt to restore when a real console is attached (UserInteractive + non-redirected stdin/stdout + ConsoleHost); the desktop Update button and bootstrap have no usable console, so they default to restore and never hang on Read-Host. - On restore conflict or a failed update, the stash is preserved with recovery instructions — work is never silently dropped. Validated on Windows (PowerShell 5.1, git 2.54): AST parse clean; E2E non-conflicting restore applies+drops cleanly with ignored paths (node_modules) untouched; conflicting restore preserves the stash. --------- Co-authored-by: alt-glitch <balyan.sid@gmail.com>
This commit is contained in:
+41
-15
@@ -1097,24 +1097,50 @@ clone_repo() {
|
||||
log_info "Existing installation found, updating..."
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# This is a managed clone the user never edits, so any working-tree
|
||||
# dirt is git artifact (CRLF renormalization, npm lockfile churn,
|
||||
# files left behind when a directory was deleted upstream such as
|
||||
# apps/bootstrap-installer/). The old path stashed that dirt and
|
||||
# re-applied it after the pull, but the stash/restore cycle has
|
||||
# clobbered freshly-pulled source files (apps/desktop/ →
|
||||
# "[UNRESOLVED_ENTRY] Cannot resolve entry module index.html").
|
||||
# Discard the dirt with a hard reset instead — mirrors install.ps1's
|
||||
# update path. Fork users customize via `hermes update`, which keeps
|
||||
# the stash machinery; the installer is a managed-only entry point.
|
||||
git fetch origin
|
||||
local autostash_ref=""
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
log_info "Discarding working-tree changes on managed clone before update..."
|
||||
git reset --hard HEAD >/dev/null 2>&1 || true
|
||||
git clean -fd >/dev/null 2>&1 || true
|
||||
local stash_name
|
||||
stash_name="hermes-install-autostash-$(date -u +%Y%m%d-%H%M%S)"
|
||||
log_info "Local changes detected, stashing before update..."
|
||||
git stash push --include-untracked -m "$stash_name"
|
||||
autostash_ref="stash@{0}"
|
||||
fi
|
||||
|
||||
git fetch origin
|
||||
git checkout "$BRANCH"
|
||||
git reset --hard "origin/$BRANCH"
|
||||
git pull --ff-only origin "$BRANCH"
|
||||
|
||||
if [ -n "$autostash_ref" ]; then
|
||||
local restore_now="yes"
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
echo
|
||||
log_warn "Local changes were stashed before updating."
|
||||
log_warn "Restoring them may reapply local customizations onto the updated codebase."
|
||||
printf "Restore local changes now? [Y/n] "
|
||||
read -r restore_answer
|
||||
case "$restore_answer" in
|
||||
""|y|Y|yes|YES|Yes) restore_now="yes" ;;
|
||||
*) restore_now="no" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ "$restore_now" = "yes" ]; then
|
||||
log_info "Restoring local changes..."
|
||||
if git stash apply "$autostash_ref"; then
|
||||
git stash drop "$autostash_ref" >/dev/null
|
||||
log_warn "Local changes were restored on top of the updated codebase."
|
||||
log_warn "Review git diff / git status if Hermes behaves unexpectedly."
|
||||
else
|
||||
log_error "Update succeeded, but restoring local changes failed. Your changes are still preserved in git stash."
|
||||
log_info "Resolve manually with: git stash apply $autostash_ref"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_info "Skipped restoring local changes."
|
||||
log_info "Your changes are still preserved in git stash."
|
||||
log_info "Restore manually with: git stash apply $autostash_ref"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_error "Directory exists but is not a git repository: $INSTALL_DIR"
|
||||
log_info "Remove it or choose a different directory with --dir"
|
||||
|
||||
Reference in New Issue
Block a user