view tests/test-branches.t @ 44950:f9734b2d59cc

py3: make stdout line-buffered if connected to a TTY Status messages that are to be shown on the terminal should be written to the file descriptor before anything further is done, to keep the user updated. One common way to achieve this is to make stdout line-buffered if it is connected to a TTY. This is done on Python 2 (except on Windows, where libc, which the CPython 2 streams depend on, does not properly support this). Python 3 rolls it own I/O streams. On Python 3, buffered binary streams can't be set line-buffered. The previous code (added in 227ba1afcb65) incorrectly assumed that on Python 3, pycompat.stdout (sys.stdout.buffer) is already line-buffered. However the interpreter initializes it with a block-buffered stream or an unbuffered stream (when the -u option or the PYTHONUNBUFFERED environment variable is set), never with a line-buffered stream. One example where the current behavior is unacceptable is when running `hg pull https://www.mercurial-scm.org/repo/hg` on Python 3, where the line "pulling from https://www.mercurial-scm.org/repo/hg" does not appear on the terminal before the hg process blocks while waiting for the server. Various approaches to fix this problem are possible, including: 1. Weaken the contract of procutil.stdout to not give any guarantees about buffering behavior. In this case, users of procutil.stdout need to be changed to do enough flushes. In particular, 1. either ui must insert enough flushes for ui.write() and friends, or 2. ui.write() and friends get split into flushing and fully buffered methods, or 3. users of ui.write() and friends must flush explicitly. 2. Make stdout unbuffered. 3. Make stdout line-buffered. Since Python 3 does not natively support that for binary streams, we must implement it ourselves. (2.) is problematic because using unbuffered I/O changes the performance characteristics significantly compared to line-buffered (which is used on Python 2) and this would be a regression. (1.2.) and (1.3) are a substantial amount of work. It’s unclear whether the added complexity would be justified, given that raw performance doesn’t matter that much when writing to a terminal much faster than the user could read it. (1.1.) pushes complexity into the ui class instead of separating the concern of how stdout is buffered. Other users of procutil.stdout would still need to take care of the flushes. This patch implements (3.). The general performance considerations are very similar to (1.1.). The extra method invocation and method forwarding add a little more overhead if the class is used. In exchange, it doesn’t add overhead if not used. For the benchmarks, I compared the previous implementation (incorrect on Python 3), (1.1.), (3.) and (2.). The command was chosen so that the streams were configured as if they were writing to a TTY, but actually write to a pager, which is also the default: HGRCPATH=/dev/null python3 ./hg --cwd ~/vcs/mozilla-central --time --pager yes --config pager.pager='cat > /dev/null' status --all previous: time: real 7.880 secs (user 7.290+0.050 sys 0.580+0.170) time: real 7.830 secs (user 7.220+0.070 sys 0.590+0.140) time: real 7.800 secs (user 7.210+0.050 sys 0.570+0.170) (1.1.) using Yuya Nishihara’s patch: time: real 9.860 secs (user 8.670+0.350 sys 1.160+0.830) time: real 9.540 secs (user 8.430+0.370 sys 1.100+0.770) time: real 9.830 secs (user 8.630+0.370 sys 1.180+0.840) (3.) using this patch: time: real 9.580 secs (user 8.480+0.350 sys 1.090+0.770) time: real 9.670 secs (user 8.480+0.330 sys 1.170+0.860) time: real 9.640 secs (user 8.500+0.350 sys 1.130+0.810) (2.) using a previous patch by me: time: real 10.480 secs (user 8.850+0.720 sys 1.590+1.500) time: real 10.490 secs (user 8.750+0.750 sys 1.710+1.470) time: real 10.240 secs (user 8.600+0.700 sys 1.590+1.510) As expected, there’s no difference on Python 2, as exactly the same code paths are used: previous: time: real 6.950 secs (user 5.870+0.330 sys 1.070+0.770) time: real 7.040 secs (user 6.040+0.360 sys 0.980+0.750) time: real 7.070 secs (user 5.950+0.360 sys 1.100+0.760) this patch: time: real 7.010 secs (user 5.900+0.390 sys 1.070+0.730) time: real 7.000 secs (user 5.850+0.350 sys 1.120+0.760) time: real 7.000 secs (user 5.790+0.380 sys 1.170+0.710)
author Manuel Jacob <me@manueljacob.de>
date Wed, 10 Jun 2020 13:02:39 +0200
parents 2e1d9414ff71
children 8d72e29ad1e0
line wrap: on
line source

  $ hg init a
  $ cd a

Verify checking branch of nullrev before the cache is created doesnt crash
  $ hg log -r 'branch(.)' -T '{branch}\n'

Basic test
  $ echo 'root' >root
  $ hg add root
  $ hg commit -d '0 0' -m "Adding root node"

  $ echo 'a' >a
  $ hg add a
  $ hg branch a
  marked working directory as branch a
  (branches are permanent and global, did you want a bookmark?)
  $ hg commit -d '1 0' -m "Adding a branch"

  $ hg branch q
  marked working directory as branch q
  $ echo 'aa' >a
  $ hg branch -C
  reset working directory to branch a
  $ hg commit -d '2 0' -m "Adding to a branch"

  $ hg update -C 0
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ echo 'b' >b
  $ hg add b
  $ hg branch b
  marked working directory as branch b
  $ hg commit -d '2 0' -m "Adding b branch"

  $ echo 'bh1' >bh1
  $ hg add bh1
  $ hg commit -d '3 0' -m "Adding b branch head 1"

  $ hg update -C 2
  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
  $ echo 'bh2' >bh2
  $ hg add bh2
  $ hg commit -d '4 0' -m "Adding b branch head 2"

  $ echo 'c' >c
  $ hg add c
  $ hg branch c
  marked working directory as branch c
  $ hg commit -d '5 0' -m "Adding c branch"

reserved names

  $ hg branch tip
  abort: the name 'tip' is reserved
  [255]
  $ hg branch null
  abort: the name 'null' is reserved
  [255]
  $ hg branch .
  abort: the name '.' is reserved
  [255]

invalid characters

  $ hg branch 'foo:bar'
  abort: ':' cannot be used in a name
  [255]

  $ hg branch 'foo
  > bar'
  abort: '\n' cannot be used in a name
  [255]

trailing or leading spaces should be stripped before testing duplicates

  $ hg branch 'b '
  abort: a branch of the same name already exists
  (use 'hg update' to switch to it)
  [255]

  $ hg branch ' b'
  abort: a branch of the same name already exists
  (use 'hg update' to switch to it)
  [255]

verify update will accept invalid legacy branch names

  $ hg init test-invalid-branch-name
  $ cd test-invalid-branch-name
  $ hg unbundle -u "$TESTDIR"/bundles/test-invalid-branch-name.hg
  adding changesets
  adding manifests
  adding file changes
  added 3 changesets with 3 changes to 2 files
  new changesets f0e4c7f04036:33c2ceb9310b (3 drafts)
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved

  $ hg update '"colon:test"'
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ cd ..

  $ echo 'd' >d
  $ hg add d
  $ hg branch 'a branch name much longer than the default justification used by branches'
  marked working directory as branch a branch name much longer than the default justification used by branches
  $ hg commit -d '6 0' -m "Adding d branch"

  $ hg branches
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  b                              4:aee39cd168d0
  c                              6:589736a22561 (inactive)
  a                              5:d8cbc61dbaa6 (inactive)
  default                        0:19709c5a4e75 (inactive)

-------

  $ hg branches -a
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  b                              4:aee39cd168d0

--- Branch a

  $ hg log -b a
  changeset:   5:d8cbc61dbaa6
  branch:      a
  parent:      2:881fe2b92ad0
  user:        test
  date:        Thu Jan 01 00:00:04 1970 +0000
  summary:     Adding b branch head 2
  
  changeset:   2:881fe2b92ad0
  branch:      a
  user:        test
  date:        Thu Jan 01 00:00:02 1970 +0000
  summary:     Adding to a branch
  
  changeset:   1:dd6b440dd85a
  branch:      a
  user:        test
  date:        Thu Jan 01 00:00:01 1970 +0000
  summary:     Adding a branch
  

---- Branch b

  $ hg log -b b
  changeset:   4:aee39cd168d0
  branch:      b
  user:        test
  date:        Thu Jan 01 00:00:03 1970 +0000
  summary:     Adding b branch head 1
  
  changeset:   3:ac22033332d1
  branch:      b
  parent:      0:19709c5a4e75
  user:        test
  date:        Thu Jan 01 00:00:02 1970 +0000
  summary:     Adding b branch
  

---- going to test branch listing by rev
  $ hg branches -r0
  default                        0:19709c5a4e75 (inactive)
  $ hg branches -qr0
  default
--- now more than one rev
  $ hg branches -r2:5
  b                              4:aee39cd168d0
  a                              5:d8cbc61dbaa6 (inactive)
  $ hg branches -qr2:5
  b
  a
---- going to test branch closing

  $ hg branches
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  b                              4:aee39cd168d0
  c                              6:589736a22561 (inactive)
  a                              5:d8cbc61dbaa6 (inactive)
  default                        0:19709c5a4e75 (inactive)
  $ hg up -C b
  2 files updated, 0 files merged, 4 files removed, 0 files unresolved
  $ echo 'xxx1' >> b
  $ hg commit -d '7 0' -m 'adding cset to branch b'
  $ hg up -C aee39cd168d0
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo 'xxx2' >> b
  $ hg commit -d '8 0' -m 'adding head to branch b'
  created new head
  $ echo 'xxx3' >> b
  $ hg commit -d '9 0' -m 'adding another cset to branch b'
  $ hg branches
  b                             10:bfbe841b666e
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  c                              6:589736a22561 (inactive)
  a                              5:d8cbc61dbaa6 (inactive)
  default                        0:19709c5a4e75 (inactive)
  $ hg heads --closed
  changeset:   10:bfbe841b666e
  branch:      b
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     adding another cset to branch b
  
  changeset:   8:eebb944467c9
  branch:      b
  parent:      4:aee39cd168d0
  user:        test
  date:        Thu Jan 01 00:00:07 1970 +0000
  summary:     adding cset to branch b
  
  changeset:   7:10ff5895aa57
  branch:      a branch name much longer than the default justification used by branches
  user:        test
  date:        Thu Jan 01 00:00:06 1970 +0000
  summary:     Adding d branch
  
  changeset:   6:589736a22561
  branch:      c
  user:        test
  date:        Thu Jan 01 00:00:05 1970 +0000
  summary:     Adding c branch
  
  changeset:   5:d8cbc61dbaa6
  branch:      a
  parent:      2:881fe2b92ad0
  user:        test
  date:        Thu Jan 01 00:00:04 1970 +0000
  summary:     Adding b branch head 2
  
  changeset:   0:19709c5a4e75
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     Adding root node
  
  $ hg heads
  changeset:   10:bfbe841b666e
  branch:      b
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     adding another cset to branch b
  
  changeset:   8:eebb944467c9
  branch:      b
  parent:      4:aee39cd168d0
  user:        test
  date:        Thu Jan 01 00:00:07 1970 +0000
  summary:     adding cset to branch b
  
  changeset:   7:10ff5895aa57
  branch:      a branch name much longer than the default justification used by branches
  user:        test
  date:        Thu Jan 01 00:00:06 1970 +0000
  summary:     Adding d branch
  
  changeset:   6:589736a22561
  branch:      c
  user:        test
  date:        Thu Jan 01 00:00:05 1970 +0000
  summary:     Adding c branch
  
  changeset:   5:d8cbc61dbaa6
  branch:      a
  parent:      2:881fe2b92ad0
  user:        test
  date:        Thu Jan 01 00:00:04 1970 +0000
  summary:     Adding b branch head 2
  
  changeset:   0:19709c5a4e75
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     Adding root node
  
  $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
  $ hg branches -a
  b                              8:eebb944467c9
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  $ hg up -C b
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
  $ hg commit -d '9 0' --close-branch -m 're-closing this branch'
  abort: current revision is already a branch closing head
  [255]

  $ hg log -r tip --debug
  changeset:   12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f
  branch:      b
  tag:         tip
  phase:       draft
  parent:      8:eebb944467c9fb9651ed232aeaf31b3c0a7fc6c1
  parent:      -1:0000000000000000000000000000000000000000
  manifest:    8:6f9ed32d2b310e391a4f107d5f0f071df785bfee
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  extra:       branch=b
  extra:       close=1
  description:
  close this part branch too
  
  
--- b branch should be inactive

  $ hg branches
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  c                              6:589736a22561 (inactive)
  a                              5:d8cbc61dbaa6 (inactive)
  default                        0:19709c5a4e75 (inactive)
  $ hg branches -c
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  b                             12:e3d49c0575d8 (closed)
  c                              6:589736a22561 (inactive)
  a                              5:d8cbc61dbaa6 (inactive)
  default                        0:19709c5a4e75 (inactive)
  $ hg branches -a
  a branch name much longer than the default justification used by branches 7:10ff5895aa57
  $ hg branches -q
  a branch name much longer than the default justification used by branches
  c
  a
  default
  $ hg heads b
  no open branch heads found on branches b
  [1]
  $ hg heads --closed b
  changeset:   12:e3d49c0575d8
  branch:      b
  tag:         tip
  parent:      8:eebb944467c9
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     close this part branch too
  
  changeset:   11:d3f163457ebf
  branch:      b
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     prune bad branch
  
  $ echo 'xxx4' >> b
  $ hg commit -d '9 0' -m 'reopen branch with a change'
  reopening closed branch head 12

--- branch b is back in action

  $ hg branches -a
  b                             13:e23b5505d1ad
  a branch name much longer than the default justification used by branches 7:10ff5895aa57

---- test heads listings

  $ hg heads
  changeset:   13:e23b5505d1ad
  branch:      b
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     reopen branch with a change
  
  changeset:   7:10ff5895aa57
  branch:      a branch name much longer than the default justification used by branches
  user:        test
  date:        Thu Jan 01 00:00:06 1970 +0000
  summary:     Adding d branch
  
  changeset:   6:589736a22561
  branch:      c
  user:        test
  date:        Thu Jan 01 00:00:05 1970 +0000
  summary:     Adding c branch
  
  changeset:   5:d8cbc61dbaa6
  branch:      a
  parent:      2:881fe2b92ad0
  user:        test
  date:        Thu Jan 01 00:00:04 1970 +0000
  summary:     Adding b branch head 2
  
  changeset:   0:19709c5a4e75
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     Adding root node
  

branch default

  $ hg heads default
  changeset:   0:19709c5a4e75
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     Adding root node
  

branch a

  $ hg heads a
  changeset:   5:d8cbc61dbaa6
  branch:      a
  parent:      2:881fe2b92ad0
  user:        test
  date:        Thu Jan 01 00:00:04 1970 +0000
  summary:     Adding b branch head 2
  
  $ hg heads --active a
  no open branch heads found on branches a
  [1]

branch b

  $ hg heads b
  changeset:   13:e23b5505d1ad
  branch:      b
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     reopen branch with a change
  
  $ hg heads --closed b
  changeset:   13:e23b5505d1ad
  branch:      b
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:09 1970 +0000
  summary:     reopen branch with a change
  
  changeset:   11:d3f163457ebf
  branch:      b
  user:        test
  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
  > [extensions]
  > color =
  > [color]
  > mode = ansi
  > EOF

  $ hg up -C b
  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)

  $ cat <<EOF >> $HGRCPATH
  > [extensions]
  > color =
  > [color]
  > branches.active = green
  > branches.closed = blue
  > branches.current = red
  > branches.inactive = magenta
  > log.changeset = cyan
  > EOF

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)

template output:

  $ hg branches -Tjson --closed
  [
   {
    "active": true,
    "branch": "md",
    "closed": false,
    "current": false,
    "node": "c914c99f1fbb2b1d785a0a939ed3f67275df18e9",
    "rev": 18
   },
   {
    "active": true,
    "branch": "b",
    "closed": false,
    "current": true,
    "node": "e23b5505d1ad24aab6f84fd8c7cb8cd8e5e93be0",
    "rev": 13
   },
   {
    "active": true,
    "branch": "a branch name much longer than the default justification used by branches",
    "closed": false,
    "current": false,
    "node": "10ff5895aa5793bd378da574af8cec8ea408d831",
    "rev": 7
   },
   {
    "active": false,
    "branch": "m",
    "closed": false,
    "current": false,
    "node": "df343b0df04feb2a946cd4b6e9520e552fef14ee",
    "rev": 17
   },
   {
    "active": false,
    "branch": "c",
    "closed": true,
    "current": false,
    "node": "f894c25619d3f1484639d81be950e0a07bc6f1f6",
    "rev": 14
   },
   {
    "active": false,
    "branch": "a",
    "closed": false,
    "current": false,
    "node": "d8cbc61dbaa6dc817175d1e301eecb863f280832",
    "rev": 5
   },
   {
    "active": false,
    "branch": "default",
    "closed": false,
    "current": false,
    "node": "19709c5a4e75bf938f8e349aff97438539bb729e",
    "rev": 0
   }
  ]

  $ hg branches --closed -T '{if(closed, "{branch}\n")}'
  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

  $ cat <<'EOF' > "$TESTTMP/map-myjson"
  > docheader = '\{\n'
  > docfooter = '\n}\n'
  > separator = ',\n'
  > branches = ' {dict(branch, node|short)|json}'
  > 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"}
  }

  $ cat <<'EOF' >> .hg/hgrc
  > [templates]
  > myjson = ' {dict(branch, node|short)|json}'
  > myjson:docheader = '\{\n'
  > myjson:docfooter = '\n}\n'
  > myjson:separator = ',\n'
  > 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"}
  }

  $ cat <<'EOF' >> .hg/hgrc
  > [templates]
  > :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

Tests of revision branch name caching

We rev branch cache is updated automatically. In these tests we use a trick to
trigger rebuilds. We remove the branch head cache and run 'hg head' to cause a
rebuild that also will populate the rev branch cache.

revision branch cache is created when building the branch head cache
  $ rm -rf .hg/cache; hg head a -T '{rev}\n'
  5
  $ f --hexdump --size .hg/cache/rbc-*
  .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 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 |................|
  0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
  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 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

  $ echo >> .hg/cache/rbc-revs-v1
  $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
  $ mkdir .hg/cache/rbc-revs-v1
  $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n'
  5
  $ rmdir .hg/cache/rbc-revs-v1
  $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1

no errors when wlock cannot be acquired

#if unix-permissions
  $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
  $ rm -f .hg/cache/branch*
  $ chmod 555 .hg
  $ hg head a -T '{rev}\n'
  5
  $ chmod 755 .hg
  $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
#endif

recovery from invalid cache revs file with trailing data
  $ 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 160
  $ f --size .hg/cache/rbc-revs*
  .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
  $ f --size .hg/cache/rbc-revs*
  .hg/cache/rbc-revs-v1: size=119
  $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
  5
  truncating cache/rbc-revs-v1 to 112
  $ f --size .hg/cache/rbc-revs*
  .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=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
  $ f --size .hg/cache/rbc-revs*
  .hg/cache/rbc-revs-v1: size=8
  $ f -qDB 112 rbc-revs-v1 >> .hg/cache/rbc-revs-v1
  $ f --size .hg/cache/rbc-revs*
  .hg/cache/rbc-revs-v1: size=120
  $ hg log -r 'branch(.)' -T '{rev} ' --debug
  history modification detected - truncating revision branch cache to revision 13
  history modification detected - truncating revision branch cache to revision 1
  3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8
  $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
  5
  truncating cache/rbc-revs-v1 to 104
  $ f --size --hexdump --bytes=16 .hg/cache/rbc-revs*
  .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=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=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 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 |................|
  0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
  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 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=152

cache is rebuilt when corruption is detected
  $ echo > .hg/cache/rbc-names-v1
  $ hg log -r '5:&branch(.)' -T '{rev} ' --debug
  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=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 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 |................|
  0030: 58 97 36 a2 00 00 00 02 10 ff 58 95 00 00 00 03 |X.6.......X.....|
  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:

  $ rm .hg/cache/rbc*
  $ hg log -r "5 & branch(5)" -T "{rev}\n"
  5
  $ f --size --hexdump .hg/cache/rbc-*
  .hg/cache/rbc-names-v1: size=1
  0000: 61                                              |a|
  .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 |................|
  0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
  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 ..

Test for multiple incorrect branch cache entries:

  $ hg init b
  $ cd b
  $ touch f
  $ hg ci -Aqmf
  $ echo >> f
  $ hg ci -Amf
  $ hg branch -q branch
  $ hg ci -Amf

  $ f --size --hexdump .hg/cache/rbc-*
  .hg/cache/rbc-names-v1: size=14
  0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68       |default.branch|
  .hg/cache/rbc-revs-v1: size=24
  0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
  0010: 56 46 78 69 00 00 00 01                         |VFxi....|
  $ : > .hg/cache/rbc-revs-v1

No superfluous rebuilding of cache:
  $ hg log -r "branch(null)&branch(branch)" --debug
  $ f --size --hexdump .hg/cache/rbc-*
  .hg/cache/rbc-names-v1: size=14
  0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68       |default.branch|
  .hg/cache/rbc-revs-v1: size=24
  0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
  0010: 56 46 78 69 00 00 00 01                         |VFxi....|

  $ cd ..

Test to make sure that `--close-branch` only works on a branch head:
--------------------------------------------------------------------
  $ hg init closebranch
  $ cd closebranch
  $ for ch in a b c; do
  > echo $ch > $ch
  > hg add $ch
  > hg ci -m "added "$ch
  > done;

  $ hg up -r "desc('added b')"
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved

trying to close branch from a cset which is not a branch head
it should abort:
  $ hg ci -m "closing branch" --close-branch
  abort: can only close branch heads
  (use --force-close-branch to close branch from a non-head changeset)
  [255]

  $ hg up 0
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ hg log -GT "{rev}: {node|short} {desc|firstline}\n\t{branch}\n\n"
  o  2: 155349b645be added c
  |  	default
  |
  o  1: 5f6d8a4bf34a added b
  |  	default
  |
  @  0: 9092f1db7931 added a
     	default
  
Test --force-close-branch to close a branch from a non-head changeset:
---------------------------------------------------------------------

  $ hg show stack --config extensions.show=
    o  1553 added c
    o  5f6d added b
    @  9092 added a

  $ hg ci -m "branch closed" --close-branch
  abort: can only close branch heads
  (use --force-close-branch to close branch from a non-head changeset)
  [255]

  $ hg ci -m "branch closed" --force-close-branch
  created new head