diff --git a/Sources/LucaCore/Core/FileManagerProtocols/GitHookInstallerFileManaging.swift b/Sources/LucaCore/Core/FileManagerProtocols/GitHookInstallerFileManaging.swift index b630ac9..d5b5b35 100644 --- a/Sources/LucaCore/Core/FileManagerProtocols/GitHookInstallerFileManaging.swift +++ b/Sources/LucaCore/Core/FileManagerProtocols/GitHookInstallerFileManaging.swift @@ -12,4 +12,5 @@ public protocol GitHookInstallerFileManaging { func readString(at url: URL) throws -> String func writeString(_ content: String, to url: URL) throws func setAttributes(_ attributes: [FileAttributeKey: Any], ofItemAtPath path: String) throws + func attributesOfItem(atPath path: String) throws -> [FileAttributeKey: Any] } diff --git a/Sources/LucaCore/Core/GitHookInstaller/GitHookInstaller.swift b/Sources/LucaCore/Core/GitHookInstaller/GitHookInstaller.swift index ba74c6a..b527ebb 100644 --- a/Sources/LucaCore/Core/GitHookInstaller/GitHookInstaller.swift +++ b/Sources/LucaCore/Core/GitHookInstaller/GitHookInstaller.swift @@ -30,6 +30,12 @@ public struct GitHookInstaller { return } + // In a git worktree, `.git` is a file (not a directory) pointing to the main repo. + // Hooks live in the main repo; skip installation to avoid failing in a worktree context. + guard (try? fileManager.attributesOfItem(atPath: gitDirectory.path)[.type] as? FileAttributeType) == .typeDirectory else { + return + } + let sourceHookPath = fileManager.homeDirectoryForCurrentUser .appending(components: Constants.toolFolder, "post-checkout") diff --git a/Tests/Core/GitHookInstallerTests.swift b/Tests/Core/GitHookInstallerTests.swift index eb691e4..52b8d7d 100644 --- a/Tests/Core/GitHookInstallerTests.swift +++ b/Tests/Core/GitHookInstallerTests.swift @@ -40,6 +40,22 @@ final class GitHookInstallerTests: XCTestCase { XCTAssertTrue(printer.printedMessages.isEmpty) } + // MARK: - Git Worktree + + func test_installPostCheckoutHook_whenGitWorktree_doesNothing() throws { + // Given: `.git` is a file (worktree), not a directory + let currentDirectory = URL(fileURLWithPath: fileManager.currentDirectoryPath) + try fileManager.createDirectory(at: currentDirectory, withIntermediateDirectories: true) + let gitFile = currentDirectory.appending(component: ".git") + _ = fileManager.createFile(atPath: gitFile.path, contents: Data("gitdir: /some/main/repo/.git/worktrees/feature\n".utf8)) + + // When + try sut.installPostCheckoutHook() + + // Then + XCTAssertTrue(printer.printedMessages.isEmpty) + } + // MARK: - Source Hook Missing func test_installPostCheckoutHook_whenSourceHookMissing_printsWarning() throws {