Skip to content

Commit 0642fc3

Browse files
authored
Merge pull request #5543 from unisonweb/syncv2/file-sync
Allow syncing to/from a local file
2 parents b83f2f7 + b4adcb6 commit 0642fc3

File tree

34 files changed

+1661
-80
lines changed

34 files changed

+1661
-80
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dist-newstyle
2727
*.prof.html
2828
*.hp
2929
*.ps
30+
*.profiterole.*
3031
/.direnv/
3132
/.envrc
3233

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/Branch/Format.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ data BranchLocalIds' t d p c = LocalIds
101101
branchPatchLookup :: Vector p,
102102
branchChildLookup :: Vector c
103103
}
104-
deriving (Show)
104+
deriving (Show, Eq)
105105

106106
-- | Bytes encoding a LocalBranch
107107
newtype LocalBranchBytes = LocalBranchBytes ByteString
@@ -110,6 +110,7 @@ newtype LocalBranchBytes = LocalBranchBytes ByteString
110110
data SyncBranchFormat' parent text defn patch child
111111
= SyncFull (BranchLocalIds' text defn patch child) LocalBranchBytes
112112
| SyncDiff parent (BranchLocalIds' text defn patch child) LocalBranchBytes
113+
deriving (Eq, Show)
113114

114115
type SyncBranchFormat = SyncBranchFormat' BranchObjectId TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId)
115116

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/Causal.hs

+1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ data SyncCausalFormat' causalHash valueHash = SyncCausalFormat
2222
{ valueHash :: valueHash,
2323
parents :: Vector causalHash
2424
}
25+
deriving stock (Eq, Show)
2526

2627
type SyncCausalFormat = SyncCausalFormat' CausalHashId BranchHashId

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/Decl/Format.hs

+2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ type SyncDeclFormat =
3636

3737
data SyncDeclFormat' t d
3838
= SyncDecl (SyncLocallyIndexedComponent' t d)
39+
deriving stock (Eq, Show)
3940

4041
newtype SyncLocallyIndexedComponent' t d
4142
= SyncLocallyIndexedComponent (Vector (LocalIds' t d, ByteString))
43+
deriving stock (Eq, Show)
4244

4345
-- [OldDecl] ==map==> [NewDecl] ==number==> [(NewDecl, Int)] ==sort==> [(NewDecl, Int)] ==> permutation is map snd of that
4446

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/Entity.hs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ data SyncEntity' text hash defn patch branchh branch causal
2424
| N (Namespace.SyncBranchFormat' branch text defn patch (branch, causal))
2525
| P (Patch.SyncPatchFormat' patch text hash defn)
2626
| C (Causal.SyncCausalFormat' causal branchh)
27+
deriving stock (Eq, Show)
2728

2829
entityType :: SyncEntity' text hash defn patch branchh branch causal -> TempEntityType
2930
entityType = \case

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ data LocalIds' t h = LocalIds
1515
{ textLookup :: Vector t,
1616
defnLookup :: Vector h
1717
}
18-
deriving (Functor, Show)
18+
deriving stock (Functor, Show, Eq)
1919

2020
type LocalIds = LocalIds' TextId ObjectId
2121

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/Format.hs

+2
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ data PatchLocalIds' t h d = LocalIds
4040
patchHashLookup :: Vector h,
4141
patchDefnLookup :: Vector d
4242
}
43+
deriving stock (Eq, Show)
4344

4445
type SyncPatchFormat = SyncPatchFormat' PatchObjectId TextId HashId ObjectId
4546

4647
data SyncPatchFormat' parent text hash defn
4748
= SyncFull (PatchLocalIds' text hash defn) ByteString
4849
| -- | p is the identity of the thing that the diff is relative to
4950
SyncDiff parent (PatchLocalIds' text hash defn) ByteString
51+
deriving stock (Eq, Show)
5052

5153
-- | Apply a list of patch diffs to a patch, left to right.
5254
applyPatchDiffs :: Patch -> [PatchDiff] -> Patch

Diff for: codebase2/codebase-sqlite/U/Codebase/Sqlite/Term/Format.hs

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ newtype LocallyIndexedComponent' t d = LocallyIndexedComponent
4949

5050
newtype SyncLocallyIndexedComponent' t d
5151
= SyncLocallyIndexedComponent (Vector (LocalIds' t d, ByteString))
52+
deriving stock (Eq, Show)
5253

5354
{-
5455
message = "hello, world" -> ABT { ... { Term.F.Text "hello, world" } } -> hashes to (#abc, 0)
@@ -127,6 +128,7 @@ data TermFormat' t d = Term (LocallyIndexedComponent' t d)
127128
type SyncTermFormat = SyncTermFormat' TextId ObjectId
128129

129130
data SyncTermFormat' t d = SyncTerm (SyncLocallyIndexedComponent' t d)
131+
deriving stock (Eq, Show)
130132

131133
data WatchResultFormat
132134
= WatchResult WatchLocalIds Term

Diff for: codebase2/codebase-sqlite/unison-codebase-sqlite.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 1.12
22

3-
-- This file has been generated from package.yaml by hpack version 0.36.0.
3+
-- This file has been generated from package.yaml by hpack version 0.37.0.
44
--
55
-- see: https://github.com/sol/hpack
66

Diff for: hie.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ cradle:
140140
- path: "unison-share-api/src"
141141
component: "unison-share-api:lib"
142142

143+
- path: "unison-share-api/tests"
144+
component: "unison-share-api:test:unison-share-api-tests"
145+
143146
- path: "unison-share-projects-api/src"
144147
component: "unison-share-projects-api:lib"
145148

Diff for: lib/unison-sqlite/src/Unison/Sqlite.hs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Unison.Sqlite
1919
Transaction,
2020
runTransaction,
2121
runTransactionWithRollback,
22+
runTransactionExceptT,
2223
runReadOnlyTransaction,
2324
runWriteTransaction,
2425
cacheTransaction,

Diff for: lib/unison-sqlite/src/Unison/Sqlite/Transaction.hs

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Unison.Sqlite.Transaction
33
Transaction,
44
runTransaction,
55
runTransactionWithRollback,
6+
runTransactionExceptT,
67
runReadOnlyTransaction,
78
runWriteTransaction,
89
cacheTransaction,
@@ -44,6 +45,7 @@ where
4445

4546
import Control.Concurrent (threadDelay)
4647
import Control.Exception (Exception (fromException), onException, throwIO)
48+
import Control.Monad.Trans.Except (ExceptT, runExceptT)
4749
import Control.Monad.Trans.Reader (ReaderT (..))
4850
import Data.Text qualified as Text
4951
import Data.Unique (Unique, newUnique)
@@ -130,6 +132,13 @@ runTransactionWithRollback conn transaction = liftIO do
130132
Right x -> pure x
131133
{-# SPECIALIZE runTransactionWithRollback :: Connection -> ((forall void. a -> Transaction void) -> Transaction a) -> IO a #-}
132134

135+
-- | Run a transaction wrapped in an 'ExceptT'. If the ExceptT fails, the transaction is rolled back.
136+
runTransactionExceptT :: (MonadIO m, HasCallStack) => Connection -> ExceptT e Transaction a -> m (Either e a)
137+
runTransactionExceptT conn transaction = runTransactionWithRollback conn \rollback -> do
138+
runExceptT transaction >>= \case
139+
Left e -> rollback (Left e)
140+
Right a -> pure (Right a)
141+
133142
-- | Run a transaction that is known to only perform reads.
134143
--
135144
-- The action is provided a function that peels off the 'Transaction' newtype without sending the corresponding

Diff for: parser-typechecker/src/Unison/Codebase.hs

+10
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ module Unison.Codebase
9999
-- * Direct codebase access
100100
runTransaction,
101101
runTransactionWithRollback,
102+
runTransactionExceptT,
102103
withConnection,
103104
withConnectionIO,
104105

@@ -112,6 +113,7 @@ module Unison.Codebase
112113
)
113114
where
114115

116+
import Control.Monad.Except (ExceptT)
115117
import Data.Map qualified as Map
116118
import Data.Set qualified as Set
117119
import U.Codebase.Branch qualified as V2Branch
@@ -174,6 +176,14 @@ runTransactionWithRollback ::
174176
runTransactionWithRollback Codebase {withConnection} action =
175177
withConnection \conn -> Sqlite.runTransactionWithRollback conn action
176178

179+
runTransactionExceptT ::
180+
(MonadIO m) =>
181+
Codebase m v a ->
182+
ExceptT e Sqlite.Transaction b ->
183+
m (Either e b)
184+
runTransactionExceptT Codebase {withConnection} action =
185+
withConnection \conn -> Sqlite.runTransactionExceptT conn action
186+
177187
getShallowCausalAtPathFromRootHash ::
178188
-- Causal to start at, if Nothing use the codebase's root branch.
179189
CausalHash ->

Diff for: unison-cli/package.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ library:
2020
- condition: "!os(windows)"
2121
dependencies: unix
2222
dependencies:
23+
- attoparsec
2324
- Diff
2425
- IntervalMap
2526
- ListLike
@@ -32,7 +33,10 @@ library:
3233
- co-log-core
3334
- code-page
3435
- concurrent-output
36+
- conduit
3537
- containers >= 0.6.3
38+
- conduit
39+
- conduit-extra
3640
- cryptonite
3741
- either
3842
- errors
@@ -65,8 +69,10 @@ library:
6569
- recover-rtti
6670
- regex-tdfa
6771
- semialign
72+
- serialise
6873
- servant
6974
- servant-client
75+
- servant-conduit
7076
- stm
7177
- temporary
7278
- text-ansi

Diff for: unison-cli/src/Unison/Cli/MonadUtils.hs

+8
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ module Unison.Cli.MonadUtils
4949
stepManyAtM,
5050
updateProjectBranchRoot,
5151
updateProjectBranchRoot_,
52+
setProjectBranchRootToCausalHash,
5253
updateAtM,
5354
updateAt,
5455
updateAndStepAt,
@@ -454,6 +455,13 @@ updateProjectBranchRoot projectBranch reason f = do
454455
liftIO (env.lspCheckForChanges projectPathIds)
455456
pure result
456457

458+
setProjectBranchRootToCausalHash :: ProjectBranch -> Text -> CausalHash -> Cli ()
459+
setProjectBranchRootToCausalHash projectBranch reason targetCH = do
460+
Cli.time "setProjectBranchRootToCausalHash" do
461+
Cli.runTransaction $ do
462+
targetCHID <- Q.expectCausalHashIdByCausalHash targetCH
463+
Q.setProjectBranchHead reason (projectBranch ^. #projectId) (projectBranch ^. #branchId) targetCHID
464+
457465
updateProjectBranchRoot_ :: ProjectBranch -> Text -> (Branch IO -> Branch IO) -> Cli ()
458466
updateProjectBranchRoot_ projectBranch reason f = do
459467
updateProjectBranchRoot projectBranch reason (\b -> pure (f b, ()))

Diff for: unison-cli/src/Unison/Codebase/Editor/HandleInput.hs

+13
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import Unison.Codebase.Editor.HandleInput.ReleaseDraft (handleReleaseDraft)
8888
import Unison.Codebase.Editor.HandleInput.Run (handleRun)
8989
import Unison.Codebase.Editor.HandleInput.RuntimeUtils qualified as RuntimeUtils
9090
import Unison.Codebase.Editor.HandleInput.ShowDefinition (handleShowDefinition)
91+
import Unison.Codebase.Editor.HandleInput.SyncV2 qualified as SyncV2
9192
import Unison.Codebase.Editor.HandleInput.TermResolution (resolveMainRef)
9293
import Unison.Codebase.Editor.HandleInput.Tests qualified as Tests
9394
import Unison.Codebase.Editor.HandleInput.Todo (handleTodo)
@@ -668,6 +669,13 @@ loop e = do
668669
Cli.respond Success
669670
PullI sourceTarget pullMode -> handlePull sourceTarget pullMode
670671
PushRemoteBranchI pushRemoteBranchInput -> handlePushRemoteBranch pushRemoteBranchInput
672+
SyncToFileI syncFileDest projectBranchName -> SyncV2.handleSyncToFile syncFileDest projectBranchName
673+
SyncFromFileI syncFileSrc projectBranchName -> do
674+
description <- inputDescription input
675+
SyncV2.handleSyncFromFile description syncFileSrc projectBranchName
676+
SyncFromCodebaseI srcCodebasePath srcBranch destBranch -> do
677+
description <- inputDescription input
678+
SyncV2.handleSyncFromCodebase description srcCodebasePath srcBranch destBranch
671679
ListDependentsI hq -> handleDependents hq
672680
ListDependenciesI hq -> handleDependencies hq
673681
NamespaceDependenciesI path -> handleNamespaceDependencies path
@@ -992,6 +1000,11 @@ inputDescription input =
9921000
ProjectsI -> wat
9931001
PullI {} -> wat
9941002
PushRemoteBranchI {} -> wat
1003+
SyncToFileI {} -> wat
1004+
SyncFromFileI fp pab ->
1005+
pure $ "sync.from-file " <> into @Text fp <> " " <> into @Text pab
1006+
SyncFromCodebaseI fp srcBranch destBranch -> do
1007+
pure $ "sync.from-file " <> into @Text fp <> " " <> into @Text srcBranch <> " " <> into @Text destBranch
9951008
QuitI {} -> wat
9961009
ReleaseDraftI {} -> wat
9971010
ShowDefinitionI {} -> wat
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
module Unison.Codebase.Editor.HandleInput.SyncV2
2+
( handleSyncToFile,
3+
handleSyncFromFile,
4+
handleSyncFromCodebase,
5+
)
6+
where
7+
8+
import Control.Lens
9+
import Control.Monad.Reader (MonadReader (..))
10+
import U.Codebase.Sqlite.Queries qualified as Q
11+
import Unison.Cli.Monad (Cli)
12+
import Unison.Cli.Monad qualified as Cli
13+
import Unison.Cli.MonadUtils qualified as Cli
14+
import Unison.Cli.ProjectUtils qualified as Project
15+
import Unison.Codebase (CodebasePath)
16+
import Unison.Codebase qualified as Codebase
17+
import Unison.Codebase.Editor.Output qualified as Output
18+
import Unison.Codebase.Init qualified as Init
19+
import Unison.Codebase.SqliteCodebase qualified as SqliteCodebase
20+
import Unison.Prelude
21+
import Unison.Project (ProjectAndBranch (..), ProjectBranchName, ProjectName)
22+
import Unison.Share.SyncV2 qualified as SyncV2
23+
import Unison.SyncV2.Types (BranchRef)
24+
25+
handleSyncToFile :: FilePath -> ProjectAndBranch (Maybe ProjectName) (Maybe ProjectBranchName) -> Cli ()
26+
handleSyncToFile destSyncFile branchToSync = do
27+
pp <- Cli.getCurrentProjectPath
28+
projectBranch <- Project.resolveProjectBranchInProject (pp ^. #project) branchToSync
29+
causalHash <- Cli.runTransaction $ Project.getProjectBranchCausalHash (projectBranch ^. #branch)
30+
let branchRef = into @BranchRef $ ProjectAndBranch (projectBranch ^. #project . #name) (projectBranch ^. #branch . #name)
31+
Cli.Env {codebase} <- ask
32+
liftIO (SyncV2.syncToFile codebase causalHash (Just branchRef) destSyncFile) >>= \case
33+
Left err -> Cli.respond (Output.SyncPullError err)
34+
Right _ -> pure ()
35+
36+
handleSyncFromFile :: Text -> FilePath -> ProjectAndBranch (Maybe ProjectName) ProjectBranchName -> Cli ()
37+
handleSyncFromFile description srcSyncFile branchToSync = do
38+
pp <- Cli.getCurrentProjectPath
39+
projectBranch <- Project.resolveProjectBranchInProject (pp ^. #project) (over #branch Just branchToSync)
40+
let shouldValidate = True
41+
SyncV2.syncFromFile shouldValidate srcSyncFile >>= \case
42+
Left err -> Cli.respond (Output.SyncPullError err)
43+
Right causalHash -> do
44+
Cli.setProjectBranchRootToCausalHash (projectBranch ^. #branch) description causalHash
45+
46+
handleSyncFromCodebase :: Text -> CodebasePath -> ProjectAndBranch ProjectName ProjectBranchName -> ProjectAndBranch (Maybe ProjectName) ProjectBranchName -> Cli ()
47+
handleSyncFromCodebase description srcCodebasePath srcBranch destBranch = do
48+
Cli.Env {codebase} <- ask
49+
pp <- Cli.getCurrentProjectPath
50+
projectBranch <- Project.resolveProjectBranchInProject (pp ^. #project) (over #branch Just destBranch)
51+
r <- liftIO $ Init.withOpenCodebase SqliteCodebase.init "sync-src" srcCodebasePath Init.DontLock (Init.MigrateAfterPrompt Init.Backup Init.Vacuum) \srcCodebase -> do
52+
Codebase.withConnection srcCodebase \srcConn -> do
53+
maySrcCausalHash <- Codebase.runTransaction srcCodebase $ do
54+
let ProjectAndBranch srcProjName srcBranchName = srcBranch
55+
runMaybeT do
56+
project <- MaybeT (Q.loadProjectByName srcProjName)
57+
branch <- MaybeT (Q.loadProjectBranchByName (project ^. #projectId) srcBranchName)
58+
lift $ Project.getProjectBranchCausalHash branch
59+
case maySrcCausalHash of
60+
Nothing -> pure $ Left (Output.SyncFromCodebaseMissingProjectBranch srcBranch)
61+
Just srcCausalHash -> do
62+
let shouldValidate = True
63+
Right . fmap (const srcCausalHash) <$> liftIO (SyncV2.syncFromCodebase shouldValidate srcConn codebase srcCausalHash)
64+
65+
case r of
66+
Left openCodebaseErr -> Cli.respond (Output.OpenCodebaseError srcCodebasePath openCodebaseErr)
67+
Right (Left errOutput) -> Cli.respond errOutput
68+
Right (Right (Right causalHash)) -> do
69+
Cli.setProjectBranchRootToCausalHash (projectBranch ^. #branch) description causalHash
70+
Right (Right (Left syncErr)) -> do
71+
Cli.respond (Output.SyncPullError syncErr)

Diff for: unison-cli/src/Unison/Codebase/Editor/Input.hs

+3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ data Input
136136
| DiffNamespaceI BranchId2 BranchId2 -- old new
137137
| PullI !PullSourceTarget !PullMode
138138
| PushRemoteBranchI PushRemoteBranchInput
139+
| SyncToFileI FilePath (ProjectAndBranch (Maybe ProjectName) (Maybe ProjectBranchName))
140+
| SyncFromFileI FilePath UnresolvedProjectBranch
141+
| SyncFromCodebaseI FilePath (ProjectAndBranch ProjectName ProjectBranchName) UnresolvedProjectBranch
139142
| ResetI (BranchId2 {- namespace to reset it to -}) (Maybe UnresolvedProjectBranch {- ProjectBranch to reset -})
140143
| -- | used in Welcome module to give directions to user
141144
--

Diff for: unison-cli/src/Unison/Codebase/Editor/Output.hs

+9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import U.Codebase.Sqlite.ProjectReflog qualified as ProjectReflog
3535
import Unison.Auth.Types (CredentialFailure)
3636
import Unison.Cli.MergeTypes (MergeSourceAndTarget, MergeSourceOrTarget)
3737
import Unison.Cli.Share.Projects.Types qualified as Share
38+
import Unison.Codebase (CodebasePath)
3839
import Unison.Codebase.Editor.Input
3940
import Unison.Codebase.Editor.Output.BranchDiff (BranchDiffOutput)
4041
import Unison.Codebase.Editor.Output.BranchDiff qualified as BD
@@ -43,6 +44,7 @@ import Unison.Codebase.Editor.RemoteRepo
4344
import Unison.Codebase.Editor.SlurpResult (SlurpResult (..))
4445
import Unison.Codebase.Editor.SlurpResult qualified as SR
4546
import Unison.Codebase.Editor.StructuredArgument (StructuredArgument)
47+
import Unison.Codebase.Init.OpenCodebaseError (OpenCodebaseError)
4648
import Unison.Codebase.IntegrityCheck (IntegrityResult (..))
4749
import Unison.Codebase.Path (Path')
4850
import Unison.Codebase.Path qualified as Path
@@ -80,6 +82,7 @@ import Unison.Share.Sync.Types qualified as Sync
8082
import Unison.ShortHash (ShortHash)
8183
import Unison.Symbol (Symbol)
8284
import Unison.Sync.Types qualified as Share (DownloadEntitiesError, UploadEntitiesError)
85+
import Unison.SyncV2.Types qualified as SyncV2
8386
import Unison.Syntax.Parser qualified as Parser
8487
import Unison.Term (Term)
8588
import Unison.Type (Type)
@@ -442,6 +445,9 @@ data Output
442445
| -- | A literal output message. Use this if it's too cumbersome to create a new Output constructor, e.g. for
443446
-- ephemeral progress messages that are just simple strings like "Loading branch..."
444447
Literal !(P.Pretty P.ColorText)
448+
| SyncPullError (Sync.SyncError SyncV2.PullError)
449+
| SyncFromCodebaseMissingProjectBranch (ProjectAndBranch ProjectName ProjectBranchName)
450+
| OpenCodebaseError CodebasePath OpenCodebaseError
445451

446452
data MoreEntriesThanShown = MoreEntriesThanShown | AllEntriesShown
447453
deriving (Eq, Show)
@@ -680,6 +686,9 @@ isFailure o = case o of
680686
IncoherentDeclDuringMerge {} -> True
681687
IncoherentDeclDuringUpdate {} -> True
682688
Literal _ -> False
689+
SyncPullError {} -> True
690+
SyncFromCodebaseMissingProjectBranch {} -> True
691+
OpenCodebaseError {} -> True
683692

684693
isNumberedFailure :: NumberedOutput -> Bool
685694
isNumberedFailure = \case

0 commit comments

Comments
 (0)