fix(git): make post-mutation status refresh authoritative (#2281)
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
co-authored by
Serhii Dziupin
parent
fe331c093b
commit
696349d606
@@ -9,6 +9,7 @@ import type {
|
||||
} from '@/lib/api/types';
|
||||
import { getDeferredSafeStorage } from '@/stores/utils/safeStorage';
|
||||
import { getRuntimeKey } from '@/lib/runtime-switch';
|
||||
import { subscribeGitStatusInvalidations } from '@/lib/gitStatusInvalidation';
|
||||
|
||||
const LOG_STALE_THRESHOLD = 10000;
|
||||
const REPO_CHECK_STALE_THRESHOLD = 60_000;
|
||||
@@ -57,7 +58,7 @@ interface GitStore {
|
||||
setActiveDirectory: (directory: string | null) => void;
|
||||
getDirectoryState: (directory: string) => DirectoryGitState | null;
|
||||
|
||||
fetchStatus: (directory: string, git: GitAPI, options?: { silent?: boolean; mode?: 'light' }) => Promise<boolean>;
|
||||
fetchStatus: (directory: string, git: GitAPI, options?: { silent?: boolean; mode?: 'light'; force?: boolean }) => Promise<boolean>;
|
||||
fetchBranches: (directory: string, git: GitAPI) => Promise<void>;
|
||||
fetchLog: (directory: string, git: GitAPI, maxCount?: number) => Promise<void>;
|
||||
fetchIdentity: (directory: string, git: GitAPI) => Promise<void>;
|
||||
@@ -99,7 +100,7 @@ interface GitAPI {
|
||||
|
||||
const inFlightDiffFetchesByDirectory = new Map<string, Set<string>>();
|
||||
const diffFetchGenerationByDirectory = new Map<string, number>();
|
||||
const inFlightStatusFetches = new Map<string, Promise<boolean>>();
|
||||
const inFlightStatusFetches = new Map<string, { promise: Promise<boolean>; statusMutationRevision: number }>();
|
||||
const inFlightEnsureAllByDirectory = new Map<string, Promise<void>>();
|
||||
const requestGenerationByChannel = new Map<string, number>();
|
||||
const statusMutationRevisionByDirectory = new Map<string, number>();
|
||||
@@ -150,6 +151,18 @@ const bumpStatusMutationRevision = (runtimeKey: string, directory: string): void
|
||||
statusMutationRevisionByDirectory.set(key, (statusMutationRevisionByDirectory.get(key) ?? 0) + 1);
|
||||
};
|
||||
|
||||
const getStatusMutationRevision = (runtimeKey: string, directory: string): number =>
|
||||
statusMutationRevisionByDirectory.get(runtimeDirectoryKey(runtimeKey, directory)) ?? 0;
|
||||
|
||||
// A successful status-affecting git mutation invalidates the runtime adapter's
|
||||
// status cache (see lib/gitStatusInvalidation.ts). Bump the per-directory
|
||||
// mutation revision so a status request admitted before the mutation can
|
||||
// neither be joined by a post-mutation refresh nor commit its stale payload
|
||||
// over the refreshed state.
|
||||
subscribeGitStatusInvalidations((directory) => {
|
||||
bumpStatusMutationRevision(getRuntimeKey(), directory);
|
||||
});
|
||||
|
||||
const getDiffFetchGeneration = (directory: string): number =>
|
||||
diffFetchGenerationByDirectory.get(runtimeDirectoryKey(getRuntimeKey(), directory)) ?? 0;
|
||||
|
||||
@@ -590,10 +603,16 @@ export const useGitStore = create<GitStore>()(
|
||||
const statusFetchMode: GitStatusFetchMode = options.mode ?? 'full';
|
||||
const runtimeKey = getRuntimeKey();
|
||||
const statusFetchKey = getStatusFetchKey(runtimeKey, directory, statusFetchMode);
|
||||
const existing = inFlightStatusFetches.get(statusFetchKey)
|
||||
?? (statusFetchMode === 'light' ? inFlightStatusFetches.get(getStatusFetchKey(runtimeKey, directory, 'full')) : undefined);
|
||||
if (existing) {
|
||||
return existing;
|
||||
const statusMutationRevision = getStatusMutationRevision(runtimeKey, directory);
|
||||
if (!options.force) {
|
||||
const existing = inFlightStatusFetches.get(statusFetchKey)
|
||||
?? (statusFetchMode === 'light' ? inFlightStatusFetches.get(getStatusFetchKey(runtimeKey, directory, 'full')) : undefined);
|
||||
// Join an in-flight request only when it was admitted at the current
|
||||
// mutation revision; a request that predates a mutation must not
|
||||
// satisfy the post-mutation refresh.
|
||||
if (existing && existing.statusMutationRevision === statusMutationRevision) {
|
||||
return existing.promise;
|
||||
}
|
||||
}
|
||||
|
||||
const token = startRequest(directory, 'status', true);
|
||||
@@ -727,12 +746,12 @@ export const useGitStore = create<GitStore>()(
|
||||
return statusChanged;
|
||||
})();
|
||||
|
||||
inFlightStatusFetches.set(statusFetchKey, fetchPromise);
|
||||
inFlightStatusFetches.set(statusFetchKey, { promise: fetchPromise, statusMutationRevision });
|
||||
|
||||
try {
|
||||
return await fetchPromise;
|
||||
} finally {
|
||||
if (inFlightStatusFetches.get(statusFetchKey) === fetchPromise) {
|
||||
if (inFlightStatusFetches.get(statusFetchKey)?.promise === fetchPromise) {
|
||||
inFlightStatusFetches.delete(statusFetchKey);
|
||||
}
|
||||
}
|
||||
@@ -936,8 +955,11 @@ export const useGitStore = create<GitStore>()(
|
||||
const { force = false, silentIfCached = false } = options;
|
||||
const now = Date.now();
|
||||
|
||||
// `force` applies to status as well as log: a forced refresh must not
|
||||
// resolve from an in-flight status request admitted earlier.
|
||||
await get().fetchStatus(directory, git, {
|
||||
silent: silentIfCached && Boolean(dirState?.status),
|
||||
force,
|
||||
});
|
||||
|
||||
const updatedDirState = get().directories.get(directory);
|
||||
|
||||
Reference in New Issue
Block a user