temp for tree extraction
This commit is contained in:
187
internal/git/conflicts.go
Normal file
187
internal/git/conflicts.go
Normal file
@ -0,0 +1,187 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/index"
|
||||
)
|
||||
|
||||
// ConflictInfo represents information about a merge conflict
|
||||
type ConflictInfo struct {
|
||||
FilePath string
|
||||
OursHash string
|
||||
TheirsHash string
|
||||
BaseHash string
|
||||
HasConflict bool
|
||||
}
|
||||
|
||||
// ConflictResolver handles conflict detection and resolution guidance
|
||||
type ConflictResolver struct {
|
||||
repo *gogit.Repository
|
||||
repoPath string
|
||||
}
|
||||
|
||||
// NewConflictResolver creates a new ConflictResolver instance
|
||||
func NewConflictResolver(repo *gogit.Repository, repoPath string) *ConflictResolver {
|
||||
return &ConflictResolver{
|
||||
repo: repo,
|
||||
repoPath: repoPath,
|
||||
}
|
||||
}
|
||||
|
||||
// DetectConflicts checks for merge conflicts in the working tree
|
||||
func (cr *ConflictResolver) DetectConflicts() ([]ConflictInfo, error) {
|
||||
idx, err := cr.repo.Storer.Index()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read index: %w", err)
|
||||
}
|
||||
|
||||
conflicts := []ConflictInfo{}
|
||||
|
||||
// Check for conflicts in the index
|
||||
for _, entry := range idx.Entries {
|
||||
// Stage > 0 indicates a conflict
|
||||
if entry.Stage != 0 {
|
||||
// Find all stages for this file
|
||||
conflict := cr.findConflictStages(idx, entry.Name)
|
||||
if conflict.HasConflict {
|
||||
conflicts = append(conflicts, conflict)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts, nil
|
||||
}
|
||||
|
||||
// findConflictStages finds all conflict stages for a file
|
||||
func (cr *ConflictResolver) findConflictStages(idx *index.Index, path string) ConflictInfo {
|
||||
conflict := ConflictInfo{
|
||||
FilePath: path,
|
||||
HasConflict: false,
|
||||
}
|
||||
|
||||
for _, entry := range idx.Entries {
|
||||
if entry.Name == path {
|
||||
switch entry.Stage {
|
||||
case 1:
|
||||
// Base/common ancestor
|
||||
conflict.BaseHash = entry.Hash.String()
|
||||
conflict.HasConflict = true
|
||||
case 2:
|
||||
// Ours (current branch)
|
||||
conflict.OursHash = entry.Hash.String()
|
||||
conflict.HasConflict = true
|
||||
case 3:
|
||||
// Theirs (incoming branch)
|
||||
conflict.TheirsHash = entry.Hash.String()
|
||||
conflict.HasConflict = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conflict
|
||||
}
|
||||
|
||||
// HasConflicts checks if there are any conflicts in the working tree
|
||||
func (cr *ConflictResolver) HasConflicts() (bool, error) {
|
||||
conflicts, err := cr.DetectConflicts()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return len(conflicts) > 0, nil
|
||||
}
|
||||
|
||||
// PresentConflicts presents conflicts to the user with clear guidance
|
||||
func (cr *ConflictResolver) PresentConflicts(conflicts []ConflictInfo) string {
|
||||
if len(conflicts) == 0 {
|
||||
return "No conflicts detected."
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(fmt.Sprintf("\n%s\n", strings.Repeat("=", 70)))
|
||||
sb.WriteString(fmt.Sprintf(" MERGE CONFLICTS DETECTED (%d file(s))\n", len(conflicts)))
|
||||
sb.WriteString(fmt.Sprintf("%s\n\n", strings.Repeat("=", 70)))
|
||||
|
||||
for i, conflict := range conflicts {
|
||||
sb.WriteString(fmt.Sprintf("%d. %s\n", i+1, conflict.FilePath))
|
||||
sb.WriteString(fmt.Sprintf(" Base: %s\n", conflict.BaseHash[:8]))
|
||||
sb.WriteString(fmt.Sprintf(" Ours: %s\n", conflict.OursHash[:8]))
|
||||
sb.WriteString(fmt.Sprintf(" Theirs: %s\n", conflict.TheirsHash[:8]))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
sb.WriteString("To resolve conflicts:\n")
|
||||
sb.WriteString(" 1. Edit the conflicting files to resolve conflicts\n")
|
||||
sb.WriteString(" 2. Look for conflict markers: <<<<<<<, =======, >>>>>>>\n")
|
||||
sb.WriteString(" 3. Remove the conflict markers after resolving\n")
|
||||
sb.WriteString(" 4. Stage the resolved files: git add <file>\n")
|
||||
sb.WriteString(" 5. Continue the rebase: git rebase --continue\n")
|
||||
sb.WriteString(fmt.Sprintf("%s\n", strings.Repeat("=", 70)))
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// GetConflictMarkers reads a file and extracts conflict marker sections
|
||||
func (cr *ConflictResolver) GetConflictMarkers(filePath string) ([]ConflictMarker, error) {
|
||||
fullPath := filepath.Join(cr.repoPath, filePath)
|
||||
file, err := os.Open(fullPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
markers := []ConflictMarker{}
|
||||
scanner := bufio.NewScanner(file)
|
||||
lineNum := 0
|
||||
var currentMarker *ConflictMarker
|
||||
|
||||
for scanner.Scan() {
|
||||
lineNum++
|
||||
line := scanner.Text()
|
||||
|
||||
if strings.HasPrefix(line, "<<<<<<<") {
|
||||
// Start of conflict
|
||||
currentMarker = &ConflictMarker{
|
||||
FilePath: filePath,
|
||||
StartLine: lineNum,
|
||||
}
|
||||
} else if strings.HasPrefix(line, "=======") && currentMarker != nil {
|
||||
currentMarker.SeparatorLine = lineNum
|
||||
} else if strings.HasPrefix(line, ">>>>>>>") && currentMarker != nil {
|
||||
currentMarker.EndLine = lineNum
|
||||
markers = append(markers, *currentMarker)
|
||||
currentMarker = nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error reading file: %w", err)
|
||||
}
|
||||
|
||||
return markers, nil
|
||||
}
|
||||
|
||||
// ConflictMarker represents a conflict marker section in a file
|
||||
type ConflictMarker struct {
|
||||
FilePath string
|
||||
StartLine int
|
||||
SeparatorLine int
|
||||
EndLine int
|
||||
}
|
||||
|
||||
// IsFileConflicted checks if a specific file has conflict markers
|
||||
func (cr *ConflictResolver) IsFileConflicted(filePath string) (bool, error) {
|
||||
markers, err := cr.GetConflictMarkers(filePath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return len(markers) > 0, nil
|
||||
}
|
Reference in New Issue
Block a user