summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-01 23:37:12 -0500
committerChristian Cleberg <[email protected]>2026-04-01 23:37:12 -0500
commit0bef9acc515cfcb4ac6e2530cd34cbdbc47c2c1d (patch)
tree4457ca8d3fcc1f53383c9da46e19661a039834df
parenta83893afa086f0050c825144fa818024db4df056 (diff)
downloadhutch-2.8.1.tar.gz
hutch-2.8.1.tar.bz2
hutch-2.8.1.zip
feat: show commit dates in Refs tabv2.8.1
-rw-r--r--Hutch.xcodeproj/project.pbxproj16
-rw-r--r--Hutch/Models/Git.swift7
-rw-r--r--Hutch/Views/Repositories/ReferencesListView.swift13
-rw-r--r--Hutch/Views/Repositories/RepositoryDetailViewModel.swift42
-rw-r--r--Hutch/Views/Repositories/RepositorySettingsView.swift2
-rw-r--r--Hutch/Views/Repositories/RepositorySettingsViewModel.swift4
-rw-r--r--codex-fix-refs-type-conflict.md89
7 files changed, 153 insertions, 20 deletions
diff --git a/Hutch.xcodeproj/project.pbxproj b/Hutch.xcodeproj/project.pbxproj
index 71bbc39..cdab899 100644
--- a/Hutch.xcodeproj/project.pbxproj
+++ b/Hutch.xcodeproj/project.pbxproj
@@ -539,7 +539,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 19;
+ CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -556,7 +556,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2.8.0;
+ MARKETING_VERSION = 2.8.1;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -576,7 +576,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 19;
+ CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -593,7 +593,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2.8.0;
+ MARKETING_VERSION = 2.8.1;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -656,7 +656,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 19;
+ CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -666,7 +666,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 2.8.0;
+ MARKETING_VERSION = 2.8.1;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -685,7 +685,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 19;
+ CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -695,7 +695,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 2.8.0;
+ MARKETING_VERSION = 2.8.1;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
diff --git a/Hutch/Models/Git.swift b/Hutch/Models/Git.swift
index 905822e..a45086b 100644
--- a/Hutch/Models/Git.swift
+++ b/Hutch/Models/Git.swift
@@ -82,6 +82,13 @@ struct Reference: Codable, Sendable, Hashable {
let target: String?
}
+/// A Git reference enriched with the date of its tip commit or tag, for display in the Refs tab.
+struct ReferenceDetail: Sendable, Hashable {
+ let name: String
+ let target: String?
+ let date: Date?
+}
+
// MARK: - TreeEntry
/// An entry in a Git tree (file or directory).
diff --git a/Hutch/Views/Repositories/ReferencesListView.swift b/Hutch/Views/Repositories/ReferencesListView.swift
index 615ba25..6470a42 100644
--- a/Hutch/Views/Repositories/ReferencesListView.swift
+++ b/Hutch/Views/Repositories/ReferencesListView.swift
@@ -51,14 +51,21 @@ struct ReferencesListView: View {
}
private struct ReferenceRow: View {
- let reference: Reference
+ let reference: ReferenceDetail
let prefix: String
var body: some View {
HStack {
Label {
- Text(shortName)
- .font(.body.monospaced())
+ VStack(alignment: .leading, spacing: 2) {
+ Text(shortName)
+ .font(.body.monospaced())
+ if let date = reference.date {
+ Text(date.relativeDescription)
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ }
+ }
} icon: {
Image(systemName: icon)
.foregroundStyle(iconColor)
diff --git a/Hutch/Views/Repositories/RepositoryDetailViewModel.swift b/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
index e2eaf4d..72c4d1f 100644
--- a/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
+++ b/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
@@ -24,10 +24,33 @@ private struct RefsRepository: Decodable, Sendable {
}
private struct RefsPage: Decodable, Sendable {
- let results: [Reference]
+ let results: [ReferencePayload]
let cursor: String?
}
+/// Decodes a single reference including the optional follow object for date extraction.
+private struct ReferencePayload: Decodable, Sendable {
+ let name: String
+ let target: String?
+ let follow: ReferenceFollow?
+
+ func toDetail() -> ReferenceDetail {
+ // Branches and lightweight tags follow to a Commit (author.time).
+ // Annotated tags follow to a Tag object (tagger.time).
+ let date = follow?.author?.time ?? follow?.tagger?.time
+ return ReferenceDetail(name: name, target: target, date: date)
+ }
+}
+
+private struct ReferenceFollow: Decodable, Sendable {
+ let author: ReferenceSignature?
+ let tagger: ReferenceSignature?
+}
+
+private struct ReferenceSignature: Decodable, Sendable {
+ let time: Date
+}
+
private struct ReadmeResponse: Decodable, Sendable {
let repository: ReadmeRepository?
}
@@ -98,8 +121,8 @@ final class RepositoryDetailViewModel {
// MARK: - References state
- private(set) var branches: [Reference] = []
- private(set) var tags: [Reference] = []
+ private(set) var branches: [ReferenceDetail] = []
+ private(set) var tags: [ReferenceDetail] = []
private(set) var isLoadingRefs = false
// MARK: - README state
@@ -220,7 +243,14 @@ final class RepositoryDetailViewModel {
query refs($rid: ID!) {
repository(rid: $rid) {
references {
- results { name target }
+ results {
+ name
+ target
+ follow {
+ ... on Commit { author { time } }
+ ... on Tag { tagger { time } }
+ }
+ }
cursor
}
}
@@ -240,8 +270,8 @@ final class RepositoryDetailViewModel {
responseType: RefsResponse.self
)
let allRefs = result.repository?.references.results ?? []
- branches = allRefs.filter { $0.name.hasPrefix("refs/heads/") }
- tags = allRefs.filter { $0.name.hasPrefix("refs/tags/") }
+ branches = allRefs.filter { $0.name.hasPrefix("refs/heads/") }.map { $0.toDetail() }
+ tags = allRefs.filter { $0.name.hasPrefix("refs/tags/") }.map { $0.toDetail() }
} catch {
self.error = error.userFacingMessage
}
diff --git a/Hutch/Views/Repositories/RepositorySettingsView.swift b/Hutch/Views/Repositories/RepositorySettingsView.swift
index aca2ff9..98d3d8a 100644
--- a/Hutch/Views/Repositories/RepositorySettingsView.swift
+++ b/Hutch/Views/Repositories/RepositorySettingsView.swift
@@ -2,7 +2,7 @@ import SwiftUI
struct RepositorySettingsView: View {
let repository: RepositorySummary
- let branches: [Reference]
+ let branches: [ReferenceDetail]
let client: SRHTClient
let onRenamed: (String) -> Void
let onDeleted: () -> Void
diff --git a/Hutch/Views/Repositories/RepositorySettingsViewModel.swift b/Hutch/Views/Repositories/RepositorySettingsViewModel.swift
index 2847cb7..571ae85 100644
--- a/Hutch/Views/Repositories/RepositorySettingsViewModel.swift
+++ b/Hutch/Views/Repositories/RepositorySettingsViewModel.swift
@@ -102,7 +102,7 @@ final class RepositorySettingsViewModel {
// MARK: - Branches (for HEAD picker)
- var branches: [Reference]
+ var branches: [ReferenceDetail]
// MARK: - Results
@@ -112,7 +112,7 @@ final class RepositorySettingsViewModel {
init(
repository: RepositorySummary,
- branches: [Reference],
+ branches: [ReferenceDetail],
client: SRHTClient
) {
self.repositoryId = repository.id
diff --git a/codex-fix-refs-type-conflict.md b/codex-fix-refs-type-conflict.md
new file mode 100644
index 0000000..4ff2eec
--- /dev/null
+++ b/codex-fix-refs-type-conflict.md
@@ -0,0 +1,89 @@
+# Fix: Conflicting `[Reference]` vs `[ReferenceDetail]` in RepositorySettingsView
+
+## Background
+
+`RepositoryDetailViewModel.branches` was recently changed from `[Reference]` to
+`[ReferenceDetail]` to support displaying commit dates in the Refs tab.
+`ReferenceDetail` has the same `name: String` and `target: String?` fields as
+`Reference`, plus an additional `date: Date?`.
+
+`RepositorySettingsView` and `RepositorySettingsViewModel` still declare their
+`branches` parameter as `[Reference]`, causing this compiler error:
+
+```
+RepositoryDetailView.swift:56:51
+Conflicting arguments to generic parameter 'T' ('[ReferenceDetail]' vs. '[Reference]')
+```
+
+The settings view model only uses `branches` for the HEAD branch picker
+(`selectedHeadReferenceForSave()` accesses `$0.name`). No mapping back to
+`Reference` is needed — just update the type throughout the settings layer.
+
+---
+
+## Changes Required
+
+### 1. `Hutch/Views/Repositories/RepositorySettingsViewModel.swift`
+
+**Change the stored property type:**
+
+Find:
+```swift
+var branches: [Reference]
+```
+
+Replace with:
+```swift
+var branches: [ReferenceDetail]
+```
+
+**Change the `init` parameter type:**
+
+Find:
+```swift
+init(
+ repository: RepositorySummary,
+ branches: [Reference],
+ client: SRHTClient
+) {
+```
+
+Replace with:
+```swift
+init(
+ repository: RepositorySummary,
+ branches: [ReferenceDetail],
+ client: SRHTClient
+) {
+```
+
+### 2. `Hutch/Views/Repositories/RepositorySettingsView.swift`
+
+**Change the stored property type:**
+
+Find:
+```swift
+let branches: [Reference]
+```
+
+Replace with:
+```swift
+let branches: [ReferenceDetail]
+```
+
+---
+
+## No Other Changes
+
+- Do not modify `Reference` or `ReferenceDetail` in `Git.swift`.
+- Do not modify `RepositoryDetailView.swift` — the call site is already correct.
+- Do not modify `ReferencesListView.swift` or `RepositoryDetailViewModel.swift`.
+- `selectedHeadReferenceForSave()` in the settings view model accesses only
+ `$0.name` on each branch, which exists on `ReferenceDetail` — no logic
+ changes are needed.
+
+## Verification
+
+Build the project. The compiler error at `RepositoryDetailView.swift:56` should
+be gone. Confirm the repository settings sheet still opens and the HEAD branch
+picker populates correctly.