@ -128,7 +128,7 @@ func (e *errMergeConflict) Error() string {
return fmt . Sprintf ( "conflict detected at: %s" , e . filename )
return fmt . Sprintf ( "conflict detected at: %s" , e . filename )
}
}
func attemptMerge ( ctx context . Context , file * unmergedFile , tmpBasePath string , gitRepo * git . Repository ) error {
func attemptMerge ( ctx context . Context , file * unmergedFile , tmpBasePath string , filesToRemove * [ ] string , filesToAdd * [ ] git . IndexObjectInfo ) error {
log . Trace ( "Attempt to merge:\n%v" , file )
log . Trace ( "Attempt to merge:\n%v" , file )
switch {
switch {
@ -142,14 +142,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
}
}
// Not a genuine conflict and we can simply remove the file from the index
// Not a genuine conflict and we can simply remove the file from the index
return gitRepo . RemoveFilesFromIndex ( file . stage1 . path )
* filesToRemove = append ( * filesToRemove , file . stage1 . path )
return nil
case file . stage1 == nil && file . stage2 != nil && ( file . stage3 == nil || file . stage2 . SameAs ( file . stage3 ) ) :
case file . stage1 == nil && file . stage2 != nil && ( file . stage3 == nil || file . stage2 . SameAs ( file . stage3 ) ) :
// 2. Added in ours but not in theirs or identical in both
// 2. Added in ours but not in theirs or identical in both
//
//
// Not a genuine conflict just add to the index
// Not a genuine conflict just add to the index
if err := gitRepo . AddObjectToIndex ( file . stage2 . mode , git . MustIDFromString ( file . stage2 . sha ) , file . stage2 . path ) ; err != nil {
* filesToAdd = append ( * filesToAdd , git . IndexObjectInfo { Mode : file . stage2 . mode , Object : git . MustIDFromString ( file . stage2 . sha ) , Filename : file . stage2 . path } )
return err
}
return nil
return nil
case file . stage1 == nil && file . stage2 != nil && file . stage3 != nil && file . stage2 . sha == file . stage3 . sha && file . stage2 . mode != file . stage3 . mode :
case file . stage1 == nil && file . stage2 != nil && file . stage3 != nil && file . stage2 . sha == file . stage3 . sha && file . stage2 . mode != file . stage3 . mode :
// 3. Added in both with the same sha but the modes are different
// 3. Added in both with the same sha but the modes are different
@ -160,7 +159,8 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
// 4. Added in theirs but not ours:
// 4. Added in theirs but not ours:
//
//
// Not a genuine conflict just add to the index
// Not a genuine conflict just add to the index
return gitRepo . AddObjectToIndex ( file . stage3 . mode , git . MustIDFromString ( file . stage3 . sha ) , file . stage3 . path )
* filesToAdd = append ( * filesToAdd , git . IndexObjectInfo { Mode : file . stage3 . mode , Object : git . MustIDFromString ( file . stage3 . sha ) , Filename : file . stage3 . path } )
return nil
case file . stage1 == nil :
case file . stage1 == nil :
// 5. Created by new in both
// 5. Created by new in both
//
//
@ -221,7 +221,8 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
return err
return err
}
}
hash = strings . TrimSpace ( hash )
hash = strings . TrimSpace ( hash )
return gitRepo . AddObjectToIndex ( file . stage2 . mode , git . MustIDFromString ( hash ) , file . stage2 . path )
* filesToAdd = append ( * filesToAdd , git . IndexObjectInfo { Mode : file . stage2 . mode , Object : git . MustIDFromString ( hash ) , Filename : file . stage2 . path } )
return nil
default :
default :
if file . stage1 != nil {
if file . stage1 != nil {
return & errMergeConflict { file . stage1 . path }
return & errMergeConflict { file . stage1 . path }
@ -245,6 +246,9 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
return false , nil , fmt . Errorf ( "unable to run read-tree -m! Error: %w" , err )
return false , nil , fmt . Errorf ( "unable to run read-tree -m! Error: %w" , err )
}
}
var filesToRemove [ ] string
var filesToAdd [ ] git . IndexObjectInfo
// Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles
// Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles
unmerged := make ( chan * unmergedFile )
unmerged := make ( chan * unmergedFile )
go unmergedFiles ( ctx , gitPath , unmerged )
go unmergedFiles ( ctx , gitPath , unmerged )
@ -270,7 +274,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
}
}
// OK now we have the unmerged file triplet attempt to merge it
// OK now we have the unmerged file triplet attempt to merge it
if err := attemptMerge ( ctx , file , gitPath , gitRepo ) ; err != nil {
if err := attemptMerge ( ctx , file , gitPath , & filesToRemove , & filesToAdd ) ; err != nil {
if conflictErr , ok := err . ( * errMergeConflict ) ; ok {
if conflictErr , ok := err . ( * errMergeConflict ) ; ok {
log . Trace ( "Conflict: %s in %s" , conflictErr . filename , description )
log . Trace ( "Conflict: %s in %s" , conflictErr . filename , description )
conflict = true
conflict = true
@ -283,6 +287,15 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
return false , nil , err
return false , nil , err
}
}
}
}
// Add and remove files in one command, as this is slow with many files otherwise
if err := gitRepo . RemoveFilesFromIndex ( filesToRemove ... ) ; err != nil {
return false , nil , err
}
if err := gitRepo . AddObjectsToIndex ( filesToAdd ... ) ; err != nil {
return false , nil , err
}
return conflict , conflictedFiles , nil
return conflict , conflictedFiles , nil
}
}