Skip to content

Commit c08f3a7

Browse files
Ivan GorshkovIvan Gorshkov
authored andcommitted
multitarget conflict resolution fix
1 parent 9d96db4 commit c08f3a7

File tree

1 file changed

+78
-2
lines changed

1 file changed

+78
-2
lines changed

internal/registercustomcode/registercustomcode.go

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,40 @@ func ResolveCustomCodeConflicts(ctx context.Context) error {
276276

277277
hadConflicts := false
278278

279+
// First pass: identify targets with conflicts and unstage those without conflicts
280+
// This is necessary because git add . stages everything, including targets that weren't regenerated
281+
for targetName, target := range wf.Targets {
282+
outDir := getTargetOutput(target)
283+
284+
// Check if this target has conflicted files
285+
checkConflictCmd := exec.Command("git", "diff", "--name-only", "--diff-filter=U", "--", outDir)
286+
conflictCheckOutput, err := checkConflictCmd.Output()
287+
if err != nil {
288+
return fmt.Errorf("failed to check for conflicts in target %s: %w", targetName, err)
289+
}
290+
291+
hasConflicts := strings.TrimSpace(string(conflictCheckOutput)) != ""
292+
293+
if !hasConflicts {
294+
// Unstage and restore this target's files to prevent them from being included in conflict resolution
295+
// This is necessary because the target was never regenerated in this run
296+
logger.Info(fmt.Sprintf("Target %s has no conflicts, unstaging and restoring its files to HEAD", targetName))
297+
298+
// First unstage
299+
unstageCmd := exec.Command("git", "reset", "--", outDir)
300+
if output, err := unstageCmd.CombinedOutput(); err != nil {
301+
logger.Warn(fmt.Sprintf("Failed to unstage target %s: %v\nOutput: %s", targetName, err, string(output)))
302+
}
303+
304+
// Then restore to HEAD state (removes modifications from working directory)
305+
checkoutCmd := exec.Command("git", "checkout", "HEAD", "--", outDir)
306+
if output, err := checkoutCmd.CombinedOutput(); err != nil {
307+
logger.Warn(fmt.Sprintf("Failed to restore target %s to HEAD: %v\nOutput: %s", targetName, err, string(output)))
308+
}
309+
}
310+
}
311+
312+
// Second pass: process targets with conflicts
279313
for targetName, target := range wf.Targets {
280314
outDir := getTargetOutput(target)
281315

@@ -289,6 +323,19 @@ func ResolveCustomCodeConflicts(ctx context.Context) error {
289323
continue
290324
}
291325

326+
// Check if this target actually has conflicted files from the current generation
327+
// Only targets that were regenerated and have conflicts should be processed
328+
checkConflictCmd := exec.Command("git", "diff", "--name-only", "--diff-filter=U", "--", outDir)
329+
conflictCheckOutput, err := checkConflictCmd.Output()
330+
if err != nil {
331+
return fmt.Errorf("failed to check for conflicts in target %s: %w", targetName, err)
332+
}
333+
334+
if strings.TrimSpace(string(conflictCheckOutput)) == "" {
335+
logger.Info(fmt.Sprintf("Target %s has no conflicts (not regenerated in this run), skipping conflict resolution", targetName))
336+
continue
337+
}
338+
292339
logger.Info(fmt.Sprintf("Resolving conflicts for target %s", targetName))
293340

294341
// Step 1: Undo patch application - extract clean new generation from "ours" side
@@ -425,19 +472,48 @@ func completeConflictResolution(ctx context.Context, wf *workflow.Workflow) erro
425472

426473
logger.Info("Completing conflict resolution registration")
427474

475+
// First, identify which targets were part of the conflict resolution
476+
targetsInResolution := make(map[string]bool)
477+
for targetName, target := range wf.Targets {
478+
outDir := getTargetOutput(target)
479+
checkCommitCmd := exec.Command("git", "log", "-1", "--grep=clean generation (conflict resolution)", "--format=%H", "--", outDir)
480+
commitOutput, err := checkCommitCmd.Output()
481+
if err == nil && strings.TrimSpace(string(commitOutput)) != "" {
482+
targetsInResolution[targetName] = true
483+
logger.Info(fmt.Sprintf("Target %s was part of conflict resolution", targetName))
484+
}
485+
}
486+
428487
targetPatches, err := getPatchesPerTarget(wf)
429488
if err != nil {
430489
return err
431490
}
432491

433-
for _, target := range wf.Targets {
492+
// Only revert patches for targets that were part of conflict resolution
493+
for targetName, target := range wf.Targets {
494+
if !targetsInResolution[targetName] {
495+
logger.Info(fmt.Sprintf("Skipping patch revert for target %s (not part of conflict resolution)", targetName))
496+
continue
497+
}
498+
434499
if err := RevertCustomCodePatch(ctx, target); err != nil {
435-
return fmt.Errorf("failed to revert custom code patch: %w", err)
500+
// If reverting fails, it might be because the patch was already removed (user accepted ours)
501+
// Log but continue - we'll handle this in the next phase
502+
logger.Warn(fmt.Sprintf("Could not revert patch for target %s (may already be reverted): %v", targetName, err))
436503
}
437504
}
438505

439506
for targetName, target := range wf.Targets {
440507
if targetPatches[targetName] == "" {
508+
// Check if this target was part of the conflict resolution using the map we built earlier
509+
if !targetsInResolution[targetName] {
510+
// This target was not part of conflict resolution (wasn't regenerated)
511+
// Keep its existing patch unchanged
512+
logger.Info(fmt.Sprintf("Target %s was not part of conflict resolution, preserving existing patch", targetName))
513+
continue
514+
}
515+
516+
// Target was part of conflict resolution but has no new patches
441517
// Check if there's actually a patch to clean up
442518
if patchFileExists(getTargetOutput(target)) {
443519
fmt.Printf("No changes detected for target %s after conflict resolution, cleaning up patch registration\n", targetName)

0 commit comments

Comments
 (0)