make a Unison.Merge module that re-exports most of the merge API

This commit is contained in:
Mitchell Rosen 2024-07-29 13:25:52 -04:00
parent 3df6418512
commit 43bfa09e43
6 changed files with 171 additions and 120 deletions

View File

@ -71,29 +71,14 @@ import Unison.DataDeclaration (Decl)
import Unison.DataDeclaration qualified as DataDeclaration
import Unison.Debug qualified as Debug
import Unison.Hash qualified as Hash
import Unison.Merge.CombineDiffs (CombinedDiffOp (..), combineDiffs)
import Unison.Merge qualified as Merge
import Unison.Merge.Database (MergeDatabase (..), makeMergeDatabase, referent2to1)
import Unison.Merge.DeclCoherencyCheck (checkDeclCoherency, lenientCheckDeclCoherency)
import Unison.Merge.DeclNameLookup (DeclNameLookup (..), expectConstructorNames)
import Unison.Merge.Diff qualified as Merge
import Unison.Merge.DiffOp (DiffOp (..))
import Unison.Merge.EitherWay (EitherWay (..))
import Unison.Merge.EitherWayI (EitherWayI (..))
import Unison.Merge.DeclNameLookup (expectConstructorNames)
import Unison.Merge.EitherWayI qualified as EitherWayI
import Unison.Merge.Libdeps qualified as Merge
import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup)
import Unison.Merge.PartitionCombinedDiffs (partitionCombinedDiffs)
import Unison.Merge.Synhashed (Synhashed (..))
import Unison.Merge.Synhashed qualified as Synhashed
import Unison.Merge.ThreeWay (ThreeWay (..))
import Unison.Merge.ThreeWay qualified as ThreeWay
import Unison.Merge.TwoOrThreeWay (TwoOrThreeWay (..))
import Unison.Merge.TwoWay (TwoWay (..))
import Unison.Merge.TwoWay qualified as TwoWay
import Unison.Merge.TwoWayI qualified as TwoWayI
import Unison.Merge.Unconflicts (Unconflicts (..))
import Unison.Merge.Unconflicts qualified as Unconflicts
import Unison.Merge.Updated (Updated (..))
import Unison.Name (Name)
import Unison.NameSegment qualified as NameSegment
import Unison.NameSegment.Internal (NameSegment (NameSegment))
@ -158,7 +143,7 @@ handleMerge (ProjectAndBranch maybeBobProjectName bobBranchName) = do
let bobProjectAndBranch = ProjectAndBranch bobProject bobProjectBranch
doMergeLocalBranch
TwoWay
Merge.TwoWay
{ alice = aliceProjectAndBranch,
bob = bobProjectAndBranch
}
@ -218,7 +203,7 @@ doMerge info = do
causals <- Cli.runTransaction do
traverse
Operations.expectCausalBranchByCausalHash
TwoOrThreeWay
Merge.TwoOrThreeWay
{ alice = info.alice.causalHash,
bob = info.bob.causalHash,
lca = info.lca.causalHash
@ -232,7 +217,7 @@ doMerge info = do
alice <- causals.alice.value
bob <- causals.bob.value
lca <- for causals.lca \causal -> causal.value
pure TwoOrThreeWay {lca, alice, bob}
pure Merge.TwoOrThreeWay {lca, alice, bob}
-- Assert that neither Alice nor Bob have defns in lib
for_ [(mergeTarget, branches.alice), (mergeSource, branches.bob)] \(who, branch) -> do
@ -246,21 +231,21 @@ doMerge info = do
Cli.runTransaction (loadNamespaceDefinitions (referent2to1 db) branch)
& onLeftM (done . Output.ConflictedDefn "merge")
let load = \case
Nothing -> pure (emptyNametree, DeclNameLookup Map.empty Map.empty)
Nothing -> pure (emptyNametree, Merge.DeclNameLookup Map.empty Map.empty)
Just (who, branch) -> do
defns <- loadDefns branch
declNameLookup <-
Cli.runTransaction (checkDeclCoherency db.loadDeclNumConstructors defns)
Cli.runTransaction (Merge.oldCheckDeclCoherency db.loadDeclNumConstructors defns)
& onLeftM (done . Output.IncoherentDeclDuringMerge who)
pure (defns, declNameLookup)
(aliceDefns0, aliceDeclNameLookup) <- load (Just (mergeTarget, branches.alice))
(bobDefns0, bobDeclNameLookup) <- load (Just (mergeSource, branches.bob))
lcaDefns0 <- maybe (pure emptyNametree) loadDefns branches.lca
lcaDeclNameLookup <- Cli.runTransaction (lenientCheckDeclCoherency db.loadDeclNumConstructors lcaDefns0)
lcaDeclNameLookup <- Cli.runTransaction (Merge.oldLenientCheckDeclCoherency db.loadDeclNumConstructors lcaDefns0)
let defns3 = flattenNametrees <$> ThreeWay {alice = aliceDefns0, bob = bobDefns0, lca = lcaDefns0}
let declNameLookups = TwoWay {alice = aliceDeclNameLookup, bob = bobDeclNameLookup}
let defns3 = flattenNametrees <$> Merge.ThreeWay {alice = aliceDefns0, bob = bobDefns0, lca = lcaDefns0}
let declNameLookups = Merge.TwoWay {alice = aliceDeclNameLookup, bob = bobDeclNameLookup}
pure (defns3, declNameLookups, lcaDeclNameLookup)
@ -269,23 +254,23 @@ doMerge info = do
liftIO (debugFunctions.debugDefns defns3 declNameLookups lcaDeclNameLookup)
-- Diff LCA->Alice and LCA->Bob
diffs <- Cli.runTransaction (Merge.nameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns3)
diffs <- Cli.runTransaction (Merge.oldNameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns3)
liftIO (debugFunctions.debugDiffs diffs)
-- Bail early if it looks like we can't proceed with the merge, because Alice or Bob has one or more conflicted alias
for_ ((,) <$> TwoWay mergeTarget mergeSource <*> diffs) \(who, diff) ->
for_ ((,) <$> Merge.TwoWay mergeTarget mergeSource <*> diffs) \(who, diff) ->
whenJust (findConflictedAlias defns3.lca diff) \(name1, name2) ->
done (Output.MergeConflictedAliases who name1 name2)
-- Combine the LCA->Alice and LCA->Bob diffs together
let diff = combineDiffs diffs
let diff = Merge.combineDiffs diffs
liftIO (debugFunctions.debugCombinedDiff diff)
-- Partition the combined diff into the conflicted things and the unconflicted things
(conflicts, unconflicts) <-
partitionCombinedDiffs defns declNameLookups diff & onLeft \name ->
Merge.partitionCombinedDiffs defns declNameLookups diff & onLeft \name ->
done (Output.MergeConflictInvolvingBuiltin name)
liftIO (debugFunctions.debugPartitionedDiff conflicts unconflicts)
@ -314,7 +299,7 @@ doMerge info = do
libdepsToBranch0 db (Merge.mergeLibdeps getTwoFreshNames libdeps)
-- Make PPE for Alice that contains all of Alice's names, but suffixified against her names + Bob's names
let mkPpes :: TwoWay Names -> Names -> TwoWay PrettyPrintEnvDecl
let mkPpes :: Merge.TwoWay Names -> Names -> Merge.TwoWay PrettyPrintEnvDecl
mkPpes defnsNames libdepsNames =
defnsNames <&> \names -> PPED.makePPED (PPE.namer (names <> libdepsNames)) suffixifier
where
@ -339,7 +324,7 @@ doMerge info = do
let prettyUnisonFile =
makePrettyUnisonFile
TwoWay
Merge.TwoWay
{ alice = into @Text aliceBranchNames,
bob =
case info.bob.source of
@ -398,7 +383,7 @@ doMerge info = do
Cli.respond finalOutput
doMergeLocalBranch :: TwoWay (ProjectAndBranch Project ProjectBranch) -> Cli ()
doMergeLocalBranch :: Merge.TwoWay (ProjectAndBranch Project ProjectBranch) -> Cli ()
doMergeLocalBranch branches = do
(aliceCausalHash, bobCausalHash, lcaCausalHash) <-
Cli.runTransaction do
@ -432,8 +417,8 @@ doMergeLocalBranch branches = do
-- Loading basic info out of the database
loadLibdeps ::
TwoOrThreeWay (V2.Branch Transaction) ->
Transaction (ThreeWay (Map NameSegment (V2.CausalBranch Transaction)))
Merge.TwoOrThreeWay (V2.Branch Transaction) ->
Transaction (Merge.ThreeWay (Map NameSegment (V2.CausalBranch Transaction)))
loadLibdeps branches = do
lca <-
case branches.lca of
@ -441,7 +426,7 @@ loadLibdeps branches = do
Just lcaBranch -> load lcaBranch
alice <- load branches.alice
bob <- load branches.bob
pure ThreeWay {lca, alice, bob}
pure Merge.ThreeWay {lca, alice, bob}
where
load :: V2.Branch Transaction -> Transaction (Map NameSegment (V2.CausalBranch Transaction))
load branch =
@ -466,9 +451,9 @@ hasDefnsInLib branch = do
-- Creating Unison files
makePrettyUnisonFile ::
TwoWay Text ->
TwoWay (DefnsF (Map Name) (Pretty ColorText) (Pretty ColorText)) ->
TwoWay (DefnsF (Map Name) (Pretty ColorText) (Pretty ColorText)) ->
Merge.TwoWay Text ->
Merge.TwoWay (DefnsF (Map Name) (Pretty ColorText) (Pretty ColorText)) ->
Merge.TwoWay (DefnsF (Map Name) (Pretty ColorText) (Pretty ColorText)) ->
Pretty ColorText
makePrettyUnisonFile authors conflicts dependents =
fold
@ -546,7 +531,7 @@ makePrettyUnisonFile authors conflicts dependents =
-- terms = { "foo", "Maybe.Nothing", "Maybe.Just" }
-- types = { "Maybe" }
-- }
refIdsToNames :: DeclNameLookup -> DefnsF (Map Name) term typ -> DefnsF Set Name Name
refIdsToNames :: Merge.DeclNameLookup -> DefnsF (Map Name) term typ -> DefnsF Set Name Name
refIdsToNames declNameLookup =
bifoldMap goTerms goTypes
where
@ -610,25 +595,25 @@ nametreeToBranch0 nametree =
-- FIXME: let's come up with a better term for "dependencies" in the implementation of this function
identifyDependents ::
TwoWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) ->
DefnsF Unconflicts Referent TypeReference ->
Transaction (TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId))
Merge.TwoWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) ->
DefnsF Merge.Unconflicts Referent TypeReference ->
Transaction (Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId))
identifyDependents defns conflicts unconflicts = do
let -- The other person's (i.e. with "Alice" and "Bob" swapped) solo-deleted and solo-updated names
theirSoloUpdatesAndDeletes :: TwoWay (DefnsF Set Name Name)
theirSoloUpdatesAndDeletes :: Merge.TwoWay (DefnsF Set Name Name)
theirSoloUpdatesAndDeletes =
TwoWay.swap (unconflictedSoloDeletedNames <> unconflictedSoloUpdatedNames)
where
unconflictedSoloDeletedNames :: TwoWay (DefnsF Set Name Name)
unconflictedSoloDeletedNames :: Merge.TwoWay (DefnsF Set Name Name)
unconflictedSoloDeletedNames =
bitraverse Unconflicts.soloDeletedNames Unconflicts.soloDeletedNames unconflicts
unconflictedSoloUpdatedNames :: TwoWay (DefnsF Set Name Name)
unconflictedSoloUpdatedNames :: Merge.TwoWay (DefnsF Set Name Name)
unconflictedSoloUpdatedNames =
bitraverse Unconflicts.soloUpdatedNames Unconflicts.soloUpdatedNames unconflicts
let dependencies :: TwoWay (Set Reference)
let dependencies :: Merge.TwoWay (Set Reference)
dependencies =
fold
[ -- One source of dependencies: Alice's versions of Bob's unconflicted deletes and updates, and vice-versa.
@ -668,7 +653,7 @@ identifyDependents defns conflicts unconflicts = do
-- 1. It is Alice-conflicted (since we only want to return *unconflicted* things).
-- 2. It was deleted by Bob.
-- 3. It was updated by Bob and not updated by Alice.
let dependents1 :: TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId)
let dependents1 :: Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId)
dependents1 =
zipDefnsWith Map.withoutKeys Map.withoutKeys
<$> dependents0
@ -689,7 +674,7 @@ identifyDependents defns conflicts unconflicts = do
--
-- { alice = { terms = {"foo" => #alice} } }
-- { bob = { terms = {} } }
let dependents2 :: TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId)
let dependents2 :: Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId)
dependents2 =
dependents1 & over #bob \bob ->
zipDefnsWith Map.difference Map.difference bob dependents1.alice
@ -697,20 +682,20 @@ identifyDependents defns conflicts unconflicts = do
pure dependents2
makeStageOne ::
TwoWay DeclNameLookup ->
TwoWay (DefnsF (Map Name) termid typeid) ->
DefnsF Unconflicts term typ ->
TwoWay (DefnsF (Map Name) termid typeid) ->
Merge.TwoWay Merge.DeclNameLookup ->
Merge.TwoWay (DefnsF (Map Name) termid typeid) ->
DefnsF Merge.Unconflicts term typ ->
Merge.TwoWay (DefnsF (Map Name) termid typeid) ->
DefnsF (Map Name) term typ ->
DefnsF (Map Name) term typ
makeStageOne declNameLookups conflicts unconflicts dependents =
zipDefnsWith3 makeStageOneV makeStageOneV unconflicts (f conflicts <> f dependents)
where
f :: TwoWay (DefnsF (Map Name) term typ) -> DefnsF Set Name Name
f :: Merge.TwoWay (DefnsF (Map Name) term typ) -> DefnsF Set Name Name
f defns =
fold (refIdsToNames <$> declNameLookups <*> defns)
makeStageOneV :: Unconflicts v -> Set Name -> Map Name v -> Map Name v
makeStageOneV :: Merge.Unconflicts v -> Set Name -> Map Name v -> Map Name v
makeStageOneV unconflicts namesToDelete =
(`Map.withoutKeys` namesToDelete) . Unconflicts.apply unconflicts
@ -786,33 +771,33 @@ findTemporaryBranchName projectId mergeSourceAndTarget = do
findConflictedAlias ::
(Ord term, Ord typ) =>
Defns (BiMultimap term Name) (BiMultimap typ Name) ->
DefnsF3 (Map Name) DiffOp Synhashed term typ ->
DefnsF3 (Map Name) Merge.DiffOp Merge.Synhashed term typ ->
Maybe (Name, Name)
findConflictedAlias defns diff =
asum [go defns.terms diff.terms, go defns.types diff.types]
where
go :: forall ref. (Ord ref) => BiMultimap ref Name -> Map Name (DiffOp (Synhashed ref)) -> Maybe (Name, Name)
go :: forall ref. (Ord ref) => BiMultimap ref Name -> Map Name (Merge.DiffOp (Merge.Synhashed ref)) -> Maybe (Name, Name)
go namespace diff =
asum (map f (Map.toList diff))
where
f :: (Name, DiffOp (Synhashed ref)) -> Maybe (Name, Name)
f :: (Name, Merge.DiffOp (Merge.Synhashed ref)) -> Maybe (Name, Name)
f (name, op) =
case op of
DiffOp'Add _ -> Nothing
DiffOp'Delete _ -> Nothing
DiffOp'Update hashed1 ->
Merge.DiffOp'Add _ -> Nothing
Merge.DiffOp'Delete _ -> Nothing
Merge.DiffOp'Update hashed1 ->
BiMultimap.lookupPreimage name namespace
& Set.delete name
& Set.toList
& map (g hashed1.new)
& asum
where
g :: Synhashed ref -> Name -> Maybe (Name, Name)
g :: Merge.Synhashed ref -> Name -> Maybe (Name, Name)
g hashed1 alias =
case Map.lookup alias diff of
Just (DiffOp'Update hashed2) | hashed1 == hashed2.new -> Nothing
Just (Merge.DiffOp'Update hashed2) | hashed1 == hashed2.new -> Nothing
-- If "foo" was updated but its alias "bar" was deleted, that's ok
Just (DiffOp'Delete _) -> Nothing
Just (Merge.DiffOp'Delete _) -> Nothing
_ -> Just (name, alias)
-- Given a name like "base", try "base__1", then "base__2", etc, until we find a name that doesn't
@ -904,19 +889,19 @@ typecheckedUnisonFileToBranchAdds tuf = do
-- Debugging by printing a bunch of stuff out
data DebugFunctions = DebugFunctions
{ debugCausals :: TwoOrThreeWay (V2.CausalBranch Transaction) -> IO (),
{ debugCausals :: Merge.TwoOrThreeWay (V2.CausalBranch Transaction) -> IO (),
debugDefns ::
ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
TwoWay DeclNameLookup ->
PartialDeclNameLookup ->
Merge.ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
Merge.TwoWay Merge.DeclNameLookup ->
Merge.PartialDeclNameLookup ->
IO (),
debugDiffs :: TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) -> IO (),
debugCombinedDiff :: DefnsF2 (Map Name) CombinedDiffOp Referent TypeReference -> IO (),
debugDiffs :: Merge.TwoWay (DefnsF3 (Map Name) Merge.DiffOp Merge.Synhashed Referent TypeReference) -> IO (),
debugCombinedDiff :: DefnsF2 (Map Name) Merge.CombinedDiffOp Referent TypeReference -> IO (),
debugPartitionedDiff ::
TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) ->
DefnsF Unconflicts Referent TypeReference ->
Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) ->
DefnsF Merge.Unconflicts Referent TypeReference ->
IO (),
debugDependents :: TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) -> IO (),
debugDependents :: Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) -> IO (),
debugStageOne :: DefnsF (Map Name) Referent TypeReference -> IO ()
}
@ -936,7 +921,7 @@ fakeDebugFunctions :: DebugFunctions
fakeDebugFunctions =
DebugFunctions mempty mempty mempty mempty mempty mempty mempty
realDebugCausals :: TwoOrThreeWay (V2.CausalBranch Transaction) -> IO ()
realDebugCausals :: Merge.TwoOrThreeWay (V2.CausalBranch Transaction) -> IO ()
realDebugCausals causals = do
Text.putStrLn (Text.bold "\n=== Alice causal hash ===")
Text.putStrLn (Hash.toBase32HexText (unCausalHash causals.alice.causalHash))
@ -948,9 +933,9 @@ realDebugCausals causals = do
Just causal -> "Just " <> Hash.toBase32HexText (unCausalHash causal.causalHash)
realDebugDefns ::
ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
TwoWay DeclNameLookup ->
PartialDeclNameLookup ->
Merge.ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
Merge.TwoWay Merge.DeclNameLookup ->
Merge.PartialDeclNameLookup ->
IO ()
realDebugDefns defns declNameLookups _lcaDeclNameLookup = do
Text.putStrLn (Text.bold "\n=== Alice definitions ===")
@ -965,19 +950,19 @@ realDebugDefns defns declNameLookups _lcaDeclNameLookup = do
Text.putStrLn (Text.bold "\n=== Bob constructor names ===")
debugConstructorNames declNameLookups.bob.declToConstructors
realDebugDiffs :: TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) -> IO ()
realDebugDiffs :: Merge.TwoWay (DefnsF3 (Map Name) Merge.DiffOp Merge.Synhashed Referent TypeReference) -> IO ()
realDebugDiffs diffs = do
Text.putStrLn (Text.bold "\n=== LCA→Alice diff ===")
renderDiff diffs.alice
Text.putStrLn (Text.bold "\n=== LCA→Bob diff ===")
renderDiff diffs.bob
where
renderDiff :: DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference -> IO ()
renderDiff :: DefnsF3 (Map Name) Merge.DiffOp Merge.Synhashed Referent TypeReference -> IO ()
renderDiff diff = do
renderThings referentLabel diff.terms
renderThings (const "type") diff.types
renderThings :: (ref -> Text) -> Map Name (DiffOp (Synhashed ref)) -> IO ()
renderThings :: (ref -> Text) -> Map Name (Merge.DiffOp (Merge.Synhashed ref)) -> IO ()
renderThings label things =
for_ (Map.toList things) \(name, op) ->
let go color action x =
@ -990,21 +975,21 @@ realDebugDiffs diffs = do
<> " #"
<> Hash.toBase32HexText (Synhashed.hash x)
in Text.putStrLn case op of
DiffOp'Add x -> go Text.green "+" x
DiffOp'Delete x -> go Text.red "-" x
DiffOp'Update x -> go Text.yellow "%" x.new
Merge.DiffOp'Add x -> go Text.green "+" x
Merge.DiffOp'Delete x -> go Text.red "-" x
Merge.DiffOp'Update x -> go Text.yellow "%" x.new
realDebugCombinedDiff :: DefnsF2 (Map Name) CombinedDiffOp Referent TypeReference -> IO ()
realDebugCombinedDiff :: DefnsF2 (Map Name) Merge.CombinedDiffOp Referent TypeReference -> IO ()
realDebugCombinedDiff diff = do
Text.putStrLn (Text.bold "\n=== Combined diff ===")
renderThings referentLabel Referent.toText diff.terms
renderThings (const "type") Reference.toText diff.types
where
renderThings :: (ref -> Text) -> (ref -> Text) -> Map Name (CombinedDiffOp ref) -> IO ()
renderThings :: (ref -> Text) -> (ref -> Text) -> Map Name (Merge.CombinedDiffOp ref) -> IO ()
renderThings label renderRef things =
for_ (Map.toList things) \(name, op) ->
Text.putStrLn case op of
CombinedDiffOp'Add who ->
Merge.CombinedDiffOp'Add who ->
Text.green $
"+ "
<> Text.italic (label (EitherWayI.value who))
@ -1015,7 +1000,7 @@ realDebugCombinedDiff diff = do
<> " ("
<> renderWho who
<> ")"
CombinedDiffOp'Delete who ->
Merge.CombinedDiffOp'Delete who ->
Text.red $
"- "
<> Text.italic (label (EitherWayI.value who))
@ -1026,7 +1011,7 @@ realDebugCombinedDiff diff = do
<> " ("
<> renderWho who
<> ")"
CombinedDiffOp'Update who ->
Merge.CombinedDiffOp'Update who ->
Text.yellow $
"% "
<> Text.italic (label (EitherWayI.value who).new)
@ -1037,7 +1022,7 @@ realDebugCombinedDiff diff = do
<> " ("
<> renderWho who
<> ")"
CombinedDiffOp'Conflict ref ->
Merge.CombinedDiffOp'Conflict ref ->
Text.magenta $
"! "
<> Text.italic (label ref.alice)
@ -1050,24 +1035,24 @@ realDebugCombinedDiff diff = do
<> "/"
<> renderRef ref.bob
renderWho :: EitherWayI v -> Text
renderWho :: Merge.EitherWayI v -> Text
renderWho = \case
OnlyAlice _ -> "Alice"
OnlyBob _ -> "Bob"
AliceAndBob _ -> "Alice and Bob"
Merge.OnlyAlice _ -> "Alice"
Merge.OnlyBob _ -> "Bob"
Merge.AliceAndBob _ -> "Alice and Bob"
realDebugPartitionedDiff ::
TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) ->
DefnsF Unconflicts Referent TypeReference ->
Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) ->
DefnsF Merge.Unconflicts Referent TypeReference ->
IO ()
realDebugPartitionedDiff conflicts unconflicts = do
Text.putStrLn (Text.bold "\n=== Alice conflicts ===")
renderConflicts "termid" conflicts.alice.terms (Alice ())
renderConflicts "typeid" conflicts.alice.types (Alice ())
renderConflicts "termid" conflicts.alice.terms (Merge.Alice ())
renderConflicts "typeid" conflicts.alice.types (Merge.Alice ())
Text.putStrLn (Text.bold "\n=== Bob conflicts ===")
renderConflicts "termid" conflicts.bob.terms (Bob ())
renderConflicts "typeid" conflicts.bob.types (Bob ())
renderConflicts "termid" conflicts.bob.terms (Merge.Bob ())
renderConflicts "typeid" conflicts.bob.types (Merge.Bob ())
Text.putStrLn (Text.bold "\n=== Alice unconflicts ===")
renderUnconflicts Text.green "+" referentLabel Referent.toText unconflicts.terms.adds.alice
@ -1093,7 +1078,7 @@ realDebugPartitionedDiff conflicts unconflicts = do
renderUnconflicts Text.yellow "%" referentLabel Referent.toText unconflicts.terms.updates.both
renderUnconflicts Text.yellow "%" (const "type") Reference.toText unconflicts.types.updates.both
where
renderConflicts :: Text -> Map Name Reference.Id -> EitherWay () -> IO ()
renderConflicts :: Text -> Map Name Reference.Id -> Merge.EitherWay () -> IO ()
renderConflicts label conflicts who =
for_ (Map.toList conflicts) \(name, ref) ->
Text.putStrLn $
@ -1105,7 +1090,7 @@ realDebugPartitionedDiff conflicts unconflicts = do
<> " "
<> Reference.idToText ref
<> " ("
<> (case who of Alice () -> "Alice"; Bob () -> "Bob")
<> (case who of Merge.Alice () -> "Alice"; Merge.Bob () -> "Bob")
<> ")"
renderUnconflicts ::
@ -1127,7 +1112,7 @@ realDebugPartitionedDiff conflicts unconflicts = do
<> " "
<> renderRef ref
realDebugDependents :: TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) -> IO ()
realDebugDependents :: Merge.TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId) -> IO ()
realDebugDependents dependents = do
Text.putStrLn (Text.bold "\n=== Alice dependents of Bob deletes, Bob updates, and Alice conflicts ===")
renderThings "termid" dependents.alice.terms

View File

@ -38,7 +38,7 @@ import Unison.Codebase.Path qualified as Path
import Unison.Codebase.SqliteCodebase.Operations qualified as Operations
import Unison.DataDeclaration (Decl)
import Unison.DataDeclaration qualified as Decl
import Unison.Merge.DeclCoherencyCheck (checkDeclCoherency)
import Unison.Merge.DeclCoherencyCheck (oldCheckDeclCoherency)
import Unison.Merge.DeclNameLookup (DeclNameLookup (..))
import Unison.Name (Name)
import Unison.Names (Names)
@ -84,7 +84,7 @@ handleUpdate2 = do
-- Assert that the namespace doesn't have any incoherent decls
declNameLookup <-
Cli.runTransaction (checkDeclCoherency Operations.expectDeclNumConstructors defns)
Cli.runTransaction (oldCheckDeclCoherency Operations.expectDeclNumConstructors defns)
& onLeftM (Cli.returnEarly . Output.IncoherentDeclDuringUpdate)
Cli.respond Output.UpdateLookingForDependents

View File

@ -0,0 +1,65 @@
module Unison.Merge
( -- * Decl coherency checks
DeclNameLookup (..),
PartialDeclNameLookup (..),
IncoherentDeclReason (..),
oldCheckDeclCoherency,
checkDeclCoherency,
oldLenientCheckDeclCoherency,
lenientCheckDeclCoherency,
IncoherentDeclReasons (..),
checkAllDeclCoherency,
-- * 3-way namespace diff
DiffOp (..),
oldNameBasedNamespaceDiff,
nameBasedNamespaceDiff,
-- * Combining namespace diffs
CombinedDiffOp (..),
combineDiffs,
-- * Partitioning combined namespace diffs
Unconflicts (..),
partitionCombinedDiffs,
-- * Merging libdeps
mergeLibdeps,
-- * Utility types
EitherWay (..),
ThreeWay (..),
TwoOrThreeWay (..),
EitherWayI (..),
Synhashed (..),
TwoWay (..),
TwoWayI (..),
Updated (..),
)
where
import Unison.Merge.CombineDiffs (CombinedDiffOp (..), combineDiffs)
import Unison.Merge.DeclCoherencyCheck
( IncoherentDeclReason (..),
IncoherentDeclReasons (..),
checkAllDeclCoherency,
checkDeclCoherency,
lenientCheckDeclCoherency,
oldCheckDeclCoherency,
oldLenientCheckDeclCoherency,
)
import Unison.Merge.DeclNameLookup (DeclNameLookup (..))
import Unison.Merge.Diff (nameBasedNamespaceDiff, oldNameBasedNamespaceDiff)
import Unison.Merge.DiffOp (DiffOp (..))
import Unison.Merge.EitherWay (EitherWay (..))
import Unison.Merge.EitherWayI (EitherWayI (..))
import Unison.Merge.Libdeps (mergeLibdeps)
import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup (..))
import Unison.Merge.PartitionCombinedDiffs (partitionCombinedDiffs)
import Unison.Merge.Synhashed (Synhashed (..))
import Unison.Merge.ThreeWay (ThreeWay (..))
import Unison.Merge.TwoOrThreeWay (TwoOrThreeWay (..))
import Unison.Merge.TwoWay (TwoWay (..))
import Unison.Merge.TwoWayI (TwoWayI (..))
import Unison.Merge.Unconflicts (Unconflicts (..))
import Unison.Merge.Updated (Updated (..))

View File

@ -81,10 +81,10 @@
-- machinery was invented.
module Unison.Merge.DeclCoherencyCheck
( IncoherentDeclReason (..),
oldCheckDeclCoherency,
checkDeclCoherency,
checkDeclCoherency2,
oldLenientCheckDeclCoherency,
lenientCheckDeclCoherency,
lenientCheckDeclCoherency2,
-- * Getting all failures rather than just the first
IncoherentDeclReasons (..),
@ -137,12 +137,12 @@ data IncoherentDeclReason
| IncoherentDeclReason'StrayConstructor !TypeReferenceId !Name
deriving stock (Show)
checkDeclCoherency ::
oldCheckDeclCoherency ::
(Monad m) =>
(TypeReferenceId -> m Int) ->
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
m (Either IncoherentDeclReason DeclNameLookup)
checkDeclCoherency loadDeclNumConstructors nametree =
oldCheckDeclCoherency loadDeclNumConstructors nametree =
Except.runExceptT $
checkDeclCoherencyWith
(lift . loadDeclNumConstructors)
@ -154,12 +154,12 @@ checkDeclCoherency loadDeclNumConstructors nametree =
}
nametree
checkDeclCoherency2 ::
checkDeclCoherency ::
(HasCallStack) =>
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
Map TypeReferenceId Int ->
Either IncoherentDeclReason DeclNameLookup
checkDeclCoherency2 nametree numConstructorsById =
checkDeclCoherency nametree numConstructorsById =
checkDeclCoherencyWith
(\refId -> Right (expectNumConstructors refId numConstructorsById))
OnIncoherentDeclReasons
@ -366,13 +366,13 @@ checkDeclCoherencyWith_DoTypes2 loadDeclNumConstructors callbacks go prefix chil
-- This function exists merely to extract a best-effort name mapping for the LCA of a merge. We require Alice and Bob to
-- have coherent decls, but their LCA is out of the user's control and may have incoherent decls, and whether or not it
-- does, we still need to compute *some* syntactic hash for its decls.
lenientCheckDeclCoherency ::
oldLenientCheckDeclCoherency ::
forall m.
(Monad m) =>
(TypeReferenceId -> m Int) ->
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
m PartialDeclNameLookup
lenientCheckDeclCoherency loadDeclNumConstructors =
oldLenientCheckDeclCoherency loadDeclNumConstructors =
fmap (view #declNameLookup)
. (`State.execStateT` LenientDeclCoherencyCheckState Map.empty (PartialDeclNameLookup Map.empty Map.empty))
. go []
@ -452,11 +452,11 @@ lenientCheckDeclCoherency loadDeclNumConstructors =
-- This function exists merely to extract a best-effort name mapping for the LCA of a merge. We require Alice and Bob to
-- have coherent decls, but their LCA is out of the user's control and may have incoherent decls, and whether or not it
-- does, we still need to compute *some* syntactic hash for its decls.
lenientCheckDeclCoherency2 ::
lenientCheckDeclCoherency ::
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
Map TypeReferenceId Int ->
PartialDeclNameLookup
lenientCheckDeclCoherency2 nametree numConstructorsById =
lenientCheckDeclCoherency nametree numConstructorsById =
nametree
& go []
& (`State.execState` LenientDeclCoherencyCheckState Map.empty (PartialDeclNameLookup Map.empty Map.empty))

View File

@ -1,6 +1,6 @@
module Unison.Merge.Diff
( nameBasedNamespaceDiff,
nameBasedNamespaceDiff2,
( oldNameBasedNamespaceDiff,
nameBasedNamespaceDiff,
)
where
@ -50,13 +50,13 @@ import Unison.Util.Defns (Defns (..), DefnsF2, DefnsF3, zipDefnsWith)
--
-- where each name is paired with its diff-op (added, deleted, or updated), relative to the LCA between Alice and Bob's
-- branches. If the hash of a name did not change, it will not appear in the map.
nameBasedNamespaceDiff ::
oldNameBasedNamespaceDiff ::
MergeDatabase ->
TwoWay DeclNameLookup ->
PartialDeclNameLookup ->
ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
Transaction (TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference))
nameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns = do
oldNameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns = do
lcaHashes <- synhashLcaDefns db ppe lcaDeclNameLookup defns.lca
hashes <- sequence (synhashDefns db ppe <$> declNameLookups <*> ThreeWay.forgetLca defns)
pure (diffNamespaceDefns lcaHashes <$> hashes)
@ -77,13 +77,13 @@ nameBasedNamespaceDiff db declNameLookups lcaDeclNameLookup defns = do
--
-- where each name is paired with its diff-op (added, deleted, or updated), relative to the LCA between Alice and Bob's
-- branches. If the hash of a name did not change, it will not appear in the map.
nameBasedNamespaceDiff2 ::
nameBasedNamespaceDiff ::
TwoWay DeclNameLookup ->
PartialDeclNameLookup ->
ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
Defns (Map TermReferenceId (Term Symbol Ann)) (Map TypeReferenceId (Decl Symbol Ann)) ->
TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference)
nameBasedNamespaceDiff2 declNameLookups lcaDeclNameLookup defns hydratedDefns =
nameBasedNamespaceDiff declNameLookups lcaDeclNameLookup defns hydratedDefns =
let lcaHashes = synhashLcaDefns2 ppe lcaDeclNameLookup defns.lca hydratedDefns
hashes = synhashDefns2 ppe hydratedDefns <$> declNameLookups <*> ThreeWay.forgetLca defns
in diffNamespaceDefns lcaHashes <$> hashes

View File

@ -17,6 +17,7 @@ source-repository head
library
exposed-modules:
Unison.Merge
Unison.Merge.CombineDiffs
Unison.Merge.Database
Unison.Merge.DeclCoherencyCheck