Skip to content

Commit 6a53eaa

Browse files
committed
[SR-13484] Use --no-checkout when cloning packages, since it's immediately followed by a checkout anyway
SwiftPM fetches a remote repository by mirroring it to the local file system, then clones that local repository to a working directory, after which it checks out a specific ref. When the local clone happens, Git also does a check out. This is unnecessary, since the checkout will immediately be replaced by the intended ref, and it also makes the checkout sensitive to whether the default branch name is set up correctly for the repository. Since the working copy is guaranteed to be checked out after the clone, there is no need to do a checkout as part of the cloning. rdar://68165300
1 parent a399011 commit 6a53eaa

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

Sources/SourceControl/GitRepository.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class GitRepositoryProvider: RepositoryProvider {
8383
// a clone from our cache of repositories and then we replace the remote to the one originally
8484
// present in the bare repository.
8585
try Process.checkNonZeroExit(args:
86-
Git.tool, "clone", sourcePath.pathString, destinationPath.pathString)
86+
Git.tool, "clone", "--no-checkout", sourcePath.pathString, destinationPath.pathString)
8787
// The default name of the remote.
8888
let origin = "origin"
8989
// In destination repo remove the remote which will be pointing to the source repo.
@@ -102,7 +102,7 @@ public class GitRepositoryProvider: RepositoryProvider {
102102
// only ever expect to get back a revision that remains present in the
103103
// object storage.
104104
try Process.checkNonZeroExit(args:
105-
Git.tool, "clone", "--shared", sourcePath.pathString, destinationPath.pathString)
105+
Git.tool, "clone", "--shared", "--no-checkout", sourcePath.pathString, destinationPath.pathString)
106106
}
107107
}
108108

Sources/SourceControl/InMemoryGitRepository.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,6 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider {
341341
) throws {
342342
let checkout = fetchedMap[sourcePath]!.copy(at: destinationPath)
343343
checkoutsMap[destinationPath] = checkout
344-
try checkout.installHead()
345344
}
346345

347346
public func checkoutExists(at path: AbsolutePath) throws -> Bool {

Sources/SourceControl/Repository.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ public protocol RepositoryProvider {
8383

8484
/// Clone a managed repository into a working copy at on the local file system.
8585
///
86-
/// Once complete, the repository can be opened using `openCheckout`.
86+
/// Once complete, the repository can be opened using `openCheckout`. Note
87+
/// that there is no requirement that the files have been materialized into
88+
/// the file system at the completion of this call, since it will always be
89+
/// followed by checking out the cloned working copy at a particular ref.
8790
///
8891
/// - Parameters:
8992
/// - sourcePath: The location of the repository on disk, at which the

Tests/SourceControlTests/GitRepositoryTests.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,4 +639,43 @@ class GitRepositoryTests: XCTestCase {
639639
XCTAssertTrue(ignored[0])
640640
}
641641
}
642+
643+
func testMissingDefaultBranch() throws {
644+
mktmpdir { path in
645+
// Create a repository.
646+
let testRepoPath = path.appending(component: "test-repo")
647+
try makeDirectories(testRepoPath)
648+
initGitRepo(testRepoPath)
649+
let repo = GitRepository(path: testRepoPath)
650+
651+
// Create a `main` branch and remove `master`.
652+
try repo.checkout(newBranch: "main")
653+
try systemQuietly([Git.tool, "-C", testRepoPath.pathString, "branch", "-D", "master"])
654+
655+
// Change the branch name to something non-existent.
656+
try systemQuietly([Git.tool, "-C", testRepoPath.pathString, "symbolic-ref", "HEAD", "refs/heads/_non_existent_branch_"])
657+
658+
// Clone it somewhere.
659+
let testClonePath = path.appending(component: "clone")
660+
let provider = GitRepositoryProvider()
661+
let repoSpec = RepositorySpecifier(url: testRepoPath.pathString)
662+
try provider.fetch(repository: repoSpec, to: testClonePath)
663+
let clonedRepo = provider.open(repository: repoSpec, at: testClonePath)
664+
XCTAssertEqual(clonedRepo.tags, [])
665+
666+
// Clone off a checkout.
667+
let checkoutPath = path.appending(component: "checkout")
668+
try provider.cloneCheckout(repository: repoSpec, at: testClonePath, to: checkoutPath, editable: false)
669+
XCTAssertFalse(localFileSystem.exists(checkoutPath.appending(component: "file.swift")))
670+
let checkoutRepo = try provider.openCheckout(at: checkoutPath)
671+
672+
// Try to check out the `main` branch.
673+
try checkoutRepo.checkout(revision: Revision(identifier: "main"))
674+
XCTAssertTrue(localFileSystem.exists(checkoutPath.appending(component: "file.swift")))
675+
676+
// The following will throw if HEAD was set incorrectly and we didn't do a no-checkout clone.
677+
XCTAssertNoThrow(try checkoutRepo.getCurrentRevision())
678+
}
679+
}
680+
642681
}

0 commit comments

Comments
 (0)