/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2019-06-03 23:48:08 UTC
  • mfrom: (7316 work)
  • mto: This revision was merged to the branch mainline in revision 7328.
  • Revision ID: jelmer@jelmer.uk-20190603234808-15yk5c7054tj8e2b
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
lazy_import(globals(), """
21
21
import itertools
22
22
from breezy import (
23
 
    cleanup,
24
23
    config as _mod_config,
25
24
    debug,
26
25
    memorytree,
51
50
    text_type,
52
51
    viewitems,
53
52
    )
54
 
from .trace import mutter, mutter_callsite, note, is_quiet, warning
 
53
from .trace import mutter, mutter_callsite, note, is_quiet
55
54
 
56
55
 
57
56
class UnstackableBranchFormat(errors.BzrError):
676
675
            raise errors.UpgradeRequired(self.user_url)
677
676
        self.get_config_stack().set('append_revisions_only', enabled)
678
677
 
679
 
    def fetch(self, from_branch, stop_revision=None, limit=None, lossy=False):
 
678
    def set_reference_info(self, tree_path, branch_location, file_id=None):
 
679
        """Set the branch location to use for a tree reference."""
 
680
        raise errors.UnsupportedOperation(self.set_reference_info, self)
 
681
 
 
682
    def get_reference_info(self, path):
 
683
        """Get the tree_path and branch_location for a tree reference."""
 
684
        raise errors.UnsupportedOperation(self.get_reference_info, self)
 
685
 
 
686
    def fetch(self, from_branch, last_revision=None, limit=None):
680
687
        """Copy revisions from from_branch into this branch.
681
688
 
682
689
        :param from_branch: Where to copy from.
683
 
        :param stop_revision: What revision to stop at (None for at the end
 
690
        :param last_revision: What revision to stop at (None for at the end
684
691
                              of the branch.
685
692
        :param limit: Optional rough limit of revisions to fetch
686
693
        :return: None
687
694
        """
688
695
        with self.lock_write():
689
696
            return InterBranch.get(from_branch, self).fetch(
690
 
                stop_revision, limit=limit, lossy=lossy)
 
697
                last_revision, limit=limit)
691
698
 
692
699
    def get_bound_location(self):
693
700
        """Return the URL of the branch we are bound to.
1194
1201
        if revno < 1 or revno > self.revno():
1195
1202
            raise errors.InvalidRevisionNumber(revno)
1196
1203
 
1197
 
    def clone(self, to_controldir, revision_id=None, name=None,
1198
 
              repository_policy=None, tag_selector=None):
 
1204
    def clone(self, to_controldir, revision_id=None, repository_policy=None):
1199
1205
        """Clone this branch into to_controldir preserving all semantic values.
1200
1206
 
1201
1207
        Most API users will want 'create_clone_on_transport', which creates a
1204
1210
        revision_id: if not None, the revision history in the new branch will
1205
1211
                     be truncated to end with revision_id.
1206
1212
        """
1207
 
        result = to_controldir.create_branch(name=name)
 
1213
        result = to_controldir.create_branch()
1208
1214
        with self.lock_read(), result.lock_write():
1209
1215
            if repository_policy is not None:
1210
1216
                repository_policy.configure_branch(result)
1211
 
            self.copy_content_into(
1212
 
                result, revision_id=revision_id, tag_selector=tag_selector)
 
1217
            self.copy_content_into(result, revision_id=revision_id)
1213
1218
        return result
1214
1219
 
1215
1220
    def sprout(self, to_controldir, revision_id=None, repository_policy=None,
1216
 
               repository=None, lossy=False, tag_selector=None):
 
1221
               repository=None, lossy=False):
1217
1222
        """Create a new line of development from the branch, into to_controldir.
1218
1223
 
1219
1224
        to_controldir controls the branch format.
1230
1235
        with self.lock_read(), result.lock_write():
1231
1236
            if repository_policy is not None:
1232
1237
                repository_policy.configure_branch(result)
1233
 
            self.copy_content_into(
1234
 
                result, revision_id=revision_id, tag_selector=tag_selector)
 
1238
            self.copy_content_into(result, revision_id=revision_id)
1235
1239
            master_url = self.get_bound_location()
1236
1240
            if master_url is None:
1237
1241
                result.set_parent(self.user_url)
1264
1268
                revno = 1
1265
1269
        destination.set_last_revision_info(revno, revision_id)
1266
1270
 
1267
 
    def copy_content_into(self, destination, revision_id=None, tag_selector=None):
 
1271
    def copy_content_into(self, destination, revision_id=None):
1268
1272
        """Copy the content of self into destination.
1269
1273
 
1270
1274
        revision_id: if not None, the revision history in the new branch will
1271
1275
                     be truncated to end with revision_id.
1272
 
        tag_selector: Optional callback that receives a tag name
1273
 
            and should return a boolean to indicate whether a tag should be copied
1274
1276
        """
1275
1277
        return InterBranch.get(self, destination).copy_content_into(
1276
 
            revision_id=revision_id, tag_selector=tag_selector)
 
1278
            revision_id=revision_id)
1277
1279
 
1278
1280
    def update_references(self, target):
1279
 
        if not self._format.supports_reference_locations:
1280
 
            return
1281
 
        return InterBranch.get(self, target).update_references()
 
1281
        if not getattr(self._format, 'supports_reference_locations', False):
 
1282
            return
 
1283
        reference_dict = self._get_all_reference_info()
 
1284
        if len(reference_dict) == 0:
 
1285
            return
 
1286
        old_base = self.base
 
1287
        new_base = target.base
 
1288
        target_reference_dict = target._get_all_reference_info()
 
1289
        for tree_path, (branch_location, file_id) in viewitems(reference_dict):
 
1290
            branch_location = urlutils.rebase_url(branch_location,
 
1291
                                                  old_base, new_base)
 
1292
            target_reference_dict.setdefault(
 
1293
                tree_path, (branch_location, file_id))
 
1294
        target._set_all_reference_info(target_reference_dict)
1282
1295
 
1283
1296
    def check(self, refs):
1284
1297
        """Check consistency of the branch.
1318
1331
 
1319
1332
    def create_clone_on_transport(self, to_transport, revision_id=None,
1320
1333
                                  stacked_on=None, create_prefix=False,
1321
 
                                  use_existing_dir=False, no_tree=None,
1322
 
                                  tag_selector=None):
 
1334
                                  use_existing_dir=False, no_tree=None):
1323
1335
        """Create a clone of this branch and its bzrdir.
1324
1336
 
1325
1337
        :param to_transport: The transport to clone onto.
1339
1351
        dir_to = self.controldir.clone_on_transport(
1340
1352
            to_transport, revision_id=revision_id, stacked_on=stacked_on,
1341
1353
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1342
 
            no_tree=no_tree, tag_selector=tag_selector)
 
1354
            no_tree=no_tree)
1343
1355
        return dir_to.open_branch()
1344
1356
 
1345
1357
    def create_checkout(self, to_location, revision_id=None,
1346
1358
                        lightweight=False, accelerator_tree=None,
1347
 
                        hardlink=False, recurse_nested=True):
 
1359
                        hardlink=False):
1348
1360
        """Create a checkout of a branch.
1349
1361
 
1350
1362
        :param to_location: The url to produce the checkout at
1357
1369
            content is different.
1358
1370
        :param hardlink: If true, hard-link files from accelerator_tree,
1359
1371
            where possible.
1360
 
        :param recurse_nested: Whether to recurse into nested trees
1361
1372
        :return: The tree of the created checkout
1362
1373
        """
1363
1374
        t = transport.get_transport(to_location)
1398
1409
                                           hardlink=hardlink)
1399
1410
        basis_tree = tree.basis_tree()
1400
1411
        with basis_tree.lock_read():
1401
 
            for path in basis_tree.iter_references():
1402
 
                reference_parent = tree.reference_parent(path)
1403
 
                if reference_parent is None:
1404
 
                    warning('Branch location for %s unknown.', path)
1405
 
                    continue
 
1412
            for path, file_id in basis_tree.iter_references():
 
1413
                reference_parent = self.reference_parent(path, file_id)
1406
1414
                reference_parent.create_checkout(
1407
1415
                    tree.abspath(path),
1408
1416
                    basis_tree.get_reference_revision(path), lightweight)
1415
1423
        """
1416
1424
        raise NotImplementedError(self.reconcile)
1417
1425
 
 
1426
    def reference_parent(self, path, file_id=None, possible_transports=None):
 
1427
        """Return the parent branch for a tree-reference file_id
 
1428
 
 
1429
        :param path: The path of the file_id in the tree
 
1430
        :param file_id: Optional file_id of the tree reference
 
1431
        :return: A branch associated with the file_id
 
1432
        """
 
1433
        # FIXME should provide multiple branches, based on config
 
1434
        return Branch.open(self.controldir.root_transport.clone(path).base,
 
1435
                           possible_transports=possible_transports)
 
1436
 
1418
1437
    def supports_tags(self):
1419
1438
        return self._format.supports_tags()
1420
1439
 
1643
1662
        """True if uncommitted changes can be stored in this branch."""
1644
1663
        return True
1645
1664
 
1646
 
    def stores_revno(self):
1647
 
        """True if this branch format store revision numbers."""
1648
 
        return True
1649
 
 
1650
1665
 
1651
1666
class BranchHooks(Hooks):
1652
1667
    """A dictionary mapping hook name to a list of callables for branch hooks.
2005
2020
        tag_updates = getattr(self, "tag_updates", None)
2006
2021
        if not is_quiet():
2007
2022
            if self.old_revid != self.new_revid:
2008
 
                if self.new_revno is not None:
2009
 
                    note(gettext('Pushed up to revision %d.'),
2010
 
                         self.new_revno)
2011
 
                else:
2012
 
                    note(gettext('Pushed up to revision id %s.'),
2013
 
                         self.new_revid.decode('utf-8'))
 
2023
                note(gettext('Pushed up to revision %d.') % self.new_revno)
2014
2024
            if tag_updates:
2015
2025
                note(ngettext('%d tag updated.', '%d tags updated.',
2016
2026
                              len(tag_updates)) % len(tag_updates))
2066
2076
        raise NotImplementedError(klass._get_branch_formats_to_test)
2067
2077
 
2068
2078
    def pull(self, overwrite=False, stop_revision=None,
2069
 
             possible_transports=None, local=False, tag_selector=None):
 
2079
             possible_transports=None, local=False):
2070
2080
        """Mirror source into target branch.
2071
2081
 
2072
2082
        The target branch is considered to be 'local', having low latency.
2076
2086
        raise NotImplementedError(self.pull)
2077
2087
 
2078
2088
    def push(self, overwrite=False, stop_revision=None, lossy=False,
2079
 
             _override_hook_source_branch=None, tag_selector=None):
 
2089
             _override_hook_source_branch=None):
2080
2090
        """Mirror the source branch into the target branch.
2081
2091
 
2082
2092
        The source branch is considered to be 'local', having low latency.
2083
2093
        """
2084
2094
        raise NotImplementedError(self.push)
2085
2095
 
2086
 
    def copy_content_into(self, revision_id=None, tag_selector=None):
 
2096
    def copy_content_into(self, revision_id=None):
2087
2097
        """Copy the content of source into target
2088
2098
 
2089
 
        :param revision_id:
2090
 
            if not None, the revision history in the new branch will
2091
 
            be truncated to end with revision_id.
2092
 
        :param tag_selector: Optional callback that can decide
2093
 
            to copy or not copy tags.
 
2099
        revision_id: if not None, the revision history in the new branch will
 
2100
                     be truncated to end with revision_id.
2094
2101
        """
2095
2102
        raise NotImplementedError(self.copy_content_into)
2096
2103
 
2097
 
    def fetch(self, stop_revision=None, limit=None, lossy=False):
 
2104
    def fetch(self, stop_revision=None, limit=None):
2098
2105
        """Fetch revisions.
2099
2106
 
2100
2107
        :param stop_revision: Last revision to fetch
2101
2108
        :param limit: Optional rough limit of revisions to fetch
2102
 
        :return: FetchResult object
2103
2109
        """
2104
2110
        raise NotImplementedError(self.fetch)
2105
2111
 
2106
 
    def update_references(self):
2107
 
        """Import reference information from source to target.
2108
 
        """
2109
 
        raise NotImplementedError(self.update_references)
2110
 
 
2111
2112
 
2112
2113
def _fix_overwrite_type(overwrite):
2113
2114
    if isinstance(overwrite, bool):
2137
2138
            return format._custom_format
2138
2139
        return format
2139
2140
 
2140
 
    def copy_content_into(self, revision_id=None, tag_selector=None):
 
2141
    def copy_content_into(self, revision_id=None):
2141
2142
        """Copy the content of source into target
2142
2143
 
2143
2144
        revision_id: if not None, the revision history in the new branch will
2144
2145
                     be truncated to end with revision_id.
2145
2146
        """
2146
2147
        with self.source.lock_read(), self.target.lock_write():
 
2148
            self.source.update_references(self.target)
2147
2149
            self.source._synchronize_history(self.target, revision_id)
2148
 
            self.update_references()
2149
2150
            try:
2150
2151
                parent = self.source.get_parent()
2151
2152
            except errors.InaccessibleParent as e:
2154
2155
                if parent:
2155
2156
                    self.target.set_parent(parent)
2156
2157
            if self.source._push_should_merge_tags():
2157
 
                self.source.tags.merge_to(self.target.tags, selector=tag_selector)
 
2158
                self.source.tags.merge_to(self.target.tags)
2158
2159
 
2159
 
    def fetch(self, stop_revision=None, limit=None, lossy=False):
 
2160
    def fetch(self, stop_revision=None, limit=None):
2160
2161
        if self.target.base == self.source.base:
2161
2162
            return (0, [])
2162
2163
        with self.source.lock_read(), self.target.lock_write():
2171
2172
            fetch_spec = fetch_spec_factory.make_fetch_spec()
2172
2173
            return self.target.repository.fetch(
2173
2174
                self.source.repository,
2174
 
                lossy=lossy,
2175
2175
                fetch_spec=fetch_spec)
2176
2176
 
2177
2177
    def _update_revisions(self, stop_revision=None, overwrite=False,
2215
2215
 
2216
2216
    def pull(self, overwrite=False, stop_revision=None,
2217
2217
             possible_transports=None, run_hooks=True,
2218
 
             _override_hook_target=None, local=False,
2219
 
             tag_selector=None):
 
2218
             _override_hook_target=None, local=False):
2220
2219
        """Pull from source into self, updating my master if any.
2221
2220
 
2222
2221
        :param run_hooks: Private parameter - if false, this branch
2223
2222
            is being called because it's the master of the primary branch,
2224
2223
            so it should not run its hooks.
2225
2224
        """
2226
 
        with cleanup.ExitStack() as exit_stack:
2227
 
            exit_stack.enter_context(self.target.lock_write())
 
2225
        with self.target.lock_write():
2228
2226
            bound_location = self.target.get_bound_location()
2229
2227
            if local and not bound_location:
2230
2228
                raise errors.LocalRequiresBoundBranch()
2243
2241
                # not pulling from master, so we need to update master.
2244
2242
                master_branch = self.target.get_master_branch(
2245
2243
                    possible_transports)
2246
 
                exit_stack.enter_context(master_branch.lock_write())
2247
 
            if master_branch:
2248
 
                # pull from source into master.
2249
 
                master_branch.pull(
2250
 
                    self.source, overwrite, stop_revision, run_hooks=False,
2251
 
                    tag_selector=tag_selector)
2252
 
            return self._pull(
2253
 
                overwrite, stop_revision, _hook_master=master_branch,
2254
 
                run_hooks=run_hooks,
2255
 
                _override_hook_target=_override_hook_target,
2256
 
                merge_tags_to_master=not source_is_master,
2257
 
                tag_selector=tag_selector)
 
2244
                master_branch.lock_write()
 
2245
            try:
 
2246
                if master_branch:
 
2247
                    # pull from source into master.
 
2248
                    master_branch.pull(
 
2249
                        self.source, overwrite, stop_revision, run_hooks=False)
 
2250
                return self._pull(
 
2251
                    overwrite, stop_revision, _hook_master=master_branch,
 
2252
                    run_hooks=run_hooks,
 
2253
                    _override_hook_target=_override_hook_target,
 
2254
                    merge_tags_to_master=not source_is_master)
 
2255
            finally:
 
2256
                if master_branch:
 
2257
                    master_branch.unlock()
2258
2258
 
2259
2259
    def push(self, overwrite=False, stop_revision=None, lossy=False,
2260
 
             _override_hook_source_branch=None, tag_selector=None):
 
2260
             _override_hook_source_branch=None):
2261
2261
        """See InterBranch.push.
2262
2262
 
2263
2263
        This is the basic concrete implementation of push()
2289
2289
                with master_branch.lock_write():
2290
2290
                    # push into the master from the source branch.
2291
2291
                    master_inter = InterBranch.get(self.source, master_branch)
2292
 
                    master_inter._basic_push(
2293
 
                        overwrite, stop_revision, tag_selector=tag_selector)
 
2292
                    master_inter._basic_push(overwrite, stop_revision)
2294
2293
                    # and push into the target branch from the source. Note
2295
2294
                    # that we push from the source branch again, because it's
2296
2295
                    # considered the highest bandwidth repository.
2297
 
                    result = self._basic_push(
2298
 
                        overwrite, stop_revision, tag_selector=tag_selector)
 
2296
                    result = self._basic_push(overwrite, stop_revision)
2299
2297
                    result.master_branch = master_branch
2300
2298
                    result.local_branch = self.target
2301
2299
                    _run_hooks()
2302
2300
            else:
2303
2301
                master_branch = None
2304
2302
                # no master branch
2305
 
                result = self._basic_push(
2306
 
                    overwrite, stop_revision, tag_selector=tag_selector)
 
2303
                result = self._basic_push(overwrite, stop_revision)
2307
2304
                # TODO: Why set master_branch and local_branch if there's no
2308
2305
                # binding?  Maybe cleaner to just leave them unset? -- mbp
2309
2306
                # 20070504
2312
2309
                _run_hooks()
2313
2310
            return result
2314
2311
 
2315
 
    def _basic_push(self, overwrite, stop_revision, tag_selector=None):
 
2312
    def _basic_push(self, overwrite, stop_revision):
2316
2313
        """Basic implementation of push without bound branches or hooks.
2317
2314
 
2318
2315
        Must be called with source read locked and target write locked.
2321
2318
        result.source_branch = self.source
2322
2319
        result.target_branch = self.target
2323
2320
        result.old_revno, result.old_revid = self.target.last_revision_info()
 
2321
        self.source.update_references(self.target)
2324
2322
        overwrite = _fix_overwrite_type(overwrite)
2325
2323
        if result.old_revid != stop_revision:
2326
2324
            # We assume that during 'push' this repository is closer than
2331
2329
        if self.source._push_should_merge_tags():
2332
2330
            result.tag_updates, result.tag_conflicts = (
2333
2331
                self.source.tags.merge_to(
2334
 
                    self.target.tags, "tags" in overwrite, selector=tag_selector))
2335
 
        self.update_references()
 
2332
                    self.target.tags, "tags" in overwrite))
2336
2333
        result.new_revno, result.new_revid = self.target.last_revision_info()
2337
2334
        return result
2338
2335
 
2339
2336
    def _pull(self, overwrite=False, stop_revision=None,
2340
2337
              possible_transports=None, _hook_master=None, run_hooks=True,
2341
2338
              _override_hook_target=None, local=False,
2342
 
              merge_tags_to_master=True, tag_selector=None):
 
2339
              merge_tags_to_master=True):
2343
2340
        """See Branch.pull.
2344
2341
 
2345
2342
        This function is the core worker, used by GenericInterBranch.pull to
2368
2365
        with self.source.lock_read():
2369
2366
            # We assume that during 'pull' the target repository is closer than
2370
2367
            # the source one.
 
2368
            self.source.update_references(self.target)
2371
2369
            graph = self.target.repository.get_graph(self.source.repository)
2372
2370
            # TODO: Branch formats should have a flag that indicates
2373
2371
            # that revno's are expensive, and pull() should honor that flag.
2383
2381
            result.tag_updates, result.tag_conflicts = (
2384
2382
                self.source.tags.merge_to(
2385
2383
                    self.target.tags, "tags" in overwrite,
2386
 
                    ignore_master=not merge_tags_to_master,
2387
 
                    selector=tag_selector))
2388
 
            self.update_references()
 
2384
                    ignore_master=not merge_tags_to_master))
2389
2385
            result.new_revno, result.new_revid = (
2390
2386
                self.target.last_revision_info())
2391
2387
            if _hook_master:
2399
2395
                    hook(result)
2400
2396
            return result
2401
2397
 
2402
 
    def update_references(self):
2403
 
        if not getattr(self.source._format, 'supports_reference_locations', False):
2404
 
            return
2405
 
        reference_dict = self.source._get_all_reference_info()
2406
 
        if len(reference_dict) == 0:
2407
 
            return
2408
 
        old_base = self.source.base
2409
 
        new_base = self.target.base
2410
 
        target_reference_dict = self.target._get_all_reference_info()
2411
 
        for tree_path, (branch_location, file_id) in viewitems(reference_dict):
2412
 
            try:
2413
 
                branch_location = urlutils.rebase_url(branch_location,
2414
 
                                                      old_base, new_base)
2415
 
            except urlutils.InvalidRebaseURLs:
2416
 
                # Fall back to absolute URL
2417
 
                branch_location = urlutils.join(old_base, branch_location)
2418
 
            target_reference_dict.setdefault(
2419
 
                tree_path, (branch_location, file_id))
2420
 
        self.target._set_all_reference_info(target_reference_dict)
2421
 
 
2422
2398
 
2423
2399
InterBranch.register_optimiser(GenericInterBranch)