Skip to content

Commit 90f0185

Browse files
authored
Merge pull request #664 from tiennou/conflict_marked_file
Wrap git_merge_file, take 2
2 parents c294035 + 3bb0078 commit 90f0185

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

ObjectiveGit/GTRepository+Merging.h

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
#import "GTRepository.h"
10+
#import "GTIndexEntry.h"
1011
#import "git2/merge.h"
1112

1213
NS_ASSUME_NONNULL_BEGIN
@@ -52,6 +53,18 @@ typedef NS_OPTIONS(NSInteger, GTMergeAnalysis) {
5253
/// will point to an error describing what happened).
5354
- (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)fromBranch withError:(NSError **)error;
5455

56+
/// Gets the file content with conflict markers for the given file
57+
///
58+
/// The parameters taked are the ones received from `enumerateConflictedFiles`.
59+
///
60+
/// ancestor - The ancestor entry
61+
/// ours - The index entry of our side
62+
/// theirs - The index entry of their side
63+
/// error - The error if one occurred. Can be NULL.
64+
///
65+
/// Returns The file content annotated with conflict markers or null on error
66+
- (NSString * _Nullable)contentsOfDiffWithAncestor:(GTIndexEntry *)ancestor ourSide:(GTIndexEntry *)ourSide theirSide:(GTIndexEntry *)theirSide error:(NSError **)error;
67+
5568
/// Analyze which merge to perform.
5669
///
5770
/// analysis - The resulting analysis.

ObjectiveGit/GTRepository+Merging.m

+77
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#import "GTTree.h"
1818
#import "GTIndex.h"
1919
#import "GTIndexEntry.h"
20+
#import "GTOdbObject.h"
21+
#import "GTObjectDatabase.h"
2022

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

@@ -170,6 +172,81 @@ - (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)branch withError:(NSError **)er
170172
return NO;
171173
}
172174

175+
- (NSString * _Nullable)contentsOfDiffWithAncestor:(GTIndexEntry *)ancestor ourSide:(GTIndexEntry *)ourSide theirSide:(GTIndexEntry *)theirSide error:(NSError **)error {
176+
177+
GTObjectDatabase *database = [self objectDatabaseWithError:error];
178+
if (database == nil) {
179+
return nil;
180+
}
181+
182+
// initialize the ancestor's merge file input
183+
git_merge_file_input ancestorInput;
184+
int gitError = git_merge_file_init_input(&ancestorInput, GIT_MERGE_FILE_INPUT_VERSION);
185+
if (gitError != GIT_OK) {
186+
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input for ancestor"];
187+
return nil;
188+
}
189+
190+
git_oid ancestorId = ancestor.git_index_entry->id;
191+
GTOID *ancestorOID = [[GTOID alloc] initWithGitOid:&ancestorId];
192+
NSData *ancestorData = [[database objectWithOID:ancestorOID error: error] data];
193+
if (ancestorData == nil) {
194+
return nil;
195+
}
196+
ancestorInput.ptr = ancestorData.bytes;
197+
ancestorInput.size = ancestorData.length;
198+
199+
200+
// initialize our merge file input
201+
git_merge_file_input ourInput;
202+
gitError = git_merge_file_init_input(&ourInput, GIT_MERGE_FILE_INPUT_VERSION);
203+
if (gitError != GIT_OK) {
204+
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input for our side"];
205+
return nil;
206+
}
207+
208+
git_oid ourId = ourSide.git_index_entry->id;
209+
GTOID *ourOID = [[GTOID alloc] initWithGitOid:&ourId];
210+
NSData *ourData = [[database objectWithOID:ourOID error: error] data];
211+
if (ourData == nil) {
212+
return nil;
213+
}
214+
ourInput.ptr = ourData.bytes;
215+
ourInput.size = ourData.length;
216+
217+
218+
// initialize their merge file input
219+
git_merge_file_input theirInput;
220+
gitError = git_merge_file_init_input(&theirInput, GIT_MERGE_FILE_INPUT_VERSION);
221+
if (gitError != GIT_OK) {
222+
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input other side"];
223+
return nil;
224+
}
225+
226+
git_oid theirId = theirSide.git_index_entry->id;
227+
GTOID *theirOID = [[GTOID alloc] initWithGitOid:&theirId];
228+
NSData *theirData = [[database objectWithOID:theirOID error: error] data];
229+
if (theirData == nil) {
230+
return nil;
231+
}
232+
theirInput.ptr = theirData.bytes;
233+
theirInput.size = theirData.length;
234+
235+
236+
git_merge_file_result result;
237+
gitError = git_merge_file(&result, &ancestorInput, &ourInput, &theirInput, nil);
238+
if (gitError != GIT_OK) {
239+
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file"];
240+
return nil;
241+
}
242+
243+
NSString *mergedContent = [[NSString alloc] initWithBytes:result.ptr length:result.len encoding:NSUTF8StringEncoding];
244+
245+
git_merge_file_result_free(&result);
246+
247+
return mergedContent;
248+
}
249+
173250
- (BOOL)annotatedCommit:(git_annotated_commit **)annotatedCommit fromCommit:(GTCommit *)fromCommit error:(NSError **)error {
174251
int gitError = git_annotated_commit_lookup(annotatedCommit, self.git_repository, fromCommit.OID.git_oid);
175252
if (gitError != GIT_OK) {

ObjectiveGitTests/GTRepositorySpec.m

+42
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,48 @@
260260
});
261261
});
262262

263+
describe(@"-contentsOfDiffWithAncestor:ourSide:theirSide:error:", ^{
264+
it(@"should produce a nice merge conflict description", ^{
265+
NSURL *mainURL = [repository.fileURL URLByAppendingPathComponent:@"main.m"];
266+
NSData *mainData = [[NSFileManager defaultManager] contentsAtPath:mainURL.path];
267+
expect(mainData).notTo(beNil());
268+
269+
NSString *mainString = [[NSString alloc] initWithData:mainData encoding:NSUTF8StringEncoding];
270+
NSData *masterData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 41\n return"] dataUsingEncoding:NSUTF8StringEncoding];
271+
NSData *otherData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 42\n return"] dataUsingEncoding:NSUTF8StringEncoding];
272+
273+
expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:masterData attributes:nil])).to(beTruthy());
274+
275+
GTIndex *index = [repository indexWithError:NULL];
276+
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
277+
GTReference *head = [repository headReferenceWithError:NULL];
278+
GTCommit *parent = [repository lookUpObjectByOID:head.targetOID objectType:GTObjectTypeCommit error:NULL];
279+
expect(parent).toNot(beNil());
280+
GTTree *masterTree = [index writeTree:NULL];
281+
expect(masterTree).toNot(beNil());
282+
283+
GTBranch *otherBranch = [repository lookUpBranchWithName:@"other-branch" type:GTBranchTypeLocal success:NULL error:NULL];
284+
expect(otherBranch).toNot(beNil());
285+
expect(@([repository checkoutReference:otherBranch.reference options:nil error:NULL])).to(beTruthy());
286+
287+
expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:otherData attributes:nil])).to(beTruthy());
288+
289+
index = [repository indexWithError:NULL];
290+
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
291+
GTTree *otherTree = [index writeTree:NULL];
292+
expect(otherTree).toNot(beNil());
293+
294+
GTIndex *conflictIndex = [otherTree merge:masterTree ancestor:parent.tree error:NULL];
295+
expect(@([conflictIndex hasConflicts])).to(beTruthy());
296+
297+
[conflictIndex enumerateConflictedFilesWithError:NULL usingBlock:^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) {
298+
299+
NSString *conflictString = [repository contentsOfDiffWithAncestor:ancestor ourSide:ours theirSide:theirs error:NULL];
300+
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"));
301+
}];
302+
});
303+
});
304+
263305
describe(@"-mergeBaseBetweenFirstOID:secondOID:error:", ^{
264306
it(@"should find the merge base between two branches", ^{
265307
NSError *error = nil;

0 commit comments

Comments
 (0)