diff --git a/Classes/GTDiff.h b/Classes/GTDiff.h new file mode 100644 index 000000000..997cc9a1c --- /dev/null +++ b/Classes/GTDiff.h @@ -0,0 +1,221 @@ +// +// GTDiff.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 29/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "git2.h" + +#import "GTDiffDelta.h" + +@class GTDiffDelta; +@class GTRepository; +@class GTTree; + +// An `NSNumber` wrapped `GTDiffOptionsFlags` representing any flags you wish to +// pass into the initialisation. +extern NSString *const GTDiffOptionsFlagsKey; + +// An `NSNumber` wrapped `NSUInteger` dictating the number of unchanged lines +// that define the boundary of a hunk (and to display around it). +// +// Defaults to 3. +extern NSString *const GTDiffOptionsContextLinesKey; + +// An `NSNumber` wrapped `NSUInteger` dictating the maximum number of unchanged +// lines between hunk boundaries before the hunks will be merged. +// +// Defaults to 0. +extern NSString *const GTDiffOptionsInterHunkLinesKey; + +// An `NSString` to prefix old file names with. +// +// Defaults to "a". +extern NSString *const GTDiffOptionsOldPrefixKey; + +// An `NSString` to prefix new file names with. +// +// Defaults to "b". +extern NSString *const GTDiffOptionsNewPrefixKey; + +// An `NSNumber` wrapped `NSUInteger` determining the maximum size (in bytes) +// of a file to diff. Above this size the file will be treated as binary. +// +// Defaults to 512MB. +extern NSString *const GTDiffOptionsMaxSizeKey; + +// Enum for use as documented in the options dictionary with the +// `GTDiffOptionsFlagsKey` key. +// +// See diff.h for documentation of each individual flag. +typedef enum : git_diff_option_t { + GTDiffOptionsFlagsNormal = GIT_DIFF_NORMAL, + GTDiffOptionsFlagsReverse = GIT_DIFF_REVERSE, + GTDiffOptionsFlagsForceText = GIT_DIFF_FORCE_TEXT, + GTDiffOptionsFlagsIgnoreWhitespace = GIT_DIFF_IGNORE_WHITESPACE, + GTDiffOptionsFlagsIgnoreWhitespaceChange = GIT_DIFF_IGNORE_WHITESPACE_CHANGE, + GTDiffOptionsFlagsIgnoreWhitespaceEOL = GIT_DIFF_IGNORE_WHITESPACE_EOL, + GTDiffOptionsFlagsIgnoreSubmodules = GIT_DIFF_IGNORE_SUBMODULES, + GTDiffOptionsFlagsPatience = GIT_DIFF_PATIENCE, + GTDiffOptionsFlagsIncludeIgnored = GIT_DIFF_INCLUDE_IGNORED, + GTDiffOptionsFlagsIncludeUntracked = GIT_DIFF_INCLUDE_UNTRACKED, + GTDiffOptionsFlagsIncludeUnmodified = GIT_DIFF_INCLUDE_UNMODIFIED, + GTDiffOptionsFlagsRecurseUntrackedDirs = GIT_DIFF_RECURSE_UNTRACKED_DIRS, + GTDiffOptionsFlagsDisablePathspecMatch = GIT_DIFF_DISABLE_PATHSPEC_MATCH, + GTDiffOptionsFlagsDeltasAreICase = GIT_DIFF_DELTAS_ARE_ICASE, + GTDiffOptionsFlagsIncludeUntrackedContent = GIT_DIFF_INCLUDE_UNTRACKED_CONTENT, + GTDiffOptionsFlagsSkipBinaryCheck = GIT_DIFF_SKIP_BINARY_CHECK, + GTDiffOptionsFlagsIncludeTypeChange = GIT_DIFF_INCLUDE_TYPECHANGE, + GTDiffOptionsFlagsIncludeTypeChangeTrees = GIT_DIFF_INCLUDE_TYPECHANGE_TREES, + GTDiffOptionsFlagsIgnoreFileMode = GIT_DIFF_IGNORE_FILEMODE, +} GTDiffOptionsFlags; + +// An `NSNumber` wrapped `GTDiffOptionsFlags` bitmask containing any of the +// flags documented below. +// +// Defualts to `GTDiffFindOptionsFlagsFindRenames`. +extern NSString *const GTDiffFindOptionsFlagsKey; + +// An `NSNumber` wrapped `NSUInteger` dictating the similarity between files +// to be considered a rename. +// +// This is a value as per the git similarity index and should be between 1 and +// 100 (0 and above 100 use the default). +// +// Defaults to 50. +extern NSString *const GTDiffFindOptionsRenameThresholdKey; + +// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can +// be to be eligable as a rename. +// +// This is a value as per the git similarity index and should be between 1 and +// 100 (0 and above 100 use the default). +// +// Defaults to 50. +extern NSString *const GTDiffFindOptionsRenameFromRewriteThresholdKey; + +// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can +// be to be considered a copy. +// +// This is a value as per the git similarity index and should be between 1 and +// 100 (0 and above 100 use the default). +// +// Defaults to 50. +extern NSString *const GTDiffFindOptionsCopyThresholdKey; + +// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can +// be to be to be broken into a separate deletion and addition pair. +// +// This is a value as per the git similarity index and should be between 1 and +// 100 (0 and above 100 use the default). +// +// Defaults to 60. +extern NSString *const GTDiffFindOptionsBreakRewriteThresholdKey; + +// An `NSNumber` wrapped `NSUInteger` dictating the maximum amount of similarity +// sources to examine. +// +// This is the equivalent of the `diff.renameLimit` config value. +// +// Defaults to 200. +extern NSString *const GTDiffFindOptionsTargetLimitKey; + +// Enum for options passed into `-findSimilarWithOptions:`. +// +// For individual case documentation see `diff.h`. +typedef enum : git_diff_find_t { + GTDiffFindOptionsFlagsFindRenames = GIT_DIFF_FIND_RENAMES, + GTDiffFindOptionsFlagsFindRenamesFromRewrites = GIT_DIFF_FIND_RENAMES_FROM_REWRITES, + GTDiffFindOptionsFlagsFindCopies = GIT_DIFF_FIND_COPIES, + GTDiffFindOptionsFlagsFindCopiesFromUnmodified = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, + GTDiffFindOptionsFlagsFindAndBreakRewrites = GIT_DIFF_FIND_AND_BREAK_REWRITES, +} GTDiffFindOptionsFlags; + +// A class representing a single "diff". +// +// Analagous to `git_diff_list` in libgit2, this object represents a list of +// changes or "deltas", which are represented by `GTDiffDelta` objects. +@interface GTDiff : NSObject + +// The libgit2 diff list object. +@property (nonatomic, readonly) git_diff_list *git_diff_list; + +// The number of deltas represented by the diff object. +@property (nonatomic, readonly) NSUInteger deltaCount; + +// Create a diff between 2 `GTTree`s. +// +// The 2 trees must be from the same repository, or an exception will be thrown. +// +// oldTree - The "left" side of the diff. +// newTree - The "right" side of the diff. +// options - A dictionary containing any of the above options key constants, or +// nil to use the defaults. +// +// Returns a newly created `GTDiff` object or nil on error. ++ (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree options:(NSDictionary *)options; + +// Create a diff between a repository's current index. +// +// This is equivalent to `git diff --cached ` or if you pass the HEAD +// tree, then `git diff --cached`. +// +// The tree you pass will be used for the "left" side of the diff, and the +// index will be used for the "right" side of the diff. +// +// tree - The tree to be diffed. The index will be taken from this tree's +// repository. The left side of the diff. +// options - A dictionary containing any of the above options key constants, or +// nil to use the defaults. +// +// Returns a newly created `GTDiff` object or nil on error. ++ (GTDiff *)diffIndexFromTree:(GTTree *)tree options:(NSDictionary *)options; + +// Create a diff between the index and working directory in a given repository. +// +// This matches the `git diff` command. +// +// repository - The repository to be used for the diff. +// options - A dictionary containing any of the above options key constants, +// or nil to use the defaults. +// +// Returns a newly created `GTDiff` object or nil on error. ++ (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary *)options; + +// Create a diff between a repository's working directory and a tree. +// +// tree - The tree to be diffed. The tree will be the left side of the diff. +// options - A dictionary containing any of the above options key constants, or +// nil to use the defaults. +// +// Returns a newly created `GTDiff` object or nil on error. ++ (GTDiff *)diffWorkingDirectoryFromTree:(GTTree *)tree options:(NSDictionary *)options; + +// Designated initialiser. +- (instancetype)initWithGitDiffList:(git_diff_list *)diffList; + +// The number of deltas of the given type that are contained in the diff. +- (NSUInteger)numberOfDeltasWithType:(GTDiffDeltaType)deltaType; + +// Enumerate the deltas in a diff. +// +// It is worth noting that the `git_diff_patch` objects backing each delta +// contain the entire contents in memory. It is therefore recommended you +// do not store the `delta` object given here, but instead perform any work +// necessary within the provided block. +// +// Also note that this method blocks during the enumeration. +// +// block - A block to be executed for each delta. Setting `stop` to `YES` +// immediately stops the enumeration. +- (void)enumerateDeltasUsingBlock:(void (^)(GTDiffDelta *delta, BOOL *stop))block; + +// Modify the diff list to combine similar changes using the given options. +// +// options - A dictionary containing any of the above find options key constants +// or nil to use the defaults. +- (void)findSimilarWithOptions:(NSDictionary *)options; + +@end diff --git a/Classes/GTDiff.m b/Classes/GTDiff.m new file mode 100644 index 000000000..780d1ef60 --- /dev/null +++ b/Classes/GTDiff.m @@ -0,0 +1,177 @@ +// +// GTDiff.m +// ObjectiveGitFramework +// +// Created by Danny Greg on 29/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTDiff.h" + +#import "GTDiffDelta.h" +#import "GTRepository.h" +#import "GTTree.h" + +NSString *const GTDiffOptionsFlagsKey = @"GTDiffOptionsFlagsKey"; +NSString *const GTDiffOptionsContextLinesKey = @"GTDiffOptionsContextLinesKey"; +NSString *const GTDiffOptionsInterHunkLinesKey = @"GTDiffOptionsInterHunkLinesKey"; +NSString *const GTDiffOptionsOldPrefixKey = @"GTDiffOptionsOldPrefixKey"; +NSString *const GTDiffOptionsNewPrefixKey = @"GTDiffOptionsNewPrefixKey"; +NSString *const GTDiffOptionsMaxSizeKey = @"GTDiffOptionsMaxSizeKey"; + +NSString *const GTDiffFindOptionsFlagsKey = @"GTDiffFindOptionsFlagsKey"; +NSString *const GTDiffFindOptionsRenameThresholdKey = @"GTDiffFindOptionsRenameThresholdKey"; +NSString *const GTDiffFindOptionsRenameFromRewriteThresholdKey = @"GTDiffFindOptionsRenameFromRewriteThresholdKey"; +NSString *const GTDiffFindOptionsCopyThresholdKey = @"GTDiffFindOptionsCopyThresholdKey"; +NSString *const GTDiffFindOptionsBreakRewriteThresholdKey = @"GTDiffFindOptionsBreakRewriteThresholdKey"; +NSString *const GTDiffFindOptionsTargetLimitKey = @"GTDiffFindOptionsTargetLimitKey"; + +@implementation GTDiff + ++ (BOOL)optionsStructFromDictionary:(NSDictionary *)dictionary optionsStruct:(git_diff_options *)newOptions { + if (dictionary == nil || dictionary.count < 1) return NO; + + NSNumber *flagsNumber = dictionary[GTDiffOptionsFlagsKey]; + if (flagsNumber != nil) newOptions->flags = (uint32_t)flagsNumber.unsignedIntegerValue; + + NSNumber *contextLinesNumber = dictionary[GTDiffOptionsContextLinesKey]; + if (contextLinesNumber != nil) newOptions->context_lines = (uint16_t)contextLinesNumber.unsignedIntegerValue; + + NSNumber *interHunkLinesNumber = dictionary[GTDiffOptionsInterHunkLinesKey]; + 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; + + NSString *newPrefix = dictionary[GTDiffOptionsNewPrefixKey]; + if (newPrefix != nil) newOptions->new_prefix = (char *)newPrefix.UTF8String; + + NSNumber *maxSizeNumber = dictionary[GTDiffOptionsMaxSizeKey]; + if (maxSizeNumber != nil) newOptions->max_size = (uint16_t)maxSizeNumber.unsignedIntegerValue; + + return YES; +} + ++ (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree options:(NSDictionary *)options { + NSParameterAssert([oldTree.repository isEqualTo:newTree.repository]); + + git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT; + BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct]; + 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)); + if (returnValue != GIT_OK) return nil; + + GTDiff *newDiff = [[GTDiff alloc] initWithGitDiffList:diffList]; + return newDiff; +} + ++ (GTDiff *)diffIndexFromTree:(GTTree *)tree options:(NSDictionary *)options { + NSParameterAssert(tree != nil); + + git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT; + BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct]; + git_diff_list *diffList; + int returnValue = git_diff_tree_to_index(&diffList, tree.repository.git_repository, tree.git_tree, NULL, (optionsStructCreated ? &optionsStruct : NULL)); + if (returnValue != GIT_OK) return nil; + + GTDiff *newDiff = [[GTDiff alloc] initWithGitDiffList:diffList]; + return newDiff; +} + ++ (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary *)options { + NSParameterAssert(repository != nil); + + git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT; + BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct]; + git_diff_list *diffList; + int returnValue = git_diff_index_to_workdir(&diffList, repository.git_repository, NULL, (optionsStructCreated ? &optionsStruct : NULL)); + if (returnValue != GIT_OK) return nil; + + GTDiff *newDiff = [[GTDiff alloc] initWithGitDiffList:diffList]; + return newDiff; +} + ++ (GTDiff *)diffWorkingDirectoryFromTree:(GTTree *)tree options:(NSDictionary *)options { + NSParameterAssert(tree != nil); + + git_diff_options optionsStruct = GIT_DIFF_OPTIONS_INIT; + BOOL optionsStructCreated = [self optionsStructFromDictionary:options optionsStruct:&optionsStruct]; + git_diff_list *diffList; + int returnValue = git_diff_tree_to_workdir(&diffList, tree.repository.git_repository, tree.git_tree, (optionsStructCreated ? &optionsStruct : NULL)); + if (returnValue != GIT_OK) return nil; + + GTDiff *newDiff = [[GTDiff alloc] initWithGitDiffList:diffList]; + return newDiff; +} + +- (instancetype)initWithGitDiffList:(git_diff_list *)diffList { + NSParameterAssert(diffList != NULL); + + self = [super init]; + if (self == nil) return nil; + + _git_diff_list = diffList; + + return self; +} + +- (void)dealloc { + git_diff_list_free(self.git_diff_list); +} + +- (void)enumerateDeltasUsingBlock:(void (^)(GTDiffDelta *delta, BOOL *stop))block { + NSParameterAssert(block != nil); + + for (NSUInteger idx = 0; idx < self.deltaCount; idx ++) { + git_diff_patch *patch; + int result = git_diff_get_patch(&patch, NULL, self.git_diff_list, idx); + if (result != GIT_OK) continue; + GTDiffDelta *delta = [[GTDiffDelta alloc] initWithGitPatch:patch]; + BOOL stop = NO; + block(delta, &stop); + if (stop) return; + } +} + +- (NSUInteger)deltaCount { + return git_diff_num_deltas(self.git_diff_list); +} + +- (NSUInteger)numberOfDeltasWithType:(GTDiffDeltaType)deltaType { + return git_diff_num_deltas_of_type(self.git_diff_list, (git_delta_t)deltaType); +} + +- (BOOL)findOptionsStructWithDictionary:(NSDictionary *)dictionary optionsStruct:(git_diff_find_options *)newOptions { + if (dictionary == nil || dictionary.count < 1) return NO; + + NSNumber *flagsNumber = dictionary[GTDiffFindOptionsFlagsKey]; + if (flagsNumber != nil) newOptions->flags = (uint32_t)flagsNumber.unsignedIntegerValue; + + NSNumber *renameThresholdNumber = dictionary[GTDiffFindOptionsRenameThresholdKey]; + if (renameThresholdNumber != nil) newOptions->rename_threshold = renameThresholdNumber.unsignedIntValue; + + NSNumber *renameFromRewriteThresholdNumber = dictionary[GTDiffFindOptionsRenameFromRewriteThresholdKey]; + if (renameFromRewriteThresholdNumber != nil) newOptions->rename_from_rewrite_threshold = renameFromRewriteThresholdNumber.unsignedIntValue; + + NSNumber *copyThresholdNumber = dictionary[GTDiffFindOptionsCopyThresholdKey]; + if (copyThresholdNumber != nil) newOptions->copy_threshold = copyThresholdNumber.unsignedIntValue; + + NSNumber *breakRewriteThresholdNumber = dictionary[GTDiffFindOptionsBreakRewriteThresholdKey]; + if (renameThresholdNumber != nil) newOptions->break_rewrite_threshold = breakRewriteThresholdNumber.unsignedIntValue; + + NSNumber *targetLimitNumber = dictionary[GTDiffFindOptionsTargetLimitKey]; + if (targetLimitNumber != nil) newOptions->target_limit = targetLimitNumber.unsignedIntValue; + + return YES; +} + +- (void)findSimilarWithOptions:(NSDictionary *)options { + git_diff_find_options findOptions = GIT_DIFF_FIND_OPTIONS_INIT; + BOOL findOptionsCreated = [self findOptionsStructWithDictionary:options optionsStruct:&findOptions]; + git_diff_find_similar(self.git_diff_list, (findOptionsCreated ? &findOptions : NULL)); +} + +@end diff --git a/Classes/GTDiffDelta.h b/Classes/GTDiffDelta.h new file mode 100644 index 000000000..f30d252b0 --- /dev/null +++ b/Classes/GTDiffDelta.h @@ -0,0 +1,81 @@ +// +// GTDiffDelta.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "git2.h" + +@class GTDiffFile; +@class GTDiffHunk; + +// The type of change that this delta represents. +// +// GTDiffFileDeltaUnmodified - No Change. +// GTDiffFileDeltaAdded - The file was added to the index. +// GTDiffFileDeltaDeleted - The file was removed from the working directory. +// GTDiffFileDeltaModified - The file was modified. +// GTDiffFileDeltaRenamed - The file has been renamed. +// GTDiffFileDeltaCopied - The file was duplicated. +// GTDiffFileDeltaIgnored - The file was ignored by git. +// GTDiffFileDeltaUntracked - The file has been added to the working directory +// and is therefore currently untracked. +// GTDiffFileDeltaTypeChange - The file has changed from a blob to either a +// submodule, symlink or directory. Or vice versa. +typedef enum : git_delta_t { + GTDiffFileDeltaUnmodified = GIT_DELTA_UNMODIFIED, + GTDiffFileDeltaAdded = GIT_DELTA_ADDED, + GTDiffFileDeltaDeleted = GIT_DELTA_DELETED, + GTDiffFileDeltaModified = GIT_DELTA_MODIFIED, + GTDiffFileDeltaRenamed = GIT_DELTA_RENAMED, + GTDiffFileDeltaCopied = GIT_DELTA_COPIED, + GTDiffFileDeltaIgnored = GIT_DELTA_IGNORED, + GTDiffFileDeltaUntracked = GIT_DELTA_UNTRACKED, + GTDiffFileDeltaTypeChange = GIT_DELTA_TYPECHANGE, +} GTDiffDeltaType; + +// A class representing a single change within a diff. +// +// The change may not be simply a change of text within a given file, it could +// be that the file was renamed, or added to the index. See `GTDiffDeltaType` +// for the types of change represented. +@interface GTDiffDelta : NSObject + +// A convenience accessor to fetch the `git_diff_delta` represented by the +// object. +@property (nonatomic, readonly) const git_diff_delta *git_diff_delta; + +// The backing libgit2 `git_diff_patch` object. +@property (nonatomic, readonly) git_diff_patch *git_diff_patch; + +// Whether the file(s) are to be treated as binary. +@property (nonatomic, readonly, getter = isBinary) BOOL binary; + +// The file to the "left" of the diff. +@property (nonatomic, readonly, copy) GTDiffFile *oldFile; + +// The file to the "right" of the diff. +@property (nonatomic, readonly, copy) GTDiffFile *newFile; + +// The type of change that this delta represents. +// +// Think "status" as in `git status`. +@property (nonatomic, readonly) GTDiffDeltaType type; + +// The number of hunks represented by this delta. +@property (nonatomic, readonly) NSUInteger hunkCount; + +// Designated initialiser. +- (instancetype)initWithGitPatch:(git_diff_patch *)patch; + +// Enumerate the hunks contained in the delta. +// +// Blocks during enumeration. +// +// block - A block to be executed for each hunk. Setting `stop` to `YES` +// immediately stops the enumeration. +- (void)enumerateHunksWithBlock:(void (^)(GTDiffHunk *hunk, BOOL *stop))block; + +@end diff --git a/Classes/GTDiffDelta.m b/Classes/GTDiffDelta.m new file mode 100644 index 000000000..6862b7963 --- /dev/null +++ b/Classes/GTDiffDelta.m @@ -0,0 +1,69 @@ +// +// GTDiffDelta.m +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTDiffDelta.h" + +#import "GTDiffFile.h" +#import "GTDiffHunk.h" + +@implementation GTDiffDelta + +- (instancetype)initWithGitPatch:(git_diff_patch *)patch { + NSParameterAssert(patch != NULL); + + self = [super init]; + if (self == nil) return nil; + + _git_diff_patch = patch; + + return self; +} + +- (void)dealloc { + git_diff_patch_free(self.git_diff_patch); +} + +#pragma mark - Properties + +- (const git_diff_delta *)git_diff_delta { + return git_diff_patch_delta(self.git_diff_patch); +} + +- (BOOL)isBinary { + return (BOOL)self.git_diff_delta->binary; +} + +- (GTDiffFile *)oldFile { + return [[GTDiffFile alloc] initWithGitDiffFile:self.git_diff_delta->old_file]; +} + +- (GTDiffFile *)newFile { + return [[GTDiffFile alloc] initWithGitDiffFile:self.git_diff_delta->new_file]; +} + +- (GTDiffDeltaType)type { + return (GTDiffDeltaType)self.git_diff_delta->status; +} + +- (NSUInteger)hunkCount { + return git_diff_patch_num_hunks(self.git_diff_patch); +} + +- (void)enumerateHunksWithBlock:(void (^)(GTDiffHunk *hunk, BOOL *stop))block { + NSParameterAssert(block != nil); + + for (NSUInteger idx = 0; idx < self.hunkCount; idx ++) { + GTDiffHunk *hunk = [[GTDiffHunk alloc] initWithDelta:self hunkIndex:idx]; + if (hunk == nil) return; + BOOL shouldStop = NO; + block(hunk, &shouldStop); + if (shouldStop) return; + } +} + +@end diff --git a/Classes/GTDiffFile.h b/Classes/GTDiffFile.h new file mode 100644 index 000000000..0f49c42a4 --- /dev/null +++ b/Classes/GTDiffFile.h @@ -0,0 +1,42 @@ +// +// GTDiffFile.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "git2.h" + +// Flags which may be set on the file. +// +// See diff.h for individual documentation. +typedef enum : git_diff_file_flag_t { + GTDiffFileFlagValidOID = GIT_DIFF_FILE_VALID_OID, + GTDiffFileFlagFreePath = GIT_DIFF_FILE_FREE_PATH, + GTDiffFileFlagBinary = GIT_DIFF_FILE_BINARY, + GTDiffFileFlagNotBinary = GIT_DIFF_FILE_NOT_BINARY, + GTDiffFileFlagFreeData = GIT_DIFF_FILE_FREE_DATA, + GTDiffFileFlagUnmapData = GIT_DIFF_FILE_UNMAP_DATA, + GTDiffFileFlagNoData = GIT_DIFF_FILE_NO_DATA, +} GTDiffFileFlag; + +// A class representing a file on one side of a diff. +@interface GTDiffFile : NSObject + +// The location within the working directory of the file. +@property (nonatomic, readonly, copy) NSString *path; + +// The size (in bytes) of the file. +@property (nonatomic, readonly) NSUInteger size; + +// Any flags set on the file (see `GTDiffFileFlag` for more info). +@property (nonatomic, readonly) GTDiffFileFlag flags; + +// The mode of the file. +@property (nonatomic, readonly) mode_t mode; + +// Designated initialiser. +- (instancetype)initWithGitDiffFile:(git_diff_file)file; + +@end diff --git a/Classes/GTDiffFile.m b/Classes/GTDiffFile.m new file mode 100644 index 000000000..eb4378264 --- /dev/null +++ b/Classes/GTDiffFile.m @@ -0,0 +1,25 @@ +// +// GTDiffFile.m +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTDiffFile.h" + +@implementation GTDiffFile + +- (instancetype)initWithGitDiffFile:(git_diff_file)file { + self = [super init]; + if (self == nil) return nil; + + _size = (NSUInteger)file.size; + _flags = (GTDiffFileFlag)file.flags; + _mode = file.mode; + _path = [NSString stringWithUTF8String:file.path]; + + return self; +} + +@end diff --git a/Classes/GTDiffHunk.h b/Classes/GTDiffHunk.h new file mode 100644 index 000000000..fd97d64ea --- /dev/null +++ b/Classes/GTDiffHunk.h @@ -0,0 +1,47 @@ +// +// GTDiffHunk.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "git2.h" + +@class GTDiffDelta; + +// A character representing the origin of a given line. +// +// See diff.h for individual documentation. +typedef enum : git_diff_line_t { + GTDiffHunkLineOriginContext = GIT_DIFF_LINE_CONTEXT, + GTDiffHunkLineOriginAddition = GIT_DIFF_LINE_ADDITION, + GTDiffHunkLineOriginDeletion = GIT_DIFF_LINE_DELETION, + GTDiffHunkLineOriginAddEOFNewLine = GIT_DIFF_LINE_ADD_EOFNL, + GTDiffHunkLineOriginDeleteEOFNewLine = GIT_DIFF_LINE_DEL_EOFNL, +} GTDiffHunkLineOrigin; + +// A class representing a hunk within a diff delta. +@interface GTDiffHunk : NSObject + +// The header of the hunk. +@property (nonatomic, readonly, copy) NSString *header; + +// The number of lines represented in the hunk. +@property (nonatomic, readonly) NSUInteger lineCount; + +// Designated initialiser. +// +// The contents of a hunk are lazily loaded, therefore we initialise the object +// simply with the delta it originates from and which hunk index it represents. +- (instancetype)initWithDelta:(GTDiffDelta *)delta hunkIndex:(NSUInteger)hunkIndex; + +// Perfoms the given block on each ine in the hunk. +// +// Note that this method blocks during the enumeration. +// +// block - A block to execute on each line. Setting `stop` to `NO` will +// immediately stop the enumeration and return from the method. +- (void)enumerateLinesInHunkUsingBlock:(void (^)(NSString *lineContent, NSUInteger oldLineNumber, NSUInteger newLineNumber, GTDiffHunkLineOrigin lineOrigin, BOOL *stop))block; + +@end diff --git a/Classes/GTDiffHunk.m b/Classes/GTDiffHunk.m new file mode 100644 index 000000000..182741143 --- /dev/null +++ b/Classes/GTDiffHunk.m @@ -0,0 +1,60 @@ +// +// GTDiffHunk.m +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTDiffHunk.h" + +#import "GTDiffDelta.h" + +@interface GTDiffHunk () + +@property (nonatomic, strong, readonly) GTDiffDelta *delta; +@property (nonatomic, readonly) NSUInteger hunkIndex; + +@end + +@implementation GTDiffHunk + +- (instancetype)initWithDelta:(GTDiffDelta *)delta hunkIndex:(NSUInteger)hunkIndex { + self = [super init]; + if (self == nil) return nil; + + _delta = delta; + _hunkIndex = hunkIndex; + + const char *headerCString; + size_t headerLength; + size_t lineCount; + int result = git_diff_patch_get_hunk(NULL, &headerCString, &headerLength, &lineCount, delta.git_diff_patch, hunkIndex); + if (result != GIT_OK) return nil; + + _header = [[[NSString alloc] initWithBytes:headerCString length:headerLength encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.newlineCharacterSet]; + _lineCount = lineCount; + + return self; +} + +- (void)enumerateLinesInHunkUsingBlock:(void (^)(NSString *lineContent, NSUInteger oldLineNumber, NSUInteger newLineNumber, GTDiffHunkLineOrigin lineOrigin, BOOL *stop))block { + NSParameterAssert(block != nil); + + for (NSUInteger idx = 0; idx < self.lineCount; idx ++) { + char lineOrigin; + const char *content; + size_t contentLength; + int oldLineNumber; + int newLineNumber; + int result = git_diff_patch_get_line_in_hunk(&lineOrigin, &content, &contentLength, &oldLineNumber, &newLineNumber, self.delta.git_diff_patch, self.hunkIndex, idx); + if (result != GIT_OK) continue; + + NSString *lineString = [[[NSString alloc] initWithBytes:content length:contentLength encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.newlineCharacterSet]; + BOOL stop = NO; + block(lineString, (NSUInteger)oldLineNumber, (NSUInteger)newLineNumber, lineOrigin, &stop); + if (stop) return; + } +} + +@end diff --git a/Classes/GTRepository.m b/Classes/GTRepository.m index 054553656..7a23400b8 100644 --- a/Classes/GTRepository.m +++ b/Classes/GTRepository.m @@ -161,27 +161,41 @@ static void transferProgressCallback(const git_transfer_progress *progress, void } + (id)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL barely:(BOOL)barely withCheckout:(BOOL)withCheckout error:(NSError **)error transferProgressBlock:(void (^)(const git_transfer_progress *))transferProgressBlock checkoutProgressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))checkoutProgressBlock { - const char *cOriginURL = originURL.absoluteString.UTF8String; - const char *cWorkdirURL = workdirURL.path.UTF8String; - - git_repository *r; - int gitError; + + git_clone_options cloneOptions = GIT_CLONE_OPTIONS_INIT; if (barely) { - gitError = git_clone_bare(&r, cOriginURL, cWorkdirURL, transferProgressCallback, (__bridge void *)transferProgressBlock); - } else { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - opts.checkout_strategy = GIT_CHECKOUT_SAFE; - opts.progress_cb = checkoutProgressCallback; - opts.progress_payload = (__bridge void *)checkoutProgressBlock; - gitError = git_clone(&r, cOriginURL, cWorkdirURL, (withCheckout ? &opts : NULL), transferProgressCallback, (__bridge void *)transferProgressBlock); + cloneOptions.bare = 1; + } + + if (withCheckout) { + git_checkout_opts checkoutOptions = GIT_CHECKOUT_OPTS_INIT; + checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE; + checkoutOptions.progress_cb = checkoutProgressCallback; + checkoutOptions.progress_payload = (__bridge void *)checkoutProgressBlock; + 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); if (gitError < GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError withAdditionalDescription:@"Failed to clone repository."]; return nil; } - return [[self alloc] initWithGitRepository:r]; + return [[self alloc] initWithGitRepository:repository]; } diff --git a/Classes/ObjectiveGit.h b/Classes/ObjectiveGit.h index 8e73ce138..4db8f02fc 100644 --- a/Classes/ObjectiveGit.h +++ b/Classes/ObjectiveGit.h @@ -47,6 +47,11 @@ #import #import +#import +#import +#import +#import + // This must be called before doing any ObjectiveGit work. Under normal // circumstances, it will automatically be called on your behalf. // If you've linked ObjectiveGit as a static library but haven't set diff --git a/ObjectiveGitFramework.xcodeproj/project.pbxproj b/ObjectiveGitFramework.xcodeproj/project.pbxproj index bd7da616a..bbc2c77f5 100644 --- a/ObjectiveGitFramework.xcodeproj/project.pbxproj +++ b/ObjectiveGitFramework.xcodeproj/project.pbxproj @@ -62,6 +62,23 @@ 04DB466E133AB5EB00D9C624 /* GTReference.m in Sources */ = {isa = PBXBuildFile; fileRef = BD441E07131ED0C300187010 /* GTReference.m */; }; 04DB466F133AB5EB00D9C624 /* GTBranch.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F50F57132054D800584FBE /* GTBranch.m */; }; 04DB4672133AB5FE00D9C624 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 04DB4671133AB5FE00D9C624 /* libz.dylib */; }; + 3011D86B1668E48500CE3409 /* GTDiffFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8691668E48500CE3409 /* GTDiffFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3011D86C1668E48500CE3409 /* GTDiffFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8691668E48500CE3409 /* GTDiffFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3011D86D1668E48500CE3409 /* GTDiffFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D86A1668E48500CE3409 /* GTDiffFile.m */; }; + 3011D86E1668E48500CE3409 /* GTDiffFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D86A1668E48500CE3409 /* GTDiffFile.m */; }; + 3011D8711668E78500CE3409 /* GTDiffHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D86F1668E78500CE3409 /* GTDiffHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3011D8721668E78500CE3409 /* GTDiffHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D86F1668E78500CE3409 /* GTDiffHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3011D8731668E78500CE3409 /* GTDiffHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8701668E78500CE3409 /* GTDiffHunk.m */; }; + 3011D8741668E78500CE3409 /* GTDiffHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8701668E78500CE3409 /* GTDiffHunk.m */; }; + 3011D8771668F29600CE3409 /* GTDiffDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8751668F29600CE3409 /* GTDiffDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3011D8781668F29600CE3409 /* GTDiffDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8751668F29600CE3409 /* GTDiffDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3011D8791668F29600CE3409 /* GTDiffDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8761668F29600CE3409 /* GTDiffDelta.m */; }; + 3011D87A1668F29600CE3409 /* GTDiffDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8761668F29600CE3409 /* GTDiffDelta.m */; }; + 30865A91167F503400B1AB6E /* GTDiffSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30865A90167F503400B1AB6E /* GTDiffSpec.m */; }; + 30A3D6541667F11C00C49A39 /* GTDiff.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A3D6521667F11C00C49A39 /* GTDiff.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30A3D6551667F11C00C49A39 /* GTDiff.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A3D6521667F11C00C49A39 /* GTDiff.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30A3D6561667F11C00C49A39 /* GTDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A3D6531667F11C00C49A39 /* GTDiff.m */; }; + 30A3D6571667F11C00C49A39 /* GTDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A3D6531667F11C00C49A39 /* GTDiff.m */; }; 3E0A23E5159E0FDB00A6068F /* GTObjectDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C8054C13861F34004DCB0F /* GTObjectDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55C8054F13861FE7004DCB0F /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; }; 55C8055013861FE7004DCB0F /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; }; @@ -258,6 +275,15 @@ 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 3011D8691668E48500CE3409 /* GTDiffFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffFile.h; sourceTree = ""; }; + 3011D86A1668E48500CE3409 /* GTDiffFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffFile.m; sourceTree = ""; }; + 3011D86F1668E78500CE3409 /* GTDiffHunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffHunk.h; sourceTree = ""; }; + 3011D8701668E78500CE3409 /* GTDiffHunk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffHunk.m; sourceTree = ""; }; + 3011D8751668F29600CE3409 /* GTDiffDelta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffDelta.h; sourceTree = ""; }; + 3011D8761668F29600CE3409 /* GTDiffDelta.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffDelta.m; sourceTree = ""; }; + 30865A90167F503400B1AB6E /* GTDiffSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffSpec.m; sourceTree = ""; }; + 30A3D6521667F11C00C49A39 /* GTDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiff.h; sourceTree = ""; }; + 30A3D6531667F11C00C49A39 /* GTDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiff.m; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* ObjectiveGitFramework_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectiveGitFramework_Prefix.pch; sourceTree = ""; }; 55C8054C13861F34004DCB0F /* GTObjectDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTObjectDatabase.h; sourceTree = ""; }; 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GTObjectDatabase.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -470,6 +496,7 @@ 88F05AAC16011FFD00B7AD1D /* GTTagTest.m */, 88F05AAD16011FFD00B7AD1D /* GTTreeTest.m */, 88F05AAE16011FFD00B7AD1D /* GTWalkerTest.m */, + 30865A90167F503400B1AB6E /* GTDiffSpec.m */, 88F05AAF16011FFD00B7AD1D /* ObjectiveGitTests-Info.plist */, 88F05AB016011FFD00B7AD1D /* ObjectiveGitTests-Prefix.pch */, 88F05A7616011E5400B7AD1D /* Supporting Files */, @@ -580,6 +607,14 @@ 88EB7E4C14AEBA600046FEA4 /* GTConfiguration.m */, 883CD6A91600EBC600F57354 /* GTRemote.h */, 883CD6AA1600EBC600F57354 /* GTRemote.m */, + 30A3D6521667F11C00C49A39 /* GTDiff.h */, + 30A3D6531667F11C00C49A39 /* GTDiff.m */, + 3011D8691668E48500CE3409 /* GTDiffFile.h */, + 3011D86A1668E48500CE3409 /* GTDiffFile.m */, + 3011D86F1668E78500CE3409 /* GTDiffHunk.h */, + 3011D8701668E78500CE3409 /* GTDiffHunk.m */, + 3011D8751668F29600CE3409 /* GTDiffDelta.h */, + 3011D8761668F29600CE3409 /* GTDiffDelta.m */, ); path = Classes; sourceTree = ""; @@ -624,6 +659,10 @@ 04DB465E133AB5E200D9C624 /* GTBranch.h in Headers */, AA046113134F4D2000DF526B /* GTOdbObject.h in Headers */, E9FFC6C01577CC8A00A9E736 /* GTConfiguration.h in Headers */, + 30A3D6551667F11C00C49A39 /* GTDiff.h in Headers */, + 3011D86C1668E48500CE3409 /* GTDiffFile.h in Headers */, + 3011D8721668E78500CE3409 /* GTDiffHunk.h in Headers */, + 3011D8781668F29600CE3409 /* GTDiffDelta.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -653,6 +692,10 @@ 55C8057D13875C11004DCB0F /* NSData+Git.h in Headers */, 883CD6AB1600EBC600F57354 /* GTRemote.h in Headers */, 55C8057E13875C1B004DCB0F /* NSString+Git.h in Headers */, + 30A3D6541667F11C00C49A39 /* GTDiff.h in Headers */, + 3011D86B1668E48500CE3409 /* GTDiffFile.h in Headers */, + 3011D8711668E78500CE3409 /* GTDiffHunk.h in Headers */, + 3011D8771668F29600CE3409 /* GTDiffDelta.h in Headers */, 8849C6A214AD81FF003890AF /* GTRepository+Private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -685,7 +728,6 @@ 88F05A6616011E5400B7AD1D /* Sources */, 88F05A6716011E5400B7AD1D /* Frameworks */, 88F05A6816011E5400B7AD1D /* Resources */, - 88F05A6916011E5400B7AD1D /* ShellScript */, ); buildRules = ( ); @@ -844,19 +886,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 88F05A6916011E5400B7AD1D /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; - }; D0A330F116027F2300A616FA /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -908,6 +937,10 @@ 55C8054F13861FE7004DCB0F /* GTObjectDatabase.m in Sources */, 55C8057C13875579004DCB0F /* NSString+Git.m in Sources */, E9FFC6BF1577CC8300A9E736 /* GTConfiguration.m in Sources */, + 30A3D6571667F11C00C49A39 /* GTDiff.m in Sources */, + 3011D86E1668E48500CE3409 /* GTDiffFile.m in Sources */, + 3011D8741668E78500CE3409 /* GTDiffHunk.m in Sources */, + 3011D87A1668F29600CE3409 /* GTDiffDelta.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -930,6 +963,7 @@ 88F05ABD16011FFD00B7AD1D /* GTTagTest.m in Sources */, 88F05ABE16011FFD00B7AD1D /* GTTreeTest.m in Sources */, 88F05ABF16011FFD00B7AD1D /* GTWalkerTest.m in Sources */, + 30865A91167F503400B1AB6E /* GTDiffSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -957,6 +991,10 @@ 88EB7E4E14AEBA600046FEA4 /* GTConfiguration.m in Sources */, 883CD6AC1600EBC600F57354 /* GTRemote.m in Sources */, 88F05AC61601209A00B7AD1D /* ObjectiveGit.m in Sources */, + 30A3D6561667F11C00C49A39 /* GTDiff.m in Sources */, + 3011D86D1668E48500CE3409 /* GTDiffFile.m in Sources */, + 3011D8731668E78500CE3409 /* GTDiffHunk.m in Sources */, + 3011D8791668F29600CE3409 /* GTDiffDelta.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ObjectiveGitTests/Contants.h b/ObjectiveGitTests/Contants.h index 7ca512d6f..d18864cb5 100644 --- a/ObjectiveGitTests/Contants.h +++ b/ObjectiveGitTests/Contants.h @@ -45,47 +45,61 @@ #import "GTReference.h" #import "GTBranch.h" +static inline BOOL unzipFileFromArchiveAtPathIntoDirectory(NSString *fileName, NSString *zipPath, NSString *destinationPath) { + NSTask *task = [[NSTask alloc] init]; + task.launchPath = @"/usr/bin/unzip"; + task.arguments = @[ @"-qq", @"-d", destinationPath, zipPath, [fileName stringByAppendingString:@"*"] ]; + + [task launch]; + [task waitUntilExit]; + + BOOL success = (task.terminationStatus == 0); + return success; +} + +static inline NSString *repositoryFixturePathForName(NSString *repositoryName, Class cls) { + static NSString *unzippedFixturesPath = nil; + if (unzippedFixturesPath == nil) { + NSString *containerPath = nil; + while (containerPath == nil) { + containerPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:@"com.libgit2.objectivegit"] stringByAppendingPathComponent:NSProcessInfo.processInfo.globallyUniqueString]; + if ([NSFileManager.defaultManager fileExistsAtPath:containerPath]) containerPath = nil; + } + + unzippedFixturesPath = containerPath; + } + + return [unzippedFixturesPath stringByAppendingPathComponent:repositoryName]; +} + +static inline BOOL setupRepositoryFixtureIfNeeded(NSString *repositoryName, Class cls) { + NSString *path = repositoryFixturePathForName(repositoryName, cls); + BOOL isDirectory = NO; + if ([NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory) return YES; + + if (![NSFileManager.defaultManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:NULL]) return NO; + + NSString *zippedFixturesPath = [[[NSBundle bundleForClass:cls] resourcePath] stringByAppendingPathComponent:@"fixtures/fixtures.zip"]; + return unzipFileFromArchiveAtPathIntoDirectory(repositoryName, zippedFixturesPath, path.stringByDeletingLastPathComponent); +} static inline NSString *TEST_REPO_PATH(Class cls) { -#if TARGET_OS_IPHONE - return [NSTemporaryDirectory() stringByAppendingFormat:@"fixtures/testrepo.git"]; -#else - return [[[NSBundle bundleForClass:cls] resourcePath] stringByAppendingPathComponent:@"fixtures/testrepo.git"]; -#endif + if (!setupRepositoryFixtureIfNeeded(@"testrepo.git", cls)) { + NSLog(@"Failed to unzip fixtures."); + } + + return repositoryFixturePathForName(@"testrepo.git", cls); } static inline NSString *TEST_INDEX_PATH(Class cls) { -#if TARGET_OS_IPHONE - return [NSTemporaryDirectory() stringByAppendingFormat:@"fixtures/testrepo.git/index"]; -#else - return [[[NSBundle bundleForClass:cls] resourcePath] stringByAppendingPathComponent:@"fixtures/testrepo.git/index"]; -#endif + return [TEST_REPO_PATH(cls) stringByAppendingPathComponent:@"index"]; } -static inline void CREATE_WRITABLE_FIXTURES(void) { -#if TARGET_OS_IPHONE - NSFileManager *fm = [NSFileManager defaultManager]; - - if([fm fileExistsAtPath:TEST_REPO_PATH()]) - [fm removeItemAtPath:TEST_REPO_PATH() error:nil]; - - NSString *repoInBundle = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"fixtures/testrepo.git"]; - - NSString *fixturesDir = [TEST_REPO_PATH() stringByDeletingPathExtension]; - NSError *error = nil; - [fm createDirectoryAtPath:fixturesDir withIntermediateDirectories:YES attributes:nil error:&error]; - if (error) { - NSLog(@"%@", [error localizedDescription]); - [NSException raise:@"Fixture Setup Error:" format:[error localizedDescription]]; - } - - [fm copyItemAtPath:repoInBundle toPath:TEST_REPO_PATH() error:&error]; - if (error) { - NSLog(@"%@", [error localizedDescription]); - [NSException raise:@"Fixture Setup Error:" format:[error localizedDescription]]; - } - -#endif +static inline NSString *TEST_APP_REPO_PATH(Class cls) { + if (!setupRepositoryFixtureIfNeeded(@"Test_App", cls)) { + NSLog(@"Failed to unzip fixtures."); + } + return repositoryFixturePathForName(@"Test_App", cls); } static inline void rm_loose(Class cls, NSString *sha) { diff --git a/ObjectiveGitTests/GTDiffSpec.m b/ObjectiveGitTests/GTDiffSpec.m new file mode 100644 index 000000000..f6c0e47b7 --- /dev/null +++ b/ObjectiveGitTests/GTDiffSpec.m @@ -0,0 +1,176 @@ +// +// GTDiffSpec.m +// ObjectiveGitFramework +// +// Created by Danny Greg on 17/12/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "Contants.h" + +SpecBegin(GTDiff) + +__block GTRepository *repository = nil; + +describe(@"GTDiff initialisation", ^{ + __block GTCommit *firstCommit = nil; + __block GTCommit *secondCommit = nil; + + beforeEach(^{ + repository = [GTRepository repositoryWithURL:[NSURL fileURLWithPath:TEST_REPO_PATH(self.class)] error:NULL]; + expect(repository).toNot.beNil(); + + firstCommit = (GTCommit *)[repository lookupObjectBySha:@"5b5b025afb0b4c913b4c338a42934a3863bf3644" objectType:GTObjectTypeCommit error:NULL]; + expect(firstCommit).toNot.beNil(); + + secondCommit = (GTCommit *)[repository lookupObjectBySha:@"36060c58702ed4c2a40832c51758d5344201d89a" objectType:GTObjectTypeCommit error:NULL]; + expect(secondCommit).toNot.beNil(); + }); + + it(@"should be able to initialise a diff from 2 trees", ^{ + expect([GTDiff diffOldTree:firstCommit.tree withNewTree:secondCommit.tree options:nil]).toNot.beNil(); + }); + + it(@"should be able to initialise a diff against the index with a tree", ^{ + expect([GTDiff diffIndexFromTree:secondCommit.tree options:nil]).toNot.beNil(); + }); + + it(@"should be able to initialise a diff against a working directory and a tree", ^{ + expect([GTDiff diffWorkingDirectoryFromTree:firstCommit.tree options:nil]).toNot.beNil(); + }); + + it(@"should be able to initialse a diff against an index from a repo's working directory", ^{ + expect([GTDiff diffIndexToWorkingDirectoryInRepository:repository options:nil]).toNot.beNil(); + }); +}); + +describe(@"GTDiff diffing", ^{ + __block GTCommit *firstCommit = nil; + __block GTCommit *secondCommit = nil; + __block GTDiff *diff = nil; + __block void (^setupDiffFromCommitSHAsAndOptions)(NSString *, NSString *, NSDictionary *) = nil; + + beforeEach(^{ + repository = [GTRepository repositoryWithURL:[NSURL fileURLWithPath:TEST_APP_REPO_PATH(self.class)] error:NULL]; + expect(repository).toNot.beNil(); + + setupDiffFromCommitSHAsAndOptions = [^(NSString *firstCommitSHA, NSString *secondCommitSHA, NSDictionary *options) { + firstCommit = (GTCommit *)[repository lookupObjectBySha:firstCommitSHA objectType:GTObjectTypeCommit error:NULL]; + expect(firstCommit).toNot.beNil(); + secondCommit = (GTCommit *)[repository lookupObjectBySha:secondCommitSHA objectType:GTObjectTypeCommit error:NULL]; + expect(secondCommit).toNot.beNil(); + + diff = [GTDiff diffOldTree:firstCommit.tree withNewTree:secondCommit.tree options:options]; + expect(diff).toNot.beNil(); + } copy]; + }); + + it(@"should be able to diff simple file changes", ^{ + setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", nil); + expect(diff.deltaCount).to.equal(1); + expect([diff numberOfDeltasWithType:GTDiffFileDeltaModified]).to.equal(1); + + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { + expect(delta.oldFile.path).to.equal(@"TestAppWindowController.h"); + expect(delta.oldFile.path).to.equal(delta.newFile.path); + expect(delta.hunkCount).to.equal(1); + expect(delta.binary).to.beFalsy(); + expect((NSUInteger)delta.type).to.equal(GTDiffFileDeltaModified); + + [delta enumerateHunksWithBlock:^(GTDiffHunk *hunk, BOOL *stop) { + expect(hunk.header).to.equal(@"@@ -4,7 +4,7 @@"); + expect(hunk.lineCount).to.equal(8); + + NSArray *expectedLines = @[ @"//", + @"// Created by Joe Ricioppo on 9/29/10.", + @"// Copyright 2010 __MyCompanyName__. All rights reserved.", + @"//", + @"// duuuuuuuude", + @"", + @"#import ", + @"#import " ]; + + NSUInteger subtractionLine = 3; + NSUInteger additionLine = 4; + __block NSUInteger lineIndex = 0; + [hunk enumerateLinesInHunkUsingBlock:^(NSString *lineContent, NSUInteger oldLineNumber, NSUInteger newLineNumber, GTDiffHunkLineOrigin lineOrigin, BOOL *stop) { + expect(lineContent).to.equal(expectedLines[lineIndex]); + if (lineIndex == subtractionLine) { + expect((NSUInteger)lineOrigin).to.equal(GTDiffHunkLineOriginDeletion); + } else if (lineIndex == additionLine) { + expect((NSUInteger)lineOrigin).to.equal(GTDiffHunkLineOriginAddition); + } else { + expect((NSUInteger)lineOrigin).to.equal(GTDiffHunkLineOriginContext); + } + + lineIndex ++; + }]; + }]; + + // just in case we have failed an above test, don't add a whole bunch + // more false failures by iterating again. + *stop = YES; + }]; + }); + + it(@"should recognised added files", ^{ + setupDiffFromCommitSHAsAndOptions(@"4d5a6cc7a4d810be71bd47331c947b22580a5997", @"38f1e536cfc2ee41e07d55b38baec00149b2b0d1", nil); + expect(diff.deltaCount).to.equal(1); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { + expect(delta.newFile.path).to.equal(@"REAME"); //loltypo + expect((NSUInteger)delta.type).to.equal(GTDiffFileDeltaAdded); + *stop = YES; + }]; + }); + + it(@"should recognise deleted files", ^{ + setupDiffFromCommitSHAsAndOptions(@"6317779b4731d9c837dcc6972b964bdf4211eeef", @"9f90c6e24629fae3ef51101bb6448342b44098ef", nil); + expect(diff.deltaCount).to.equal(1); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { + expect((NSUInteger)delta.type).to.equal(GTDiffFileDeltaDeleted); + *stop = YES; + }]; + }); + + it(@"should recognise binary files", ^{ + setupDiffFromCommitSHAsAndOptions(@"2ba9cdca982ac35a8db29f51c635251374008229", @"524500582248889ef2243931aa7fc48aa21dd12f", nil); + expect(diff.deltaCount).to.equal(1); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { + expect(delta.binary).to.beTruthy(); + *stop = YES; + }]; + + }); + + it(@"should recognise renames", ^{ + setupDiffFromCommitSHAsAndOptions(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0", @"6b0c1c8b8816416089c534e474f4c692a76ac14f", nil); + [diff findSimilarWithOptions:nil]; + expect(diff.deltaCount).to.equal(1); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { + expect((NSUInteger)delta.type).to.equal(GTDiffFileDeltaRenamed); + expect(delta.oldFile.path).to.equal(@"README"); + expect(delta.newFile.path).to.equal(@"README_renamed"); + *stop = YES; + }]; + }); + + it(@"should correctly pass options to libgit2", ^{ + NSDictionary *options = @{ GTDiffOptionsContextLinesKey: @(5) }; + setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", options); + expect(diff.deltaCount).to.equal(1); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { + expect(delta.hunkCount).to.equal(1); + [delta enumerateHunksWithBlock:^(GTDiffHunk *hunk, BOOL *stop) { + __block NSUInteger contextCount = 0; + [hunk enumerateLinesInHunkUsingBlock:^(NSString *lineContent, NSUInteger oldLineNumber, NSUInteger newLineNumber, GTDiffHunkLineOrigin lineOrigin, BOOL *stop) { + if (lineOrigin == GTDiffHunkLineOriginContext) contextCount ++; + }]; + expect(contextCount).to.equal(10); + *stop = YES; + }]; + *stop = YES; + }]; + }); +}); + +SpecEnd diff --git a/ObjectiveGitTests/fixtures/Fixtures.zip b/ObjectiveGitTests/fixtures/Fixtures.zip new file mode 100644 index 000000000..53a4f9047 Binary files /dev/null and b/ObjectiveGitTests/fixtures/Fixtures.zip differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/HEAD b/ObjectiveGitTests/fixtures/testrepo.git/HEAD deleted file mode 100644 index cb089cd89..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/master diff --git a/ObjectiveGitTests/fixtures/testrepo.git/config b/ObjectiveGitTests/fixtures/testrepo.git/config deleted file mode 100644 index af107929f..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/config +++ /dev/null @@ -1,6 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true - ignorecase = true diff --git a/ObjectiveGitTests/fixtures/testrepo.git/description b/ObjectiveGitTests/fixtures/testrepo.git/description deleted file mode 100644 index 498b267a8..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/ObjectiveGitTests/fixtures/testrepo.git/index b/ObjectiveGitTests/fixtures/testrepo.git/index deleted file mode 100644 index a529451b1..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/index and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/info/exclude b/ObjectiveGitTests/fixtures/testrepo.git/info/exclude deleted file mode 100644 index 2c87b72df..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git-ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/logs/HEAD b/ObjectiveGitTests/fixtures/testrepo.git/logs/HEAD deleted file mode 100644 index cd6d2267a..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/logs/HEAD +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 8496071c1b46c854b31185ea97743be6a8774479 Scott Chacon 1273360386 -0700 commit (initial): testing -8496071c1b46c854b31185ea97743be6a8774479 5b5b025afb0b4c913b4c338a42934a3863bf3644 Scott Chacon 1273610322 -0700 commit: another commit -5b5b025afb0b4c913b4c338a42934a3863bf3644 36060c58702ed4c2a40832c51758d5344201d89a Scott Chacon 1288115062 -0200 push diff --git a/ObjectiveGitTests/fixtures/testrepo.git/logs/refs/heads/master b/ObjectiveGitTests/fixtures/testrepo.git/logs/refs/heads/master deleted file mode 100644 index cd6d2267a..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/logs/refs/heads/master +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 8496071c1b46c854b31185ea97743be6a8774479 Scott Chacon 1273360386 -0700 commit (initial): testing -8496071c1b46c854b31185ea97743be6a8774479 5b5b025afb0b4c913b4c338a42934a3863bf3644 Scott Chacon 1273610322 -0700 commit: another commit -5b5b025afb0b4c913b4c338a42934a3863bf3644 36060c58702ed4c2a40832c51758d5344201d89a Scott Chacon 1288115062 -0200 push diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/04/7142d55581bfb3a703eec247e4fadd5666e789 b/ObjectiveGitTests/fixtures/testrepo.git/objects/04/7142d55581bfb3a703eec247e4fadd5666e789 deleted file mode 100644 index 36946a78a..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/04/7142d55581bfb3a703eec247e4fadd5666e789 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/08/ca5b320ed2111f62ec74158b52242757664516 b/ObjectiveGitTests/fixtures/testrepo.git/objects/08/ca5b320ed2111f62ec74158b52242757664516 deleted file mode 100644 index 3336369c1..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/08/ca5b320ed2111f62ec74158b52242757664516 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0 {+t/%B#[QCUCOf`XŠ b0 ʹ$~ax`g]:m;d}Ŷ3߸>G m?&#_P9Y2$ \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d b/ObjectiveGitTests/fixtures/testrepo.git/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d deleted file mode 100644 index bfe146a5a..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/ObjectiveGitTests/fixtures/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 deleted file mode 100644 index cedb2a22e..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/ObjectiveGitTests/fixtures/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 deleted file mode 100644 index 93a16f146..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/ObjectiveGitTests/fixtures/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd deleted file mode 100644 index ba0bfb30c..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/23/c5964c265e17b12ce89320ec21b5015f033a66 b/ObjectiveGitTests/fixtures/testrepo.git/objects/23/c5964c265e17b12ce89320ec21b5015f033a66 deleted file mode 100644 index 0297a001b..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/23/c5964c265e17b12ce89320ec21b5015f033a66 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E)f_(I@)=D/|G t*H<>6bLDSe?#cdɧN*tWYK,7l ץ1w]mi(_I8?20 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/2c/8611f5dacf3c106cd90f4b71e40f87c64cfdab b/ObjectiveGitTests/fixtures/testrepo.git/objects/2c/8611f5dacf3c106cd90f4b71e40f87c64cfdab deleted file mode 100644 index 355b05218..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/2c/8611f5dacf3c106cd90f4b71e40f87c64cfdab and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 b/ObjectiveGitTests/fixtures/testrepo.git/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 deleted file mode 100644 index 3cd240db5..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/31/b7680598f36b4e8d0948ddf13ee17d009e9a80 b/ObjectiveGitTests/fixtures/testrepo.git/objects/31/b7680598f36b4e8d0948ddf13ee17d009e9a80 deleted file mode 100644 index c5dbb4db3..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/31/b7680598f36b4e8d0948ddf13ee17d009e9a80 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/32/4d981d11440240df53e825b6eba0afddcb39fa b/ObjectiveGitTests/fixtures/testrepo.git/objects/32/4d981d11440240df53e825b6eba0afddcb39fa deleted file mode 100644 index a3b4c322d..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/32/4d981d11440240df53e825b6eba0afddcb39fa +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]Jb& 8)^<>5B uٲD~&Gɇ≓-& Uay-Uף t?q20 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/36/060c58702ed4c2a40832c51758d5344201d89a b/ObjectiveGitTests/fixtures/testrepo.git/objects/36/060c58702ed4c2a40832c51758d5344201d89a deleted file mode 100644 index 0c6246061..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/36/060c58702ed4c2a40832c51758d5344201d89a +++ /dev/null @@ -1,2 +0,0 @@ -xQ -0)reݴ $ۭ-F-00𸖲?iL#HSS#q2D據jC|HSL8$)a#2i׹6js?JZftΞUiͶqiZ"_/H6 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/36/a1a2e1ebed3bb4de53d2d0d3982dd724caa7de b/ObjectiveGitTests/fixtures/testrepo.git/objects/36/a1a2e1ebed3bb4de53d2d0d3982dd724caa7de deleted file mode 100644 index 6725b6f35..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/36/a1a2e1ebed3bb4de53d2d0d3982dd724caa7de +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$if 8)^{j5D'Dq$8᜽xNgGT"Kֶ*܍ߢϥz 7}@ҍ!nT=/72 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/ObjectiveGitTests/fixtures/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 deleted file mode 100644 index 7ca4ceed5..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/46/bf0f2b6d602a0d0a8971216dfb81692bde5286 b/ObjectiveGitTests/fixtures/testrepo.git/objects/46/bf0f2b6d602a0d0a8971216dfb81692bde5286 deleted file mode 100644 index 07e162292..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/46/bf0f2b6d602a0d0a8971216dfb81692bde5286 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/49/4a45810d66a43ff2ed747db6f13d7779436de5 b/ObjectiveGitTests/fixtures/testrepo.git/objects/49/4a45810d66a43ff2ed747db6f13d7779436de5 deleted file mode 100644 index 420c28eaa..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/49/4a45810d66a43ff2ed747db6f13d7779436de5 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E)f/h2@)=D/: H{HQo3 /ɲHn!Fi$GM:u;CZngS'C0Lli(_/̗ |i?)2> \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/ObjectiveGitTests/fixtures/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 deleted file mode 100644 index 8953b6cef..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 +++ /dev/null @@ -1,2 +0,0 @@ -xQ -0D)6ͦ "xO-FbEo0 Ǥ,ske[Pn8R,EpD?g}^3 <GhYK8ЖDA);gݧjp4-r;sGA4ۺ=(in7IKFE \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/4b/d7c21254336b430f3dcaca0bf27124edbfd979 b/ObjectiveGitTests/fixtures/testrepo.git/objects/4b/d7c21254336b430f3dcaca0bf27124edbfd979 deleted file mode 100644 index 32ca2d676..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/4b/d7c21254336b430f3dcaca0bf27124edbfd979 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$1cH 8)^9<>5B uٲD~&|Dɇ≓-& U`PK(7jwO,Wg?? 22 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/58/2ab21929a82c7c1c10521f371fa011e41a6730 b/ObjectiveGitTests/fixtures/testrepo.git/objects/58/2ab21929a82c7c1c10521f371fa011e41a6730 deleted file mode 100644 index fb67e6122..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/58/2ab21929a82c7c1c10521f371fa011e41a6730 +++ /dev/null @@ -1,3 +0,0 @@ -xA -0E]$1m@Cxwo߽&R\'ݘ aBa -slgX<8bkU7s&r4FglLG|K2" \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/58/3cf9deb2f587481b3e88f700066764351c5a98 b/ObjectiveGitTests/fixtures/testrepo.git/objects/58/3cf9deb2f587481b3e88f700066764351c5a98 deleted file mode 100644 index dcd7c5019..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/58/3cf9deb2f587481b3e88f700066764351c5a98 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$i&" $ip S9<>5B 3u3.ٲD~!Gɇ≓-&^U`Rz<תYnnHc"\m(_A8?2, \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/ObjectiveGitTests/fixtures/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 deleted file mode 100644 index c1f22c54f..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 +++ /dev/null @@ -1,2 +0,0 @@ -x 1ENi@k2 "X$YW0YcÅszMD08!s Xgd::@X0Pw"F/RUzmZZV}|/o5I!1z:vUim}/> -F- \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 b/ObjectiveGitTests/fixtures/testrepo.git/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 deleted file mode 100644 index 1fd79b477..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/63/b661110a0c7e53c5db0389de17f6aee08b21c3 b/ObjectiveGitTests/fixtures/testrepo.git/objects/63/b661110a0c7e53c5db0389de17f6aee08b21c3 deleted file mode 100644 index 30dddcbd5..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/63/b661110a0c7e53c5db0389de17f6aee08b21c3 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/6d/e3956f865eb06b3cf5707325df92f3526d9927 b/ObjectiveGitTests/fixtures/testrepo.git/objects/6d/e3956f865eb06b3cf5707325df92f3526d9927 deleted file mode 100644 index a900ed7e9..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/6d/e3956f865eb06b3cf5707325df92f3526d9927 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E)f_(hRA2> \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/ObjectiveGitTests/fixtures/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a deleted file mode 100644 index 2ef4faa0f..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/ObjectiveGitTests/fixtures/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d deleted file mode 100644 index 2f9b6b6e3..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/ObjectiveGitTests/fixtures/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 deleted file mode 100644 index 5df58dda5..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/92/867bc5db73e8182a7bf7387dabda57efa9f0c8 b/ObjectiveGitTests/fixtures/testrepo.git/objects/92/867bc5db73e8182a7bf7387dabda57efa9f0c8 deleted file mode 100644 index a4502a0f0..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/92/867bc5db73e8182a7bf7387dabda57efa9f0c8 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$1Ic@Cxw o߽U)O1ˆb"OC >VζrnjBwś97]z*r|6 n{2* \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/ObjectiveGitTests/fixtures/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a deleted file mode 100644 index a79612435..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a +++ /dev/null @@ -1,3 +0,0 @@ -x[ -0E*fդ "W0-Ft݁pS[Yx^ -Db CLhut}8X*4ZsYUA X3RM) s6輢Mរ&Jm;}<\@ޏpĀv?jۺL?H \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/a3/5622ee2ca7c7cf68abdcad335588fbb8bca9ec b/ObjectiveGitTests/fixtures/testrepo.git/objects/a3/5622ee2ca7c7cf68abdcad335588fbb8bca9ec deleted file mode 100644 index 4b3010a61..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/a3/5622ee2ca7c7cf68abdcad335588fbb8bca9ec +++ /dev/null @@ -1,2 +0,0 @@ -xA -0 {+t/B#[QCUCOf`XŠ#&7 s03>sX. Ea0K7#RosͶ {Z22 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/ObjectiveGitTests/fixtures/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f deleted file mode 100644 index f8588696b..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f +++ /dev/null @@ -1,2 +0,0 @@ -x;j1Dmdǎ|M3`V{ >QvL0I?!4Z=!צ8F!rsQy9]$D&l6A>jFWҵ IKNiZ%S - U~̽>' w [ DGڡQ-M>dO}\8g_ШoYr \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/ObjectiveGitTests/fixtures/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd deleted file mode 100644 index d0d7e736e..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/ObjectiveGitTests/fixtures/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 deleted file mode 100644 index 18a7f61c2..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/b5/fd0db2023fefaad6d654498b19da0452d6ed6f b/ObjectiveGitTests/fixtures/testrepo.git/objects/b5/fd0db2023fefaad6d654498b19da0452d6ed6f deleted file mode 100644 index e000e2e38..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/b5/fd0db2023fefaad6d654498b19da0452d6ed6f +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$q&@ 8)^{j51l;Dsbn Kֶ*oRm=ʅ> \=\} *w2 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/b6/8f98fb09ce029c462ab6cb4b0a6bd388991008 b/ObjectiveGitTests/fixtures/testrepo.git/objects/b6/8f98fb09ce029c462ab6cb4b0a6bd388991008 deleted file mode 100644 index 7b44de1c6..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/b6/8f98fb09ce029c462ab6cb4b0a6bd388991008 +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]Jb& 8)^<>5B uٲD~&Gɇ≓-& Uay-Uף t?_2* \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/b8/f63858750d58a047fb01de56600a848d148c5c b/ObjectiveGitTests/fixtures/testrepo.git/objects/b8/f63858750d58a047fb01de56600a848d148c5c deleted file mode 100644 index 1f9a17806..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/b8/f63858750d58a047fb01de56600a848d148c5c +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]d$ 8)^{j51lw0g4s x`X[p7~>jQ.q$3nT=/2 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/ObjectiveGitTests/fixtures/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 deleted file mode 100644 index 0817229bc..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 +++ /dev/null @@ -1,3 +0,0 @@ -xKj1D)zUB-0uV9<#+W`[F3nT=/2< \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/dc/e40996517d8183ba11d2d0de35c5513db7cd6b b/ObjectiveGitTests/fixtures/testrepo.git/objects/dc/e40996517d8183ba11d2d0de35c5513db7cd6b deleted file mode 100644 index 69ada751c..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/dc/e40996517d8183ba11d2d0de35c5513db7cd6b +++ /dev/null @@ -1,2 +0,0 @@ -x[ -0E*_$ME\Hnp SܾY8\TM ;3OhO~Ζ- GBqdɧu&4*,e,7TyPwOCi=]mi(_I8?)2D \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/e3/02ade1ce3d2729bbe053dd8f923b294336437e b/ObjectiveGitTests/fixtures/testrepo.git/objects/e3/02ade1ce3d2729bbe053dd8f923b294336437e deleted file mode 100644 index c18cfcd1b..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/e3/02ade1ce3d2729bbe053dd8f923b294336437e +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$if 8)^{j5D'Dq$8᜽xNgGT"Kֶ*܍ߢϥz 7}@ҍ'\} *2" \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/e4/065ef1811aa1279362c275aab504b846067a9f b/ObjectiveGitTests/fixtures/testrepo.git/objects/e4/065ef1811aa1279362c275aab504b846067a9f deleted file mode 100644 index 957818e24..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/e4/065ef1811aa1279362c275aab504b846067a9f +++ /dev/null @@ -1,2 +0,0 @@ -xA -0E]$1Ic@Cxw o߽U)O1ˆb"OC >VζrnjBwś97]z*r|!n26 \ No newline at end of file diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/ObjectiveGitTests/fixtures/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 deleted file mode 100644 index 711223894..000000000 Binary files a/ObjectiveGitTests/fixtures/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 and /dev/null differ diff --git a/ObjectiveGitTests/fixtures/testrepo.git/objects/e7/d45d5c5b1d3897cba0fb342df207b833b83d7c b/ObjectiveGitTests/fixtures/testrepo.git/objects/e7/d45d5c5b1d3897cba0fb342df207b833b83d7c deleted file mode 100644 index d5b86771c..000000000 --- a/ObjectiveGitTests/fixtures/testrepo.git/objects/e7/d45d5c5b1d3897cba0fb342df207b833b83d7c +++ /dev/null @@ -1,3 +0,0 @@ -xA -0E]$1iACxwo߽&R\'ݘ aBa -sl1|,