Skip to content

Commit

Permalink
Merge pull request #119 from libgit2/diff-improvements
Browse files Browse the repository at this point in the history
Diff Pathspec
  • Loading branch information
jspahrsummers committed Jan 14, 2013
2 parents 453cc43 + a08b5cf commit c929abb
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 32 deletions.
11 changes: 11 additions & 0 deletions Classes/GTDiff.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ extern NSString *const GTDiffOptionsNewPrefixKey;
// Defaults to 512MB.
extern NSString *const GTDiffOptionsMaxSizeKey;

// An `NSArray` of `NSStrings`s to limit the diff to specific paths inside the
// repository. The entries in the array represent either single paths or
// filename patterns with wildcard matching a la standard shell glob (see
// http://linux.die.net/man/7/glob for wildcard matching rules).
//
// The diff will only contain the files or patterns included in this options
// array.
//
// Defaults to including all files.
extern NSString *const GTDiffOptionsPathSpecArrayKey;

// Enum for use as documented in the options dictionary with the
// `GTDiffOptionsFlagsKey` key.
//
Expand Down
65 changes: 44 additions & 21 deletions Classes/GTDiff.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
NSString *const GTDiffOptionsOldPrefixKey = @"GTDiffOptionsOldPrefixKey";
NSString *const GTDiffOptionsNewPrefixKey = @"GTDiffOptionsNewPrefixKey";
NSString *const GTDiffOptionsMaxSizeKey = @"GTDiffOptionsMaxSizeKey";
NSString *const GTDiffOptionsPathSpecArrayKey = @"GTDiffOptionsPathSpecArrayKey";

NSString *const GTDiffFindOptionsFlagsKey = @"GTDiffFindOptionsFlagsKey";
NSString *const GTDiffFindOptionsRenameThresholdKey = @"GTDiffFindOptionsRenameThresholdKey";
Expand All @@ -30,40 +31,62 @@

@implementation GTDiff

+ (BOOL)optionsStructFromDictionary:(NSDictionary *)dictionary optionsStruct:(git_diff_options *)newOptions {
if (dictionary == nil || dictionary.count < 1) return NO;
+ (git_diff_options *)optionsStructFromDictionary:(NSDictionary *)dictionary {
if (dictionary == nil || dictionary.count < 1) return nil;

git_diff_options newOptions = GIT_DIFF_OPTIONS_INIT;

NSNumber *flagsNumber = dictionary[GTDiffOptionsFlagsKey];
if (flagsNumber != nil) newOptions->flags = (uint32_t)flagsNumber.unsignedIntegerValue;
if (flagsNumber != nil) newOptions.flags = (uint32_t)flagsNumber.unsignedIntegerValue;

NSNumber *contextLinesNumber = dictionary[GTDiffOptionsContextLinesKey];
if (contextLinesNumber != nil) newOptions->context_lines = (uint16_t)contextLinesNumber.unsignedIntegerValue;
if (contextLinesNumber != nil) newOptions.context_lines = (uint16_t)contextLinesNumber.unsignedIntegerValue;

NSNumber *interHunkLinesNumber = dictionary[GTDiffOptionsInterHunkLinesKey];
if (interHunkLinesNumber != nil) newOptions->interhunk_lines = (uint16_t)interHunkLinesNumber.unsignedIntegerValue;
if (interHunkLinesNumber != nil) newOptions.interhunk_lines = (uint16_t)interHunkLinesNumber.unsignedIntegerValue;

// We cast to char* below to work around a current bug in libgit2, which is
// fixed in https://github.com/libgit2/libgit2/pull/1118

NSString *oldPrefix = dictionary[GTDiffOptionsOldPrefixKey];
if (oldPrefix != nil) newOptions->old_prefix = (char *)oldPrefix.UTF8String;
if (oldPrefix != nil) newOptions.old_prefix = (char *)oldPrefix.UTF8String;

NSString *newPrefix = dictionary[GTDiffOptionsNewPrefixKey];
if (newPrefix != nil) newOptions->new_prefix = (char *)newPrefix.UTF8String;
if (newPrefix != nil) newOptions.new_prefix = (char *)newPrefix.UTF8String;

NSNumber *maxSizeNumber = dictionary[GTDiffOptionsMaxSizeKey];
if (maxSizeNumber != nil) newOptions->max_size = (uint16_t)maxSizeNumber.unsignedIntegerValue;
if (maxSizeNumber != nil) newOptions.max_size = (uint16_t)maxSizeNumber.unsignedIntegerValue;

NSArray *pathSpec = dictionary[GTDiffOptionsPathSpecArrayKey];
if (pathSpec != nil) {
char **cStrings = malloc(sizeof(*cStrings) * pathSpec.count);
for (NSUInteger idx = 0; idx < pathSpec.count; idx ++) {
cStrings[idx] = (char *)[pathSpec[idx] cStringUsingEncoding:NSUTF8StringEncoding];
}

git_strarray optionsPathSpec = {.strings = cStrings, .count = pathSpec.count};
newOptions.pathspec = optionsPathSpec;
}

return YES;
git_diff_options *returnOptions = malloc(sizeof(*returnOptions));
memcpy(returnOptions, &newOptions, sizeof(*returnOptions));

return returnOptions;
}

+ (void)freeOptionsStruct:(git_diff_options *)options {
if (options == NULL) return;
free(options->pathspec.strings);
free(options);
}

+ (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree options:(NSDictionary *)options error:(NSError **)error {
NSParameterAssert([oldTree.repository isEqual:newTree.repository]);

git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT;
BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct];
git_diff_options *optionsStruct = [self optionsStructFromDictionary:options];
git_diff_list *diffList;
int returnValue = git_diff_tree_to_tree(&diffList, oldTree.repository.git_repository, oldTree.git_tree, newTree.git_tree, (optionsStructCreated ? &optionsStruct : NULL));
int returnValue = git_diff_tree_to_tree(&diffList, oldTree.repository.git_repository, oldTree.git_tree, newTree.git_tree, optionsStruct);
[self freeOptionsStruct:optionsStruct];
if (returnValue != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:returnValue withAdditionalDescription:@"Failed to create diff."];
return nil;
Expand All @@ -76,10 +99,10 @@ + (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree options:
+ (GTDiff *)diffIndexFromTree:(GTTree *)tree options:(NSDictionary *)options error:(NSError **)error {
NSParameterAssert(tree != nil);

git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT;
BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct];
git_diff_options *optionsStruct = [self optionsStructFromDictionary:options];
git_diff_list *diffList;
int returnValue = git_diff_tree_to_index(&diffList, tree.repository.git_repository, tree.git_tree, NULL, (optionsStructCreated ? &optionsStruct : NULL));
int returnValue = git_diff_tree_to_index(&diffList, tree.repository.git_repository, tree.git_tree, NULL, optionsStruct);
[self freeOptionsStruct:optionsStruct];
if (returnValue != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:returnValue withAdditionalDescription:@"Failed to create diff."];
return nil;
Expand All @@ -92,10 +115,10 @@ + (GTDiff *)diffIndexFromTree:(GTTree *)tree options:(NSDictionary *)options err
+ (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error {
NSParameterAssert(repository != nil);

git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT;
BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct];
git_diff_options *optionsStruct = [self optionsStructFromDictionary:options];
git_diff_list *diffList;
int returnValue = git_diff_index_to_workdir(&diffList, repository.git_repository, NULL, (optionsStructCreated ? &optionsStruct : NULL));
int returnValue = git_diff_index_to_workdir(&diffList, repository.git_repository, NULL, optionsStruct);
[self freeOptionsStruct:optionsStruct];
if (returnValue != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:returnValue withAdditionalDescription:@"Failed to create diff."];
return nil;
Expand All @@ -108,10 +131,10 @@ + (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository o
+ (GTDiff *)diffWorkingDirectoryFromTree:(GTTree *)tree options:(NSDictionary *)options error:(NSError **)error {
NSParameterAssert(tree != nil);

git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT;
BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct];
git_diff_options *optionsStruct = [self optionsStructFromDictionary:options];
git_diff_list *diffList;
int returnValue = git_diff_tree_to_workdir(&diffList, tree.repository.git_repository, tree.git_tree, (optionsStructCreated ? &optionsStruct : NULL));
int returnValue = git_diff_tree_to_workdir(&diffList, tree.repository.git_repository, tree.git_tree, optionsStruct);
[self freeOptionsStruct:optionsStruct];
if (returnValue != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:returnValue withAdditionalDescription:@"Failed to create diff."];
return nil;
Expand Down
12 changes: 2 additions & 10 deletions Classes/GTRepository.m
Original file line number Diff line number Diff line change
Expand Up @@ -172,24 +172,16 @@ + (id)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL bar
checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE;
checkoutOptions.progress_cb = checkoutProgressCallback;
checkoutOptions.progress_payload = (__bridge void *)checkoutProgressBlock;
cloneOptions.checkout_opts = &checkoutOptions;
cloneOptions.checkout_opts = checkoutOptions;
}

cloneOptions.fetch_progress_cb = transferProgressCallback;
cloneOptions.fetch_progress_payload = (__bridge void *)transferProgressBlock;

git_remote *remote;
const char *remoteURL = originURL.absoluteString.UTF8String;
int gitError = git_remote_new(&remote, NULL, "origin", remoteURL, GIT_REMOTE_DEFAULT_FETCH);
if (gitError != GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:gitError withAdditionalDescription:@"Failed to create remote to clone repository."];
return nil;
}

const char *workingDirectoryPath = workdirURL.path.UTF8String;
git_repository *repository;
gitError = git_clone(&repository, remote, workingDirectoryPath, &cloneOptions);
git_remote_free(remote);
int gitError = git_clone(&repository, remoteURL, workingDirectoryPath, &cloneOptions);
if (gitError < GIT_OK) {
if (error != NULL) *error = [NSError git_errorFor:gitError withAdditionalDescription:@"Failed to clone repository."];
return nil;
Expand Down
22 changes: 22 additions & 0 deletions ObjectiveGitTests/GTDiffSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@
}];
});

it(@"should correctly limit itself to a given pathspec", ^{
NSDictionary *options = @{ GTDiffOptionsPathSpecArrayKey: @[ @"ladflbahjgdf" ] };
setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", options);
expect(diff.deltaCount).to.equal(0);

options = @{ GTDiffOptionsPathSpecArrayKey: @[ @"TestAppWindowController.h" ] };
setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", options);
expect(diff.deltaCount).to.equal(1);
});

it(@"should correctly recognise binary and text files", ^{
setupDiffFromCommitSHAsAndOptions(@"6b0c1c8b8816416089c534e474f4c692a76ac14f", @"a4bca6b67a5483169963572ee3da563da33712f7", nil);
expect(diff.deltaCount).to.equal(3);
Expand All @@ -198,6 +208,18 @@
*stop = YES;
}];
});

it(@"should correctly find untracked files if asked", ^{
diff = [GTDiff diffIndexToWorkingDirectoryInRepository:repository options:@{ GTDiffOptionsFlagsKey: @(GTDiffOptionsFlagsIncludeUntracked) } error:NULL];
__block BOOL foundImage = NO;
[diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) {
if (![delta.newFile.path isEqualToString:@"UntrackedImage.png"]) return;
foundImage = YES;
*stop = YES;
}];

expect(foundImage).to.beTruthy();
});
});

SpecEnd
Binary file modified ObjectiveGitTests/fixtures/Fixtures.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion libgit2
Submodule libgit2 updated 296 files

0 comments on commit c929abb

Please sign in to comment.