diff --git a/Sources/Get/Git.swift b/Sources/Get/Git.swift index d8e4080a289..d894bbb4259 100644 --- a/Sources/Get/Git.swift +++ b/Sources/Get/Git.swift @@ -36,7 +36,12 @@ extension Git { "--depth", "10", url, dstdir, environment: environment, message: "Cloning \(url)") } catch POSIX.Error.ExitStatus { - throw Error.GitCloneFailure(url, dstdir) + // Git 2.0 or higher is required + if Git.majorVersionNumber < 2 { + throw Utility.Error.ObsoleteGitVersion + } else { + throw Error.GitCloneFailure(url, dstdir) + } } return Repo(path: dstdir)! //TODO no bangs @@ -45,7 +50,7 @@ extension Git { extension Git.Repo { var versions: [Version] { - let out = (try? popen([Git.tool, "-C", path, "tag", "-l"])) ?? "" + let out = (try? Git.runPopen([Git.tool, "-C", path, "tag", "-l"])) ?? "" let tags = out.characters.split(separator: Character.newline) let versions = tags.flatMap(Version.init).sorted() if !versions.isEmpty { @@ -66,6 +71,6 @@ extension Git.Repo { no versions, returns false. */ var versionsArePrefixed: Bool { - return (try? popen([Git.tool, "-C", path, "tag", "-l"]))?.hasPrefix("v") ?? false + return (try? Git.runPopen([Git.tool, "-C", path, "tag", "-l"]))?.hasPrefix("v") ?? false } } diff --git a/Sources/Get/RawClone.swift b/Sources/Get/RawClone.swift index fdfd1cd649b..a86d44c9923 100644 --- a/Sources/Get/RawClone.swift +++ b/Sources/Get/RawClone.swift @@ -63,8 +63,8 @@ class RawClone: Fetchable { func setVersion(ver: Version) throws { let packageVersionsArePrefixed = repo.versionsArePrefixed let v = (packageVersionsArePrefixed ? "v" : "") + ver.description - try popen([Git.tool, "-C", path, "reset", "--hard", v]) - try popen([Git.tool, "-C", path, "branch", "-m", v]) + try Git.runPopen([Git.tool, "-C", path, "reset", "--hard", v]) + try Git.runPopen([Git.tool, "-C", path, "branch", "-m", v]) print("Resolved version:", ver) diff --git a/Sources/Utility/Error.swift b/Sources/Utility/Error.swift new file mode 100644 index 00000000000..f12766b49c6 --- /dev/null +++ b/Sources/Utility/Error.swift @@ -0,0 +1,25 @@ +/* + This source file is part of the Swift.org open source project + + Copyright 2015 - 2016 Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for Swift project authors +*/ + +public enum Error: ErrorProtocol { + case ObsoleteGitVersion + case UnknownGitError +} + +extension Error: CustomStringConvertible { + public var description: String { + switch self { + case ObsoleteGitVersion: + return "Git 2.0 or higher is required. Please update git and retry." + case UnknownGitError: + return "Failed to invoke git command. Please try updating git" + } + } +} diff --git a/Sources/Utility/Git.swift b/Sources/Utility/Git.swift index b384c0afa9e..8edecc811ec 100644 --- a/Sources/Utility/Git.swift +++ b/Sources/Utility/Git.swift @@ -10,6 +10,7 @@ import func POSIX.realpath import func POSIX.getenv +import libc public class Git { public class Repo { @@ -23,7 +24,7 @@ public class Git { public lazy var origin: String? = { repo in do { - guard let url = try popen([Git.tool, "-C", repo.path, "config", "--get", "remote.origin.url"]).chuzzle() else { + guard let url = try Git.runPopen([Git.tool, "-C", repo.path, "config", "--get", "remote.origin.url"]).chuzzle() else { return nil } if URL.scheme(url) == nil { @@ -40,15 +41,54 @@ public class Git { }(self) public var branch: String! { - return try? popen([Git.tool, "-C", path, "rev-parse", "--abbrev-ref", "HEAD"]).chomp() + return try? Git.runPopen([Git.tool, "-C", path, "rev-parse", "--abbrev-ref", "HEAD"]).chomp() } public func fetch() throws { - try system(Git.tool, "-C", path, "fetch", "--tags", "origin", message: nil) + do { + try system(Git.tool, "-C", path, "fetch", "--tags", "origin", message: nil) + } catch let errror { + Git.handle(errror) + } } } public class var tool: String { return getenv("SWIFT_GIT") ?? "git" } + + public class var version: String! { + return try? Git.runPopen([Git.tool, "version"]) + } + + public class var majorVersionNumber: Int? { + let prefix = "git version" + var version = self.version + if version.hasPrefix(prefix) { + let prefixRange = version.startIndex...version.startIndex.advanced(by: prefix.characters.count) + version.removeSubrange(prefixRange) + } + guard let first = version.characters.first else { + return nil + } + return Int(String(first)) + } + + @noreturn public class func handle(error: ErrorProtocol) { + // Git 2.0 or higher is required + if Git.majorVersionNumber < 2 { + print("error: ", Error.ObsoleteGitVersion) + } else { + print("error: ", Error.UnknownGitError) + } + exit(1) + } + + public class func runPopen(arguments: [String]) throws -> String { + do { + return try popen(arguments) + } catch let error { + handle(error) + } + } } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index e1e7940f1cf..e0a608c32b0 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -40,4 +40,5 @@ XCTMain([ testCase(WalkTests.allTests), testCase(ModuleMapsTestCase.allTests), testCase(DescribeTests.allTests), + testCase(GitUtilityTests.allTests), ]) diff --git a/Tests/Utility/GitTests.swift b/Tests/Utility/GitTests.swift new file mode 100644 index 00000000000..062b058ed59 --- /dev/null +++ b/Tests/Utility/GitTests.swift @@ -0,0 +1,43 @@ +/* + This source file is part of the Swift.org open source project + + Copyright 2015 - 2016 Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for Swift project authors + */ + +@testable import Utility +import XCTest + +class GitMoc: Git { + static var mocVersion: String = "git version 2.5.4 (Apple Git-61)" + override class var version: String! { + return mocVersion + } +} + +class GitUtilityTests: XCTestCase { + + func testGitVersion() { + XCTAssertEqual(GitMoc.majorVersionNumber, 2) + + GitMoc.mocVersion = "2.5.4" + XCTAssertEqual(GitMoc.majorVersionNumber, 2) + + GitMoc.mocVersion = "git version 1.5.4" + XCTAssertEqual(GitMoc.majorVersionNumber, 1) + + GitMoc.mocVersion = "1.25.4" + XCTAssertEqual(GitMoc.majorVersionNumber, 1) + } +} + +extension GitUtilityTests { + static var allTests : [(String, GitUtilityTests -> () throws -> Void)] { + return [ + ("testGitVersion", testGitVersion), + ] + } +}