mirror of
https://github.com/urbit/shrub.git
synced 2025-01-08 06:00:27 +03:00
fixed clay bug
This commit is contained in:
parent
8d7ecb1f5b
commit
06b0890e22
@ -1251,7 +1251,7 @@
|
|||||||
^+ +>
|
^+ +>
|
||||||
?: =(let oan) +>
|
?: =(let oan) +>
|
||||||
?: (gth oan let) !! :: don't have this version
|
?: (gth oan let) !! :: don't have this version
|
||||||
+>(ank (azel q:(need (~(get by hut) (need (~(get by hit) oan))))), let oan)
|
+>(ank (azel q:(~(got by hut) (~(got by hit) oan))), let oan)
|
||||||
::
|
::
|
||||||
::::
|
::::
|
||||||
++ aqel :: aqel:ze
|
++ aqel :: aqel:ze
|
||||||
@ -1310,8 +1310,9 @@
|
|||||||
|= [[pat=path bar=lobe] ank=ankh]
|
|= [[pat=path bar=lobe] ank=ankh]
|
||||||
^- ankh
|
^- ankh
|
||||||
%- cosh
|
%- cosh
|
||||||
|
?~ pat
|
||||||
=+ zar=(zaul bar)
|
=+ zar=(zaul bar)
|
||||||
?~ pat [_cash [~ [(sham zar) zar]] `(map ,@ta ankh)`~]
|
ank(q [~ (sham zar) zar])
|
||||||
=+ nak=(~(get by r.ank) i.pat)
|
=+ nak=(~(get by r.ank) i.pat)
|
||||||
%= ank
|
%= ank
|
||||||
r %+ ~(put by r.ank) i.pat
|
r %+ ~(put by r.ank) i.pat
|
||||||
|
@ -30,7 +30,8 @@ skimming, this so that you get a rough idea of how our state is organized.
|
|||||||
The types that are certainly worth reading are `++raft`, `++room`, `++dome`,
|
The types that are certainly worth reading are `++raft`, `++room`, `++dome`,
|
||||||
`++ankh`, `++rung`, `++rang`, `++blob`, `++yaki`, and `++nori` (possibly in
|
`++ankh`, `++rung`, `++rang`, `++blob`, `++yaki`, and `++nori` (possibly in
|
||||||
that order). All in all, though, this section isn't too long, so many readers
|
that order). All in all, though, this section isn't too long, so many readers
|
||||||
may wish to quickly read through all of it.
|
may wish to quickly read through all of it. If you get bored, though, just
|
||||||
|
skip to the next section. You can always come back when you need to.
|
||||||
|
|
||||||
###`++raft`, formal state
|
###`++raft`, formal state
|
||||||
|
|
||||||
@ -988,9 +989,248 @@ to get the requested information.
|
|||||||
^+ +>
|
^+ +>
|
||||||
?: =(let oan) +>
|
?: =(let oan) +>
|
||||||
?: (gth oan let) !! :: don't have this version
|
?: (gth oan let) !! :: don't have this version
|
||||||
+>(ank (azel q:(need (~(get by hut) (need (~(get by hit) oan))))), let oan)
|
+>(ank (azel q:(~(got by hut) (~(got by hit) oan))), let oan)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If we're already at the requested version, we do nothing. If we're requesting
|
||||||
|
a version later than our head, we are unable to comply.
|
||||||
|
|
||||||
|
Otherwise, we get the hash of the commit at the number, and from that we get
|
||||||
|
the commit itself (the yaki), which has the map of path to lobe that represents
|
||||||
|
a version of the filesystem. We call `++azel` to checkout the commit, and we
|
||||||
|
replace `ank` in our context with the result.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ azel :: azel:ze
|
||||||
|
|= hat=(map path lobe) :: checkout commit
|
||||||
|
^- ankh
|
||||||
|
%- cosh
|
||||||
|
%+ roll (~(tap by hat) ~)
|
||||||
|
|= [[pat=path bar=lobe] ank=ankh]
|
||||||
|
^- ankh
|
||||||
|
%- cosh
|
||||||
|
?~ pat
|
||||||
|
=+ zar=(zaul bar)
|
||||||
|
[_cash [~ [(sham zar) zar]] `(map ,@ta ankh)`~]
|
||||||
|
=+ nak=(~(get by r.ank) i.pat)
|
||||||
|
%= ank
|
||||||
|
r %+ ~(put by r.ank) i.pat
|
||||||
|
$(pat t.pat, ank (fall nak _ankh))
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
Twice we call `++cosh`, which hashes a commit, updating `p` in an `ankh`.
|
||||||
|
Let's jump into that algorithm before we describe `++azel`.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ cosh :: locally rehash
|
||||||
|
|= ank=ankh
|
||||||
|
ank(p dash:(zu ank))
|
||||||
|
```
|
||||||
|
|
||||||
|
We simply replace `p` in the hash with the `cash` we get from a call to
|
||||||
|
`++dash:zu`.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ zu !: :: filesystem
|
||||||
|
|= ank=ankh :: filesystem state
|
||||||
|
=| myz=(list ,[p=path q=miso]) :: changes in reverse
|
||||||
|
=| ram=path :: reverse path into
|
||||||
|
|%
|
||||||
|
++ dash :: local rehash
|
||||||
|
^- cash
|
||||||
|
%+ mix ?~(q.ank 0 p.u.q.ank)
|
||||||
|
=+ axe=1
|
||||||
|
|- ^- cash
|
||||||
|
?~ r.ank _@
|
||||||
|
;: mix
|
||||||
|
(shaf %dash (mix axe (shaf %dush (mix p.n.r.ank p.q.n.r.ank))))
|
||||||
|
$(r.ank l.r.ank, axe (peg axe 2))
|
||||||
|
$(r.ank r.r.ank, axe (peg axe 3))
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
`++zu` is a core we set up with a particular filesystem node to traverse a
|
||||||
|
checkout of the filesystem and access the actual data inside it. One of the
|
||||||
|
things we can do with it is to create a recursive hash of the node.
|
||||||
|
|
||||||
|
In `++dash`, if this node is a file, then we xor the remainder of the hash with
|
||||||
|
the hash of the contents of the file. The remainder of the hash is `0` if we
|
||||||
|
have no children, else we descend into our children. Basically, we do a half
|
||||||
|
SHA-256 of the xor of the axis of this child and the half SHA-256 of the xor of
|
||||||
|
the name of the child and the hash of the child. This is done for each child
|
||||||
|
and all the results are xored together.
|
||||||
|
|
||||||
|
Now we return to our discussion of `++azel`.
|
||||||
|
|
||||||
|
We fold over every path in this version of the filesystem and create a great
|
||||||
|
ankh out of them. First, we call `++zaul` to get the raw data referred to be
|
||||||
|
each lobe.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ zaul :: grab blob
|
||||||
|
|= p=lobe :: ^- *
|
||||||
|
%- zaru
|
||||||
|
(zaal p)
|
||||||
|
```
|
||||||
|
|
||||||
|
This converts a lobe into the raw data it refers to by first getting the blob
|
||||||
|
with `++zaal` and converting that into data with `++zaru`.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ zaal :: grab blob
|
||||||
|
|= p=lobe :: (raw)
|
||||||
|
^- blob
|
||||||
|
(~(got by lat) p)
|
||||||
|
```
|
||||||
|
|
||||||
|
This just grabs the blob that the lobe refers to.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ zaru :: grab blob
|
||||||
|
|= p=blob
|
||||||
|
?- -.p
|
||||||
|
%delta (lump r.p (zaul q.p))
|
||||||
|
%direct q.p
|
||||||
|
%indirect q.p
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
If we have either a direct or an indirect blob, then the data is stored right
|
||||||
|
in the blob. Otherwise, we have to reconstruct it from the diffs. We do this
|
||||||
|
by calling `++lump` on the diff in the blob with the data obtained by
|
||||||
|
recursively calling the parent of this blob.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ lump :: apply patch
|
||||||
|
|= [don=udon src=*]
|
||||||
|
^- *
|
||||||
|
?+ p.don ~|(%unsupported !!)
|
||||||
|
%a
|
||||||
|
?+ -.q.don ~|(%unsupported !!)
|
||||||
|
%a q.q.don
|
||||||
|
%c (lurk ((hard (list)) src) p.q.don)
|
||||||
|
%d (lure src p.q.don)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
%c
|
||||||
|
=+ dst=(lore ((hard ,@) src))
|
||||||
|
%- roly
|
||||||
|
?+ -.q.don ~|(%unsupported !!)
|
||||||
|
%a ((hard (list ,@t)) q.q.don)
|
||||||
|
%c (lurk dst p.q.don)
|
||||||
|
==
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
This is defined in `arvo/hoon.hoon` for historical reasons which are likely no
|
||||||
|
longer applicable. Since the `++umph` structure will likely change we convert
|
||||||
|
clay to be a typed filesystem, we'll only give a high-level description of this
|
||||||
|
process. If we have a `%a` udon, then we're performing a trivial replace, so
|
||||||
|
we produce simply `q.q.don`. If we have a `%c` udon, then we're performing a
|
||||||
|
list merge (as in, for example, lines of text). The merge is performed by
|
||||||
|
`++lurk`.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ lurk :: apply list patch
|
||||||
|
|* [hel=(list) rug=(urge)]
|
||||||
|
^+ hel
|
||||||
|
=+ war=`_hel`~
|
||||||
|
|- ^+ hel
|
||||||
|
?~ rug (flop war)
|
||||||
|
?- -.i.rug
|
||||||
|
&
|
||||||
|
%= $
|
||||||
|
rug t.rug
|
||||||
|
hel (slag p.i.rug hel)
|
||||||
|
war (weld (flop (scag p.i.rug hel)) war)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
|
|
||||||
|
%= $
|
||||||
|
rug t.rug
|
||||||
|
hel =+ gur=(flop p.i.rug)
|
||||||
|
|- ^+ hel
|
||||||
|
?~ gur hel
|
||||||
|
?>(&(?=(^ hel) =(i.gur i.hel)) $(hel t.hel, gur t.gur))
|
||||||
|
war (weld q.i.rug war)
|
||||||
|
==
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
We accumulate our final result in `war`. If there's nothing more in our list
|
||||||
|
of merge instructions (unces), we just reverse `war` and produce it.
|
||||||
|
Otherwise, we process another unce. If the unce is of type `&`, then we have
|
||||||
|
`p.i.rug` lines of no changes, so we just copy them over from `hel` to `war`.
|
||||||
|
If the unice is of type `|`, then we verify that the source lines (in `hel`)
|
||||||
|
are what we expect them to be (`p.i.rug`), crashing on failure. If they're
|
||||||
|
good, then we append the new lines in `q.i.rug` onto `war`.
|
||||||
|
|
||||||
|
And that's really it. List merges are pretty easy. Anyway, if you recall, we
|
||||||
|
were discussing `++azel`.
|
||||||
|
|
||||||
|
```
|
||||||
|
++ azel :: azel:ze
|
||||||
|
|= hat=(map path lobe) :: checkout commit
|
||||||
|
^- ankh
|
||||||
|
%- cosh
|
||||||
|
%+ roll (~(tap by hat) ~)
|
||||||
|
|= [[pat=path bar=lobe] ank=ankh]
|
||||||
|
^- ankh
|
||||||
|
%- cosh
|
||||||
|
?~ pat
|
||||||
|
=+ zar=(zaul bar)
|
||||||
|
[_cash [~ [(sham zar) zar]] `(map ,@ta ankh)`~]
|
||||||
|
=+ nak=(~(get by r.ank) i.pat)
|
||||||
|
%= ank
|
||||||
|
r %+ ~(put by r.ank) i.pat
|
||||||
|
$(pat t.pat, ank (fall nak _ankh))
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
If the path is null, then we calculate `zar`, the raw data at the path `pat` in
|
||||||
|
this version. We produce a default ankh with the correct data (and its hash)
|
||||||
|
and no children.
|
||||||
|
|
||||||
|
Otherwise, we try to get the child we're looking at from our parent `ankh`. If
|
||||||
|
it's already been created, this succeeds; otherwise, we simply create a default
|
||||||
|
blank ankh. We place ourselves in our parent after recursively computing our
|
||||||
|
children.
|
||||||
|
|
||||||
|
This algorithm really isn't that complicated, but it may not be immediately
|
||||||
|
obvious why it works. An example should clear everything up.
|
||||||
|
|
||||||
|
Suppose `hat` is a map of the following information.
|
||||||
|
|
||||||
|
/greeting --> "customary upon meeting"
|
||||||
|
/greeting/english --> "hello"
|
||||||
|
/greeting/spanish --> "hola"
|
||||||
|
/greeting/russian/short --> "привет"
|
||||||
|
/greeting/russian/long --> "Здравствуйте"
|
||||||
|
/farewell/russian --> "до свидания"
|
||||||
|
|
||||||
|
Furthermore, let's say that we process them in this order:
|
||||||
|
|
||||||
|
/greeting/english
|
||||||
|
/greeting/russian/short
|
||||||
|
/greeting/russian/long
|
||||||
|
/greeting
|
||||||
|
/greeting/spanish
|
||||||
|
/farewell/russian
|
||||||
|
|
||||||
|
Then, the first path we process is `/greeting/english` . Since our path is not
|
||||||
|
null, we try to get `nak`, but because our ankh is blank at this point it
|
||||||
|
doesn't find anything. Thus, update our blank top-level ankh with a child
|
||||||
|
`greeting`. and recurse with the blank `nak` to create the ankh of the new
|
||||||
|
child.
|
||||||
|
|
||||||
|
In the recursion, we our path is `/english` and our ankh is again blank. We
|
||||||
|
try to get the `english` child of our ankh, but this of course fails. Thus,
|
||||||
|
we update our blank `/greeting` ankh with a child `english` produced by recursing.
|
||||||
|
|
||||||
|
Now our path is null, so we call `++zaul` to get the actual data, and we place
|
||||||
|
it in a brand-new ankh filled with this data and no children.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
|
||||||
=+ nab=(~(aeon ze lim dom ran) p.p.rav)
|
=+ nab=(~(aeon ze lim dom ran) p.p.rav)
|
||||||
|
Loading…
Reference in New Issue
Block a user