diff --git a/hgext/graphlog.py b/hgext/graphlog.py --- a/hgext/graphlog.py +++ b/hgext/graphlog.py @@ -276,12 +276,12 @@ def goutgoing(ui, repo, dest=None, **opt """ check_unsupported_flags(opts) - dest, revs, checkout = hg.parseurl( - ui.expandpath(dest or 'default-push', dest or 'default'), - opts.get('rev')) + dest = ui.expandpath(dest or 'default-push', dest or 'default') + dest, branches = hg.parseurl(dest) + revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) + other = hg.repository(cmdutil.remoteui(ui, opts), dest) if revs: revs = [repo.lookup(rev) for rev in revs] - other = hg.repository(cmdutil.remoteui(ui, opts), dest) ui.status(_('comparing with %s\n') % url.hidepassword(dest)) o = repo.findoutgoing(other, force=opts.get('force')) if not o: @@ -305,8 +305,9 @@ def gincoming(ui, repo, source="default" """ check_unsupported_flags(opts) - source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev')) + source, branches = hg.parseurl(ui.expandpath(source)) other = hg.repository(cmdutil.remoteui(repo, opts), source) + revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) ui.status(_('comparing with %s\n') % url.hidepassword(source)) if revs: revs = [other.lookup(rev) for rev in revs] diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py --- a/hgext/patchbomb.py +++ b/hgext/patchbomb.py @@ -233,7 +233,8 @@ def patchbomb(ui, repo, *revs, **opts): def outgoing(dest, revs): '''Return the revisions present locally but not in dest''' dest = ui.expandpath(dest or 'default-push', dest or 'default') - dest, revs, checkout = hg.parseurl(dest, revs) + dest, branches = hg.parseurl(dest) + revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) if revs: revs = [repo.lookup(rev) for rev in revs] other = hg.repository(cmdutil.remoteui(repo, opts), dest) diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -538,9 +538,10 @@ def bundle(ui, repo, fname, dest=None, * seen[p] = 1 visit.append(p) else: - dest, revs, checkout = hg.parseurl( - ui.expandpath(dest or 'default-push', dest or 'default'), revs) + dest = ui.expandpath(dest or 'default-push', dest or 'default') + dest, branches = hg.parseurl(dest) other = hg.repository(cmdutil.remoteui(repo, opts), dest) + revs, checkout = hg.addbranchrevs(repo, other, branches, revs) o = repo.findoutgoing(other, force=opts.get('force')) if revs: @@ -607,7 +608,8 @@ def clone(ui, source, dest=None, **opts) a) the changeset, tag or branch specified with -u/--updaterev b) the changeset, tag or branch given with the first -r/--rev - c) the head of the default branch + c) the branch given with the url#branch source syntax + d) the head of the default branch Use 'hg clone -u . src dst' to checkout the source repository's parent changeset (applicable for local source repositories only). @@ -1727,8 +1729,9 @@ def identify(ui, repo, source=None, revs = [] if source: - source, revs, checkout = hg.parseurl(ui.expandpath(source), []) + source, branches = hg.parseurl(ui.expandpath(source)) repo = hg.repository(ui, source) + revs, checkout = hg.addbranchrevs(repo, repo, branches, None) if not repo.local(): if not rev and revs: @@ -1919,9 +1922,10 @@ def incoming(ui, repo, source="default", See pull for valid source format details. """ limit = cmdutil.loglimit(opts) - source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev')) + source, branches = hg.parseurl(ui.expandpath(source)) other = hg.repository(cmdutil.remoteui(repo, opts), source) ui.status(_('comparing with %s\n') % url.hidepassword(source)) + revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) if revs: revs = [other.lookup(rev) for rev in revs] common, incoming, rheads = repo.findcommonincoming(other, heads=revs, @@ -2206,9 +2210,9 @@ def outgoing(ui, repo, dest=None, **opts See pull for valid destination format details. """ limit = cmdutil.loglimit(opts) - dest, revs, checkout = hg.parseurl( - ui.expandpath(dest or 'default-push', dest or 'default'), - opts.get('rev')) + dest = ui.expandpath(dest or 'default-push', dest or 'default') + dest, branches = hg.parseurl(dest) + revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) if revs: revs = [repo.lookup(rev) for rev in revs] @@ -2327,9 +2331,10 @@ def pull(ui, repo, source="default", **o If SOURCE is omitted, the 'default' path will be used. See 'hg help urls' for more information. """ - source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev')) + source, branches = hg.parseurl(ui.expandpath(source)) other = hg.repository(cmdutil.remoteui(repo, opts), source) ui.status(_('pulling from %s\n') % url.hidepassword(source)) + revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) if revs: try: revs = [other.lookup(rev) for rev in revs] @@ -2363,9 +2368,9 @@ def push(ui, repo, dest=None, **opts): Please see 'hg help urls' for important details about ``ssh://`` URLs. If DESTINATION is omitted, a default path will be used. """ - dest, revs, checkout = hg.parseurl( - ui.expandpath(dest or 'default-push', dest or 'default'), - opts.get('rev')) + dest = ui.expandpath(dest or 'default-push', dest or 'default') + dest, branches = hg.parseurl(dest) + revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) other = hg.repository(cmdutil.remoteui(repo, opts), dest) ui.status(_('pushing to %s\n') % url.hidepassword(dest)) if revs: diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -9,7 +9,7 @@ from i18n import _ from lock import release import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo -import lock, util, extensions, error, encoding +import lock, util, extensions, error, encoding, node import merge as _merge import verify as _verify import errno, os, shutil @@ -18,15 +18,31 @@ def _local(path): return (os.path.isfile(util.drop_scheme('file', path)) and bundlerepo or localrepo) -def parseurl(url, revs=[]): - '''parse url#branch, returning url, branch + revs''' +def addbranchrevs(lrepo, repo, branches, revs): + if not branches: + return revs or None, revs and revs[0] or None + branchmap = repo.branchmap() + revs = revs and list(revs) or [] + for branch in branches: + if branch == '.': + if not lrepo or not lrepo.local(): + raise util.Abort(_("dirstate branch not accessible")) + revs.append(lrepo.dirstate.branch()) + else: + butf8 = encoding.fromlocal(branch) + if butf8 in branchmap: + revs.extend(node.hex(r) for r in reversed(branchmap[butf8])) + else: + revs.append(branch) + return revs, revs[0] + +def parseurl(url, branches=None): + '''parse url#branch, returning url, branches+[branch]''' if '#' not in url: - return url, (revs or None), revs and revs[0] or None - + return url, branches or [] url, branch = url.split('#', 1) - checkout = revs and revs[0] or branch - return url, (revs or []) + [branch], checkout + return url, (branches or []) + [branch] schemes = { 'bundle': bundlerepo, @@ -94,8 +110,9 @@ def share(ui, source, dest=None, update= if isinstance(source, str): origsource = ui.expandpath(source) - source, rev, checkout = parseurl(origsource, '') + source, branches = parseurl(origsource) srcrepo = repository(ui, source) + rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None) else: srcrepo = source origsource = source = srcrepo.url() @@ -183,12 +200,12 @@ def clone(ui, source, dest=None, pull=Fa if isinstance(source, str): origsource = ui.expandpath(source) - source, rev, checkout = parseurl(origsource, rev) + source, branch = parseurl(origsource) src_repo = repository(ui, source) else: src_repo = source origsource = source = src_repo.url() - checkout = rev and rev[0] or None + rev, checkout = addbranchrevs(src_repo, src_repo, branch, rev) if dest is None: dest = defaultdest(source) diff --git a/tests/test-branch-option b/tests/test-branch-option new file mode 100644 --- /dev/null +++ b/tests/test-branch-option @@ -0,0 +1,41 @@ +#!/bin/sh + +# test branch selection options +hg init branch +cd branch +hg branch a +echo a > foo +hg ci -d '0 0' -Ama +echo a2 > foo +hg ci -d '0 0' -ma2 +hg up 0 +hg branch c +echo c > foo +hg ci -d '0 0' -mc +cd .. +hg clone -r 0 branch branch2 +cd branch2 +hg up 0 +hg branch b +echo b > foo +hg ci -d '0 0' -mb +hg up 0 +hg branch -f b +echo b2 > foo +hg ci -d '0 0' -mb2 + +echo in rev c branch a +hg in -qr c ../branch#a +echo out branch . +hg out -q ../branch#. +echo clone branch b +cd .. +hg clone branch2#b branch3 +hg -q -R branch3 heads b +hg -q -R branch3 parents +rm -rf branch3 +echo clone rev a branch b +hg clone -r a branch2#b branch3 +hg -q -R branch3 heads b +hg -q -R branch3 parents +rm -rf branch3 diff --git a/tests/test-branch-option.out b/tests/test-branch-option.out new file mode 100644 --- /dev/null +++ b/tests/test-branch-option.out @@ -0,0 +1,44 @@ +marked working directory as branch a +adding foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +marked working directory as branch c +created new head +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +updating to branch a +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +marked working directory as branch b +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +marked working directory as branch b +created new head +in rev c branch a +1:dd6e60a716c6 +2:f25d57ab0566 +out branch . +2:65511d0e2b55 +clone branch b +requesting all changes +adding changesets +adding manifests +adding file changes +added 3 changesets with 3 changes to 1 files (+1 heads) +updating to branch b +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +2:65511d0e2b55 +1:b84708d77ab7 +2:65511d0e2b55 +clone rev a branch b +requesting all changes +adding changesets +adding manifests +adding file changes +added 3 changesets with 3 changes to 1 files (+1 heads) +updating to branch a +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +2:65511d0e2b55 +1:b84708d77ab7 +0:5b65ba7c951d diff --git a/tests/test-hg-parseurl.py b/tests/test-hg-parseurl.py --- a/tests/test-hg-parseurl.py +++ b/tests/test-hg-parseurl.py @@ -2,11 +2,11 @@ from mercurial.hg import parseurl -def testparse(url, rev=[]): - print '%s, revs: %r, checkout: %r' % parseurl(url, rev) +def testparse(url, branch=[]): + print '%s, branches: %r' % parseurl(url, branch) testparse('http://example.com/no/anchor') testparse('http://example.com/an/anchor#foo') -testparse('http://example.com/no/anchor/revs', rev=['foo']) -testparse('http://example.com/an/anchor/revs#bar', rev=['foo']) -testparse('http://example.com/an/anchor/rev-None#foo', rev=None) +testparse('http://example.com/no/anchor/branches', branch=['foo']) +testparse('http://example.com/an/anchor/branches#bar', branch=['foo']) +testparse('http://example.com/an/anchor/branches-None#foo', branch=None) diff --git a/tests/test-hg-parseurl.py.out b/tests/test-hg-parseurl.py.out --- a/tests/test-hg-parseurl.py.out +++ b/tests/test-hg-parseurl.py.out @@ -1,5 +1,5 @@ -http://example.com/no/anchor, revs: None, checkout: None -http://example.com/an/anchor, revs: ['foo'], checkout: 'foo' -http://example.com/no/anchor/revs, revs: ['foo'], checkout: 'foo' -http://example.com/an/anchor/revs, revs: ['foo', 'bar'], checkout: 'foo' -http://example.com/an/anchor/rev-None, revs: ['foo'], checkout: 'foo' +http://example.com/no/anchor, branches: [] +http://example.com/an/anchor, branches: ['foo'] +http://example.com/no/anchor/branches, branches: ['foo'] +http://example.com/an/anchor/branches, branches: ['foo', 'bar'] +http://example.com/an/anchor/branches-None, branches: ['foo']