Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add merge analysis between two branches #629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion ObjectiveGit/GTRepository+Merging.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ typedef NS_OPTIONS(NSInteger, GTMergeAnalysis) {
/// will point to an error describing what happened).
- (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)fromBranch withError:(NSError **)error;

/// Analyze which merge to perform.
/// Analyze which merge to perform from the branch to HEAD
///
/// analysis - The resulting analysis.
/// fromBranch - The branch to merge from.
Expand All @@ -62,6 +62,17 @@ typedef NS_OPTIONS(NSInteger, GTMergeAnalysis) {
/// will point to an error describing what happened).
- (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis fromBranch:(GTBranch *)fromBranch error:(NSError **)error;

/// Analyze which merge to perform between two branches
///
/// analysis - The resulting analysis.
/// fromBranch - The branch to merge from.
/// toBranch - The branch to merge into
/// error - The error if one occurred. Can be NULL.
///
/// Returns YES if the analysis was successful, NO otherwise (and `error`, if provided,
/// will point to an error describing what happened).
- (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis fromBranch:(GTBranch *)fromBranch toBranch:(GTBranch *)toBranch error:(NSError **)error;

@end

NS_ASSUME_NONNULL_END
30 changes: 30 additions & 0 deletions ObjectiveGit/GTRepository+Merging.m
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,34 @@ - (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis fromBranch:(GTBranch *)fromBran
return YES;
}

- (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis fromBranch:(GTBranch *)fromBranch toBranch:(GTBranch *)toBranch error:(NSError **)error {
NSParameterAssert(analysis != NULL);
NSParameterAssert(fromBranch != nil);
NSParameterAssert(toBranch != nil);

GTCommit *theirs = [toBranch targetCommitWithError:error];
GTCommit *ours = [fromBranch targetCommitWithError:error];
if (theirs == nil || ours == nil) {
return NO;
}

GTCommit *ancestor = [self mergeBaseBetweenFirstOID:theirs.OID secondOID:ours.OID error:NULL];

if (ours.SHA == nil && theirs.SHA == nil) {
*analysis = GTMergeAnalysisUnborn | 0;;
} else if (ours.SHA == nil && theirs.SHA != nil) {
*analysis = GTMergeAnalysisUnborn | GTMergeAnalysisFastForward;
} else if (ancestor.SHA == nil || theirs.SHA == nil) {
*analysis = GTMergeAnalysisNone | 0;
} else if ([ours.SHA isEqualToString:theirs.SHA] || [ancestor.SHA isEqualToString:theirs.SHA]) {
*analysis = GTMergeAnalysisUpToDate | 0;
} else if ([ancestor.SHA isEqualToString:ours.SHA]) {
*analysis = GTMergeAnalysisFastForward | GTMergeAnalysisNormal;
} else {
*analysis = GTMergeAnalysisNormal | 0;
}

return YES;
}

@end
107 changes: 107 additions & 0 deletions ObjectiveGitTests/GTRepositorySpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,113 @@
});
});

describe(@"analyzeMerge", ^{
it(@"should correctly recognize up-to-date merges", ^{
__block NSError *error = nil;
GTBranch *branch1 = [repository lookUpBranchWithName:@"new" type:GTBranchTypeLocal success:NULL error:&error];
expect(branch1).notTo(beNil());
expect(error).to(beNil());

GTBranch *branch2 = [repository lookUpBranchWithName:@"hi!" type:GTBranchTypeLocal success:NULL error:&error];
expect(branch2).notTo(beNil());
expect(error).to(beNil());

__block GTMergeAnalysis analysis = 0;
expect(@([repository analyzeMerge:&analysis fromBranch:branch2 toBranch:branch1 error:&error])).to(beTruthy());
expect(error).to(beNil());
expect(@(analysis)).to(equal(GTMergeAnalysisUpToDate | 0));
});

it(@"should correctly recognize fast-forward merges", ^{
__block NSError *error = nil;
GTBranch *masterBranch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:NULL error:&error];
expect(masterBranch).notTo(beNil());
expect(error).to(beNil());

GTBranch *otherBranch = [repository lookUpBranchWithName:@"other-branch" type:GTBranchTypeLocal success:NULL error:&error];
expect(otherBranch).notTo(beNil());
expect(error).to(beNil());

__block GTMergeAnalysis analysis = 0;
expect(@([repository analyzeMerge:&analysis fromBranch:otherBranch toBranch:masterBranch error:&error])).to(beTruthy());
expect(error).to(beNil());
expect(@(analysis)).to(equal(GTMergeAnalysisFastForward | GTMergeAnalysisNormal));
});

it(@"should correctly recognize 3-way merges", ^{
__block NSError *error = nil;
GTOID *oid1 = [GTOID oidWithSHA:@"3921db10e466211cb4d60b7f0e871667f318a7cd"];
GTBranch *branch1 = [repository createBranchNamed:@"branch1" fromOID:oid1 message:@"new branch 1" error:&error];
expect(branch1).notTo(beNil());
expect(error).to(beNil());

GTOID *oid2 = [GTOID oidWithSHA:@"47563e7aba76b656a43c04d30b3b6fbe76da6e81"];
GTBranch *branch2 = [repository createBranchNamed:@"branch2" fromOID:oid2 message:@"new branch 2" error:&error];
expect(branch2).notTo(beNil());
expect(error).to(beNil());

__block GTMergeAnalysis analysis = 0;
expect(@([repository analyzeMerge:&analysis fromBranch:branch1 toBranch:branch2 error:&error])).to(beTruthy());
expect(error).to(beNil());
expect(@(analysis)).to(equal(GTMergeAnalysisNormal | 0));
});

it(@"should correctly recognize unmergeable merges", ^{
__block NSError *error = nil;
GTBranch *masterBranch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:NULL error:&error];
expect(masterBranch).notTo(beNil());
expect(error).to(beNil());

NSURL *mainURL = [repository.fileURL URLByAppendingPathComponent:@"main.m"];
NSData *newMainContent = [@"This used to be main..." dataUsingEncoding:NSUTF8StringEncoding];
expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:newMainContent attributes:nil])).to(beTruthy());

GTIndex *index = [repository indexWithError:NULL];
expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy());
GTCommit *unconnectedCommit = [repository createCommitWithTree:[index writeTree:NULL] message:@"unconnected commit" parents:[[NSArray alloc] init] updatingReferenceNamed:nil error:&error];
expect(unconnectedCommit).toNot(beNil());
expect(error).to(beNil());

GTBranch *unconnectedBranch = [repository createBranchNamed:@"unconnected" fromOID:unconnectedCommit.OID message:@"Create a new branch that has no common ancestor with head" error:&error];
expect(unconnectedBranch).notTo(beNil());
expect(error).to(beNil());

__block GTMergeAnalysis analysis = 0;
expect(@([repository analyzeMerge:&analysis fromBranch:masterBranch toBranch:unconnectedBranch error:&error])).to(beTruthy());
expect(error).to(beNil());
expect(@(analysis)).to(equal(GTMergeAnalysisNone | 0));
});

it(@"should correctly recognize unborn merges", ^{
__block NSError *error = nil;
GTRepository *repo = self.blankFixtureRepository;
expect(@(repo.isHEADUnborn)).to(beTruthy());

NSURL *mainURL = [repo.fileURL URLByAppendingPathComponent:@"main.m"];
NSData *newMainContent = [@"new main file" dataUsingEncoding:NSUTF8StringEncoding];
expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:newMainContent attributes:nil])).to(beTruthy());

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

GTCommit *initialCommit = [repo createCommitWithTree:commitTree message:@"initial commit" parents:[[NSArray alloc] init] updatingReferenceNamed:nil error:&error];
expect(initialCommit).toNot(beNil());
expect(error).to(beNil());

GTBranch *unrelatedNewMaster = [repo createBranchNamed:@"new_master" fromOID:initialCommit.OID message:@"create master" error:&error];
expect(unrelatedNewMaster).toNot(beNil());
expect(error).to(beNil());

__block GTMergeAnalysis analysis = 0;
expect(@([repo analyzeMerge:&analysis fromBranch:unrelatedNewMaster error:&error])).to(beTruthy());
expect(error).to(beNil());
expect(@(analysis)).to(equal(GTMergeAnalysisUnborn | GTMergeAnalysisFastForward));
});
});

describe(@"-allTagsWithError:", ^{
it(@"should return all tags", ^{
NSError *error = nil;
Expand Down