├── .gitignore ├── .travis.yml ├── Framework ├── Git.h └── Info.plist ├── Git.podspec ├── Git.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── Git.xcscheme │ └── libgit2.xcscheme ├── ImplementationProgress.md ├── README.md ├── Sources ├── Branch.swift ├── Commit.swift ├── Credential.swift ├── Diff.swift ├── Git.swift ├── Helpers │ ├── Data+Git.swift │ ├── Date+Git.swift │ └── Errors.swift ├── Index.swift ├── OID.swift ├── Private │ ├── RevWalk.swift │ └── StrArray.swift ├── Reference.swift ├── Remote.swift ├── Repository.swift ├── Signature.swift ├── Status.swift └── Tree.swift ├── Tests ├── GitTests.swift └── Info.plist └── build.plist /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .ibuild 3 | *.xcworkspacedata 4 | *.xcuserdatad 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9.2 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | - PROJECT=Git.xcodeproj 8 | - IOS_FRAMEWORK_SCHEME="Git" 9 | - IOS_SIM_SDK=iphonesimulator11.2 10 | - IOS_DEV_SDK=iphoneos11.2 11 | matrix: 12 | - SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SIM_SDK" 13 | - SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_DEV_SDK" 14 | script: 15 | - set -o pipefail 16 | - xcodebuild -version 17 | - xcodebuild -showsdks 18 | - xcodebuild -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -configuration Release ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES build; 19 | 20 | branches: 21 | only: 22 | - master 23 | -------------------------------------------------------------------------------- /Framework/Git.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | //! Project version number for Git. 5 | FOUNDATION_EXPORT double GitVersionNumber; 6 | 7 | //! Project version string for Git. 8 | FOUNDATION_EXPORT const unsigned char GitVersionString[]; 9 | -------------------------------------------------------------------------------- /Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Git.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint InputAssistant.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "Git" 19 | s.version = "1.0.0" 20 | s.summary = "Wrapper around libgit2, written in Swift" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | Git 1.0.0 - Wrapper around libgit2, written in Swift 29 | DESC 30 | 31 | s.homepage = "https://github.com/IMcD23/Git" 32 | s.screenshots = "" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | s.license = { :type => "MIT", :file => "LICENSE" } 43 | 44 | 45 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 46 | # 47 | # Specify the authors of the library, with email addresses. Email addresses 48 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 49 | # accepts just a name if you'd rather not provide an email address. 50 | # 51 | # Specify a social_media_url where others can refer to, for example a twitter 52 | # profile URL. 53 | # 54 | 55 | s.author = { "Ian McDowell" => "me@ianmcdowell.net" } 56 | # Or just: s.author = "Ian McDowell" 57 | # s.authors = { "Ian McDowell" => "me@ianmcdowell.net" } 58 | s.social_media_url = "http://twitter.com/ian_mcdowell" 59 | 60 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 61 | # 62 | # If this Pod runs only on iOS or OS X, then specify the platform and 63 | # the deployment target. You can optionally include the target after the platform. 64 | # 65 | 66 | s.platform = :ios, "11.0" 67 | # s.platform = :ios, "5.0" 68 | 69 | # When using multiple platforms 70 | # s.ios.deployment_target = "5.0" 71 | # s.osx.deployment_target = "10.7" 72 | # s.watchos.deployment_target = "2.0" 73 | # s.tvos.deployment_target = "9.0" 74 | 75 | 76 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 77 | # 78 | # Specify the location from where the source should be retrieved. 79 | # Supports git, hg, bzr, svn and HTTP. 80 | # 81 | 82 | s.source = { :git => "https://github.com/IMcD23/Git.git", :tag => "#{s.version}" } 83 | 84 | 85 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 86 | # 87 | # CocoaPods is smart about how it includes source code. For source files 88 | # giving a folder will include any swift, h, m, mm, c & cpp files. 89 | # For header files it will include any header in the folder. 90 | # Not including the public_header_files will make all headers public. 91 | # 92 | 93 | s.source_files = "Sources/*.swift" 94 | 95 | 96 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 97 | # 98 | # A list of resources included with the Pod. These are copied into the 99 | # target bundle with a build phase script. Anything else will be cleaned. 100 | # You can preserve files from being cleaned, please don't preserve 101 | # non-essential files like tests, examples and documentation. 102 | # 103 | 104 | # s.resource = "icon.png" 105 | # s.resources = "Resources/*.png" 106 | 107 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 108 | 109 | 110 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 111 | # 112 | # Link your library with frameworks, or libraries. Libraries do not include 113 | # the lib prefix of their name. 114 | # 115 | 116 | # s.framework = "SomeFramework" 117 | # s.frameworks = "SomeFramework", "AnotherFramework" 118 | 119 | # s.library = "iconv" 120 | # s.libraries = "iconv", "xml2" 121 | 122 | 123 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 124 | # 125 | # If your library depends on compiler flags you can set them in the xcconfig hash 126 | # where they will only apply to your library. If you depend on other Podspecs 127 | # you can include multiple dependencies to ensure it works. 128 | 129 | s.requires_arc = true 130 | s.swift_version = "4.0" 131 | 132 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 133 | # s.dependency "JSONKit", "~> 1.4" 134 | 135 | end 136 | -------------------------------------------------------------------------------- /Git.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3CB477AF1FF7A6FC00C6B265 /* Git.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB477A51FF7A6FC00C6B265 /* Git.framework */; }; 11 | 3CB477B41FF7A6FC00C6B265 /* GitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477B31FF7A6FC00C6B265 /* GitTests.swift */; }; 12 | 3CB477B61FF7A6FC00C6B265 /* Git.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CB477A81FF7A6FC00C6B265 /* Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 3CB477D51FF7A7F900C6B265 /* Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C21FF7A7F900C6B265 /* Remote.swift */; }; 14 | 3CB477D61FF7A7F900C6B265 /* Reference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C31FF7A7F900C6B265 /* Reference.swift */; }; 15 | 3CB477D71FF7A7F900C6B265 /* Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C41FF7A7F900C6B265 /* Index.swift */; }; 16 | 3CB477D81FF7A7F900C6B265 /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C51FF7A7F900C6B265 /* Diff.swift */; }; 17 | 3CB477D91FF7A7F900C6B265 /* Branch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C61FF7A7F900C6B265 /* Branch.swift */; }; 18 | 3CB477DA1FF7A7F900C6B265 /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C71FF7A7F900C6B265 /* Commit.swift */; }; 19 | 3CB477DB1FF7A7F900C6B265 /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C81FF7A7F900C6B265 /* OID.swift */; }; 20 | 3CB477DC1FF7A7F900C6B265 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477C91FF7A7F900C6B265 /* Signature.swift */; }; 21 | 3CB477DD1FF7A7F900C6B265 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477CA1FF7A7F900C6B265 /* Repository.swift */; }; 22 | 3CB477DE1FF7A7F900C6B265 /* RevWalk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477CC1FF7A7F900C6B265 /* RevWalk.swift */; }; 23 | 3CB477DF1FF7A7F900C6B265 /* StrArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477CD1FF7A7F900C6B265 /* StrArray.swift */; }; 24 | 3CB477E01FF7A7F900C6B265 /* Tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477CE1FF7A7F900C6B265 /* Tree.swift */; }; 25 | 3CB477E11FF7A7F900C6B265 /* Credential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477CF1FF7A7F900C6B265 /* Credential.swift */; }; 26 | 3CB477E21FF7A7F900C6B265 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477D01FF7A7F900C6B265 /* Status.swift */; }; 27 | 3CB477E31FF7A7F900C6B265 /* Data+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477D21FF7A7F900C6B265 /* Data+Git.swift */; }; 28 | 3CB477E41FF7A7F900C6B265 /* Date+Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477D31FF7A7F900C6B265 /* Date+Git.swift */; }; 29 | 3CB477E51FF7A7F900C6B265 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB477D41FF7A7F900C6B265 /* Errors.swift */; }; 30 | 3CB477EE1FF7A97C00C6B265 /* libgit2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB477ED1FF7A97C00C6B265 /* libgit2.a */; }; 31 | 3CB477F31FF7A98200C6B265 /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB477EF1FF7A98200C6B265 /* libcrypto.a */; }; 32 | 3CB477F41FF7A98200C6B265 /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB477F01FF7A98200C6B265 /* libssl.a */; }; 33 | 3CB477F51FF7A98200C6B265 /* libssh2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB477F11FF7A98200C6B265 /* libssh2.a */; }; 34 | 3CB477F61FF7A98200C6B265 /* libcurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB477F21FF7A98200C6B265 /* libcurl.a */; }; 35 | 3CE064A3200DD95C00680549 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CE064A2200DD95C00680549 /* libz.tbd */; }; 36 | 3CE064A5200DDA1000680549 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CE064A4200DDA1000680549 /* libiconv.tbd */; }; 37 | B0F362A820003427007466AE /* Git.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F362A720003427007466AE /* Git.swift */; }; 38 | /* End PBXBuildFile section */ 39 | 40 | /* Begin PBXContainerItemProxy section */ 41 | 3CB477B01FF7A6FC00C6B265 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 3CB4779C1FF7A6FC00C6B265 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 3CB477A41FF7A6FC00C6B265; 46 | remoteInfo = Git; 47 | }; 48 | 3CB477EA1FF7A8B200C6B265 /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = 3CB4779C1FF7A6FC00C6B265 /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = 3CB477E61FF7A8A500C6B265; 53 | remoteInfo = libgit2; 54 | }; 55 | /* End PBXContainerItemProxy section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 3C7CD4451FFA47700049E5FE /* ibuild_search_paths.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = ibuild_search_paths.xcconfig; path = /usr/local/opt/ibuild/libexec/ibuild_search_paths.xcconfig; sourceTree = ""; }; 59 | 3CB477A51FF7A6FC00C6B265 /* Git.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Git.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 3CB477A81FF7A6FC00C6B265 /* Git.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Git.h; sourceTree = ""; }; 61 | 3CB477A91FF7A6FC00C6B265 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | 3CB477AE1FF7A6FC00C6B265 /* GitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 3CB477B31FF7A6FC00C6B265 /* GitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitTests.swift; sourceTree = ""; }; 64 | 3CB477B51FF7A6FC00C6B265 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 3CB477C21FF7A7F900C6B265 /* Remote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Remote.swift; sourceTree = ""; }; 66 | 3CB477C31FF7A7F900C6B265 /* Reference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reference.swift; sourceTree = ""; }; 67 | 3CB477C41FF7A7F900C6B265 /* Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Index.swift; sourceTree = ""; }; 68 | 3CB477C51FF7A7F900C6B265 /* Diff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diff.swift; sourceTree = ""; }; 69 | 3CB477C61FF7A7F900C6B265 /* Branch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Branch.swift; sourceTree = ""; }; 70 | 3CB477C71FF7A7F900C6B265 /* Commit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Commit.swift; sourceTree = ""; }; 71 | 3CB477C81FF7A7F900C6B265 /* OID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OID.swift; sourceTree = ""; }; 72 | 3CB477C91FF7A7F900C6B265 /* Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signature.swift; sourceTree = ""; }; 73 | 3CB477CA1FF7A7F900C6B265 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; 74 | 3CB477CC1FF7A7F900C6B265 /* RevWalk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RevWalk.swift; sourceTree = ""; }; 75 | 3CB477CD1FF7A7F900C6B265 /* StrArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StrArray.swift; sourceTree = ""; }; 76 | 3CB477CE1FF7A7F900C6B265 /* Tree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tree.swift; sourceTree = ""; }; 77 | 3CB477CF1FF7A7F900C6B265 /* Credential.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Credential.swift; sourceTree = ""; }; 78 | 3CB477D01FF7A7F900C6B265 /* Status.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; 79 | 3CB477D21FF7A7F900C6B265 /* Data+Git.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Git.swift"; sourceTree = ""; }; 80 | 3CB477D31FF7A7F900C6B265 /* Date+Git.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Git.swift"; sourceTree = ""; }; 81 | 3CB477D41FF7A7F900C6B265 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 82 | 3CB477ED1FF7A97C00C6B265 /* libgit2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgit2.a; path = "../Debug-iphonesimulator/lib/libgit2.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | 3CB477EF1FF7A98200C6B265 /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcrypto.a; path = "../Debug-iphonesimulator/lib/libcrypto.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 84 | 3CB477F01FF7A98200C6B265 /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssl.a; path = "../Debug-iphonesimulator/lib/libssl.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | 3CB477F11FF7A98200C6B265 /* libssh2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssh2.a; path = "../Debug-iphonesimulator/lib/libssh2.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | 3CB477F21FF7A98200C6B265 /* libcurl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcurl.a; path = "../Debug-iphonesimulator/lib/libcurl.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | 3CE064A2200DD95C00680549 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 88 | 3CE064A4200DDA1000680549 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; 89 | B0F362A720003427007466AE /* Git.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Git.swift; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | 3CB477A11FF7A6FC00C6B265 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | 3CE064A5200DDA1000680549 /* libiconv.tbd in Frameworks */, 98 | 3CE064A3200DD95C00680549 /* libz.tbd in Frameworks */, 99 | 3CB477EE1FF7A97C00C6B265 /* libgit2.a in Frameworks */, 100 | 3CB477F31FF7A98200C6B265 /* libcrypto.a in Frameworks */, 101 | 3CB477F41FF7A98200C6B265 /* libssl.a in Frameworks */, 102 | 3CB477F51FF7A98200C6B265 /* libssh2.a in Frameworks */, 103 | 3CB477F61FF7A98200C6B265 /* libcurl.a in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 3CB477AB1FF7A6FC00C6B265 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | 3CB477AF1FF7A6FC00C6B265 /* Git.framework in Frameworks */, 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | /* End PBXFrameworksBuildPhase section */ 116 | 117 | /* Begin PBXGroup section */ 118 | 3CB4779B1FF7A6FC00C6B265 = { 119 | isa = PBXGroup; 120 | children = ( 121 | 3C7CD4451FFA47700049E5FE /* ibuild_search_paths.xcconfig */, 122 | 3CB477C11FF7A7F900C6B265 /* Sources */, 123 | 3CB477A71FF7A6FC00C6B265 /* Framework */, 124 | 3CB477B21FF7A6FC00C6B265 /* Tests */, 125 | 3CB477A61FF7A6FC00C6B265 /* Products */, 126 | 3CB477EC1FF7A97B00C6B265 /* Frameworks */, 127 | ); 128 | sourceTree = ""; 129 | }; 130 | 3CB477A61FF7A6FC00C6B265 /* Products */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 3CB477A51FF7A6FC00C6B265 /* Git.framework */, 134 | 3CB477AE1FF7A6FC00C6B265 /* GitTests.xctest */, 135 | ); 136 | name = Products; 137 | sourceTree = ""; 138 | }; 139 | 3CB477A71FF7A6FC00C6B265 /* Framework */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 3CB477A81FF7A6FC00C6B265 /* Git.h */, 143 | 3CB477A91FF7A6FC00C6B265 /* Info.plist */, 144 | ); 145 | path = Framework; 146 | sourceTree = ""; 147 | }; 148 | 3CB477B21FF7A6FC00C6B265 /* Tests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 3CB477B31FF7A6FC00C6B265 /* GitTests.swift */, 152 | 3CB477B51FF7A6FC00C6B265 /* Info.plist */, 153 | ); 154 | path = Tests; 155 | sourceTree = ""; 156 | }; 157 | 3CB477C11FF7A7F900C6B265 /* Sources */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | B0F362A720003427007466AE /* Git.swift */, 161 | 3CB477C21FF7A7F900C6B265 /* Remote.swift */, 162 | 3CB477C31FF7A7F900C6B265 /* Reference.swift */, 163 | 3CB477C41FF7A7F900C6B265 /* Index.swift */, 164 | 3CB477C51FF7A7F900C6B265 /* Diff.swift */, 165 | 3CB477C61FF7A7F900C6B265 /* Branch.swift */, 166 | 3CB477C71FF7A7F900C6B265 /* Commit.swift */, 167 | 3CB477C81FF7A7F900C6B265 /* OID.swift */, 168 | 3CB477C91FF7A7F900C6B265 /* Signature.swift */, 169 | 3CB477CA1FF7A7F900C6B265 /* Repository.swift */, 170 | 3CB477CB1FF7A7F900C6B265 /* Private */, 171 | 3CB477CE1FF7A7F900C6B265 /* Tree.swift */, 172 | 3CB477CF1FF7A7F900C6B265 /* Credential.swift */, 173 | 3CB477D01FF7A7F900C6B265 /* Status.swift */, 174 | 3CB477D11FF7A7F900C6B265 /* Helpers */, 175 | ); 176 | path = Sources; 177 | sourceTree = ""; 178 | }; 179 | 3CB477CB1FF7A7F900C6B265 /* Private */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 3CB477CC1FF7A7F900C6B265 /* RevWalk.swift */, 183 | 3CB477CD1FF7A7F900C6B265 /* StrArray.swift */, 184 | ); 185 | path = Private; 186 | sourceTree = ""; 187 | }; 188 | 3CB477D11FF7A7F900C6B265 /* Helpers */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 3CB477D21FF7A7F900C6B265 /* Data+Git.swift */, 192 | 3CB477D31FF7A7F900C6B265 /* Date+Git.swift */, 193 | 3CB477D41FF7A7F900C6B265 /* Errors.swift */, 194 | ); 195 | path = Helpers; 196 | sourceTree = ""; 197 | }; 198 | 3CB477EC1FF7A97B00C6B265 /* Frameworks */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 3CE064A4200DDA1000680549 /* libiconv.tbd */, 202 | 3CE064A2200DD95C00680549 /* libz.tbd */, 203 | 3CB477EF1FF7A98200C6B265 /* libcrypto.a */, 204 | 3CB477F21FF7A98200C6B265 /* libcurl.a */, 205 | 3CB477F11FF7A98200C6B265 /* libssh2.a */, 206 | 3CB477F01FF7A98200C6B265 /* libssl.a */, 207 | 3CB477ED1FF7A97C00C6B265 /* libgit2.a */, 208 | ); 209 | name = Frameworks; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXGroup section */ 213 | 214 | /* Begin PBXHeadersBuildPhase section */ 215 | 3CB477A21FF7A6FC00C6B265 /* Headers */ = { 216 | isa = PBXHeadersBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | 3CB477B61FF7A6FC00C6B265 /* Git.h in Headers */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXHeadersBuildPhase section */ 224 | 225 | /* Begin PBXLegacyTarget section */ 226 | 3CB477E61FF7A8A500C6B265 /* libgit2 */ = { 227 | isa = PBXLegacyTarget; 228 | buildArgumentsString = "$(ACTION)"; 229 | buildConfigurationList = 3CB477E71FF7A8A500C6B265 /* Build configuration list for PBXLegacyTarget "libgit2" */; 230 | buildPhases = ( 231 | ); 232 | buildToolPath = /usr/local/bin/ibuild; 233 | buildWorkingDirectory = "$(SRCROOT)"; 234 | dependencies = ( 235 | ); 236 | name = libgit2; 237 | passBuildSettingsInEnvironment = 1; 238 | productName = libgit2; 239 | }; 240 | /* End PBXLegacyTarget section */ 241 | 242 | /* Begin PBXNativeTarget section */ 243 | 3CB477A41FF7A6FC00C6B265 /* Git */ = { 244 | isa = PBXNativeTarget; 245 | buildConfigurationList = 3CB477B91FF7A6FC00C6B265 /* Build configuration list for PBXNativeTarget "Git" */; 246 | buildPhases = ( 247 | 3CB477A01FF7A6FC00C6B265 /* Sources */, 248 | 3CB477A11FF7A6FC00C6B265 /* Frameworks */, 249 | 3CB477A21FF7A6FC00C6B265 /* Headers */, 250 | 3CB477A31FF7A6FC00C6B265 /* Resources */, 251 | ); 252 | buildRules = ( 253 | ); 254 | dependencies = ( 255 | 3CB477EB1FF7A8B200C6B265 /* PBXTargetDependency */, 256 | ); 257 | name = Git; 258 | productName = Git; 259 | productReference = 3CB477A51FF7A6FC00C6B265 /* Git.framework */; 260 | productType = "com.apple.product-type.framework"; 261 | }; 262 | 3CB477AD1FF7A6FC00C6B265 /* GitTests */ = { 263 | isa = PBXNativeTarget; 264 | buildConfigurationList = 3CB477BC1FF7A6FC00C6B265 /* Build configuration list for PBXNativeTarget "GitTests" */; 265 | buildPhases = ( 266 | 3CB477AA1FF7A6FC00C6B265 /* Sources */, 267 | 3CB477AB1FF7A6FC00C6B265 /* Frameworks */, 268 | 3CB477AC1FF7A6FC00C6B265 /* Resources */, 269 | ); 270 | buildRules = ( 271 | ); 272 | dependencies = ( 273 | 3CB477B11FF7A6FC00C6B265 /* PBXTargetDependency */, 274 | ); 275 | name = GitTests; 276 | productName = GitTests; 277 | productReference = 3CB477AE1FF7A6FC00C6B265 /* GitTests.xctest */; 278 | productType = "com.apple.product-type.bundle.unit-test"; 279 | }; 280 | /* End PBXNativeTarget section */ 281 | 282 | /* Begin PBXProject section */ 283 | 3CB4779C1FF7A6FC00C6B265 /* Project object */ = { 284 | isa = PBXProject; 285 | attributes = { 286 | LastSwiftUpdateCheck = 0920; 287 | LastUpgradeCheck = 0920; 288 | ORGANIZATIONNAME = "Ian McDowell"; 289 | TargetAttributes = { 290 | 3CB477A41FF7A6FC00C6B265 = { 291 | CreatedOnToolsVersion = 9.2; 292 | ProvisioningStyle = Automatic; 293 | }; 294 | 3CB477AD1FF7A6FC00C6B265 = { 295 | CreatedOnToolsVersion = 9.2; 296 | ProvisioningStyle = Automatic; 297 | }; 298 | 3CB477E61FF7A8A500C6B265 = { 299 | CreatedOnToolsVersion = 9.2; 300 | ProvisioningStyle = Automatic; 301 | }; 302 | }; 303 | }; 304 | buildConfigurationList = 3CB4779F1FF7A6FC00C6B265 /* Build configuration list for PBXProject "Git" */; 305 | compatibilityVersion = "Xcode 8.0"; 306 | developmentRegion = en; 307 | hasScannedForEncodings = 0; 308 | knownRegions = ( 309 | en, 310 | ); 311 | mainGroup = 3CB4779B1FF7A6FC00C6B265; 312 | productRefGroup = 3CB477A61FF7A6FC00C6B265 /* Products */; 313 | projectDirPath = ""; 314 | projectRoot = ""; 315 | targets = ( 316 | 3CB477A41FF7A6FC00C6B265 /* Git */, 317 | 3CB477AD1FF7A6FC00C6B265 /* GitTests */, 318 | 3CB477E61FF7A8A500C6B265 /* libgit2 */, 319 | ); 320 | }; 321 | /* End PBXProject section */ 322 | 323 | /* Begin PBXResourcesBuildPhase section */ 324 | 3CB477A31FF7A6FC00C6B265 /* Resources */ = { 325 | isa = PBXResourcesBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | }; 331 | 3CB477AC1FF7A6FC00C6B265 /* Resources */ = { 332 | isa = PBXResourcesBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | /* End PBXResourcesBuildPhase section */ 339 | 340 | /* Begin PBXSourcesBuildPhase section */ 341 | 3CB477A01FF7A6FC00C6B265 /* Sources */ = { 342 | isa = PBXSourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | 3CB477DA1FF7A7F900C6B265 /* Commit.swift in Sources */, 346 | 3CB477D51FF7A7F900C6B265 /* Remote.swift in Sources */, 347 | 3CB477E11FF7A7F900C6B265 /* Credential.swift in Sources */, 348 | 3CB477DE1FF7A7F900C6B265 /* RevWalk.swift in Sources */, 349 | 3CB477D61FF7A7F900C6B265 /* Reference.swift in Sources */, 350 | 3CB477DB1FF7A7F900C6B265 /* OID.swift in Sources */, 351 | 3CB477E41FF7A7F900C6B265 /* Date+Git.swift in Sources */, 352 | B0F362A820003427007466AE /* Git.swift in Sources */, 353 | 3CB477DC1FF7A7F900C6B265 /* Signature.swift in Sources */, 354 | 3CB477D91FF7A7F900C6B265 /* Branch.swift in Sources */, 355 | 3CB477E31FF7A7F900C6B265 /* Data+Git.swift in Sources */, 356 | 3CB477D81FF7A7F900C6B265 /* Diff.swift in Sources */, 357 | 3CB477E21FF7A7F900C6B265 /* Status.swift in Sources */, 358 | 3CB477D71FF7A7F900C6B265 /* Index.swift in Sources */, 359 | 3CB477E51FF7A7F900C6B265 /* Errors.swift in Sources */, 360 | 3CB477E01FF7A7F900C6B265 /* Tree.swift in Sources */, 361 | 3CB477DD1FF7A7F900C6B265 /* Repository.swift in Sources */, 362 | 3CB477DF1FF7A7F900C6B265 /* StrArray.swift in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | 3CB477AA1FF7A6FC00C6B265 /* Sources */ = { 367 | isa = PBXSourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | 3CB477B41FF7A6FC00C6B265 /* GitTests.swift in Sources */, 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | }; 374 | /* End PBXSourcesBuildPhase section */ 375 | 376 | /* Begin PBXTargetDependency section */ 377 | 3CB477B11FF7A6FC00C6B265 /* PBXTargetDependency */ = { 378 | isa = PBXTargetDependency; 379 | target = 3CB477A41FF7A6FC00C6B265 /* Git */; 380 | targetProxy = 3CB477B01FF7A6FC00C6B265 /* PBXContainerItemProxy */; 381 | }; 382 | 3CB477EB1FF7A8B200C6B265 /* PBXTargetDependency */ = { 383 | isa = PBXTargetDependency; 384 | target = 3CB477E61FF7A8A500C6B265 /* libgit2 */; 385 | targetProxy = 3CB477EA1FF7A8B200C6B265 /* PBXContainerItemProxy */; 386 | }; 387 | /* End PBXTargetDependency section */ 388 | 389 | /* Begin XCBuildConfiguration section */ 390 | 3CB477B71FF7A6FC00C6B265 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_ANALYZER_NONNULL = YES; 395 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 401 | CLANG_WARN_BOOL_CONVERSION = YES; 402 | CLANG_WARN_COMMA = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INFINITE_RECURSION = YES; 409 | CLANG_WARN_INT_CONVERSION = YES; 410 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 414 | CLANG_WARN_STRICT_PROTOTYPES = YES; 415 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 416 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 417 | CLANG_WARN_UNREACHABLE_CODE = YES; 418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 419 | CODE_SIGN_IDENTITY = ""; 420 | COPY_PHASE_STRIP = NO; 421 | CURRENT_PROJECT_VERSION = 1; 422 | DEBUG_INFORMATION_FORMAT = dwarf; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | ENABLE_TESTABILITY = YES; 425 | GCC_C_LANGUAGE_STANDARD = gnu11; 426 | GCC_DYNAMIC_NO_PIC = NO; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_OPTIMIZATION_LEVEL = 0; 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 434 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 435 | GCC_WARN_UNDECLARED_SELECTOR = YES; 436 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 437 | GCC_WARN_UNUSED_FUNCTION = YES; 438 | GCC_WARN_UNUSED_VARIABLE = YES; 439 | IBUILD_DEPENDENCIES_ONLY = ""; 440 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 441 | MTL_ENABLE_DEBUG_INFO = YES; 442 | ONLY_ACTIVE_ARCH = YES; 443 | SDKROOT = iphoneos; 444 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 445 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 446 | SWIFT_VERSION = 4.0; 447 | TARGETED_DEVICE_FAMILY = "1,2"; 448 | VERSIONING_SYSTEM = "apple-generic"; 449 | VERSION_INFO_PREFIX = ""; 450 | }; 451 | name = Debug; 452 | }; 453 | 3CB477B81FF7A6FC00C6B265 /* Release */ = { 454 | isa = XCBuildConfiguration; 455 | buildSettings = { 456 | ALWAYS_SEARCH_USER_PATHS = NO; 457 | CLANG_ANALYZER_NONNULL = YES; 458 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 459 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 460 | CLANG_CXX_LIBRARY = "libc++"; 461 | CLANG_ENABLE_MODULES = YES; 462 | CLANG_ENABLE_OBJC_ARC = YES; 463 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 464 | CLANG_WARN_BOOL_CONVERSION = YES; 465 | CLANG_WARN_COMMA = YES; 466 | CLANG_WARN_CONSTANT_CONVERSION = YES; 467 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 468 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 469 | CLANG_WARN_EMPTY_BODY = YES; 470 | CLANG_WARN_ENUM_CONVERSION = YES; 471 | CLANG_WARN_INFINITE_RECURSION = YES; 472 | CLANG_WARN_INT_CONVERSION = YES; 473 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 474 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 476 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 477 | CLANG_WARN_STRICT_PROTOTYPES = YES; 478 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 479 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 480 | CLANG_WARN_UNREACHABLE_CODE = YES; 481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 482 | CODE_SIGN_IDENTITY = ""; 483 | COPY_PHASE_STRIP = NO; 484 | CURRENT_PROJECT_VERSION = 1; 485 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 486 | ENABLE_NS_ASSERTIONS = NO; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | GCC_C_LANGUAGE_STANDARD = gnu11; 489 | GCC_NO_COMMON_BLOCKS = YES; 490 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 491 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 492 | GCC_WARN_UNDECLARED_SELECTOR = YES; 493 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 494 | GCC_WARN_UNUSED_FUNCTION = YES; 495 | GCC_WARN_UNUSED_VARIABLE = YES; 496 | IBUILD_DEPENDENCIES_ONLY = ""; 497 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 498 | MTL_ENABLE_DEBUG_INFO = NO; 499 | SDKROOT = iphoneos; 500 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 501 | SWIFT_VERSION = 4.0; 502 | TARGETED_DEVICE_FAMILY = "1,2"; 503 | VALIDATE_PRODUCT = YES; 504 | VERSIONING_SYSTEM = "apple-generic"; 505 | VERSION_INFO_PREFIX = ""; 506 | }; 507 | name = Release; 508 | }; 509 | 3CB477BA1FF7A6FC00C6B265 /* Debug */ = { 510 | isa = XCBuildConfiguration; 511 | baseConfigurationReference = 3C7CD4451FFA47700049E5FE /* ibuild_search_paths.xcconfig */; 512 | buildSettings = { 513 | APPLICATION_EXTENSION_API_ONLY = YES; 514 | DEFINES_MODULE = YES; 515 | DYLIB_COMPATIBILITY_VERSION = 1; 516 | DYLIB_CURRENT_VERSION = 1; 517 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 518 | INFOPLIST_FILE = "$(SRCROOT)/Framework/Info.plist"; 519 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 521 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.Git; 522 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 523 | SKIP_INSTALL = YES; 524 | SWIFT_INCLUDE_PATHS = "$(IBUILD_CURRENT_BUILD_ROOT)/include/libgit2"; 525 | }; 526 | name = Debug; 527 | }; 528 | 3CB477BB1FF7A6FC00C6B265 /* Release */ = { 529 | isa = XCBuildConfiguration; 530 | baseConfigurationReference = 3C7CD4451FFA47700049E5FE /* ibuild_search_paths.xcconfig */; 531 | buildSettings = { 532 | APPLICATION_EXTENSION_API_ONLY = YES; 533 | DEFINES_MODULE = YES; 534 | DYLIB_COMPATIBILITY_VERSION = 1; 535 | DYLIB_CURRENT_VERSION = 1; 536 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 537 | INFOPLIST_FILE = "$(SRCROOT)/Framework/Info.plist"; 538 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.Git; 541 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 542 | SKIP_INSTALL = YES; 543 | SWIFT_INCLUDE_PATHS = "$(IBUILD_CURRENT_BUILD_ROOT)/include/libgit2"; 544 | }; 545 | name = Release; 546 | }; 547 | 3CB477BD1FF7A6FC00C6B265 /* Debug */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 551 | INFOPLIST_FILE = Tests/Info.plist; 552 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 553 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.GitTests; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | }; 556 | name = Debug; 557 | }; 558 | 3CB477BE1FF7A6FC00C6B265 /* Release */ = { 559 | isa = XCBuildConfiguration; 560 | buildSettings = { 561 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 562 | INFOPLIST_FILE = Tests/Info.plist; 563 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 564 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.GitTests; 565 | PRODUCT_NAME = "$(TARGET_NAME)"; 566 | }; 567 | name = Release; 568 | }; 569 | 3CB477E81FF7A8A500C6B265 /* Debug */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | CODE_SIGN_STYLE = Automatic; 573 | DEBUGGING_SYMBOLS = YES; 574 | DEBUG_INFORMATION_FORMAT = dwarf; 575 | DEVELOPMENT_TEAM = A8DZBD3K9C; 576 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES; 577 | GCC_OPTIMIZATION_LEVEL = 0; 578 | IBUILD_DEPENDENCIES_ONLY = YES; 579 | OTHER_CFLAGS = ""; 580 | OTHER_LDFLAGS = ""; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | }; 583 | name = Debug; 584 | }; 585 | 3CB477E91FF7A8A500C6B265 /* Release */ = { 586 | isa = XCBuildConfiguration; 587 | buildSettings = { 588 | CODE_SIGN_STYLE = Automatic; 589 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 590 | DEVELOPMENT_TEAM = A8DZBD3K9C; 591 | IBUILD_DEPENDENCIES_ONLY = YES; 592 | OTHER_CFLAGS = ""; 593 | OTHER_LDFLAGS = ""; 594 | PRODUCT_NAME = "$(TARGET_NAME)"; 595 | }; 596 | name = Release; 597 | }; 598 | /* End XCBuildConfiguration section */ 599 | 600 | /* Begin XCConfigurationList section */ 601 | 3CB4779F1FF7A6FC00C6B265 /* Build configuration list for PBXProject "Git" */ = { 602 | isa = XCConfigurationList; 603 | buildConfigurations = ( 604 | 3CB477B71FF7A6FC00C6B265 /* Debug */, 605 | 3CB477B81FF7A6FC00C6B265 /* Release */, 606 | ); 607 | defaultConfigurationIsVisible = 0; 608 | defaultConfigurationName = Release; 609 | }; 610 | 3CB477B91FF7A6FC00C6B265 /* Build configuration list for PBXNativeTarget "Git" */ = { 611 | isa = XCConfigurationList; 612 | buildConfigurations = ( 613 | 3CB477BA1FF7A6FC00C6B265 /* Debug */, 614 | 3CB477BB1FF7A6FC00C6B265 /* Release */, 615 | ); 616 | defaultConfigurationIsVisible = 0; 617 | defaultConfigurationName = Release; 618 | }; 619 | 3CB477BC1FF7A6FC00C6B265 /* Build configuration list for PBXNativeTarget "GitTests" */ = { 620 | isa = XCConfigurationList; 621 | buildConfigurations = ( 622 | 3CB477BD1FF7A6FC00C6B265 /* Debug */, 623 | 3CB477BE1FF7A6FC00C6B265 /* Release */, 624 | ); 625 | defaultConfigurationIsVisible = 0; 626 | defaultConfigurationName = Release; 627 | }; 628 | 3CB477E71FF7A8A500C6B265 /* Build configuration list for PBXLegacyTarget "libgit2" */ = { 629 | isa = XCConfigurationList; 630 | buildConfigurations = ( 631 | 3CB477E81FF7A8A500C6B265 /* Debug */, 632 | 3CB477E91FF7A8A500C6B265 /* Release */, 633 | ); 634 | defaultConfigurationIsVisible = 0; 635 | defaultConfigurationName = Release; 636 | }; 637 | /* End XCConfigurationList section */ 638 | }; 639 | rootObject = 3CB4779C1FF7A6FC00C6B265 /* Project object */; 640 | } 641 | -------------------------------------------------------------------------------- /Git.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Git.xcodeproj/xcshareddata/xcschemes/Git.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Git.xcodeproj/xcshareddata/xcschemes/libgit2.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ImplementationProgress.md: -------------------------------------------------------------------------------- 1 | ## Implementation Progress 2 | 3 | This file describes how much of libgit2 has successfully been wrapped by swift methods. 4 | 5 | | libgit2 header | Swift file | Percentage | Notes | 6 | | -------------- | ---------- | ---------- | ----- | 7 | | branch.h | Branch.swift | 100% | | 8 | | commit.h | Commit.swift | 100% | | 9 | | diff.h | Diff.swift | 20% | | 10 | | index.h | Index.swift | 10% | lots to do here | 11 | | oid.h | OID.swift | 30% | sha+short sha | 12 | | revwalk.h | RevWalk.swift | 20% | next | 13 | | refs.h | Reference.swift | 30% | | 14 | | remote.h | Remote.swift | 0% | Storing reference | 15 | | repository.h | Repository.swift | 30% | lots left | 16 | | signature.h | Signature.swift | 40% | create with name/email/time | 17 | | status.h | Status.swift | 95% | all that is left is options | 18 | | transport.h | Credential.swift | 80% | git_cred_ssh_interactive_new is left | 19 | | tree.h | Tree.swift | 0% | storing reference | 20 | | | | | | 21 | | checkout.h | Repository+Checkout | 30% | | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git 2 | 3 |

4 | TiltedTabView • 5 | TabView • 6 | InputAssistant • 7 | Git 8 |

9 | 10 | -------- 11 | 12 | Wrapper around libgit2, written in Swift 13 | 14 | [![Build Status](http://img.shields.io/travis/IMcD23/Git.svg)](https://travis-ci.org/IMcD23/Git) 15 | [![Version](https://img.shields.io/github/release/IMcD23/Git.svg)](https://github.com/IMcD23/Git/releases/latest) 16 | ![Package Managers](https://img.shields.io/badge/supports-Carthage-orange.svg) 17 | [![Contact](https://img.shields.io/badge/contact-%40ian__mcdowell-3a8fc1.svg)](https://twitter.com/ian_mcdowell) 18 | 19 | 20 | 21 | # Requirements 22 | 23 | * Xcode 9 or later 24 | * iOS 11.0 or later 25 | 26 | # Usage 27 | 28 | Take a look at the documentation for each class for information on how to use it. 29 | 30 | # Installation 31 | 32 | ## Carthage 33 | To install Git using [Carthage](https://github.com/Carthage/Carthage), add the following line to your Cartfile: 34 | 35 | ``` 36 | github "IMcD23/Git" "master" 37 | ``` 38 | 39 | ## Submodule 40 | To install Git as a submodule into your git repository, run the following command: 41 | 42 | ``` 43 | git submodule add -b master https://github.com/IMcD23/Git.git Path/To/Git 44 | git submodule update --init --recursive 45 | ``` 46 | 47 | Then, add the `.xcodeproj` in the root of the repository into your Xcode project, and add it as a build dependency. 48 | 49 | ## ibuild 50 | A Swift static library of this project is also available for the ibuild build system. Learn more about ibuild [here](https://github.com/IMcD23/ibuild) 51 | 52 | # Author 53 | Created by [Ian McDowell](https://ianmcdowell.net) 54 | 55 | # License 56 | All code in this project is available under the license specified in the LICENSE file. However, since this project also bundles code from other projects, you are subject to those projects' licenses as well. 57 | -------------------------------------------------------------------------------- /Sources/Branch.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | /// A branch in a git repo. 5 | /// - See: git2/branch.h 6 | public class Branch: Reference { 7 | 8 | internal override init(reference: git_reference, repository: Repository) throws { 9 | try super.init(reference: reference, repository: repository) 10 | } 11 | 12 | public enum Location: Int { 13 | case local = 1 14 | case remote = 2 15 | 16 | fileprivate var branchType: git_branch_t { 17 | switch self { 18 | case .local: 19 | return GIT_BRANCH_LOCAL 20 | case .remote: 21 | return GIT_BRANCH_REMOTE 22 | } 23 | } 24 | } 25 | 26 | /// Retrieves the branch in the repository with the given name 27 | /// 28 | /// - Parameters: 29 | /// - repository: repo the repository to look up the branch 30 | /// - name: Name of the branch to be looked-up; this name is validated for consistency. 31 | /// - type: Type of the considered branch. 32 | /// - See: git_branch_lookup 33 | public convenience init(repository: Repository, name: String, type: Branch.Location) throws { 34 | let reference = try git_try("lookup branch") { git_branch_lookup($0, repository.repository, name.cString(using: .utf8), type.branchType) } 35 | try self.init(reference: reference, repository: repository) 36 | } 37 | 38 | /// Create a new branch pointing at a target commit 39 | /// 40 | /// A new direct reference will be created pointing to this target commit. If `force` is true and a reference already exists with the given name, it'll be replaced. 41 | /// The branch name will be checked for validity. 42 | /// - See: git_branch_create 43 | public static func create(repository: Repository, name: String, commit: Commit, force: Bool = false) throws -> Branch { 44 | let reference = try git_try("create branch") { git_branch_create($0, repository.repository, name.cString(using: .utf8), commit.commit, force ? 1 : 0) } 45 | return try Branch(reference: reference, repository: repository) 46 | } 47 | 48 | /// Create a new branch pointing at a remote branch 49 | /// 50 | /// The branch name will be checked for validity. 51 | /// - See: git_reference_symbolic_create 52 | public static func create(repository: Repository, name: String, remoteBranch: Branch, force: Bool = false) throws -> Branch { 53 | let reference = try git_try("create local branch") { git_reference_create($0, repository.repository, ("refs/heads/" + name).cString(using: .utf8), &remoteBranch.oid.oid, force ? 1 : 0, []) } 54 | return try Branch(reference: reference, repository: repository) 55 | } 56 | 57 | /// The location of the branch, either local or remote 58 | public var location: Location { 59 | if self.isRemoteBranch { 60 | return .remote 61 | } else { 62 | return .local 63 | } 64 | } 65 | 66 | /// The name of the local or remote branch. 67 | /// - See: git_branch_name 68 | public var branchName: String? { 69 | do { 70 | let name = try git_try { git_branch_name($0, self.reference) } 71 | return String.init(cString: name) 72 | } catch { 73 | print("Unable to get name for branch: \(error.localizedDescription)") 74 | return nil 75 | } 76 | } 77 | 78 | /// Helper method: The branchName, without the first part of the path (if remote) 79 | public var shortBranchName: String? { 80 | var name = branchName 81 | if isRemoteBranch, let firstSlash = name?.index(of: "/"), let afterFirstSlash = name?.index(after: firstSlash) { 82 | name = String(name!.suffix(from: afterFirstSlash)) 83 | } 84 | return name 85 | } 86 | 87 | /// Determine if the current local branch is pointed at by HEAD. 88 | /// - See: git_branch_is_head 89 | public var isHead: Bool { 90 | return git_branch_is_head(self.reference) == 1 91 | } 92 | 93 | /// The reference supporting the remote tracking branch, given that this is a local branch. 94 | /// - See: git_branch_upstream 95 | public var upstream: Branch? { 96 | get { 97 | if let branchRef = try? git_try { git_branch_upstream($0, self.reference) }, let branch = try? Branch(reference: branchRef, repository: self.repository) { 98 | return branch 99 | } 100 | return nil 101 | } 102 | } 103 | 104 | /// Set the reference supporting the remote tracking branch, given that this is a local branch. 105 | /// - See: git_branch_set_upstream 106 | public func setUpstream(_ upstream: Branch?) throws { 107 | let name: String? 108 | if let upstream = upstream, upstream.isRemoteBranch { 109 | name = upstream.name.replacingOccurrences(of: "refs/remotes/", with: "") 110 | } else { 111 | name = upstream?.shortBranchName 112 | } 113 | try git_try { git_branch_set_upstream(self.reference, name?.cString(using: .utf8)) } 114 | } 115 | 116 | /// The upstream configuration for the local branch 117 | /// - See: git_branch_upstream_name 118 | public var upstreamName: String? { 119 | get { 120 | if let data = try? git_try { git_branch_upstream_name($0, self.repository.repository, self.name.cString(using: .utf8)) } { 121 | return String.init(data: data, encoding: .utf8) 122 | } 123 | return nil 124 | } 125 | } 126 | 127 | public func setUpstreamName(_ upstreamName: String?) throws { 128 | try git_try { git_branch_set_upstream(self.reference, upstreamName?.cString(using: .utf8)) } 129 | } 130 | 131 | /// Return the name of remote that the remote tracking branch belongs to. 132 | /// - See: git_branch_remote_name 133 | public var remoteName: String? { 134 | if let data = try? git_try { git_branch_remote_name($0, self.repository.repository, self.name.cString(using: .utf8)) } { 135 | return String.init(data: data, encoding: .utf8) 136 | } 137 | 138 | return nil 139 | } 140 | 141 | /// Retrieve the name of the upstream remote of a local branch 142 | /// - See: git_branch_upstream_remote 143 | public var upstreamRemoteName: String? { 144 | if let data = try? git_try { git_branch_upstream_remote($0, self.repository.repository, self.name.cString(using: .utf8)) } { 145 | return String.init(data: data, encoding: .utf8) 146 | } 147 | 148 | return nil 149 | } 150 | 151 | /// Delete an existing branch reference. 152 | /// - See: git_branch_delete 153 | public func delete() throws { 154 | try git_try("delete branch") { git_branch_delete(self.reference) } 155 | } 156 | 157 | /// Move/rename an existing local branch reference 158 | /// 159 | /// The new branch name will be checked for validity. 160 | /// 161 | /// - Parameters: 162 | /// - newName: Target name of the branch once the move is performed; this name is validated for consistency. 163 | /// - force: Overwrite existing branch. 164 | /// - Returns: The renamed branch. 165 | /// - See: git_branch_move 166 | public func move(to newName: String, force: Bool = false) throws -> Branch { 167 | let reference = try git_try("move branch") { git_branch_move($0, self.reference, newName.cString(using: .utf8), force ? 1 : 0) } 168 | return try Branch(reference: reference, repository: self.repository) 169 | } 170 | 171 | /// The number of commits on the branch 172 | public var commitCount: Int { 173 | guard let revwalk = try? RevWalk(repository: self.repository, from: self.oid, to: nil) else { 174 | return 0 175 | } 176 | 177 | var count = 0 178 | while let _ = revwalk.next() { 179 | count += 1 180 | } 181 | return count 182 | } 183 | 184 | /// Commits on the branch. Caution: May be expensive to call. 185 | /// TODO: Paginate the results. 186 | public func commits() throws -> [Commit] { 187 | let revwalk = try RevWalk(repository: self.repository, from: self.oid, to: nil) 188 | 189 | var commits = [Commit]() 190 | while let oid = revwalk.next() { 191 | let commit = try Commit.init(repository: self.repository, oid: oid) 192 | commits.append(commit) 193 | } 194 | return commits 195 | } 196 | 197 | public func commits(relativeTo otherBranch: Branch) throws -> [Commit] { 198 | return [] 199 | } 200 | } 201 | 202 | // MARK: Helpers 203 | internal extension Branch { 204 | 205 | /// Iterates through all branches in a repository with the given type. 206 | /// - See: git_branch_iterator 207 | static func branches(inRepo repo: Repository, withType type: git_branch_t) -> [Branch] { 208 | var branches = [Branch]() 209 | do { 210 | let iterator = try git_try("create branch enumerator") { git_branch_iterator_new($0, repo.repository, type) } 211 | 212 | var branchType: git_branch_t = GIT_BRANCH_ALL 213 | while let branchRef = try? git_try { git_branch_next($0, &branchType, iterator) }, let branch = try? Branch(reference: branchRef, repository: repo) { 214 | branches.append(branch) 215 | } 216 | 217 | git_branch_iterator_free(iterator) 218 | } catch { 219 | return [] 220 | } 221 | return branches 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /Sources/Commit.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_commit = OpaquePointer 5 | 6 | /// A commit in a git repo. 7 | /// - See: git2/commit.h 8 | public class Commit { 9 | internal let commit: git_commit 10 | 11 | internal init(commit: git_commit) { 12 | self.commit = commit 13 | } 14 | 15 | deinit { 16 | git_commit_free(self.commit) 17 | } 18 | 19 | /// Lookup a commit object from a repository. 20 | /// 21 | /// - Parameters: 22 | /// - repository: repo the repository to look up the branch 23 | /// - oid: id of the commit to locate. 24 | /// - See: git_commit_lookup 25 | public convenience init(repository: Repository, oid: OID) throws { 26 | let commit = try git_try("lookup commit") { git_commit_lookup($0, repository.repository, &oid.oid) } 27 | self.init(commit: commit) 28 | } 29 | 30 | /// Create new commit in the repository 31 | /// - See: git_commit_create 32 | public static func create(repository: Repository, updateRef: String?, author: Signature, committer: Signature, messageEncoding: String, message: String, tree: Tree, parents: [Commit]) throws -> Commit { 33 | 34 | let parentCommits = UnsafeMutablePointer.allocate(capacity: parents.count) 35 | for (i, parent) in parents.enumerated() { 36 | parentCommits.advanced(by: i).pointee = parent.commit 37 | } 38 | 39 | let oid = OID() 40 | try git_try("create commit in repository") { git_commit_create(&oid.oid, repository.repository, updateRef?.cString(using: .utf8), author.signature, committer.signature, messageEncoding.cString(using: .utf8), message.cString(using: .utf8), tree.tree, parents.count, parentCommits) } 41 | 42 | parentCommits.deallocate() 43 | 44 | return try Commit(repository: repository, oid: oid) 45 | } 46 | 47 | /// Amend an existing commit by replacing only non-nil values 48 | /// - See: git_commit_amend 49 | public func amend(updateRef: String? = nil, author: Signature? = nil, committer: Signature? = nil, messageEncoding: String? = nil, message: String? = nil, tree: Tree? = nil) throws -> Commit { 50 | let oid = OID() 51 | try git_try("amend commit") { git_commit_amend(&oid.oid, self.commit, updateRef?.cString(using: .utf8), author?.signature, committer?.signature, messageEncoding?.cString(using: .utf8), message?.cString(using: .utf8), tree?.tree) } 52 | 53 | return try Commit(repository: repository, oid: oid) 54 | } 55 | 56 | /// Get the id of a commit. 57 | /// - See: git_commit_id 58 | public var oid: OID { 59 | return OID(oid: git_commit_id(self.commit)) 60 | } 61 | 62 | /// Get the repository that contains the reference 63 | /// - See: git_commit_owner 64 | public var repository: Repository { 65 | let repository = git_commit_owner(self.commit) 66 | return Repository(repository: repository!) 67 | } 68 | 69 | /// Get the encoding for the message of a commit, as a string representing a standard encoding name. 70 | /// - See: git_commit_message_encoding 71 | public var messageEncoding: String { 72 | if let encoding = git_commit_message_encoding(self.commit) { 73 | return String.init(cString: encoding) 74 | } 75 | return "UTF-8" 76 | } 77 | 78 | /// Get the full message of a commit. 79 | /// The returned message will be slightly prettified by removing any potential leading newlines 80 | /// - See: git_commit_message 81 | public var message: String { 82 | if let message = git_commit_message(self.commit) { 83 | return String.init(cString: message) 84 | } 85 | return "" 86 | } 87 | 88 | /// Get the full raw message of a commit. 89 | /// - See: git_commit_message_raw 90 | public var rawMessage: String { 91 | if let message = git_commit_message_raw(self.commit) { 92 | return String.init(cString: message) 93 | } 94 | return "" 95 | } 96 | 97 | /// Get the short "summary" of the git commit message. 98 | /// The returned message is the summary of the commit, comprising the first paragraph of the message with whitespace trimmed and squashed. 99 | /// - See: git_commit_summary 100 | public var summary: String? { 101 | if let summary = git_commit_summary(self.commit) { 102 | return String.init(cString: summary) 103 | } 104 | return nil 105 | } 106 | 107 | /// Get the long "body" of the git commit message. 108 | /// The returned message is the body of the commit, comprising everything but the first paragraph of the message. Leading and trailing whitespaces are trimmed. 109 | /// - See: git_commit_body 110 | public var body: String? { 111 | if let body = git_commit_body(self.commit) { 112 | return String.init(cString: body) 113 | } 114 | return nil 115 | } 116 | 117 | /// Get the commit time (i.e. committer time) of a commit. 118 | /// - See: git_commit_time 119 | public var time: Date { 120 | return Date.init(timeIntervalSince1970: TimeInterval(git_commit_time(self.commit))) 121 | } 122 | 123 | /// Get the commit timezone offset (i.e. committer's preferred timezone) of a commit. 124 | /// - See: git_commit_time_offset 125 | public var timeZone: TimeZone { 126 | return TimeZone.init(secondsFromGMT: Int(git_commit_time_offset(self.commit) * 60))! 127 | } 128 | 129 | /// Get the committer of a commit. 130 | /// - See: git_commit_committer 131 | public var committer: Signature? { 132 | if let committer = git_commit_committer(self.commit) { 133 | return Signature(signature: committer) 134 | } 135 | return nil 136 | } 137 | 138 | /// Get the author of a commit. 139 | /// - See: git_commit_author 140 | public var author: Signature? { 141 | if let author = git_commit_author(self.commit) { 142 | return Signature(signature: author) 143 | } 144 | return nil 145 | } 146 | 147 | /// Get the full raw text of the commit header. 148 | /// - See: git_commit_raw_header 149 | public var rawHeader: String { 150 | return String.init(cString: git_commit_raw_header(self.commit)) 151 | } 152 | 153 | /// Get the tree pointed to by a commit. 154 | /// - See: git_commit_tree 155 | public func tree() throws -> Tree { 156 | let tree = try git_try("get commit's tree") { git_commit_tree($0, self.commit) } 157 | return Tree(tree: tree) 158 | } 159 | 160 | /// Get the id of the tree pointed to by a commit. This differs from `git_commit_tree` in that no attempts are made to fetch an object from the ODB. 161 | /// - See: git_commit_tree_id 162 | public var treeID: OID? { 163 | if let oid = git_commit_tree_id(self.commit) { 164 | return OID(oid: oid) 165 | } 166 | return nil 167 | } 168 | 169 | /// Get the number of parents of this commit 170 | /// - See: git_commit_parentcount 171 | public var parentCount: UInt32 { 172 | return git_commit_parentcount(self.commit) 173 | } 174 | 175 | /// Get the specified parent of the commit. 176 | /// 177 | /// - Parameter index: the position of the parent (from 0 to `parentcount`) 178 | /// - See: git_commit_parent 179 | public func parent(atIndex index: UInt32 = 0) throws -> Commit { 180 | let commit = try git_try("git commit's parent") { git_commit_parent($0, self.commit, index) } 181 | return Commit(commit: commit) 182 | } 183 | 184 | /// Get the oid of a specified parent for a commit. This is different from `git_commit_parent`, which will attempt to load the parent commit from the ODB. 185 | /// 186 | /// - Parameter index: the position of the parent (from 0 to `parentcount`) 187 | /// - See: git_commit_parent 188 | public func parentID(atIndex index: UInt32 = 0) -> OID? { 189 | if let oid = git_commit_parent_id(self.commit, index) { 190 | return OID(oid: oid) 191 | } 192 | return nil 193 | } 194 | 195 | /// Get the commit object that is the th generation ancestor of the named commit object, following only the first parents. 196 | /// Passing `0` as the generation number returns another instance of the base commit itself. 197 | /// 198 | /// - Parameter generation: the requested generation 199 | /// - See: git_commit_nth_gen_ancestor 200 | public func ancestor(generation: UInt32) throws -> Commit { 201 | let commit = try git_try("git commit's ancestor") { git_commit_nth_gen_ancestor($0, self.commit, generation) } 202 | return Commit(commit: commit) 203 | } 204 | 205 | /// Get an arbitrary header field 206 | /// - See: git_commit_header_field 207 | public func header(field: String) -> String? { 208 | if let data = try? git_try { git_commit_header_field($0, self.commit, field.cString(using: .utf8)) } { 209 | return String.init(data: data, encoding: .utf8) 210 | } 211 | return nil 212 | } 213 | 214 | /// Extract the signature from a commit 215 | /// - See: git_commit_extract_signature 216 | public func signature(field: String? = nil) -> Data? { 217 | var signed_data = git_buf() 218 | return try? git_try { git_commit_extract_signature($0, &signed_data, self.repository.repository, &self.oid.oid, field?.cString(using: .utf8)) } 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /Sources/Credential.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | public typealias CredentialProvider = (_ username: String) throws -> Credential 5 | internal class CredentialProviderMetadata { 6 | 7 | private let provider: CredentialProvider 8 | internal let fetchProgress: (Double) -> Void 9 | internal let checkoutProgress: (Double) -> Void 10 | 11 | private var credential: Credential? 12 | 13 | internal init(provider: @escaping CredentialProvider, fetchProgress: @escaping (Double) -> Void, checkoutProgress: @escaping (Double) -> Void) { 14 | self.provider = provider 15 | self.fetchProgress = fetchProgress 16 | self.checkoutProgress = checkoutProgress 17 | self.credential = nil 18 | } 19 | 20 | internal func credential(forUsername username: String) -> Credential? { 21 | do { 22 | self.credential = try provider(username) 23 | return self.credential 24 | } catch { 25 | return nil 26 | } 27 | } 28 | } 29 | 30 | /// Credential used for authenticating with remotes. 31 | /// - See: git2/transport.h 32 | public class Credential { 33 | internal let cred: UnsafeMutablePointer 34 | 35 | internal init(cred: UnsafeMutablePointer) { 36 | self.cred = cred 37 | } 38 | 39 | /// Create a credential to specify a username. 40 | /// - See: git_cred_username_new 41 | public convenience init(username: String) throws { 42 | let cred = try git_try("create credential") { git_cred_username_new($0, username.cString(using: .utf8)) } 43 | self.init(cred: cred) 44 | } 45 | 46 | /// Create a new plain-text username and password credential object. 47 | /// - See: git_cred_userpass_plaintext_new 48 | public convenience init(username: String, password: String) throws { 49 | let cred = try git_try("create credential") { git_cred_userpass_plaintext_new($0, username.cString(using: .utf8), password.cString(using: .utf8)) } 50 | self.init(cred: cred) 51 | } 52 | 53 | /// Create a new passphrase-protected ssh key credential object. 54 | /// - See: git_cred_ssh_key_new 55 | public convenience init(username: String, publicKey: URL, privateKey: URL, passphrase: String?) throws { 56 | let cred = try git_try("create credential") { git_cred_ssh_key_new($0, username.cString(using: .utf8), publicKey.path.cString(using: .utf8), privateKey.path.cString(using: .utf8), passphrase?.cString(using: .utf8)) } 57 | self.init(cred: cred) 58 | } 59 | 60 | /// Create a new ssh key credential object reading the keys from memory. 61 | /// - See: git_cred_ssh_key_memory_new 62 | public convenience init(username: String, publicKey: String, privateKey: String, passphrase: String?) throws { 63 | let cred = try git_try("create credential") { git_cred_ssh_key_memory_new($0, username.cString(using: .utf8), publicKey.cString(using: .utf8), privateKey.cString(using: .utf8), passphrase?.cString(using: .utf8)) } 64 | self.init(cred: cred) 65 | } 66 | 67 | // TODO: Interactive credential: git_cred_ssh_interactive_new 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Sources/Diff.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_diff = OpaquePointer 5 | 6 | public class Diff { 7 | 8 | /// Structure describing options about how the diff should be executed. 9 | /// - See: git_diff_options 10 | public struct Options { 11 | 12 | public init() {} 13 | 14 | internal var options: git_diff_options { 15 | var options = git_diff_options.init() 16 | git_diff_init_options(&options, UInt32(GIT_DIFF_OPTIONS_VERSION)) 17 | return options 18 | } 19 | } 20 | 21 | internal let diff: git_diff 22 | 23 | internal init(diff: git_diff) { 24 | self.diff = diff 25 | } 26 | 27 | 28 | /// Create a diff with the difference between two tree objects. 29 | /// The first tree will be used for the "old_file" side of the delta and the second tree will be used for the "new_file" side of the delta. You can pass NULL to indicate an empty tree, although it is an error to pass NULL for both the `old_tree` and `new_tree`. 30 | /// - See: git_diff_tree_to_tree 31 | public convenience init(repository: Repository, oldTree: Tree?, newTree: Tree?, options: Options) throws { 32 | assert(oldTree != nil || newTree != nil, "Either oldTree or newTree must be provided.") 33 | var options = options.options 34 | 35 | let diff = try git_try("diff tree to tree") { git_diff_tree_to_tree($0, repository.repository, oldTree?.tree, newTree?.tree, &options) } 36 | self.init(diff: diff) 37 | } 38 | 39 | /// Create a diff between a tree and repository index. 40 | /// This is equivalent to `git diff --cached ` or if you pass the HEAD tree, then like `git diff --cached`. 41 | /// The tree you pass will be used for the "old_file" side of the delta, and the index will be used for the "new_file" side of the delta. 42 | /// If you pass NULL for the index, then the existing index of the `repo` will be used. In this case, the index will be refreshed from disk (if it has changed) before the diff is generated. 43 | /// - See: git_diff_tree_to_index 44 | public convenience init(repository: Repository, oldTree: Tree, index: Index?, options: Options) throws { 45 | var options = options.options 46 | 47 | let diff = try git_try("diff tree to index") { git_diff_tree_to_index($0, repository.repository, oldTree.tree, index?.index, &options) } 48 | self.init(diff: diff) 49 | } 50 | 51 | // TODO: Other init 52 | // TODO: get deltas 53 | 54 | public func deltas() throws -> [DiffDelta] { 55 | return [] // TODO 56 | } 57 | } 58 | 59 | /// Description of changes to one entry. 60 | /// - See: git2/diff.h 61 | public class DiffDelta { 62 | 63 | public enum Status: UInt32 { 64 | case unmodified 65 | case added 66 | case deleted 67 | case modified 68 | case renamed 69 | case copied 70 | case ignored 71 | case untracked 72 | case typeChange 73 | case unreadable 74 | case conflicted 75 | 76 | internal static func fromStatus(_ status: git_delta_t) -> Status { 77 | switch status { 78 | case GIT_DELTA_UNMODIFIED: return .unmodified 79 | case GIT_DELTA_ADDED: return .added 80 | case GIT_DELTA_DELETED: return .deleted 81 | case GIT_DELTA_MODIFIED: return .modified 82 | case GIT_DELTA_RENAMED: return .renamed 83 | case GIT_DELTA_COPIED: return .copied 84 | case GIT_DELTA_IGNORED: return .ignored 85 | case GIT_DELTA_UNTRACKED: return .untracked 86 | case GIT_DELTA_TYPECHANGE: return .typeChange 87 | case GIT_DELTA_UNREADABLE: return .unreadable 88 | case GIT_DELTA_CONFLICTED: return .conflicted 89 | default: 90 | fatalError("Unhandled git_delta_t in Status.fromStatus(_:)") 91 | } 92 | } 93 | } 94 | internal let delta: UnsafePointer 95 | 96 | internal init(delta: UnsafePointer) { 97 | self.delta = delta 98 | } 99 | 100 | public var status: Status { 101 | return Status.fromStatus(delta.pointee.status) 102 | } 103 | 104 | public var newFile: DiffFile { 105 | return DiffFile(file: delta.pointee.new_file) 106 | } 107 | 108 | public var oldFile: DiffFile { 109 | return DiffFile(file: delta.pointee.old_file) 110 | } 111 | // flags 112 | // similarity 113 | // nfiles 114 | } 115 | 116 | /// Description of one side of a delta. 117 | /// - See: git2/diff.h 118 | public class DiffFile { 119 | internal let file: git_diff_file 120 | 121 | internal init(file: git_diff_file) { 122 | self.file = file 123 | } 124 | 125 | public var id: OID { 126 | return OID(oid: self.file.id) 127 | } 128 | 129 | public var path: String? { 130 | if let path = self.file.path { 131 | return String(cString: path) 132 | } 133 | return nil 134 | } 135 | 136 | // size: git_off_t 137 | // flags 138 | // mode 139 | // id_abbrev 140 | } 141 | -------------------------------------------------------------------------------- /Sources/Git.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | public struct Git { 5 | 6 | /// Sets up the git framework. Call this method before performing any other actions. 7 | public static func setup() { 8 | git_libgit2_init() 9 | } 10 | 11 | /// Tears down the git framework. Call this after you are done using git. (optional) 12 | public static func tearDown() { 13 | git_libgit2_shutdown() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Helpers/Data+Git.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | extension Data { 5 | 6 | static func fromBuffer(buffer: UnsafeMutablePointer) throws -> Data { 7 | if buffer.pointee.size == 0 { 8 | return Data() 9 | } 10 | 11 | try git_try("grow git buffer") { git_buf_grow(buffer, 0) } 12 | 13 | return Data(bytes: buffer.pointee.ptr, count: buffer.pointee.size) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Helpers/Date+Git.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal extension Date { 5 | 6 | func gitTime(withTimeZone timeZone: TimeZone = .current) -> git_time { 7 | return git_time.init(time: git_time_t(self.timeIntervalSince1970), offset: Int32(timeZone.secondsFromGMT() * 60), sign: 0) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Helpers/Errors.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | /// Try to execute a method, and throw if git returns a non-zero status code 5 | internal func git_try(_ action: String? = nil, _ method: () -> Int32) throws { 6 | let error = method() 7 | guard error == GIT_OK.rawValue else { 8 | throw GitError.create(action) 9 | } 10 | } 11 | 12 | /// Try to execute a method, and throw if git returns a non-zero status code 13 | /// This override is for git methods that return a value into an argument pointer 14 | internal func git_try(_ action: String? = nil, _ method: (UnsafeMutablePointer) -> Int32) throws -> T { 15 | var val: T? = nil 16 | let error = method(&val) 17 | guard let value = val, error == GIT_OK.rawValue else { 18 | throw GitError.create(action) 19 | } 20 | return value 21 | } 22 | 23 | /// Try to execute a method, and throw if git returns a non-zero status code 24 | /// This override is for git methods that return 2 values into argument pointers 25 | internal func git_try(_ action: String? = nil, _ method: (UnsafeMutablePointer, UnsafeMutablePointer) -> Int32) throws -> (T, V) { 26 | var val1: T? = nil 27 | var val2: V? = nil 28 | let error = method(&val1, &val2) 29 | guard let value1 = val1, let value2 = val2, error == GIT_OK.rawValue else { 30 | throw GitError.create(action) 31 | } 32 | return (value1, value2) 33 | } 34 | 35 | internal func git_try(_ action: String? = nil, _ method: (UnsafeMutablePointer) -> Int32) throws -> Data { 36 | var buf = git_buf.init() 37 | let error = method(&buf) 38 | guard error == GIT_OK.rawValue else { 39 | throw GitError.create(action) 40 | } 41 | let data = try Data.fromBuffer(buffer: &buf) 42 | git_buf_dispose(&buf) 43 | return data 44 | } 45 | 46 | enum GitError: LocalizedError { 47 | case gitError(errorStr: String, action: String?) 48 | case notImplemented 49 | 50 | static func create(_ action: String? = nil) -> GitError { 51 | let errorStr: String 52 | if let error = giterr_last() { 53 | errorStr = String.init(cString: error.pointee.message) 54 | } else { 55 | errorStr = "" 56 | } 57 | return GitError.gitError(errorStr: errorStr, action: action) 58 | } 59 | 60 | var errorDescription: String? { 61 | switch self { 62 | case .gitError(let errorStr, let action): 63 | if let action = action { 64 | return "Failed to \(action). \(errorStr)" 65 | } 66 | return errorStr 67 | case .notImplemented: 68 | return "Method not implemented" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Index.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_index = OpaquePointer 5 | 6 | /// Git index parsing and manipulation 7 | /// - See: git2/index.h 8 | public class Index { 9 | internal let index: git_index 10 | 11 | internal init(index: git_index) { 12 | self.index = index 13 | } 14 | 15 | deinit { 16 | git_index_free(self.index) 17 | } 18 | 19 | public var repository: Repository { 20 | let repository = git_index_owner(self.index) 21 | 22 | return Repository(repository: repository!) 23 | } 24 | 25 | /// Adds a file at the given repository-relative path to the index 26 | /// 27 | /// - Parameter path: Path relative to root of git repo 28 | /// - See: git_index_add_bypath 29 | public func addFile(at path: String) throws { 30 | try git_try("add file at \"\(path)\" to index") { git_index_add_bypath(self.index, path.cString(using: .utf8)) } 31 | } 32 | 33 | public func write() throws { 34 | try git_try("write index") { git_index_write(self.index) } 35 | } 36 | } 37 | 38 | // MARK: Tree 39 | public extension Index { 40 | 41 | @discardableResult 42 | func writeTree() throws -> Tree { 43 | 44 | let oid = OID() 45 | try git_try("write tree") { git_index_write_tree(&oid.oid, self.index) } 46 | 47 | let tree = try git_try("find written tree") { git_tree_lookup($0, self.repository.repository, &oid.oid) } 48 | 49 | return Tree(tree: tree) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/OID.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | /// Git object id 5 | /// - See: git2/oid.h 6 | public class OID { 7 | internal var oid: git_oid 8 | 9 | public init() { 10 | self.oid = git_oid() 11 | } 12 | internal init(oid: git_oid) { 13 | self.oid = oid 14 | } 15 | internal convenience init(oid: UnsafePointer) { 16 | self.init() 17 | git_oid_cpy(&self.oid, oid) 18 | } 19 | public convenience init(sha: String) throws { 20 | self.init() 21 | try git_try { git_oid_fromstrp(&self.oid, sha.cString(using: .utf8)) } 22 | } 23 | 24 | public var sha: String { 25 | let str = git_oid_tostr_s(&self.oid) 26 | return String(cString: str!) 27 | } 28 | 29 | public var sha_short: String { 30 | let buffer = UnsafeMutablePointer.allocate(capacity: 8) 31 | git_oid_tostr(buffer, 8, &self.oid) 32 | let str = String(cString: buffer) 33 | buffer.deallocate() 34 | return str 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Private/RevWalk.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_revwalk = OpaquePointer 5 | 6 | /// Git revision traversal 7 | /// - See: git2/revwalk.h 8 | internal class RevWalk { 9 | private let revwalk: git_revwalk 10 | 11 | private init(revwalk: git_revwalk) { 12 | self.revwalk = revwalk 13 | } 14 | 15 | convenience init(repository: Repository, from: OID?, to: OID?) throws { 16 | let revwalk = try git_try("create rev walker") { git_revwalk_new($0, repository.repository) } 17 | 18 | if let from = from { 19 | git_revwalk_push(revwalk, &from.oid) 20 | } 21 | if let to = to { 22 | git_revwalk_hide(revwalk, &to.oid) 23 | } 24 | 25 | self.init(revwalk: revwalk) 26 | } 27 | 28 | deinit { 29 | git_revwalk_free(self.revwalk) 30 | } 31 | 32 | func next() -> OID? { 33 | let oid = OID() 34 | if git_revwalk_next(&oid.oid, self.revwalk) == GIT_OK.rawValue { 35 | return oid 36 | } 37 | return nil 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Private/StrArray.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal class StrArray { 5 | var strarray: git_strarray 6 | 7 | init(_ strings: [String]) { 8 | let count = strings.count 9 | self.strarray = git_strarray.init() 10 | if count == 0 { return } 11 | 12 | let cStrings = UnsafeMutablePointer?>.allocate(capacity: count) 13 | for (index, string) in strings.enumerated() { 14 | cStrings.advanced(by: index).pointee = strdup(string.cString(using: .utf8)!) 15 | } 16 | 17 | self.strarray.strings = cStrings 18 | self.strarray.count = count 19 | } 20 | 21 | deinit { 22 | git_strarray_free(&self.strarray) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Reference.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_reference = OpaquePointer 5 | internal typealias git_object = OpaquePointer 6 | 7 | private enum ReferenceError: LocalizedError { 8 | case invalidReference 9 | 10 | var errorDescription: String? { 11 | switch self { 12 | case .invalidReference: 13 | return "The git reference was neither to an object or another reference." 14 | } 15 | } 16 | } 17 | 18 | /// Git reference management 19 | /// - See: git2/refs.h 20 | public class Reference { 21 | internal let reference: git_reference 22 | 23 | public enum ReferenceType { 24 | /// A reference pointing to an object 25 | case object 26 | 27 | /// A reference pointing to another reference 28 | case symbolic 29 | } 30 | 31 | internal init(reference: git_reference, repository: Repository) throws { 32 | if git_reference_type(reference) == GIT_REF_INVALID { 33 | throw ReferenceError.invalidReference 34 | } 35 | self.reference = reference 36 | self.repository = repository 37 | } 38 | 39 | deinit { 40 | git_reference_free(self.reference) 41 | } 42 | 43 | /// Get the type of a reference. 44 | /// Can be either object or symbolic 45 | /// - See: git_reference_type 46 | public var type: ReferenceType { 47 | switch git_reference_type(self.reference) { 48 | case GIT_REF_OID: 49 | return .object 50 | case GIT_REF_SYMBOLIC: 51 | return .symbolic 52 | default: 53 | // This should be caught in Reference.init 54 | fatalError("Type of git reference was invalid") 55 | } 56 | } 57 | 58 | /// Get the repository that contains the reference 59 | public var repository: Repository 60 | 61 | /// Get the full name of a reference. 62 | /// - See: git_reference_name 63 | public var name: String { 64 | let name = git_reference_name(self.reference)! 65 | return String.init(cString: name) 66 | } 67 | 68 | /// Retrieve the OID of the reference. 69 | /// - See: git_reference_name_to_id 70 | public var oid: OID { 71 | let oid = OID() 72 | try! git_try("resolve reference name to oid") { git_reference_name_to_id(&oid.oid, self.repository.repository, self.name.cString(using: .utf8)) } 73 | return oid 74 | } 75 | 76 | /// Check if a reference is a remote tracking branch 77 | /// - See: git_reference_is_remote 78 | public var isRemoteBranch: Bool { 79 | return git_reference_is_remote(self.reference) == 1 80 | } 81 | 82 | /// Check if a reference is a local branch 83 | /// - See: git_reference_is_branch 84 | public var isLocalBranch: Bool { 85 | return git_reference_is_branch(self.reference) == 1 86 | } 87 | 88 | /// If the reference is a local or remote branch, returns a branch object that the reference is equivalent to. 89 | /// If not, returns nil. 90 | public var asBranch: Branch? { 91 | if !isLocalBranch && !isRemoteBranch { 92 | return nil 93 | } 94 | let name = try! git_try { git_branch_name($0, self.reference) } 95 | let reference: git_reference 96 | if isLocalBranch { 97 | reference = try! git_try { git_branch_lookup($0, self.repository.repository, name, GIT_BRANCH_LOCAL) } 98 | } else { 99 | reference = try! git_try { git_branch_lookup($0, self.repository.repository, name, GIT_BRANCH_REMOTE) } 100 | } 101 | return try? Branch(reference: reference, repository: self.repository) 102 | } 103 | 104 | /// Returns the commit that this reference is pointing to. 105 | public var asCommit: Commit? { 106 | return try? Commit(repository: self.repository, oid: self.oid) 107 | } 108 | 109 | internal func peel() throws -> git_object { 110 | return try git_try { git_reference_peel($0, self.reference, GIT_OBJ_ANY) } 111 | } 112 | } 113 | 114 | private extension Reference { 115 | 116 | func resolved() throws -> Reference { 117 | if self.type == .symbolic { 118 | let resolved = try git_try("resolve reference") { git_reference_resolve($0, self.reference) } 119 | return try Reference(reference: resolved, repository: repository) 120 | } 121 | return self 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Sources/Remote.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_remote = OpaquePointer 5 | 6 | /// Git remote 7 | /// - See: git2/remote.h 8 | public class Remote { 9 | internal let remote: git_remote 10 | /// Get the repository that contains the remote 11 | public var repository: Repository 12 | 13 | internal init(remote: git_remote, repository: Repository) { 14 | self.remote = remote 15 | self.repository = repository 16 | } 17 | 18 | public convenience init(repository: Repository, name: String) throws { 19 | let remote = try git_try("lookup remote") { git_remote_lookup($0, repository.repository, name.cString(using: .utf8)) } 20 | self.init(remote: remote, repository: repository) 21 | } 22 | 23 | /// Create a new remote in the repository. 24 | /// - See: git_remote_create 25 | public static func create(repository: Repository, name: String, url: URL) throws -> Remote { 26 | let remote = try git_try("create remote") { git_remote_create($0, repository.repository, name.cString(using: .utf8), url.absoluteString.cString(using: .utf8)) } 27 | return Remote(remote: remote, repository: repository) 28 | } 29 | 30 | deinit { 31 | git_remote_free(self.remote) 32 | } 33 | 34 | /// Get the remote's name 35 | /// - See: git_remote_name, git_remote_rename 36 | public var name: String { 37 | get { 38 | if let name = git_remote_name(self.remote) { 39 | return String(cString: name) 40 | } 41 | return "" 42 | } 43 | set { 44 | var problematic_pathspecs = git_strarray.init() 45 | try? git_try("rename remote") { git_remote_rename(&problematic_pathspecs, self.repository.repository, self.name.cString(using: .utf8), name.cString(using: .utf8)) } 46 | git_strarray_free(&problematic_pathspecs) 47 | } 48 | } 49 | 50 | /// Get the remote's url 51 | /// - See: git_remote_url, git_remote_set_url 52 | public var url: URL { 53 | get { 54 | // Potentially unsafe. Two nil checks missed here 55 | return URL(string: String(cString: git_remote_url(self.remote)))! 56 | } 57 | set { 58 | try? git_try { git_remote_set_url(self.repository.repository, self.name.cString(using: .utf8), url.absoluteString.cString(using: .utf8)) } 59 | } 60 | } 61 | 62 | /// Get the remote's push url 63 | /// - See: git_remote_pushurl 64 | public var pushURL: URL? { 65 | get { 66 | if let url = git_remote_pushurl(self.remote) { 67 | let str = String(cString: url) 68 | return URL(string: str) 69 | } 70 | return nil 71 | } 72 | set { 73 | try? git_try { git_remote_set_pushurl(self.repository.repository, self.name.cString(using: .utf8), pushURL?.absoluteString.cString(using: .utf8)) } 74 | } 75 | } 76 | } 77 | 78 | extension Remote: Equatable { 79 | 80 | public static func ==(a: Remote, b: Remote) -> Bool { 81 | return a.repository == b.repository && a.name == b.name && a.url == b.url 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/Repository.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | enum RepositoryError: LocalizedError { 5 | case unsupportedURL 6 | 7 | 8 | var errorDescription: String? { 9 | switch self { 10 | case .unsupportedURL: 11 | return "Unsupported URL" 12 | } 13 | } 14 | } 15 | 16 | internal typealias git_repository = OpaquePointer 17 | 18 | /// Git repository management 19 | /// - See: git2/repository.h 20 | public class Repository { 21 | internal let repository: git_repository 22 | 23 | /// Keep a cache of all repositories open, so libgit2 can cache database calls, etc. 24 | /// This means that multiple `Repository` objects can point to the same git_repository. 25 | private static var Cache: [URL: git_repository] = [:] 26 | 27 | public convenience init(url: URL) throws { 28 | guard url.isFileURL, !url.path.isEmpty else { 29 | throw RepositoryError.unsupportedURL 30 | } 31 | 32 | if let repository = Repository.Cache[url] { 33 | self.init(repository: repository) 34 | } else { 35 | let repository = try git_try("open repository at URL: \(url.path)") { git_repository_open($0, url.path.cString(using: .utf8)) } 36 | 37 | Repository.Cache[url] = repository 38 | 39 | self.init(repository: repository) 40 | } 41 | } 42 | 43 | public enum CloneProgress { 44 | case fetch(progress: Double) 45 | case checkout(progress: Double) 46 | } 47 | @discardableResult 48 | public static func clone(from url: URL, to repositoryURL: URL, credentialProvider: @escaping CredentialProvider, progress: ((_ progress: CloneProgress) -> Void)? = nil) throws -> Repository { 49 | 50 | // Create a metadata object that will contain the fetch method and the provided credential. 51 | // This will be passed into the credentials block of the fetchOptions. 52 | // We can't just pass the CredentialProvider in, since the credential it produces must be retained 53 | // throughout the lifetime of the clone action. 54 | var credentialProviderMetadata = CredentialProviderMetadata( 55 | provider: credentialProvider, 56 | fetchProgress: { percent in 57 | progress?(.fetch(progress: percent)) 58 | }, 59 | checkoutProgress: { percent in 60 | progress?(.checkout(progress: percent)) 61 | } 62 | ) 63 | 64 | var options = git_clone_options.init() 65 | git_clone_init_options(&options, UInt32(GIT_CLONE_OPTIONS_VERSION)) 66 | 67 | // Options for fetching 68 | var fetchOptions = git_fetch_options.init() 69 | git_fetch_init_options(&fetchOptions, UInt32(GIT_FETCH_OPTIONS_VERSION)) 70 | 71 | fetchOptions.callbacks.credentials = { cred, url, usernameFromURL, allowedTypes, payload in 72 | 73 | guard 74 | let credentialProviderMetadata = payload?.assumingMemoryBound(to: CredentialProviderMetadata.self).pointee, 75 | let usernameFromURL = usernameFromURL 76 | else { 77 | giterr_set_str(GIT_EUSER.rawValue, "Unable to load credential metadata".cString(using: .utf8)) 78 | return GIT_ERROR.rawValue 79 | } 80 | 81 | let username = String.init(cString: usernameFromURL) 82 | 83 | guard let credential = credentialProviderMetadata.credential(forUsername: username) else { 84 | giterr_set_str(GIT_EUSER.rawValue, "Unable to load credential from credential provider".cString(using: .utf8)) 85 | return GIT_ERROR.rawValue 86 | } 87 | 88 | cred?.pointee = credential.cred 89 | print("Git: credentials requested.") 90 | return GIT_OK.rawValue 91 | } 92 | 93 | fetchOptions.callbacks.payload = UnsafeMutableRawPointer(&credentialProviderMetadata) 94 | 95 | fetchOptions.callbacks.transfer_progress = { stats, payload in 96 | print("Git: fetch transfer progressed.") 97 | 98 | if 99 | let metadata = payload?.assumingMemoryBound(to: CredentialProviderMetadata.self).pointee, 100 | let stats = stats?.pointee 101 | { 102 | let percent = Double(stats.received_objects) / Double(stats.total_objects) 103 | metadata.fetchProgress(percent) 104 | } 105 | 106 | return GIT_OK.rawValue 107 | } 108 | 109 | options.fetch_opts = fetchOptions 110 | 111 | // Options for checking out 112 | var checkoutOptions = git_checkout_options.init() 113 | git_checkout_init_options(&checkoutOptions, UInt32(GIT_CHECKOUT_OPTIONS_VERSION)) 114 | checkoutOptions.checkout_strategy = GIT_CHECKOUT_FORCE.rawValue 115 | 116 | checkoutOptions.progress_payload = UnsafeMutableRawPointer(&credentialProviderMetadata) 117 | checkoutOptions.progress_cb = { path, completed, total, payload in 118 | print("Git: checkout progressed. \(completed) / \(total)") 119 | 120 | if let metadata = payload?.assumingMemoryBound(to: CredentialProviderMetadata.self).pointee { 121 | let percent = Double(completed) / Double(total) 122 | metadata.checkoutProgress(percent) 123 | } 124 | } 125 | 126 | checkoutOptions.notify_cb = { why, path, baseline, target, workdir, payload in 127 | print("Git: checkout notified.") 128 | return GIT_OK.rawValue 129 | } 130 | checkoutOptions.notify_flags = GIT_CHECKOUT_NOTIFY_NONE.rawValue 131 | 132 | options.checkout_opts = checkoutOptions 133 | 134 | // Get the remote URL 135 | let remotePath: String 136 | if url.isFileURL { 137 | remotePath = url.standardizedFileURL.path 138 | } else { 139 | remotePath = url.absoluteString 140 | } 141 | 142 | let repository = try git_try("clone repository") { git_clone($0, remotePath.cString(using: .utf8), repositoryURL.path.cString(using: .utf8), &options) } 143 | 144 | return Repository(repository: repository) 145 | } 146 | 147 | @discardableResult 148 | public static func create(at url: URL) throws -> Repository { 149 | guard url.isFileURL, !url.path.isEmpty else { 150 | throw RepositoryError.unsupportedURL 151 | } 152 | 153 | let repository = try git_try("create repository at URL: \(url.path)") { git_repository_init($0, url.path.cString(using: .utf8), 0) } 154 | 155 | return Repository(repository: repository) 156 | } 157 | 158 | internal init(repository: git_repository) { 159 | self.repository = repository 160 | } 161 | 162 | /// Get the path of the working directory for this repository 163 | /// If the repository is bare, this will be nil. 164 | /// - See: git_repository_workdir 165 | public var workingDirectory: URL? { 166 | if let workdir = git_repository_workdir(self.repository) { 167 | return URL(fileURLWithPath: String(cString: workdir)) 168 | } 169 | return nil 170 | } 171 | 172 | /// Retrieve and resolve the reference pointed at by HEAD. 173 | /// - See: git_repository_head 174 | public var head: Reference? { 175 | if let head = try? git_try { git_repository_head($0, self.repository) } { 176 | return try? Reference(reference: head, repository: self) 177 | } 178 | return nil 179 | } 180 | } 181 | 182 | extension Repository: Equatable { 183 | public static func ==(a: Repository, b: Repository) -> Bool { 184 | return a.workingDirectory == b.workingDirectory 185 | } 186 | } 187 | 188 | // MARK: Branches 189 | /// - See: git2/branch.h 190 | public extension Repository { 191 | /// A list of the local and remote branches 192 | public var branches: [Branch] { 193 | return Branch.branches(inRepo: self, withType: GIT_BRANCH_ALL) 194 | } 195 | 196 | /// A list of the local branches 197 | public var localBranches: [Branch] { 198 | return Branch.branches(inRepo: self, withType: GIT_BRANCH_LOCAL) 199 | } 200 | 201 | /// A list of the remote branches 202 | public var remoteBranches: [Branch] { 203 | return Branch.branches(inRepo: self, withType: GIT_BRANCH_REMOTE) 204 | } 205 | } 206 | 207 | // MARK: Remotes 208 | /// - See: git2/remote.h 209 | public extension Repository { 210 | public var remotes: [Remote] { 211 | do { 212 | var list = git_strarray.init() 213 | try git_try("retrieve remote list") { git_remote_list(&list, self.repository) } 214 | var remotes: [Remote] = [] 215 | for i in 0.. (ahead: Int, behind: Int) { 230 | var ahead: Int = 0 231 | var behind: Int = 0 232 | git_graph_ahead_behind(&ahead, &behind, self.repository, &local.oid, &upstream.oid) 233 | return (ahead, behind) 234 | } 235 | 236 | /// Returns an array of commits from 237 | func uniqueCommits(from: OID, to: OID) throws -> [Commit] { 238 | 239 | let revwalk = try RevWalk(repository: self, from: from, to: to) 240 | 241 | var commits: [Commit] = [] 242 | 243 | while let oid = revwalk.next() { 244 | commits.append(try Commit.init(repository: self, oid: oid)) 245 | } 246 | 247 | return commits 248 | } 249 | 250 | } 251 | 252 | // MARK: Index 253 | public extension Repository { 254 | 255 | func index() throws -> Index { 256 | let index = try git_try("get index for the repository") { git_repository_index($0, self.repository) } 257 | return Index(index: index) 258 | } 259 | } 260 | 261 | // MARK: Status 262 | /// - See: git2/status.h 263 | public extension Repository { 264 | 265 | /// Get file status for a single file. 266 | /// 267 | /// - Parameter file: The repository-relative path of a file in the repository 268 | /// - Returns: Status OptionSet that describes the file's status 269 | /// - See: git_status_file 270 | func status(forFile path: String) throws -> Status { 271 | var status: UInt32 = 0 272 | try git_try("get status for file \"\(path)\"", { git_status_file(&status, self.repository, path.cString(using: .utf8)) }) 273 | return Status(rawValue: status) 274 | } 275 | 276 | /// Test if the ignore rules apply to a given file. 277 | /// 278 | /// This function checks the ignore rules to see if they would apply to the given file. 279 | /// This indicates if the file would be ignored regardless ofwhether the file is already in the index or committed to the repository. 280 | /// 281 | /// One way to think of this is if you were to do "git add ." on the directory containing the file, would it be added or not? 282 | /// 283 | /// - Parameter file: The URL of a file in the repository 284 | /// - See: git_status_should_ignore 285 | func ignoreRulesApply(toFile file: URL) throws -> Bool { 286 | var ignored: Int32 = 0 287 | try git_try("get ignore status for file \"\(file.lastPathComponent)\"", { git_status_should_ignore(&ignored, self.repository, file.absoluteString.cString(using: .utf8)) }) 288 | return ignored == 1 289 | } 290 | } 291 | 292 | // MARK: Reset 293 | /// - See: git2/reset.h 294 | public extension Repository { 295 | 296 | /// Kinds of reset operation 297 | public enum ResetType { 298 | /// Move the head to the given commit 299 | case soft 300 | /// soft plus reset the index to the commit 301 | case mixed 302 | /// mixed plus changes in the working tree are discarded 303 | case hard 304 | 305 | internal var resetType: git_reset_t { 306 | switch self { 307 | case .soft: return GIT_RESET_SOFT 308 | case .mixed: return GIT_RESET_MIXED 309 | case .hard: return GIT_RESET_HARD 310 | } 311 | } 312 | } 313 | 314 | /// Sets the current head to the specified commit oid and optionally resets the index and working tree to match. 315 | /// - See: git_reset 316 | func reset(toCommit commit: Commit, type: ResetType) throws { 317 | var options = git_checkout_options.init() 318 | git_checkout_init_options(&options, UInt32(GIT_CHECKOUT_OPTIONS_VERSION)) 319 | 320 | try git_try("reset to commit") { git_reset(self.repository, commit.commit, type.resetType, &options) } 321 | } 322 | 323 | /// Updates some entries in the index from the target commit tree. 324 | /// The scope of the updated entries is determined by the paths being passed in the `pathspec` parameters. 325 | /// 326 | /// Passing a nil commit will result in removing entries in the index matching the provided pathspecs. 327 | /// 328 | /// - See: git_reset_default 329 | func reset(pathspecs: [String], toCommit commit: Commit?) throws { 330 | let paths = StrArray(pathspecs) 331 | try git_try("reset pathspecs to commit") { git_reset_default(self.repository, commit?.commit, &paths.strarray) } 332 | } 333 | } 334 | 335 | // MARK: Checkout 336 | /// - See: git2/checkout.h 337 | public extension Repository { 338 | 339 | public struct CheckoutOptions { 340 | 341 | var strategy: Strategy 342 | var paths: [String]? { 343 | didSet { 344 | if let paths = paths { 345 | pathsStrArray = StrArray(paths) 346 | } else { 347 | pathsStrArray = nil 348 | } 349 | } 350 | } 351 | private var pathsStrArray: StrArray? 352 | 353 | public struct Strategy: OptionSet { 354 | public let rawValue: UInt32 355 | public init(rawValue: UInt32) { self.rawValue = rawValue } 356 | 357 | /// Default value. A dry run. No actual updates 358 | public static let none = Strategy(rawValue: GIT_CHECKOUT_NONE.rawValue) 359 | /// Allow safe updates that cannot overwrite uncommitted data 360 | public static let safe = Strategy(rawValue: GIT_CHECKOUT_SAFE.rawValue) 361 | /// Allow all updates to force working directory to look like index 362 | public static let force = Strategy(rawValue: GIT_CHECKOUT_FORCE.rawValue) 363 | /// Allow checkout to recreate missing files 364 | public static let recreateMissingFiles = Strategy(rawValue: GIT_CHECKOUT_RECREATE_MISSING.rawValue) 365 | /// Allow checkout to make safe updates even if conflicts are found 366 | public static let allowConflicts = Strategy(rawValue: GIT_CHECKOUT_ALLOW_CONFLICTS.rawValue) 367 | /// Remove untracked files not in index (that are not ignored) 368 | public static let removeUntracked = Strategy(rawValue: GIT_CHECKOUT_REMOVE_UNTRACKED.rawValue) 369 | /// Remove ignored files that are not in index 370 | public static let removeIgnored = Strategy(rawValue: GIT_CHECKOUT_REMOVE_IGNORED.rawValue) 371 | /// Only update existing files, don't create new ones 372 | public static let updateOnly = Strategy(rawValue: GIT_CHECKOUT_UPDATE_ONLY.rawValue) 373 | /// Normally checkout updates index entries as it goes; this stops that. Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`. 374 | public static let dontUpdateIndex = Strategy(rawValue: GIT_CHECKOUT_DONT_UPDATE_INDEX.rawValue) 375 | /// Don't refresh index/config/etc before doing checkout 376 | public static let noRefresh = Strategy(rawValue: GIT_CHECKOUT_NO_REFRESH.rawValue) 377 | /// Allow checkout to skip unmerged files 378 | public static let skipUnmerged = Strategy(rawValue: GIT_CHECKOUT_SKIP_UNMERGED.rawValue) 379 | /// For unmerged files, checkout stage 2 from index 380 | public static let useOurs = Strategy(rawValue: GIT_CHECKOUT_USE_OURS.rawValue) 381 | /// For unmerged files, checkout stage 3 from index 382 | public static let useTheirs = Strategy(rawValue: GIT_CHECKOUT_USE_THEIRS.rawValue) 383 | /// Treat pathspec as simple list of exact match file paths 384 | public static let disablePathspecMatch = Strategy(rawValue: GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH.rawValue) 385 | /// Ignore directories in use, they will be left empty 386 | public static let skipLockedDirectories = Strategy(rawValue: GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES.rawValue) 387 | /// Don't overwrite ignored files that exist in the checkout target 388 | public static let dontOverwriteIgnored = Strategy(rawValue: GIT_CHECKOUT_DONT_OVERWRITE_IGNORED.rawValue) 389 | /// Write normal merge files for conflicts 390 | public static let conflictStyleMerge = Strategy(rawValue: GIT_CHECKOUT_CONFLICT_STYLE_MERGE.rawValue) 391 | /// Include common ancestor data in diff3 format files for conflicts 392 | public static let conflictStyleDiff3 = Strategy(rawValue: GIT_CHECKOUT_CONFLICT_STYLE_DIFF3.rawValue) 393 | /// Don't overwrite existing files and folders 394 | public static let dontRemoveExisting = Strategy(rawValue: GIT_CHECKOUT_DONT_REMOVE_EXISTING.rawValue) 395 | /// Normally checkout writes the index upon completion; This prevents that. 396 | public static let dontWriteIndex = Strategy(rawValue: GIT_CHECKOUT_DONT_WRITE_INDEX.rawValue) 397 | 398 | // updateSubmodules : Not implemented 399 | // updateSubmodulesIfChanged : Not implemented 400 | } 401 | 402 | public init(strategy: Strategy = .none, paths: [String]? = nil) { 403 | self.strategy = strategy; self.paths = paths; 404 | if let paths = paths { 405 | self.pathsStrArray = StrArray(paths) 406 | } 407 | } 408 | 409 | internal var options: git_checkout_options { 410 | var options = git_checkout_options.init() 411 | git_checkout_init_options(&options, UInt32(GIT_CHECKOUT_OPTIONS_VERSION)) 412 | 413 | options.checkout_strategy = strategy.rawValue 414 | if let paths = pathsStrArray { 415 | options.paths = paths.strarray 416 | } 417 | 418 | return options 419 | } 420 | } 421 | 422 | func checkout(reference: Reference, options: CheckoutOptions) throws { 423 | let obj = try reference.peel() 424 | var checkoutOptions = options.options 425 | 426 | try git_try("checkout tree") { git_checkout_tree(self.repository, obj, &checkoutOptions)} 427 | try git_try("move head") { git_repository_set_head(self.repository, reference.name.cString(using: .utf8)) } 428 | } 429 | 430 | /// Updates files in the index and the working tree to match the content of the commit pointed at by HEAD. 431 | /// - See: git_checkout_head 432 | func checkoutHead(options: CheckoutOptions) throws { 433 | var checkoutOptions = options.options 434 | 435 | try git_try("checkout HEAD") { git_checkout_head(self.repository, &checkoutOptions) } 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /Sources/Signature.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | /// Git signature creation 5 | /// - See: git2/signature.h 6 | public class Signature { 7 | internal var signature: UnsafePointer 8 | 9 | internal init(signature: UnsafePointer) { 10 | self.signature = signature 11 | } 12 | 13 | /// Create a new action signature. 14 | /// - See: git_signature_new 15 | public convenience init(name: String, email: String, time: Date = Date()) throws { 16 | let gitTime = time.gitTime() 17 | 18 | let signature = try git_try("create signature") { git_signature_new($0, name.cString(using: .utf8), email.cString(using: .utf8), gitTime.time, gitTime.offset) } 19 | 20 | self.init(signature: signature) 21 | } 22 | 23 | public var name: String { 24 | return String(cString: self.signature.pointee.name) 25 | } 26 | 27 | public var email: String { 28 | return String(cString: self.signature.pointee.email) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Status.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_status_list = OpaquePointer 5 | 6 | /// Git file status 7 | /// - See: git2/status.h 8 | 9 | /// Status flags for a single file. 10 | /// - See: git_status_t 11 | public struct Status: OptionSet { 12 | public let rawValue: UInt32 13 | public init(rawValue: UInt32) { self.rawValue = rawValue } 14 | 15 | public static let none = Status(rawValue: GIT_STATUS_CURRENT.rawValue) 16 | 17 | public static let indexNew = Status(rawValue: GIT_STATUS_INDEX_NEW.rawValue) 18 | public static let indexModified = Status(rawValue: GIT_STATUS_INDEX_MODIFIED.rawValue) 19 | public static let indexDeleted = Status(rawValue: GIT_STATUS_INDEX_DELETED.rawValue) 20 | public static let indexRenamed = Status(rawValue: GIT_STATUS_INDEX_RENAMED.rawValue) 21 | public static let indexTypeChanged = Status(rawValue: GIT_STATUS_INDEX_TYPECHANGE.rawValue) 22 | 23 | public static let workingDirectoryNew = Status(rawValue: GIT_STATUS_WT_NEW.rawValue) 24 | public static let workingDirectoryModified = Status(rawValue: GIT_STATUS_WT_MODIFIED.rawValue) 25 | public static let workingDirectoryDeleted = Status(rawValue: GIT_STATUS_WT_DELETED.rawValue) 26 | public static let workingDirectoryRenamed = Status(rawValue: GIT_STATUS_WT_RENAMED.rawValue) 27 | public static let workingDirectoryTypeChanged = Status(rawValue: GIT_STATUS_WT_TYPECHANGE.rawValue) 28 | 29 | public static let ignored = Status(rawValue: GIT_STATUS_IGNORED.rawValue) 30 | public static let conflicted = Status(rawValue: GIT_STATUS_CONFLICTED.rawValue) 31 | 32 | /// Flags to control status callbacks 33 | /// - See: git_status_opt_t 34 | public struct Options: OptionSet { 35 | public let rawValue: UInt32 36 | public init(rawValue: UInt32) { self.rawValue = rawValue } 37 | 38 | /// Callbacks should be made on untracked files. These will only be made if the workdir files are included in the status "show" option. 39 | public static let includeUntracked = Options(rawValue: GIT_STATUS_OPT_INCLUDE_UNTRACKED.rawValue) 40 | /// Ignored files should get callbacks. Again, these callbacks will only be made if the workdir files are included in the status "show" option. 41 | public static let includeIgnored = Options(rawValue: GIT_STATUS_OPT_INCLUDE_IGNORED.rawValue) 42 | /// Callback should be made even on unmodified files. 43 | public static let includeUnmodified = Options(rawValue: GIT_STATUS_OPT_INCLUDE_UNMODIFIED.rawValue) 44 | /// Submodules should be skipped. This only applies if there are no pending typechanges to the submodule (either from or to another type). 45 | public static let excludeSubmodules = Options(rawValue: GIT_STATUS_OPT_EXCLUDE_SUBMODULES.rawValue) 46 | /// The given path should be treated as a literal path, and not as a pathspec pattern. 47 | public static let disablePathspecMatch = Options(rawValue: GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH.rawValue) 48 | /// All files in untracked directories should be included. Normally if an entire directory is new, then just the top-level directory is included (with a trailing slash on the entry name). This flag says to include all of the individual files in the directory instead. 49 | public static let recurseUntrackedDirectories = Options(rawValue: GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS.rawValue) 50 | /// The contents of ignored directories should be included in the status. This is like doing `git ls-files -o -i --exclude-standard` with core git. 51 | public static let recurseIgnoredDirectories = Options(rawValue: GIT_STATUS_OPT_RECURSE_IGNORED_DIRS.rawValue) 52 | /// Rename detection should be processed between the head and the index and enables the GIT_STATUS_INDEX_RENAMED as a possible status flag. 53 | public static let renamesHeadToIndex = Options(rawValue: GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX.rawValue) 54 | /// Rename detection should be run between the index and the working directory and enabled GIT_STATUS_WT_RENAMED as a possible status flag. 55 | public static let renamesIndexToWorkingDirectory = Options(rawValue: GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR.rawValue) 56 | /// Overrides the native case sensitivity for the file system and forces the output to be in case-sensitive order 57 | public static let sortCaseSensitively = Options(rawValue: GIT_STATUS_OPT_SORT_CASE_SENSITIVELY.rawValue) 58 | /// Overrides the native case sensitivity for the file system and forces the output to be in case-insensitive order 59 | public static let sortCaseInsenstively = Options(rawValue: GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY.rawValue) 60 | /// Rename detection should include rewritten files 61 | public static let renamesFromRewrites = Options(rawValue: GIT_STATUS_OPT_RENAMES_FROM_REWRITES.rawValue) 62 | /// Bypasses the default status behavior of doing a "soft" index reload (i.e. reloading the index data if the file on disk has been modified outside libgit2). 63 | public static let noRefresh = Options(rawValue: GIT_STATUS_OPT_NO_REFRESH.rawValue) 64 | /// Refresh the stat cache in the index for files that are unchanged but have out of date stat information in the index. It will result in less work being done on subsequent calls to get status. This is mutually exclusive with the NO_REFRESH option. 65 | public static let updateIndex = Options(rawValue: GIT_STATUS_OPT_UPDATE_INDEX.rawValue) 66 | public static let includeUnreadable = Options(rawValue: GIT_STATUS_OPT_INCLUDE_UNREADABLE.rawValue) 67 | public static let includeUnreadableAsUntracked = Options(rawValue: GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED.rawValue) 68 | } 69 | 70 | /// Select the files on which to report status. 71 | /// - See: git_status_show_t 72 | public enum ShowOptions { 73 | 74 | /// Only gives status based on HEAD to index comparison, not looking at working directory changes. 75 | case index 76 | /// Only gives status based on index to working directory comparison, not comparing the index to the HEAD. 77 | case workingDirectory 78 | /// The default. 79 | case indexAndWorkingDirectory 80 | 81 | internal var showOptions: git_status_show_t { 82 | switch self { 83 | case .index: return GIT_STATUS_SHOW_INDEX_ONLY 84 | case .workingDirectory: return GIT_STATUS_SHOW_WORKDIR_ONLY 85 | case .indexAndWorkingDirectory: return GIT_STATUS_SHOW_INDEX_AND_WORKDIR 86 | } 87 | } 88 | } 89 | } 90 | 91 | /// A status entry, providing the differences between the file as it exists in HEAD and the index, and providing the differences between the index and the working directory. 92 | /// - See: git2/status.h 93 | public class StatusEntry { 94 | internal let entry: UnsafePointer 95 | 96 | internal init(entry: UnsafePointer) { 97 | self.entry = entry 98 | } 99 | 100 | /// Provides the status flags for this file 101 | public var status: Status { 102 | return Status(rawValue: entry.pointee.status.rawValue) 103 | } 104 | 105 | /// Provides detailed information about the differences between the file in HEAD and the file in the index. 106 | public var diffToIndex: DiffDelta? { 107 | if let diffToIndex = entry.pointee.head_to_index { 108 | return DiffDelta(delta: diffToIndex) 109 | } 110 | return nil 111 | } 112 | 113 | /// Provides detailed information about the differences between the file in the index and the file in the working directory. 114 | public var diffToWorkingDirectory: DiffDelta? { 115 | if let diffToWorkingDirectory = entry.pointee.index_to_workdir { 116 | return DiffDelta(delta: diffToWorkingDirectory) 117 | } 118 | return nil 119 | } 120 | } 121 | 122 | public class StatusList: Collection { 123 | public typealias Index = Int 124 | 125 | internal let list: git_status_list 126 | 127 | /// Options to control how `git_status_foreach_ext()` will issue callbacks. 128 | public struct Options { 129 | 130 | /// Control which files to scan and in what order. 131 | let show: Status.ShowOptions 132 | 133 | /// Options to apply to the operation 134 | let flags: Status.Options 135 | 136 | /// Array of path patterns to match, or just an array of paths to match exactly if pathspec matching is disabled. 137 | let pathspec: [String]? 138 | internal let pathspecStrArray: StrArray? 139 | 140 | public init(show: Status.ShowOptions = .indexAndWorkingDirectory, flags: Status.Options, pathspec: [String]? = nil) { 141 | self.show = show; self.flags = flags; self.pathspec = pathspec; 142 | if let pathspec = pathspec { 143 | self.pathspecStrArray = StrArray(pathspec) 144 | } else { 145 | self.pathspecStrArray = nil 146 | } 147 | } 148 | } 149 | 150 | public init(repository: Repository, options: Options? = nil) throws { 151 | var statusOptions = git_status_options.init() 152 | git_status_init_options(&statusOptions, UInt32(GIT_STATUS_OPTIONS_VERSION)) 153 | 154 | 155 | if let options = options { 156 | statusOptions.show = options.show.showOptions 157 | statusOptions.flags = options.flags.rawValue 158 | if let pathspec = options.pathspecStrArray { 159 | statusOptions.pathspec = pathspec.strarray 160 | } 161 | } 162 | 163 | self.list = try git_try("create git status list for repository", { git_status_list_new($0, repository.repository, &statusOptions) }) 164 | } 165 | 166 | deinit { 167 | git_status_list_free(list) 168 | } 169 | 170 | public var startIndex: Int { return 0 } 171 | public var endIndex: Int { return count } 172 | public func index(after i: Int) -> Int { return i + 1 } 173 | 174 | /// Number of entries in the list. 175 | public var count: Int { 176 | return git_status_list_entrycount(list) 177 | } 178 | 179 | /// Retrieve the entry at a given index. 180 | public subscript(index: Int) -> StatusEntry { 181 | return StatusEntry(entry: git_status_byindex(list, index)!) 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /Sources/Tree.swift: -------------------------------------------------------------------------------- 1 | import libgit2 2 | import Foundation 3 | 4 | internal typealias git_tree = OpaquePointer 5 | 6 | /// Git tree parsing, loading 7 | /// - See: git2/tree.h 8 | public class Tree { 9 | internal let tree: git_tree 10 | 11 | internal init(tree: git_tree) { 12 | self.tree = tree 13 | } 14 | 15 | deinit { 16 | git_tree_free(self.tree) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/GitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitTests.swift 3 | // GitTests 4 | // 5 | // Created by Ian McDowell on 12/30/17. 6 | // Copyright © 2017 Ian McDowell. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Git 11 | 12 | class GitTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /build.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Git 7 | build 8 | 9 | buildSystem 10 | xcode 11 | buildArgs 12 | 13 | -project 14 | Git.xcodeproj 15 | -target 16 | Git 17 | 18 | outputs 19 | 20 | Git.framework 21 | 22 | 23 | dependencies 24 | 25 | 26 | type 27 | github 28 | path 29 | IMcD23/libgit2-ios 30 | branch 31 | master 32 | 33 | 34 | 35 | 36 | --------------------------------------------------------------------------------