##// END OF EJS Templates
localrepo: iteratively derive local repository type...
localrepo: iteratively derive local repository type This commit implements the dynamic local repository type derivation that was explained in the recent commit bfeab472e3c0 "localrepo: create new function for instantiating a local repo object." Instead of a static localrepository class/type which must be customized after construction, we now dynamically construct a type by building up base classes/types to represent specific repository interfaces. Conceptually, the end state is similar to what was happening when various extensions would monkeypatch the __class__ of newly-constructed repo instances. However, the approach is inverted. Instead of making the instance then customizing it, we do the customization up front by influencing the behavior of the type then we instantiate that custom type. This approach gives us much more flexibility. For example, we can use completely separate classes for implementing different aspects of the repository. For example, we could have one class representing revlog-based file storage and another representing non-revlog based file storage. When then choose which implementation to use based on the presence of repo requirements. A concern with this approach is that it creates a lot more types and complexity and that complexity adds overhead. Yes, it is true that this approach will result in more types being created. Yes, this is more complicated than traditional "instantiate a static type." However, I believe the alternatives to supporting alternate storage backends are just as complicated. (Before I arrived at this solution, I had patches storing factory functions on local repo instances for e.g. constructing a file storage instance. We ended up having a handful of these. And this was logically identical to assigning custom methods. Since we were logically changing the type of the instance, I figured it would be better to just use specialized types instead of introducing levels of abstraction at run-time.) On the performance front, I don't believe that having N base classes has any significant performance overhead compared to just a single base class. Intuition says that Python will need to iterate the base classes to find an attribute. However, CPython caches method lookups: as long as the __class__ or MRO isn't changing, method attribute lookup should be constant time after first access. And non-method attributes are stored in __dict__, of which there is only 1 per object, so the number of base classes for __dict__ is irrelevant. Anyway, this commit splits up the monolithic completelocalrepository interface into sub-interfaces: 1 for file storage and 1 representing everything else. We've taught ``makelocalrepository()`` to call a series of factory functions which will produce types implementing specific interfaces. It then calls type() to create a new type from the built-up list of base types. This commit should be considered a start and not the end state. I suspect we'll hit a number of problems as we start to implement alternate storage backends: * Passing custom arguments to __init__ and setting custom attributes on __dict__. * Customizing the set of interfaces that are needed. e.g. the "readonly" intent could translate to not requesting an interface providing methods related to writing. * More ergonomic way for extensions to insert themselves so their callbacks aren't unconditionally called. * Wanting to modify vfs instances, other arguments passed to __init__. That being said, this code is usable in its current state and I'm convinced future commits will demonstrate the value in this approach. Differential Revision: https://phab.mercurial-scm.org/D4642

File last commit:

r35166:27196b7f stable
r39800:e4e88157 default
Show More
subrepos.txt
169 lines | 7.1 KiB | text/plain | TextLexer
Subrepositories let you nest external repositories or projects into a
parent Mercurial repository, and make commands operate on them as a
group.
Mercurial currently supports Mercurial, Git, and Subversion
subrepositories.
Subrepositories are made of three components:
1. Nested repository checkouts. They can appear anywhere in the
parent working directory.
2. Nested repository references. They are defined in ``.hgsub``, which
should be placed in the root of working directory, and
tell where the subrepository checkouts come from. Mercurial
subrepositories are referenced like::
path/to/nested = https://example.com/nested/repo/path
Git and Subversion subrepos are also supported::
path/to/nested = [git]git://example.com/nested/repo/path
path/to/nested = [svn]https://example.com/nested/trunk/path
where ``path/to/nested`` is the checkout location relatively to the
parent Mercurial root, and ``https://example.com/nested/repo/path``
is the source repository path. The source can also reference a
filesystem path.
Note that ``.hgsub`` does not exist by default in Mercurial
repositories, you have to create and add it to the parent
repository before using subrepositories.
3. Nested repository states. They are defined in ``.hgsubstate``, which
is placed in the root of working directory, and
capture whatever information is required to restore the
subrepositories to the state they were committed in a parent
repository changeset. Mercurial automatically record the nested
repositories states when committing in the parent repository.
.. note::
The ``.hgsubstate`` file should not be edited manually.
Adding a Subrepository
======================
If ``.hgsub`` does not exist, create it and add it to the parent
repository. Clone or checkout the external projects where you want it
to live in the parent repository. Edit ``.hgsub`` and add the
subrepository entry as described above. At this point, the
subrepository is tracked and the next commit will record its state in
``.hgsubstate`` and bind it to the committed changeset.
Synchronizing a Subrepository
=============================
Subrepos do not automatically track the latest changeset of their
sources. Instead, they are updated to the changeset that corresponds
with the changeset checked out in the top-level changeset. This is so
developers always get a consistent set of compatible code and
libraries when they update.
Thus, updating subrepos is a manual process. Simply check out target
subrepo at the desired revision, test in the top-level repo, then
commit in the parent repository to record the new combination.
Deleting a Subrepository
========================
To remove a subrepository from the parent repository, delete its
reference from ``.hgsub``, then remove its files.
Interaction with Mercurial Commands
===================================
:add: add does not recurse in subrepos unless -S/--subrepos is
specified. However, if you specify the full path of a file in a
subrepo, it will be added even without -S/--subrepos specified.
Subversion subrepositories are currently silently
ignored.
:addremove: addremove does not recurse into subrepos unless
-S/--subrepos is specified. However, if you specify the full
path of a directory in a subrepo, addremove will be performed on
it even without -S/--subrepos being specified. Git and
Subversion subrepositories will print a warning and continue.
:archive: archive does not recurse in subrepositories unless
-S/--subrepos is specified.
:cat: Git subrepositories only support exact file matches.
Subversion subrepositories are currently ignored.
:commit: commit creates a consistent snapshot of the state of the
entire project and its subrepositories. If any subrepositories
have been modified, Mercurial will abort. Mercurial can be made
to instead commit all modified subrepositories by specifying
-S/--subrepos, or setting "ui.commitsubrepos=True" in a
configuration file (see :hg:`help config`). After there are no
longer any modified subrepositories, it records their state and
finally commits it in the parent repository. The --addremove
option also honors the -S/--subrepos option. However, Git and
Subversion subrepositories will print a warning and abort.
:diff: diff does not recurse in subrepos unless -S/--subrepos is
specified. Changes are displayed as usual, on the subrepositories
elements. Subversion subrepositories are currently silently ignored.
:files: files does not recurse into subrepos unless -S/--subrepos is
specified. However, if you specify the full path of a file or
directory in a subrepo, it will be displayed even without
-S/--subrepos being specified. Git and Subversion subrepositories
are currently silently ignored.
:forget: forget currently only handles exact file matches in subrepos.
Git and Subversion subrepositories are currently silently ignored.
:incoming: incoming does not recurse in subrepos unless -S/--subrepos
is specified. Git and Subversion subrepositories are currently
silently ignored.
:outgoing: outgoing does not recurse in subrepos unless -S/--subrepos
is specified. Git and Subversion subrepositories are currently
silently ignored.
:pull: pull is not recursive since it is not clear what to pull prior
to running :hg:`update`. Listing and retrieving all
subrepositories changes referenced by the parent repository pulled
changesets is expensive at best, impossible in the Subversion
case.
:push: Mercurial will automatically push all subrepositories first
when the parent repository is being pushed. This ensures new
subrepository changes are available when referenced by top-level
repositories. Push is a no-op for Subversion subrepositories.
:serve: serve does not recurse into subrepositories unless
-S/--subrepos is specified. Git and Subversion subrepositories
are currently silently ignored.
:status: status does not recurse into subrepositories unless
-S/--subrepos is specified. Subrepository changes are displayed as
regular Mercurial changes on the subrepository
elements. Subversion subrepositories are currently silently
ignored.
:remove: remove does not recurse into subrepositories unless
-S/--subrepos is specified. However, if you specify a file or
directory path in a subrepo, it will be removed even without
-S/--subrepos. Git and Subversion subrepositories are currently
silently ignored.
:update: update restores the subrepos in the state they were
originally committed in target changeset. If the recorded
changeset is not available in the current subrepository, Mercurial
will pull it in first before updating. This means that updating
can require network access when using subrepositories.
Remapping Subrepositories Sources
=================================
A subrepository source location may change during a project life,
invalidating references stored in the parent repository history. To
fix this, rewriting rules can be defined in parent repository ``hgrc``
file or in Mercurial configuration. See the ``[subpaths]`` section in
hgrc(5) for more details.