fix(devutils): keep mtimes of files sync pops and pushes back unchanged#39

merged2
main
fix/sync-keep-mtimes
opened5h agobyKKainoa Newton
View on GitHubTwo-way sync enabledPR last imported 49m ago

Problem. When a patch in the middle of the series changes or is removed, worktree.sh sync pops it and every patch after it, then pushes the new stack. Most popped files come back byte-identical but with new mtimes, so siso recompiles them. On the Chromium 156 base this blocks a needed change: Helium's lint rejects the empty rac/spaces/composited-transition.patch (which fails lint on every PR stacked on the sync branch), but dropping it mid-series would make every 156 clone recompile what the ~150 later patches touch.

Change.

  • Before popping, cmd_sync runs worktree_state.py snapshot <keep>. It records the path, mtime and sha256 of every file the popped patches touch, plus a build marker: the mtimes of out/*/.siso_fs_state, .ninja_log, build.ninja and build.ninja.stamp.
  • After pushing, and before dev_names sub, restore puts the old mtime back on every file whose content is byte-identical. The restore runs in the same name state as the snapshot. Files whose content changed keep their new mtime.
  • If the tree was built or regenerated in between, nothing is restored.
  • A retry after a failed push keeps the earlier record, but only if nothing was built since.
  • A malformed record is dropped with a warning. Symlinks are skipped.

Verified.

  • test_worktree.py: 46 tests, including 7 new ones, all confirmed to fail on main:
    • a changed middle patch;
    • dropping an empty middle patch;
    • a malformed record;
    • a build in between;
    • a gn gen in between;
    • a retry after a failed push;
    • a retry after a build.
  • test_dev_names.py: 19 tests.
  • On a real Chromium 156 clone: I removed the empty patch from the series and ran worktree.sh sync. It popped 149 patches and printed kept mtimes on 459 unchanged files (2 changed). The next build was 15 steps: 13 string steps (#35's job) and 2 compiles of the two files that really changed. Without the fix the same tree needs at least 1,107. Putting the line back gave 461 unchanged (0 changed) and 15 steps again.
  • Devin SWE-2 wrote it. Claude Opus reviewed it, fixed three defects, and ran the real-tree proof. The defects: the build marker missed gn gen, which is a real wrong-build risk; a retry lost most of the savings; and symlink handling.

Patch surface (D17): no change, because this PR touches no patches.

🤖 Generated with Claude Code


Devin Review


Note

Medium Risk Changes sync/build interaction and mtime restoration logic; incorrect restoration could hide real edits from siso, though build-marker and SHA256 checks mitigate that.

Overview worktree.sh sync now snapshots file mtimes and content hashes before popping patches, then restores prior mtimes after the stack is pushed again for any file that ended up byte-identical—so siso does not treat harmless pop/push rewrites as edits.

worktree_state.py adds snapshot / restore and .rac_mtimes.json, recording paths touched by popped patches plus a build marker (e.g. out/*/.siso_fs_state, .ninja_log, build.ninja, build.ninja.stamp). If a build or gn gen ran between snapshot and restore, restoration is skipped to avoid stale outputs. Failed-sync retries can reuse an earlier snapshot; malformed records are warned and dropped; symlinks are ignored; interrupted restore leaves the record for a retry.

test_worktree.py adds coverage for identical rewrites, dropping empty patches, malformed JSON, build/gn between snapshot and restore, failed-push retry, build-after-failure, and interrupted restore.

Reviewed by Cursor Bugbot for commit 29dfe9dad368f80a99f531a0b6a175bc5fa965b5. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Keeps the mtimes of files worktree.sh sync pops and pushes back, so siso doesn't rebuild files that come back byte-identical from a pop and push.

Bug Fixes

  • Before popping, sync snapshots the mtime and hash of each file the popped patches touch, plus a build marker that covers builds and gn gen.
  • After the push, unmodified files get their old mtime back; changed files keep the new one.
  • A retry after a failed push keeps the earlier record; one after a build drops it.
  • The record now survives an interrupted restore, so a Ctrl-C or failed utime mid-loop can be retried to completion.
  • Malformed records are dropped with a warning; symlinks are skipped.
  • Verified on a Chromium 156 clone: dropping an empty patch 149 patches from the top kept mtimes on 459 of 461 files and cut the next build from at least 1,107 steps to 15.

Written for commit 29dfe9dad368f80a99f531a0b6a175bc5fa965b5. Summary will update on new commits.

View guided diff Turn on auto-fix

Checks

2 failing · 7 successful · 2 neutral · 1 skipped
Some checks failed

Failing2

  • pullfrogGitHub
    Failed
  • VercelGitHub
    Failed
29dfe9d

Added 1 commit

  • fix(devutils): keep mtimes of files sync pops and pushes back unchangedefcce83
Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

External image Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: a28c98d8-489f-4ec5-b82d-6119c7959863

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Synced from GitHub

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 0d99c622-8761-4c1e-876c-10ee5812031f)

Synced from GitHub

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The change affects patch syncing and rebuild work in local worktrees.

The PR appears safe to merge, with a non-blocking recovery issue if restore is interrupted.

Findings

  1. P2 Interrupted restore loses saved times ▶
Fix with agent prompt
markdown
1### Issue 1
2devutils/rac/worktree_state.py:359
3`restore()` deletes the saved record before restoring any file times. If the user presses Ctrl-C during the loop, or a file read or `os.utime()` fails, the remaining saved times are lost. The patches have already been pushed and recorded, so retrying `sync` cannot recover those times. The remaining files trigger an avoidable rebuild.
4
5Keep the record until the loop finishes. Still delete it immediately when the build marker has changed.
6
7---
8
9For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Preserves file times when sync pops and reapplies patches without changing file contents.

  • Worktree sync keeps old times on files that come back unchanged.

Diagram

mermaid
1%%{init: {'theme': 'neutral'}}%%
2flowchart TD
3 A[Revert name passes] --> B[Save hashes, times, and build markers]
4 B --> C[Pop and push patches]
5 C --> D{Push succeeded?}
6 D -- No --> E[Keep record for retry]
7 D -- Yes --> F[Record applied patches]
8 F --> G{Build markers unchanged?}
9 G -- No --> H[Keep new file times]
10 G -- Yes --> I[Restore times where hashes match]
11 H --> J[Apply name passes]
12 I --> J

Reviews (1) · Last reviewed commit: "fix(devutils): keep mtimes of files sync..." · Reviewed by Greptile

devutils/rac/worktree_state.pyline 359Outdated · efcce83Open thread
……@@ -258,6 +265,115 @@ def produces_tree(data):
356+ record = read_mtimes(tree)
357+ if record is None:
358+ return
359+ path.unlink()
Ggreptile-appsBoton line 3595h ago

P2 Interrupted restore loses saved times

restore() deletes the saved record before restoring any file times. If the user presses Ctrl-C during the loop, or a file read or os.utime() fails, the remaining saved times are lost. The patches have already been pushed and recorded, so retrying sync cannot recover those times. The remaining files trigger an avoidable rebuild.

Keep the record until the loop finishes. Still delete it immediately when the build marker has changed.

Prompt To Fix With AI
markdown
1This is a comment left during a code review.
2Path: devutils/rac/worktree_state.py
3Line: 359
4
5Comment:
6**Interrupted restore loses saved times**
7
8`restore()` deletes the saved record before restoring any file times. If the user presses Ctrl-C during the loop, or a file read or `os.utime()` fails, the remaining saved times are lost. The patches have already been pushed and recorded, so retrying `sync` cannot recover those times. The remaining files trigger an avoidable rebuild.
9
10Keep the record until the loop finishes. Still delete it immediately when the build marker has changed.
11
12---
13
14For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Added 1 commit

  • fix(devutils): keep the sync mtime record until restore finishes29dfe9d
Synced from GitHub

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: c18e15ad-ed33-4334-89f1-3a9212209faa)