Mercurial > hg
view hgext/share.py @ 46527:018d622e814d
test-copies: reinstall initial identical (empty) files for chained copied
This effectively back out changeset deeb215be337. Changeset deeb215be33 does not
really include a justification for its change and make mes uncomfortable. I have
been thinking about it and they are two options:
- either having empty/full files does not make a difference, and deeb215be337 is
a gratuitous changes.
- either having empty/full files do make a difference and deeb215be33 silently
change the test coverage. In such situation if we want the "not empty" case to
be tested, we should add new cases to cover them
In practice, we know that the "file content did not change, but merge still need
to create a new filenode" case exists (for example if merging result in similar
content but both parent of the file need to be recorded), and that such case are
easy to miss/mess-up in the tests. Having all the file using the same (empty)
content was done on purpose to increase the coverage of such corner case.
As a result I am reinstalling the previous test situation. To
increase the coverage of some case involving content-merge in
test-copies-chain-merge.t, we will add a new, dedicated, cases later in this
series, once various cleanup and test improvement have been set in place.
This changeset starts with reinstalling the previous situation as (1) it is more
fragile, so I am more confided getting it back in the initial situation, (2) I
have specific test further down the line that are base on these one.
The next changeset will slightly alter the test to use non-empty files for these
tests (with identical content). It should help to make the initial intent "merge file with identical
content" clearer. I am still using a two steps (backout, then change content)
approach to facilitate careful validation of the output change.
Doing so has a large impact on the output of the "copy info in changeset extra" variant
added in 5e72827dae1e (2 changesets after deeb215be33). It seems to highlight
various breakage when merge without content change are involved, this is a good
example of why we want to explicitly test theses cases. Because the different
-do- matters a lot.
Fixing the "copy info in changeset extra" is not a priority here. Because (1)
this changeset does not break anything, it only highlight that they were always
broken. (2) the only people using "copy info in changeset extra" do not have
merge.
Differential Revision: https://phab.mercurial-scm.org/D9587
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Thu, 10 Dec 2020 14:25:36 +0100 |
parents | c80f9e3daec3 |
children | d4ba4d51f85f |
line wrap: on
line source
# Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''share a common history between several working directories The share extension introduces a new command :hg:`share` to create a new working directory. This is similar to :hg:`clone`, but doesn't involve copying or linking the storage of the repository. This allows working on different branches or changes in parallel without the associated cost in terms of disk space. Note: destructive operations or extensions like :hg:`rollback` should be used with care as they can result in confusing problems. Automatic Pooled Storage for Clones ----------------------------------- When this extension is active, :hg:`clone` can be configured to automatically share/pool storage across multiple clones. This mode effectively converts :hg:`clone` to :hg:`clone` + :hg:`share`. The benefit of using this mode is the automatic management of store paths and intelligent pooling of related repositories. The following ``share.`` config options influence this feature: ``share.pool`` Filesystem path where shared repository data will be stored. When defined, :hg:`clone` will automatically use shared repository storage instead of creating a store inside each clone. ``share.poolnaming`` How directory names in ``share.pool`` are constructed. "identity" means the name is derived from the first changeset in the repository. In this mode, different remotes share storage if their root/initial changeset is identical. In this mode, the local shared repository is an aggregate of all encountered remote repositories. "remote" means the name is derived from the source repository's path or URL. In this mode, storage is only shared if the path or URL requested in the :hg:`clone` command matches exactly to a repository that was cloned before. The default naming mode is "identity". .. container:: verbose Sharing requirements and configs of source repository with shares: By default creating a shared repository only enables sharing a common history and does not share requirements and configs between them. This may lead to problems in some cases, for example when you upgrade the storage format from one repository but does not set related configs in the shares. Setting `format.exp-share-safe = True` enables sharing configs and requirements. This only applies to shares which are done after enabling the config option. For enabling this in existing shares, enable the config option and reshare. For resharing existing shares, make sure your working directory is clean and there are no untracked files, delete that share and create a new share. ''' from __future__ import absolute_import import errno from mercurial.i18n import _ from mercurial import ( bookmarks, commands, error, extensions, hg, registrar, txnutil, util, ) cmdtable = {} command = registrar.command(cmdtable) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should # be specifying the version(s) of Mercurial they are tested with, or # leave the attribute unspecified. testedwith = b'ships-with-hg-core' @command( b'share', [ (b'U', b'noupdate', None, _(b'do not create a working directory')), (b'B', b'bookmarks', None, _(b'also share bookmarks')), ( b'', b'relative', None, _(b'point to source using a relative path'), ), ], _(b'[-U] [-B] SOURCE [DEST]'), helpcategory=command.CATEGORY_REPO_CREATION, norepo=True, ) def share( ui, source, dest=None, noupdate=False, bookmarks=False, relative=False ): """create a new shared repository Initialize a new repository and working directory that shares its history (and optionally bookmarks) with another repository. .. note:: using rollback or extensions that destroy/modify history (mq, rebase, etc.) can cause considerable confusion with shared clones. In particular, if two shared clones are both updated to the same changeset, and one of them destroys that changeset with rollback, the other clone will suddenly stop working: all operations will fail with "abort: working directory has unknown parent". The only known workaround is to use debugsetparents on the broken clone to reset it to a changeset that still exists. """ hg.share( ui, source, dest=dest, update=not noupdate, bookmarks=bookmarks, relative=relative, ) return 0 @command(b'unshare', [], b'', helpcategory=command.CATEGORY_MAINTENANCE) def unshare(ui, repo): """convert a shared repository to a normal one Copy the store data to the repo and remove the sharedpath data. """ if not repo.shared(): raise error.Abort(_(b"this is not a shared repo")) hg.unshare(ui, repo) # Wrap clone command to pass auto share options. def clone(orig, ui, source, *args, **opts): pool = ui.config(b'share', b'pool') if pool: pool = util.expandpath(pool) opts['shareopts'] = { b'pool': pool, b'mode': ui.config(b'share', b'poolnaming'), } return orig(ui, source, *args, **opts) def extsetup(ui): extensions.wrapfunction(bookmarks, b'_getbkfile', getbkfile) extensions.wrapfunction(bookmarks.bmstore, b'_recordchange', recordchange) extensions.wrapfunction(bookmarks.bmstore, b'_writerepo', writerepo) extensions.wrapcommand(commands.table, b'clone', clone) def _hassharedbookmarks(repo): """Returns whether this repo has shared bookmarks""" if bookmarks.bookmarksinstore(repo): # Kind of a lie, but it means that we skip our custom reads and writes # from/to the source repo. return False try: shared = repo.vfs.read(b'shared').splitlines() except IOError as inst: if inst.errno != errno.ENOENT: raise return False return hg.sharedbookmarks in shared def getbkfile(orig, repo): if _hassharedbookmarks(repo): srcrepo = hg.sharedreposource(repo) if srcrepo is not None: # just orig(srcrepo) doesn't work as expected, because # HG_PENDING refers repo.root. try: fp, pending = txnutil.trypending( repo.root, repo.vfs, b'bookmarks' ) if pending: # only in this case, bookmark information in repo # is up-to-date. return fp fp.close() except IOError as inst: if inst.errno != errno.ENOENT: raise # otherwise, we should read bookmarks from srcrepo, # because .hg/bookmarks in srcrepo might be already # changed via another sharing repo repo = srcrepo # TODO: Pending changes in repo are still invisible in # srcrepo, because bookmarks.pending is written only into repo. # See also https://www.mercurial-scm.org/wiki/SharedRepository return orig(repo) def recordchange(orig, self, tr): # Continue with write to local bookmarks file as usual orig(self, tr) if _hassharedbookmarks(self._repo): srcrepo = hg.sharedreposource(self._repo) if srcrepo is not None: category = b'share-bookmarks' tr.addpostclose(category, lambda tr: self._writerepo(srcrepo)) def writerepo(orig, self, repo): # First write local bookmarks file in case we ever unshare orig(self, repo) if _hassharedbookmarks(self._repo): srcrepo = hg.sharedreposource(self._repo) if srcrepo is not None: orig(self, srcrepo)