Skip to content

Commit

Permalink
EXP-13070: Update s7 to checkout subrepos in case of merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
Piranja committed Dec 5, 2024
1 parent 4840b42 commit 64eaf63
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/bin/sh

git clone github/rd2 pastey/rd2

cd pastey/rd2

assert s7 init
assert git add .
assert git commit -m "\"init s7\""

assert s7 add --stage Dependencies/ReaddleLib '"$S7_ROOT/github/ReaddleLib"'

pushd Dependencies/ReaddleLib > /dev/null
echo sqrt > RDMath.h
git add RDMath.h
git commit -m"add RDMath.h"
popd > /dev/null

assert s7 rebind --stage
assert git commit -m '"add ReaddleLib subrepo"'

echo
git checkout -b experiment

pushd Dependencies/ReaddleLib > /dev/null
echo experiment > RDMath.h
git commit -am"experiment in ReaddleLib"
popd > /dev/null

# Update existing ReaddleLib subrepo
assert s7 rebind --stage
assert git commit -m '"up ReaddleLib"'

# Add new subrepo
assert s7 add --stage Dependencies/RDPDFKit '"$S7_ROOT/github/RDPDFKit"'
assert test -d Dependencies/RDPDFKit
assert git commit -m '"add RDPDFKit subrepo"'

echo
git checkout main

pushd Dependencies/ReaddleLib > /dev/null
echo main > RDMath.h
git commit -am"changes at main in ReaddleLib"
popd > /dev/null

# Update existing ReaddleLib subrepo in another branch
# to trigger a merge conflict in .s7substate
assert s7 rebind --stage
assert git commit -m '"up ReaddleLib"'

echo
echo
S7_MERGE_DRIVER_RESPONSE="m" git merge --no-edit experiment
assert test 1 -eq $?

echo
echo "resulting .s7substate:"
cat .s7substate
echo

assert grep '"<<<"' .s7substate > /dev/null # must be a conflict marker in .s7substate
assert test -d Dependencies/ReaddleLib # subrepo must be present in conflict state
assert test -d Dependencies/RDPDFKit # new subrepo must be checked out
134 changes: 134 additions & 0 deletions system7-tests/mergeDriverTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,136 @@ - (void)testBothSideUpdateSubrepoToDifferentRevisionConflictResolution_KeepConfl
S7Config *controlConfig = [[S7Config alloc] initWithContentsOfFile:S7ControlFileName];
XCTAssertNotNil(controlConfig);
XCTAssertEqualObjects(actualConfig, controlConfig);

NSString *readdleLibSubrepoRevision = nil;
XCTAssertEqual(0, [readdleLibSubrepoGit getCurrentRevision:&readdleLibSubrepoRevision]);
XCTAssertEqualObjects(readdleLibSubrepoRevision, readdleLib_pasteys_Revision);

NSString *pdfkitSubrepoRevision = nil;
XCTAssertEqual(0, [pdfkitSubrepoGit getCurrentRevision:&pdfkitSubrepoRevision]);
XCTAssertEqualObjects(pdfkitSubrepoRevision, pdfkit_pasteys_Revision);

GitRepository *sftpSubrepoGit = [GitRepository repoAtPath:@"Dependencies/RDSFTP"];
NSString *sftpSubrepoRevision = nil;
XCTAssertEqual(0, [sftpSubrepoGit getCurrentRevision:&sftpSubrepoRevision]);
XCTAssertEqualObjects(sftpSubrepoRevision, sftp_niks_Revision);
}];
}

- (void)testTheirAddNewSubrepoBothSideUpdateSubrepoToDifferentRevisionConflictResolution_KeepConflict {
__block S7Config *baseConfig = nil;
[self.env.pasteyRd2Repo run:^(GitRepository * _Nonnull repo) {
s7add(@"Dependencies/ReaddleLib", self.env.githubReaddleLibRepo.absolutePath);

[repo add:@[S7ConfigFileName, @".gitignore"]];
[repo commitWithMessage:@"add subrepos"];

baseConfig = [[S7Config alloc] initWithContentsOfFile:S7ConfigFileName];

s7push_currentBranch(repo);
}];

__block S7Config *niksConfig = nil;
__block NSString *readdleLib_niks_Revision = nil;
__block NSString *rdpdfKitLib_niks_Revision = nil;
[self.env.nikRd2Repo run:^(GitRepository * _Nonnull repo) {
[repo pull];

s7init_deactivateHooks();

S7PostMergeHook *postMergeHook = [S7PostMergeHook new];
const int mergeHookExitStatus = [postMergeHook runWithArguments:@[]];
XCTAssertEqual(0, mergeHookExitStatus);

GitRepository *readdleLibSubrepoGit = [GitRepository repoAtPath:@"Dependencies/ReaddleLib"];
XCTAssertNotNil(readdleLibSubrepoGit);

readdleLib_niks_Revision = commit(readdleLibSubrepoGit, @"RDGeometry.h", @"xyz", @"some useful math func");

s7add(@"Dependencies/RDPDFKit", self.env.githubRDPDFKitRepo.absolutePath);

GitRepository *rdpdfKitSubrepoGit = [GitRepository repoAtPath:@"Dependencies/RDPDFKit"];
XCTAssertEqual(0, [rdpdfKitSubrepoGit getCurrentRevision:&rdpdfKitLib_niks_Revision]);

s7rebind_with_stage();
[repo commitWithMessage:@"up ReaddleLib and RDSFTP"];

niksConfig = [[S7Config alloc] initWithContentsOfFile:S7ConfigFileName];

s7push_currentBranch(repo);
}];

[self.env.pasteyRd2Repo run:^(GitRepository * _Nonnull repo) {
GitRepository *readdleLibSubrepoGit = [GitRepository repoAtPath:@"Dependencies/ReaddleLib"];
NSString *readdleLib_pasteys_Revision =
commit(readdleLibSubrepoGit, @"RDSystemInfo.h", @"iPad 11''", @"add support for a new iPad model");

s7rebind_with_stage();
[repo commitWithMessage:@"up ReaddleLib and RDPDFKit"];

S7Config *ourConfig = [[S7Config alloc] initWithContentsOfFile:S7ConfigFileName];

const int pushExitStatus = s7push_currentBranch(repo);
XCTAssertNotEqual(0, pushExitStatus, @"nik has pushed. I must merge");

[repo fetch];

S7ConfigMergeDriver *configMergeDriver = [S7ConfigMergeDriver new];

[configMergeDriver setResolveConflictBlock:^S7ConflictResolutionOption(S7SubrepoDescription * _Nonnull ourVersion,
S7SubrepoDescription * _Nonnull theirVersion)
{
XCTAssertEqualObjects(ourVersion.path, @"Dependencies/ReaddleLib");

return S7ConflictResolutionOptionKeepConflict;
}];

const int mergeExitStatus = [configMergeDriver
mergeRepo:repo
baseConfig:baseConfig
ourConfig:ourConfig
theirConfig:niksConfig
saveResultToFilePath:S7ConfigFileName];
XCTAssertNotEqual(0, mergeExitStatus);

S7Config *actualConfig = [[S7Config alloc] initWithContentsOfFile:S7ConfigFileName];

S7Config *expectedConfig = [[S7Config alloc] initWithSubrepoDescriptions:@[
[[S7SubrepoDescriptionConflict alloc]
initWithOurVersion:[[S7SubrepoDescription alloc]
initWithPath:@"Dependencies/ReaddleLib"
url:self.env.githubReaddleLibRepo.absolutePath
revision:readdleLib_pasteys_Revision
branch:@"main"]
theirVersion:[[S7SubrepoDescription alloc]
initWithPath:@"Dependencies/ReaddleLib"
url:self.env.githubReaddleLibRepo.absolutePath
revision:readdleLib_niks_Revision
branch:@"main"]],

[[S7SubrepoDescription alloc]
initWithPath:@"Dependencies/RDPDFKit"
url:self.env.githubRDPDFKitRepo.absolutePath
revision:rdpdfKitLib_niks_Revision
branch:@"main"]
]];

XCTAssertEqualObjects(actualConfig, expectedConfig);

S7Config *controlConfig = [[S7Config alloc] initWithContentsOfFile:S7ControlFileName];
XCTAssertNotNil(controlConfig);
XCTAssertEqualObjects(actualConfig, controlConfig);

// Test ReaddleLib has merge conflict and subrepo is set to local revision.
NSString *readdleLibSubrepoRevision = nil;
XCTAssertEqual(0, [readdleLibSubrepoGit getCurrentRevision:&readdleLibSubrepoRevision]);
XCTAssertEqualObjects(readdleLibSubrepoRevision, readdleLib_pasteys_Revision);

// Test RDPDFKit has been checked out and points to 'their' revision.
GitRepository *pdfkitSubrepoGit = [GitRepository repoAtPath:@"Dependencies/RDPDFKit"];
NSString *pdfkitSubrepoRevision = nil;
XCTAssertEqual(0, [pdfkitSubrepoGit getCurrentRevision:&pdfkitSubrepoRevision]);
XCTAssertEqualObjects(pdfkitSubrepoRevision, rdpdfKitLib_niks_Revision);
}];
}

Expand Down Expand Up @@ -769,6 +899,8 @@ - (void)testBothSideUpdateSubrepoToDifferentRevisionSameFileSubrepoConflictResol
S7Config *controlConfig = [[S7Config alloc] initWithContentsOfFile:S7ControlFileName];
XCTAssertNotNil(controlConfig);
XCTAssertEqualObjects(actualConfig, controlConfig);

XCTAssertEqualObjects(readdleLibActualRevision, mergedReaddleLibRevision);
}];
}

Expand Down Expand Up @@ -1441,6 +1573,8 @@ - (void)testOneSideUpdateOtherSideDeleteConflictResolution_UnexpectedEnvResoluti
S7Config *controlConfig = [[S7Config alloc] initWithContentsOfFile:S7ControlFileName];
XCTAssertNotNil(controlConfig);
XCTAssertEqualObjects(actualConfig, controlConfig);

XCTAssertNil([GitRepository repoAtPath:@"Dependencies/ReaddleLib"]);
}];
}

Expand Down
6 changes: 6 additions & 0 deletions system7/Hooks/S7PostCheckoutHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ NS_ASSUME_NONNULL_BEGIN
toConfig:(S7Config *)toConfig
clean:(BOOL)clean;

+ (int)checkoutSubreposForRepo:(GitRepository *)repo
fromConfig:(S7Config *)fromConfig
toConfig:(S7Config *)toConfig
clean:(BOOL)clean
skipConflicts:(BOOL)skipConflicts;

@end

NS_ASSUME_NONNULL_END
52 changes: 52 additions & 0 deletions system7/Hooks/S7PostCheckoutHook.m
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,29 @@ + (int)checkoutSubreposForRepo:(GitRepository *)repo
toConfig:(S7Config *)toConfig
clean:(BOOL)clean
{
return [self checkoutSubreposForRepo:repo
fromConfig:fromConfig
toConfig:toConfig
clean:clean
skipConflicts:NO];
}

+ (int)checkoutSubreposForRepo:(GitRepository *)repo
fromConfig:(S7Config *)fromConfig
toConfig:(S7Config *)toConfig
clean:(BOOL)clean
skipConflicts:(BOOL)skipConflicts
{
if (skipConflicts) {
NSAssert(0 == [[self findConflictsInConfig:fromConfig] count], @"");

NSArray<S7SubrepoDescription *> *const subrepoConflicts = [self findConflictsInConfig:toConfig];
if (0 != subrepoConflicts.count) {
fromConfig = [self makeConfigWithConfig:fromConfig removingSubrepoDescriptions:subrepoConflicts];
toConfig = [self makeConfigWithConfig:toConfig removingSubrepoDescriptions:subrepoConflicts];
}
}

NSDictionary<NSString *, S7SubrepoDescription *> *subreposToDelete = nil;
NSDictionary<NSString *, S7SubrepoDescription *> *dummy = nil;
diffConfigs(fromConfig,
Expand Down Expand Up @@ -387,6 +410,35 @@ + (int)checkoutSubreposForRepo:(GitRepository *)repo
return exitCode;
}

+ (NSArray<S7SubrepoDescription *> *)findConflictsInConfig:(S7Config *)config {
NSPredicate *const conflictPredicate = [NSPredicate predicateWithBlock:
^BOOL(S7SubrepoDescription * _Nullable subrepoDescription,
NSDictionary<NSString *,id> * _Nullable bindings) {
return subrepoDescription.hasConflict;
}];

return [config.subrepoDescriptions filteredArrayUsingPredicate:conflictPredicate];
}

+ (S7Config *)makeConfigWithConfig:(S7Config *)config
removingSubrepoDescriptions:(NSArray<S7SubrepoDescription *> *)subrepoDescriptionsToRemove
{
NSMutableSet<NSString *> *pathsToRemove = [NSMutableSet set];
for (S7SubrepoDescription *const subrepoDescription in subrepoDescriptionsToRemove) {
[pathsToRemove addObject:subrepoDescription.path];
}

NSPredicate *const predicate = [NSPredicate predicateWithBlock:
^BOOL(S7SubrepoDescription * _Nullable subrepoDescription,
NSDictionary<NSString *,id> * _Nullable bindings) {
return NO == [pathsToRemove containsObject:subrepoDescription.path];
}];

NSArray<S7SubrepoDescription *> *subrepoDescriptionsToRetain = [config.subrepoDescriptions
filteredArrayUsingPredicate:predicate];
return [[S7Config alloc] initWithSubrepoDescriptions:subrepoDescriptionsToRetain];
}

+ (int)tryMovingSameOriginSubrepos:(NSArray<S7SubrepoDescription *> *)subrepos
ifPresentInSubrepos:(NSArray<S7SubrepoDescription *> *)subreposToCheckout
parentRepoAbsolutePath:(NSString *)parentRepoAbsolutePath
Expand Down
6 changes: 6 additions & 0 deletions system7/Merge Driver/S7ConfigMergeDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ - (int)mergeRepo:(GitRepository *)repo
}

if (NO == conflictResolved) {
[S7PostCheckoutHook checkoutSubreposForRepo:repo
fromConfig:ourConfig
toConfig:mergeResult
clean:NO
skipConflicts:YES];

return S7ExitCodeMergeFailed;
}

Expand Down
2 changes: 2 additions & 0 deletions system7/Types/S7SubrepoDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, nullable) NSString *comment;

@property (nonatomic, readonly) BOOL hasConflict;

- (instancetype)initWithConfigLine:(NSString *)trimmedLine;

- (instancetype)initWithPath:(NSString *)path
Expand Down
6 changes: 6 additions & 0 deletions system7/Types/S7SubrepoDescription.m
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,10 @@ - (NSString *)description {
return self.stringRepresentation;
}

#pragma mark -

- (BOOL)hasConflict {
return NO;
}

@end
3 changes: 3 additions & 0 deletions system7/Types/S7SubrepoDescriptionConflict.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,8 @@ - (NSString *)stringRepresentation {
self.theirVersion ? [NSString stringWithFormat:@"%@\n", self.theirVersion.stringRepresentation] : @""];
}

- (BOOL)hasConflict {
return YES;
}

@end

0 comments on commit 64eaf63

Please sign in to comment.