From 433ef719eb78ebce91a8ca4171b298d523e12051 Mon Sep 17 00:00:00 2001 From: Tom Booth Date: Tue, 23 Feb 2016 15:19:07 +0000 Subject: [PATCH 1/9] Correct the dependency between openssl and libssh2 Previously, libgit2 was depending on both openssl and libssh2. This was incorrect as libssh2 requires openssl for its build phase. I have moved the openssl dependency back to libssh2. I was testing using the scripts/cibuild executable, which masks any dependency issues with openssl as it prebuilds it. This prebuilding was done to get around TravisCI timeouts and so can't be removed to stop this being missed again. --- SwiftGit2.xcodeproj/project.pbxproj | 52 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/SwiftGit2.xcodeproj/project.pbxproj b/SwiftGit2.xcodeproj/project.pbxproj index a53cc63..81ed635 100644 --- a/SwiftGit2.xcodeproj/project.pbxproj +++ b/SwiftGit2.xcodeproj/project.pbxproj @@ -193,20 +193,6 @@ remoteGlobalIDString = 621E66611C72958800A0F352; remoteInfo = "SwiftGit2-iOS"; }; - 621E66F71C729F0200A0F352 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BEB31F1A1A0D6F7A00F525B9 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 621E66E71C729EB800A0F352; - remoteInfo = "OpenSSL-iOS"; - }; - 621E66F91C729F0200A0F352 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BEB31F1A1A0D6F7A00F525B9 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 621E66ED1C729EBB00A0F352; - remoteInfo = "libssh2-iOS"; - }; 621E66FB1C72A25D00A0F352 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BEB31F1A1A0D6F7A00F525B9 /* Project object */; @@ -214,6 +200,20 @@ remoteGlobalIDString = 621E66DC1C729CE500A0F352; remoteInfo = "libgit2-iOS"; }; + 624349871C7CADCD0087C234 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BEB31F1A1A0D6F7A00F525B9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 621E66ED1C729EBB00A0F352; + remoteInfo = "libssh2-iOS"; + }; + 624349891C7CADD90087C234 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BEB31F1A1A0D6F7A00F525B9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 621E66E71C729EB800A0F352; + remoteInfo = "OpenSSL-iOS"; + }; BEB31F301A0D6F7A00F525B9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BEB31F1A1A0D6F7A00F525B9 /* Project object */; @@ -873,8 +873,7 @@ buildRules = ( ); dependencies = ( - 621E66F81C729F0200A0F352 /* PBXTargetDependency */, - 621E66FA1C729F0200A0F352 /* PBXTargetDependency */, + 624349881C7CADCD0087C234 /* PBXTargetDependency */, ); name = "libgit2-iOS"; productName = libgit2; @@ -905,6 +904,7 @@ buildRules = ( ); dependencies = ( + 6243498A1C7CADD90087C234 /* PBXTargetDependency */, ); name = "libssh2-iOS"; productName = libgit2; @@ -1157,21 +1157,21 @@ target = 621E66611C72958800A0F352 /* SwiftGit2-iOS */; targetProxy = 621E66E41C729D8A00A0F352 /* PBXContainerItemProxy */; }; - 621E66F81C729F0200A0F352 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 621E66E71C729EB800A0F352 /* OpenSSL-iOS */; - targetProxy = 621E66F71C729F0200A0F352 /* PBXContainerItemProxy */; - }; - 621E66FA1C729F0200A0F352 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 621E66ED1C729EBB00A0F352 /* libssh2-iOS */; - targetProxy = 621E66F91C729F0200A0F352 /* PBXContainerItemProxy */; - }; 621E66FC1C72A25D00A0F352 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 621E66DC1C729CE500A0F352 /* libgit2-iOS */; targetProxy = 621E66FB1C72A25D00A0F352 /* PBXContainerItemProxy */; }; + 624349881C7CADCD0087C234 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 621E66ED1C729EBB00A0F352 /* libssh2-iOS */; + targetProxy = 624349871C7CADCD0087C234 /* PBXContainerItemProxy */; + }; + 6243498A1C7CADD90087C234 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 621E66E71C729EB800A0F352 /* OpenSSL-iOS */; + targetProxy = 624349891C7CADD90087C234 /* PBXContainerItemProxy */; + }; BEB31F311A0D6F7A00F525B9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BEB31F221A0D6F7A00F525B9 /* SwiftGit2-OSX */; From 8ea6f56fa01bdd5ac70e314f50eb5f94e3505d4c Mon Sep 17 00:00:00 2001 From: Tom Booth Date: Fri, 26 Feb 2016 16:15:37 +0000 Subject: [PATCH 2/9] Upgrade libgit2 to v0.23.4 The main reason for the upgrade is to enable use of SecureTransport for https transport. You can find a full diff here: https://github.com/libgit2/libgit2/compare/7c63a33ffe1198b77b481974cd0e74e9ace1745c...e8feafe32007ebd16a61820c70abd221655d053c and the changelog changes: https://github.com/libgit2/libgit2/compare/7c63a33ffe1198b77b481974cd0e74e9ace1745c...e8feafe32007ebd16a61820c70abd221655d053c#diff-4ac32a78649ca5bdd8e0ba38b7006a1e --- External/libgit2 | 2 +- SwiftGit2.xcodeproj/project.pbxproj | 8 ++------ SwiftGit2/SwiftGit2.modulemap | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/External/libgit2 b/External/libgit2 index 7c63a33..e8feafe 160000 --- a/External/libgit2 +++ b/External/libgit2 @@ -1 +1 @@ -Subproject commit 7c63a33ffe1198b77b481974cd0e74e9ace1745c +Subproject commit e8feafe32007ebd16a61820c70abd221655d053c diff --git a/SwiftGit2.xcodeproj/project.pbxproj b/SwiftGit2.xcodeproj/project.pbxproj index a53cc63..5b0da26 100644 --- a/SwiftGit2.xcodeproj/project.pbxproj +++ b/SwiftGit2.xcodeproj/project.pbxproj @@ -38,7 +38,6 @@ 621E66811C72958800A0F352 /* pack.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBC1AA6A7E200AFE62D /* pack.h */; }; 621E66821C72958800A0F352 /* patch.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBD1AA6A7E200AFE62D /* patch.h */; }; 621E66831C72958800A0F352 /* pathspec.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBE1AA6A7E200AFE62D /* pathspec.h */; }; - 621E66841C72958800A0F352 /* push.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBF1AA6A7E200AFE62D /* push.h */; }; 621E66851C72958800A0F352 /* rebase.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDC01AA6A7E200AFE62D /* rebase.h */; }; 621E66861C72958800A0F352 /* refdb.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDC11AA6A7E200AFE62D /* refdb.h */; }; 621E66871C72958800A0F352 /* reflog.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDC21AA6A7E200AFE62D /* reflog.h */; }; @@ -143,7 +142,6 @@ BE8DEE6D1AA6A8AD00AFE62D /* pack.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBC1AA6A7E200AFE62D /* pack.h */; }; BE8DEE6E1AA6A8AD00AFE62D /* patch.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBD1AA6A7E200AFE62D /* patch.h */; }; BE8DEE6F1AA6A8AD00AFE62D /* pathspec.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBE1AA6A7E200AFE62D /* pathspec.h */; }; - BE8DEE701AA6A8AD00AFE62D /* push.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDBF1AA6A7E200AFE62D /* push.h */; }; BE8DEE711AA6A8AD00AFE62D /* rebase.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDC01AA6A7E200AFE62D /* rebase.h */; }; BE8DEE721AA6A8AD00AFE62D /* refdb.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDC11AA6A7E200AFE62D /* refdb.h */; }; BE8DEE731AA6A8AD00AFE62D /* reflog.h in Copy libgit2 Headers */ = {isa = PBXBuildFile; fileRef = BE8DEDC21AA6A7E200AFE62D /* reflog.h */; }; @@ -268,7 +266,6 @@ 621E66811C72958800A0F352 /* pack.h in Copy libgit2 Headers */, 621E66821C72958800A0F352 /* patch.h in Copy libgit2 Headers */, 621E66831C72958800A0F352 /* pathspec.h in Copy libgit2 Headers */, - 621E66841C72958800A0F352 /* push.h in Copy libgit2 Headers */, 621E66851C72958800A0F352 /* rebase.h in Copy libgit2 Headers */, 621E66861C72958800A0F352 /* refdb.h in Copy libgit2 Headers */, 621E66871C72958800A0F352 /* reflog.h in Copy libgit2 Headers */, @@ -336,7 +333,6 @@ BE8DEE6D1AA6A8AD00AFE62D /* pack.h in Copy libgit2 Headers */, BE8DEE6E1AA6A8AD00AFE62D /* patch.h in Copy libgit2 Headers */, BE8DEE6F1AA6A8AD00AFE62D /* pathspec.h in Copy libgit2 Headers */, - BE8DEE701AA6A8AD00AFE62D /* push.h in Copy libgit2 Headers */, BE8DEE711AA6A8AD00AFE62D /* rebase.h in Copy libgit2 Headers */, BE8DEE721AA6A8AD00AFE62D /* refdb.h in Copy libgit2 Headers */, BE8DEE731AA6A8AD00AFE62D /* reflog.h in Copy libgit2 Headers */, @@ -427,7 +423,6 @@ BE8DEDBC1AA6A7E200AFE62D /* pack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pack.h; path = git2/pack.h; sourceTree = ""; }; BE8DEDBD1AA6A7E200AFE62D /* patch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = patch.h; path = git2/patch.h; sourceTree = ""; }; BE8DEDBE1AA6A7E200AFE62D /* pathspec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pathspec.h; path = git2/pathspec.h; sourceTree = ""; }; - BE8DEDBF1AA6A7E200AFE62D /* push.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = push.h; path = git2/push.h; sourceTree = ""; }; BE8DEDC01AA6A7E200AFE62D /* rebase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rebase.h; path = git2/rebase.h; sourceTree = ""; }; BE8DEDC11AA6A7E200AFE62D /* refdb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = refdb.h; path = git2/refdb.h; sourceTree = ""; }; BE8DEDC21AA6A7E200AFE62D /* reflog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reflog.h; path = git2/reflog.h; sourceTree = ""; }; @@ -607,7 +602,6 @@ BE8DEDBC1AA6A7E200AFE62D /* pack.h */, BE8DEDBD1AA6A7E200AFE62D /* patch.h */, BE8DEDBE1AA6A7E200AFE62D /* pathspec.h */, - BE8DEDBF1AA6A7E200AFE62D /* push.h */, BE8DEDC01AA6A7E200AFE62D /* rebase.h */, BE8DEDC11AA6A7E200AFE62D /* refdb.h */, BE8DEDC21AA6A7E200AFE62D /* reflog.h */, @@ -1524,6 +1518,7 @@ /usr/local/lib/libssh2.a, "-lcrypto", "-lssl", + "-lcurl", ); PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftGit2; @@ -1555,6 +1550,7 @@ /usr/local/lib/libssh2.a, "-lcrypto", "-lssl", + "-lcurl", ); PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftGit2; diff --git a/SwiftGit2/SwiftGit2.modulemap b/SwiftGit2/SwiftGit2.modulemap index 2e2d4b8..2289f9e 100644 --- a/SwiftGit2/SwiftGit2.modulemap +++ b/SwiftGit2/SwiftGit2.modulemap @@ -36,7 +36,6 @@ framework module SwiftGit2 { header "git2/pack.h" header "git2/patch.h" header "git2/pathspec.h" - header "git2/push.h" header "git2/rebase.h" header "git2/refdb.h" header "git2/reflog.h" From 1292c6a78f24df489e1d5fb5cfc8bda05b30a5de Mon Sep 17 00:00:00 2001 From: Tom Booth Date: Thu, 3 Mar 2016 14:13:03 +0000 Subject: [PATCH 3/9] Upgrade Quick to v0.9.1 This improves the quality of the test output, as noted by @modocache in https://github.com/SwiftGit2/SwiftGit2/pull/52 A full diff of the changes to Quick can be found here: https://github.com/Quick/Quick/compare/v0.8.0...v0.9.1 --- Cartfile.private | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Quick | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 2121ad9..e299b6c 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,5 +1,5 @@ github "jspahrsummers/xcconfigs" >= 0.7.2 -github "Quick/Quick" "v0.8.0" +github "Quick/Quick" "v0.9.1" github "Quick/Nimble" ~> 3.0.0 github "modocache/Guanaco" "5031bf67297afbe61ac0f2fbf3e3e8400b3f8888" github "ZipArchive/ZipArchive" ~> 1.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index 652edea..24f5043 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,6 +1,6 @@ github "modocache/Guanaco" "5031bf67297afbe61ac0f2fbf3e3e8400b3f8888" github "Quick/Nimble" "v3.0.0" -github "Quick/Quick" "v0.8.0" +github "Quick/Quick" "v0.9.1" github "antitypical/Result" "1.0.1" github "ZipArchive/ZipArchive" "v1.1" github "jspahrsummers/xcconfigs" "0.8.1" diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 46b38c9..2f03756 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 46b38c9c06b068baede09586aa281a6f075b2494 +Subproject commit 2f037560be197f0f5ae992512549bc29fabb3818 From 281d1aed2bce43239c0d79c135aaf238f9bcf422 Mon Sep 17 00:00:00 2001 From: Tom Booth Date: Tue, 1 Mar 2016 19:24:11 +0000 Subject: [PATCH 4/9] Fix EXC_BAD_ACCESS when progress updates Previously when a block was passed into checkout() it would be copied but then the memory would be freed as nothing had retained a reference to the copy. This meant that when block() was called in SG2CheckoutProgressCallback() it would crash due to an attempt to execute a random chunk of memory that previously contained the block. This code now retains the payload after copying it. --- SwiftGit2/SwiftGit2.m | 2 +- SwiftGit2Tests/RepositorySpec.swift | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/SwiftGit2/SwiftGit2.m b/SwiftGit2/SwiftGit2.m index 0e5a55c..9fa199c 100644 --- a/SwiftGit2/SwiftGit2.m +++ b/SwiftGit2/SwiftGit2.m @@ -29,7 +29,7 @@ git_checkout_options SG2CheckoutOptions(SG2CheckoutProgressBlock progress) { if (progress != nil) { result.progress_cb = SG2CheckoutProgressCallback; - result.progress_payload = (__bridge void *)[progress copy]; + result.progress_payload = (__bridge_retained void *)[progress copy]; } return result; diff --git a/SwiftGit2Tests/RepositorySpec.swift b/SwiftGit2Tests/RepositorySpec.swift index c499824..a0fa179 100644 --- a/SwiftGit2Tests/RepositorySpec.swift +++ b/SwiftGit2Tests/RepositorySpec.swift @@ -475,10 +475,24 @@ class RepositorySpec: QuickSpec { let HEAD = repo.HEAD().value expect(HEAD?.longName).to(equal("HEAD")) expect(HEAD?.oid).to(equal(oid)) - + expect(repo.checkout(repo.localBranchWithName("master").value!, strategy: CheckoutStrategy.None)).to(haveSucceeded()) expect(repo.HEAD().value?.shortName).to(equal("master")) } + + it("should call block on progress") { + let repo = Fixtures.simpleRepository + let oid = OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")! + expect(repo.HEAD().value?.shortName).to(equal("master")) + + expect(repo.checkout(oid, strategy: .None, progress: { (path, completedSteps, totalSteps) -> Void in + expect(completedSteps).to(beLessThanOrEqualTo(totalSteps)) + })).to(haveSucceeded()) + + let HEAD = repo.HEAD().value + expect(HEAD?.longName).to(equal("HEAD")) + expect(HEAD?.oid).to(equal(oid)) + } } describe("Repository.checkout(ReferenceType)") { From 393414fef6ad02e56dd9a7e3aa93642106099e3e Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Tue, 8 Mar 2016 21:14:51 +0100 Subject: [PATCH 5/9] Update println > print --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a18fd33..49b47fa 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ if let repo = repo.value { .HEAD() .flatMap { repo.commitWithOID($0.oid) } if let commit = latestCommit.value { - println("Latest Commit: \(commit.message) by \(commit.author.name)") + print("Latest Commit: \(commit.message) by \(commit.author.name)") } else { - println("Could not get commit: \(latestCommit.error)") + print("Could not get commit: \(latestCommit.error)") } } else { println("Could not open repository: \(repo.error)") From 876cd3edc1655c7bd24d8e926060a0945458faf6 Mon Sep 17 00:00:00 2001 From: "Dr. Kibitz" Date: Fri, 18 Mar 2016 23:34:30 -0700 Subject: [PATCH 6/9] Squashed commit of the following: commit 1f2cc37d17c534d1df30824c8c462572dbd006df Author: Dr. Kibitz Date: Fri Mar 18 23:30:46 2016 -0700 Check for progress completion to destroy/dealloc callback pointer - Also some general cleanup commit 792bb9d8a587b6d83dc9fd8e6119d6c79a23a679 Author: Dr. Kibitz Date: Fri Mar 18 10:05:03 2016 -0700 this is a progress callback, don't clean up anything here commit ce3c88058716089f511c5dd64c8c40813df783e9 Author: Dr. Kibitz Date: Fri Mar 18 00:45:22 2016 -0700 remove one more uneeded type commit 21b586ac57640d605af382d2b0f57d92900ab18e Author: Dr. Kibitz Date: Fri Mar 18 00:44:46 2016 -0700 Use move instead commit 88630c94dbcf0b91e0223f45897168736044297a Author: Dr. Kibitz Date: Fri Mar 18 00:40:16 2016 -0700 address comments, dealloc after UnsafeMutablePointer alloc commit e0fcfb82491dda4db815554a07b4c933d48ae420 Author: Dr. Kibitz Date: Fri Mar 18 00:39:51 2016 -0700 add git2.h back as public header commit 79287c952d1e288c5426b5ee1c4b3a5f9ab4efe7 Author: Dr. Kibitz Date: Sun Mar 13 18:00:18 2016 -0700 Remove the implementation detail leak, implemented in swift --- SwiftGit2/Repository.swift | 41 ++++++++++++++++++++++++++++++++++++-- SwiftGit2/SwiftGit2.h | 8 -------- SwiftGit2/SwiftGit2.m | 23 +-------------------- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/SwiftGit2/Repository.swift b/SwiftGit2/Repository.swift index f5f2205..b00171c 100644 --- a/SwiftGit2/Repository.swift +++ b/SwiftGit2/Repository.swift @@ -9,7 +9,44 @@ import Foundation import Result -public typealias CheckoutProgressBlock = SG2CheckoutProgressBlock +public typealias CheckoutProgressBlock = (String?, Int, Int) -> Void + +/// Helper function used as the libgit2 progress callback in git_checkout_options. +/// This is a function with a type signature of git_checkout_progress_cb. +private func checkoutProgressCallback(path: UnsafePointer, completed_steps: Int, total_steps: Int, payload: UnsafeMutablePointer) -> Void { + if (payload != nil) { + let buffer = UnsafeMutablePointer(payload) + let block: CheckoutProgressBlock + if completed_steps < total_steps { + block = buffer.memory + } else { + block = buffer.move() + buffer.dealloc(1) + } + block((path == nil ? nil : String(path)), completed_steps, total_steps); + } +} + +/// Helper function for initializing libgit2 got_checkout_options. +/// +/// :param: progress A block that's called with the progress of the checkout. +/// :returns: Returns a git_checkout_options struct with the progress members set. +private func checkoutOptions(progress: CheckoutProgressBlock? = nil) -> git_checkout_options { + // Do this because GIT_CHECKOUT_OPTIONS_INIT is unavailable in swift + let pointer = UnsafeMutablePointer.alloc(1) + git_checkout_init_options(pointer, UInt32(GIT_CHECKOUT_OPTIONS_VERSION)) + var options = pointer.move() + pointer.dealloc(1) + + if progress != nil { + options.progress_cb = checkoutProgressCallback + let blockPointer = UnsafeMutablePointer.alloc(1) + blockPointer.initialize(progress!) + options.progress_payload = UnsafeMutablePointer(blockPointer) + } + + return options +} /// A git repository. final public class Repository { @@ -359,7 +396,7 @@ final public class Repository { /// :param: progress A block that's called with the progress of the checkout. /// :returns: Returns a result with void or the error that occurred. public func checkout(strategy strategy: CheckoutStrategy, progress: CheckoutProgressBlock? = nil) -> Result<(), NSError> { - var options = SG2CheckoutOptions(progress) + var options = checkoutOptions(progress) options.checkout_strategy = strategy.git_checkout_strategy.rawValue let result = git_checkout_head(self.pointer, &options) diff --git a/SwiftGit2/SwiftGit2.h b/SwiftGit2/SwiftGit2.h index 54943aa..59b2b64 100644 --- a/SwiftGit2/SwiftGit2.h +++ b/SwiftGit2/SwiftGit2.h @@ -15,11 +15,3 @@ FOUNDATION_EXPORT double SwiftGit2VersionNumber; FOUNDATION_EXPORT const unsigned char SwiftGit2VersionString[]; // In this header, you should import all the public headers of your framework using statements like #import - -#import "git2.h" - -typedef void (^SG2CheckoutProgressBlock)(NSString * __nullable, NSUInteger, NSUInteger); - -/// A C function for working with Libgit2. This shouldn't be called directly. It's an -/// implementation detail that, unfortunately, leaks through to the public headers. -extern git_checkout_options SG2CheckoutOptions(SG2CheckoutProgressBlock __nullable progress); diff --git a/SwiftGit2/SwiftGit2.m b/SwiftGit2/SwiftGit2.m index 9fa199c..3ac5099 100644 --- a/SwiftGit2/SwiftGit2.m +++ b/SwiftGit2/SwiftGit2.m @@ -7,30 +7,9 @@ // #import "SwiftGit2.h" - +#import "git2.h" __attribute__((constructor)) static void SwiftGit2Init(void) { git_libgit2_init(); } - -static void SG2CheckoutProgressCallback(const char *path, size_t completed_steps, size_t total_steps, void *payload) { - if (payload == NULL) return; - - SG2CheckoutProgressBlock block = (__bridge SG2CheckoutProgressBlock)payload; - block((path == nil ? nil : @(path)), completed_steps, total_steps); -} - -git_checkout_options SG2CheckoutOptions(SG2CheckoutProgressBlock progress) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-field-initializers" - git_checkout_options result = GIT_CHECKOUT_OPTIONS_INIT; -#pragma clang diagnostic pop - - if (progress != nil) { - result.progress_cb = SG2CheckoutProgressCallback; - result.progress_payload = (__bridge_retained void *)[progress copy]; - } - - return result; -} From 86fcebd10c743b3f1a84ca7c3a1736ee6c2ec510 Mon Sep 17 00:00:00 2001 From: "Dr. Kibitz" Date: Fri, 18 Mar 2016 23:56:44 -0700 Subject: [PATCH 7/9] Use fromCString --- SwiftGit2/Repository.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftGit2/Repository.swift b/SwiftGit2/Repository.swift index b00171c..ba72f1d 100644 --- a/SwiftGit2/Repository.swift +++ b/SwiftGit2/Repository.swift @@ -23,7 +23,7 @@ private func checkoutProgressCallback(path: UnsafePointer, completed_steps block = buffer.move() buffer.dealloc(1) } - block((path == nil ? nil : String(path)), completed_steps, total_steps); + block(String.fromCString(path), completed_steps, total_steps); } } From c3b155cfd4a8544de175258fedd551aea66218cd Mon Sep 17 00:00:00 2001 From: Tom Booth Date: Tue, 22 Mar 2016 22:26:40 +0000 Subject: [PATCH 8/9] Switch cibuild to xcodebuild and xcpretty `xctool` runs the tests as logic only whereas `xcodebuild` and XCode run them as application tests. When tests are running logic only a lot of the subsystems that would be available on the simulator are not there, for example keychain and networking. https://github.com/facebook/xctool/issues/269 https://github.com/AFNetworking/AFNetworking/pull/1707 https://github.com/facebook/xctool/issues/367 https://github.com/facebook/xctool/issues/553 --- script/bootstrap | 10 +++++++++- script/cibuild | 26 +++++++++++++------------- script/xcodebuild.awk | 16 ++-------------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 84b7dd6..92d7d75 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,9 +10,12 @@ config () { # A whitespace-separated list of executables that must be present and locatable. # These will each be installed through Homebrew if not found. - : ${REQUIRED_TOOLS="xctool cmake libssh2 libtool autoconf automake pkg-config"} + : ${REQUIRED_TOOLS="cmake libssh2 libtool autoconf automake pkg-config"} + : ${REQUIRED_GEMS="xcpretty"} export REQUIRED_TOOLS + export REQUIRED_GEMS + } ## @@ -102,6 +105,11 @@ check_deps () sudo ln -s "$brew_prefix/$product" "$destination" done fi + + for gem in $REQUIRED_GEMS + do + gem install "$gem" + done } bootstrap_submodule () diff --git a/script/cibuild b/script/cibuild index 2369fb1..dff90b0 100755 --- a/script/cibuild +++ b/script/cibuild @@ -13,7 +13,7 @@ config () # The workspace to build. # # If not set and no workspace is found, the -workspace flag will not be passed - # to `xctool`. + # to `xcodebuild`. # # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will # take precedence. @@ -22,7 +22,7 @@ config () # The project to build. # # If not set and no project is found, the -project flag will not be passed - # to `xctool`. + # to `xcodebuild`. # # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will # take precedence. @@ -33,8 +33,8 @@ config () # If this file does not exist, it is not considered an error. : ${BOOTSTRAP="$SCRIPT_DIR/bootstrap"} - # Extra options to pass to xctool. - : ${XCTOOL_OPTIONS="RUN_CLANG_STATIC_ANALYZER=NO"} + # Extra options to pass to xcodebuild. + : ${XCODEBUILD_OPTIONS="RUN_CLANG_STATIC_ANALYZER=NO"} # A whitespace-separated list of default schemes to build. # @@ -44,7 +44,7 @@ config () export XCWORKSPACE export XCODEPROJ export BOOTSTRAP - export XCTOOL_OPTIONS + export XCODEBUILD_OPTIONS export SCHEMES } @@ -86,17 +86,17 @@ find_pattern () ls -d $1 2>/dev/null | head -n 1 } -run_xctool () +run_xcodebuild () { if [ -n "$XCWORKSPACE" ] then - xctool -workspace "$XCWORKSPACE" $XCTOOL_OPTIONS "$@" \ + xcodebuild -workspace "$XCWORKSPACE" $XCODEBUILD_OPTIONS "$@" \ ONLY_ACTIVE_ARCH=NO \ CODE_SIGN_IDENTITY="" \ CODE_SIGNING_REQUIRED=NO 2>&1 elif [ -n "$XCODEPROJ" ] then - xctool -project "$XCODEPROJ" $XCTOOL_OPTIONS "$@" \ + xcodebuild -project "$XCODEPROJ" $XCODEBUILD_OPTIONS "$@" \ ONLY_ACTIVE_ARCH=NO \ CODE_SIGN_IDENTITY="" \ CODE_SIGNING_REQUIRED=NO 2>&1 @@ -108,7 +108,7 @@ run_xctool () parse_build () { - awk -f "$SCRIPT_DIR/xctool.awk" 2>&1 >/dev/null + awk -f "$SCRIPT_DIR/xcodebuild.awk" 2>&1 >/dev/null } build_scheme () @@ -122,7 +122,7 @@ build_scheme () local action=test # Determine whether we can run unit tests for this target. - run_xctool -scheme "$scheme" run-tests | parse_build + run_xcodebuild -scheme "$scheme" test | parse_build local awkstatus=$? @@ -132,7 +132,7 @@ build_scheme () sdkflags=(-sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 5") # Determine whether the unit tests will run with iphonesimulator - run_xctool "${sdkflags[@]}" -scheme "$scheme" run-tests | parse_build + run_xcodebuild "${sdkflags[@]}" -scheme "$scheme" tests | parse_build awkstatus=$? @@ -149,11 +149,11 @@ build_scheme () action=build fi - run_xctool "${sdkflags[@]}" -scheme "$scheme" $action + run_xcodebuild "${sdkflags[@]}" -scheme "$scheme" $action | xcpretty } export -f build_scheme -export -f run_xctool +export -f run_xcodebuild export -f parse_build main diff --git a/script/xcodebuild.awk b/script/xcodebuild.awk index c746b09..7258554 100644 --- a/script/xcodebuild.awk +++ b/script/xcodebuild.awk @@ -13,23 +13,11 @@ BEGIN { fflush(stdout); } -/is not valid for Testing/ { - exit 2; -} - -/[0-9]+: (error|warning):/ { - errors = errors $0 "\n"; -} - -/(TEST|BUILD) FAILED/ { - status = 1; +/A build only device cannot be used to run this target/ { + status = 1 } END { - if (length(errors) > 0) { - print "\n*** All errors:\n" errors; - } - fflush(stdout); exit status; } From 60cb395cc685baeffea610b2d655f61763f580bf Mon Sep 17 00:00:00 2001 From: Tom Booth Date: Wed, 24 Feb 2016 14:34:37 +0000 Subject: [PATCH 9/9] Add cloneFromURL function This introduces a new static function on Repository that will allow a user to clone a local or remote repository. At the moment only SSH in-memory, username + password and default (no creds) are implemented. It provides an enum wrapper around the underlying libgit2 `git_cred_t` type to abstract it away from the user into something that is a little more Swift-like. As much of the C callback code and struct creation has been moved into Swift-land as well, this is now possible as of 2.* whereas before it was not. I had to use a wrapper class in Credentials.swift in order to convert blocks to pointers, as blocks do not implement the AnyObject protocol that `Unmanaged` requies. The test requires you passing through a set of environment variables otherwise it will not run. This is so that secret/private information isn't leaked into the repository. The required variables are as follows: - SG2TestPrivateRepo - the url of the private jrepo to clone; - SG2TestUsername - the url of the user that will be connecting; - SG2TestPublicKey - the public key data to be used; - SG2TestPrivateKey - the private key data; - SG2TestPassphrase - passphrase needed to use the private key (blank if none). --- SwiftGit2.xcodeproj/project.pbxproj | 6 ++ SwiftGit2/Credentials.swift | 50 ++++++++++++++++ SwiftGit2/Repository.swift | 83 ++++++++++++++++++++++++-- SwiftGit2Tests/RepositorySpec.swift | 91 +++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 SwiftGit2/Credentials.swift diff --git a/SwiftGit2.xcodeproj/project.pbxproj b/SwiftGit2.xcodeproj/project.pbxproj index 5c687cf..ccf3db5 100644 --- a/SwiftGit2.xcodeproj/project.pbxproj +++ b/SwiftGit2.xcodeproj/project.pbxproj @@ -94,6 +94,8 @@ 621E66E61C729D9600A0F352 /* SwiftGit2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 621E66B41C72958800A0F352 /* SwiftGit2.framework */; }; 621E66FE1C72A5FF00A0F352 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 621E66FD1C72A5FF00A0F352 /* libiconv.tbd */; }; 621E67001C72A60B00A0F352 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 621E66FF1C72A60B00A0F352 /* libz.tbd */; }; + 622726341C84E52500C53D17 /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = 622726331C84E52500C53D17 /* Credentials.swift */; }; + 622726351C84E52500C53D17 /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = 622726331C84E52500C53D17 /* Credentials.swift */; }; 62E6FD8F1C727E9C00A312B0 /* ZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62E6FD8E1C727E9C00A312B0 /* ZipArchive.framework */; }; BE0991F71A578FB1007D4E6A /* Mantle.zip in Resources */ = {isa = PBXBuildFile; fileRef = BE0991F61A578FB1007D4E6A /* Mantle.zip */; }; BE0B1C5D1A9978890004726D /* detached-head.zip in Resources */ = {isa = PBXBuildFile; fileRef = BE0B1C5C1A9978890004726D /* detached-head.zip */; }; @@ -374,6 +376,7 @@ 621E66F21C729EBB00A0F352 /* liblibssh2-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "liblibssh2-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 621E66FD1C72A5FF00A0F352 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.2.sdk/usr/lib/libiconv.tbd; sourceTree = DEVELOPER_DIR; }; 621E66FF1C72A60B00A0F352 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; + 622726331C84E52500C53D17 /* Credentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = ""; }; 62E6FD8E1C727E9C00A312B0 /* ZipArchive.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ZipArchive.framework; path = ../Carthage/Checkouts/ZipArchive/build/Debug/ZipArchive.framework; sourceTree = ""; }; BE0991F61A578FB1007D4E6A /* Mantle.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = Mantle.zip; sourceTree = ""; }; BE0B1C5C1A9978890004726D /* detached-head.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = "detached-head.zip"; sourceTree = ""; }; @@ -663,6 +666,7 @@ BEB31F281A0D6F7A00F525B9 /* SwiftGit2.h */, BE14AA4F1A1974010015B439 /* SwiftGit2.m */, BE276B281ACCD3CF00D6DAD7 /* CheckoutStrategy.swift */, + 622726331C84E52500C53D17 /* Credentials.swift */, DA5914751A94579000AED74C /* Errors.swift */, BE36354B1A632C9700D37EC8 /* Libgit2.swift */, BE2E3BE51A31261300C67092 /* Objects.swift */, @@ -1088,6 +1092,7 @@ 621E66A01C72958800A0F352 /* OID.swift in Sources */, 621E66A11C72958800A0F352 /* Remotes.swift in Sources */, 621E66A21C72958800A0F352 /* CheckoutStrategy.swift in Sources */, + 622726351C84E52500C53D17 /* Credentials.swift in Sources */, 621E66A31C72958800A0F352 /* Repository.swift in Sources */, 621E66A41C72958800A0F352 /* Objects.swift in Sources */, 621E66A51C72958800A0F352 /* References.swift in Sources */, @@ -1119,6 +1124,7 @@ BE70B3E51A1ACB1A002C3F4E /* OID.swift in Sources */, BECB5F6E1A57284700999413 /* Remotes.swift in Sources */, BE276B291ACCD3CF00D6DAD7 /* CheckoutStrategy.swift in Sources */, + 622726341C84E52500C53D17 /* Credentials.swift in Sources */, BEB31F6D1A0D78F300F525B9 /* Repository.swift in Sources */, BE2E3BE61A31261300C67092 /* Objects.swift in Sources */, BECB5F6A1A56F19900999413 /* References.swift in Sources */, diff --git a/SwiftGit2/Credentials.swift b/SwiftGit2/Credentials.swift new file mode 100644 index 0000000..b2273bd --- /dev/null +++ b/SwiftGit2/Credentials.swift @@ -0,0 +1,50 @@ +// +// Credentials.swift +// SwiftGit2 +// +// Created by Tom Booth on 29/02/2016. +// Copyright © 2016 GitHub, Inc. All rights reserved. +// + +import Foundation +import Result + +private class Wrapper { + let value: T + + init(_ value: T) { + self.value = value + } +} + +public enum Credentials { + case Default() + case Plaintext(username: String, password: String) + case SSHMemory(username: String, publicKey: String, privateKey: String, passphrase: String) + + internal static func fromPointer(pointer: UnsafeMutablePointer<()>) -> Credentials { + return Unmanaged>.fromOpaque(COpaquePointer(pointer)).takeRetainedValue().value + } + + internal func toPointer() -> UnsafeMutablePointer<()> { + return UnsafeMutablePointer(Unmanaged.passRetained(Wrapper(self)).toOpaque()) + } +} + +/// Handle the request of credentials, passing through to a wrapped block after converting the arguments. +/// Converts the result to the correct error code required by libgit2 (0 = success, 1 = rejected setting creds, -1 error) +internal func credentialsCallback(cred: UnsafeMutablePointer>, _: UnsafePointer, _: UnsafePointer, _: UInt32, + payload: UnsafeMutablePointer<()>) -> Int32 { + let result: Int32 + + switch Credentials.fromPointer(payload) { + case .Default(): + result = git_cred_default_new(cred) + case .Plaintext(let username, let password): + result = git_cred_userpass_plaintext_new(cred, username, password) + case .SSHMemory(let username, let publicKey, let privateKey, let passphrase): + result = git_cred_ssh_key_memory_new(cred, username, publicKey, privateKey, passphrase) + } + + return (result != GIT_OK.rawValue) ? -1 : 0 +} diff --git a/SwiftGit2/Repository.swift b/SwiftGit2/Repository.swift index ba72f1d..1fbe8d6 100644 --- a/SwiftGit2/Repository.swift +++ b/SwiftGit2/Repository.swift @@ -27,17 +27,20 @@ private func checkoutProgressCallback(path: UnsafePointer, completed_steps } } -/// Helper function for initializing libgit2 got_checkout_options. +/// Helper function for initializing libgit2 git_checkout_options. /// +/// :param: strategy The strategy to be used when checking out the repo, see CheckoutStrategy /// :param: progress A block that's called with the progress of the checkout. /// :returns: Returns a git_checkout_options struct with the progress members set. -private func checkoutOptions(progress: CheckoutProgressBlock? = nil) -> git_checkout_options { +private func checkoutOptions(strategy: CheckoutStrategy, progress: CheckoutProgressBlock? = nil) -> git_checkout_options { // Do this because GIT_CHECKOUT_OPTIONS_INIT is unavailable in swift let pointer = UnsafeMutablePointer.alloc(1) git_checkout_init_options(pointer, UInt32(GIT_CHECKOUT_OPTIONS_VERSION)) var options = pointer.move() pointer.dealloc(1) + options.checkout_strategy = strategy.git_checkout_strategy.rawValue + if progress != nil { options.progress_cb = checkoutProgressCallback let blockPointer = UnsafeMutablePointer.alloc(1) @@ -48,9 +51,50 @@ private func checkoutOptions(progress: CheckoutProgressBlock? = nil) -> git_chec return options } +private func fetchOptions(credentials: Credentials) -> git_fetch_options { + let pointer = UnsafeMutablePointer.alloc(1) + git_fetch_init_options(pointer, UInt32(GIT_FETCH_OPTIONS_VERSION)) + + var options = pointer.move() + + pointer.dealloc(1) + + options.callbacks.payload = credentials.toPointer() + options.callbacks.credentials = credentialsCallback + + return options +} + +private func cloneOptions(bare: Bool = false, localClone: Bool = false, fetchOptions: git_fetch_options? = nil, + checkoutOptions: git_checkout_options? = nil) -> git_clone_options { + + let pointer = UnsafeMutablePointer.alloc(1) + git_clone_init_options(pointer, UInt32(GIT_CLONE_OPTIONS_VERSION)) + + var options = pointer.move() + + pointer.dealloc(1) + + options.bare = bare ? 1 : 0 + + if localClone { + options.local = GIT_CLONE_NO_LOCAL + } + + if let checkoutOptions = checkoutOptions { + options.checkout_opts = checkoutOptions + } + + if let fetchOptions = fetchOptions { + options.fetch_opts = fetchOptions + } + + return options +} + /// A git repository. final public class Repository { - + // MARK: - Creating Repositories /// Load the repository at the given URL. @@ -69,6 +113,36 @@ final public class Repository { let repository = Repository(pointer) return Result.Success(repository) } + + /// Clone the repository from a given URL. + /// + /// remoteURL - The URL of the remote repository + /// localURL - The URL to clone the remote repository into + /// localClone - Will not bypass the git-aware transport, even if remote is local. + /// bare - Clone remote as a bare repository. + /// credentials - Credentials to be used when connecting to the remote. + /// checkoutStrategy - The checkout strategy to use, if being checked out. + /// checkoutProgress - A block that's called with the progress of the checkout. + /// + /// Returns a `Result` with a `Repository` or an error. + class public func cloneFromURL(remoteURL: NSURL, toURL: NSURL, localClone: Bool = false, bare: Bool = false, + credentials: Credentials = .Default(), checkoutStrategy: CheckoutStrategy = .Safe, checkoutProgress: CheckoutProgressBlock? = nil) -> Result { + var options = cloneOptions( + bare, localClone: localClone, + fetchOptions: fetchOptions(credentials), + checkoutOptions: checkoutOptions(checkoutStrategy, progress: checkoutProgress)) + + var pointer: COpaquePointer = nil + let remoteURLString = remoteURL.isFileReferenceURL() ? remoteURL.path! : remoteURL.absoluteString + let result = git_clone(&pointer, remoteURLString, toURL.fileSystemRepresentation, &options) + + if result != GIT_OK.rawValue { + return Result.Failure(libGit2Error(result, libGit2PointOfFailure: "git_clone")) + } + + let repository = Repository(pointer) + return Result.Success(repository) + } // MARK: - Initializers @@ -396,8 +470,7 @@ final public class Repository { /// :param: progress A block that's called with the progress of the checkout. /// :returns: Returns a result with void or the error that occurred. public func checkout(strategy strategy: CheckoutStrategy, progress: CheckoutProgressBlock? = nil) -> Result<(), NSError> { - var options = checkoutOptions(progress) - options.checkout_strategy = strategy.git_checkout_strategy.rawValue + var options = checkoutOptions(strategy, progress: progress) let result = git_checkout_head(self.pointer, &options) if result != GIT_OK.rawValue { diff --git a/SwiftGit2Tests/RepositorySpec.swift b/SwiftGit2Tests/RepositorySpec.swift index a0fa179..820d68d 100644 --- a/SwiftGit2Tests/RepositorySpec.swift +++ b/SwiftGit2Tests/RepositorySpec.swift @@ -29,6 +29,91 @@ class RepositorySpec: QuickSpec { ))) } } + + describe("Repository.Type.clone()") { + it("should handle local clones") { + let remoteRepo = Fixtures.simpleRepository + let localURL = self.temporaryURLForPurpose("local-clone") + let result = Repository.cloneFromURL(remoteRepo.directoryURL!, toURL: localURL, localClone: true) + + expect(result).to(haveSucceeded()) + + if case .Success(let clonedRepo) = result { + expect(clonedRepo.directoryURL).notTo(beNil()) + } + } + + it("should handle bare clones") { + let remoteRepo = Fixtures.simpleRepository + let localURL = self.temporaryURLForPurpose("bare-clone") + let result = Repository.cloneFromURL(remoteRepo.directoryURL!, toURL: localURL, localClone: true, bare: true) + + expect(result).to(haveSucceeded()) + + if case .Success(let clonedRepo) = result { + expect(clonedRepo.directoryURL).to(beNil()) + } + } + + it("should have set a valid remote url") { + let remoteRepo = Fixtures.simpleRepository + let localURL = self.temporaryURLForPurpose("valid-remote-clone") + let cloneResult = Repository.cloneFromURL(remoteRepo.directoryURL!, toURL: localURL, localClone: true) + + expect(cloneResult).to(haveSucceeded()) + + if case .Success(let clonedRepo) = cloneResult { + let remoteResult = clonedRepo.remoteWithName("origin") + expect(remoteResult).to(haveSucceeded()) + + if case .Success(let remote) = remoteResult { + expect(remote.URL).to(equal(remoteRepo.directoryURL?.absoluteString)) + } + } + } + + it("should be able to clone a remote repository") { + let remoteRepoURL = NSURL(string: "https://github.com/libgit2/libgit2.github.com.git") + let localURL = self.temporaryURLForPurpose("public-remote-clone") + let cloneResult = Repository.cloneFromURL(remoteRepoURL!, toURL: localURL) + + expect(cloneResult).to(haveSucceeded()) + + if case .Success(let clonedRepo) = cloneResult { + let remoteResult = clonedRepo.remoteWithName("origin") + expect(remoteResult).to(haveSucceeded()) + + if case .Success(let remote) = remoteResult { + expect(remote.URL).to(equal(remoteRepoURL?.absoluteString)) + } + } + } + + let env = NSProcessInfo.processInfo().environment + + if let privateRepo = env["SG2TestPrivateRepo"], gitUsername = env["SG2TestUsername"], publicKey = env["SG2TestPublicKey"], + privateKey = env["SG2TestPrivateKey"], passphrase = env["SG2TestPassphrase"] { + + it("should be able to clone a remote repository requiring credentials") { + let remoteRepoURL = NSURL(string: privateRepo) + let localURL = self.temporaryURLForPurpose("private-remote-clone") + + let cloneResult = Repository.cloneFromURL(remoteRepoURL!, toURL: localURL, + credentials: .SSHMemory(username: gitUsername, publicKey: publicKey, privateKey: privateKey, passphrase: passphrase)) + + expect(cloneResult).to(haveSucceeded()) + + if case .Success(let clonedRepo) = cloneResult { + let remoteResult = clonedRepo.remoteWithName("origin") + expect(remoteResult).to(haveSucceeded()) + + if case .Success(let remote) = remoteResult { + expect(remote.URL).to(equal(remoteRepoURL?.absoluteString)) + } + } + } + } + } describe("Repository.blobWithOID()") { it("should return the commit if it exists") { @@ -510,4 +595,10 @@ class RepositorySpec: QuickSpec { } } } + + func temporaryURLForPurpose(purpose: String) -> NSURL { + let globallyUniqueString = NSProcessInfo.processInfo().globallyUniqueString + let path = "\(NSTemporaryDirectory())\(globallyUniqueString)_\(purpose)" + return NSURL(fileURLWithPath: path) + } }