@gitgov/core 2.7.1 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/fs.d.ts +14 -2
- package/dist/src/fs.js +96 -6
- package/dist/src/fs.js.map +1 -1
- package/package.json +1 -1
package/dist/src/fs.d.ts
CHANGED
|
@@ -1248,13 +1248,19 @@ declare class FsWorktreeSyncStateModule implements ISyncStateModule {
|
|
|
1248
1248
|
getWorktreePath(): string;
|
|
1249
1249
|
/** [WTSYNC-A1..A6] Ensures worktree exists and is healthy */
|
|
1250
1250
|
ensureWorktree(): Promise<void>;
|
|
1251
|
+
/**
|
|
1252
|
+
* [WTSYNC-A7] Remove .gitignore from state branch if it exists.
|
|
1253
|
+
* The worktree module filters files in code (shouldSyncFile()), not via .gitignore.
|
|
1254
|
+
* Legacy state branches initialized by FsSyncState may have a .gitignore — remove it.
|
|
1255
|
+
*/
|
|
1256
|
+
private removeLegacyGitignore;
|
|
1251
1257
|
/** Check worktree health */
|
|
1252
1258
|
private checkWorktreeHealth;
|
|
1253
1259
|
/** Remove worktree cleanly */
|
|
1254
1260
|
private removeWorktree;
|
|
1255
|
-
/** [WTSYNC-B1..
|
|
1261
|
+
/** [WTSYNC-B1..B16] Push local state to remote */
|
|
1256
1262
|
pushState(options: SyncStatePushOptions): Promise<SyncStatePushResult>;
|
|
1257
|
-
/** [WTSYNC-C1..
|
|
1263
|
+
/** [WTSYNC-C1..C9] Pull remote state */
|
|
1258
1264
|
pullState(options?: SyncStatePullOptions): Promise<SyncStatePullResult>;
|
|
1259
1265
|
/** [WTSYNC-D1..D7] Resolve rebase conflict */
|
|
1260
1266
|
resolveConflict(options: SyncStateResolveOptions): Promise<SyncStateResolveResult>;
|
|
@@ -1280,6 +1286,12 @@ declare class FsWorktreeSyncStateModule implements ISyncStateModule {
|
|
|
1280
1286
|
private execGit;
|
|
1281
1287
|
/** Execute git command in worktree context (throws on non-zero exit) */
|
|
1282
1288
|
private execInWorktree;
|
|
1289
|
+
/**
|
|
1290
|
+
* [WTSYNC-B16] Check if local gitgov-state has commits not present on remote.
|
|
1291
|
+
* Returns { ahead: true } if local has unpushed commits or remote branch doesn't exist.
|
|
1292
|
+
* Also returns { remoteExists } to let caller decide whether reconciliation is needed.
|
|
1293
|
+
*/
|
|
1294
|
+
private isLocalAheadOfRemote;
|
|
1283
1295
|
/** Calculate file delta (uncommitted changes in worktree) */
|
|
1284
1296
|
private calculateFileDelta;
|
|
1285
1297
|
/** [WTSYNC-B4/B9/B10/B11] Stage only syncable files from delta (adds, mods, and deletions) */
|
package/dist/src/fs.js
CHANGED
|
@@ -7646,7 +7646,7 @@ var FsWorktreeSyncStateModule = class {
|
|
|
7646
7646
|
this.gitgovPath = path9__default.join(this.worktreePath, ".gitgov");
|
|
7647
7647
|
}
|
|
7648
7648
|
// ═══════════════════════════════════════════════
|
|
7649
|
-
// Section A: Worktree Management (WTSYNC-A1..
|
|
7649
|
+
// Section A: Worktree Management (WTSYNC-A1..A7)
|
|
7650
7650
|
// ═══════════════════════════════════════════════
|
|
7651
7651
|
/** [WTSYNC-A4] Returns the worktree path */
|
|
7652
7652
|
getWorktreePath() {
|
|
@@ -7657,6 +7657,7 @@ var FsWorktreeSyncStateModule = class {
|
|
|
7657
7657
|
const health = await this.checkWorktreeHealth();
|
|
7658
7658
|
if (health.healthy) {
|
|
7659
7659
|
logger7.debug("Worktree is healthy");
|
|
7660
|
+
await this.removeLegacyGitignore();
|
|
7660
7661
|
return;
|
|
7661
7662
|
}
|
|
7662
7663
|
if (health.exists && !health.healthy) {
|
|
@@ -7674,6 +7675,26 @@ var FsWorktreeSyncStateModule = class {
|
|
|
7674
7675
|
error instanceof Error ? error : void 0
|
|
7675
7676
|
);
|
|
7676
7677
|
}
|
|
7678
|
+
await this.removeLegacyGitignore();
|
|
7679
|
+
}
|
|
7680
|
+
/**
|
|
7681
|
+
* [WTSYNC-A7] Remove .gitignore from state branch if it exists.
|
|
7682
|
+
* The worktree module filters files in code (shouldSyncFile()), not via .gitignore.
|
|
7683
|
+
* Legacy state branches initialized by FsSyncState may have a .gitignore — remove it.
|
|
7684
|
+
*/
|
|
7685
|
+
async removeLegacyGitignore() {
|
|
7686
|
+
const gitignorePath = path9__default.join(this.worktreePath, ".gitignore");
|
|
7687
|
+
if (!existsSync(gitignorePath)) return;
|
|
7688
|
+
logger7.info("Removing legacy .gitignore from state branch");
|
|
7689
|
+
try {
|
|
7690
|
+
await this.execInWorktree(["rm", ".gitignore"]);
|
|
7691
|
+
await this.execInWorktree(["commit", "-m", "gitgov: remove legacy .gitignore (filtering is in code)"]);
|
|
7692
|
+
} catch {
|
|
7693
|
+
try {
|
|
7694
|
+
await promises.unlink(gitignorePath);
|
|
7695
|
+
} catch {
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7677
7698
|
}
|
|
7678
7699
|
/** Check worktree health */
|
|
7679
7700
|
async checkWorktreeHealth() {
|
|
@@ -7719,9 +7740,9 @@ var FsWorktreeSyncStateModule = class {
|
|
|
7719
7740
|
}
|
|
7720
7741
|
}
|
|
7721
7742
|
// ═══════════════════════════════════════════════
|
|
7722
|
-
// Section B: Push Operations (WTSYNC-B1..
|
|
7743
|
+
// Section B: Push Operations (WTSYNC-B1..B16)
|
|
7723
7744
|
// ═══════════════════════════════════════════════
|
|
7724
|
-
/** [WTSYNC-B1..
|
|
7745
|
+
/** [WTSYNC-B1..B16] Push local state to remote */
|
|
7725
7746
|
async pushState(options) {
|
|
7726
7747
|
const { actorId, dryRun = false, force = false } = options;
|
|
7727
7748
|
const log = (msg) => logger7.debug(`[pushState] ${msg}`);
|
|
@@ -7749,11 +7770,53 @@ var FsWorktreeSyncStateModule = class {
|
|
|
7749
7770
|
const delta = rawDelta.filter((f) => shouldSyncFile2(f.file));
|
|
7750
7771
|
log(`Delta: ${delta.length} syncable files (${rawDelta.length} total)`);
|
|
7751
7772
|
if (delta.length === 0) {
|
|
7773
|
+
const { ahead: aheadOfRemote, remoteExists } = await this.isLocalAheadOfRemote();
|
|
7774
|
+
if (!aheadOfRemote) {
|
|
7775
|
+
return {
|
|
7776
|
+
success: true,
|
|
7777
|
+
filesSynced: 0,
|
|
7778
|
+
sourceBranch: options.sourceBranch ?? "current",
|
|
7779
|
+
commitHash: null,
|
|
7780
|
+
commitMessage: null,
|
|
7781
|
+
conflictDetected: false
|
|
7782
|
+
};
|
|
7783
|
+
}
|
|
7784
|
+
log("No uncommitted changes but local is ahead of remote \u2014 pushing existing commits");
|
|
7785
|
+
if (remoteExists && !force) {
|
|
7786
|
+
try {
|
|
7787
|
+
await this.execInWorktree(["pull", "--rebase", "origin", this.stateBranchName]);
|
|
7788
|
+
} catch (err) {
|
|
7789
|
+
if (await this.isRebaseInProgress()) {
|
|
7790
|
+
const affectedFiles = await this.getConflictedFiles();
|
|
7791
|
+
return {
|
|
7792
|
+
success: false,
|
|
7793
|
+
filesSynced: 0,
|
|
7794
|
+
sourceBranch: options.sourceBranch ?? "current",
|
|
7795
|
+
commitHash: null,
|
|
7796
|
+
commitMessage: null,
|
|
7797
|
+
conflictDetected: true,
|
|
7798
|
+
conflictInfo: {
|
|
7799
|
+
type: "rebase_conflict",
|
|
7800
|
+
affectedFiles,
|
|
7801
|
+
message: "Rebase conflict during push reconciliation (local ahead, no uncommitted changes)",
|
|
7802
|
+
resolutionSteps: [
|
|
7803
|
+
`Edit conflicted files in ${this.worktreePath}/.gitgov/`,
|
|
7804
|
+
'Run `gitgov sync resolve --reason "..."` to finalize'
|
|
7805
|
+
]
|
|
7806
|
+
},
|
|
7807
|
+
error: "Rebase conflict during push reconciliation"
|
|
7808
|
+
};
|
|
7809
|
+
}
|
|
7810
|
+
throw err;
|
|
7811
|
+
}
|
|
7812
|
+
}
|
|
7813
|
+
await this.execInWorktree(["push", "origin", this.stateBranchName]);
|
|
7814
|
+
const currentHead = (await this.execInWorktree(["rev-parse", "HEAD"])).trim();
|
|
7752
7815
|
return {
|
|
7753
7816
|
success: true,
|
|
7754
7817
|
filesSynced: 0,
|
|
7755
7818
|
sourceBranch: options.sourceBranch ?? "current",
|
|
7756
|
-
commitHash:
|
|
7819
|
+
commitHash: currentHead,
|
|
7757
7820
|
commitMessage: null,
|
|
7758
7821
|
conflictDetected: false
|
|
7759
7822
|
};
|
|
@@ -7848,9 +7911,9 @@ var FsWorktreeSyncStateModule = class {
|
|
|
7848
7911
|
return result;
|
|
7849
7912
|
}
|
|
7850
7913
|
// ═══════════════════════════════════════════════
|
|
7851
|
-
// Section C: Pull Operations (WTSYNC-C1..
|
|
7914
|
+
// Section C: Pull Operations (WTSYNC-C1..C9)
|
|
7852
7915
|
// ═══════════════════════════════════════════════
|
|
7853
|
-
/** [WTSYNC-C1..
|
|
7916
|
+
/** [WTSYNC-C1..C9] Pull remote state */
|
|
7854
7917
|
async pullState(options) {
|
|
7855
7918
|
const { forceReindex = false, force = false } = options ?? {};
|
|
7856
7919
|
const log = (msg) => logger7.debug(`[pullState] ${msg}`);
|
|
@@ -8276,6 +8339,33 @@ var FsWorktreeSyncStateModule = class {
|
|
|
8276
8339
|
async execInWorktree(args, options) {
|
|
8277
8340
|
return this.execGit(["-C", this.worktreePath, ...args], options);
|
|
8278
8341
|
}
|
|
8342
|
+
/**
|
|
8343
|
+
* [WTSYNC-B16] Check if local gitgov-state has commits not present on remote.
|
|
8344
|
+
* Returns { ahead: true } if local has unpushed commits or remote branch doesn't exist.
|
|
8345
|
+
* Also returns { remoteExists } to let caller decide whether reconciliation is needed.
|
|
8346
|
+
*/
|
|
8347
|
+
async isLocalAheadOfRemote() {
|
|
8348
|
+
try {
|
|
8349
|
+
await this.execGit(["ls-remote", "--exit-code", "origin", this.stateBranchName]);
|
|
8350
|
+
} catch {
|
|
8351
|
+
return { ahead: true, remoteExists: false };
|
|
8352
|
+
}
|
|
8353
|
+
try {
|
|
8354
|
+
await this.execInWorktree(["fetch", "origin", this.stateBranchName]);
|
|
8355
|
+
} catch {
|
|
8356
|
+
return { ahead: false, remoteExists: true };
|
|
8357
|
+
}
|
|
8358
|
+
try {
|
|
8359
|
+
const count = (await this.execInWorktree([
|
|
8360
|
+
"rev-list",
|
|
8361
|
+
`origin/${this.stateBranchName}..HEAD`,
|
|
8362
|
+
"--count"
|
|
8363
|
+
])).trim();
|
|
8364
|
+
return { ahead: parseInt(count, 10) > 0, remoteExists: true };
|
|
8365
|
+
} catch {
|
|
8366
|
+
return { ahead: true, remoteExists: true };
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8279
8369
|
/** Calculate file delta (uncommitted changes in worktree) */
|
|
8280
8370
|
async calculateFileDelta() {
|
|
8281
8371
|
try {
|