From 830df9efbefed702c71f6115a3a67790817ee268 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 10 Dec 2025 12:42:39 -0800 Subject: [PATCH 1/7] Add shell of Branch History endpoint --- share-api/src/Share/Web/Share/Branches/API.hs | 10 +++- .../src/Share/Web/Share/Branches/Impl.hs | 35 ++++++++++++- .../src/Share/Web/Share/Branches/Types.hs | 52 +++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/share-api/src/Share/Web/Share/Branches/API.hs b/share-api/src/Share/Web/Share/Branches/API.hs index 7a433a0e..9253d378 100644 --- a/share-api/src/Share/Web/Share/Branches/API.hs +++ b/share-api/src/Share/Web/Share/Branches/API.hs @@ -4,13 +4,13 @@ module Share.Web.Share.Branches.API where import Data.Time (UTCTime) +import Servant import Share.IDs import Share.Utils.API import Share.Utils.Caching -import Share.Web.Share.Branches.Types (BranchKindFilter, ShareBranch) +import Share.Web.Share.Branches.Types (BranchHistoryResponse, BranchKindFilter, ShareBranch) import Share.Web.Share.CodeBrowsing.API (CodeBrowseAPI) import Share.Web.Share.Types -import Servant import U.Codebase.HashTags (CausalHash) type ProjectBranchesAPI = @@ -22,6 +22,7 @@ type ProjectBranchResourceAPI = :<|> ("releaseNotes" :> ProjectBranchReleaseNotesEndpoint) :<|> ProjectBranchDetailsEndpoint :<|> ProjectBranchDeleteEndpoint + :<|> ProjectBranchHistoryEndpoint :<|> CodeBrowseAPI ) @@ -29,6 +30,11 @@ type ProjectBranchDetailsEndpoint = Get '[JSON] ShareBranch type ProjectBranchDeleteEndpoint = Delete '[JSON] () +type ProjectBranchHistoryEndpoint = + QueryParam "cursor" (Cursor CausalHash) + :> QueryParam "limit" Limit + :> Get '[JSON] BranchHistoryResponse + type ProjectBranchReadmeEndpoint = QueryParam "rootHash" CausalHash :> Get '[JSON] (Cached JSON ReadmeResponse) diff --git a/share-api/src/Share/Web/Share/Branches/Impl.hs b/share-api/src/Share/Web/Share/Branches/Impl.hs index fe78ee28..7997c9a5 100644 --- a/share-api/src/Share/Web/Share/Branches/Impl.hs +++ b/share-api/src/Share/Web/Share/Branches/Impl.hs @@ -40,7 +40,7 @@ import Share.Web.Authorization qualified as AuthZ import Share.Web.Errors import Share.Web.Share.Branches.API (ListBranchesCursor) import Share.Web.Share.Branches.API qualified as API -import Share.Web.Share.Branches.Types (BranchKindFilter (..), ShareBranch (..)) +import Share.Web.Share.Branches.Types (BranchHistoryCausal (..), BranchHistoryEntry (..), BranchHistoryResponse (..), BranchKindFilter (..), ShareBranch (..)) import Share.Web.Share.Branches.Types qualified as API import Share.Web.Share.CodeBrowsing.API qualified as API import Share.Web.Share.Contributions.Types @@ -89,6 +89,7 @@ branchesServer session userHandle projectSlug = :<|> getProjectBranchReleaseNotesEndpoint session userHandle projectSlug branchShortHand :<|> getProjectBranchDetailsEndpoint session userHandle projectSlug branchShortHand :<|> deleteProjectBranchEndpoint session userHandle projectSlug branchShortHand + :<|> branchHistoryEndpoint session userHandle projectSlug branchShortHand :<|> branchCodeBrowsingServer session userHandle projectSlug branchShortHand ) ) @@ -491,6 +492,38 @@ deleteProjectBranchEndpoint session userHandle projectSlug branchShortHand = do PG.runTransaction $ Q.softDeleteBranch branchId pure () +branchHistoryEndpoint :: + Maybe Session -> + UserHandle -> + ProjectSlug -> + BranchShortHand -> + Maybe (Cursor CausalHash) -> + Maybe Limit -> + WebApp BranchHistoryResponse +branchHistoryEndpoint (AuthN.MaybeAuthedUserID callerUserId) userHandle projectSlug branchRef@(BranchShortHand {contributorHandle, branchName}) mayCursor mayLimit = do + (Project {ownerUserId = projectOwnerUserId, projectId}, Branch {causal = branchHead, contributorId}) <- getProjectBranch projectBranchShortHand + authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkProjectBranchRead callerUserId projectId + let codebaseLoc = Codebase.codebaseLocationForProjectBranchCodebase projectOwnerUserId contributorId + let codebase = Codebase.codebaseEnv authZReceipt codebaseLoc + causalId <- resolveRootHash codebase branchHead Nothing + PG.runTransaction do + (causalHashesWithTimes, mayNextCursor) <- CausalQ.pagedCausalAncestors codebase causalId limit mayCursor + let history = + causalHashesWithTimes <&> \(causalHash) -> + BranchHistoryCausalEntry (BranchHistoryCausal {causalHash}) + pure $ + BranchHistoryResponse + { projectRef, + branchRef, + cursor = mayNextCursor, + history + } + where + projectRef = ProjectShortHand {userHandle, projectSlug} + limit = fromMaybe defaultLimit mayLimit + defaultLimit = Limit 20 + projectBranchShortHand = ProjectBranchShortHand {userHandle, projectSlug, contributorHandle, branchName} + getProjectBranchDocEndpoint :: Text -> Set NameSegment -> diff --git a/share-api/src/Share/Web/Share/Branches/Types.hs b/share-api/src/Share/Web/Share/Branches/Types.hs index bd70a576..14ad56b2 100644 --- a/share-api/src/Share/Web/Share/Branches/Types.hs +++ b/share-api/src/Share/Web/Share/Branches/Types.hs @@ -13,6 +13,8 @@ import Share.Branch (Branch (..)) import Share.IDs import Share.IDs qualified as IDs import Share.Postgres.IDs +import Share.Prelude +import Share.Utils.API (Cursor, (:++) (..)) import Share.Web.Share.Contributions.Types (ShareContribution) import Share.Web.Share.DisplayInfo.Types (UserDisplayInfo) import Share.Web.Share.Projects.Types @@ -75,3 +77,53 @@ instance ToHttpApiData BranchKindFilter where toQueryParam AllBranchKinds = "all" toQueryParam OnlyCoreBranches = "core" toQueryParam OnlyContributorBranches = "contributor" + +-- { +-- "projectRef": "@unison/base", +-- "branchRef": "main", +-- "prevCursor": "c-asdf-1234", +-- "nextCursor": "c-asdf-1234", +-- "history": [ +-- { +-- "tag": "Changeset", +-- "causalHash": "#asdf-1234", +-- } +-- ] +-- } + +data BranchHistoryResponse = BranchHistoryResponse + { projectRef :: ProjectShortHand, + branchRef :: BranchShortHand, + cursor :: Maybe (Cursor CausalHash), + history :: [BranchHistoryEntry] + } + deriving stock (Eq, Show) + +instance ToJSON BranchHistoryResponse where + toJSON BranchHistoryResponse {..} = + object + [ "projectRef" .= IDs.toText @ProjectShortHand projectRef, + "branchRef" .= IDs.toText @BranchShortHand branchRef, + "cursor" .= cursor, + "history" .= history + ] + +data BranchHistoryCausal = BranchHistoryCausal + { causalHash :: CausalHash + } + deriving stock (Eq, Show) + +instance ToJSON BranchHistoryCausal where + toJSON BranchHistoryCausal {..} = + object + [ "causalHash" .= causalHash + ] + +data BranchHistoryEntry + = BranchHistoryCausalEntry BranchHistoryCausal + deriving stock (Eq, Show) + +instance ToJSON BranchHistoryEntry where + toJSON = \case + (BranchHistoryCausalEntry causal) -> + toJSON (causal :++ (object ["tag" .= ("Changeset" :: Text)])) From 3915ab8916e13703adc49c0b6cadc4b56e3d8e99 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 17 Dec 2025 14:54:11 -0800 Subject: [PATCH 2/7] Implement paged causal history --- .../src/Share/Postgres/Causal/Queries.hs | 54 +++++++++++++++++++ share-api/src/Share/Web/Share/Branches/API.hs | 3 +- .../src/Share/Web/Share/Branches/Impl.hs | 12 ++--- .../src/Share/Web/Share/Branches/Types.hs | 7 ++- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/share-api/src/Share/Postgres/Causal/Queries.hs b/share-api/src/Share/Postgres/Causal/Queries.hs index 646c3323..27770aee 100644 --- a/share-api/src/Share/Postgres/Causal/Queries.hs +++ b/share-api/src/Share/Postgres/Causal/Queries.hs @@ -25,6 +25,9 @@ module Share.Postgres.Causal.Queries hashCausal, bestCommonAncestor, isFastForward, + pagedCausalAncestors, + CausalDepth, + CausalHistoryCursor, -- * Sync expectCausalEntity, @@ -54,6 +57,7 @@ import Share.Postgres.Patches.Queries qualified as PatchQ import Share.Postgres.Serialization qualified as S import Share.Postgres.Sync.Conversions qualified as Cv import Share.Prelude +import Share.Utils.API (Cursor (..), CursorDirection (..), Limit (..), Paged, guardPaged, pagedOn) import Share.Utils.Postgres (OrdBy, ordered) import Share.Web.Authorization.Types (RolePermission (..)) import Share.Web.Errors (MissingExpectedEntity (MissingExpectedEntity)) @@ -985,3 +989,53 @@ isFastForward fromCausalId toCausalId = do WHERE history.causal_id = #{fromCausalId} ); |] + +type CausalHistoryCursor = (CausalHash, CausalDepth) + +type CausalDepth = Int64 + +pagedCausalAncestors :: + (QueryM m) => + CausalId -> + Limit -> + Maybe (Cursor CausalHistoryCursor) -> + m (Paged CausalHistoryCursor CausalHash) +pagedCausalAncestors rootCausalId limit mayCursor = + do + let (filter, ordering, maybeReverse) = case mayCursor of + Nothing -> mempty + Just (Cursor (hash, depth) Next) -> ([sql| WHERE (h.causal_depth, h.hash) > (#{depth}, #{hash}) |], [sql| ORDER BY h.causal_depth DESC, h.hash ASC |], id) + Just (Cursor (hash, depth) Previous) -> ([sql| WHERE (h.causal_depth, h.hash) < (#{depth}, #{hash}) |], [sql| ORDER BY h.causal_depth ASC, h.hash DESC |], reverse) + rawResults <- + queryListRows @(CausalHash, CausalDepth) + [sql| + WITH RECURSIVE history(causal_id, causal_hash, causal_depth) AS ( + SELECT causal.id, causal.hash, cd.depth + FROM causals causal + JOIN causal_depth cd ON causal.id = cd.causal_id + WHERE causal.id = #{rootCausalId} + UNION + SELECT c.id, c.hash, cd.depth + FROM history h + JOIN causal_ancestors ca ON h.causal_id = ca.causal_id + JOIN causals c ON ca.ancestor_id = c.id + JOIN causal_depth cd ON c.id = cd.causal_id + ) SELECT h.causal_hash, h.causal_depth + FROM history h + ^{ordering} + ^{filter} + LIMIT #{limit + 1} + |] + let hasPrevPage = case mayCursor of + Just (Cursor _ Previous) -> length rawResults > fromIntegral (getLimit limit) + Just (Cursor _ Next) -> True + Nothing -> False + let hasNextPage = case mayCursor of + Just (Cursor _ Next) -> length rawResults > fromIntegral (getLimit limit) + Nothing -> length rawResults > fromIntegral (getLimit limit) + Just (Cursor _ Previous) -> True + pure rawResults + <&> maybeReverse + <&> pagedOn id + <&> guardPaged hasPrevPage hasNextPage + <&> fmap fst diff --git a/share-api/src/Share/Web/Share/Branches/API.hs b/share-api/src/Share/Web/Share/Branches/API.hs index 9253d378..c28f8a73 100644 --- a/share-api/src/Share/Web/Share/Branches/API.hs +++ b/share-api/src/Share/Web/Share/Branches/API.hs @@ -6,6 +6,7 @@ module Share.Web.Share.Branches.API where import Data.Time (UTCTime) import Servant import Share.IDs +import Share.Postgres.Causal.Queries (CausalHistoryCursor) import Share.Utils.API import Share.Utils.Caching import Share.Web.Share.Branches.Types (BranchHistoryResponse, BranchKindFilter, ShareBranch) @@ -31,7 +32,7 @@ type ProjectBranchDetailsEndpoint = Get '[JSON] ShareBranch type ProjectBranchDeleteEndpoint = Delete '[JSON] () type ProjectBranchHistoryEndpoint = - QueryParam "cursor" (Cursor CausalHash) + QueryParam "cursor" (Cursor CausalHistoryCursor) :> QueryParam "limit" Limit :> Get '[JSON] BranchHistoryResponse diff --git a/share-api/src/Share/Web/Share/Branches/Impl.hs b/share-api/src/Share/Web/Share/Branches/Impl.hs index 7997c9a5..4f52fc88 100644 --- a/share-api/src/Share/Web/Share/Branches/Impl.hs +++ b/share-api/src/Share/Web/Share/Branches/Impl.hs @@ -20,6 +20,7 @@ import Share.IDs (BranchId, BranchShortHand (..), ProjectBranchShortHand (..), P import Share.IDs qualified as IDs import Share.OAuth.Session import Share.Postgres qualified as PG +import Share.Postgres.Causal.Queries (CausalHistoryCursor) import Share.Postgres.Causal.Queries qualified as CausalQ import Share.Postgres.Contributions.Queries qualified as ContributionsQ import Share.Postgres.IDs (CausalId) @@ -497,7 +498,7 @@ branchHistoryEndpoint :: UserHandle -> ProjectSlug -> BranchShortHand -> - Maybe (Cursor CausalHash) -> + Maybe (Cursor CausalHistoryCursor) -> Maybe Limit -> WebApp BranchHistoryResponse branchHistoryEndpoint (AuthN.MaybeAuthedUserID callerUserId) userHandle projectSlug branchRef@(BranchShortHand {contributorHandle, branchName}) mayCursor mayLimit = do @@ -507,15 +508,14 @@ branchHistoryEndpoint (AuthN.MaybeAuthedUserID callerUserId) userHandle projectS let codebase = Codebase.codebaseEnv authZReceipt codebaseLoc causalId <- resolveRootHash codebase branchHead Nothing PG.runTransaction do - (causalHashesWithTimes, mayNextCursor) <- CausalQ.pagedCausalAncestors codebase causalId limit mayCursor - let history = - causalHashesWithTimes <&> \(causalHash) -> - BranchHistoryCausalEntry (BranchHistoryCausal {causalHash}) + history <- + CausalQ.pagedCausalAncestors causalId limit mayCursor + <&> fmap \(causalHash) -> + BranchHistoryCausalEntry (BranchHistoryCausal {causalHash}) pure $ BranchHistoryResponse { projectRef, branchRef, - cursor = mayNextCursor, history } where diff --git a/share-api/src/Share/Web/Share/Branches/Types.hs b/share-api/src/Share/Web/Share/Branches/Types.hs index 14ad56b2..03997a39 100644 --- a/share-api/src/Share/Web/Share/Branches/Types.hs +++ b/share-api/src/Share/Web/Share/Branches/Types.hs @@ -12,9 +12,10 @@ import Servant.API (FromHttpApiData (..), ToHttpApiData (..)) import Share.Branch (Branch (..)) import Share.IDs import Share.IDs qualified as IDs +import Share.Postgres.Causal.Queries (CausalHistoryCursor) import Share.Postgres.IDs import Share.Prelude -import Share.Utils.API (Cursor, (:++) (..)) +import Share.Utils.API (Paged, (:++) (..)) import Share.Web.Share.Contributions.Types (ShareContribution) import Share.Web.Share.DisplayInfo.Types (UserDisplayInfo) import Share.Web.Share.Projects.Types @@ -94,8 +95,7 @@ instance ToHttpApiData BranchKindFilter where data BranchHistoryResponse = BranchHistoryResponse { projectRef :: ProjectShortHand, branchRef :: BranchShortHand, - cursor :: Maybe (Cursor CausalHash), - history :: [BranchHistoryEntry] + history :: Paged CausalHistoryCursor BranchHistoryEntry } deriving stock (Eq, Show) @@ -104,7 +104,6 @@ instance ToJSON BranchHistoryResponse where object [ "projectRef" .= IDs.toText @ProjectShortHand projectRef, "branchRef" .= IDs.toText @BranchShortHand branchRef, - "cursor" .= cursor, "history" .= history ] From 2623867d29cf1be236ceed35efdf1e23148ca976 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 17 Dec 2025 14:59:09 -0800 Subject: [PATCH 3/7] Fix history routing --- share-api/src/Share/Web/Share/Branches/API.hs | 2 +- share-api/src/Share/Web/Share/Branches/Impl.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share-api/src/Share/Web/Share/Branches/API.hs b/share-api/src/Share/Web/Share/Branches/API.hs index c28f8a73..5e4a1e09 100644 --- a/share-api/src/Share/Web/Share/Branches/API.hs +++ b/share-api/src/Share/Web/Share/Branches/API.hs @@ -21,9 +21,9 @@ type ProjectBranchesAPI = type ProjectBranchResourceAPI = ( ("readme" :> ProjectBranchReadmeEndpoint) :<|> ("releaseNotes" :> ProjectBranchReleaseNotesEndpoint) + :<|> ("history" :> ProjectBranchHistoryEndpoint) :<|> ProjectBranchDetailsEndpoint :<|> ProjectBranchDeleteEndpoint - :<|> ProjectBranchHistoryEndpoint :<|> CodeBrowseAPI ) diff --git a/share-api/src/Share/Web/Share/Branches/Impl.hs b/share-api/src/Share/Web/Share/Branches/Impl.hs index 4f52fc88..75676dd4 100644 --- a/share-api/src/Share/Web/Share/Branches/Impl.hs +++ b/share-api/src/Share/Web/Share/Branches/Impl.hs @@ -88,9 +88,9 @@ branchesServer session userHandle projectSlug = hoistServer (Proxy @API.ProjectBranchResourceAPI) (addTags branchShortHand) $ ( getProjectBranchReadmeEndpoint session userHandle projectSlug branchShortHand :<|> getProjectBranchReleaseNotesEndpoint session userHandle projectSlug branchShortHand + :<|> branchHistoryEndpoint session userHandle projectSlug branchShortHand :<|> getProjectBranchDetailsEndpoint session userHandle projectSlug branchShortHand :<|> deleteProjectBranchEndpoint session userHandle projectSlug branchShortHand - :<|> branchHistoryEndpoint session userHandle projectSlug branchShortHand :<|> branchCodeBrowsingServer session userHandle projectSlug branchShortHand ) ) From ac9891e85d8edc683a2c460793db88e7b6c591e1 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 17 Dec 2025 14:59:09 -0800 Subject: [PATCH 4/7] Add transcript for branch history --- transcripts/share-apis/branches/prelude.md | 22 ++++++++++++++++++++++ transcripts/share-apis/branches/run.zsh | 5 +++++ 2 files changed, 27 insertions(+) create mode 100644 transcripts/share-apis/branches/prelude.md diff --git a/transcripts/share-apis/branches/prelude.md b/transcripts/share-apis/branches/prelude.md new file mode 100644 index 00000000..3bd347dc --- /dev/null +++ b/transcripts/share-apis/branches/prelude.md @@ -0,0 +1,22 @@ +```ucm +branch-with-history/main> builtins.merge builtins +``` + +```unison +x1 = 1 +``` + +```ucm +branch-with-history/main> update +branch-with-history/main> alias.term x1 x2 +branch-with-history/main> alias.term x2 x3 +branch-with-history/main> alias.term x3 x4 +branch-with-history/main> alias.term x4 x5 +branch-with-history/main> alias.term x5 x6 +branch-with-history/main> alias.term x6 x7 +branch-with-history/main> alias.term x7 x8 +branch-with-history/main> alias.term x8 x9 +branch-with-history/main> alias.term x9 x10 +branch-with-history/main> push @transcripts/branch-with-history/main +``` + diff --git a/transcripts/share-apis/branches/run.zsh b/transcripts/share-apis/branches/run.zsh index 05d8874e..cbe9e2f9 100755 --- a/transcripts/share-apis/branches/run.zsh +++ b/transcripts/share-apis/branches/run.zsh @@ -55,3 +55,8 @@ fetch "$test_user" DELETE branch-delete '/users/test/projects/publictestproject/ # Branch should no longer exist fetch "$test_user" GET branch-details-deleted '/users/test/projects/publictestproject/branches/main' + +# Add some history to a branch. +transcript_ucm transcript prelude.md + +fetch "$transcripts_user" GET branch-history '/users/transcripts/projects/branch-browse/branches/main/history?limit=3' From f17d5159f1bc15479d90c61bfe266f2d60b77a9e Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 17 Dec 2025 15:00:39 -0800 Subject: [PATCH 5/7] More paging logic fixes --- share-api/src/Share/Postgres/Causal/Queries.hs | 9 +++++---- share-api/src/Share/Web/Share/Branches/Impl.hs | 3 +++ .../share-apis/branches/out/branch-history.json | 16 ++++++++++++++++ transcripts/share-apis/branches/prelude.md | 5 +---- transcripts/share-apis/branches/run.zsh | 2 +- 5 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 transcripts/share-apis/branches/out/branch-history.json diff --git a/share-api/src/Share/Postgres/Causal/Queries.hs b/share-api/src/Share/Postgres/Causal/Queries.hs index 27770aee..50c64062 100644 --- a/share-api/src/Share/Postgres/Causal/Queries.hs +++ b/share-api/src/Share/Postgres/Causal/Queries.hs @@ -1003,9 +1003,9 @@ pagedCausalAncestors :: pagedCausalAncestors rootCausalId limit mayCursor = do let (filter, ordering, maybeReverse) = case mayCursor of - Nothing -> mempty - Just (Cursor (hash, depth) Next) -> ([sql| WHERE (h.causal_depth, h.hash) > (#{depth}, #{hash}) |], [sql| ORDER BY h.causal_depth DESC, h.hash ASC |], id) - Just (Cursor (hash, depth) Previous) -> ([sql| WHERE (h.causal_depth, h.hash) < (#{depth}, #{hash}) |], [sql| ORDER BY h.causal_depth ASC, h.hash DESC |], reverse) + Nothing -> (mempty, [sql| ORDER BY (h.causal_depth, h.causal_hash) DESC |], id) + Just (Cursor (hash, depth) Next) -> ([sql| WHERE (h.causal_depth, h.causal_hash) < (#{depth}, #{hash}) |], [sql| ORDER BY (h.causal_depth, h.causal_hash) DESC |], id) + Just (Cursor (hash, depth) Previous) -> ([sql| WHERE (h.causal_depth, h.causal_hash) > (#{depth}, #{hash}) |], [sql| ORDER BY (h.causal_depth , h.causal_hash) ASC |], reverse) rawResults <- queryListRows @(CausalHash, CausalDepth) [sql| @@ -1022,8 +1022,8 @@ pagedCausalAncestors rootCausalId limit mayCursor = JOIN causal_depth cd ON c.id = cd.causal_id ) SELECT h.causal_hash, h.causal_depth FROM history h - ^{ordering} ^{filter} + ^{ordering} LIMIT #{limit + 1} |] let hasPrevPage = case mayCursor of @@ -1036,6 +1036,7 @@ pagedCausalAncestors rootCausalId limit mayCursor = Just (Cursor _ Previous) -> True pure rawResults <&> maybeReverse + <&> take (fromIntegral (getLimit limit)) <&> pagedOn id <&> guardPaged hasPrevPage hasNextPage <&> fmap fst diff --git a/share-api/src/Share/Web/Share/Branches/Impl.hs b/share-api/src/Share/Web/Share/Branches/Impl.hs index 75676dd4..a207f530 100644 --- a/share-api/src/Share/Web/Share/Branches/Impl.hs +++ b/share-api/src/Share/Web/Share/Branches/Impl.hs @@ -50,6 +50,7 @@ import Share.Web.Share.Projects.Types (projectToAPI) import Share.Web.Share.Types import U.Codebase.HashTags (CausalHash) import Unison.Codebase.Path qualified as Path +import Unison.Debug qualified as Debug import Unison.HashQualified qualified as HQ import Unison.Name (Name) import Unison.NameSegment.Internal (NameSegment (..)) @@ -507,11 +508,13 @@ branchHistoryEndpoint (AuthN.MaybeAuthedUserID callerUserId) userHandle projectS let codebaseLoc = Codebase.codebaseLocationForProjectBranchCodebase projectOwnerUserId contributorId let codebase = Codebase.codebaseEnv authZReceipt codebaseLoc causalId <- resolveRootHash codebase branchHead Nothing + Debug.debugM Debug.Temp "branchHistoryEndpoint Resolved causalId: " causalId PG.runTransaction do history <- CausalQ.pagedCausalAncestors causalId limit mayCursor <&> fmap \(causalHash) -> BranchHistoryCausalEntry (BranchHistoryCausal {causalHash}) + Debug.debugM Debug.Temp "branchHistoryEndpoint Retrieved history entries: " (history) pure $ BranchHistoryResponse { projectRef, diff --git a/transcripts/share-apis/branches/out/branch-history.json b/transcripts/share-apis/branches/out/branch-history.json new file mode 100644 index 00000000..0234ae69 --- /dev/null +++ b/transcripts/share-apis/branches/out/branch-history.json @@ -0,0 +1,16 @@ +{ + "body": { + "branchRef": "main", + "history": { + "items": [], + "nextCursor": null, + "prevCursor": null + }, + "projectRef": "@transcripts/branch-with-history" + }, + "status": [ + { + "status_code": 200 + } + ] +} diff --git a/transcripts/share-apis/branches/prelude.md b/transcripts/share-apis/branches/prelude.md index 3bd347dc..46473a84 100644 --- a/transcripts/share-apis/branches/prelude.md +++ b/transcripts/share-apis/branches/prelude.md @@ -1,7 +1,3 @@ -```ucm -branch-with-history/main> builtins.merge builtins -``` - ```unison x1 = 1 ``` @@ -17,6 +13,7 @@ branch-with-history/main> alias.term x6 x7 branch-with-history/main> alias.term x7 x8 branch-with-history/main> alias.term x8 x9 branch-with-history/main> alias.term x9 x10 +branch-with-history/main> history branch-with-history/main> push @transcripts/branch-with-history/main ``` diff --git a/transcripts/share-apis/branches/run.zsh b/transcripts/share-apis/branches/run.zsh index cbe9e2f9..75b16967 100755 --- a/transcripts/share-apis/branches/run.zsh +++ b/transcripts/share-apis/branches/run.zsh @@ -59,4 +59,4 @@ fetch "$test_user" GET branch-details-deleted '/users/test/projects/publictestpr # Add some history to a branch. transcript_ucm transcript prelude.md -fetch "$transcripts_user" GET branch-history '/users/transcripts/projects/branch-browse/branches/main/history?limit=3' +fetch "$transcripts_user" GET branch-history '/users/transcripts/projects/branch-with-history/branches/main/history?limit=3' From 0dcb81b2cb366441b198d58f8e2b67ef0055c9cd Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 17 Dec 2025 15:51:50 -0800 Subject: [PATCH 6/7] Rerun branch history transcripts --- .../out/branch-history-next-page.json | 29 +++++++++++++++++++ .../out/branch-history-prev-page.json | 29 +++++++++++++++++++ .../branches/out/branch-history.json | 17 +++++++++-- transcripts/share-apis/branches/run.zsh | 7 +++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 transcripts/share-apis/branches/out/branch-history-next-page.json create mode 100644 transcripts/share-apis/branches/out/branch-history-prev-page.json diff --git a/transcripts/share-apis/branches/out/branch-history-next-page.json b/transcripts/share-apis/branches/out/branch-history-next-page.json new file mode 100644 index 00000000..fdd47f1f --- /dev/null +++ b/transcripts/share-apis/branches/out/branch-history-next-page.json @@ -0,0 +1,29 @@ +{ + "body": { + "branchRef": "main", + "history": { + "items": [ + { + "causalHash": "qt7njnf877g2q9g1f44eaigmcrgsdphc74jkdoos0i1pq6hguo03697adec2cb8lvgp71d4st3ss59g0haut5pfs5rpd4dru6pidt0g", + "tag": "Changeset" + }, + { + "causalHash": "2k8f975ovhkm64bvog66rr8i7e4dripu8nkl7u62oif96if23lh5fh73n8p9qg21or98n4aljunidn6avonqpt1eu971h74iqlmgk5g", + "tag": "Changeset" + }, + { + "causalHash": "n8b2r8o7u4f8ct0u79rhamnb16l3jhhr8986nudm6cgvg2j1fetouu0ojuiums5vtt8imsnsa7ek6lt18tcq3pf9knsinpsiqrq1r5o", + "tag": "Changeset" + } + ], + "nextCursor": "", + "prevCursor": "" + }, + "projectRef": "@transcripts/branch-with-history" + }, + "status": [ + { + "status_code": 200 + } + ] +} diff --git a/transcripts/share-apis/branches/out/branch-history-prev-page.json b/transcripts/share-apis/branches/out/branch-history-prev-page.json new file mode 100644 index 00000000..6a7673a9 --- /dev/null +++ b/transcripts/share-apis/branches/out/branch-history-prev-page.json @@ -0,0 +1,29 @@ +{ + "body": { + "branchRef": "main", + "history": { + "items": [ + { + "causalHash": "bpm4dqkous3flfcu8t07jhirta2i5kondhbqa7u3plg2racaohhdupam5k1bm7pnlbhpuphih36mdufuhsnv2832ri45u1nvn0j4qr8", + "tag": "Changeset" + }, + { + "causalHash": "6h6qn76m9053vmg8a36cilbdnolked4uqh6bgm4qkpflpmr2pji3pais6f74k7364avo0rqn9kgdfje8pqldph0u52u8kjc8j923v00", + "tag": "Changeset" + }, + { + "causalHash": "hqul4i7u7gud3u5ovcdgbgdsmvfnh3o98baolc5f5g35aic1j84jtd97otnn0reuig39jnnsp7376j4adsko3v12o4h09vqc2drbbd8", + "tag": "Changeset" + } + ], + "nextCursor": "", + "prevCursor": null + }, + "projectRef": "@transcripts/branch-with-history" + }, + "status": [ + { + "status_code": 200 + } + ] +} diff --git a/transcripts/share-apis/branches/out/branch-history.json b/transcripts/share-apis/branches/out/branch-history.json index 0234ae69..6a7673a9 100644 --- a/transcripts/share-apis/branches/out/branch-history.json +++ b/transcripts/share-apis/branches/out/branch-history.json @@ -2,8 +2,21 @@ "body": { "branchRef": "main", "history": { - "items": [], - "nextCursor": null, + "items": [ + { + "causalHash": "bpm4dqkous3flfcu8t07jhirta2i5kondhbqa7u3plg2racaohhdupam5k1bm7pnlbhpuphih36mdufuhsnv2832ri45u1nvn0j4qr8", + "tag": "Changeset" + }, + { + "causalHash": "6h6qn76m9053vmg8a36cilbdnolked4uqh6bgm4qkpflpmr2pji3pais6f74k7364avo0rqn9kgdfje8pqldph0u52u8kjc8j923v00", + "tag": "Changeset" + }, + { + "causalHash": "hqul4i7u7gud3u5ovcdgbgdsmvfnh3o98baolc5f5g35aic1j84jtd97otnn0reuig39jnnsp7376j4adsko3v12o4h09vqc2drbbd8", + "tag": "Changeset" + } + ], + "nextCursor": "", "prevCursor": null }, "projectRef": "@transcripts/branch-with-history" diff --git a/transcripts/share-apis/branches/run.zsh b/transcripts/share-apis/branches/run.zsh index 75b16967..bf35e32d 100755 --- a/transcripts/share-apis/branches/run.zsh +++ b/transcripts/share-apis/branches/run.zsh @@ -60,3 +60,10 @@ fetch "$test_user" GET branch-details-deleted '/users/test/projects/publictestpr transcript_ucm transcript prelude.md fetch "$transcripts_user" GET branch-history '/users/transcripts/projects/branch-with-history/branches/main/history?limit=3' + +next_cursor=$(fetch_data_jq "$transcripts_user" GET branch-history-next-cursor '/users/transcripts/projects/branch-with-history/branches/main/history?limit=3' '.history.nextCursor') + +fetch "$transcripts_user" GET branch-history-next-page "/users/transcripts/projects/branch-with-history/branches/main/history?limit=3&cursor=$next_cursor" '.history.prevCursor' +prev_cursor=$(fetch_data_jq "$transcripts_user" GET branch-history-prev-cursor "/users/transcripts/projects/branch-with-history/branches/main/history?limit=3&cursor=$next_cursor" '.history.prevCursor') + +fetch "$transcripts_user" GET branch-history-prev-page "/users/transcripts/projects/branch-with-history/branches/main/history?limit=3&cursor=$prev_cursor" From 8187533c1c39afd5443318767872dafcff358e36 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 17 Dec 2025 16:13:18 -0800 Subject: [PATCH 7/7] Fix servant-client build --- share-api/src/Share/Web/Share/Branches/Impl.hs | 3 --- share-api/src/Share/Web/Share/Branches/Types.hs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/share-api/src/Share/Web/Share/Branches/Impl.hs b/share-api/src/Share/Web/Share/Branches/Impl.hs index a207f530..75676dd4 100644 --- a/share-api/src/Share/Web/Share/Branches/Impl.hs +++ b/share-api/src/Share/Web/Share/Branches/Impl.hs @@ -50,7 +50,6 @@ import Share.Web.Share.Projects.Types (projectToAPI) import Share.Web.Share.Types import U.Codebase.HashTags (CausalHash) import Unison.Codebase.Path qualified as Path -import Unison.Debug qualified as Debug import Unison.HashQualified qualified as HQ import Unison.Name (Name) import Unison.NameSegment.Internal (NameSegment (..)) @@ -508,13 +507,11 @@ branchHistoryEndpoint (AuthN.MaybeAuthedUserID callerUserId) userHandle projectS let codebaseLoc = Codebase.codebaseLocationForProjectBranchCodebase projectOwnerUserId contributorId let codebase = Codebase.codebaseEnv authZReceipt codebaseLoc causalId <- resolveRootHash codebase branchHead Nothing - Debug.debugM Debug.Temp "branchHistoryEndpoint Resolved causalId: " causalId PG.runTransaction do history <- CausalQ.pagedCausalAncestors causalId limit mayCursor <&> fmap \(causalHash) -> BranchHistoryCausalEntry (BranchHistoryCausal {causalHash}) - Debug.debugM Debug.Temp "branchHistoryEndpoint Retrieved history entries: " (history) pure $ BranchHistoryResponse { projectRef, diff --git a/share-api/src/Share/Web/Share/Branches/Types.hs b/share-api/src/Share/Web/Share/Branches/Types.hs index 03997a39..01a8997f 100644 --- a/share-api/src/Share/Web/Share/Branches/Types.hs +++ b/share-api/src/Share/Web/Share/Branches/Types.hs @@ -107,6 +107,13 @@ instance ToJSON BranchHistoryResponse where "history" .= history ] +instance FromJSON BranchHistoryResponse where + parseJSON = withObject "BranchHistoryResponse" $ \o -> + BranchHistoryResponse + <$> (o .: "projectRef") + <*> (o .: "branchRef") + <*> o .: "history" + data BranchHistoryCausal = BranchHistoryCausal { causalHash :: CausalHash } @@ -118,6 +125,11 @@ instance ToJSON BranchHistoryCausal where [ "causalHash" .= causalHash ] +instance FromJSON BranchHistoryCausal where + parseJSON = withObject "BranchHistoryCausal" $ \o -> + BranchHistoryCausal + <$> o .: "causalHash" + data BranchHistoryEntry = BranchHistoryCausalEntry BranchHistoryCausal deriving stock (Eq, Show) @@ -126,3 +138,8 @@ instance ToJSON BranchHistoryEntry where toJSON = \case (BranchHistoryCausalEntry causal) -> toJSON (causal :++ (object ["tag" .= ("Changeset" :: Text)])) + +instance FromJSON BranchHistoryEntry where + parseJSON v = do + causal <- parseJSON v + return $ BranchHistoryCausalEntry causal