Commit c619fe4a96

c619fe4a96bd0af63b1f8777ca8f9f2dea227e86

parent: 55f362e4c3

Unsigned

cmc <hello@cleberg.net> · 2026-04-02 04:37 UTC

feat: show commit dates in Refs tab

Layout: unified · split

Hutch.xcodeproj/project.pbxproj +8 −8
@@ -539,7 +539,7 @@
539539 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
540540 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
541541 CODE_SIGN_STYLE = Automatic;
542 CURRENT_PROJECT_VERSION = 19;
542 CURRENT_PROJECT_VERSION = 20;
543543 DEVELOPMENT_TEAM = ZCNAX3VL9D;
544544 ENABLE_PREVIEWS = YES;
545545 GENERATE_INFOPLIST_FILE = YES;
@@ -556,7 +556,7 @@
556556 "$(inherited)",
557557 "@executable_path/Frameworks",
558558 );
559 MARKETING_VERSION = 2.8.0;
559 MARKETING_VERSION = 2.8.1;
560560 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
561561 PRODUCT_NAME = "$(TARGET_NAME)";
562562 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -576,7 +576,7 @@
576576 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
577577 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
578578 CODE_SIGN_STYLE = Automatic;
579 CURRENT_PROJECT_VERSION = 19;
579 CURRENT_PROJECT_VERSION = 20;
580580 DEVELOPMENT_TEAM = ZCNAX3VL9D;
581581 ENABLE_PREVIEWS = YES;
582582 GENERATE_INFOPLIST_FILE = YES;
@@ -593,7 +593,7 @@
593593 "$(inherited)",
594594 "@executable_path/Frameworks",
595595 );
596 MARKETING_VERSION = 2.8.0;
596 MARKETING_VERSION = 2.8.1;
597597 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
598598 PRODUCT_NAME = "$(TARGET_NAME)";
599599 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -656,7 +656,7 @@
656656 APPLICATION_EXTENSION_API_ONLY = YES;
657657 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
658658 CODE_SIGN_STYLE = Automatic;
659 CURRENT_PROJECT_VERSION = 19;
659 CURRENT_PROJECT_VERSION = 20;
660660 DEVELOPMENT_TEAM = ZCNAX3VL9D;
661661 GENERATE_INFOPLIST_FILE = NO;
662662 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -666,7 +666,7 @@
666666 "@executable_path/Frameworks",
667667 "@executable_path/../../Frameworks",
668668 );
669 MARKETING_VERSION = 2.8.0;
669 MARKETING_VERSION = 2.8.1;
670670 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
671671 PRODUCT_NAME = "$(TARGET_NAME)";
672672 SKIP_INSTALL = YES;
@@ -685,7 +685,7 @@
685685 APPLICATION_EXTENSION_API_ONLY = YES;
686686 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
687687 CODE_SIGN_STYLE = Automatic;
688 CURRENT_PROJECT_VERSION = 19;
688 CURRENT_PROJECT_VERSION = 20;
689689 DEVELOPMENT_TEAM = ZCNAX3VL9D;
690690 GENERATE_INFOPLIST_FILE = NO;
691691 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -695,7 +695,7 @@
695695 "@executable_path/Frameworks",
696696 "@executable_path/../../Frameworks",
697697 );
698 MARKETING_VERSION = 2.8.0;
698 MARKETING_VERSION = 2.8.1;
699699 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
700700 PRODUCT_NAME = "$(TARGET_NAME)";
701701 SKIP_INSTALL = YES;
Hutch/Models/Git.swift +7
@@ -82,6 +82,13 @@ struct Reference: Codable, Sendable, Hashable {
8282 let target: String?
8383}
8484
85/// A Git reference enriched with the date of its tip commit or tag, for display in the Refs tab.
86struct ReferenceDetail: Sendable, Hashable {
87 let name: String
88 let target: String?
89 let date: Date?
90}
91
8592// MARK: - TreeEntry
8693
8794/// An entry in a Git tree (file or directory).
Hutch/Views/Repositories/ReferencesListView.swift +10 −3
@@ -51,14 +51,21 @@ struct ReferencesListView: View {
5151}
5252
5353private struct ReferenceRow: View {
54 let reference: Reference
54 let reference: ReferenceDetail
5555 let prefix: String
5656
5757 var body: some View {
5858 HStack {
5959 Label {
60 Text(shortName)
61 .font(.body.monospaced())
60 VStack(alignment: .leading, spacing: 2) {
61 Text(shortName)
62 .font(.body.monospaced())
63 if let date = reference.date {
64 Text(date.relativeDescription)
65 .font(.caption)
66 .foregroundStyle(.secondary)
67 }
68 }
6269 } icon: {
6370 Image(systemName: icon)
6471 .foregroundStyle(iconColor)
Hutch/Views/Repositories/RepositoryDetailViewModel.swift +36 −6
@@ -24,10 +24,33 @@ private struct RefsRepository: Decodable, Sendable {
2424}
2525
2626private struct RefsPage: Decodable, Sendable {
27 let results: [Reference]
27 let results: [ReferencePayload]
2828 let cursor: String?
2929}
3030
31/// Decodes a single reference including the optional follow object for date extraction.
32private struct ReferencePayload: Decodable, Sendable {
33 let name: String
34 let target: String?
35 let follow: ReferenceFollow?
36
37 func toDetail() -> ReferenceDetail {
38 // Branches and lightweight tags follow to a Commit (author.time).
39 // Annotated tags follow to a Tag object (tagger.time).
40 let date = follow?.author?.time ?? follow?.tagger?.time
41 return ReferenceDetail(name: name, target: target, date: date)
42 }
43}
44
45private struct ReferenceFollow: Decodable, Sendable {
46 let author: ReferenceSignature?
47 let tagger: ReferenceSignature?
48}
49
50private struct ReferenceSignature: Decodable, Sendable {
51 let time: Date
52}
53
3154private struct ReadmeResponse: Decodable, Sendable {
3255 let repository: ReadmeRepository?
3356}
@@ -98,8 +121,8 @@ final class RepositoryDetailViewModel {
98121
99122 // MARK: - References state
100123
101 private(set) var branches: [Reference] = []
102 private(set) var tags: [Reference] = []
124 private(set) var branches: [ReferenceDetail] = []
125 private(set) var tags: [ReferenceDetail] = []
103126 private(set) var isLoadingRefs = false
104127
105128 // MARK: - README state
@@ -220,7 +243,14 @@ final class RepositoryDetailViewModel {
220243 query refs($rid: ID!) {
221244 repository(rid: $rid) {
222245 references {
223 results { name target }
246 results {
247 name
248 target
249 follow {
250 ... on Commit { author { time } }
251 ... on Tag { tagger { time } }
252 }
253 }
224254 cursor
225255 }
226256 }
@@ -240,8 +270,8 @@ final class RepositoryDetailViewModel {
240270 responseType: RefsResponse.self
241271 )
242272 let allRefs = result.repository?.references.results ?? []
243 branches = allRefs.filter { $0.name.hasPrefix("refs/heads/") }
244 tags = allRefs.filter { $0.name.hasPrefix("refs/tags/") }
273 branches = allRefs.filter { $0.name.hasPrefix("refs/heads/") }.map { $0.toDetail() }
274 tags = allRefs.filter { $0.name.hasPrefix("refs/tags/") }.map { $0.toDetail() }
245275 } catch {
246276 self.error = error.userFacingMessage
247277 }
Hutch/Views/Repositories/RepositorySettingsView.swift +1 −1
@@ -2,7 +2,7 @@ import SwiftUI
22
33struct RepositorySettingsView: View {
44 let repository: RepositorySummary
5 let branches: [Reference]
5 let branches: [ReferenceDetail]
66 let client: SRHTClient
77 let onRenamed: (String) -> Void
88 let onDeleted: () -> Void
Hutch/Views/Repositories/RepositorySettingsViewModel.swift +2 −2
@@ -102,7 +102,7 @@ final class RepositorySettingsViewModel {
102102
103103 // MARK: - Branches (for HEAD picker)
104104
105 var branches: [Reference]
105 var branches: [ReferenceDetail]
106106
107107 // MARK: - Results
108108
@@ -112,7 +112,7 @@ final class RepositorySettingsViewModel {
112112
113113 init(
114114 repository: RepositorySummary,
115 branches: [Reference],
115 branches: [ReferenceDetail],
116116 client: SRHTClient
117117 ) {
118118 self.repositoryId = repository.id
codex-fix-refs-type-conflict.md added +89
@@ -0,0 +1,89 @@
1# Fix: Conflicting `[Reference]` vs `[ReferenceDetail]` in RepositorySettingsView
2
3## Background
4
5`RepositoryDetailViewModel.branches` was recently changed from `[Reference]` to
6`[ReferenceDetail]` to support displaying commit dates in the Refs tab.
7`ReferenceDetail` has the same `name: String` and `target: String?` fields as
8`Reference`, plus an additional `date: Date?`.
9
10`RepositorySettingsView` and `RepositorySettingsViewModel` still declare their
11`branches` parameter as `[Reference]`, causing this compiler error:
12
13```
14RepositoryDetailView.swift:56:51
15Conflicting arguments to generic parameter 'T' ('[ReferenceDetail]' vs. '[Reference]')
16```
17
18The settings view model only uses `branches` for the HEAD branch picker
19(`selectedHeadReferenceForSave()` accesses `$0.name`). No mapping back to
20`Reference` is needed — just update the type throughout the settings layer.
21
22---
23
24## Changes Required
25
26### 1. `Hutch/Views/Repositories/RepositorySettingsViewModel.swift`
27
28**Change the stored property type:**
29
30Find:
31```swift
32var branches: [Reference]
33```
34
35Replace with:
36```swift
37var branches: [ReferenceDetail]
38```
39
40**Change the `init` parameter type:**
41
42Find:
43```swift
44init(
45 repository: RepositorySummary,
46 branches: [Reference],
47 client: SRHTClient
48) {
49```
50
51Replace with:
52```swift
53init(
54 repository: RepositorySummary,
55 branches: [ReferenceDetail],
56 client: SRHTClient
57) {
58```
59
60### 2. `Hutch/Views/Repositories/RepositorySettingsView.swift`
61
62**Change the stored property type:**
63
64Find:
65```swift
66let branches: [Reference]
67```
68
69Replace with:
70```swift
71let branches: [ReferenceDetail]
72```
73
74---
75
76## No Other Changes
77
78- Do not modify `Reference` or `ReferenceDetail` in `Git.swift`.
79- Do not modify `RepositoryDetailView.swift` — the call site is already correct.
80- Do not modify `ReferencesListView.swift` or `RepositoryDetailViewModel.swift`.
81- `selectedHeadReferenceForSave()` in the settings view model accesses only
82 `$0.name` on each branch, which exists on `ReferenceDetail` — no logic
83 changes are needed.
84
85## Verification
86
87Build the project. The compiler error at `RepositoryDetailView.swift:56` should
88be gone. Confirm the repository settings sheet still opens and the HEAD branch
89picker populates correctly.