Merge pull request #4986 from unisonweb/24-05-20-merge-commit

This commit is contained in:
Arya Irani 2024-06-13 14:53:57 -04:00 committed by GitHub
commit 2e38bf860d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 711 additions and 215 deletions

View File

@ -47,7 +47,8 @@ module Unison.Cli.ProjectUtils
findTemporaryBranchName,
expectLatestReleaseBranchName,
-- * Upgrade branch utils
-- * Merge/upgrade branch utils
getMergeBranchParent,
getUpgradeBranchParent,
)
where
@ -411,6 +412,17 @@ expectLatestReleaseBranchName remoteProject =
Nothing -> Cli.returnEarly (Output.ProjectHasNoReleases remoteProject.projectName)
Just semver -> pure (UnsafeProjectBranchName ("releases/" <> into @Text semver))
-- | @getMergeBranchParent branch@ returns the parent branch of a "merge" branch.
--
-- When a merge fails, we put you on a branch called `merge-<source>-into-<target>`. That's a "merge" branch. It's not
-- currently distinguished in the database, so we first just switch on whether its name begins with "merge-". If it
-- does, then we get the branch's parent, which should exist, but perhaps wouldn't if the user had manually made a
-- parentless branch called "merge-whatever" for whatever reason.
getMergeBranchParent :: Sqlite.ProjectBranch -> Maybe ProjectBranchId
getMergeBranchParent branch = do
guard ("merge-" `Text.isPrefixOf` into @Text branch.name)
branch.parentBranchId
-- | @getUpgradeBranchParent branch@ returns the parent branch of an "upgrade" branch.
--
-- When an upgrade fails, we put you on a branch called `upgrade-<old>-to-<new>`. That's an "upgrade" branch. It's not

View File

@ -57,6 +57,7 @@ import Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin)
import Unison.Codebase.Editor.HandleInput.Branch (handleBranch)
import Unison.Codebase.Editor.HandleInput.BranchRename (handleBranchRename)
import Unison.Codebase.Editor.HandleInput.Branches (handleBranches)
import Unison.Codebase.Editor.HandleInput.CommitMerge (handleCommitMerge)
import Unison.Codebase.Editor.HandleInput.CommitUpgrade (handleCommitUpgrade)
import Unison.Codebase.Editor.HandleInput.DebugDefinition qualified as DebugDefinition
import Unison.Codebase.Editor.HandleInput.DebugFoldRanges qualified as DebugFoldRanges
@ -362,6 +363,7 @@ loop e = do
then Success
else BranchEmpty branchEmpty
MergeI branch -> handleMerge branch
MergeCommitI -> handleCommitMerge
MergeLocalBranchI src0 dest0 mergeMode -> do
description <- inputDescription input
src0 <- ProjectUtils.expectLooseCodeOrProjectBranch src0
@ -1115,6 +1117,7 @@ inputDescription input =
ListDependentsI {} -> wat
LoadI {} -> wat
MergeI {} -> wat
MergeCommitI {} -> wat
NamesI {} -> wat
NamespaceDependenciesI {} -> wat
PopBranchI {} -> wat

View File

@ -124,7 +124,8 @@ doCreateBranch createFrom project newBranchName description = do
CreateFrom'Branch (ProjectAndBranch _ sourceBranch)
| sourceBranch.projectId == project.projectId -> Just sourceBranch.branchId
_ -> Nothing
doCreateBranch' sourceNamespaceObject parentBranchId project (pure newBranchName) description
(newBranchId, _) <- doCreateBranch' sourceNamespaceObject parentBranchId project (pure newBranchName) description
pure newBranchId
doCreateBranch' ::
Branch IO ->
@ -132,10 +133,10 @@ doCreateBranch' ::
Sqlite.Project ->
Sqlite.Transaction ProjectBranchName ->
Text ->
Cli ProjectBranchId
Cli (ProjectBranchId, ProjectBranchName)
doCreateBranch' sourceNamespaceObject parentBranchId project getNewBranchName description = do
let projectId = project ^. #projectId
newBranchId <-
(newBranchId, newBranchName) <-
Cli.runTransactionWithRollback \rollback -> do
newBranchName <- getNewBranchName
Queries.projectBranchExistsByName projectId newBranchName >>= \case
@ -152,9 +153,9 @@ doCreateBranch' sourceNamespaceObject parentBranchId project getNewBranchName de
parentBranchId = parentBranchId
}
Queries.setMostRecentBranch projectId newBranchId
pure newBranchId
pure (newBranchId, newBranchName)
let newBranchPath = ProjectUtils.projectBranchPath (ProjectAndBranch projectId newBranchId)
_ <- Cli.updateAt description newBranchPath (const sourceNamespaceObject)
Cli.cd newBranchPath
pure newBranchId
pure (newBranchId, newBranchName)

View File

@ -0,0 +1,52 @@
-- | @merge.commit@ handler.
module Unison.Codebase.Editor.HandleInput.CommitMerge
( handleCommitMerge,
)
where
import U.Codebase.Sqlite.Project qualified
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.ProjectUtils qualified as ProjectUtils
import Unison.Codebase.Editor.HandleInput.DeleteBranch qualified as DeleteBranch
import Unison.Codebase.Editor.HandleInput.Merge2 qualified as Merge
import Unison.Codebase.Editor.HandleInput.ProjectSwitch qualified as ProjectSwitch
import Unison.Codebase.Editor.Output qualified as Output
import Unison.Merge.TwoWay (TwoWay (..))
import Unison.Prelude
import Unison.Project (ProjectAndBranch (..))
-- Note: this implementation is similar to `upgrade.commit`.
handleCommitMerge :: Cli ()
handleCommitMerge = do
(mergeProjectAndBranch, _path) <- ProjectUtils.expectCurrentProjectBranch
-- Assert that this is a "merge" branch and get its parent, which is the branch we were on when we ran `merge`.
parentBranchId <-
ProjectUtils.getMergeBranchParent mergeProjectAndBranch.branch
& onNothing (Cli.returnEarly Output.NoMergeInProgress)
parentBranch <-
Cli.runTransaction do
Queries.expectProjectBranch mergeProjectAndBranch.project.projectId parentBranchId
let parentProjectAndBranch =
ProjectAndBranch mergeProjectAndBranch.project parentBranch
-- Switch to the parent
ProjectSwitch.switchToProjectBranch (ProjectUtils.justTheIds parentProjectAndBranch)
-- Merge the merge branch into the parent
Merge.doMergeLocalBranch
TwoWay
{ alice = parentProjectAndBranch,
bob = mergeProjectAndBranch
}
-- Delete the merge branch
DeleteBranch.doDeleteProjectBranch mergeProjectAndBranch

View File

@ -17,6 +17,8 @@ import Unison.Merge.TwoWay (TwoWay (..))
import Unison.Prelude
import Unison.Project (ProjectAndBranch (..))
-- Note: this implementation is similar to `merge.commit`.
handleCommitUpgrade :: Cli ()
handleCommitUpgrade = do
(upgradeProjectAndBranch, _path) <- ProjectUtils.expectCurrentProjectBranch

View File

@ -200,18 +200,17 @@ doMerge info = do
Cli.Env {codebase} <- ask
finalOutput <-
Cli.label \done -> do
-- If alice == bob, or LCA == bob (so alice is ahead of bob), then we are done.
when (info.alice.causalHash == info.bob.causalHash || info.lca.causalHash == Just info.bob.causalHash) do
Cli.respond (Output.MergeAlreadyUpToDate2 mergeSourceAndTarget)
done ()
done (Output.MergeAlreadyUpToDate2 mergeSourceAndTarget)
-- Otherwise, if LCA == alice (so alice is behind bob), then we could fast forward to bob, so we're done.
when (info.lca.causalHash == Just info.alice.causalHash) do
bobBranch <- liftIO (Codebase.expectBranchForHash codebase info.bob.causalHash)
_ <- Cli.updateAt info.description alicePath (\_aliceBranch -> bobBranch)
Cli.respond (Output.MergeSuccessFastForward mergeSourceAndTarget)
done ()
done (Output.MergeSuccessFastForward mergeSourceAndTarget)
-- Create a bunch of cached database lookup functions
db <- makeMergeDatabase codebase
@ -243,14 +242,14 @@ doMerge info = do
Nothing -> pure V2.Branch.empty
Just libdeps -> Cli.runTransaction libdeps.value
when (not (Map.null libdeps.terms) || not (Map.null libdeps.types)) do
Cli.returnEarly (Output.MergeDefnsInLib who)
done (Output.MergeDefnsInLib who)
-- Load Alice/Bob/LCA definitions and decl name lookups
(defns3, declNameLookups, lcaDeclToConstructors) <- do
let emptyNametree = Nametree {value = Defns Map.empty Map.empty, children = Map.empty}
let loadDefns branch =
Cli.runTransaction (loadNamespaceDefinitions (referent2to1 db) branch) & onLeftM \conflictedName ->
Cli.returnEarly case conflictedName of
done case conflictedName of
ConflictedName'Term name refs -> Output.MergeConflictedTermName name refs
ConflictedName'Type name refs -> Output.MergeConflictedTypeName name refs
let load = \case
@ -259,7 +258,7 @@ doMerge info = do
defns <- loadDefns branch
declNameLookup <-
Cli.runTransaction (checkDeclCoherency db.loadDeclNumConstructors defns) & onLeftM \err ->
Cli.returnEarly case err of
done case err of
IncoherentDeclReason'ConstructorAlias typeName conName1 conName2 ->
Output.MergeConstructorAlias who typeName conName1 conName2
IncoherentDeclReason'MissingConstructorName name -> Output.MergeMissingConstructorName who name
@ -291,7 +290,7 @@ doMerge info = do
-- 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) ->
whenJust (findConflictedAlias defns3.lca diff) \(name1, name2) ->
Cli.returnEarly (Output.MergeConflictedAliases who name1 name2)
done (Output.MergeConflictedAliases who name1 name2)
-- Combine the LCA->Alice and LCA->Bob diffs together
let diff = combineDiffs diffs
@ -301,7 +300,7 @@ doMerge info = do
-- Partition the combined diff into the conflicted things and the unconflicted things
(conflicts, unconflicts) <-
partitionCombinedDiffs defns declNameLookups diff & onLeft \name ->
Cli.returnEarly (Output.MergeConflictInvolvingBuiltin name)
done (Output.MergeConflictInvolvingBuiltin name)
liftIO (debugFunctions.debugPartitionedDiff conflicts unconflicts)
@ -407,10 +406,10 @@ doMerge info = do
case maybeTypecheckedUnisonFile of
Nothing -> do
Cli.Env {writeSource} <- ask
_temporaryBranchId <-
(_temporaryBranchId, temporaryBranchName) <-
HandleInput.Branch.doCreateBranch'
(Branch.mergeNode stageOneBranch parents.alice parents.bob)
Nothing
(Just info.alice.projectAndBranch.branch.branchId)
info.alice.projectAndBranch.project
(findTemporaryBranchName info.alice.projectAndBranch.project.projectId mergeSourceAndTarget)
info.description
@ -419,7 +418,7 @@ doMerge info = do
Nothing -> "scratch.u"
Just (file, _) -> file
liftIO $ writeSource (Text.pack scratchFilePath) (Text.pack $ Pretty.toPlain 80 prettyUnisonFile)
Cli.respond (Output.MergeFailure scratchFilePath mergeSourceAndTarget)
pure (Output.MergeFailure scratchFilePath mergeSourceAndTarget temporaryBranchName)
Just tuf -> do
Cli.runTransaction (Codebase.addDefsToCodebase codebase tuf)
let stageTwoBranch = Branch.batchUpdates (typecheckedUnisonFileToBranchAdds tuf) stageOneBranch
@ -428,7 +427,9 @@ doMerge info = do
info.description
alicePath
(\_aliceBranch -> Branch.mergeNode stageTwoBranch parents.alice parents.bob)
Cli.respond (Output.MergeSuccess mergeSourceAndTarget)
pure (Output.MergeSuccess mergeSourceAndTarget)
Cli.respond finalOutput
doMergeLocalBranch :: TwoWay (ProjectAndBranch Project ProjectBranch) -> Cli ()
doMergeLocalBranch branches = do
@ -886,7 +887,7 @@ findTemporaryBranchName projectId mergeSourceAndTarget = do
-- Fails if there is a conflicted name.
loadNamespaceDefinitions ::
forall m.
Monad m =>
(Monad m) =>
(V2.Referent -> m Referent) ->
V2.Branch m ->
m (Either ConflictedName (Nametree (DefnsF (Map NameSegment) Referent TypeReference)))

View File

@ -228,6 +228,7 @@ data Input
!Bool -- Remind the user to use `lib.install` next time, not `pull`?
!(ProjectAndBranch ProjectName (Maybe ProjectBranchNameOrLatestRelease))
| UpgradeCommitI
| MergeCommitI
deriving (Eq, Show)
-- | The source of a `branch` command: what to make the new branch from.

View File

@ -392,7 +392,7 @@ data Output
| UpgradeFailure !ProjectBranchName !ProjectBranchName !FilePath !NameSegment !NameSegment
| UpgradeSuccess !NameSegment !NameSegment
| LooseCodePushDeprecated
| MergeFailure !FilePath !MergeSourceAndTarget
| MergeFailure !FilePath !MergeSourceAndTarget !ProjectBranchName
| MergeSuccess !MergeSourceAndTarget
| MergeSuccessFastForward !MergeSourceAndTarget
| MergeConflictedAliases !MergeSourceOrTarget !Name !Name
@ -408,6 +408,7 @@ data Output
| NoUpgradeInProgress
| UseLibInstallNotPull !(ProjectAndBranch ProjectName ProjectBranchName)
| PullIntoMissingBranch !(ReadRemoteNamespace Share.RemoteProjectBranch) !(ProjectAndBranch (Maybe ProjectName) ProjectBranchName)
| NoMergeInProgress
data UpdateOrUpgrade = UOUUpdate | UOUUpgrade
@ -647,6 +648,7 @@ isFailure o = case o of
NoUpgradeInProgress {} -> True
UseLibInstallNotPull {} -> False
PullIntoMissingBranch {} -> True
NoMergeInProgress {} -> True
isNumberedFailure :: NumberedOutput -> Bool
isNumberedFailure = \case

View File

@ -73,6 +73,7 @@ module Unison.CommandLine.InputPatterns
load,
makeStandalone,
mergeBuiltins,
mergeCommitInputPattern,
mergeIOBuiltins,
mergeInputPattern,
mergeOldInputPattern,
@ -2126,6 +2127,48 @@ mergeInputPattern =
_ -> Left $ I.help mergeInputPattern
}
mergeCommitInputPattern :: InputPattern
mergeCommitInputPattern =
InputPattern
{ patternName = "merge.commit",
aliases = ["commit.merge"],
visibility = I.Visible,
args = [],
help =
let mainBranch = UnsafeProjectBranchName "main"
tempBranch = UnsafeProjectBranchName "merge-topic-into-main"
in P.wrap
( makeExample' mergeCommitInputPattern
<> "merges a temporary branch created by the"
<> makeExample' mergeInputPattern
<> "command back into its parent branch, and removes the temporary branch."
)
<> P.newline
<> P.newline
<> P.wrap
( "For example, if you've done"
<> makeExample mergeInputPattern ["topic"]
<> "from"
<> P.group (prettyProjectBranchName mainBranch <> ",")
<> "then"
<> makeExample' mergeCommitInputPattern
<> "is equivalent to doing"
)
<> P.newline
<> P.newline
<> P.indentN
2
( P.bulleted
[ makeExampleNoBackticks projectSwitch [prettySlashProjectBranchName mainBranch],
makeExampleNoBackticks mergeInputPattern [prettySlashProjectBranchName tempBranch],
makeExampleNoBackticks deleteBranch [prettySlashProjectBranchName tempBranch]
]
),
parse = \case
[] -> Right Input.MergeCommitI
_ -> Left (I.help mergeCommitInputPattern)
}
parseLooseCodeOrProject :: String -> Maybe Input.LooseCodeOrProject
parseLooseCodeOrProject inputString =
case (asLooseCode, asBranch) of
@ -3326,6 +3369,7 @@ validInputs =
mergeOldPreviewInputPattern,
mergeOldSquashInputPattern,
mergeInputPattern,
mergeCommitInputPattern,
names False, -- names
names True, -- names.global
namespaceDependencies,

View File

@ -2082,14 +2082,32 @@ notifyUser dir = \case
"",
"Your non-project code is still available to pull from Share, and you can pull it into a local namespace using `pull myhandle.public`"
]
MergeFailure path aliceAndBob ->
pure . P.wrap $
MergeFailure path aliceAndBob temp ->
pure $
P.lines $
[ P.wrap $
"I couldn't automatically merge"
<> prettyMergeSource aliceAndBob.bob
<> "into"
<> P.group (prettyProjectAndBranchName aliceAndBob.alice <> ".")
<> "However, I've added the definitions that need attention to the top of"
<> P.group (prettyFilePath path <> ".")
<> P.group (prettyFilePath path <> "."),
"",
P.wrap "When you're done, you can run",
"",
P.indentN 2 (IP.makeExampleNoBackticks IP.mergeCommitInputPattern []),
"",
P.wrap $
"to merge your changes back into"
<> prettyProjectBranchName aliceAndBob.alice.branch
<> "and delete the temporary branch. Or, if you decide to cancel the merge instead, you can run",
"",
P.indentN 2 (IP.makeExampleNoBackticks IP.deleteBranch [prettySlashProjectBranchName temp]),
"",
P.wrap $
"to delete the temporary branch and switch back to"
<> P.group (prettyProjectBranchName aliceAndBob.alice.branch <> ".")
]
MergeSuccess aliceAndBob ->
pure . P.wrap $
"I merged"
@ -2133,6 +2151,8 @@ notifyUser dir = \case
case maybeTargetProject of
Nothing -> prettyProjectBranchName targetBranch
Just targetProject -> prettyProjectAndBranchName (ProjectAndBranch targetProject targetBranch)
NoMergeInProgress ->
pure . P.wrap $ "It doesn't look like there's a merge in progress."
expectedEmptyPushDest :: WriteRemoteNamespace Void -> Pretty
expectedEmptyPushDest namespace =

View File

@ -55,6 +55,7 @@ library
Unison.Codebase.Editor.HandleInput.Branch
Unison.Codebase.Editor.HandleInput.Branches
Unison.Codebase.Editor.HandleInput.BranchRename
Unison.Codebase.Editor.HandleInput.CommitMerge
Unison.Codebase.Editor.HandleInput.CommitUpgrade
Unison.Codebase.Editor.HandleInput.DebugDefinition
Unison.Codebase.Editor.HandleInput.DebugFoldRanges

View File

@ -1,7 +1,12 @@
# The `merge` command
The `merge` command merges together two branches in the same project: the current branch (unspecificed), and the target
branch. For example, to merge `topic` into `main`, switch to `main` and run `merge topic`.
branch. For example, to merge `topic` into `main`, switch to `main` and run `merge topic`:
```ucm:error
.> help merge
.> help merge.commit
```
Let's see a simple unconflicted merge in action: Alice (us) and Bob (them) add different terms. The merged result
contains both additions.
@ -949,6 +954,94 @@ project/alice> merge bob
.> project.delete project
```
## `merge.commit` example (success)
After merge conflicts are resolved, you can use `merge.commit` rather than `switch` + `merge` + `branch.delete` to
"commit" your changes.
```ucm:hide
.> project.create-empty project
project/main> builtins.mergeio
```
Original branch:
```unison:hide
foo : Text
foo = "old foo"
```
```ucm:hide
project/main> add
project/main> branch alice
```
Alice's changes:
```unison:hide
foo : Text
foo = "alices foo"
```
```ucm:hide
project/alice> update
project/main> branch bob
```
Bob's changes:
```unison:hide
foo : Text
foo = "bobs foo"
```
Attempt to merge:
```ucm:hide
project/bob> update
```
```ucm:error
project/alice> merge /bob
```
Resolve conflicts and commit:
```unison
foo : Text
foo = "alice and bobs foo"
```
```ucm
project/merge-bob-into-alice> update
project/merge-bob-into-alice> merge.commit
project/alice> view foo
project/alice> branches
```
```ucm:hide
.> project.delete project
```
## `merge.commit` example (failure)
`merge.commit` can only be run on a "merge branch".
```ucm:hide
.> project.create-empty project
project/main> builtins.mergeio
```
```ucm
project/main> branch topic
```
```ucm:error
project/topic> merge.commit
```
```ucm:hide
.> project.delete project
```
## Precondition violations
There are a number of conditions under which we can't perform a merge, and the user will have to fix up the namespace(s) manually before attempting to merge again.

View File

@ -1,8 +1,29 @@
# The `merge` command
The `merge` command merges together two branches in the same project: the current branch (unspecificed), and the target
branch. For example, to merge `topic` into `main`, switch to `main` and run `merge topic`.
branch. For example, to merge `topic` into `main`, switch to `main` and run `merge topic`:
```ucm
.> help merge
merge
`merge /branch` merges `branch` into the current branch
.> help merge.commit
merge.commit (or commit.merge)
`merge.commit` merges a temporary branch created by the `merge`
command back into its parent branch, and removes the temporary
branch.
For example, if you've done `merge topic` from main, then
`merge.commit` is equivalent to doing
* switch /main
* merge /merge-topic-into-main
* delete.branch /merge-topic-into-main
```
Let's see a simple unconflicted merge in action: Alice (us) and Bob (them) add different terms. The merged result
contains both additions.
@ -489,6 +510,18 @@ project/alice> merge /bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
bar : Text
@ -530,6 +563,18 @@ project/alice> merge /bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
bar : Text
@ -583,6 +628,18 @@ project/alice> merge /bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -640,6 +697,18 @@ project/alice> merge /bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -677,6 +746,18 @@ project/alice> merge /bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -718,6 +799,18 @@ project/alice> merge bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -752,6 +845,18 @@ project/alice> merge bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -798,6 +903,18 @@ project/alice> merge bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -860,6 +977,18 @@ project/alice> merge bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -907,6 +1036,18 @@ project/alice> merge bob
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
@ -929,6 +1070,129 @@ bob _ = 19
```
## `merge.commit` example (success)
After merge conflicts are resolved, you can use `merge.commit` rather than `switch` + `merge` + `branch.delete` to
"commit" your changes.
Original branch:
```unison
foo : Text
foo = "old foo"
```
Alice's changes:
```unison
foo : Text
foo = "alices foo"
```
Bob's changes:
```unison
foo : Text
foo = "bobs foo"
```
Attempt to merge:
```ucm
project/alice> merge /bob
I couldn't automatically merge project/bob into project/alice.
However, I've added the definitions that need attention to the
top of scratch.u.
When you're done, you can run
merge.commit
to merge your changes back into alice and delete the temporary
branch. Or, if you decide to cancel the merge instead, you can
run
delete.branch /merge-bob-into-alice
to delete the temporary branch and switch back to alice.
```
```unison:added-by-ucm scratch.u
-- project/alice
foo : Text
foo = "alices foo"
-- project/bob
foo : Text
foo = "bobs foo"
```
Resolve conflicts and commit:
```unison
foo : Text
foo = "alice and bobs foo"
```
```ucm
Loading changes detected in scratch.u.
I found and typechecked these definitions in scratch.u. If you
do an `add` or `update`, here's how your codebase would
change:
⍟ These new definitions are ok to `add`:
foo : Text
```
```ucm
project/merge-bob-into-alice> update
Okay, I'm searching the branch for code that needs to be
updated...
Done.
project/merge-bob-into-alice> merge.commit
I fast-forward merged project/merge-bob-into-alice into
project/alice.
project/alice> view foo
foo : Text
foo = "alice and bobs foo"
project/alice> branches
Branch Remote branch
1. alice
2. bob
3. main
```
## `merge.commit` example (failure)
`merge.commit` can only be run on a "merge branch".
```ucm
project/main> branch topic
Done. I've created the topic branch based off of main.
Tip: To merge your work back into the main branch, first
`switch /main` then `merge /topic`.
```
```ucm
project/topic> merge.commit
It doesn't look like there's a merge in progress.
```
## Precondition violations
There are a number of conditions under which we can't perform a merge, and the user will have to fix up the namespace(s) manually before attempting to merge again.