@@ -40,9 +40,11 @@ import Unison.DeclCoherencyCheck (asOneRandomIncoherentDeclReason)
4040import Unison.DeclNameLookup (DeclNameLookup )
4141import Unison.Merge qualified as Merge
4242import Unison.Merge.DiffOp qualified as Merge.DiffOp
43+ import Unison.Merge.Diffblob qualified as Merge
4344import Unison.Merge.ThreeWay qualified as Merge.ThreeWay
44- import Unison.Merge.TwoOrThreeWay qualified as TwoOrThreeWay
45+ import Unison.Merge.TwoOrThreeWay qualified as Merge. TwoOrThreeWay
4546import Unison.Merge.TwoWay qualified as Merge.TwoWay
47+ import Unison.Merge.Updated qualified as Merge.Updated
4648import Unison.Name (Name )
4749import Unison.NameSegment (NameSegment )
4850import Unison.NamesUtils qualified as NamesUtils
@@ -76,25 +78,47 @@ import Unison.Var (Var)
7678
7779handleDiffBranch :: DiffBranchArg -> DiffBranchArg -> Cli ()
7880handleDiffBranch aliceArg bobArg = do
79- let args = Merge. TwoWay {alice = aliceArg, bob = bobArg}
81+ let originalArgs = Merge. TwoWay {alice = aliceArg, bob = bobArg}
8082
8183 env <- ask
8284
8385 currentProject <- Cli. getCurrentProject
8486
85- (namespaces, diffblob) <-
87+ (namespaces, diffblob, swapped ) <-
8688 Cli. runTransactionWithRollback \ abort -> do
87- aliceAndBobCausalHashes <- traverse (resolveDiffBranchArg abort currentProject) args
88- lcaCausalHash <- Operations. lca aliceAndBobCausalHashes. alice aliceAndBobCausalHashes. bob
89- let causalHashes0 =
90- Merge. TwoOrThreeWay
91- { alice = aliceAndBobCausalHashes. alice,
92- bob = aliceAndBobCausalHashes. bob,
93- lca = lcaCausalHash
94- }
95-
96- let causalHashes =
97- TwoOrThreeWay. toThreeWay causalHashes0. alice causalHashes0
89+ causalHashes2 <-
90+ traverse (resolveDiffBranchArg abort currentProject) originalArgs
91+
92+ -- If the causal hashes are the same, there's certainly no diff to show
93+ when (Merge.TwoWay. twoWay (==) causalHashes2) do
94+ abort Output. ShowEmptyBranchDiff
95+
96+ maybeLcaCausalHash <-
97+ Operations. lca causalHashes2. alice causalHashes2. bob
98+
99+ -- From now on, all throughout the algorithm, a missing LCA means one of two things, which we treat uniformly:
100+ --
101+ -- 1. The LCA is Alice, i.e. this is is a fast-forward to Bob
102+ -- 2. The LCA is actually missing, i.e. the branches don't share a history
103+ --
104+ -- In both cases, we treat Alice as the effective LCA for the purpose of the diff. (In the no-history case, this
105+ -- allows users to see the diff between e.g. two squashed releases, in a readable/intuitive way, so long as they
106+ -- put the older release first on the command line.
107+ --
108+ -- You might wonder: what if the LCA is actually Bob, and Alice is ahead? We track that with a separate boolean,
109+ -- "swapped". If swapped, we're treating Alice as Bob and vice-versa, so just before displaying the diff, we swap
110+ -- them back. So, this case is also (1).
111+ let causalHashes :: Merge. TwoOrThreeWay CausalHash
112+ swapped :: Bool
113+ (causalHashes, swapped) =
114+ case maybeLcaCausalHash of
115+ Nothing -> (Merge.TwoWay. toTwoOrThreeWay Nothing causalHashes2, False )
116+ Just lcaCausalHash
117+ | lcaCausalHash == causalHashes2. alice ->
118+ (Merge.TwoWay. toTwoOrThreeWay Nothing causalHashes2, False )
119+ | lcaCausalHash == causalHashes2. bob ->
120+ (Merge.TwoWay. toTwoOrThreeWay Nothing (Merge.TwoWay. swap causalHashes2), True )
121+ | otherwise -> (Merge.TwoWay. toTwoOrThreeWay maybeLcaCausalHash causalHashes2, False )
98122
99123 namespaces <-
100124 for causalHashes (Codebase. expectBranchForHashTx env. codebase)
@@ -107,47 +131,74 @@ handleDiffBranch aliceArg bobArg = do
107131 Branch. asUnconflicted namespace
108132 & onLeft (abort . Output. ConflictedDefn )
109133
110- declNameLookups <- do
111- aliceAndBob <-
112- sequence $
113- ( \ x y z ->
114- Codebase. getBranchDeclNameLookup env. codebase (Branch. namespaceHash x) y
115- & onLeftM
116- ( abort
117- . Output. IncoherentDeclDuringDiffBranch z
118- . asOneRandomIncoherentDeclReason
119- )
120- )
121- <$> Merge.ThreeWay. forgetLca namespaces
122- <*> Merge.ThreeWay. forgetLca defns
123- <*> args
124- lca <- Codebase. getBranchPartialDeclNameLookup env. codebase (Branch. namespaceHash namespaces. lca) defns. lca
125- pure (Merge.ThreeWay. gfromTwoWay lca aliceAndBob)
134+ declNameLookups2 :: Merge. TwoWay DeclNameLookup <-
135+ sequence $
136+ ( \ x y z ->
137+ Codebase. getBranchDeclNameLookup env. codebase (Branch. namespaceHash x) y
138+ & onLeftM
139+ ( abort
140+ . Output. IncoherentDeclDuringDiffBranch z
141+ . asOneRandomIncoherentDeclReason
142+ )
143+ )
144+ <$> Merge.TwoOrThreeWay. forgetLca namespaces
145+ <*> Merge.TwoOrThreeWay. forgetLca defns
146+ <*> (if swapped then Merge.TwoWay. swap originalArgs else originalArgs)
126147
127148 diffblob <-
128- Merge. makeDiffblob
129- Merge. emptyDiffblobLog
130- (UpdateUtils. hydrateRefs env. codebase . fold)
131- (\ _ -> pure (Branch. toNames <$> namespaces0))
132- defns
133- (view Branch. libdeps_ <$> namespaces0)
134- declNameLookups
135-
136- pure (namespaces0, diffblob)
149+ -- These are all Just or all Nothing
150+ case (namespaces. lca, defns. lca) of
151+ (Just lcaNamespace, Just lcaDefns) -> do
152+ let namespaces0' = Merge.TwoOrThreeWay. toThreeWay (Branch. head lcaNamespace) namespaces0
153+ lcaDeclNameLookup <-
154+ Codebase. getBranchPartialDeclNameLookup env. codebase (Branch. namespaceHash lcaNamespace) lcaDefns
155+ Merge. makeDiffblob
156+ Merge. emptyDiffblobLog
157+ (UpdateUtils. hydrateRefs env. codebase . fold)
158+ (\ _ -> pure (Branch. toNames <$> namespaces0'))
159+ (Merge.TwoOrThreeWay. toThreeWay lcaDefns defns)
160+ (view Branch. libdeps_ <$> namespaces0')
161+ (Merge.TwoWay. gtoThreeWay lcaDeclNameLookup declNameLookups2)
162+ _ ->
163+ let f :: Merge. TwoOrThreeWay a -> Merge. Updated a
164+ f x =
165+ Merge. Updated x. alice x. bob
166+ in Merge. makeFastForwardDiffblob
167+ (UpdateUtils. hydrateRefs env. codebase . Merge.Updated. fold)
168+ (\ _ -> pure (Merge.Updated. map Branch. toNames (f namespaces0)))
169+ (f defns)
170+ (Merge.Updated. map (view Branch. libdeps_) (f namespaces0))
171+ (Merge.TwoWay. twoWay Merge. Updated declNameLookups2)
172+
173+ pure (namespaces0, diffblob, swapped)
174+
175+ let maybeSwap :: Merge. TwoWay a -> Merge. TwoWay a
176+ maybeSwap
177+ | swapped = Merge.TwoWay. swap
178+ | otherwise = id
137179
138180 -- Identify the set of all names changed (added, deleted, updated) on both branches.
139181 let changedNames :: DefnsF Set Name Name
140182 changedNames =
141183 foldMap (bimap Map. keysSet Map. keysSet) diffblob. diffsFromLCA
142184
143185 -- Restrict all definitions to just those changed names (regardless of which branch changed it)
144- let changedDefns :: Merge. ThreeWay (Defns (BiMultimap Referent Name ) (BiMultimap TypeReference Name ))
186+ let changedDefns :: Merge. TwoOrThreeWay (Defns (BiMultimap Referent Name ) (BiMultimap TypeReference Name ))
145187 changedDefns =
146- diffblob. defns <&> \ defns ->
147- NamesUtils. restrictNames changedNames defns. defns
188+ ( if isJust namespaces. lca
189+ then
190+ diffblob. defns
191+ & Merge.ThreeWay. toTwoOrThreeWay
192+ else
193+ diffblob. defns
194+ & Merge.ThreeWay. forgetLca
195+ & Merge.TwoWay. toTwoOrThreeWay Nothing
196+ )
197+ <&> \ defns ->
198+ NamesUtils. restrictNames changedNames defns. defns
148199
149200 -- Extract out just the builtins, to be rendered specially in the file later
150- let changedBuiltinDefns :: Merge. ThreeWay (DefnsF (Map Name ) Text Text )
201+ let changedBuiltinDefns :: Merge. TwoOrThreeWay (DefnsF (Map Name ) Text Text )
151202 changedBuiltinDefns =
152203 changedDefns
153204 <&> bimap
@@ -219,6 +270,7 @@ handleDiffBranch aliceArg bobArg = do
219270 lcaLibdepsDiff :: Map NameSegment CausalHash
220271 lcaLibdepsDiff =
221272 namespaces. lca
273+ & fromMaybe namespaces. alice -- a missing LCA means we're treating Alice as LCA
222274 & view Branch. libdeps_
223275 & (`Map.restrictKeys` deletedAndUpdatedLibdepsNames)
224276 & Map. map Branch. headHash
@@ -247,7 +299,7 @@ handleDiffBranch aliceArg bobArg = do
247299 }
248300 where
249301 slugs =
250- mangleDiffBranchArg <$> args
302+ mangleDiffBranchArg <$> maybeSwap originalArgs
251303
252304 let difftool =
253305 difftool0
@@ -259,23 +311,37 @@ handleDiffBranch aliceArg bobArg = do
259311
260312 exitCode <-
261313 liftIO do
262- for_
263- ( (,,,,,)
264- <$> filenames
265- <*> ( diffblob. declNameLookups
266- & over # lca (PartialDeclNameLookup. toDeclNameLookup Name. unsafeParseText)
267- & Merge.ThreeWay. gtoThreeWay
268- )
269- <*> namespaces
270- <*> diffblob. defns
271- <*> changedBuiltinDefns
272- <*> libdepsDiffs
273- )
274- \ (name, declNameLookup, namespace, defns, builtinDefns, libdeps) ->
275- env. writeSource
276- name
277- (renderUnisonFile declNameLookup namespace libdeps defns builtinDefns hydratedDefns)
278- True
314+ let renderedUnisonFiles :: Merge. ThreeWay Text
315+ renderedUnisonFiles =
316+ Merge.TwoWay. toThreeWay
317+ ( -- These either both have a Nothing lca or both have a Just lca
318+ case (namespaces. lca, changedBuiltinDefns. lca) of
319+ (Just lca, Just builtins) ->
320+ renderUnisonFile
321+ -- FIXME whoops, we can't always `unsafeParseText` out of a missing name in the LCA here...
322+ -- need a rendering function that knows how to print decls with missing names, I guess
323+ (PartialDeclNameLookup. toDeclNameLookup Name. unsafeParseText diffblob. declNameLookups. lca)
324+ lca
325+ libdepsDiffs. lca
326+ diffblob. defns. lca
327+ builtins
328+ hydratedDefns
329+ _ -> aliceAndBobFiles. alice
330+ )
331+ aliceAndBobFiles
332+ where
333+ aliceAndBobFiles :: Merge. TwoWay Text
334+ aliceAndBobFiles =
335+ renderUnisonFile
336+ <$> Merge.ThreeWay. gforgetLca diffblob. declNameLookups
337+ <*> Merge.TwoOrThreeWay. forgetLca namespaces
338+ <*> Merge.ThreeWay. forgetLca libdepsDiffs
339+ <*> Merge.ThreeWay. forgetLca diffblob. defns
340+ <*> Merge.TwoOrThreeWay. forgetLca changedBuiltinDefns
341+ <*> pure hydratedDefns
342+
343+ for_ ((,) <$> filenames <*> renderedUnisonFiles) \ (name, contents) ->
344+ env. writeSource name contents True
279345 let createProcess = (Process. shell (Text. unpack difftool)) {Process. delegate_ctlc = True }
280346 Process. withCreateProcess createProcess \ _ _ _ -> Process. waitForProcess
281347
@@ -360,10 +426,10 @@ handleDiffBranch aliceArg bobArg = do
360426
361427 Cli. respond $
362428 Output. ShowBranchDiff
363- args
364- ((. suffixifiedPPE) . Branch. toPrettyPrintEnvDecl 10 <$> Merge.ThreeWay . forgetLca namespaces)
365- (Map. map (Merge.DiffOp. map Branch. headHash) <$> diffblob. libdepsDiffs)
366- diffs
429+ originalArgs
430+ ((. suffixifiedPPE) . Branch. toPrettyPrintEnvDecl 10 <$> maybeSwap ( Merge.TwoOrThreeWay . forgetLca namespaces) )
431+ (Map. map (Merge.DiffOp. map Branch. headHash) <$> maybeSwap diffblob. libdepsDiffs)
432+ (maybeSwap diffs)
367433 maybeDifftoolResult
368434
369435resolveDiffBranchArg ::
0 commit comments