@@ -103,6 +103,32 @@ - (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis preference:(GTMergePreference *
103103 return YES ;
104104}
105105
106+ - (BOOL )mergeAnnotatedCommits : (NSArray <GTAnnotatedCommit *> *)annotatedCommits mergeOptions : (NSDictionary *)mergeOptions checkoutOptions : (GTCheckoutOptions *)checkoutOptions error : (NSError **)error {
107+ NSParameterAssert (annotatedCommits);
108+
109+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
110+
111+ const git_annotated_commit **annotated_commits = NULL ;
112+ if (annotatedCommits.count > 0 ) {
113+ annotated_commits = calloc (annotatedCommits.count , sizeof (git_annotated_commit *));
114+ for (NSUInteger i = 0 ; i < annotatedCommits.count ; i++){
115+ annotated_commits[i] = [annotatedCommits[i] git_annotated_commit ];
116+ }
117+ }
118+ @onExit {
119+ free (annotated_commits);
120+ };
121+
122+ int gitError = git_merge (self.git_repository , annotated_commits, annotatedCommits.count , &merge_opts, checkoutOptions.git_checkoutOptions );
123+ if (gitError != GIT_OK) {
124+ if (error != NULL ) {
125+ *error = [NSError git_errorFor: gitError description: @" Merge failed" ];
126+ }
127+ return NO ;
128+ }
129+ return YES ;
130+ }
131+
106132- (BOOL )mergeBranchIntoCurrentBranch : (GTBranch *)branch withError : (NSError **)error {
107133 // Check if merge is necessary
108134 GTBranch *localBranch = [self currentBranchWithError: error];
@@ -156,54 +182,51 @@ - (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)branch withError:(NSError **)er
156182 }
157183
158184 // Do normal merge
159- GTTree *localTree = localCommit.tree ;
160- GTTree *remoteTree = remoteCommit.tree ;
161-
162- // TODO: Find common ancestor
163- GTTree *ancestorTree = nil ;
164-
165- // Merge
166- GTIndex *index = [localTree merge: remoteTree ancestor: ancestorTree error: error];
167- if (!index) {
185+ GTIndex *index = [self indexWithError: error];
186+ if (index == nil ) {
168187 return NO ;
169188 }
170189
171- // Check for conflict
172- if (index.hasConflicts ) {
173- NSMutableArray <NSString *>*files = [NSMutableArray array ];
174- [index enumerateConflictedFilesWithError: error usingBlock: ^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) {
175- [files addObject: ours.path];
176- }];
190+ NSError *mergeError = nil ;
191+ GTCheckoutOptions *checkoutOptions = [GTCheckoutOptions checkoutOptionsWithStrategy: GTCheckoutStrategySafe|GTCheckoutStrategyAllowConflicts];
177192
193+ success = [self mergeAnnotatedCommits: @[remoteAnnotatedCommit]
194+ mergeOptions: nil
195+ checkoutOptions: checkoutOptions
196+ error: &mergeError];
197+ if (!success) {
178198 if (error != NULL ) {
179- NSDictionary *userInfo = @{GTPullMergeConflictedFiles: files};
180- *error = [NSError git_errorFor: GIT_ECONFLICT description: @" Merge conflict" userInfo: userInfo failureReason: nil ];
199+ *error = mergeError;
181200 }
201+ return NO ;
202+ }
182203
183- // Write conflicts
184- git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
185- git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
186- checkout_opts.checkout_strategy = (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS);
187-
188- git_annotated_commit *annotatedCommit;
189- [self annotatedCommit: &annotatedCommit fromCommit: remoteCommit error: error];
190-
191- git_merge (self.git_repository , (const git_annotated_commit **)&annotatedCommit, 1 , &merge_opts, &checkout_opts);
204+ if (![index refresh: error]) {
205+ return NO ;
206+ }
192207
208+ if (index.hasConflicts ) {
209+ if (error) {
210+ NSMutableArray <NSString *> *files = [NSMutableArray array ];
211+ [index enumerateConflictedFilesWithError: error usingBlock: ^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) {
212+ [files addObject: ours.path];
213+ }];
214+ NSDictionary *userInfo = @{GTPullMergeConflictedFiles: files};
215+ *error = [NSError git_errorFor: GIT_EMERGECONFLICT description: @" Merge conflict" userInfo: userInfo failureReason: nil ];
216+ }
193217 return NO ;
194218 }
195219
196- GTTree *newTree = [index writeTreeToRepository: self error : error];
197- if (!newTree ) {
220+ GTTree *mergedTree = [index writeTree : error];
221+ if (mergedTree == nil ) {
198222 return NO ;
199223 }
200224
201225 // Create merge commit
202226 NSString *message = [NSString stringWithFormat: @" Merge branch '%@ '" , localBranch.shortName];
203227 NSArray *parents = @[ localCommit, remoteCommit ];
204228
205- // FIXME: This is stepping on the local tree
206- GTCommit *mergeCommit = [self createCommitWithTree: newTree message: message parents: parents updatingReferenceNamed: localBranch.reference.name error: error];
229+ GTCommit *mergeCommit = [self createCommitWithTree: mergedTree message: message parents: parents updatingReferenceNamed: localBranch.reference.name error: error];
207230 if (!mergeCommit) {
208231 return NO ;
209232 }
@@ -287,16 +310,6 @@ - (NSString * _Nullable)contentsOfDiffWithAncestor:(GTIndexEntry *)ancestor ourS
287310 return mergedContent;
288311}
289312
290- - (BOOL )annotatedCommit : (git_annotated_commit **)annotatedCommit fromCommit : (GTCommit *)fromCommit error : (NSError **)error {
291- int gitError = git_annotated_commit_lookup (annotatedCommit, self.git_repository , fromCommit.OID .git_oid );
292- if (gitError != GIT_OK) {
293- if (error != NULL ) *error = [NSError git_errorFor: gitError description: @" Failed to lookup annotated commit for %@ " , fromCommit];
294- return NO ;
295- }
296-
297- return YES ;
298- }
299-
300313- (BOOL )analyzeMerge : (GTMergeAnalysis *)analysis fromBranch : (GTBranch *)fromBranch error : (NSError **)error {
301314 NSParameterAssert (analysis != NULL );
302315 NSParameterAssert (fromBranch != nil );
0 commit comments