mirror of
https://github.com/unisonweb/unison.git
synced 2024-09-11 02:25:39 +03:00
Merge pull request #5138 from unisonweb/24-06-26-port-old-merge-transcripts
This commit is contained in:
commit
e28c4f3d43
@ -1,99 +0,0 @@
|
||||
# Behaviour of namespace histories during a merge.
|
||||
|
||||
Note: This is a descriptive test meant to capture the current behaviour of
|
||||
branch histories during a merge.
|
||||
It isn't prescriptive about how merges _should_ work with respect to child branches,
|
||||
but I think we should at least notice if we change things by accident.
|
||||
|
||||
|
||||
## Setting up some history
|
||||
|
||||
```ucm:hide
|
||||
.> builtins.merge
|
||||
```
|
||||
|
||||
```unison:hide
|
||||
parent.top = "top"
|
||||
parent.child.thing = "parent.child.thing"
|
||||
```
|
||||
|
||||
The child branch has a single history node representing the addition of `parent.child.thing`.
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
.> history parent.child
|
||||
```
|
||||
|
||||
If we add another thing to the child namespace it should add another history node to both the child and parent.
|
||||
|
||||
```unison:hide
|
||||
parent.child.thing2 = "parent.child.thing2"
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
.> history parent
|
||||
.> history parent.child
|
||||
```
|
||||
|
||||
## Forking off some history on a separate branch
|
||||
|
||||
Now we fork the parent namespace to make some changes.
|
||||
|
||||
```ucm
|
||||
.> fork parent parent_fork
|
||||
```
|
||||
|
||||
```unison:hide
|
||||
parent_fork.child.thing3 = "parent_fork.child.thing3"
|
||||
```
|
||||
|
||||
The child should have a new history node after adding `thing3`
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
.> history parent_fork.child
|
||||
```
|
||||
|
||||
## Saving our parent state
|
||||
|
||||
Split off two separate forks, one for testing squash merges, one for standard merges.
|
||||
|
||||
```ucm:hide
|
||||
.> fork parent parent_squash_base
|
||||
.> fork parent parent_merge_base
|
||||
```
|
||||
|
||||
## Squash merge
|
||||
|
||||
For a squash merge, when I squash-merge back into parent, we expect `parent_fork.child.thing3` to be added.
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash parent_fork parent_squash_base
|
||||
.> history parent_squash_base
|
||||
```
|
||||
|
||||
Notice that with the current behaviour, the history of `parent.child` is completely wiped out, containing nothing from the source OR destination.
|
||||
|
||||
```ucm
|
||||
.> history parent.child
|
||||
.> history parent_fork.child
|
||||
.> history parent_squash_base.child
|
||||
```
|
||||
|
||||
## Standard merge
|
||||
|
||||
For a standard merge, if I merge back into parent, we expect `parent_fork.child.thing3` to be added.
|
||||
|
||||
```ucm
|
||||
.> merge.old parent_fork parent_merge_base
|
||||
.> history parent_merge_base
|
||||
```
|
||||
|
||||
Child histories should also be *merged*.
|
||||
|
||||
```ucm
|
||||
.> history parent.child
|
||||
.> history parent_fork.child
|
||||
.> history parent_merge_base.child
|
||||
```
|
@ -1,302 +0,0 @@
|
||||
# Behaviour of namespace histories during a merge.
|
||||
|
||||
Note: This is a descriptive test meant to capture the current behaviour of
|
||||
branch histories during a merge.
|
||||
It isn't prescriptive about how merges _should_ work with respect to child branches,
|
||||
but I think we should at least notice if we change things by accident.
|
||||
|
||||
|
||||
## Setting up some history
|
||||
|
||||
```unison
|
||||
parent.top = "top"
|
||||
parent.child.thing = "parent.child.thing"
|
||||
```
|
||||
|
||||
The child branch has a single history node representing the addition of `parent.child.thing`.
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
parent.child.thing : Text
|
||||
parent.top : Text
|
||||
|
||||
.> history parent.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #0r73mam57g (start of history)
|
||||
|
||||
```
|
||||
If we add another thing to the child namespace it should add another history node to both the child and parent.
|
||||
|
||||
```unison
|
||||
parent.child.thing2 = "parent.child.thing2"
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
parent.child.thing2 : Text
|
||||
|
||||
.> history parent
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #2hv7t9lp40
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
child.thing2
|
||||
|
||||
□ 2. #i9lji1bli0 (start of history)
|
||||
|
||||
.> history parent.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 2. #0r73mam57g (start of history)
|
||||
|
||||
```
|
||||
## Forking off some history on a separate branch
|
||||
|
||||
Now we fork the parent namespace to make some changes.
|
||||
|
||||
```ucm
|
||||
.> fork parent parent_fork
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
```unison
|
||||
parent_fork.child.thing3 = "parent_fork.child.thing3"
|
||||
```
|
||||
|
||||
The child should have a new history node after adding `thing3`
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
parent_fork.child.thing3 : Text
|
||||
|
||||
.> history parent_fork.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #9rcfgbsp81
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing3
|
||||
|
||||
⊙ 2. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 3. #0r73mam57g (start of history)
|
||||
|
||||
```
|
||||
## Saving our parent state
|
||||
|
||||
Split off two separate forks, one for testing squash merges, one for standard merges.
|
||||
|
||||
## Squash merge
|
||||
|
||||
For a squash merge, when I squash-merge back into parent, we expect `parent_fork.child.thing3` to be added.
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash parent_fork parent_squash_base
|
||||
|
||||
Here's what's changed in parent_squash_base after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. child.thing3 : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history parent_squash_base
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #594e0e1p39
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
child.thing3
|
||||
|
||||
⊙ 2. #2hv7t9lp40
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
child.thing2
|
||||
|
||||
□ 3. #i9lji1bli0 (start of history)
|
||||
|
||||
```
|
||||
Notice that with the current behaviour, the history of `parent.child` is completely wiped out, containing nothing from the source OR destination.
|
||||
|
||||
```ucm
|
||||
.> history parent.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 2. #0r73mam57g (start of history)
|
||||
|
||||
.> history parent_fork.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #9rcfgbsp81
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing3
|
||||
|
||||
⊙ 2. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 3. #0r73mam57g (start of history)
|
||||
|
||||
.> history parent_squash_base.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #19fd4mhpp4 (start of history)
|
||||
|
||||
```
|
||||
## Standard merge
|
||||
|
||||
For a standard merge, if I merge back into parent, we expect `parent_fork.child.thing3` to be added.
|
||||
|
||||
```ucm
|
||||
.> merge.old parent_fork parent_merge_base
|
||||
|
||||
Here's what's changed in parent_merge_base after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. child.thing3 : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history parent_merge_base
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #mtn8sha7gd
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
child.thing3
|
||||
|
||||
⊙ 2. #2hv7t9lp40
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
child.thing2
|
||||
|
||||
□ 3. #i9lji1bli0 (start of history)
|
||||
|
||||
```
|
||||
Child histories should also be *merged*.
|
||||
|
||||
```ucm
|
||||
.> history parent.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 2. #0r73mam57g (start of history)
|
||||
|
||||
.> history parent_fork.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #9rcfgbsp81
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing3
|
||||
|
||||
⊙ 2. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 3. #0r73mam57g (start of history)
|
||||
|
||||
.> history parent_merge_base.child
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #9rcfgbsp81
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing3
|
||||
|
||||
⊙ 2. #ggnrs01131
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
thing2
|
||||
|
||||
□ 3. #0r73mam57g (start of history)
|
||||
|
||||
```
|
@ -12,9 +12,11 @@ x = 23
|
||||
.> fork b1 b2
|
||||
.b2> alias.term x abc
|
||||
```
|
||||
|
||||
```unison:hide
|
||||
fslkdjflskdjflksjdf = 663
|
||||
```
|
||||
|
||||
```ucm
|
||||
.b0> add
|
||||
.> merge.old b0 b1
|
||||
|
@ -28,14 +28,6 @@ The history of the namespace should be empty.
|
||||
.> history mynamespace
|
||||
```
|
||||
|
||||
Merging an empty namespace should be a no-op
|
||||
|
||||
```ucm:error
|
||||
.empty> history
|
||||
.empty> merge.old .mynamespace
|
||||
.empty> history
|
||||
```
|
||||
|
||||
Add and then delete a term to add some history to a deleted namespace.
|
||||
|
||||
```unison:hide
|
||||
|
@ -54,26 +54,6 @@ The history of the namespace should be empty.
|
||||
|
||||
☝️ The namespace .mynamespace is empty.
|
||||
|
||||
```
|
||||
Merging an empty namespace should be a no-op
|
||||
|
||||
```ucm
|
||||
☝️ The namespace .empty is empty.
|
||||
|
||||
.empty> history
|
||||
|
||||
☝️ The namespace .empty is empty.
|
||||
|
||||
.empty> merge.old .mynamespace
|
||||
|
||||
⚠️
|
||||
|
||||
The namespace .mynamespace doesn't exist.
|
||||
|
||||
.empty> history
|
||||
|
||||
☝️ The namespace .empty is empty.
|
||||
|
||||
```
|
||||
Add and then delete a term to add some history to a deleted namespace.
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
Checks that squash and merge do the same thing, with nontrivial history that
|
||||
includes a merge conflict.
|
||||
|
||||
```ucm:hide
|
||||
.> builtins.merge
|
||||
```
|
||||
|
||||
```unison
|
||||
x.a.p = "af"
|
||||
x.a.q = "ef"
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
.> fork x y
|
||||
.> fork x s
|
||||
.> fork x m
|
||||
.> delete.verbose y.a.p
|
||||
```
|
||||
|
||||
```unison
|
||||
y.a.p = "fij"
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
```
|
||||
|
||||
```unison
|
||||
y.b.p = "wie"
|
||||
```
|
||||
|
||||
Merge back into the ancestor.
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
.> merge.old y.b y.a
|
||||
.> delete.term.verbose 1
|
||||
.> merge.old y m
|
||||
.> merge.old.squash y s
|
||||
.s> todo
|
||||
.m> todo
|
||||
```
|
||||
|
@ -1,188 +0,0 @@
|
||||
Checks that squash and merge do the same thing, with nontrivial history that
|
||||
includes a merge conflict.
|
||||
|
||||
```unison
|
||||
x.a.p = "af"
|
||||
x.a.q = "ef"
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
x.a.p : Text
|
||||
x.a.q : Text
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
x.a.p : Text
|
||||
x.a.q : Text
|
||||
|
||||
.> fork x y
|
||||
|
||||
Done.
|
||||
|
||||
.> fork x s
|
||||
|
||||
Done.
|
||||
|
||||
.> fork x m
|
||||
|
||||
Done.
|
||||
|
||||
.> delete.verbose y.a.p
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. m.a.p ┐ 2. y.a.p (removed)
|
||||
3. s.a.p │
|
||||
4. x.a.p │
|
||||
5. y.a.p ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
```
|
||||
```unison
|
||||
y.a.p = "fij"
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
y.a.p : Text
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
y.a.p : Text
|
||||
|
||||
```
|
||||
```unison
|
||||
y.b.p = "wie"
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
y.b.p : Text
|
||||
|
||||
```
|
||||
Merge back into the ancestor.
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
y.b.p : Text
|
||||
|
||||
.> merge.old y.b y.a
|
||||
|
||||
Here's what's changed in y.a after the merge:
|
||||
|
||||
New name conflicts:
|
||||
|
||||
1. p#l2mmpgn323 : Text
|
||||
↓
|
||||
2. ┌ p#l2mmpgn323 : Text
|
||||
3. └ p#nm3omrdks9 : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> delete.term.verbose 1
|
||||
|
||||
Resolved name conflicts:
|
||||
|
||||
1. ┌ y.a.p#l2mmpgn323 : Text
|
||||
2. └ y.a.p#nm3omrdks9 : Text
|
||||
↓
|
||||
3. y.a.p#nm3omrdks9 : Text
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.> merge.old y m
|
||||
|
||||
Here's what's changed in m after the merge:
|
||||
|
||||
Updates:
|
||||
|
||||
1. a.p : Text
|
||||
↓
|
||||
2. a.p : Text
|
||||
|
||||
Added definitions:
|
||||
|
||||
3. ┌ a.p : Text
|
||||
4. └ b.p : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> merge.old.squash y s
|
||||
|
||||
Here's what's changed in s after the merge:
|
||||
|
||||
Updates:
|
||||
|
||||
1. a.p : Text
|
||||
↓
|
||||
2. a.p : Text
|
||||
|
||||
Added definitions:
|
||||
|
||||
3. ┌ a.p : Text
|
||||
4. └ b.p : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.s> todo
|
||||
|
||||
|
||||
|
||||
.m> todo
|
||||
|
||||
|
||||
|
||||
```
|
@ -1,82 +0,0 @@
|
||||
|
||||
```ucm:hide
|
||||
.> builtins.merge
|
||||
```
|
||||
|
||||
Here's the scenario that can produce bad empty namespace LCAs:
|
||||
|
||||
```
|
||||
deletes of v4
|
||||
j1: ... - v1 - v2 - v3 - v4 - v4a - v5 - v6 - v7
|
||||
/
|
||||
<empty> - v5a
|
||||
|
||||
adds of unrelated
|
||||
j2: ... - v1 - v2 - v3 - v4 - x0 - x1 - x2 - x3
|
||||
/
|
||||
<empty> - z1
|
||||
|
||||
```
|
||||
|
||||
So `j1` and `j2` have common history up through `v4`, then `j1` deletes some definitions while `j2` adds some definitions via shallow merges. These shallow merges then result in the LCA being the empty namespace rather than `v4`.
|
||||
|
||||
First, we create some common history before a fork:
|
||||
|
||||
```ucm
|
||||
.> alias.term builtin.Nat.+ a.delete1
|
||||
.> alias.term builtin.Nat.* a.delete2
|
||||
.> alias.term builtin.Nat.drop a.delete3
|
||||
.> alias.type builtin.Nat a.Delete4
|
||||
```
|
||||
|
||||
Now we fork `a2` off of `a`. `a` continues on, deleting the terms it added previously and then adding one unrelated term via a merge with little history. It's this short history merge which will become a bad LCA of the empty namespace.
|
||||
|
||||
```ucm
|
||||
.> fork a a2
|
||||
.> delete.term.verbose a.delete1
|
||||
.> delete.term.verbose a.delete2
|
||||
.> delete.term.verbose a.delete3
|
||||
.> delete.type.verbose a.Delete4
|
||||
.> alias.term .builtin.Float.+ newbranchA.dontDelete
|
||||
.> merge.old newbranchA a
|
||||
.a> find
|
||||
```
|
||||
|
||||
Meanwhile, `a2` adds some other unrelated terms, some via merging in namespaces with little history. When merging `a2` back into `a`, the deletes from their common history should be respected.
|
||||
|
||||
```ucm
|
||||
.> alias.term builtin.Text.take a2.keep1
|
||||
.> alias.term builtin.Text.take a2.keep2
|
||||
.> alias.term builtin.Text.take a2.keep3
|
||||
.> alias.term builtin.Text.take a2.keep4
|
||||
.> alias.term builtin.Text.take a2.keep5
|
||||
.> alias.term builtin.Text.take newbranchA2.keep6
|
||||
.> merge.old newbranchA2 a2
|
||||
.a2> find
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> fork a asquash
|
||||
.> merge.old a2 a
|
||||
.> merge.old.squash a2 asquash
|
||||
```
|
||||
|
||||
At this point, all the things that `a` has deleted (`delete1`, `delete2`, etc) should be deleted in both the merged and squashed results. Let's verify this:
|
||||
|
||||
```ucm
|
||||
.a> find
|
||||
.asquash> find
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> view a.keep1 a.keep2 a.keep3
|
||||
.> view asquash.keep1 asquash.keep2 asquash.keep3
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
.> view a.Delete4
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
.> view asquash.delete1
|
||||
```
|
@ -1,267 +0,0 @@
|
||||
|
||||
Here's the scenario that can produce bad empty namespace LCAs:
|
||||
|
||||
```deletes
|
||||
of v4
|
||||
j1: ... - v1 - v2 - v3 - v4 - v4a - v5 - v6 - v7
|
||||
/
|
||||
<empty> - v5a
|
||||
|
||||
adds of unrelated
|
||||
j2: ... - v1 - v2 - v3 - v4 - x0 - x1 - x2 - x3
|
||||
/
|
||||
<empty> - z1
|
||||
|
||||
|
||||
```
|
||||
|
||||
So `j1` and `j2` have common history up through `v4`, then `j1` deletes some definitions while `j2` adds some definitions via shallow merges. These shallow merges then result in the LCA being the empty namespace rather than `v4`.
|
||||
|
||||
First, we create some common history before a fork:
|
||||
|
||||
```ucm
|
||||
.> alias.term builtin.Nat.+ a.delete1
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Nat.* a.delete2
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Nat.drop a.delete3
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.type builtin.Nat a.Delete4
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
Now we fork `a2` off of `a`. `a` continues on, deleting the terms it added previously and then adding one unrelated term via a merge with little history. It's this short history merge which will become a bad LCA of the empty namespace.
|
||||
|
||||
```ucm
|
||||
.> fork a a2
|
||||
|
||||
Done.
|
||||
|
||||
.> delete.term.verbose a.delete1
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. a.delete1 ┐ 2. a.delete1 (removed)
|
||||
3. a2.delete1 │
|
||||
4. builtin.Nat.+ ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.> delete.term.verbose a.delete2
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. a.delete2 ┐ 2. a.delete2 (removed)
|
||||
3. a2.delete2 │
|
||||
4. builtin.Nat.* ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.> delete.term.verbose a.delete3
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. a.delete3 ┐ 2. a.delete3 (removed)
|
||||
3. a2.delete3 │
|
||||
4. builtin.Nat.drop ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.> delete.type.verbose a.Delete4
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. a.Delete4 ┐ 2. a.Delete4 (removed)
|
||||
3. a2.Delete4 │
|
||||
4. builtin.Nat ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.> alias.term .builtin.Float.+ newbranchA.dontDelete
|
||||
|
||||
Done.
|
||||
|
||||
.> merge.old newbranchA a
|
||||
|
||||
Here's what's changed in a after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. dontDelete : Float -> Float -> Float
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.a> find
|
||||
|
||||
1. dontDelete : ##Float -> ##Float -> ##Float
|
||||
|
||||
|
||||
```
|
||||
Meanwhile, `a2` adds some other unrelated terms, some via merging in namespaces with little history. When merging `a2` back into `a`, the deletes from their common history should be respected.
|
||||
|
||||
```ucm
|
||||
.> alias.term builtin.Text.take a2.keep1
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Text.take a2.keep2
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Text.take a2.keep3
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Text.take a2.keep4
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Text.take a2.keep5
|
||||
|
||||
Done.
|
||||
|
||||
.> alias.term builtin.Text.take newbranchA2.keep6
|
||||
|
||||
Done.
|
||||
|
||||
.> merge.old newbranchA2 a2
|
||||
|
||||
Here's what's changed in a2 after the merge:
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. keep1 ┐ 2. keep6 (added)
|
||||
3. keep2 │
|
||||
4. keep3 │
|
||||
5. keep4 │
|
||||
6. keep5 ┘
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.a2> find
|
||||
|
||||
1. delete1 : Delete4 -> Delete4 -> Delete4
|
||||
2. delete2 : Delete4 -> Delete4 -> Delete4
|
||||
3. delete3 : Delete4 -> Delete4 -> Delete4
|
||||
4. builtin type Delete4
|
||||
5. keep1 : Delete4 -> ##Text -> ##Text
|
||||
6. keep2 : Delete4 -> ##Text -> ##Text
|
||||
7. keep3 : Delete4 -> ##Text -> ##Text
|
||||
8. keep4 : Delete4 -> ##Text -> ##Text
|
||||
9. keep5 : Delete4 -> ##Text -> ##Text
|
||||
10. keep6 : Delete4 -> ##Text -> ##Text
|
||||
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> fork a asquash
|
||||
|
||||
Done.
|
||||
|
||||
.> merge.old a2 a
|
||||
|
||||
Here's what's changed in a after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. ┌ keep1 : Delete4 -> Text -> Text
|
||||
2. │ keep2 : Delete4 -> Text -> Text
|
||||
3. │ keep3 : Delete4 -> Text -> Text
|
||||
4. │ keep4 : Delete4 -> Text -> Text
|
||||
5. │ keep5 : Delete4 -> Text -> Text
|
||||
6. └ keep6 : Delete4 -> Text -> Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> merge.old.squash a2 asquash
|
||||
|
||||
Here's what's changed in asquash after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. ┌ keep1 : Delete4 -> Text -> Text
|
||||
2. │ keep2 : Delete4 -> Text -> Text
|
||||
3. │ keep3 : Delete4 -> Text -> Text
|
||||
4. │ keep4 : Delete4 -> Text -> Text
|
||||
5. │ keep5 : Delete4 -> Text -> Text
|
||||
6. └ keep6 : Delete4 -> Text -> Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
```
|
||||
At this point, all the things that `a` has deleted (`delete1`, `delete2`, etc) should be deleted in both the merged and squashed results. Let's verify this:
|
||||
|
||||
```ucm
|
||||
.a> find
|
||||
|
||||
1. dontDelete : ##Float -> ##Float -> ##Float
|
||||
2. keep1 : ##Nat -> ##Text -> ##Text
|
||||
3. keep2 : ##Nat -> ##Text -> ##Text
|
||||
4. keep3 : ##Nat -> ##Text -> ##Text
|
||||
5. keep4 : ##Nat -> ##Text -> ##Text
|
||||
6. keep5 : ##Nat -> ##Text -> ##Text
|
||||
7. keep6 : ##Nat -> ##Text -> ##Text
|
||||
|
||||
|
||||
.asquash> find
|
||||
|
||||
1. dontDelete : ##Float -> ##Float -> ##Float
|
||||
2. keep1 : ##Nat -> ##Text -> ##Text
|
||||
3. keep2 : ##Nat -> ##Text -> ##Text
|
||||
4. keep3 : ##Nat -> ##Text -> ##Text
|
||||
5. keep4 : ##Nat -> ##Text -> ##Text
|
||||
6. keep5 : ##Nat -> ##Text -> ##Text
|
||||
7. keep6 : ##Nat -> ##Text -> ##Text
|
||||
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> view a.Delete4
|
||||
|
||||
⚠️
|
||||
|
||||
The following names were not found in the codebase. Check your spelling.
|
||||
a.Delete4
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> view asquash.delete1
|
||||
|
||||
⚠️
|
||||
|
||||
The following names were not found in the codebase. Check your spelling.
|
||||
asquash.delete1
|
||||
|
||||
```
|
@ -456,6 +456,17 @@ project/alice> merge /bob
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## No-op merge: merge empty namespace into empty namespace
|
||||
|
||||
```ucm
|
||||
project/main> branch topic
|
||||
project/main> merge /topic
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Merge failure: someone deleted something
|
||||
|
||||
If either Alice or Bob delete something, so long as the other person didn't update it (in which case we ignore the delete, as explained above), then the delete goes through.
|
||||
@ -1583,3 +1594,54 @@ But `bar` was put into the scratch file instead.
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
### Merge loop test
|
||||
|
||||
This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the
|
||||
history.
|
||||
|
||||
Let's make three identical namespaces with different histories:
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> add
|
||||
```
|
||||
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> add
|
||||
```
|
||||
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/bob> add
|
||||
```
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/bob> add
|
||||
```
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/carol> add
|
||||
project/bob> merge /alice
|
||||
project/carol> merge /bob
|
||||
project/carol> history
|
||||
```
|
||||
|
@ -469,6 +469,23 @@ project/alice> merge /bob
|
||||
|
||||
I fast-forward merged project/bob into project/alice.
|
||||
|
||||
```
|
||||
## No-op merge: merge empty namespace into empty namespace
|
||||
|
||||
```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`.
|
||||
|
||||
project/main> merge /topic
|
||||
|
||||
😶
|
||||
|
||||
project/main was already up-to-date with project/topic.
|
||||
|
||||
```
|
||||
## Merge failure: someone deleted something
|
||||
|
||||
@ -1956,3 +1973,151 @@ bar =
|
||||
|
||||
But `bar` was put into the scratch file instead.
|
||||
|
||||
### Merge loop test
|
||||
|
||||
This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the
|
||||
history.
|
||||
|
||||
Let's make three identical namespaces with different histories:
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
project/alice> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
b : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
project/alice> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
b : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked the definitions in scratch.u. This
|
||||
file has been previously added to the codebase.
|
||||
|
||||
```
|
||||
```ucm
|
||||
project/bob> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
b : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
project/bob> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
a = 1
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked the definitions in scratch.u. This
|
||||
file has been previously added to the codebase.
|
||||
|
||||
```
|
||||
```ucm
|
||||
project/carol> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
a : ##Nat
|
||||
b : ##Nat
|
||||
|
||||
project/bob> merge /alice
|
||||
|
||||
I merged project/alice into project/bob.
|
||||
|
||||
project/carol> merge /bob
|
||||
|
||||
I merged project/bob into project/carol.
|
||||
|
||||
project/carol> history
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
This segment of history starts with a merge. Use
|
||||
`history #som3n4m3space` to view history starting from a given
|
||||
namespace hash.
|
||||
|
||||
⊙ 1. #b7fr6ifj87
|
||||
⑃
|
||||
2. #9npggauqo9
|
||||
3. #dm4u1eokg1
|
||||
|
||||
```
|
||||
|
@ -1,51 +0,0 @@
|
||||
# Merge loop test
|
||||
|
||||
This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the history.
|
||||
|
||||
Let's make three identical namespaces with different histories:
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
.x> add
|
||||
```
|
||||
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
.x> add
|
||||
```
|
||||
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
.y> add
|
||||
```
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
.y> add
|
||||
```
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
.z> add
|
||||
.> merge.old x y
|
||||
.> merge.old y z
|
||||
.> history z
|
||||
```
|
||||
|
||||
|
@ -1,157 +0,0 @@
|
||||
# Merge loop test
|
||||
|
||||
This tests for regressions of https://github.com/unisonweb/unison/issues/1276 where trivial merges cause loops in the history.
|
||||
|
||||
Let's make three identical namespaces with different histories:
|
||||
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
☝️ The namespace .x is empty.
|
||||
|
||||
.x> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
b : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
.x> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
b : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked the definitions in scratch.u. This
|
||||
file has been previously added to the codebase.
|
||||
|
||||
```
|
||||
```ucm
|
||||
☝️ The namespace .y is empty.
|
||||
|
||||
.y> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
b : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
a = 1
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
.y> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
a : ##Nat
|
||||
|
||||
```
|
||||
```unison
|
||||
a = 1
|
||||
b = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked the definitions in scratch.u. This
|
||||
file has been previously added to the codebase.
|
||||
|
||||
```
|
||||
```ucm
|
||||
☝️ The namespace .z is empty.
|
||||
|
||||
.z> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
a : ##Nat
|
||||
b : ##Nat
|
||||
|
||||
.> merge.old x y
|
||||
|
||||
Nothing changed as a result of the merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> merge.old y z
|
||||
|
||||
Nothing changed as a result of the merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history z
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
This segment of history starts with a merge. Use
|
||||
`history #som3n4m3space` to view history starting from a given
|
||||
namespace hash.
|
||||
|
||||
⊙ 1. #b7fr6ifj87
|
||||
⑃
|
||||
2. #9npggauqo9
|
||||
3. #dm4u1eokg1
|
||||
|
||||
```
|
@ -1,121 +0,0 @@
|
||||
# Forking and merging namespaces in `ucm`
|
||||
|
||||
```ucm:hide
|
||||
.master> builtins.merge
|
||||
```
|
||||
|
||||
The Unison namespace is a versioned tree of names that map to Unison definitions. You can change this namespace and fork and merge subtrees of it. Let's start by introducing a few definitions into a new namespace, `foo`:
|
||||
|
||||
```unison
|
||||
x = 42
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> add
|
||||
```
|
||||
|
||||
Let's move `x` into a new namespace, `master`:
|
||||
|
||||
```ucm
|
||||
.> rename.term x master.x
|
||||
```
|
||||
|
||||
If you want to do some experimental work in a namespace without disturbing anyone else, you can `fork` it (which is a shorthand for `copy.namespace`). This creates a copy of it, preserving its history.
|
||||
|
||||
> __Note:__ these copies are very efficient to create as they just have pointers into the same underlying definitions. Create as many as you like.
|
||||
|
||||
Let's go ahead and do this:
|
||||
|
||||
```
|
||||
.> fork master feature1
|
||||
.> view master.x
|
||||
.> view feature1.x
|
||||
```
|
||||
|
||||
Great! We can now do some further work in the `feature1` branch, then merge it back into `master` when we're ready.
|
||||
|
||||
```unison
|
||||
y = "hello"
|
||||
```
|
||||
|
||||
```ucm
|
||||
.feature1> add
|
||||
.master> merge.old .feature1
|
||||
.master> view y
|
||||
```
|
||||
|
||||
> Note: `merge src`, with one argument, merges `src` into the current namespace. You can also do `merge src dest` to merge into any destination namespace.
|
||||
|
||||
Notice that `master` now has the definition of `y` we wrote.
|
||||
|
||||
We can also delete the fork if we're done with it. (Don't worry, even though the history at that path is now empty,
|
||||
it's still in the `history` of the parent namespace and can be resurrected at any time.)
|
||||
|
||||
```ucm
|
||||
.> delete.namespace feature1
|
||||
.> history .feature1
|
||||
.> history
|
||||
```
|
||||
|
||||
To resurrect an old version of a namespace, you can learn its hash via the `history` command, then use `fork #namespacehash .newname`.
|
||||
|
||||
## Concurrent edits and merges
|
||||
|
||||
In the above scenario the destination namespace (`master`) was strictly behind the source namespace, so the merge didn't have anything interesting to do (Git would call this a "fast forward" merge). In other cases, the source and destination namespaces will each have changes the other doesn't know about, and the merge needs to something more interesting. That's okay too, and Unison will merge those results, using a 3-way merge algorithm.
|
||||
|
||||
> __Note:__ When merging nested namespaces, Unison actually uses a recursive 3-way merge, so it finds a different (and possibly closer) common ancestor at each level of the tree.
|
||||
|
||||
Let's see how this works. We are going to create a copy of `master`, add and delete some definitions in `master` and in the fork, then merge.
|
||||
|
||||
```ucm
|
||||
.> fork master feature2
|
||||
```
|
||||
|
||||
Here's one fork, we add `z` and delete `x`:
|
||||
|
||||
```unison
|
||||
z = 99
|
||||
```
|
||||
|
||||
```ucm
|
||||
.feature2> add
|
||||
.feature2> delete.term.verbose x
|
||||
```
|
||||
|
||||
And here's the other fork, where we update `y` and add a new definition, `frobnicate`:
|
||||
|
||||
```unison
|
||||
master.y = "updated y"
|
||||
master.frobnicate n = n + 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> update
|
||||
.> view master.y
|
||||
.> view master.frobnicate
|
||||
```
|
||||
|
||||
At this point, `master` and `feature2` both have some changes the other doesn't know about. Let's merge them.
|
||||
|
||||
```ucm
|
||||
.> merge.old feature2 master
|
||||
```
|
||||
|
||||
Notice that `x` is deleted in the merged branch (it was deleted in `feature2` and untouched by `master`):
|
||||
|
||||
```ucm:error
|
||||
.> view master.x
|
||||
```
|
||||
|
||||
And notice that `y` has the most recent value, and that `z` and `frobnicate` both exist as well:
|
||||
|
||||
```ucm
|
||||
.> view master.y
|
||||
.> view master.z
|
||||
.> view master.frobnicate
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
* What happens if namespace1 deletes a name that namespace2 has updated? A: ???
|
||||
* ...
|
@ -1,312 +0,0 @@
|
||||
# Forking and merging namespaces in `ucm`
|
||||
|
||||
The Unison namespace is a versioned tree of names that map to Unison definitions. You can change this namespace and fork and merge subtrees of it. Let's start by introducing a few definitions into a new namespace, `foo`:
|
||||
|
||||
```unison
|
||||
x = 42
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
x : Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
x : Nat
|
||||
|
||||
```
|
||||
Let's move `x` into a new namespace, `master`:
|
||||
|
||||
```ucm
|
||||
.> rename.term x master.x
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
If you want to do some experimental work in a namespace without disturbing anyone else, you can `fork` it (which is a shorthand for `copy.namespace`). This creates a copy of it, preserving its history.
|
||||
|
||||
> __Note:__ these copies are very efficient to create as they just have pointers into the same underlying definitions. Create as many as you like.
|
||||
|
||||
Let's go ahead and do this:
|
||||
|
||||
```
|
||||
.> fork master feature1
|
||||
.> view master.x
|
||||
.> view feature1.x
|
||||
|
||||
```
|
||||
|
||||
Great! We can now do some further work in the `feature1` branch, then merge it back into `master` when we're ready.
|
||||
|
||||
```unison
|
||||
y = "hello"
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
y : Text
|
||||
|
||||
```
|
||||
```ucm
|
||||
☝️ The namespace .feature1 is empty.
|
||||
|
||||
.feature1> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
y : ##Text
|
||||
|
||||
.master> merge.old .feature1
|
||||
|
||||
Here's what's changed in the current namespace after the
|
||||
merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. y : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.master> view y
|
||||
|
||||
y : Text
|
||||
y = "hello"
|
||||
|
||||
```
|
||||
> Note: `merge src`, with one argument, merges `src` into the current namespace. You can also do `merge src dest` to merge into any destination namespace.
|
||||
|
||||
Notice that `master` now has the definition of `y` we wrote.
|
||||
|
||||
We can also delete the fork if we're done with it. (Don't worry, even though the history at that path is now empty,
|
||||
it's still in the `history` of the parent namespace and can be resurrected at any time.)
|
||||
|
||||
```ucm
|
||||
.> delete.namespace feature1
|
||||
|
||||
Done.
|
||||
|
||||
.> history .feature1
|
||||
|
||||
☝️ The namespace .feature1 is empty.
|
||||
|
||||
.> history
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #6j9omad7mv
|
||||
|
||||
- Deletes:
|
||||
|
||||
feature1.y
|
||||
|
||||
⊙ 2. #59u4sdgodu
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
master.y
|
||||
|
||||
= Copies:
|
||||
|
||||
Original name New name(s)
|
||||
feature1.y master.y
|
||||
|
||||
⊙ 3. #0je96at36h
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
feature1.y
|
||||
|
||||
⊙ 4. #cnv4gjntbl
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
x master.x
|
||||
|
||||
⊙ 5. #tp0bn8ulih
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
x
|
||||
|
||||
□ 6. #cujaete914 (start of history)
|
||||
|
||||
```
|
||||
To resurrect an old version of a namespace, you can learn its hash via the `history` command, then use `fork #namespacehash .newname`.
|
||||
|
||||
## Concurrent edits and merges
|
||||
|
||||
In the above scenario the destination namespace (`master`) was strictly behind the source namespace, so the merge didn't have anything interesting to do (Git would call this a "fast forward" merge). In other cases, the source and destination namespaces will each have changes the other doesn't know about, and the merge needs to something more interesting. That's okay too, and Unison will merge those results, using a 3-way merge algorithm.
|
||||
|
||||
> __Note:__ When merging nested namespaces, Unison actually uses a recursive 3-way merge, so it finds a different (and possibly closer) common ancestor at each level of the tree.
|
||||
|
||||
Let's see how this works. We are going to create a copy of `master`, add and delete some definitions in `master` and in the fork, then merge.
|
||||
|
||||
```ucm
|
||||
.> fork master feature2
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
Here's one fork, we add `z` and delete `x`:
|
||||
|
||||
```unison
|
||||
z = 99
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
z : Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
.feature2> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
z : Nat
|
||||
|
||||
.feature2> delete.term.verbose x
|
||||
|
||||
Removed definitions:
|
||||
|
||||
1. x : Nat
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
```
|
||||
And here's the other fork, where we update `y` and add a new definition, `frobnicate`:
|
||||
|
||||
```unison
|
||||
master.y = "updated y"
|
||||
master.frobnicate n = n + 1
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
master.frobnicate : Nat -> Nat
|
||||
master.y : Text
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> update
|
||||
|
||||
Okay, I'm searching the branch for code that needs to be
|
||||
updated...
|
||||
|
||||
Done.
|
||||
|
||||
.> view master.y
|
||||
|
||||
master.y : Text
|
||||
master.y = "updated y"
|
||||
|
||||
.> view master.frobnicate
|
||||
|
||||
master.frobnicate : Nat -> Nat
|
||||
master.frobnicate n =
|
||||
use Nat +
|
||||
n + 1
|
||||
|
||||
```
|
||||
At this point, `master` and `feature2` both have some changes the other doesn't know about. Let's merge them.
|
||||
|
||||
```ucm
|
||||
.> merge.old feature2 master
|
||||
|
||||
Here's what's changed in master after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. z : Nat
|
||||
|
||||
Removed definitions:
|
||||
|
||||
2. x : Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
```
|
||||
Notice that `x` is deleted in the merged branch (it was deleted in `feature2` and untouched by `master`):
|
||||
|
||||
```ucm
|
||||
.> view master.x
|
||||
|
||||
⚠️
|
||||
|
||||
The following names were not found in the codebase. Check your spelling.
|
||||
master.x
|
||||
|
||||
```
|
||||
And notice that `y` has the most recent value, and that `z` and `frobnicate` both exist as well:
|
||||
|
||||
```ucm
|
||||
.> view master.y
|
||||
|
||||
master.y : Text
|
||||
master.y = "updated y"
|
||||
|
||||
.> view master.z
|
||||
|
||||
master.z : Nat
|
||||
master.z = 99
|
||||
|
||||
.> view master.frobnicate
|
||||
|
||||
master.frobnicate : Nat -> Nat
|
||||
master.frobnicate n =
|
||||
use Nat +
|
||||
n + 1
|
||||
|
||||
```
|
||||
## FAQ
|
||||
|
||||
* What happens if namespace1 deletes a name that namespace2 has updated? A: ???
|
||||
* ...
|
@ -1,39 +0,0 @@
|
||||
# projects merge
|
||||
|
||||
```ucm
|
||||
.> builtins.merge
|
||||
```
|
||||
|
||||
```unison
|
||||
zonk = 0
|
||||
```
|
||||
|
||||
```ucm
|
||||
.foo> add
|
||||
.> project.create-empty foo
|
||||
.> merge.old foo foo/main
|
||||
```
|
||||
|
||||
```unison
|
||||
bonk = 2
|
||||
```
|
||||
|
||||
```ucm
|
||||
foo/main> add
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> project.create-empty bar
|
||||
bar/main> merge.old foo/main
|
||||
bar/main> branch /topic
|
||||
```
|
||||
|
||||
```unison
|
||||
xonk = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
bar/main> add
|
||||
bar/topic> merge.old /main
|
||||
.bar> merge.old foo/main
|
||||
```
|
@ -1,193 +0,0 @@
|
||||
# projects merge
|
||||
|
||||
```ucm
|
||||
.> builtins.merge
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
```unison
|
||||
zonk = 0
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
zonk : Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
☝️ The namespace .foo is empty.
|
||||
|
||||
.foo> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
zonk : ##Nat
|
||||
|
||||
.> project.create-empty foo
|
||||
|
||||
🎉 I've created the project foo.
|
||||
|
||||
🎨 Type `ui` to explore this project's code in your browser.
|
||||
🔭 Discover libraries at https://share.unison-lang.org
|
||||
📖 Use `help-topic projects` to learn more about projects.
|
||||
|
||||
Write your first Unison code with UCM:
|
||||
|
||||
1. Open scratch.u.
|
||||
2. Write some Unison code and save the file.
|
||||
3. In UCM, type `add` to save it to your new project.
|
||||
|
||||
🎉 🥳 Happy coding!
|
||||
|
||||
.> merge.old foo foo/main
|
||||
|
||||
Here's what's changed in foo/main after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. zonk : Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
```
|
||||
```unison
|
||||
bonk = 2
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
bonk : Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
foo/main> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
bonk : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> project.create-empty bar
|
||||
|
||||
🎉 I've created the project bar.
|
||||
|
||||
🎨 Type `ui` to explore this project's code in your browser.
|
||||
🔭 Discover libraries at https://share.unison-lang.org
|
||||
📖 Use `help-topic projects` to learn more about projects.
|
||||
|
||||
Write your first Unison code with UCM:
|
||||
|
||||
1. Open scratch.u.
|
||||
2. Write some Unison code and save the file.
|
||||
3. In UCM, type `add` to save it to your new project.
|
||||
|
||||
🎉 🥳 Happy coding!
|
||||
|
||||
bar/main> merge.old foo/main
|
||||
|
||||
Here's what's changed in the current namespace after the
|
||||
merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. bonk : ##Nat
|
||||
2. zonk : ##Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
bar/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`.
|
||||
|
||||
```
|
||||
```unison
|
||||
xonk = 1
|
||||
```
|
||||
|
||||
```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`:
|
||||
|
||||
xonk : ##Nat
|
||||
|
||||
```
|
||||
```ucm
|
||||
bar/main> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
xonk : ##Nat
|
||||
|
||||
bar/topic> merge.old /main
|
||||
|
||||
Here's what's changed in the current namespace after the
|
||||
merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. xonk : ##Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
☝️ The namespace .bar is empty.
|
||||
|
||||
.bar> merge.old foo/main
|
||||
|
||||
Here's what's changed in the current namespace after the
|
||||
merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. bonk : ##Nat
|
||||
2. zonk : ##Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
```
|
@ -1,157 +0,0 @@
|
||||
|
||||
```ucm:hide
|
||||
.> builtins.merge
|
||||
```
|
||||
|
||||
# Squash merges
|
||||
|
||||
`squash src dest` merges can be used to merge from `src` to `dest`, discarding the history of `src`. It's useful when the source namespace history is irrelevant or has a bunch of churn you wish to discard. Often when merging small pull requests, you'll use a squash merge.
|
||||
|
||||
Let's look at some examples. We'll start with a namespace with just the builtins. Let's take a look at the hash of this namespace:
|
||||
|
||||
```ucm
|
||||
.> history builtin
|
||||
.> fork builtin builtin2
|
||||
```
|
||||
|
||||
(We make a copy of `builtin` for use later in this transcript.)
|
||||
|
||||
Now suppose we `fork` a copy of builtin, then rename `Nat.+` to `frobnicate`, then rename it back. Notice this produces multiple entries in the history:
|
||||
|
||||
```ucm
|
||||
.> fork builtin mybuiltin
|
||||
.mybuiltin> rename.term Nat.+ Nat.frobnicate
|
||||
.mybuiltin> rename.term Nat.frobnicate Nat.+
|
||||
.mybuiltin> history
|
||||
```
|
||||
|
||||
If we merge that back into `builtin`, we get that same chain of history:
|
||||
|
||||
```ucm
|
||||
.> merge.old mybuiltin builtin
|
||||
.> history builtin
|
||||
```
|
||||
|
||||
Let's try again, but using a `merge.squash` (or just `squash`) instead. The history will be unchanged:
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash mybuiltin builtin2
|
||||
.> history builtin2
|
||||
```
|
||||
|
||||
The churn that happened in `mybuiltin` namespace ended up back in the same spot, so the squash merge of that namespace with our original namespace had no effect.
|
||||
|
||||
## Another example
|
||||
|
||||
Let's look at a more interesting example, where the two namespaces have diverged a bit. Here's our starting namespace:
|
||||
|
||||
```unison:hide
|
||||
x = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
.trunk> add
|
||||
.> fork trunk alice
|
||||
.> fork trunk bob
|
||||
```
|
||||
|
||||
Alice now does some hacking:
|
||||
|
||||
```unison:hide
|
||||
radNumber = 348
|
||||
bodaciousNumero = 2394
|
||||
neatoFun x = x
|
||||
```
|
||||
|
||||
```ucm
|
||||
.alice> add
|
||||
.alice> rename.term radNumber superRadNumber
|
||||
.alice> rename.term neatoFun productionReadyId
|
||||
```
|
||||
|
||||
Meanwhile, Bob does his own hacking:
|
||||
|
||||
```unison:hide
|
||||
whatIsLove = "?"
|
||||
babyDon'tHurtMe = ".. Don't hurt me..."
|
||||
no more = no more
|
||||
```
|
||||
|
||||
```ucm
|
||||
.bob> add
|
||||
```
|
||||
|
||||
At this point, Alice and Bob both have some history beyond what's in trunk:
|
||||
|
||||
```ucm
|
||||
.> history trunk
|
||||
.> history alice
|
||||
.> history bob
|
||||
```
|
||||
|
||||
Alice then squash merges into `trunk`, as does Bob. It's as if Alice and Bob both made their changes in one single commit.
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash alice trunk
|
||||
.> history trunk
|
||||
.> merge.old.squash bob trunk
|
||||
.> history trunk
|
||||
```
|
||||
|
||||
Since squash merges don't produce any merge nodes, we can `undo` a couple times to get back to our starting state:
|
||||
|
||||
```ucm
|
||||
.> undo
|
||||
.> undo
|
||||
.> history trunk
|
||||
```
|
||||
|
||||
This time, we'll first squash Alice and Bob's changes together before squashing their combined changes into `trunk`. The resulting `trunk` will have just a single entry in it, combining both Alice and Bob's changes:
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash alice bob
|
||||
.> merge.old.squash bob trunk
|
||||
.> history trunk
|
||||
```
|
||||
|
||||
So, there you have it. With squashing, you can control the granularity of your history.
|
||||
|
||||
## Throwing out all history
|
||||
|
||||
Another thing we can do is `squash` into an empty namespace. This effectively makes a copy of the namespace, but without any of its history:
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash alice nohistoryalice
|
||||
.> history nohistoryalice
|
||||
```
|
||||
|
||||
There's nothing really special here, `squash src dest` discards `src` history that comes after the LCA of `src` and `dest`, it's just that in the case of an empty namespace, that LCA is the beginning of time (the empty namespace), so all the history of `src` is discarded.
|
||||
|
||||
## Checking for handling of deletes
|
||||
|
||||
This checks to see that squashing correctly preserves deletions:
|
||||
|
||||
```ucm
|
||||
.delete> builtins.merge
|
||||
.delete> fork builtin builtin2
|
||||
.delete> delete.term.verbose builtin2.Nat.+
|
||||
.delete> delete.term.verbose builtin2.Nat.*
|
||||
.delete> merge.old.squash builtin2 builtin
|
||||
.delete> history builtin
|
||||
```
|
||||
|
||||
Notice that `Nat.+` and `Nat.*` are deleted by the squash, and we see them deleted in one atomic step in the history.
|
||||
|
||||
Just confirming that those two definitions are in fact removed:
|
||||
|
||||
```ucm:error
|
||||
.delete> view .delete.builtin.Nat.+
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
.delete> view .delete.builtin.Nat.*
|
||||
```
|
||||
|
||||
## Caveats
|
||||
|
||||
If you `squash mystuff trunk`, you're discarding any history of `mystuff` and just cons'ing onto the history of `trunk`. Thus, don't expect to be able to `merge trunk mystuff` later and get great results. Squashing should only be used when you don't care about the history (and you know others haven't pulled and built on your line of history being discarded, so they don't care about the history either).
|
@ -1,529 +0,0 @@
|
||||
|
||||
# Squash merges
|
||||
|
||||
`squash src dest` merges can be used to merge from `src` to `dest`, discarding the history of `src`. It's useful when the source namespace history is irrelevant or has a bunch of churn you wish to discard. Often when merging small pull requests, you'll use a squash merge.
|
||||
|
||||
Let's look at some examples. We'll start with a namespace with just the builtins. Let's take a look at the hash of this namespace:
|
||||
|
||||
```ucm
|
||||
.> history builtin
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #i3vp9o9btm (start of history)
|
||||
|
||||
.> fork builtin builtin2
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
(We make a copy of `builtin` for use later in this transcript.)
|
||||
|
||||
Now suppose we `fork` a copy of builtin, then rename `Nat.+` to `frobnicate`, then rename it back. Notice this produces multiple entries in the history:
|
||||
|
||||
```ucm
|
||||
.> fork builtin mybuiltin
|
||||
|
||||
Done.
|
||||
|
||||
.mybuiltin> rename.term Nat.+ Nat.frobnicate
|
||||
|
||||
Done.
|
||||
|
||||
.mybuiltin> rename.term Nat.frobnicate Nat.+
|
||||
|
||||
Done.
|
||||
|
||||
.mybuiltin> history
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #tpkjb488ei
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.frobnicate Nat.+
|
||||
|
||||
⊙ 2. #334ak3epqt
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.+ Nat.frobnicate
|
||||
|
||||
□ 3. #i3vp9o9btm (start of history)
|
||||
|
||||
```
|
||||
If we merge that back into `builtin`, we get that same chain of history:
|
||||
|
||||
```ucm
|
||||
.> merge.old mybuiltin builtin
|
||||
|
||||
Nothing changed as a result of the merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history builtin
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #tpkjb488ei
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.frobnicate Nat.+
|
||||
|
||||
⊙ 2. #334ak3epqt
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.+ Nat.frobnicate
|
||||
|
||||
□ 3. #i3vp9o9btm (start of history)
|
||||
|
||||
```
|
||||
Let's try again, but using a `merge.squash` (or just `squash`) instead. The history will be unchanged:
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash mybuiltin builtin2
|
||||
|
||||
Nothing changed as a result of the merge.
|
||||
|
||||
😶
|
||||
|
||||
builtin2 was already up-to-date with mybuiltin.
|
||||
|
||||
.> history builtin2
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #i3vp9o9btm (start of history)
|
||||
|
||||
```
|
||||
The churn that happened in `mybuiltin` namespace ended up back in the same spot, so the squash merge of that namespace with our original namespace had no effect.
|
||||
|
||||
## Another example
|
||||
|
||||
Let's look at a more interesting example, where the two namespaces have diverged a bit. Here's our starting namespace:
|
||||
|
||||
```unison
|
||||
x = 1
|
||||
```
|
||||
|
||||
```ucm
|
||||
☝️ The namespace .trunk is empty.
|
||||
|
||||
.trunk> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
x : ##Nat
|
||||
|
||||
.> fork trunk alice
|
||||
|
||||
Done.
|
||||
|
||||
.> fork trunk bob
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
Alice now does some hacking:
|
||||
|
||||
```unison
|
||||
radNumber = 348
|
||||
bodaciousNumero = 2394
|
||||
neatoFun x = x
|
||||
```
|
||||
|
||||
```ucm
|
||||
.alice> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
bodaciousNumero : ##Nat
|
||||
neatoFun : x -> x
|
||||
radNumber : ##Nat
|
||||
|
||||
.alice> rename.term radNumber superRadNumber
|
||||
|
||||
Done.
|
||||
|
||||
.alice> rename.term neatoFun productionReadyId
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
Meanwhile, Bob does his own hacking:
|
||||
|
||||
```unison
|
||||
whatIsLove = "?"
|
||||
babyDon'tHurtMe = ".. Don't hurt me..."
|
||||
no more = no more
|
||||
```
|
||||
|
||||
```ucm
|
||||
.bob> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
babyDon'tHurtMe : ##Text
|
||||
no : more -> r
|
||||
whatIsLove : ##Text
|
||||
|
||||
```
|
||||
At this point, Alice and Bob both have some history beyond what's in trunk:
|
||||
|
||||
```ucm
|
||||
.> history trunk
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #i52j9fd57b (start of history)
|
||||
|
||||
.> history alice
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #e9jd55555o
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
neatoFun productionReadyId
|
||||
|
||||
⊙ 2. #l5ocoo2eac
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
radNumber superRadNumber
|
||||
|
||||
⊙ 3. #i1vq05628n
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
bodaciousNumero neatoFun radNumber
|
||||
|
||||
□ 4. #i52j9fd57b (start of history)
|
||||
|
||||
.> history bob
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #brr4400742
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
babyDon'tHurtMe no whatIsLove
|
||||
|
||||
□ 2. #i52j9fd57b (start of history)
|
||||
|
||||
```
|
||||
Alice then squash merges into `trunk`, as does Bob. It's as if Alice and Bob both made their changes in one single commit.
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash alice trunk
|
||||
|
||||
Here's what's changed in trunk after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. bodaciousNumero : Nat
|
||||
2. productionReadyId : x -> x
|
||||
3. superRadNumber : Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history trunk
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #f9lvm9gd2k
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
bodaciousNumero productionReadyId superRadNumber
|
||||
|
||||
□ 2. #i52j9fd57b (start of history)
|
||||
|
||||
.> merge.old.squash bob trunk
|
||||
|
||||
Here's what's changed in trunk after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. babyDon'tHurtMe : Text
|
||||
2. no : more -> r
|
||||
3. whatIsLove : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history trunk
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #dbp78ts6q3
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
babyDon'tHurtMe no whatIsLove
|
||||
|
||||
⊙ 2. #f9lvm9gd2k
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
bodaciousNumero productionReadyId superRadNumber
|
||||
|
||||
□ 3. #i52j9fd57b (start of history)
|
||||
|
||||
```
|
||||
Since squash merges don't produce any merge nodes, we can `undo` a couple times to get back to our starting state:
|
||||
|
||||
```ucm
|
||||
.> undo
|
||||
|
||||
Here are the changes I undid
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. bob.babyDon'tHurtMe 2. trunk.babyDon'tHurtMe (added)
|
||||
|
||||
3. bob.no 4. trunk.no (added)
|
||||
|
||||
5. bob.whatIsLove 6. trunk.whatIsLove (added)
|
||||
|
||||
.> undo
|
||||
|
||||
Here are the changes I undid
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. alice.bodaciousNumero 2. trunk.bodaciousNumero (added)
|
||||
|
||||
3. alice.productionReadyId 4. trunk.productionReadyId (added)
|
||||
|
||||
5. alice.superRadNumber 6. trunk.superRadNumber (added)
|
||||
|
||||
.> history trunk
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #i52j9fd57b (start of history)
|
||||
|
||||
```
|
||||
This time, we'll first squash Alice and Bob's changes together before squashing their combined changes into `trunk`. The resulting `trunk` will have just a single entry in it, combining both Alice and Bob's changes:
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash alice bob
|
||||
|
||||
Here's what's changed in bob after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. bodaciousNumero : Nat
|
||||
2. productionReadyId : x -> x
|
||||
3. superRadNumber : Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> merge.old.squash bob trunk
|
||||
|
||||
Here's what's changed in trunk after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. babyDon'tHurtMe : Text
|
||||
2. bodaciousNumero : Nat
|
||||
3. no : more -> r
|
||||
4. productionReadyId : x -> x
|
||||
5. superRadNumber : Nat
|
||||
6. whatIsLove : Text
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history trunk
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #qtotqgds4i
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
babyDon'tHurtMe bodaciousNumero no productionReadyId
|
||||
superRadNumber whatIsLove
|
||||
|
||||
□ 2. #i52j9fd57b (start of history)
|
||||
|
||||
```
|
||||
So, there you have it. With squashing, you can control the granularity of your history.
|
||||
|
||||
## Throwing out all history
|
||||
|
||||
Another thing we can do is `squash` into an empty namespace. This effectively makes a copy of the namespace, but without any of its history:
|
||||
|
||||
```ucm
|
||||
.> merge.old.squash alice nohistoryalice
|
||||
|
||||
Here's what's changed in nohistoryalice after the merge:
|
||||
|
||||
Added definitions:
|
||||
|
||||
1. bodaciousNumero : Nat
|
||||
2. productionReadyId : x -> x
|
||||
3. superRadNumber : Nat
|
||||
4. x : Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.> history nohistoryalice
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
|
||||
|
||||
□ 1. #1d9haupn3d (start of history)
|
||||
|
||||
```
|
||||
There's nothing really special here, `squash src dest` discards `src` history that comes after the LCA of `src` and `dest`, it's just that in the case of an empty namespace, that LCA is the beginning of time (the empty namespace), so all the history of `src` is discarded.
|
||||
|
||||
## Checking for handling of deletes
|
||||
|
||||
This checks to see that squashing correctly preserves deletions:
|
||||
|
||||
```ucm
|
||||
☝️ The namespace .delete is empty.
|
||||
|
||||
.delete> builtins.merge
|
||||
|
||||
Done.
|
||||
|
||||
.delete> fork builtin builtin2
|
||||
|
||||
Done.
|
||||
|
||||
.delete> delete.term.verbose builtin2.Nat.+
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. builtin.Nat.+ ┐ 2. builtin2.Nat.+ (removed)
|
||||
3. builtin2.Nat.+ ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.delete> delete.term.verbose builtin2.Nat.*
|
||||
|
||||
Name changes:
|
||||
|
||||
Original Changes
|
||||
1. builtin.Nat.* ┐ 2. builtin2.Nat.* (removed)
|
||||
3. builtin2.Nat.* ┘
|
||||
|
||||
Tip: You can use `undo` or `reflog` to undo this change.
|
||||
|
||||
.delete> merge.old.squash builtin2 builtin
|
||||
|
||||
Here's what's changed in builtin after the merge:
|
||||
|
||||
Removed definitions:
|
||||
|
||||
1. Nat.* : Nat -> Nat -> Nat
|
||||
2. Nat.+ : Nat -> Nat -> Nat
|
||||
|
||||
Tip: You can use `todo` to see if this generated any work to
|
||||
do in this namespace and `test` to run the tests. Or you
|
||||
can use `undo` or `reflog` to undo the results of this
|
||||
merge.
|
||||
|
||||
Applying changes from patch...
|
||||
|
||||
.delete> history builtin
|
||||
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #dv00hf6vmg
|
||||
|
||||
- Deletes:
|
||||
|
||||
Nat.* Nat.+
|
||||
|
||||
□ 2. #i3vp9o9btm (start of history)
|
||||
|
||||
```
|
||||
Notice that `Nat.+` and `Nat.*` are deleted by the squash, and we see them deleted in one atomic step in the history.
|
||||
|
||||
Just confirming that those two definitions are in fact removed:
|
||||
|
||||
```ucm
|
||||
.delete> view .delete.builtin.Nat.+
|
||||
|
||||
⚠️
|
||||
|
||||
The following names were not found in the codebase. Check your spelling.
|
||||
.delete.builtin.Nat.+
|
||||
|
||||
```
|
||||
```ucm
|
||||
.delete> view .delete.builtin.Nat.*
|
||||
|
||||
⚠️
|
||||
|
||||
The following names were not found in the codebase. Check your spelling.
|
||||
.delete.builtin.Nat.*
|
||||
|
||||
```
|
||||
## Caveats
|
||||
|
||||
If you `squash mystuff trunk`, you're discarding any history of `mystuff` and just cons'ing onto the history of `trunk`. Thus, don't expect to be able to `merge trunk mystuff later and get great results. Squashing should only be used when you don't care about the history (and you know others haven't pulled and built on your line of history being discarded, so they don't care about the history either).
|
Loading…
Reference in New Issue
Block a user