From 4dd920558a1c7174c0e9a5b872325df36e363516 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Wed, 18 Mar 2026 20:21:47 -0500 Subject: fix git repository settings updates for empty repos --- .../Repositories/RepositorySettingsViewModel.swift | 74 ++++++++++++++++++---- 1 file changed, 62 insertions(+), 12 deletions(-) (limited to 'Hutch/Views/Repositories/RepositorySettingsViewModel.swift') diff --git a/Hutch/Views/Repositories/RepositorySettingsViewModel.swift b/Hutch/Views/Repositories/RepositorySettingsViewModel.swift index d6f3300..71d6ab6 100644 --- a/Hutch/Views/Repositories/RepositorySettingsViewModel.swift +++ b/Hutch/Views/Repositories/RepositorySettingsViewModel.swift @@ -6,12 +6,20 @@ private struct UpdateRepoResponse: Decodable, Sendable { let updateRepository: UpdatedRepo } +private struct UpdateRepoInfoResponse: Decodable, Sendable { + let updateRepository: UpdatedRepoInfo +} + private struct UpdatedRepo: Decodable, Sendable { - let id: Int - let rid: String + let id: Int? + let rid: String? let name: String let description: String? - let visibility: Visibility + let visibility: Visibility? +} + +private struct UpdatedRepoInfo: Decodable, Sendable { + let id: Int } private struct ACLResponse: Decodable, Sendable { @@ -71,6 +79,7 @@ final class RepositorySettingsViewModel { var editedDescription: String var editedVisibility: Visibility var editedHead: String + private let originalEditedHead: String var isSavingInfo = false // MARK: - Rename fields @@ -116,11 +125,14 @@ final class RepositorySettingsViewModel { self.branches = branches // Extract branch name from HEAD reference + let initialEditedHead: String if let head = repository.head?.name { - self.editedHead = head.replacingOccurrences(of: "refs/heads/", with: "") + initialEditedHead = head.replacingOccurrences(of: "refs/heads/", with: "") } else { - self.editedHead = "main" + initialEditedHead = "main" } + self.editedHead = initialEditedHead + self.originalEditedHead = initialEditedHead } // MARK: - Update Repository Info @@ -133,22 +145,32 @@ final class RepositorySettingsViewModel { } """ + private static let updateRepoInfoMutation = """ + mutation updateRepository($id: Int!, $input: RepoInput!) { + updateRepository(id: $id, input: $input) { + id + } + } + """ + func saveInfo() async { isSavingInfo = true defer { isSavingInfo = false } error = nil do { - let input: [String: any Sendable] = [ + var input: [String: any Sendable] = [ "description": editedDescription, - "visibility": editedVisibility.rawValue, - "HEAD": editedHead + "visibility": editedVisibility.rawValue ] + if let headReference = selectedHeadReferenceForSave() { + input["HEAD"] = headReference + } _ = try await client.execute( service: service, - query: Self.updateRepoMutation, + query: Self.updateRepoInfoMutation, variables: ["id": repositoryId, "input": input], - responseType: UpdateRepoResponse.self + responseType: UpdateRepoInfoResponse.self ) } catch { self.error = error.localizedDescription @@ -238,8 +260,9 @@ final class RepositorySettingsViewModel { } func addACL() async { - let entity = newACLEntity.trimmingCharacters(in: .whitespacesAndNewlines) - guard !entity.isEmpty else { return } + let rawEntity = newACLEntity.trimmingCharacters(in: .whitespacesAndNewlines) + guard !rawEntity.isEmpty else { return } + let entity = Self.gitCanonicalEntity(from: rawEntity) isAddingACL = true defer { isAddingACL = false } error = nil @@ -267,6 +290,33 @@ final class RepositorySettingsViewModel { } } + static func gitCanonicalEntity(from input: String) -> String { + let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty else { return trimmed } + let username = trimmed.hasPrefix("~") ? String(trimmed.dropFirst()) : trimmed + return "~\(username)" + } + + static func gitHeadReference(from input: String) -> String { + let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty else { return trimmed } + if trimmed.hasPrefix("refs/") { + return trimmed + } + return "refs/heads/\(trimmed)" + } + + func selectedHeadReferenceForSave() -> String? { + let normalizedEditedHead = editedHead.trimmingCharacters(in: .whitespacesAndNewlines) + guard normalizedEditedHead != originalEditedHead else { + return nil + } + + return branches.first { + $0.name.replacingOccurrences(of: "refs/heads/", with: "") == normalizedEditedHead + }?.name + } + func deleteACL(_ entry: ACLEntry) async { isDeletingACL = true defer { isDeletingACL = false } -- cgit v1.2.3