edsger 0.21.7 → 0.21.8

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.
@@ -114,6 +114,7 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
114
114
  // Determine the actual base branch for branch chaining
115
115
  // If the current feature branch depends on another branch, use that as the base
116
116
  let actualBaseBranch = baseBranch;
117
+ let rebaseTargetBranch;
117
118
  let originalBaseBranch;
118
119
  if (currentBranch && currentBranch.base_branch_id) {
119
120
  try {
@@ -123,11 +124,15 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
123
124
  });
124
125
  const baseBranchInfo = await getBaseBranchInfo(currentBranch, allBranches, baseBranch);
125
126
  actualBaseBranch = baseBranchInfo.baseBranch;
127
+ rebaseTargetBranch = baseBranchInfo.rebaseTargetBranch;
126
128
  originalBaseBranch = baseBranchInfo.originalBaseBranch;
127
129
  if (verbose) {
128
130
  logInfo(`šŸ”— Branch chaining detected:`);
129
131
  logInfo(` Current branch: ${currentBranch.name}`);
130
- logInfo(` Base branch: ${actualBaseBranch}`);
132
+ logInfo(` Base branch (for new branches): ${actualBaseBranch}`);
133
+ if (rebaseTargetBranch) {
134
+ logInfo(` Rebase target: ${rebaseTargetBranch}`);
135
+ }
131
136
  if (originalBaseBranch) {
132
137
  logInfo(` Original base branch: ${originalBaseBranch} (for --onto rebase)`);
133
138
  }
@@ -183,7 +188,8 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
183
188
  const { cleanup: cleanupGit } = await prepareCustomBranchGitEnvironmentAsync({
184
189
  featureBranch: devBranchName,
185
190
  baseBranch: actualBaseBranch,
186
- originalBaseBranch, // For --onto rebase when base branch was squash-merged
191
+ rebaseTargetBranch, // For --onto rebase target (e.g., main) when base branch is merged
192
+ originalBaseBranch, // For --onto rebase starting point when base branch was squash-merged
187
193
  verbose,
188
194
  forcePushAfterRebase: featSyncedToMain, // Trigger GitHub to recalculate PR diff
189
195
  });
@@ -76,6 +76,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
76
76
  let currentBranch = null;
77
77
  let allBranches = [];
78
78
  let baseBranchForRebase = 'main'; // Default base branch for rebase
79
+ let rebaseTargetBranchForRebase; // Target for --onto rebase (e.g., main)
79
80
  let originalBaseBranchForRebase; // For --onto when base was squash-merged
80
81
  try {
81
82
  // Get all branches to determine base branch info
@@ -92,9 +93,12 @@ export const refineCodeFromPRFeedback = async (options, config) => {
92
93
  const baseBranchInfo = await getBaseBranchInfo(currentBranch, allBranches, 'main');
93
94
  // Always use the baseBranch from getBaseBranchInfo - it handles all cases:
94
95
  // - No base branch: returns 'main'
95
- // - Base branch merged: returns feat branch (dev/xxx -> feat/xxx)
96
+ // - Base branch merged: returns feat branch (new branches created from feat/xxx)
97
+ // - Base branch completed: returns main
96
98
  // - Base branch not merged: returns dev branch
97
99
  baseBranchForRebase = baseBranchInfo.baseBranch;
100
+ // When base branch is merged, rebaseTargetBranch is 'main' (target for --onto)
101
+ rebaseTargetBranchForRebase = baseBranchInfo.rebaseTargetBranch;
98
102
  // When base branch is merged (squash merge), we need originalBaseBranch for --onto
99
103
  originalBaseBranchForRebase = baseBranchInfo.originalBaseBranch;
100
104
  if (verbose) {
@@ -162,6 +166,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
162
166
  ? await prepareCustomBranchGitEnvironmentAsync({
163
167
  featureBranch: branchName,
164
168
  baseBranch: baseBranchForRebase,
169
+ rebaseTargetBranch: rebaseTargetBranchForRebase,
165
170
  originalBaseBranch: originalBaseBranchForRebase,
166
171
  verbose,
167
172
  resolveConflicts: true,
@@ -128,6 +128,7 @@ export const reviewPullRequest = async (options, config) => {
128
128
  let currentBranch = null;
129
129
  let allBranches = [];
130
130
  let baseBranchForRebase = 'main'; // Default base branch for rebase
131
+ let rebaseTargetBranchForRebase; // Target for --onto rebase (e.g., main)
131
132
  let originalBaseBranchForRebase; // For --onto when base was squash-merged
132
133
  try {
133
134
  // Get all branches to determine base branch info
@@ -145,9 +146,12 @@ export const reviewPullRequest = async (options, config) => {
145
146
  const baseBranchInfo = await getBaseBranchInfo(currentBranch, allBranches, 'main');
146
147
  // Always use the baseBranch from getBaseBranchInfo - it handles all cases:
147
148
  // - No base branch: returns 'main'
148
- // - Base branch merged: returns feat branch (dev/xxx -> feat/xxx)
149
+ // - Base branch merged: returns feat branch (new branches created from feat/xxx)
150
+ // - Base branch completed: returns main
149
151
  // - Base branch not merged: returns dev branch
150
152
  baseBranchForRebase = baseBranchInfo.baseBranch;
153
+ // When base branch is merged, rebaseTargetBranch is 'main' (target for --onto)
154
+ rebaseTargetBranchForRebase = baseBranchInfo.rebaseTargetBranch;
151
155
  // When base branch is merged (squash merge), we need originalBaseBranch for --onto
152
156
  originalBaseBranchForRebase = baseBranchInfo.originalBaseBranch;
153
157
  if (verbose) {
@@ -215,6 +219,7 @@ export const reviewPullRequest = async (options, config) => {
215
219
  ? await prepareCustomBranchGitEnvironmentAsync({
216
220
  featureBranch: branchName,
217
221
  baseBranch: baseBranchForRebase,
222
+ rebaseTargetBranch: rebaseTargetBranchForRebase,
218
223
  originalBaseBranch: originalBaseBranchForRebase,
219
224
  verbose,
220
225
  resolveConflicts: true,
@@ -65,14 +65,16 @@ export declare function allBranchesCompleted(options: PipelinePhaseOptions): Pro
65
65
  * Rebase strategies based on base branch status:
66
66
  * - no base_branch_id: git rebase main (first branch in chain)
67
67
  * - completed (feat merged to main): git rebase main
68
- * - merged (dev merged to feat): git rebase --onto main feat/xxx
68
+ * - merged (dev merged to feat): create from feat/xxx, then git rebase --onto main feat/xxx
69
69
  * - in_progress/pending: git rebase dev/xxx (base on parent dev branch)
70
70
  *
71
- * For 'merged' status, we use --onto to rebase only new commits (after feat branch)
72
- * onto main, avoiding conflicts from re-applying squashed commits.
71
+ * For 'merged' status:
72
+ * - New branches are created from feat/xxx (which contains squashed commits)
73
+ * - We use --onto to rebase only new commits (after feat branch) onto main
73
74
  */
74
75
  export declare function getBaseBranchInfo(branch: Branch, allBranches: Branch[], mainBranch?: string): Promise<{
75
76
  baseBranch: string;
77
+ rebaseTargetBranch?: string;
76
78
  originalBaseBranch?: string;
77
79
  needsRebase: boolean;
78
80
  baseBranchMerged: boolean;
@@ -164,11 +164,12 @@ export async function allBranchesCompleted(options) {
164
164
  * Rebase strategies based on base branch status:
165
165
  * - no base_branch_id: git rebase main (first branch in chain)
166
166
  * - completed (feat merged to main): git rebase main
167
- * - merged (dev merged to feat): git rebase --onto main feat/xxx
167
+ * - merged (dev merged to feat): create from feat/xxx, then git rebase --onto main feat/xxx
168
168
  * - in_progress/pending: git rebase dev/xxx (base on parent dev branch)
169
169
  *
170
- * For 'merged' status, we use --onto to rebase only new commits (after feat branch)
171
- * onto main, avoiding conflicts from re-applying squashed commits.
170
+ * For 'merged' status:
171
+ * - New branches are created from feat/xxx (which contains squashed commits)
172
+ * - We use --onto to rebase only new commits (after feat branch) onto main
172
173
  */
173
174
  export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main') {
174
175
  // No base branch - start from main (first branch in chain)
@@ -204,8 +205,8 @@ export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main'
204
205
  // Check if base branch is merged (dev merged to feat, but feat not yet merged to main)
205
206
  if (baseBranch.status === 'merged') {
206
207
  // Base branch's dev is squash-merged to feat
208
+ // New branches should be created from feat/xxx (which has the squashed commits)
207
209
  // Use --onto to rebase only new commits (after feat branch) onto main
208
- // This correctly handles the case where feat contains squashed commits
209
210
  if (!baseBranch.branch_name) {
210
211
  return {
211
212
  baseBranch: mainBranch,
@@ -215,8 +216,9 @@ export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main'
215
216
  }
216
217
  const featBranchName = baseBranch.branch_name.replace(/^dev\//, 'feat/');
217
218
  return {
218
- baseBranch: mainBranch, // Rebase onto main
219
- originalBaseBranch: featBranchName, // Use feat branch for --onto
219
+ baseBranch: featBranchName, // Create new branches from feat/xxx
220
+ rebaseTargetBranch: mainBranch, // Rebase onto main
221
+ originalBaseBranch: featBranchName, // Use feat branch for --onto starting point
220
222
  needsRebase: true,
221
223
  baseBranchMerged: true,
222
224
  };
@@ -174,10 +174,21 @@ export declare function syncFeatBranchWithMain(featBranch: string, githubToken:
174
174
  */
175
175
  export interface RebaseWithConflictResolutionOptions {
176
176
  featureBranch: string;
177
+ /**
178
+ * Base branch to create new branches from.
179
+ * For merged parent branches, this is feat/xxx.
180
+ * For completed parent branches, this is main.
181
+ */
177
182
  baseBranch?: string;
183
+ /**
184
+ * Target branch for --onto rebase.
185
+ * When set, uses `git rebase --onto rebaseTargetBranch originalBaseBranch`.
186
+ * If not set, baseBranch is used as the rebase target.
187
+ */
188
+ rebaseTargetBranch?: string;
178
189
  /**
179
190
  * Original base branch before squash merge.
180
- * When set, uses `git rebase --onto baseBranch originalBaseBranch` to only
191
+ * When set, uses `git rebase --onto <target> originalBaseBranch` to only
181
192
  * rebase commits that are new relative to originalBaseBranch.
182
193
  * This is required when the base branch was squash-merged, otherwise
183
194
  * git will try to reapply all the original commits causing many conflicts.
@@ -689,7 +689,10 @@ export async function syncFeatBranchWithMain(featBranch, githubToken, owner, rep
689
689
  * @returns Object containing the previous branch name and conflict resolution result
690
690
  */
691
691
  export async function switchToFeatureBranchAndRebaseAsync(options) {
692
- const { featureBranch, baseBranch = 'main', originalBaseBranch, verbose, resolveConflicts = false, conflictResolverConfig, forcePushAfterRebase = false, } = options;
692
+ const { featureBranch, baseBranch = 'main', rebaseTargetBranch, originalBaseBranch, verbose, resolveConflicts = false, conflictResolverConfig, forcePushAfterRebase = false, } = options;
693
+ // Determine the actual target branch for rebase
694
+ // If rebaseTargetBranch is set (e.g., main), use it; otherwise use baseBranch
695
+ const actualRebaseTarget = rebaseTargetBranch || baseBranch;
693
696
  const previousBranch = getCurrentBranch();
694
697
  if (verbose) {
695
698
  logInfo(`\nšŸ”„ Preparing feature branch: ${featureBranch}`);
@@ -781,13 +784,13 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
781
784
  logInfo(`āš ļø Could not sync with remote feature branch, continuing with local branch`);
782
785
  }
783
786
  }
784
- // Rebase feature branch with latest base branch
787
+ // Rebase feature branch with latest target branch
785
788
  if (verbose) {
786
789
  if (originalBaseBranch) {
787
- logInfo(`šŸ“„ Rebasing ${featureBranch} onto origin/${baseBranch} (from ${originalBaseBranch})...`);
790
+ logInfo(`šŸ“„ Rebasing ${featureBranch} onto origin/${actualRebaseTarget} (from ${originalBaseBranch})...`);
788
791
  }
789
792
  else {
790
- logInfo(`šŸ“„ Rebasing ${featureBranch} with origin/${baseBranch}...`);
793
+ logInfo(`šŸ“„ Rebasing ${featureBranch} with origin/${actualRebaseTarget}...`);
791
794
  }
792
795
  }
793
796
  try {
@@ -797,8 +800,8 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
797
800
  }
798
801
  resetUncommittedChanges(verbose);
799
802
  }
800
- // Fetch the base branch first
801
- execSync(`git fetch origin ${baseBranch}`, {
803
+ // Fetch the target branch first
804
+ execSync(`git fetch origin ${actualRebaseTarget}`, {
802
805
  encoding: 'utf-8',
803
806
  stdio: 'pipe',
804
807
  });
@@ -807,23 +810,28 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
807
810
  // This prevents re-applying commits that were already included in the squash
808
811
  // Command: git rebase --onto <new-base> <old-base>
809
812
  // This rebases commits from <old-base>..HEAD onto <new-base>
813
+ // Also fetch the originalBaseBranch for the --onto reference
814
+ execSync(`git fetch origin ${originalBaseBranch}`, {
815
+ encoding: 'utf-8',
816
+ stdio: 'pipe',
817
+ });
810
818
  if (verbose) {
811
819
  logInfo(` Using --onto to rebase only new commits (after ${originalBaseBranch})`);
812
820
  }
813
- execSync(`git rebase --onto origin/${baseBranch} origin/${originalBaseBranch}`, {
821
+ execSync(`git rebase --onto origin/${actualRebaseTarget} origin/${originalBaseBranch}`, {
814
822
  encoding: 'utf-8',
815
823
  stdio: verbose ? 'inherit' : 'pipe',
816
824
  });
817
825
  }
818
826
  else {
819
827
  // Normal rebase
820
- execSync(`git rebase origin/${baseBranch}`, {
828
+ execSync(`git rebase origin/${actualRebaseTarget}`, {
821
829
  encoding: 'utf-8',
822
830
  stdio: verbose ? 'inherit' : 'pipe',
823
831
  });
824
832
  }
825
833
  if (verbose) {
826
- logInfo(`āœ… Successfully rebased ${featureBranch} with origin/${baseBranch}\n`);
834
+ logInfo(`āœ… Successfully rebased ${featureBranch} with origin/${actualRebaseTarget}\n`);
827
835
  }
828
836
  // Force push after rebase to trigger GitHub to recalculate PR diff
829
837
  if (forcePushAfterRebase) {
@@ -866,7 +874,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
866
874
  });
867
875
  if (result.success) {
868
876
  if (verbose) {
869
- logInfo(`āœ… Successfully resolved conflicts and rebased ${featureBranch} with origin/${baseBranch}\n`);
877
+ logInfo(`āœ… Successfully resolved conflicts and rebased ${featureBranch} with origin/${actualRebaseTarget}\n`);
870
878
  }
871
879
  // Force push after rebase to trigger GitHub to recalculate PR diff
872
880
  if (forcePushAfterRebase) {
@@ -898,7 +906,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
898
906
  else {
899
907
  // Conflict resolution failed, abort rebase
900
908
  abortRebase(verbose);
901
- throw new Error(`Failed to automatically resolve conflicts during rebase ${featureBranch} with ${baseBranch}.\n` +
909
+ throw new Error(`Failed to automatically resolve conflicts during rebase ${featureBranch} with ${actualRebaseTarget}.\n` +
902
910
  `${result.error || 'Unknown error'}\n` +
903
911
  `Please resolve conflicts manually and try again.`);
904
912
  }
@@ -909,7 +917,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
909
917
  if (verbose) {
910
918
  logError(`āŒ Rebase failed and was aborted. Repository is in clean state.`);
911
919
  }
912
- throw new Error(`Failed to rebase ${featureBranch} with ${baseBranch}: ${error instanceof Error ? error.message : String(error)}\n` +
920
+ throw new Error(`Failed to rebase ${featureBranch} with ${actualRebaseTarget}: ${error instanceof Error ? error.message : String(error)}\n` +
913
921
  `This usually means there are conflicts between your feature branch and base branch.\n` +
914
922
  `Please resolve conflicts manually and try again.`);
915
923
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.21.7",
3
+ "version": "0.21.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"