Merge pull request #5138 from unisonweb/24-06-26-port-old-merge-transcripts

This commit is contained in:
Arya Irani 2024-06-26 15:44:05 -04:00 committed by GitHub
commit e28c4f3d43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 229 additions and 2569 deletions

View File

@ -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
```

View File

@ -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)
```

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
```

View File

@ -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
```

View File

@ -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
```

View File

@ -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
```

View File

@ -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
```

View File

@ -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
```

View File

@ -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
```

View File

@ -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
```

View File

@ -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: ???
* ...

View File

@ -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: ???
* ...

View File

@ -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
```

View File

@ -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...
```

View File

@ -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).

View File

@ -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).