edsger 0.21.6 ā 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.
- package/dist/phases/code-implementation/index.js +8 -2
- package/dist/phases/code-refine/index.js +6 -1
- package/dist/phases/code-review/index.js +6 -1
- package/dist/services/branches.d.ts +10 -12
- package/dist/services/branches.js +21 -29
- package/dist/utils/git-branch-manager.d.ts +12 -1
- package/dist/utils/git-branch-manager.js +20 -12
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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 (
|
|
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 (
|
|
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,
|
|
@@ -61,22 +61,20 @@ export declare function getNextPendingBranch(options: PipelinePhaseOptions): Pro
|
|
|
61
61
|
export declare function allBranchesCompleted(options: PipelinePhaseOptions): Promise<boolean>;
|
|
62
62
|
/**
|
|
63
63
|
* Get the base branch information for a branch.
|
|
64
|
-
* Returns:
|
|
65
|
-
* - If base_branch_id is null: use main (first branch in chain)
|
|
66
|
-
* - If base_branch_id is set and that branch is completed: use main
|
|
67
|
-
* (completed means feat merged to main)
|
|
68
|
-
* - If base_branch_id is set and that branch is merged: use base branch's feat branch
|
|
69
|
-
* (merged means dev merged to feat, not to main)
|
|
70
|
-
* - If base_branch_id is set and that branch is not merged: use base branch's dev branch
|
|
71
64
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* -
|
|
76
|
-
* -
|
|
65
|
+
* Rebase strategies based on base branch status:
|
|
66
|
+
* - no base_branch_id: git rebase main (first branch in chain)
|
|
67
|
+
* - completed (feat merged to main): git rebase main
|
|
68
|
+
* - merged (dev merged to feat): create from feat/xxx, then git rebase --onto main feat/xxx
|
|
69
|
+
* - in_progress/pending: git rebase dev/xxx (base on parent dev branch)
|
|
70
|
+
*
|
|
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
|
|
77
74
|
*/
|
|
78
75
|
export declare function getBaseBranchInfo(branch: Branch, allBranches: Branch[], mainBranch?: string): Promise<{
|
|
79
76
|
baseBranch: string;
|
|
77
|
+
rebaseTargetBranch?: string;
|
|
80
78
|
originalBaseBranch?: string;
|
|
81
79
|
needsRebase: boolean;
|
|
82
80
|
baseBranchMerged: boolean;
|
|
@@ -160,19 +160,16 @@ export async function allBranchesCompleted(options) {
|
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
162
|
* Get the base branch information for a branch.
|
|
163
|
-
* Returns:
|
|
164
|
-
* - If base_branch_id is null: use main (first branch in chain)
|
|
165
|
-
* - If base_branch_id is set and that branch is completed: use main
|
|
166
|
-
* (completed means feat merged to main)
|
|
167
|
-
* - If base_branch_id is set and that branch is merged: use base branch's feat branch
|
|
168
|
-
* (merged means dev merged to feat, not to main)
|
|
169
|
-
* - If base_branch_id is set and that branch is not merged: use base branch's dev branch
|
|
170
163
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* -
|
|
175
|
-
* -
|
|
164
|
+
* Rebase strategies based on base branch status:
|
|
165
|
+
* - no base_branch_id: git rebase main (first branch in chain)
|
|
166
|
+
* - completed (feat merged to main): git rebase main
|
|
167
|
+
* - merged (dev merged to feat): create from feat/xxx, then git rebase --onto main feat/xxx
|
|
168
|
+
* - in_progress/pending: git rebase dev/xxx (base on parent dev branch)
|
|
169
|
+
*
|
|
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
|
|
176
173
|
*/
|
|
177
174
|
export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main') {
|
|
178
175
|
// No base branch - start from main (first branch in chain)
|
|
@@ -195,27 +192,21 @@ export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main'
|
|
|
195
192
|
}
|
|
196
193
|
// Check if base branch is completed (feat merged to main)
|
|
197
194
|
if (baseBranch.status === 'completed') {
|
|
198
|
-
// Base branch's feat is merged to main
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
baseBranch: mainBranch,
|
|
203
|
-
needsRebase: false,
|
|
204
|
-
baseBranchMerged: true,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
const featBranchName = baseBranch.branch_name.replace(/^dev\//, 'feat/');
|
|
195
|
+
// Base branch's feat is already squash-merged to main
|
|
196
|
+
// Just rebase directly to main - no need for --onto
|
|
197
|
+
// Main already contains all the changes, so a normal rebase will work correctly
|
|
208
198
|
return {
|
|
209
199
|
baseBranch: mainBranch,
|
|
210
|
-
originalBaseBranch
|
|
211
|
-
needsRebase:
|
|
200
|
+
// Don't set originalBaseBranch - we want a normal rebase, not --onto
|
|
201
|
+
needsRebase: false,
|
|
212
202
|
baseBranchMerged: true,
|
|
213
203
|
};
|
|
214
204
|
}
|
|
215
|
-
// Check if base branch is merged (dev merged to feat)
|
|
205
|
+
// Check if base branch is merged (dev merged to feat, but feat not yet merged to main)
|
|
216
206
|
if (baseBranch.status === 'merged') {
|
|
217
|
-
// Base branch's dev is merged to feat
|
|
218
|
-
//
|
|
207
|
+
// Base branch's dev is squash-merged to feat
|
|
208
|
+
// New branches should be created from feat/xxx (which has the squashed commits)
|
|
209
|
+
// Use --onto to rebase only new commits (after feat branch) onto main
|
|
219
210
|
if (!baseBranch.branch_name) {
|
|
220
211
|
return {
|
|
221
212
|
baseBranch: mainBranch,
|
|
@@ -225,8 +216,9 @@ export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main'
|
|
|
225
216
|
}
|
|
226
217
|
const featBranchName = baseBranch.branch_name.replace(/^dev\//, 'feat/');
|
|
227
218
|
return {
|
|
228
|
-
baseBranch: featBranchName,
|
|
229
|
-
|
|
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
|
|
230
222
|
needsRebase: true,
|
|
231
223
|
baseBranchMerged: true,
|
|
232
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
|
|
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
|
|
787
|
+
// Rebase feature branch with latest target branch
|
|
785
788
|
if (verbose) {
|
|
786
789
|
if (originalBaseBranch) {
|
|
787
|
-
logInfo(`š„ Rebasing ${featureBranch} onto origin/${
|
|
790
|
+
logInfo(`š„ Rebasing ${featureBranch} onto origin/${actualRebaseTarget} (from ${originalBaseBranch})...`);
|
|
788
791
|
}
|
|
789
792
|
else {
|
|
790
|
-
logInfo(`š„ Rebasing ${featureBranch} with origin/${
|
|
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
|
|
801
|
-
execSync(`git fetch origin ${
|
|
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/${
|
|
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/${
|
|
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/${
|
|
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/${
|
|
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 ${
|
|
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 ${
|
|
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
|
}
|