Skip to content

fix: fix file monitoring lost after recreate save - #556

Draft
dengzhongyuan365-dev wants to merge 1 commit into
linuxdeepin:release/1071from
dengzhongyuan365-dev:agent/bugfix/378833
Draft

dengzhongyuan365-dev wants to merge 1 commit into
linuxdeepin:release/1071from
dengzhongyuan365-dev:agent/bugfix/378833

Conversation

@dengzhongyuan365-dev

@dengzhongyuan365-dev dengzhongyuan365-dev commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

fix: fix file monitoring lost after recreate save

  1. When editing a file in an archive with an external editor using
    delete+recreate save semantics, slotOpenFileChanged permanently
    removed the file from monitoring, preventing future change prompts
  2. addWatchFile in openFileWatcher.cpp had an inverted condition
    (!= instead of ==) and wrong key (m_strOpenFile instead of strFile)
  3. Replace immediate removal with QTimer::singleShot delayed re-check
    to restore monitoring when the file reappears after 2 seconds

Log: fix file change monitoring lost after delete+recreate save

Influence:

  1. Test editing files in archives with editors using delete+recreate
  2. Test editing files in archives with editors using in-place save
  3. Test multiple consecutive edits to the same file in an archive

fix: 修复删除重建保存后文件监控丢失

  1. 使用外部编辑器以"先删除再创建"方式保存压缩包内文件时,
    slotOpenFileChanged 首次编辑后永久移除文件监控,后续不再提示
  2. openFileWatcher.cpp 的 addWatchFile 条件反转(!= 应为 ==)且
    键名错误(m_strOpenFile 应为 strFile),修改状态未正确初始化
  3. 将直接移除监控改为 QTimer::singleShot 延迟重检,文件重新
    出现时恢复监控

Log: 修复外部编辑器删除重建保存后文件监控丢失

Influence:

  1. 测试使用删除+重建方式保存的编辑器编辑压缩包内文件
  2. 测试使用原地修改方式保存的编辑器编辑压缩包内文件
  3. 测试对压缩包内同一文件连续多次编辑

PMS: BUG-378833

Summary by Sourcery

Preserve file change detection across delete-and-recreate saves while correcting watcher state tracking.

Bug Fixes:

  • Restore archive file change monitoring after external editors save by deleting and recreating the file.
  • Correct file monitoring state initialization so newly watched files are tracked properly.

Enhancements:

  • Delay handling of temporarily missing files and remove monitoring only when they do not reappear.

1. When editing a file in an archive with an external editor using
   delete+recreate save semantics, slotOpenFileChanged permanently
   removed the file from monitoring, preventing future change prompts
2. addWatchFile in openFileWatcher.cpp had an inverted condition
   (!= instead of ==) and wrong key (m_strOpenFile instead of strFile)
3. Replace immediate removal with QTimer::singleShot delayed re-check
   to restore monitoring when the file reappears after 2 seconds

Log: fix file change monitoring lost after delete+recreate save

Influence:
1. Test editing files in archives with editors using delete+recreate
2. Test editing files in archives with editors using in-place save
3. Test multiple consecutive edits to the same file in an archive

fix: 修复删除重建保存后文件监控丢失

1. 使用外部编辑器以"先删除再创建"方式保存压缩包内文件时,
   slotOpenFileChanged 首次编辑后永久移除文件监控,后续不再提示
2. openFileWatcher.cpp 的 addWatchFile 条件反转(!= 应为 ==)且
   键名错误(m_strOpenFile 应为 strFile),修改状态未正确初始化
3. 将直接移除监控改为 QTimer::singleShot 延迟重检,文件重新
   出现时恢复监控

Log: 修复外部编辑器删除重建保存后文件监控丢失

Influence:
1. 测试使用删除+重建方式保存的编辑器编辑压缩包内文件
2. 测试使用原地修改方式保存的编辑器编辑压缩包内文件
3. 测试对压缩包内同一文件连续多次编辑

PMS: BUG-378833
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: dengzhongyuan365-dev

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@dengzhongyuan365-dev
dengzhongyuan365-dev marked this pull request as draft November 13, 2026 17:16
@sourcery-ai

sourcery-ai Bot commented Nov 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implementation

Initialize each watched file's modification state under its own path, then defer removal after a missing-file notification and restore the watch if the file reappears.

  • src/source/common/openFileWatcher.cpp L47-48
  • src/source/mainwindow.cpp L51, L2953-2963

Use tar's uncompressed output mode before piping the archive stream to pigz, avoiding an unnecessary gzip step.

  • 3rdparty/libpigzplugin/libpigzplugin.cpp L122

Populate the last-modified timestamp for the single-file archive entry.

  • 3rdparty/libsinglefileplugin/singlefile/singlefileplugin.cpp L60

Reject drag-and-drop append attempts when a local shortcut points to a non-local target.

  • src/main.cpp L287-293

Configuration

Add a manually triggered workflow that calls the shared changelog updater and reports its outputs.

  • .github/workflows/call-update-changelog.yml L1-53

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@dengzhongyuan365-dev
dengzhongyuan365-dev changed the base branch from master to release/1071 November 13, 2026 17:17

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 6 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/source/mainwindow.cpp" line_range="2956-2957" />
<code_context>
-                mapStatus.remove(strPath);
-                mapPassword.remove(strPath);
+                mapStatus[strPath] = false;
+                QTimer::singleShot(2000, this, [this, strPath]() {
+                    if (QFile::exists(strPath)) {
+                        m_pOpenFileWatcher->addWatchFile(strPath);
+                    } else {
</code_context>
<issue_to_address>
**Stale files update wrong archive**

When the archive is reset or switched before the two-second callback runs and the old extracted file still exists, the queued `QTimer::singleShot` callback calls `addWatchFile(strPath)` after `resetMainwindow()` clears the watcher state, so later changes are handled against the current archive and can prompt or apply the stale file to the wrong archive.

Cancel the pending callback or check that the same archive is still active before re-adding the watch.
</issue_to_address>

### Comment 2
<location path="src/source/mainwindow.cpp" line_range="2956-2958" />
<code_context>
-                mapStatus.remove(strPath);
-                mapPassword.remove(strPath);
+                mapStatus[strPath] = false;
+                QTimer::singleShot(2000, this, [this, strPath]() {
+                    if (QFile::exists(strPath)) {
+                        m_pOpenFileWatcher->addWatchFile(strPath);
+                    } else {
+                        QMap<QString, bool> &status = m_pOpenFileWatcher->getFileHasModified();
</code_context>
<issue_to_address>
**Confirmed updates are not saved**

When the user chooses Update while the file is temporarily absent, the missing-file branch returns before `addFiles2Archive`; the timer only restores monitoring, so the confirmed update is not written to the archive unless another edit triggers a new prompt.

Defer the requested archive update until the missing file is available again.
</issue_to_address>

### Comment 3
<location path="src/source/mainwindow.cpp" line_range="2956-2959" />
<code_context>
-                mapStatus.remove(strPath);
-                mapPassword.remove(strPath);
+                mapStatus[strPath] = false;
+                QTimer::singleShot(2000, this, [this, strPath]() {
+                    if (QFile::exists(strPath)) {
+                        m_pOpenFileWatcher->addWatchFile(strPath);
+                    } else {
+                        QMap<QString, bool> &status = m_pOpenFileWatcher->getFileHasModified();
+                        status.remove(strPath);
</code_context>
<issue_to_address>
**Later edits stop prompting**

When the editor recreates the file more than two seconds after the delayed check, the callback removes `strPath` from the status map at its sole check; when the file appears later, no watch or retry detects it, so subsequent edits stop prompting the user.

Keep monitoring the missing path with a directory watch or retry until the file is recreated.

Also at `src/source/mainwindow.cpp:2961`.
</issue_to_address>

### Comment 4
<location path="src/source/mainwindow.cpp" line_range="2956-2958" />
<code_context>
-                mapStatus.remove(strPath);
-                mapPassword.remove(strPath);
+                mapStatus[strPath] = false;
+                QTimer::singleShot(2000, this, [this, strPath]() {
+                    if (QFile::exists(strPath)) {
+                        m_pOpenFileWatcher->addWatchFile(strPath);
+                    } else {
+                        QMap<QString, bool> &status = m_pOpenFileWatcher->getFileHasModified();
</code_context>
<issue_to_address>
**Edits during the gap go unnoticed**

When the file is recreated and edited again before the delayed recheck, `QFileSystemWatcher` has no watch during the delay, and the callback only re-adds it; the intervening change is not replayed, so the user receives no prompt for that edit.

Detect changes that occur during the unwatched interval when restoring the watch.
</issue_to_address>

### Comment 5
<location path="src/source/mainwindow.cpp" line_range="2954" />
<code_context>
         if (1 == iResult) {
-            // 对重命名或删除导致的文件不存在的情况,不给出任何提示,且从文件监控中去掉此文件
+            // 文件不存在可能是编辑器"先删除再创建"的保存方式,延迟重检而非直接移除监控
             if (!QFile::exists(strPath)) {
-                mapStatus.remove(strPath);
-                mapPassword.remove(strPath);
</code_context>
<issue_to_address>
**Cancel leaves the file unwatched**

When the user cancels while a delete-and-recreate save has temporarily removed the file, `slotOpenFileChanged` schedules the delayed recheck only in the Update branch; after Cancel, `addWatchFile` runs while the path is missing and no retry restores monitoring, so later edits do not prompt the user.

Schedule the delayed recheck for a missing file regardless of whether the user chooses Update or Cancel.

Also at `src/source/mainwindow.cpp:2956-2964`.
</issue_to_address>

### Comment 6
<location path="src/source/mainwindow.cpp" line_range="2957-2958" />
<code_context>
-                mapPassword.remove(strPath);
+                mapStatus[strPath] = false;
+                QTimer::singleShot(2000, this, [this, strPath]() {
+                    if (QFile::exists(strPath)) {
+                        m_pOpenFileWatcher->addWatchFile(strPath);
+                    } else {
+                        QMap<QString, bool> &status = m_pOpenFileWatcher->getFileHasModified();
</code_context>
<issue_to_address>
**Recreation race loses watch**

When the external editor deletes or replaces the file between the existence check and `addPath()`, the file can disappear after `QFile::exists()` succeeds but before `addWatchFile()` calls `addPath()`. `addPath()` then fails while the status remains false, leaving no watch to detect a later recreation.

Check whether `addPath()` succeeded and retry or monitor the parent directory when it fails.

Also at `src/source/common/openFileWatcher.cpp:44`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/source/mainwindow.cpp
Comment on lines +2956 to +2957
QTimer::singleShot(2000, this, [this, strPath]() {
if (QFile::exists(strPath)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Stale files update wrong archive

When the archive is reset or switched before the two-second callback runs and the old extracted file still exists, the queued QTimer::singleShot callback calls addWatchFile(strPath) after resetMainwindow() clears the watcher state, so later changes are handled against the current archive and can prompt or apply the stale file to the wrong archive.

Cancel the pending callback or check that the same archive is still active before re-adding the watch.

Prompt for AI agents
In `src/source/mainwindow.cpp` at lines 2956-2957:

**Stale files update wrong archive**

When the archive is reset or switched before the two-second callback runs and the old extracted file still exists, the queued `QTimer::singleShot` callback calls `addWatchFile(strPath)` after `resetMainwindow()` clears the watcher state, so later changes are handled against the current archive and can prompt or apply the stale file to the wrong archive.

Cancel the pending callback or check that the same archive is still active before re-adding the watch.

Comment thread src/source/mainwindow.cpp
Comment on lines +2956 to +2958
QTimer::singleShot(2000, this, [this, strPath]() {
if (QFile::exists(strPath)) {
m_pOpenFileWatcher->addWatchFile(strPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Confirmed updates are not saved

When the user chooses Update while the file is temporarily absent, the missing-file branch returns before addFiles2Archive; the timer only restores monitoring, so the confirmed update is not written to the archive unless another edit triggers a new prompt.

Defer the requested archive update until the missing file is available again.

Prompt for AI agents
In `src/source/mainwindow.cpp` at lines 2956-2958:

**Confirmed updates are not saved**

When the user chooses Update while the file is temporarily absent, the missing-file branch returns before `addFiles2Archive`; the timer only restores monitoring, so the confirmed update is not written to the archive unless another edit triggers a new prompt.

Defer the requested archive update until the missing file is available again.

Comment thread src/source/mainwindow.cpp
Comment on lines +2956 to +2959
QTimer::singleShot(2000, this, [this, strPath]() {
if (QFile::exists(strPath)) {
m_pOpenFileWatcher->addWatchFile(strPath);
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Later edits stop prompting

When the editor recreates the file more than two seconds after the delayed check, the callback removes strPath from the status map at its sole check; when the file appears later, no watch or retry detects it, so subsequent edits stop prompting the user.

Keep monitoring the missing path with a directory watch or retry until the file is recreated.

Also at src/source/mainwindow.cpp:2961.

Prompt for AI agents
In `src/source/mainwindow.cpp` at lines 2956-2959:

**Later edits stop prompting**

When the editor recreates the file more than two seconds after the delayed check, the callback removes `strPath` from the status map at its sole check; when the file appears later, no watch or retry detects it, so subsequent edits stop prompting the user.

Keep monitoring the missing path with a directory watch or retry until the file is recreated.

Also at `src/source/mainwindow.cpp:2961`.

Comment thread src/source/mainwindow.cpp
Comment on lines +2956 to +2958
QTimer::singleShot(2000, this, [this, strPath]() {
if (QFile::exists(strPath)) {
m_pOpenFileWatcher->addWatchFile(strPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Edits during the gap go unnoticed

When the file is recreated and edited again before the delayed recheck, QFileSystemWatcher has no watch during the delay, and the callback only re-adds it; the intervening change is not replayed, so the user receives no prompt for that edit.

Detect changes that occur during the unwatched interval when restoring the watch.

Prompt for AI agents
In `src/source/mainwindow.cpp` at lines 2956-2958:

**Edits during the gap go unnoticed**

When the file is recreated and edited again before the delayed recheck, `QFileSystemWatcher` has no watch during the delay, and the callback only re-adds it; the intervening change is not replayed, so the user receives no prompt for that edit.

Detect changes that occur during the unwatched interval when restoring the watch.

Comment thread src/source/mainwindow.cpp
if (1 == iResult) {
// 对重命名或删除导致的文件不存在的情况,不给出任何提示,且从文件监控中去掉此文件
// 文件不存在可能是编辑器"先删除再创建"的保存方式,延迟重检而非直接移除监控
if (!QFile::exists(strPath)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Cancel leaves the file unwatched

When the user cancels while a delete-and-recreate save has temporarily removed the file, slotOpenFileChanged schedules the delayed recheck only in the Update branch; after Cancel, addWatchFile runs while the path is missing and no retry restores monitoring, so later edits do not prompt the user.

Schedule the delayed recheck for a missing file regardless of whether the user chooses Update or Cancel.

Also at src/source/mainwindow.cpp:2956-2964.

Prompt for AI agents
In `src/source/mainwindow.cpp` at line 2954:

**Cancel leaves the file unwatched**

When the user cancels while a delete-and-recreate save has temporarily removed the file, `slotOpenFileChanged` schedules the delayed recheck only in the Update branch; after Cancel, `addWatchFile` runs while the path is missing and no retry restores monitoring, so later edits do not prompt the user.

Schedule the delayed recheck for a missing file regardless of whether the user chooses Update or Cancel.

Also at `src/source/mainwindow.cpp:2956-2964`.

Comment thread src/source/mainwindow.cpp
Comment on lines +2957 to +2958
if (QFile::exists(strPath)) {
m_pOpenFileWatcher->addWatchFile(strPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Recreation race loses watch

When the external editor deletes or replaces the file between the existence check and addPath(), the file can disappear after QFile::exists() succeeds but before addWatchFile() calls addPath(). addPath() then fails while the status remains false, leaving no watch to detect a later recreation.

Check whether addPath() succeeded and retry or monitor the parent directory when it fails.

Also at src/source/common/openFileWatcher.cpp:44.

Prompt for AI agents
In `src/source/mainwindow.cpp` at lines 2957-2958:

**Recreation race loses watch**

When the external editor deletes or replaces the file between the existence check and `addPath()`, the file can disappear after `QFile::exists()` succeeds but before `addWatchFile()` calls `addPath()`. `addPath()` then fails while the status remains false, leaving no watch to detect a later recreation.

Check whether `addPath()` succeeded and retry or monitor the parent directory when it fails.

Also at `src/source/common/openFileWatcher.cpp:44`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants