Skip to content

Commit

Permalink
Update the old merge method to go through the new support
Browse files Browse the repository at this point in the history
This fixes the file path in the diff always being `file.txt`, as well as (I suspect) taking into account what's in the index instead of going straight to the ODB.
  • Loading branch information
tiennou committed Mar 24, 2019
1 parent 4057f02 commit e0103df
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 107 deletions.
18 changes: 18 additions & 0 deletions ObjectiveGit/GTIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
@class GTIndexEntry;
@class GTRepository;
@class GTTree;
@class GTMergeResult;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -234,6 +235,23 @@ NS_ASSUME_NONNULL_BEGIN
- (GTIndexEntry * _Nullable)entryWithName:(NSString *)name error:(NSError **)error __deprecated_msg("use entryWithPath:error: instead.");


@end

@interface GTIndex (FileMerging)

/// Gets the result of a merge with the given file entries
///
/// The parameters taked are the ones received from `enumerateConflictedFiles`.
///
/// ancestor - The ancestor entry
/// ours - The index entry of our side
/// theirs - The index entry of their side
/// options - The merge options to use. Can be nil.
/// error - The error if one occurred. Can be NULL.
///
/// Returns The results of the merge or nil on error
- (GTMergeResult * _Nullable)resultOfMergingAncestorEntry:(GTIndexEntry *)ancestor ourEntry:(GTIndexEntry *)ours theirEntry:(GTIndexEntry *)theirs options:(NSDictionary * _Nullable)options error:(NSError * _Nullable __autoreleasing *)error;

@end

NS_ASSUME_NONNULL_END
27 changes: 27 additions & 0 deletions ObjectiveGit/GTIndex.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
#import "GTBlob.h"
#import "NSArray+StringArray.h"
#import "NSError+Git.h"
#import "GTMerge+Private.h"

#import "git2/errors.h"
#import "git2/merge.h"

// The block synonymous with libgit2's `git_index_matched_path_cb` callback.
typedef BOOL (^GTIndexPathspecMatchedBlock)(NSString *matchedPathspec, NSString *path, BOOL *stop);
Expand Down Expand Up @@ -406,4 +408,29 @@ - (GTIndexEntry *)entryWithName:(NSString *)name {
- (GTIndexEntry *)entryWithName:(NSString *)name error:(NSError **)error {
return [self entryWithPath:name error:error];
}

@end

@implementation GTIndex (FileMerging)

- (GTMergeResult *)resultOfMergingAncestorEntry:(GTIndexEntry *)ancestorEntry ourEntry:(GTIndexEntry *)ourEntry theirEntry:(GTIndexEntry *)theirEntry options:(NSDictionary *)options error:(NSError * _Nullable __autoreleasing *)error {
NSParameterAssert(ourEntry);
NSParameterAssert(theirEntry);
NSParameterAssert(ancestorEntry);

git_merge_file_result gitResult;
git_merge_file_options opts;

BOOL success = [GTMergeFile handleMergeFileOptions:&opts optionsDict:options error:error];
if (!success) return nil;

int gitError = git_merge_file_from_index(&gitResult, self.repository.git_repository, ancestorEntry.git_index_entry, ourEntry.git_index_entry, theirEntry.git_index_entry, &opts);
if (gitError != 0) {
if (error) *error = [NSError git_errorFor:gitError description:@"Merging entries failed"];
return nil;
}

return [[GTMergeResult alloc] initWithGitMergeFileResult:&gitResult];
}

@end
15 changes: 15 additions & 0 deletions ObjectiveGit/GTMerge+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// GTMerge+Private.h
// ObjectiveGitFramework
//
// Created by Etienne on 27/10/2018.
// Copyright © 2018 GitHub, Inc. All rights reserved.
//

#import "GTMerge.h"

@interface GTMergeFile (Private)

+ (BOOL)handleMergeFileOptions:(git_merge_file_options *)opts optionsDict:(NSDictionary *)dict error:(NSError **)error;

@end
21 changes: 21 additions & 0 deletions ObjectiveGit/GTMerge.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
//

#import "GTMerge.h"
#import "GTOID.h"
#import "GTObjectDatabase.h"
#import "GTOdbObject.h"
#import "GTRepository.h"
#import "GTIndex.h"
#import "GTIndexEntry.h"
#import "NSError+Git.h"

@interface GTMergeResult ()
Expand Down Expand Up @@ -67,6 +73,21 @@ + (instancetype)fileWithString:(NSString *)string path:(NSString * _Nullable)pat
return [[self alloc] initWithData:stringData path:path mode:mode];
}

+ (instancetype)fileWithIndexEntry:(GTIndexEntry *)entry error:(NSError **)error {
NSParameterAssert(entry);

const git_index_entry *git_entry = entry.git_index_entry;
GTOID *ancestorOID = [[GTOID alloc] initWithGitOid:&git_entry->id];
GTRepository *repository = entry.index.repository;
GTObjectDatabase *database = [repository objectDatabaseWithError:error];
NSData *contents = [[database objectWithOID:ancestorOID error:error] data];
if (contents == nil) {
return nil;
}

return [[self alloc] initWithData:contents path:entry.path mode:git_entry->mode];
}

- (instancetype)initWithData:(NSData *)data path:(NSString *)path mode:(unsigned int)mode {
NSParameterAssert(data);
self = [super init];
Expand Down
72 changes: 7 additions & 65 deletions ObjectiveGit/GTRepository+Merging.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import "GTIndexEntry.h"
#import "GTOdbObject.h"
#import "GTObjectDatabase.h"
#import "GTMerge.h"

typedef void (^GTRemoteFetchTransferProgressBlock)(const git_transfer_progress *stats, BOOL *stop);

Expand Down Expand Up @@ -173,78 +174,19 @@ - (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)branch withError:(NSError **)er
}

- (NSString * _Nullable)contentsOfDiffWithAncestor:(GTIndexEntry *)ancestor ourSide:(GTIndexEntry *)ourSide theirSide:(GTIndexEntry *)theirSide error:(NSError **)error {
NSParameterAssert(ancestor && ourSide && theirSide);

GTObjectDatabase *database = [self objectDatabaseWithError:error];
if (database == nil) {
GTIndex *index = [self indexWithError:error];
if (index == nil) {
return nil;
}

// initialize the ancestor's merge file input
git_merge_file_input ancestorInput;
int gitError = git_merge_file_init_input(&ancestorInput, GIT_MERGE_FILE_INPUT_VERSION);
if (gitError != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input for ancestor"];
return nil;
}

git_oid ancestorId = ancestor.git_index_entry->id;
GTOID *ancestorOID = [[GTOID alloc] initWithGitOid:&ancestorId];
NSData *ancestorData = [[database objectWithOID:ancestorOID error: error] data];
if (ancestorData == nil) {
return nil;
}
ancestorInput.ptr = ancestorData.bytes;
ancestorInput.size = ancestorData.length;


// initialize our merge file input
git_merge_file_input ourInput;
gitError = git_merge_file_init_input(&ourInput, GIT_MERGE_FILE_INPUT_VERSION);
if (gitError != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input for our side"];
GTMergeResult *result = [index resultOfMergingAncestorEntry:ancestor ourEntry:ourSide theirEntry:theirSide options:nil error:error];
if (result == nil) {
return nil;
}

git_oid ourId = ourSide.git_index_entry->id;
GTOID *ourOID = [[GTOID alloc] initWithGitOid:&ourId];
NSData *ourData = [[database objectWithOID:ourOID error: error] data];
if (ourData == nil) {
return nil;
}
ourInput.ptr = ourData.bytes;
ourInput.size = ourData.length;


// initialize their merge file input
git_merge_file_input theirInput;
gitError = git_merge_file_init_input(&theirInput, GIT_MERGE_FILE_INPUT_VERSION);
if (gitError != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input other side"];
return nil;
}

git_oid theirId = theirSide.git_index_entry->id;
GTOID *theirOID = [[GTOID alloc] initWithGitOid:&theirId];
NSData *theirData = [[database objectWithOID:theirOID error: error] data];
if (theirData == nil) {
return nil;
}
theirInput.ptr = theirData.bytes;
theirInput.size = theirData.length;


git_merge_file_result result;
gitError = git_merge_file(&result, &ancestorInput, &ourInput, &theirInput, nil);
if (gitError != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file"];
return nil;
}

NSString *mergedContent = [[NSString alloc] initWithBytes:result.ptr length:result.len encoding:NSUTF8StringEncoding];

git_merge_file_result_free(&result);

return mergedContent;
return [[NSString alloc] initWithData:result.data encoding:NSUTF8StringEncoding];
}

- (BOOL)annotatedCommit:(git_annotated_commit **)annotatedCommit fromCommit:(GTCommit *)fromCommit error:(NSError **)error {
Expand Down
2 changes: 2 additions & 0 deletions ObjectiveGitFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@
4D7BA1B82183C4C9003CD3CE /* GTMerge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GTMerge.h; sourceTree = "<group>"; };
4D7BA1B92183C4C9003CD3CE /* GTMerge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GTMerge.m; sourceTree = "<group>"; };
4D7BA1BF2183DD55003CD3CE /* GTMergeSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GTMergeSpec.m; sourceTree = "<group>"; };
4D7BA1BE2183D3EE003CD3CE /* GTMerge+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GTMerge+Private.h"; sourceTree = "<group>"; };
4D9BCD23206D84AD003CD3CE /* libgit2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgit2.a; path = External/build/lib/libgit2.a; sourceTree = "<group>"; };
4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRemoteSpec.m; sourceTree = "<group>"; };
4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTCheckoutOptions.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -987,6 +988,7 @@
4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */,
4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */,
4D7BA1B82183C4C9003CD3CE /* GTMerge.h */,
4D7BA1BE2183D3EE003CD3CE /* GTMerge+Private.h */,
4D7BA1B92183C4C9003CD3CE /* GTMerge.m */,
);
path = ObjectiveGit;
Expand Down
69 changes: 69 additions & 0 deletions ObjectiveGitTests/GTIndexSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,75 @@
});
});

describe(@"-resultOfMergingAncestorEntry:ourEntry:theirEntry:options:error:", ^{
it(@"should produce a nice merge conflict description", ^{
NSURL *mainURL = [repository.fileURL URLByAppendingPathComponent:@"main.m"];
NSData *mainData = [[NSFileManager defaultManager] contentsAtPath:mainURL.path];
expect(mainData).notTo(beNil());

NSString *mainString = [[NSString alloc] initWithData:mainData encoding:NSUTF8StringEncoding];
NSData *masterData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 41\n return"] dataUsingEncoding:NSUTF8StringEncoding];
NSData *otherData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 42\n return"] dataUsingEncoding:NSUTF8StringEncoding];

expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:masterData attributes:nil])).to(beTruthy());

GTIndex *index = [repository indexWithError:NULL];
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
GTReference *head = [repository headReferenceWithError:NULL];
GTCommit *parent = [repository lookUpObjectByOID:head.targetOID objectType:GTObjectTypeCommit error:NULL];
expect(parent).toNot(beNil());
GTTree *masterTree = [index writeTree:NULL];
expect(masterTree).toNot(beNil());

GTBranch *otherBranch = [repository lookUpBranchWithName:@"other-branch" type:GTBranchTypeLocal success:NULL error:NULL];
expect(otherBranch).toNot(beNil());
expect(@([repository checkoutReference:otherBranch.reference options:nil error:NULL])).to(beTruthy());

expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:otherData attributes:nil])).to(beTruthy());

index = [repository indexWithError:NULL];
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
GTTree *otherTree = [index writeTree:NULL];
expect(otherTree).toNot(beNil());

GTIndex *conflictIndex = [otherTree merge:masterTree ancestor:parent.tree error:NULL];
expect(@([conflictIndex hasConflicts])).to(beTruthy());

[conflictIndex enumerateConflictedFilesWithError:NULL usingBlock:^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) {

GTMergeResult *result = [conflictIndex resultOfMergingAncestorEntry:ancestor ourEntry:ours theirEntry:theirs options:nil error:NULL];
expect(result).notTo(beNil());

NSString *conflictString = [[NSString alloc] initWithData:result.data encoding:NSUTF8StringEncoding];
NSString *expectedString = @"//\n"
"// main.m\n"
"// Test\n"
"//\n"
"// Created by Joe Ricioppo on 9/28/10.\n"
"// Copyright 2010 __MyCompanyName__. All rights reserved.\n"
"//\n"
"\n"
"#import <Cocoa/Cocoa.h>\n"
"\n"
"int main(int argc, char *argv[])\n"
"{\n"
"<<<<<<< main.m\n"
" //The meaning of life is 42\n"
"=======\n"
" //The meaning of life is 41\n"
">>>>>>> main.m\n"
" return NSApplicationMain(argc, (const char **) argv);\n"
"}\n"
"123456789\n"
"123456789\n"
"123456789\n"
"123456789!blah!\n";

expect(conflictString).to(equal(expectedString));
}];
});
});

afterEach(^{
[self tearDown];
});
Expand Down
42 changes: 0 additions & 42 deletions ObjectiveGitTests/GTRepositorySpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -260,48 +260,6 @@
});
});

describe(@"-contentsOfDiffWithAncestor:ourSide:theirSide:error:", ^{
it(@"should produce a nice merge conflict description", ^{
NSURL *mainURL = [repository.fileURL URLByAppendingPathComponent:@"main.m"];
NSData *mainData = [[NSFileManager defaultManager] contentsAtPath:mainURL.path];
expect(mainData).notTo(beNil());

NSString *mainString = [[NSString alloc] initWithData:mainData encoding:NSUTF8StringEncoding];
NSData *masterData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 41\n return"] dataUsingEncoding:NSUTF8StringEncoding];
NSData *otherData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 42\n return"] dataUsingEncoding:NSUTF8StringEncoding];

expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:masterData attributes:nil])).to(beTruthy());

GTIndex *index = [repository indexWithError:NULL];
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
GTReference *head = [repository headReferenceWithError:NULL];
GTCommit *parent = [repository lookUpObjectByOID:head.targetOID objectType:GTObjectTypeCommit error:NULL];
expect(parent).toNot(beNil());
GTTree *masterTree = [index writeTree:NULL];
expect(masterTree).toNot(beNil());

GTBranch *otherBranch = [repository lookUpBranchWithName:@"other-branch" type:GTBranchTypeLocal success:NULL error:NULL];
expect(otherBranch).toNot(beNil());
expect(@([repository checkoutReference:otherBranch.reference options:nil error:NULL])).to(beTruthy());

expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:otherData attributes:nil])).to(beTruthy());

index = [repository indexWithError:NULL];
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
GTTree *otherTree = [index writeTree:NULL];
expect(otherTree).toNot(beNil());

GTIndex *conflictIndex = [otherTree merge:masterTree ancestor:parent.tree error:NULL];
expect(@([conflictIndex hasConflicts])).to(beTruthy());

[conflictIndex enumerateConflictedFilesWithError:NULL usingBlock:^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) {

NSString *conflictString = [repository contentsOfDiffWithAncestor:ancestor ourSide:ours theirSide:theirs error:NULL];
expect(conflictString).to(equal(@"//\n// main.m\n// Test\n//\n// Created by Joe Ricioppo on 9/28/10.\n// Copyright 2010 __MyCompanyName__. All rights reserved.\n//\n\n#import <Cocoa/Cocoa.h>\n\nint main(int argc, char *argv[])\n{\n<<<<<<< file.txt\n //The meaning of life is 42\n=======\n //The meaning of life is 41\n>>>>>>> file.txt\n return NSApplicationMain(argc, (const char **) argv);\n}\n123456789\n123456789\n123456789\n123456789!blah!\n"));
}];
});
});

describe(@"-mergeBaseBetweenFirstOID:secondOID:error:", ^{
it(@"should find the merge base between two branches", ^{
NSError *error = nil;
Expand Down

0 comments on commit e0103df

Please sign in to comment.