Mercurial > hg
changeset 34074:abf91c4f9608
branches: correctly show inactive multiheaded branches
Issue being fixed here: `hg branches` incorrectly renders inactive multiheaded
branches as active if they have closed heads.
Example:
```
$ hg log --template '{rev}:{node|short} "{desc}" ({branch}) [parents: {parents}]\n'
4:2e2fa7af8357 "merge" (default) [parents: 0:c94e548c8c7d 3:7be622ae5832 ]
3:7be622ae5832 "2" (somebranch) [parents: 1:81c1d9458987 ]
2:be82cf30409c "close" (somebranch) [parents: ]
1:81c1d9458987 "1" (somebranch) [parents: ]
0:c94e548c8c7d "initial" (default) [parents: ]
$ hg branches
default 4:2e2fa7af8357
somebranch 3:7be622ae5832
```
Branch `somebranch` have two heads, the 1st one being closed (rev 2) and
the other one being merged into default (rev 3). This branch should be shown as
inactive one.
This happens because we intersect branch heads with repo heads to check branch
activity. In this case intersection in a set with one node (rev 2). This head
is closed but the branch is marked as active nevertheless.
Fix is to check branch activity by intersecting only open heads set.
Fixed output:
```
$ hg branches
default 4:2e2fa7af8357
somebranch 3:7be622ae5832 (inactive)
```
Relevant tests for multihead branches added to test-branches suite.
Implentation note about adding `iteropen` method:
At first I have tried to modify `iterbranches` is such a way that it would
filter out closed heads itself. For example it could have `closed=False`
parameter. But in this case we would have to filter closed tips as well.
Reasoning in terms of `hg branches` we actually are not allowed to do this.
Also, we need to do heads filtering only if tip is not closed itself. But if it
is - we are ok to skip filtering, because branch is already known to be inactive.
So we can't implement heads filtering in `iterbranches` in elegant way, because
we will end up with something like `closed_heads=False` or even
`closed_heads_is_tip_is_open`. Finally I decided to move this logic to the
`branches` function, adding `iteropen` helper method.
Differential Revision: https://phab.mercurial-scm.org/D583
author | the31k <the31k@thethirty.one> |
---|---|
date | Thu, 31 Aug 2017 18:24:08 +0300 |
parents | 7bbc4e113e5f |
children | a9d8caf95941 |
files | mercurial/branchmap.py mercurial/commands.py tests/test-branches.t |
diffstat | 3 files changed, 198 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/branchmap.py Sun Sep 03 21:17:25 2017 +0900 +++ b/mercurial/branchmap.py Thu Aug 31 18:24:08 2017 +0300 @@ -211,10 +211,13 @@ Raise KeyError for unknown branch.''' return self._branchtip(self[branch])[0] + def iteropen(self, nodes): + return (n for n in nodes if n not in self._closednodes) + def branchheads(self, branch, closed=False): heads = self[branch] if not closed: - heads = [h for h in heads if h not in self._closednodes] + heads = list(self.iteropen(heads)) return heads def iterbranches(self):
--- a/mercurial/commands.py Sun Sep 03 21:17:25 2017 +0900 +++ b/mercurial/commands.py Thu Aug 31 18:24:08 2017 +0300 @@ -1077,7 +1077,10 @@ allheads = set(repo.heads()) branches = [] for tag, heads, tip, isclosed in repo.branchmap().iterbranches(): - isactive = not isclosed and bool(set(heads) & allheads) + isactive = False + if not isclosed: + openheads = set(repo.branchmap().iteropen(heads)) + isactive = bool(openheads & allheads) branches.append((tag, repo[tip], isactive, not isclosed)) branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
--- a/tests/test-branches.t Sun Sep 03 21:17:25 2017 +0900 +++ b/tests/test-branches.t Thu Aug 31 18:24:08 2017 +0300 @@ -418,6 +418,131 @@ date: Thu Jan 01 00:00:09 1970 +0000 summary: prune bad branch + +reclose branch + + $ hg up -C c + 3 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg commit -d '9 0' --close-branch -m 'reclosing this branch' + $ hg branches + b 13:e23b5505d1ad + a branch name much longer than the default justification used by branches 7:10ff5895aa57 + a 5:d8cbc61dbaa6 (inactive) + default 0:19709c5a4e75 (inactive) + $ hg branches --closed + b 13:e23b5505d1ad + a branch name much longer than the default justification used by branches 7:10ff5895aa57 + c 14:f894c25619d3 (closed) + a 5:d8cbc61dbaa6 (inactive) + default 0:19709c5a4e75 (inactive) + +multihead branch + + $ hg up -C default + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg branch m + marked working directory as branch m + $ touch m + $ hg add m + $ hg commit -d '10 0' -m 'multihead base' + $ echo "m1" >m + $ hg commit -d '10 0' -m 'head 1' + $ hg up -C '.^' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo "m2" >m + $ hg commit -d '10 0' -m 'head 2' + created new head + $ hg log -b m + changeset: 17:df343b0df04f + branch: m + tag: tip + parent: 15:f3447637f53e + user: test + date: Thu Jan 01 00:00:10 1970 +0000 + summary: head 2 + + changeset: 16:a58ca5d3bdf3 + branch: m + user: test + date: Thu Jan 01 00:00:10 1970 +0000 + summary: head 1 + + changeset: 15:f3447637f53e + branch: m + parent: 0:19709c5a4e75 + user: test + date: Thu Jan 01 00:00:10 1970 +0000 + summary: multihead base + + $ hg heads --topo m + changeset: 17:df343b0df04f + branch: m + tag: tip + parent: 15:f3447637f53e + user: test + date: Thu Jan 01 00:00:10 1970 +0000 + summary: head 2 + + changeset: 16:a58ca5d3bdf3 + branch: m + user: test + date: Thu Jan 01 00:00:10 1970 +0000 + summary: head 1 + + $ hg branches + m 17:df343b0df04f + b 13:e23b5505d1ad + a branch name much longer than the default justification used by branches 7:10ff5895aa57 + a 5:d8cbc61dbaa6 (inactive) + default 0:19709c5a4e75 (inactive) + +partially merge multihead branch + + $ hg up -C default + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg branch md + marked working directory as branch md + $ hg merge m + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -d '11 0' -m 'merge head 2' + $ hg heads --topo m + changeset: 16:a58ca5d3bdf3 + branch: m + user: test + date: Thu Jan 01 00:00:10 1970 +0000 + summary: head 1 + + $ hg branches + md 18:c914c99f1fbb + m 17:df343b0df04f + b 13:e23b5505d1ad + a branch name much longer than the default justification used by branches 7:10ff5895aa57 + a 5:d8cbc61dbaa6 (inactive) + default 0:19709c5a4e75 (inactive) + +partially close multihead branch + + $ hg up -C a58ca5d3bdf3 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg commit -d '12 0' -m 'close head 1' --close-branch + $ hg heads --topo m + changeset: 19:cd21a80baa3d + branch: m + tag: tip + parent: 16:a58ca5d3bdf3 + user: test + date: Thu Jan 01 00:00:12 1970 +0000 + summary: close head 1 + + $ hg branches + md 18:c914c99f1fbb + b 13:e23b5505d1ad + a branch name much longer than the default justification used by branches 7:10ff5895aa57 + m 17:df343b0df04f (inactive) + a 5:d8cbc61dbaa6 (inactive) + default 0:19709c5a4e75 (inactive) + default branch colors: $ cat <<EOF >> $HGRCPATH @@ -427,22 +552,23 @@ > mode = ansi > EOF - $ hg up -C c - 3 files updated, 0 files merged, 2 files removed, 0 files unresolved - $ hg commit -d '9 0' --close-branch -m 'reclosing this branch' $ hg up -C b - 2 files updated, 0 files merged, 3 files removed, 0 files unresolved + 2 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg branches --color=always + \x1b[0;0mmd\x1b[0m\x1b[0;33m 18:c914c99f1fbb\x1b[0m (esc) \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc) \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc) + \x1b[0;0mm\x1b[0m\x1b[0;33m 17:df343b0df04f\x1b[0m (inactive) (esc) \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc) \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc) default closed branch color: $ hg branches --color=always --closed + \x1b[0;0mmd\x1b[0m\x1b[0;33m 18:c914c99f1fbb\x1b[0m (esc) \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc) \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc) + \x1b[0;0mm\x1b[0m\x1b[0;33m 17:df343b0df04f\x1b[0m (inactive) (esc) \x1b[0;30;1mc\x1b[0m\x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc) \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc) \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc) @@ -461,16 +587,20 @@ custom branch colors: $ hg branches --color=always + \x1b[0;32mmd\x1b[0m\x1b[0;36m 18:c914c99f1fbb\x1b[0m (esc) \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc) \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc) + \x1b[0;35mm\x1b[0m\x1b[0;36m 17:df343b0df04f\x1b[0m (inactive) (esc) \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc) \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc) custom closed branch color: $ hg branches --color=always --closed + \x1b[0;32mmd\x1b[0m\x1b[0;36m 18:c914c99f1fbb\x1b[0m (esc) \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc) \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc) + \x1b[0;35mm\x1b[0m\x1b[0;36m 17:df343b0df04f\x1b[0m (inactive) (esc) \x1b[0;34mc\x1b[0m\x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc) \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc) \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc) @@ -481,6 +611,14 @@ [ { "active": true, + "branch": "md", + "closed": false, + "current": false, + "node": "c914c99f1fbb2b1d785a0a939ed3f67275df18e9", + "rev": 18 + }, + { + "active": true, "branch": "b", "closed": false, "current": true, @@ -497,6 +635,14 @@ }, { "active": false, + "branch": "m", + "closed": false, + "current": false, + "node": "df343b0df04feb2a946cd4b6e9520e552fef14ee", + "rev": 17 + }, + { + "active": false, "branch": "c", "closed": true, "current": false, @@ -525,8 +671,10 @@ c $ hg branches -T '{word(0, branch)}: {desc|firstline}\n' + md: merge head 2 b: reopen branch with a change a: Adding d branch + m: head 2 a: Adding b branch head 2 default: Adding root node @@ -538,8 +686,10 @@ > EOF $ hg branches -T "$TESTTMP/map-myjson" { + {"branch": "md", "node": "c914c99f1fbb"}, {"branch": "b", "node": "e23b5505d1ad"}, {"branch": "a branch *", "node": "10ff5895aa57"}, (glob) + {"branch": "m", "node": "df343b0df04f"}, {"branch": "a", "node": "d8cbc61dbaa6"}, {"branch": "default", "node": "19709c5a4e75"} } @@ -553,8 +703,10 @@ > EOF $ hg branches -T myjson { + {"branch": "md", "node": "c914c99f1fbb"}, {"branch": "b", "node": "e23b5505d1ad"}, {"branch": "a branch *", "node": "10ff5895aa57"}, (glob) + {"branch": "m", "node": "df343b0df04f"}, {"branch": "a", "node": "d8cbc61dbaa6"}, {"branch": "default", "node": "19709c5a4e75"} } @@ -564,8 +716,10 @@ > :docheader = 'should not be selected as a docheader for literal templates\n' > EOF $ hg branches -T '{branch}\n' + md b a branch name much longer than the default justification used by branches + m a default @@ -579,14 +733,14 @@ $ rm -rf .hg/cache; hg head a -T '{rev}\n' 5 $ f --hexdump --size .hg/cache/rbc-* - .hg/cache/rbc-names-v1: size=87 + .hg/cache/rbc-names-v1: size=92 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a | 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much| 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the| 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi| 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b| - 0050: 72 61 6e 63 68 65 73 |ranches| - .hg/cache/rbc-revs-v1: size=120 + 0050: 72 61 6e 63 68 65 73 00 6d 00 6d 64 |ranches.m.md| + .hg/cache/rbc-revs-v1: size=160 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....| 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....| 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................| @@ -594,7 +748,9 @@ 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....| 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....| 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....| - 0070: f8 94 c2 56 80 00 00 03 |...V....| + 0070: f8 94 c2 56 80 00 00 03 f3 44 76 37 00 00 00 05 |...V.....Dv7....| + 0080: a5 8c a5 d3 00 00 00 05 df 34 3b 0d 00 00 00 05 |.........4;.....| + 0090: c9 14 c9 9f 00 00 00 06 cd 21 a8 0b 80 00 00 05 |.........!......| no errors when revbranchcache is not writable @@ -622,9 +778,9 @@ $ echo >> .hg/cache/rbc-revs-v1 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug 5 - truncating cache/rbc-revs-v1 to 120 + truncating cache/rbc-revs-v1 to 160 $ f --size .hg/cache/rbc-revs* - .hg/cache/rbc-revs-v1: size=120 + .hg/cache/rbc-revs-v1: size=160 recovery from invalid cache file with partial last record $ mv .hg/cache/rbc-revs-v1 . $ f -qDB 119 rbc-revs-v1 > .hg/cache/rbc-revs-v1 @@ -634,14 +790,14 @@ 5 truncating cache/rbc-revs-v1 to 112 $ f --size .hg/cache/rbc-revs* - .hg/cache/rbc-revs-v1: size=120 + .hg/cache/rbc-revs-v1: size=160 recovery from invalid cache file with missing record - no truncation $ mv .hg/cache/rbc-revs-v1 . $ f -qDB 112 rbc-revs-v1 > .hg/cache/rbc-revs-v1 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug 5 $ f --size .hg/cache/rbc-revs* - .hg/cache/rbc-revs-v1: size=120 + .hg/cache/rbc-revs-v1: size=160 recovery from invalid cache file with some bad records $ mv .hg/cache/rbc-revs-v1 . $ f -qDB 8 rbc-revs-v1 > .hg/cache/rbc-revs-v1 @@ -658,29 +814,29 @@ 5 truncating cache/rbc-revs-v1 to 104 $ f --size --hexdump --bytes=16 .hg/cache/rbc-revs* - .hg/cache/rbc-revs-v1: size=120 + .hg/cache/rbc-revs-v1: size=160 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....| cache is updated when committing $ hg branch i-will-regret-this marked working directory as branch i-will-regret-this $ hg ci -m regrets $ f --size .hg/cache/rbc-* - .hg/cache/rbc-names-v1: size=106 - .hg/cache/rbc-revs-v1: size=128 + .hg/cache/rbc-names-v1: size=111 + .hg/cache/rbc-revs-v1: size=168 update after rollback - the cache will be correct but rbc-names will will still contain the branch name even though it no longer is used $ hg up -qr '.^' $ hg rollback -qf $ f --size --hexdump .hg/cache/rbc-* - .hg/cache/rbc-names-v1: size=106 + .hg/cache/rbc-names-v1: size=111 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a | 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much| 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the| 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi| 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b| - 0050: 72 61 6e 63 68 65 73 00 69 2d 77 69 6c 6c 2d 72 |ranches.i-will-r| - 0060: 65 67 72 65 74 2d 74 68 69 73 |egret-this| - .hg/cache/rbc-revs-v1: size=120 + 0050: 72 61 6e 63 68 65 73 00 6d 00 6d 64 00 69 2d 77 |ranches.m.md.i-w| + 0060: 69 6c 6c 2d 72 65 67 72 65 74 2d 74 68 69 73 |ill-regret-this| + .hg/cache/rbc-revs-v1: size=160 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....| 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....| 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................| @@ -688,12 +844,14 @@ 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....| 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....| 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....| - 0070: f8 94 c2 56 80 00 00 03 |...V....| + 0070: f8 94 c2 56 80 00 00 03 f3 44 76 37 00 00 00 05 |...V.....Dv7....| + 0080: a5 8c a5 d3 00 00 00 05 df 34 3b 0d 00 00 00 05 |.........4;.....| + 0090: c9 14 c9 9f 00 00 00 06 cd 21 a8 0b 80 00 00 05 |.........!......| cache is updated/truncated when stripping - it is thus very hard to get in a situation where the cache is out of sync and the hash check detects it $ hg --config extensions.strip= strip -r tip --nob $ f --size .hg/cache/rbc-revs* - .hg/cache/rbc-revs-v1: size=112 + .hg/cache/rbc-revs-v1: size=152 cache is rebuilt when corruption is detected $ echo > .hg/cache/rbc-names-v1 @@ -701,13 +859,14 @@ referenced branch names not found - rebuilding revision branch cache from scratch 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 40 $ f --size --hexdump .hg/cache/rbc-* - .hg/cache/rbc-names-v1: size=79 + .hg/cache/rbc-names-v1: size=84 0000: 62 00 61 00 63 00 61 20 62 72 61 6e 63 68 20 6e |b.a.c.a branch n| 0010: 61 6d 65 20 6d 75 63 68 20 6c 6f 6e 67 65 72 20 |ame much longer | 0020: 74 68 61 6e 20 74 68 65 20 64 65 66 61 75 6c 74 |than the default| 0030: 20 6a 75 73 74 69 66 69 63 61 74 69 6f 6e 20 75 | justification u| - 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 |sed by branches| - .hg/cache/rbc-revs-v1: size=112 + 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 00 |sed by branches.| + 0050: 6d 00 6d 64 |m.md| + .hg/cache/rbc-revs-v1: size=152 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 01 |................| @@ -715,6 +874,9 @@ 0040: ee bb 94 44 00 00 00 00 5f 40 61 bb 00 00 00 00 |...D...._@a.....| 0050: bf be 84 1b 00 00 00 00 d3 f1 63 45 80 00 00 00 |..........cE....| 0060: e3 d4 9c 05 80 00 00 00 e2 3b 55 05 00 00 00 00 |.........;U.....| + 0070: f8 94 c2 56 80 00 00 02 f3 44 76 37 00 00 00 04 |...V.....Dv7....| + 0080: a5 8c a5 d3 00 00 00 04 df 34 3b 0d 00 00 00 04 |.........4;.....| + 0090: c9 14 c9 9f 00 00 00 05 |........| Test that cache files are created and grows correctly: @@ -724,7 +886,7 @@ $ f --size --hexdump .hg/cache/rbc-* .hg/cache/rbc-names-v1: size=1 0000: 61 |a| - .hg/cache/rbc-revs-v1: size=112 + .hg/cache/rbc-revs-v1: size=152 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 00 |................| @@ -732,6 +894,9 @@ 0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0090: 00 00 00 00 00 00 00 00 |........| $ cd ..