diff --git a/packages/insomnia/src/insomnia-data/src/models/workspace-meta.ts b/packages/insomnia/src/insomnia-data/src/models/workspace-meta.ts index 2fe5e8b6e97..4276c0b4b03 100644 --- a/packages/insomnia/src/insomnia-data/src/models/workspace-meta.ts +++ b/packages/insomnia/src/insomnia-data/src/models/workspace-meta.ts @@ -19,6 +19,7 @@ export interface BaseWorkspaceMeta { hasUnpushedChanges: boolean; gitFilePath: string | null; gitFileLastSyncTime: number | null; + gitFileLastSyncHash: string | null; } export type WorkspaceMeta = BaseWorkspaceMeta & BaseModel; @@ -35,6 +36,7 @@ export function init(): BaseWorkspaceMeta { gitRepositoryId: null, gitFilePath: null, gitFileLastSyncTime: null, + gitFileLastSyncHash: null, parentId: null, pushSnapshotOnInitialize: false, hasUncommittedChanges: false, diff --git a/packages/insomnia/src/sync/git/repo-file-watcher.ts b/packages/insomnia/src/sync/git/repo-file-watcher.ts index 14a098ef065..2da35890191 100644 --- a/packages/insomnia/src/sync/git/repo-file-watcher.ts +++ b/packages/insomnia/src/sync/git/repo-file-watcher.ts @@ -425,6 +425,13 @@ class RepoFileWatcher { this.lastKnownGitFilePath.set(workspace._id, absPath); const stat = await fs.promises.stat(absPath); this.lastSyncMtime.set(absPath, stat.mtimeMs); + + // Persist tracking state so the maps can be restored after a restart + const workspaceMeta = meta ?? (await services.workspaceMeta.getOrCreateByParentId(workspace._id)); + await services.workspaceMeta.update(workspaceMeta, { + gitFileLastSyncHash: hash, + gitFileLastSyncTime: stat.mtimeMs, + }); } catch (err) { console.warn('[repo-file-watcher] Could not flush workspace to disk:', workspace._id, err); } @@ -534,7 +541,7 @@ class RepoFileWatcher { } await this.deleteOrphans(docs); - await this.upsertDocs(absPath, normalised, result.mtimeMs, docs); + await this.upsertDocs(absPath, normalised, result.mtimeMs, result.hash, docs); this.notifyRenderer(); } @@ -649,6 +656,7 @@ class RepoFileWatcher { absPath: string, normalised: string, syncTime: number, + hash: string, docs: NonNullable['data']>, ): Promise { const bufferId = await db.bufferChanges(); @@ -660,6 +668,7 @@ class RepoFileWatcher { await services.workspaceMeta.update(workspaceMeta, { gitFilePath: this.toPosixRelPath(absPath), gitFileLastSyncTime: syncTime, + gitFileLastSyncHash: hash, }); this.lastKnownGitFilePath.set(doc._id, normalised); } @@ -818,14 +827,17 @@ class RepoFileWatcher { } /** - * Load existing workspace → gitFilePath mappings from the DB so rename - * detection works from the start. + * Load existing workspace → gitFilePath mappings from the DB and restore + * persisted tracking state (mtime + hash) so that the initial + * {@link importAllFiles} call can skip files that haven't changed since + * the last session. * - * Note: we intentionally do NOT pre-scan file mtimes here. The initial - * {@link importAllFiles} call in {@link create} populates both - * `lastSyncMtime` and `lastWrittenHash` as a side-effect of importing. - * Pre-scanning mtimes would cause `importAllFiles` to skip files it - * hasn't actually imported yet. + * Note: pre-seeding `lastWrittenHash` here is safe because + * `importAllFiles` uses `forceRead=true`, which bypasses the mtime + * fast-path but still runs the hash check in {@link readIfChanged}. + * Files whose content matches the persisted hash will be skipped, + * preventing unnecessary workspace re-imports (and modified-timestamp + * changes) on every app restart. */ private async loadKnownGitFilePaths(): Promise { const entries = await this.getWorkspacesWithMeta(); @@ -833,6 +845,12 @@ class RepoFileWatcher { if (meta?.gitFilePath) { const absPath = path.normalize(path.join(this.repoDir, meta.gitFilePath)); this.lastKnownGitFilePath.set(workspace._id, absPath); + if (meta.gitFileLastSyncTime != null) { + this.lastSyncMtime.set(absPath, meta.gitFileLastSyncTime); + } + if (meta.gitFileLastSyncHash != null) { + this.lastWrittenHash.set(absPath, meta.gitFileLastSyncHash); + } } } }