sync-worktrees 3.6.0 → 3.6.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/index.js CHANGED
@@ -2756,10 +2756,11 @@ var WorktreeStatusService = class {
2756
2756
  const reasons = [];
2757
2757
  if (!isClean) reasons.push("uncommitted changes");
2758
2758
  if (hasUnpushedCommits) reasons.push("unpushed commits");
2759
+ if (hasStashedChanges) reasons.push("stashed changes");
2759
2760
  if (hasOperationInProgress) reasons.push("operation in progress");
2760
2761
  if (hasModifiedSubmodules) reasons.push("modified submodules");
2761
2762
  if (upstreamGone) reasons.push("upstream gone");
2762
- const canRemove = isClean && !hasUnpushedCommits && !hasOperationInProgress && !hasModifiedSubmodules;
2763
+ const canRemove = isClean && !hasUnpushedCommits && !hasStashedChanges && !hasOperationInProgress && !hasModifiedSubmodules;
2763
2764
  const details = includeDetails ? this.buildStatusDetails(snap) : void 0;
2764
2765
  return {
2765
2766
  isClean,
@@ -3808,7 +3809,7 @@ var GitService = class {
3808
3809
  const bareGit = this.getCachedGit(this.bareRepoPath);
3809
3810
  const checkRef = async (ref) => {
3810
3811
  try {
3811
- await bareGit.raw(["show-ref", "--verify", "--quiet", ref]);
3812
+ await bareGit.raw(["show-ref", "--verify", ref]);
3812
3813
  return true;
3813
3814
  } catch {
3814
3815
  return false;
@@ -3825,10 +3826,22 @@ var GitService = class {
3825
3826
  const branches = await bareGit.branch();
3826
3827
  return branches.all;
3827
3828
  }
3829
+ async resolveCreateBranchBaseRef(bareGit, baseBranch) {
3830
+ const candidates = baseBranch.startsWith(GIT_CONSTANTS.REMOTE_PREFIX) || baseBranch.startsWith("refs/") ? [baseBranch] : [`${GIT_CONSTANTS.REMOTE_PREFIX}${baseBranch}`, baseBranch];
3831
+ for (const candidate of candidates) {
3832
+ try {
3833
+ await bareGit.revparse(["--verify", candidate]);
3834
+ return candidate;
3835
+ } catch {
3836
+ }
3837
+ }
3838
+ return candidates[0];
3839
+ }
3828
3840
  async createBranch(branchName, baseBranch) {
3829
3841
  const bareGit = this.getCachedGit(this.bareRepoPath);
3830
- await bareGit.raw(["branch", branchName, `origin/${baseBranch}`]);
3831
- this.logger.info(`Created branch '${branchName}' from '${baseBranch}'`);
3842
+ const baseRef = await this.resolveCreateBranchBaseRef(bareGit, baseBranch);
3843
+ await bareGit.raw(["branch", branchName, baseRef]);
3844
+ this.logger.info(`Created branch '${branchName}' from '${baseRef}'`);
3832
3845
  }
3833
3846
  async pushBranch(branchName) {
3834
3847
  const bareGit = this.getCachedGit(this.bareRepoPath);
@@ -3940,17 +3953,9 @@ var WorktreeSyncService = class {
3940
3953
  this.progressListeners.add(listener);
3941
3954
  return () => this.progressListeners.delete(listener);
3942
3955
  }
3943
- emitProgress(event) {
3944
- for (const listener of this.progressListeners) {
3945
- try {
3946
- listener(event);
3947
- } catch {
3948
- }
3949
- }
3950
- }
3951
- async sync() {
3956
+ async runExclusiveRepoOperation(operation) {
3952
3957
  if (this.syncInProgress) {
3953
- this.logger.warn("\u26A0\uFE0F Sync already in progress, skipping...");
3958
+ this.logger.warn("\u26A0\uFE0F Another repository operation is already in progress, skipping...");
3954
3959
  return { started: false, reason: "in_progress" };
3955
3960
  }
3956
3961
  const release = await this.acquireBareLock();
@@ -3959,36 +3964,55 @@ var WorktreeSyncService = class {
3959
3964
  return { started: false, reason: "locked" };
3960
3965
  }
3961
3966
  this.syncInProgress = true;
3962
- this.logger.info(`[${(/* @__PURE__ */ new Date()).toISOString()}] Starting worktree synchronization...`);
3963
- const totalTimer = new Timer();
3964
- const phaseTimer = new PhaseTimer();
3965
- const syncContext = { lfsSkipEnabled: false };
3966
- const retryOptions = this.createRetryOptions(syncContext);
3967
3967
  try {
3968
- await retry(() => this.runSyncAttempt(phaseTimer, syncContext), retryOptions);
3969
- } catch (error) {
3970
- this.logger.error("\n\u274C Error during worktree synchronization after all retry attempts:", error);
3971
- throw error;
3968
+ return { started: true, value: await operation() };
3972
3969
  } finally {
3973
- if (syncContext.lfsSkipEnabled && !this.config.skipLfs) {
3974
- this.gitService.setLfsSkipEnabled(false);
3975
- }
3976
3970
  this.syncInProgress = false;
3977
3971
  try {
3978
3972
  await release();
3979
3973
  } catch (releaseError) {
3980
3974
  this.logger.warn(`Failed to release sync lock: ${getErrorMessage(releaseError)}`);
3981
3975
  }
3982
- this.logger.info(`[${(/* @__PURE__ */ new Date()).toISOString()}] Synchronization finished.
3983
- `);
3984
- if (this.config.debug) {
3985
- const totalDuration = totalTimer.stop();
3986
- const phaseResults = phaseTimer.getResults();
3987
- const repoName = this.config.name;
3988
- this.logger.table(formatTimingTable(totalDuration, phaseResults, repoName));
3976
+ }
3977
+ }
3978
+ emitProgress(event) {
3979
+ for (const listener of this.progressListeners) {
3980
+ try {
3981
+ listener(event);
3982
+ } catch {
3989
3983
  }
3990
3984
  }
3991
- return { started: true };
3985
+ }
3986
+ async sync() {
3987
+ const result = await this.runExclusiveRepoOperation(async () => {
3988
+ if (!this.isInitialized()) {
3989
+ await this.initialize();
3990
+ }
3991
+ this.logger.info(`[${(/* @__PURE__ */ new Date()).toISOString()}] Starting worktree synchronization...`);
3992
+ const totalTimer = new Timer();
3993
+ const phaseTimer = new PhaseTimer();
3994
+ const syncContext = { lfsSkipEnabled: false };
3995
+ const retryOptions = this.createRetryOptions(syncContext);
3996
+ try {
3997
+ await retry(() => this.runSyncAttempt(phaseTimer, syncContext), retryOptions);
3998
+ } catch (error) {
3999
+ this.logger.error("\n\u274C Error during worktree synchronization after all retry attempts:", error);
4000
+ throw error;
4001
+ } finally {
4002
+ if (syncContext.lfsSkipEnabled && !this.config.skipLfs) {
4003
+ this.gitService.setLfsSkipEnabled(false);
4004
+ }
4005
+ this.logger.info(`[${(/* @__PURE__ */ new Date()).toISOString()}] Synchronization finished.
4006
+ `);
4007
+ if (this.config.debug) {
4008
+ const totalDuration = totalTimer.stop();
4009
+ const phaseResults = phaseTimer.getResults();
4010
+ const repoName = this.config.name;
4011
+ this.logger.table(formatTimingTable(totalDuration, phaseResults, repoName));
4012
+ }
4013
+ }
4014
+ });
4015
+ return result.started ? { started: true } : result;
3992
4016
  }
3993
4017
  async acquireBareLock() {
3994
4018
  if (process.env.NODE_ENV === ENV_CONSTANTS.NODE_ENV_TEST) {
@@ -4000,15 +4024,9 @@ var WorktreeSyncService = class {
4000
4024
  };
4001
4025
  }
4002
4026
  const barePath = this.gitService.getBareRepoPath();
4003
- const lockTarget = path8.join(barePath, "HEAD");
4004
- try {
4005
- await fs6.access(lockTarget);
4006
- } catch {
4007
- return async () => {
4008
- };
4009
- }
4027
+ await fs6.mkdir(barePath, { recursive: true });
4010
4028
  try {
4011
- const release = await lockfile.lock(lockTarget, {
4029
+ const release = await lockfile.lock(barePath, {
4012
4030
  stale: DEFAULT_CONFIG.LOCK_STALE_MS,
4013
4031
  update: DEFAULT_CONFIG.LOCK_UPDATE_MS,
4014
4032
  retries: 0,