This makes `hg pull --update` behave the same wrt the active bookmark as
`hg pull && hg update` does as of 13ea5e437ff8. A helper function,
bookmarks.calculateupdate, is added to prevent code duplication between
postincoming and update.
This patch resolves divergent bookmarks between the current active bookmark
MARK and the new destination. This situation can arise when pulling new
changesets, abandoning your current changesets actively bookmarked with MARK
via strip, and then doing a bare update. The non-divergent but active bookmark
MARK is then moved to a common ancestor of the new changesets and the abandoned
changesets.
Test coverage is added.
Consider a bookmark B that exists both locally and remotely. If B is updated
remotely, and then a pull is performed where the pull set contains the new
location of B, the bookmark is updated locally. However, if remote B is
updated in the middle of a pull to a location not in the pull set, the
bookmark won't be updated locally at all.
To fix this, list bookmarks before pulling in changesets, not after. This
still leaves a race open if B gets moved in between listing bookmarks and
pulling in changesets, but the race window is much smaller. Fixing the race
properly would require a bundle format upgrade.
test-hook.t's output changes because we no longer do two listkeys calls during
pull, just one.
test-pull-http.t's output changes because we now search for bookmarks before
searching for changes.
Don't expose unserved changesets to remote repos. Thanks to Sean Farley
<sean.michael.farley@gmail.com> for tracking down the issue and
Pierre-Yves David <pierre-yves.david@ens-lyon.org> for the fix.
If the current bookmark (the one listed in .hg/bookmarks.current)
doesn't point to a parent of the working directory, e.g. if it was moved
by a pull, use that as the update target instead of the tipmost
descendent.
A small predicate is (finally) added to the bookmarks module to check
whether the current bookmark is also active.
The logic recently added to `bookmark.validdest` uses data about obsolete
changesets to see if a bookmark destination is valid. Obsolete changesets
are likely to be filtered, so we need to work on an unfiltered repository.
Bookmarks persistence still showed a fair amount of its legacy as a
monkeypatching extension. This encapsulates all bookmarks
serialization and parsing in a single class, and offers a single
location where other bookmarks storage engines can be substituted
in. As a result, many files no longer import the bookmarks module,
which strikes me as an encapsulation win.
This doesn't do anything to the current bookmark state yet, but I'm
hoping put that in the bmstore class as well.
Update to this code was minimalist when `allsuccessors` argument were changed
from a list to a set. As this code is getting my attention again I realised we
can drastically simplify this part of the code by issue a single call to
`allsuccessors`.
The `%ln` revset substitution does not accept unknown node. We prune unknown
node from potential successors before computing descendants.
This have no impact on the result of this function.
- Descendants of unknown changeset as unknown,
- all successors of unknown changesets are already return by the call who
returned those same unknown changesets,
- unknown changesets are never a valid destination for a bookmark.
This consolidates the abort message for a bookmark name containing
invalid characters into one place, and renames the valid() method to
checkvalid() to reflect the fact that it's no longer a predicate.
This changeset treats the bookmark "@" as a special case for the naming of
divergent bookmarks, as per the tables below. For the <no alias> case, the
actual suffix will vary, depending on what suffixes are already in use.
Before:
Bookmark | Remote | Divergent Bookmark
--------------------------------------
foo | bar | foo@bar
foo | <no alias> | foo@1
@ | bar | @@bar
@ | <no alias> | @@1
After:
Bookmark | Remote | Divergent Bookmark
--------------------------------------
foo | bar | foo@bar
foo | <no alias> | foo@1
@ | bar | @bar
@ | <no alias> | @1
This case is likely to be more common now that c1644655c164 has made the "@"
bookmark have special meaning to clone.
The change in behavior was discussed on the mailing list in the thread below:
http://markmail.org/thread/rwedgxp7le5j2h2f
Before this patch, the argument bound to the source repository of
incoming bookmarks for "bookmarks.diff()" is named as "remote".
But in "hg outgoing" case, this argument is bound to local repository
object.
In addition to it, "local"/"remote" seem to mean not the direction of
propagation of bookmarks, but just the location of cooperative
repositories.
To indicate the direction of propagation of bookmarks clearly on the
source code, this patch uses "d(st)" and "s(rc)" combination instead
of "l(ocal)" and "r(emote)" one.
- "repo" and "remote" arguments are renamed to "dst" and "src"
- "lmarks" and "rmarks" variables are renamed to "dmarsk" and "smarks"
When we rewrite a bookmarked changeset, we want to update the
bookmark on its successors. But the successors are not descendants
of its precursor (by definition). This changeset alters the bookmarks
logic to update bookmark location if the newer location is a successor
of the old one[1].
note: valid destinations are in fact any kind of successors of any kind
of descendants (recursively.)
This changeset requires the enabling of the obsolete feature in
some bookmark tests.
We usually update bookmarks only if the new location is descendant of the old
bookmarks location. We extract this logic into a function. This is the first
step to allow more complex logic using obsolescence in this validation of the
bookmark movement.
Bookmarks are repository data, not working directory data. Only the current
bookmark is working directory data.
Some lock shuffling is required to avoid lockout between the initial mock lock
and locking of the localrepo instance that is created after copying.
This makes bookmarks.update() and bookmarks.updatecurrentbookmark() return
True or False to indicate whether the bookmark was updated or not. This allows
callers to e.g. abort if the update failed.
The usual contract is that close() makes your writes permanent, so
atomictempfile's use of close() to *discard* writes (and rename() to
keep them) is rather unexpected. Thus, change it so close() makes
things permanent and add a new discard() method to throw them away.
discard() is only used internally, in __del__(), to ensure that writes
are discarded when an atomictempfile object goes out of scope.
I audited mercurial.*, hgext.*, and ~80 third-party extensions, and
found no one using the existing semantics of close() to discard
writes, so this should be safe.