Skip to content

Commit 6632a91

Browse files
committed
Convert git WorkingCopy APIs to async
`RepositoryProviders` may not always create and open working copies synchronously. This avoids the need for concurrency workarounds that are prone to deadlocks. rdar://149982696
1 parent 6d4ce1c commit 6632a91

12 files changed

+79
-79
lines changed

Sources/Commands/Utilities/APIDigester.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ struct APIDigesterBaselineDumper {
104104
// Clone the current package in a sandbox and checkout the baseline revision.
105105
let repositoryProvider = GitRepositoryProvider()
106106
let specifier = RepositorySpecifier(path: baselinePackageRoot)
107-
let workingCopy = try repositoryProvider.createWorkingCopy(
107+
let workingCopy = try await repositoryProvider.createWorkingCopy(
108108
repository: specifier,
109109
sourcePath: packageRoot,
110110
at: baselinePackageRoot,

Sources/SourceControl/GitRepository.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public struct GitRepositoryProvider: RepositoryProvider, Cancellable {
229229
sourcePath: Basics.AbsolutePath,
230230
at destinationPath: Basics.AbsolutePath,
231231
editable: Bool
232-
) throws -> WorkingCheckout {
232+
) async throws -> WorkingCheckout {
233233
if editable {
234234
// For editable clones, i.e. the user is expected to directly work on them, first we create
235235
// a clone from our cache of repositories and then we replace the remote to the one originally

Sources/SourceControl/Repository.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public protocol RepositoryProvider: Cancellable {
121121
repository: RepositorySpecifier,
122122
sourcePath: AbsolutePath,
123123
at destinationPath: AbsolutePath,
124-
editable: Bool) throws -> WorkingCheckout
124+
editable: Bool) async throws -> WorkingCheckout
125125

126126
/// Returns true if a working repository exists at `path`
127127
func workingCopyExists(at path: AbsolutePath) throws -> Bool
@@ -131,7 +131,7 @@ public protocol RepositoryProvider: Cancellable {
131131
/// - Parameters:
132132
/// - path: The location of the repository on disk, at which the repository
133133
/// has previously been created via `copyToWorkingDirectory`.
134-
func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout
134+
func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout
135135

136136
/// Copies the repository at path `from` to path `to`.
137137
/// - Parameters:

Sources/SourceControl/RepositoryManager.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ public class RepositoryManager: Cancellable {
329329
}
330330
}
331331
}
332-
332+
333333
// We are expecting handle.repository.url to always be a resolved absolute path.
334334
let shouldCacheLocalPackages = Environment.current["SWIFTPM_TESTS_PACKAGECACHE"] == "1" || cacheLocalPackages
335335

@@ -409,8 +409,8 @@ public class RepositoryManager: Cancellable {
409409
}
410410

411411
/// Open a working copy checkout at a path
412-
public func openWorkingCopy(at path: Basics.AbsolutePath) throws -> WorkingCheckout {
413-
try self.provider.openWorkingCopy(at: path)
412+
public func openWorkingCopy(at path: Basics.AbsolutePath) async throws -> WorkingCheckout {
413+
try await self.provider.openWorkingCopy(at: path)
414414
}
415415

416416
/// Validate a working copy check is aligned with its repository setup
@@ -433,8 +433,8 @@ public class RepositoryManager: Cancellable {
433433
_ handle: RepositoryHandle,
434434
at destinationPath: Basics.AbsolutePath,
435435
editable: Bool
436-
) throws -> WorkingCheckout {
437-
try self.provider.createWorkingCopy(
436+
) async throws -> WorkingCheckout {
437+
try await self.provider.createWorkingCopy(
438438
repository: handle.repository,
439439
sourcePath: self.path.appending(handle.subpath),
440440
at: destinationPath,
@@ -548,8 +548,8 @@ extension RepositoryManager {
548548
/// expected to be non-existent when called.
549549
///
550550
/// - editable: The clone is expected to be edited by user.
551-
public func createWorkingCopy(at path: Basics.AbsolutePath, editable: Bool) throws -> WorkingCheckout {
552-
return try self.manager.createWorkingCopy(self, at: path, editable: editable)
551+
public func createWorkingCopy(at path: Basics.AbsolutePath, editable: Bool) async throws -> WorkingCheckout {
552+
return try await self.manager.createWorkingCopy(self, at: path, editable: editable)
553553
}
554554
}
555555
}

Sources/Workspace/Workspace+Editing.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ extension Workspace {
118118
throw WorkspaceDiagnostics.RevisionDoesNotExist(revision: revision.identifier)
119119
}
120120

121-
let workingCopy = try handle.createWorkingCopy(at: destination, editable: true)
121+
let workingCopy = try await handle.createWorkingCopy(at: destination, editable: true)
122122
try workingCopy.checkout(revision: revision ?? checkoutState.revision)
123123

124124
// Checkout to the new branch if provided.
@@ -187,7 +187,7 @@ extension Workspace {
187187
let path = self.location.editSubdirectory(for: dependency)
188188
// Check for uncommitted and unpushed changes if force removal is off.
189189
if !forceRemove {
190-
let workingCopy = try repositoryManager.openWorkingCopy(at: path)
190+
let workingCopy = try await repositoryManager.openWorkingCopy(at: path)
191191
guard !workingCopy.hasUncommittedChanges() else {
192192
throw WorkspaceDiagnostics.UncommittedChanges(repositoryPath: path)
193193
}

Sources/Workspace/Workspace+SourceControl.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extension Workspace {
5050
)
5151

5252
// Check out the given revision.
53-
let workingCopy = try self.repositoryManager.openWorkingCopy(at: checkoutPath)
53+
let workingCopy = try await self.repositoryManager.openWorkingCopy(at: checkoutPath)
5454

5555
// Inform the delegate that we're about to start.
5656
delegate?.willCheckOut(
@@ -147,7 +147,7 @@ extension Workspace {
147147
// This can become invalid if the build directory is moved.
148148
fetch: if self.fileSystem.isDirectory(checkoutPath) {
149149
// Fetch the checkout in case there are updates available.
150-
let workingCopy = try self.repositoryManager.openWorkingCopy(at: checkoutPath)
150+
let workingCopy = try await self.repositoryManager.openWorkingCopy(at: checkoutPath)
151151

152152
// Ensure that the alternative object store is still valid.
153153
guard try self.repositoryManager.isValidWorkingCopy(workingCopy, for: repository) else {
@@ -198,7 +198,7 @@ extension Workspace {
198198
let start = DispatchTime.now()
199199

200200
// Create the working copy.
201-
_ = try handle.createWorkingCopy(at: checkoutPath, editable: false)
201+
_ = try await handle.createWorkingCopy(at: checkoutPath, editable: false)
202202

203203
// Inform the delegate that we're done.
204204
let duration = start.distance(to: .now())
@@ -213,14 +213,14 @@ extension Workspace {
213213
}
214214

215215
/// Removes the clone and checkout of the provided specifier.
216-
func removeRepository(dependency: ManagedDependency) throws {
216+
func removeRepository(dependency: ManagedDependency) async throws {
217217
guard case .sourceControlCheckout = dependency.state else {
218218
throw InternalError("cannot remove repository for \(dependency) with state \(dependency.state)")
219219
}
220220

221221
// Remove the checkout.
222222
let dependencyPath = self.location.repositoriesCheckoutSubdirectory(for: dependency)
223-
let workingCopy = try self.repositoryManager.openWorkingCopy(at: dependencyPath)
223+
let workingCopy = try await self.repositoryManager.openWorkingCopy(at: dependencyPath)
224224
guard !workingCopy.hasUncommittedChanges() else {
225225
throw WorkspaceDiagnostics.UncommittedChanges(repositoryPath: dependencyPath)
226226
}

Sources/Workspace/Workspace.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,7 @@ extension Workspace {
13421342
case .localSourceControl:
13431343
break // NOOP
13441344
case .remoteSourceControl:
1345-
try self.removeRepository(dependency: dependencyToRemove)
1345+
try await self.removeRepository(dependency: dependencyToRemove)
13461346
case .registry:
13471347
try self.removeRegistryArchive(for: dependencyToRemove)
13481348
}

Sources/_InternalTestSupport/InMemoryGitRepository.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ extension InMemoryGitRepository: FileSystem {
281281
try self.head.fileSystem.createDirectory(path, recursive: recursive)
282282
}
283283
}
284-
284+
285285
public func createSymbolicLink(_ path: TSCAbsolutePath, pointingAt destination: TSCAbsolutePath, relative: Bool) throws {
286286
throw FileSystemError(.unsupported, path)
287287
}
@@ -458,7 +458,7 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider {
458458
sourcePath: AbsolutePath,
459459
at destinationPath: AbsolutePath,
460460
editable: Bool
461-
) throws -> WorkingCheckout {
461+
) async throws -> WorkingCheckout {
462462
guard let checkout = fetchedMap[sourcePath] else {
463463
throw InternalError("unknown checkout at \(sourcePath)")
464464
}
@@ -471,7 +471,7 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider {
471471
return checkoutsMap.contains(path)
472472
}
473473

474-
public func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout {
474+
public func openWorkingCopy(at path: AbsolutePath) async throws -> WorkingCheckout {
475475
guard let checkout = checkoutsMap[path] else {
476476
throw InternalError("unknown checkout at \(path)")
477477
}

0 commit comments

Comments
 (0)