dirstate-tree: Add tree traversal/iteration
Like Python’s, Rust’s iterators are "external" in that they are driven
by a caller who calls a `next` method. This is as opposed to "internal"
iterators who drive themselves and call a callback for each item.
Writing an internal iterator traversing a tree is easy with recursion,
but internal iterators cannot rely on the call stack in that way,
they must save in an explicit object all state that they need to be
preserved across two `next` calls.
This algorithm uses a `Vec` as a stack that contains what would be
local variables on the call stack if we could use recursion.
Differential Revision: https://phab.mercurial-scm.org/D10370
Test that, when an hg push is interrupted and the remote side recieves SIGPIPE,
the remote hg is able to successfully roll back the transaction.
$ hg init -q remote
$ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" -q ssh://user@dummy/`pwd`/remote local
$ check_for_abandoned_transaction() {
> [ -f $TESTTMP/remote/.hg/store/journal ] && echo "Abandoned transaction!"
> }
$ pidfile=`pwd`/pidfile
$ >$pidfile
$ script() {
> cat >"$1"
> chmod +x "$1"
> }
On the remote end, run hg, piping stdout and stderr through processes that we
know the PIDs of. We will later kill these to simulate an ssh client
disconnecting.
$ killable_pipe=`pwd`/killable_pipe.sh
$ script $killable_pipe <<EOF
> #!/usr/bin/env bash
> echo \$\$ >> $pidfile
> exec cat
> EOF
$ remotecmd=`pwd`/remotecmd.sh
$ script $remotecmd <<EOF
> #!/usr/bin/env bash
> hg "\$@" 1> >($killable_pipe) 2> >($killable_pipe >&2)
> EOF
In the pretxnchangegroup hook, kill the PIDs recorded above to simulate ssh
disconnecting. Then exit nonzero, to force a transaction rollback.
$ hook_script=`pwd`/pretxnchangegroup.sh
$ script $hook_script <<EOF
> #!/usr/bin/env bash
> for pid in \$(cat $pidfile) ; do
> kill \$pid
> while kill -0 \$pid 2>/dev/null ; do
> sleep 0.1
> done
> done
> exit 1
> EOF
$ cat >remote/.hg/hgrc <<EOF
> [hooks]
> pretxnchangegroup.break-things=$hook_script
> EOF
$ cd local
$ echo foo > foo ; hg commit -qAm "commit"
$ hg push -q -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --remotecmd $remotecmd 2>&1 | grep -v $killable_pipe
abort: stream ended unexpectedly (got 0 bytes, expected 4)
$ check_for_abandoned_transaction
[1]