native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #111 from supabitapp/worktree-sync-settings

Add worktree sync settings

authored by

khoi and committed by
GitHub
3923bcc7 6bbaf7e6

+117 -2
+25
supacode/Clients/Git/GitClient.swift
··· 5 5 case repoRoot = "repo_root" 6 6 case worktreeList = "worktree_list" 7 7 case worktreeCreate = "worktree_create" 8 + case worktreeSync = "worktree_sync" 8 9 case worktreeRemove = "worktree_remove" 9 10 case branchNames = "branch_names" 10 11 case branchRename = "branch_rename" ··· 140 141 detail: detail, 141 142 workingDirectory: worktreeURL, 142 143 repositoryRootURL: repositoryRootURL 144 + ) 145 + } 146 + 147 + nonisolated func syncWorktree( 148 + at worktreeURL: URL, 149 + copyIgnored: Bool, 150 + copyUntracked: Bool 151 + ) async throws { 152 + if !copyIgnored && !copyUntracked { 153 + return 154 + } 155 + let wtURL = try wtScriptURL() 156 + var arguments = ["sync", "-f"] 157 + if copyIgnored { 158 + arguments.append("--copy-ignored") 159 + } 160 + if copyUntracked { 161 + arguments.append("--copy-untracked") 162 + } 163 + _ = try await runLoginShellProcess( 164 + operation: .worktreeSync, 165 + executableURL: wtURL, 166 + arguments: arguments, 167 + currentDirectoryURL: worktreeURL 143 168 ) 144 169 } 145 170
+8
supacode/Clients/Repositories/GitClientDependency.swift
··· 6 6 var worktrees: @Sendable (URL) async throws -> [Worktree] 7 7 var localBranchNames: @Sendable (URL) async throws -> Set<String> 8 8 var createWorktree: @Sendable (_ name: String, _ repoRoot: URL) async throws -> Worktree 9 + var syncWorktree: @Sendable (_ worktreeURL: URL, _ copyIgnored: Bool, _ copyUntracked: Bool) async throws -> Void 9 10 var isWorktreeDirty: @Sendable (URL) async -> Bool 10 11 var removeWorktree: @Sendable (_ worktree: Worktree) async throws -> URL 11 12 var branchName: @Sendable (URL) async -> String? ··· 20 21 localBranchNames: { try await GitClient().localBranchNames(for: $0) }, 21 22 createWorktree: { name, repoRoot in 22 23 try await GitClient().createWorktree(named: name, in: repoRoot) 24 + }, 25 + syncWorktree: { worktreeURL, copyIgnored, copyUntracked in 26 + try await GitClient().syncWorktree( 27 + at: worktreeURL, 28 + copyIgnored: copyIgnored, 29 + copyUntracked: copyUntracked 30 + ) 23 31 }, 24 32 isWorktreeDirty: { await GitClient().isWorktreeDirty(at: $0) }, 25 33 removeWorktree: { worktree in
+16
supacode/Features/Repositories/Reducer/RepositoriesFeature.swift
··· 99 99 @Dependency(\.gitClient) private var gitClient 100 100 @Dependency(\.githubCLI) private var githubCLI 101 101 @Dependency(\.repositoryPersistence) private var repositoryPersistence 102 + @Dependency(\.repositorySettingsClient) private var repositorySettingsClient 102 103 103 104 var body: some Reducer<State, Action> { 104 105 Reduce { state, action in ··· 329 330 } 330 331 let previousSelection = state.selectedWorktreeID 331 332 let pendingID = "pending:\(UUID().uuidString)" 333 + let repositorySettings = repositorySettingsClient.load(repository.rootURL) 332 334 state.pendingWorktrees.append( 333 335 PendingWorktree( 334 336 id: pendingID, ··· 361 363 return 362 364 } 363 365 let newWorktree = try await gitClient.createWorktree(name, repository.rootURL) 366 + do { 367 + try await gitClient.syncWorktree( 368 + newWorktree.workingDirectory, 369 + repositorySettings.copyIgnoredOnWorktreeCreate, 370 + repositorySettings.copyUntrackedOnWorktreeCreate 371 + ) 372 + } catch { 373 + await send( 374 + .presentAlert( 375 + title: "Unable to copy files to new worktree", 376 + message: "Check your repository state and try again." 377 + ) 378 + ) 379 + } 364 380 await send( 365 381 .createRandomWorktreeSucceeded( 366 382 newWorktree,
+22
supacode/Features/RepositorySettings/Reducer/RepositorySettingsFeature.swift
··· 19 19 case settingsLoaded(RepositorySettings) 20 20 case setSetupScript(String) 21 21 case setRunScript(String) 22 + case setCopyIgnoredOnWorktreeCreate(Bool) 23 + case setCopyUntrackedOnWorktreeCreate(Bool) 22 24 case delegate(Delegate) 23 25 } 24 26 ··· 55 57 56 58 case .setRunScript(let script): 57 59 state.settings.runScript = script 60 + let settings = state.settings 61 + let rootURL = state.rootURL 62 + let repositorySettingsClient = repositorySettingsClient 63 + return .run { send in 64 + repositorySettingsClient.save(settings, rootURL) 65 + await send(.delegate(.settingsChanged(rootURL))) 66 + } 67 + 68 + case .setCopyIgnoredOnWorktreeCreate(let isEnabled): 69 + state.settings.copyIgnoredOnWorktreeCreate = isEnabled 70 + let settings = state.settings 71 + let rootURL = state.rootURL 72 + let repositorySettingsClient = repositorySettingsClient 73 + return .run { send in 74 + repositorySettingsClient.save(settings, rootURL) 75 + await send(.delegate(.settingsChanged(rootURL))) 76 + } 77 + 78 + case .setCopyUntrackedOnWorktreeCreate(let isEnabled): 79 + state.settings.copyUntrackedOnWorktreeCreate = isEnabled 58 80 let settings = state.settings 59 81 let rootURL = state.rootURL 60 82 let repositorySettingsClient = repositorySettingsClient
+24 -2
supacode/Features/Settings/Models/RepositorySettings.swift
··· 4 4 var setupScript: String 5 5 var runScript: String 6 6 var openActionID: String 7 + var copyIgnoredOnWorktreeCreate: Bool 8 + var copyUntrackedOnWorktreeCreate: Bool 7 9 8 10 private enum CodingKeys: String, CodingKey { 9 11 case setupScript 10 12 case runScript 11 13 case openActionID 14 + case copyIgnoredOnWorktreeCreate 15 + case copyUntrackedOnWorktreeCreate 12 16 } 13 17 14 18 static let `default` = RepositorySettings( 15 19 setupScript: "echo \"Setup your startup script in repo settings\"", 16 20 runScript: "echo \"Configure run script in Settings, default hot key is CMD+R and CMD + . to stop\"", 17 - openActionID: OpenWorktreeAction.automaticSettingsID 21 + openActionID: OpenWorktreeAction.automaticSettingsID, 22 + copyIgnoredOnWorktreeCreate: false, 23 + copyUntrackedOnWorktreeCreate: false 18 24 ) 19 25 20 - init(setupScript: String, runScript: String, openActionID: String) { 26 + init( 27 + setupScript: String, 28 + runScript: String, 29 + openActionID: String, 30 + copyIgnoredOnWorktreeCreate: Bool, 31 + copyUntrackedOnWorktreeCreate: Bool 32 + ) { 21 33 self.setupScript = setupScript 22 34 self.runScript = runScript 23 35 self.openActionID = openActionID 36 + self.copyIgnoredOnWorktreeCreate = copyIgnoredOnWorktreeCreate 37 + self.copyUntrackedOnWorktreeCreate = copyUntrackedOnWorktreeCreate 24 38 } 25 39 26 40 init(from decoder: Decoder) throws { ··· 31 45 ?? Self.default.runScript 32 46 openActionID = try container.decodeIfPresent(String.self, forKey: .openActionID) 33 47 ?? Self.default.openActionID 48 + copyIgnoredOnWorktreeCreate = try container.decodeIfPresent( 49 + Bool.self, 50 + forKey: .copyIgnoredOnWorktreeCreate 51 + ) ?? Self.default.copyIgnoredOnWorktreeCreate 52 + copyUntrackedOnWorktreeCreate = try container.decodeIfPresent( 53 + Bool.self, 54 + forKey: .copyUntrackedOnWorktreeCreate 55 + ) ?? Self.default.copyUntrackedOnWorktreeCreate 34 56 } 35 57 }
+22
supacode/Features/Settings/Views/RepositorySettingsView.swift
··· 7 7 var body: some View { 8 8 Form { 9 9 Section { 10 + Toggle( 11 + "Copy ignored files to new worktrees", 12 + isOn: Binding( 13 + get: { store.settings.copyIgnoredOnWorktreeCreate }, 14 + set: { store.send(.setCopyIgnoredOnWorktreeCreate($0)) } 15 + ) 16 + ) 17 + Toggle( 18 + "Copy untracked files to new worktrees", 19 + isOn: Binding( 20 + get: { store.settings.copyUntrackedOnWorktreeCreate }, 21 + set: { store.send(.setCopyUntrackedOnWorktreeCreate($0)) } 22 + ) 23 + ) 24 + } header: { 25 + VStack(alignment: .leading, spacing: 4) { 26 + Text("Worktree") 27 + Text("Applies when creating a new worktree") 28 + .foregroundStyle(.secondary) 29 + } 30 + } 31 + Section { 10 32 ZStack(alignment: .topLeading) { 11 33 TextEditor( 12 34 text: Binding(