##// END OF EJS Templates
hg: add peer method
Matt Mackall -
r14554:68db1704 default
parent child Browse files
Show More
@@ -1,142 +1,142
1 # fileset.py - file set queries for mercurial
1 # fileset.py - file set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import parser, error, match
8 import parser, error
9 from i18n import _
9 from i18n import _
10
10
11 elements = {
11 elements = {
12 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
12 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
13 "-": (5, ("negate", 19), ("minus", 5)),
13 "-": (5, ("negate", 19), ("minus", 5)),
14 "not": (10, ("not", 10)),
14 "not": (10, ("not", 10)),
15 "!": (10, ("not", 10)),
15 "!": (10, ("not", 10)),
16 "and": (5, None, ("and", 5)),
16 "and": (5, None, ("and", 5)),
17 "&": (5, None, ("and", 5)),
17 "&": (5, None, ("and", 5)),
18 "or": (4, None, ("or", 4)),
18 "or": (4, None, ("or", 4)),
19 "|": (4, None, ("or", 4)),
19 "|": (4, None, ("or", 4)),
20 "+": (4, None, ("or", 4)),
20 "+": (4, None, ("or", 4)),
21 ",": (2, None, ("list", 2)),
21 ",": (2, None, ("list", 2)),
22 ")": (0, None, None),
22 ")": (0, None, None),
23 "symbol": (0, ("symbol",), None),
23 "symbol": (0, ("symbol",), None),
24 "string": (0, ("string",), None),
24 "string": (0, ("string",), None),
25 "end": (0, None, None),
25 "end": (0, None, None),
26 }
26 }
27
27
28 keywords = set(['and', 'or', 'not'])
28 keywords = set(['and', 'or', 'not'])
29
29
30 globchars = ".*{}[]?/\\"
30 globchars = ".*{}[]?/\\"
31
31
32 def tokenize(program):
32 def tokenize(program):
33 pos, l = 0, len(program)
33 pos, l = 0, len(program)
34 while pos < l:
34 while pos < l:
35 c = program[pos]
35 c = program[pos]
36 if c.isspace(): # skip inter-token whitespace
36 if c.isspace(): # skip inter-token whitespace
37 pass
37 pass
38 elif c in "(),-|&+!": # handle simple operators
38 elif c in "(),-|&+!": # handle simple operators
39 yield (c, None, pos)
39 yield (c, None, pos)
40 elif (c in '"\'' or c == 'r' and
40 elif (c in '"\'' or c == 'r' and
41 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
41 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
42 if c == 'r':
42 if c == 'r':
43 pos += 1
43 pos += 1
44 c = program[pos]
44 c = program[pos]
45 decode = lambda x: x
45 decode = lambda x: x
46 else:
46 else:
47 decode = lambda x: x.decode('string-escape')
47 decode = lambda x: x.decode('string-escape')
48 pos += 1
48 pos += 1
49 s = pos
49 s = pos
50 while pos < l: # find closing quote
50 while pos < l: # find closing quote
51 d = program[pos]
51 d = program[pos]
52 if d == '\\': # skip over escaped characters
52 if d == '\\': # skip over escaped characters
53 pos += 2
53 pos += 2
54 continue
54 continue
55 if d == c:
55 if d == c:
56 yield ('string', decode(program[s:pos]), s)
56 yield ('string', decode(program[s:pos]), s)
57 break
57 break
58 pos += 1
58 pos += 1
59 else:
59 else:
60 raise error.ParseError(_("unterminated string"), s)
60 raise error.ParseError(_("unterminated string"), s)
61 elif c.isalnum() or c in globchars or ord(c) > 127:
61 elif c.isalnum() or c in globchars or ord(c) > 127:
62 # gather up a symbol/keyword
62 # gather up a symbol/keyword
63 s = pos
63 s = pos
64 pos += 1
64 pos += 1
65 while pos < l: # find end of symbol
65 while pos < l: # find end of symbol
66 d = program[pos]
66 d = program[pos]
67 if not (d.isalnum() or d in globchars or ord(d) > 127):
67 if not (d.isalnum() or d in globchars or ord(d) > 127):
68 break
68 break
69 pos += 1
69 pos += 1
70 sym = program[s:pos]
70 sym = program[s:pos]
71 if sym in keywords: # operator keywords
71 if sym in keywords: # operator keywords
72 yield (sym, None, s)
72 yield (sym, None, s)
73 else:
73 else:
74 yield ('symbol', sym, s)
74 yield ('symbol', sym, s)
75 pos -= 1
75 pos -= 1
76 else:
76 else:
77 raise error.ParseError(_("syntax error"), pos)
77 raise error.ParseError(_("syntax error"), pos)
78 pos += 1
78 pos += 1
79 yield ('end', None, pos)
79 yield ('end', None, pos)
80
80
81 parse = parser.parser(tokenize, elements).parse
81 parse = parser.parser(tokenize, elements).parse
82
82
83 def getstring(x, err):
83 def getstring(x, err):
84 if x and (x[0] == 'string' or x[0] == 'symbol'):
84 if x and (x[0] == 'string' or x[0] == 'symbol'):
85 return x[1]
85 return x[1]
86 raise error.ParseError(err)
86 raise error.ParseError(err)
87
87
88 def getset(mctx, x):
88 def getset(mctx, x):
89 if not x:
89 if not x:
90 raise error.ParseError(_("missing argument"))
90 raise error.ParseError(_("missing argument"))
91 return methods[x[0]](mctx, *x[1:])
91 return methods[x[0]](mctx, *x[1:])
92
92
93 def stringset(mctx, x):
93 def stringset(mctx, x):
94 m = mctx.matcher([x])
94 m = mctx.matcher([x])
95 return [f for f in mctx.subset if m(f)]
95 return [f for f in mctx.subset if m(f)]
96
96
97 def andset(mctx, x, y):
97 def andset(mctx, x, y):
98 return getset(mctx.narrow(getset(mctx, x)), y)
98 return getset(mctx.narrow(getset(mctx, x)), y)
99
99
100 def orset(mctx, x, y):
100 def orset(mctx, x, y):
101 # needs optimizing
101 # needs optimizing
102 xl = getset(mctx, x)
102 xl = getset(mctx, x)
103 yl = getset(mctx, y)
103 yl = getset(mctx, y)
104 return xl + [f for f in yl if f not in xl]
104 return xl + [f for f in yl if f not in xl]
105
105
106 def notset(mctx, x):
106 def notset(mctx, x):
107 s = set(getset(mctx, x))
107 s = set(getset(mctx, x))
108 return [r for r in mctx.subset if r not in s]
108 return [r for r in mctx.subset if r not in s]
109
109
110 def listset(mctx, a, b):
110 def listset(mctx, a, b):
111 raise error.ParseError(_("can't use a list in this context"))
111 raise error.ParseError(_("can't use a list in this context"))
112
112
113 methods = {
113 methods = {
114 'string': stringset,
114 'string': stringset,
115 'symbol': stringset,
115 'symbol': stringset,
116 'and': andset,
116 'and': andset,
117 'or': orset,
117 'or': orset,
118 'list': listset,
118 'list': listset,
119 'group': getset,
119 'group': getset,
120 'not': notset
120 'not': notset
121 }
121 }
122
122
123 class matchctx(object):
123 class matchctx(object):
124 def __init__(self, ctx, matchfn, subset=None):
124 def __init__(self, ctx, matchfn, subset=None):
125 self.ctx = ctx
125 self.ctx = ctx
126 self.matchfn = matchfn
126 self.matchfn = matchfn
127 self.subset = subset
127 self.subset = subset
128 if subset is None:
128 if subset is None:
129 self.subset = ctx.walk(matchfn([])) # optimize this later
129 self.subset = ctx.walk(matchfn([])) # optimize this later
130 def matcher(self, pattern):
130 def matcher(self, pattern):
131 return self.matchfn(pattern)
131 return self.matchfn(pattern)
132 def filter(self, files):
132 def filter(self, files):
133 return [f for f in files if f in self.subset]
133 return [f for f in files if f in self.subset]
134 def narrow(self, files):
134 def narrow(self, files):
135 return matchctx(self.ctx, self.matchfn,
135 return matchctx(self.ctx, self.matchfn,
136 self.filter(files))
136 self.filter(files))
137
137
138 def getfileset(ctx, matchfn, expr):
138 def getfileset(ctx, matchfn, expr):
139 tree, pos = parse(expr)
139 tree, pos = parse(expr)
140 if (pos != len(expr)):
140 if (pos != len(expr)):
141 raise error.ParseError("invalid token", pos)
141 raise error.ParseError("invalid token", pos)
142 return getset(matchctx(ctx, matchfn), tree)
142 return getset(matchctx(ctx, matchfn), tree)
@@ -1,559 +1,564
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 from node import hex, nullid
11 from node import hex, nullid
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
13 import lock, util, extensions, error, node
13 import lock, util, extensions, error, node
14 import cmdutil, discovery
14 import cmdutil, discovery
15 import merge as mergemod
15 import merge as mergemod
16 import verify as verifymod
16 import verify as verifymod
17 import errno, os, shutil
17 import errno, os, shutil
18
18
19 def _local(path):
19 def _local(path):
20 path = util.expandpath(util.localpath(path))
20 path = util.expandpath(util.localpath(path))
21 return (os.path.isfile(path) and bundlerepo or localrepo)
21 return (os.path.isfile(path) and bundlerepo or localrepo)
22
22
23 def addbranchrevs(lrepo, repo, branches, revs):
23 def addbranchrevs(lrepo, repo, branches, revs):
24 hashbranch, branches = branches
24 hashbranch, branches = branches
25 if not hashbranch and not branches:
25 if not hashbranch and not branches:
26 return revs or None, revs and revs[0] or None
26 return revs or None, revs and revs[0] or None
27 revs = revs and list(revs) or []
27 revs = revs and list(revs) or []
28 if not repo.capable('branchmap'):
28 if not repo.capable('branchmap'):
29 if branches:
29 if branches:
30 raise util.Abort(_("remote branch lookup not supported"))
30 raise util.Abort(_("remote branch lookup not supported"))
31 revs.append(hashbranch)
31 revs.append(hashbranch)
32 return revs, revs[0]
32 return revs, revs[0]
33 branchmap = repo.branchmap()
33 branchmap = repo.branchmap()
34
34
35 def primary(branch):
35 def primary(branch):
36 if branch == '.':
36 if branch == '.':
37 if not lrepo or not lrepo.local():
37 if not lrepo or not lrepo.local():
38 raise util.Abort(_("dirstate branch not accessible"))
38 raise util.Abort(_("dirstate branch not accessible"))
39 branch = lrepo.dirstate.branch()
39 branch = lrepo.dirstate.branch()
40 if branch in branchmap:
40 if branch in branchmap:
41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 return True
42 return True
43 else:
43 else:
44 return False
44 return False
45
45
46 for branch in branches:
46 for branch in branches:
47 if not primary(branch):
47 if not primary(branch):
48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 if hashbranch:
49 if hashbranch:
50 if not primary(hashbranch):
50 if not primary(hashbranch):
51 revs.append(hashbranch)
51 revs.append(hashbranch)
52 return revs, revs[0]
52 return revs, revs[0]
53
53
54 def parseurl(path, branches=None):
54 def parseurl(path, branches=None):
55 '''parse url#branch, returning (url, (branch, branches))'''
55 '''parse url#branch, returning (url, (branch, branches))'''
56
56
57 u = util.url(path)
57 u = util.url(path)
58 branch = None
58 branch = None
59 if u.fragment:
59 if u.fragment:
60 branch = u.fragment
60 branch = u.fragment
61 u.fragment = None
61 u.fragment = None
62 return str(u), (branch, branches or [])
62 return str(u), (branch, branches or [])
63
63
64 schemes = {
64 schemes = {
65 'bundle': bundlerepo,
65 'bundle': bundlerepo,
66 'file': _local,
66 'file': _local,
67 'http': httprepo,
67 'http': httprepo,
68 'https': httprepo,
68 'https': httprepo,
69 'ssh': sshrepo,
69 'ssh': sshrepo,
70 'static-http': statichttprepo,
70 'static-http': statichttprepo,
71 }
71 }
72
72
73 def _lookup(path):
73 def _lookup(path):
74 u = util.url(path)
74 u = util.url(path)
75 scheme = u.scheme or 'file'
75 scheme = u.scheme or 'file'
76 thing = schemes.get(scheme) or schemes['file']
76 thing = schemes.get(scheme) or schemes['file']
77 try:
77 try:
78 return thing(path)
78 return thing(path)
79 except TypeError:
79 except TypeError:
80 return thing
80 return thing
81
81
82 def islocal(repo):
82 def islocal(repo):
83 '''return true if repo or path is local'''
83 '''return true if repo or path is local'''
84 if isinstance(repo, str):
84 if isinstance(repo, str):
85 try:
85 try:
86 return _lookup(repo).islocal(repo)
86 return _lookup(repo).islocal(repo)
87 except AttributeError:
87 except AttributeError:
88 return False
88 return False
89 return repo.local()
89 return repo.local()
90
90
91 def repository(ui, path='', create=False):
91 def repository(ui, path='', create=False):
92 """return a repository object for the specified path"""
92 """return a repository object for the specified path"""
93 repo = _lookup(path).instance(ui, path, create)
93 repo = _lookup(path).instance(ui, path, create)
94 ui = getattr(repo, "ui", ui)
94 ui = getattr(repo, "ui", ui)
95 for name, module in extensions.extensions():
95 for name, module in extensions.extensions():
96 hook = getattr(module, 'reposetup', None)
96 hook = getattr(module, 'reposetup', None)
97 if hook:
97 if hook:
98 hook(ui, repo)
98 hook(ui, repo)
99 return repo
99 return repo
100
100
101 def peer(ui, opts, path, create=False):
102 '''return a repository peer for the specified path'''
103 rui = remoteui(ui, opts)
104 return repository(rui, path, create)
105
101 def defaultdest(source):
106 def defaultdest(source):
102 '''return default destination of clone if none is given'''
107 '''return default destination of clone if none is given'''
103 return os.path.basename(os.path.normpath(source))
108 return os.path.basename(os.path.normpath(source))
104
109
105 def share(ui, source, dest=None, update=True):
110 def share(ui, source, dest=None, update=True):
106 '''create a shared repository'''
111 '''create a shared repository'''
107
112
108 if not islocal(source):
113 if not islocal(source):
109 raise util.Abort(_('can only share local repositories'))
114 raise util.Abort(_('can only share local repositories'))
110
115
111 if not dest:
116 if not dest:
112 dest = defaultdest(source)
117 dest = defaultdest(source)
113 else:
118 else:
114 dest = ui.expandpath(dest)
119 dest = ui.expandpath(dest)
115
120
116 if isinstance(source, str):
121 if isinstance(source, str):
117 origsource = ui.expandpath(source)
122 origsource = ui.expandpath(source)
118 source, branches = parseurl(origsource)
123 source, branches = parseurl(origsource)
119 srcrepo = repository(ui, source)
124 srcrepo = repository(ui, source)
120 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
125 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
121 else:
126 else:
122 srcrepo = source
127 srcrepo = source
123 origsource = source = srcrepo.url()
128 origsource = source = srcrepo.url()
124 checkout = None
129 checkout = None
125
130
126 sharedpath = srcrepo.sharedpath # if our source is already sharing
131 sharedpath = srcrepo.sharedpath # if our source is already sharing
127
132
128 root = os.path.realpath(dest)
133 root = os.path.realpath(dest)
129 roothg = os.path.join(root, '.hg')
134 roothg = os.path.join(root, '.hg')
130
135
131 if os.path.exists(roothg):
136 if os.path.exists(roothg):
132 raise util.Abort(_('destination already exists'))
137 raise util.Abort(_('destination already exists'))
133
138
134 if not os.path.isdir(root):
139 if not os.path.isdir(root):
135 os.mkdir(root)
140 os.mkdir(root)
136 util.makedir(roothg, notindexed=True)
141 util.makedir(roothg, notindexed=True)
137
142
138 requirements = ''
143 requirements = ''
139 try:
144 try:
140 requirements = srcrepo.opener.read('requires')
145 requirements = srcrepo.opener.read('requires')
141 except IOError, inst:
146 except IOError, inst:
142 if inst.errno != errno.ENOENT:
147 if inst.errno != errno.ENOENT:
143 raise
148 raise
144
149
145 requirements += 'shared\n'
150 requirements += 'shared\n'
146 util.writefile(os.path.join(roothg, 'requires'), requirements)
151 util.writefile(os.path.join(roothg, 'requires'), requirements)
147 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
152 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
148
153
149 r = repository(ui, root)
154 r = repository(ui, root)
150
155
151 default = srcrepo.ui.config('paths', 'default')
156 default = srcrepo.ui.config('paths', 'default')
152 if default:
157 if default:
153 fp = r.opener("hgrc", "w", text=True)
158 fp = r.opener("hgrc", "w", text=True)
154 fp.write("[paths]\n")
159 fp.write("[paths]\n")
155 fp.write("default = %s\n" % default)
160 fp.write("default = %s\n" % default)
156 fp.close()
161 fp.close()
157
162
158 if update:
163 if update:
159 r.ui.status(_("updating working directory\n"))
164 r.ui.status(_("updating working directory\n"))
160 if update is not True:
165 if update is not True:
161 checkout = update
166 checkout = update
162 for test in (checkout, 'default', 'tip'):
167 for test in (checkout, 'default', 'tip'):
163 if test is None:
168 if test is None:
164 continue
169 continue
165 try:
170 try:
166 uprev = r.lookup(test)
171 uprev = r.lookup(test)
167 break
172 break
168 except error.RepoLookupError:
173 except error.RepoLookupError:
169 continue
174 continue
170 _update(r, uprev)
175 _update(r, uprev)
171
176
172 def clone(ui, opts, source, dest=None, pull=False, rev=None, update=True,
177 def clone(ui, opts, source, dest=None, pull=False, rev=None, update=True,
173 stream=False, branch=None):
178 stream=False, branch=None):
174 """Make a copy of an existing repository.
179 """Make a copy of an existing repository.
175
180
176 Create a copy of an existing repository in a new directory. The
181 Create a copy of an existing repository in a new directory. The
177 source and destination are URLs, as passed to the repository
182 source and destination are URLs, as passed to the repository
178 function. Returns a pair of repository objects, the source and
183 function. Returns a pair of repository objects, the source and
179 newly created destination.
184 newly created destination.
180
185
181 The location of the source is added to the new repository's
186 The location of the source is added to the new repository's
182 .hg/hgrc file, as the default to be used for future pulls and
187 .hg/hgrc file, as the default to be used for future pulls and
183 pushes.
188 pushes.
184
189
185 If an exception is raised, the partly cloned/updated destination
190 If an exception is raised, the partly cloned/updated destination
186 repository will be deleted.
191 repository will be deleted.
187
192
188 Arguments:
193 Arguments:
189
194
190 source: repository object or URL
195 source: repository object or URL
191
196
192 dest: URL of destination repository to create (defaults to base
197 dest: URL of destination repository to create (defaults to base
193 name of source repository)
198 name of source repository)
194
199
195 pull: always pull from source repository, even in local case
200 pull: always pull from source repository, even in local case
196
201
197 stream: stream raw data uncompressed from repository (fast over
202 stream: stream raw data uncompressed from repository (fast over
198 LAN, slow over WAN)
203 LAN, slow over WAN)
199
204
200 rev: revision to clone up to (implies pull=True)
205 rev: revision to clone up to (implies pull=True)
201
206
202 update: update working directory after clone completes, if
207 update: update working directory after clone completes, if
203 destination is local repository (True means update to default rev,
208 destination is local repository (True means update to default rev,
204 anything else is treated as a revision)
209 anything else is treated as a revision)
205
210
206 branch: branches to clone
211 branch: branches to clone
207 """
212 """
208
213
209 if isinstance(source, str):
214 if isinstance(source, str):
210 origsource = ui.expandpath(source)
215 origsource = ui.expandpath(source)
211 source, branch = parseurl(origsource, branch)
216 source, branch = parseurl(origsource, branch)
212 srcrepo = repository(remoteui(ui, opts), source)
217 srcrepo = repository(remoteui(ui, opts), source)
213 else:
218 else:
214 srcrepo = source
219 srcrepo = source
215 branch = (None, branch or [])
220 branch = (None, branch or [])
216 origsource = source = srcrepo.url()
221 origsource = source = srcrepo.url()
217 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
222 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
218
223
219 if dest is None:
224 if dest is None:
220 dest = defaultdest(source)
225 dest = defaultdest(source)
221 ui.status(_("destination directory: %s\n") % dest)
226 ui.status(_("destination directory: %s\n") % dest)
222 else:
227 else:
223 dest = ui.expandpath(dest)
228 dest = ui.expandpath(dest)
224
229
225 dest = util.localpath(dest)
230 dest = util.localpath(dest)
226 source = util.localpath(source)
231 source = util.localpath(source)
227
232
228 if os.path.exists(dest):
233 if os.path.exists(dest):
229 if not os.path.isdir(dest):
234 if not os.path.isdir(dest):
230 raise util.Abort(_("destination '%s' already exists") % dest)
235 raise util.Abort(_("destination '%s' already exists") % dest)
231 elif os.listdir(dest):
236 elif os.listdir(dest):
232 raise util.Abort(_("destination '%s' is not empty") % dest)
237 raise util.Abort(_("destination '%s' is not empty") % dest)
233
238
234 class DirCleanup(object):
239 class DirCleanup(object):
235 def __init__(self, dir_):
240 def __init__(self, dir_):
236 self.rmtree = shutil.rmtree
241 self.rmtree = shutil.rmtree
237 self.dir_ = dir_
242 self.dir_ = dir_
238 def close(self):
243 def close(self):
239 self.dir_ = None
244 self.dir_ = None
240 def cleanup(self):
245 def cleanup(self):
241 if self.dir_:
246 if self.dir_:
242 self.rmtree(self.dir_, True)
247 self.rmtree(self.dir_, True)
243
248
244 srclock = destlock = dircleanup = None
249 srclock = destlock = dircleanup = None
245 try:
250 try:
246 abspath = origsource
251 abspath = origsource
247 if islocal(origsource):
252 if islocal(origsource):
248 abspath = os.path.abspath(util.localpath(origsource))
253 abspath = os.path.abspath(util.localpath(origsource))
249
254
250 if islocal(dest):
255 if islocal(dest):
251 dircleanup = DirCleanup(dest)
256 dircleanup = DirCleanup(dest)
252
257
253 copy = False
258 copy = False
254 if srcrepo.cancopy() and islocal(dest):
259 if srcrepo.cancopy() and islocal(dest):
255 copy = not pull and not rev
260 copy = not pull and not rev
256
261
257 if copy:
262 if copy:
258 try:
263 try:
259 # we use a lock here because if we race with commit, we
264 # we use a lock here because if we race with commit, we
260 # can end up with extra data in the cloned revlogs that's
265 # can end up with extra data in the cloned revlogs that's
261 # not pointed to by changesets, thus causing verify to
266 # not pointed to by changesets, thus causing verify to
262 # fail
267 # fail
263 srclock = srcrepo.lock(wait=False)
268 srclock = srcrepo.lock(wait=False)
264 except error.LockError:
269 except error.LockError:
265 copy = False
270 copy = False
266
271
267 if copy:
272 if copy:
268 srcrepo.hook('preoutgoing', throw=True, source='clone')
273 srcrepo.hook('preoutgoing', throw=True, source='clone')
269 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
274 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
270 if not os.path.exists(dest):
275 if not os.path.exists(dest):
271 os.mkdir(dest)
276 os.mkdir(dest)
272 else:
277 else:
273 # only clean up directories we create ourselves
278 # only clean up directories we create ourselves
274 dircleanup.dir_ = hgdir
279 dircleanup.dir_ = hgdir
275 try:
280 try:
276 destpath = hgdir
281 destpath = hgdir
277 util.makedir(destpath, notindexed=True)
282 util.makedir(destpath, notindexed=True)
278 except OSError, inst:
283 except OSError, inst:
279 if inst.errno == errno.EEXIST:
284 if inst.errno == errno.EEXIST:
280 dircleanup.close()
285 dircleanup.close()
281 raise util.Abort(_("destination '%s' already exists")
286 raise util.Abort(_("destination '%s' already exists")
282 % dest)
287 % dest)
283 raise
288 raise
284
289
285 hardlink = None
290 hardlink = None
286 num = 0
291 num = 0
287 for f in srcrepo.store.copylist():
292 for f in srcrepo.store.copylist():
288 src = os.path.join(srcrepo.sharedpath, f)
293 src = os.path.join(srcrepo.sharedpath, f)
289 dst = os.path.join(destpath, f)
294 dst = os.path.join(destpath, f)
290 dstbase = os.path.dirname(dst)
295 dstbase = os.path.dirname(dst)
291 if dstbase and not os.path.exists(dstbase):
296 if dstbase and not os.path.exists(dstbase):
292 os.mkdir(dstbase)
297 os.mkdir(dstbase)
293 if os.path.exists(src):
298 if os.path.exists(src):
294 if dst.endswith('data'):
299 if dst.endswith('data'):
295 # lock to avoid premature writing to the target
300 # lock to avoid premature writing to the target
296 destlock = lock.lock(os.path.join(dstbase, "lock"))
301 destlock = lock.lock(os.path.join(dstbase, "lock"))
297 hardlink, n = util.copyfiles(src, dst, hardlink)
302 hardlink, n = util.copyfiles(src, dst, hardlink)
298 num += n
303 num += n
299 if hardlink:
304 if hardlink:
300 ui.debug("linked %d files\n" % num)
305 ui.debug("linked %d files\n" % num)
301 else:
306 else:
302 ui.debug("copied %d files\n" % num)
307 ui.debug("copied %d files\n" % num)
303
308
304 # we need to re-init the repo after manually copying the data
309 # we need to re-init the repo after manually copying the data
305 # into it
310 # into it
306 destrepo = repository(remoteui(ui, opts), dest)
311 destrepo = repository(remoteui(ui, opts), dest)
307 srcrepo.hook('outgoing', source='clone',
312 srcrepo.hook('outgoing', source='clone',
308 node=node.hex(node.nullid))
313 node=node.hex(node.nullid))
309 else:
314 else:
310 try:
315 try:
311 destrepo = repository(remoteui(ui, opts), dest, create=True)
316 destrepo = repository(remoteui(ui, opts), dest, create=True)
312 except OSError, inst:
317 except OSError, inst:
313 if inst.errno == errno.EEXIST:
318 if inst.errno == errno.EEXIST:
314 dircleanup.close()
319 dircleanup.close()
315 raise util.Abort(_("destination '%s' already exists")
320 raise util.Abort(_("destination '%s' already exists")
316 % dest)
321 % dest)
317 raise
322 raise
318
323
319 revs = None
324 revs = None
320 if rev:
325 if rev:
321 if not srcrepo.capable('lookup'):
326 if not srcrepo.capable('lookup'):
322 raise util.Abort(_("src repository does not support "
327 raise util.Abort(_("src repository does not support "
323 "revision lookup and so doesn't "
328 "revision lookup and so doesn't "
324 "support clone by revision"))
329 "support clone by revision"))
325 revs = [srcrepo.lookup(r) for r in rev]
330 revs = [srcrepo.lookup(r) for r in rev]
326 checkout = revs[0]
331 checkout = revs[0]
327 if destrepo.local():
332 if destrepo.local():
328 destrepo.clone(srcrepo, heads=revs, stream=stream)
333 destrepo.clone(srcrepo, heads=revs, stream=stream)
329 elif srcrepo.local():
334 elif srcrepo.local():
330 srcrepo.push(destrepo, revs=revs)
335 srcrepo.push(destrepo, revs=revs)
331 else:
336 else:
332 raise util.Abort(_("clone from remote to remote not supported"))
337 raise util.Abort(_("clone from remote to remote not supported"))
333
338
334 if dircleanup:
339 if dircleanup:
335 dircleanup.close()
340 dircleanup.close()
336
341
337 if destrepo.local():
342 if destrepo.local():
338 fp = destrepo.opener("hgrc", "w", text=True)
343 fp = destrepo.opener("hgrc", "w", text=True)
339 fp.write("[paths]\n")
344 fp.write("[paths]\n")
340 fp.write("default = %s\n" % abspath)
345 fp.write("default = %s\n" % abspath)
341 fp.close()
346 fp.close()
342
347
343 destrepo.ui.setconfig('paths', 'default', abspath)
348 destrepo.ui.setconfig('paths', 'default', abspath)
344
349
345 if update:
350 if update:
346 if update is not True:
351 if update is not True:
347 checkout = update
352 checkout = update
348 if srcrepo.local():
353 if srcrepo.local():
349 checkout = srcrepo.lookup(update)
354 checkout = srcrepo.lookup(update)
350 for test in (checkout, 'default', 'tip'):
355 for test in (checkout, 'default', 'tip'):
351 if test is None:
356 if test is None:
352 continue
357 continue
353 try:
358 try:
354 uprev = destrepo.lookup(test)
359 uprev = destrepo.lookup(test)
355 break
360 break
356 except error.RepoLookupError:
361 except error.RepoLookupError:
357 continue
362 continue
358 bn = destrepo[uprev].branch()
363 bn = destrepo[uprev].branch()
359 destrepo.ui.status(_("updating to branch %s\n") % bn)
364 destrepo.ui.status(_("updating to branch %s\n") % bn)
360 _update(destrepo, uprev)
365 _update(destrepo, uprev)
361
366
362 # clone all bookmarks
367 # clone all bookmarks
363 if destrepo.local() and srcrepo.capable("pushkey"):
368 if destrepo.local() and srcrepo.capable("pushkey"):
364 rb = srcrepo.listkeys('bookmarks')
369 rb = srcrepo.listkeys('bookmarks')
365 for k, n in rb.iteritems():
370 for k, n in rb.iteritems():
366 try:
371 try:
367 m = destrepo.lookup(n)
372 m = destrepo.lookup(n)
368 destrepo._bookmarks[k] = m
373 destrepo._bookmarks[k] = m
369 except error.RepoLookupError:
374 except error.RepoLookupError:
370 pass
375 pass
371 if rb:
376 if rb:
372 bookmarks.write(destrepo)
377 bookmarks.write(destrepo)
373 elif srcrepo.local() and destrepo.capable("pushkey"):
378 elif srcrepo.local() and destrepo.capable("pushkey"):
374 for k, n in srcrepo._bookmarks.iteritems():
379 for k, n in srcrepo._bookmarks.iteritems():
375 destrepo.pushkey('bookmarks', k, '', hex(n))
380 destrepo.pushkey('bookmarks', k, '', hex(n))
376
381
377 return srcrepo, destrepo
382 return srcrepo, destrepo
378 finally:
383 finally:
379 release(srclock, destlock)
384 release(srclock, destlock)
380 if dircleanup is not None:
385 if dircleanup is not None:
381 dircleanup.cleanup()
386 dircleanup.cleanup()
382
387
383 def _showstats(repo, stats):
388 def _showstats(repo, stats):
384 repo.ui.status(_("%d files updated, %d files merged, "
389 repo.ui.status(_("%d files updated, %d files merged, "
385 "%d files removed, %d files unresolved\n") % stats)
390 "%d files removed, %d files unresolved\n") % stats)
386
391
387 def update(repo, node):
392 def update(repo, node):
388 """update the working directory to node, merging linear changes"""
393 """update the working directory to node, merging linear changes"""
389 stats = mergemod.update(repo, node, False, False, None)
394 stats = mergemod.update(repo, node, False, False, None)
390 _showstats(repo, stats)
395 _showstats(repo, stats)
391 if stats[3]:
396 if stats[3]:
392 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
397 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
393 return stats[3] > 0
398 return stats[3] > 0
394
399
395 # naming conflict in clone()
400 # naming conflict in clone()
396 _update = update
401 _update = update
397
402
398 def clean(repo, node, show_stats=True):
403 def clean(repo, node, show_stats=True):
399 """forcibly switch the working directory to node, clobbering changes"""
404 """forcibly switch the working directory to node, clobbering changes"""
400 stats = mergemod.update(repo, node, False, True, None)
405 stats = mergemod.update(repo, node, False, True, None)
401 if show_stats:
406 if show_stats:
402 _showstats(repo, stats)
407 _showstats(repo, stats)
403 return stats[3] > 0
408 return stats[3] > 0
404
409
405 def merge(repo, node, force=None, remind=True):
410 def merge(repo, node, force=None, remind=True):
406 """Branch merge with node, resolving changes. Return true if any
411 """Branch merge with node, resolving changes. Return true if any
407 unresolved conflicts."""
412 unresolved conflicts."""
408 stats = mergemod.update(repo, node, True, force, False)
413 stats = mergemod.update(repo, node, True, force, False)
409 _showstats(repo, stats)
414 _showstats(repo, stats)
410 if stats[3]:
415 if stats[3]:
411 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
416 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
412 "or 'hg update -C .' to abandon\n"))
417 "or 'hg update -C .' to abandon\n"))
413 elif remind:
418 elif remind:
414 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
419 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
415 return stats[3] > 0
420 return stats[3] > 0
416
421
417 def _incoming(displaychlist, subreporecurse, ui, repo, source,
422 def _incoming(displaychlist, subreporecurse, ui, repo, source,
418 opts, buffered=False):
423 opts, buffered=False):
419 """
424 """
420 Helper for incoming / gincoming.
425 Helper for incoming / gincoming.
421 displaychlist gets called with
426 displaychlist gets called with
422 (remoterepo, incomingchangesetlist, displayer) parameters,
427 (remoterepo, incomingchangesetlist, displayer) parameters,
423 and is supposed to contain only code that can't be unified.
428 and is supposed to contain only code that can't be unified.
424 """
429 """
425 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
430 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
426 other = repository(remoteui(repo, opts), source)
431 other = repository(remoteui(repo, opts), source)
427 ui.status(_('comparing with %s\n') % util.hidepassword(source))
432 ui.status(_('comparing with %s\n') % util.hidepassword(source))
428 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
433 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
429
434
430 if revs:
435 if revs:
431 revs = [other.lookup(rev) for rev in revs]
436 revs = [other.lookup(rev) for rev in revs]
432 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
437 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
433 revs, opts["bundle"], opts["force"])
438 revs, opts["bundle"], opts["force"])
434 try:
439 try:
435 if not chlist:
440 if not chlist:
436 ui.status(_("no changes found\n"))
441 ui.status(_("no changes found\n"))
437 return subreporecurse()
442 return subreporecurse()
438
443
439 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
444 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
440
445
441 # XXX once graphlog extension makes it into core,
446 # XXX once graphlog extension makes it into core,
442 # should be replaced by a if graph/else
447 # should be replaced by a if graph/else
443 displaychlist(other, chlist, displayer)
448 displaychlist(other, chlist, displayer)
444
449
445 displayer.close()
450 displayer.close()
446 finally:
451 finally:
447 cleanupfn()
452 cleanupfn()
448 subreporecurse()
453 subreporecurse()
449 return 0 # exit code is zero since we found incoming changes
454 return 0 # exit code is zero since we found incoming changes
450
455
451 def incoming(ui, repo, source, opts):
456 def incoming(ui, repo, source, opts):
452 def subreporecurse():
457 def subreporecurse():
453 ret = 1
458 ret = 1
454 if opts.get('subrepos'):
459 if opts.get('subrepos'):
455 ctx = repo[None]
460 ctx = repo[None]
456 for subpath in sorted(ctx.substate):
461 for subpath in sorted(ctx.substate):
457 sub = ctx.sub(subpath)
462 sub = ctx.sub(subpath)
458 ret = min(ret, sub.incoming(ui, source, opts))
463 ret = min(ret, sub.incoming(ui, source, opts))
459 return ret
464 return ret
460
465
461 def display(other, chlist, displayer):
466 def display(other, chlist, displayer):
462 limit = cmdutil.loglimit(opts)
467 limit = cmdutil.loglimit(opts)
463 if opts.get('newest_first'):
468 if opts.get('newest_first'):
464 chlist.reverse()
469 chlist.reverse()
465 count = 0
470 count = 0
466 for n in chlist:
471 for n in chlist:
467 if limit is not None and count >= limit:
472 if limit is not None and count >= limit:
468 break
473 break
469 parents = [p for p in other.changelog.parents(n) if p != nullid]
474 parents = [p for p in other.changelog.parents(n) if p != nullid]
470 if opts.get('no_merges') and len(parents) == 2:
475 if opts.get('no_merges') and len(parents) == 2:
471 continue
476 continue
472 count += 1
477 count += 1
473 displayer.show(other[n])
478 displayer.show(other[n])
474 return _incoming(display, subreporecurse, ui, repo, source, opts)
479 return _incoming(display, subreporecurse, ui, repo, source, opts)
475
480
476 def _outgoing(ui, repo, dest, opts):
481 def _outgoing(ui, repo, dest, opts):
477 dest = ui.expandpath(dest or 'default-push', dest or 'default')
482 dest = ui.expandpath(dest or 'default-push', dest or 'default')
478 dest, branches = parseurl(dest, opts.get('branch'))
483 dest, branches = parseurl(dest, opts.get('branch'))
479 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
484 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
480 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
485 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
481 if revs:
486 if revs:
482 revs = [repo.lookup(rev) for rev in revs]
487 revs = [repo.lookup(rev) for rev in revs]
483
488
484 other = repository(remoteui(repo, opts), dest)
489 other = repository(remoteui(repo, opts), dest)
485 common, outheads = discovery.findcommonoutgoing(repo, other, revs,
490 common, outheads = discovery.findcommonoutgoing(repo, other, revs,
486 force=opts.get('force'))
491 force=opts.get('force'))
487 o = repo.changelog.findmissing(common, outheads)
492 o = repo.changelog.findmissing(common, outheads)
488 if not o:
493 if not o:
489 ui.status(_("no changes found\n"))
494 ui.status(_("no changes found\n"))
490 return None
495 return None
491 return o
496 return o
492
497
493 def outgoing(ui, repo, dest, opts):
498 def outgoing(ui, repo, dest, opts):
494 def recurse():
499 def recurse():
495 ret = 1
500 ret = 1
496 if opts.get('subrepos'):
501 if opts.get('subrepos'):
497 ctx = repo[None]
502 ctx = repo[None]
498 for subpath in sorted(ctx.substate):
503 for subpath in sorted(ctx.substate):
499 sub = ctx.sub(subpath)
504 sub = ctx.sub(subpath)
500 ret = min(ret, sub.outgoing(ui, dest, opts))
505 ret = min(ret, sub.outgoing(ui, dest, opts))
501 return ret
506 return ret
502
507
503 limit = cmdutil.loglimit(opts)
508 limit = cmdutil.loglimit(opts)
504 o = _outgoing(ui, repo, dest, opts)
509 o = _outgoing(ui, repo, dest, opts)
505 if o is None:
510 if o is None:
506 return recurse()
511 return recurse()
507
512
508 if opts.get('newest_first'):
513 if opts.get('newest_first'):
509 o.reverse()
514 o.reverse()
510 displayer = cmdutil.show_changeset(ui, repo, opts)
515 displayer = cmdutil.show_changeset(ui, repo, opts)
511 count = 0
516 count = 0
512 for n in o:
517 for n in o:
513 if limit is not None and count >= limit:
518 if limit is not None and count >= limit:
514 break
519 break
515 parents = [p for p in repo.changelog.parents(n) if p != nullid]
520 parents = [p for p in repo.changelog.parents(n) if p != nullid]
516 if opts.get('no_merges') and len(parents) == 2:
521 if opts.get('no_merges') and len(parents) == 2:
517 continue
522 continue
518 count += 1
523 count += 1
519 displayer.show(repo[n])
524 displayer.show(repo[n])
520 displayer.close()
525 displayer.close()
521 recurse()
526 recurse()
522 return 0 # exit code is zero since we found outgoing changes
527 return 0 # exit code is zero since we found outgoing changes
523
528
524 def revert(repo, node, choose):
529 def revert(repo, node, choose):
525 """revert changes to revision in node without updating dirstate"""
530 """revert changes to revision in node without updating dirstate"""
526 return mergemod.update(repo, node, False, True, choose)[3] > 0
531 return mergemod.update(repo, node, False, True, choose)[3] > 0
527
532
528 def verify(repo):
533 def verify(repo):
529 """verify the consistency of a repository"""
534 """verify the consistency of a repository"""
530 return verifymod.verify(repo)
535 return verifymod.verify(repo)
531
536
532 def remoteui(src, opts):
537 def remoteui(src, opts):
533 'build a remote ui from ui or repo and opts'
538 'build a remote ui from ui or repo and opts'
534 if hasattr(src, 'baseui'): # looks like a repository
539 if hasattr(src, 'baseui'): # looks like a repository
535 dst = src.baseui.copy() # drop repo-specific config
540 dst = src.baseui.copy() # drop repo-specific config
536 src = src.ui # copy target options from repo
541 src = src.ui # copy target options from repo
537 else: # assume it's a global ui object
542 else: # assume it's a global ui object
538 dst = src.copy() # keep all global options
543 dst = src.copy() # keep all global options
539
544
540 # copy ssh-specific options
545 # copy ssh-specific options
541 for o in 'ssh', 'remotecmd':
546 for o in 'ssh', 'remotecmd':
542 v = opts.get(o) or src.config('ui', o)
547 v = opts.get(o) or src.config('ui', o)
543 if v:
548 if v:
544 dst.setconfig("ui", o, v)
549 dst.setconfig("ui", o, v)
545
550
546 # copy bundle-specific options
551 # copy bundle-specific options
547 r = src.config('bundle', 'mainreporoot')
552 r = src.config('bundle', 'mainreporoot')
548 if r:
553 if r:
549 dst.setconfig('bundle', 'mainreporoot', r)
554 dst.setconfig('bundle', 'mainreporoot', r)
550
555
551 # copy selected local settings to the remote ui
556 # copy selected local settings to the remote ui
552 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
557 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
553 for key, val in src.configitems(sect):
558 for key, val in src.configitems(sect):
554 dst.setconfig(sect, key, val)
559 dst.setconfig(sect, key, val)
555 v = src.config('web', 'cacerts')
560 v = src.config('web', 'cacerts')
556 if v:
561 if v:
557 dst.setconfig('web', 'cacerts', util.expandpath(v))
562 dst.setconfig('web', 'cacerts', util.expandpath(v))
558
563
559 return dst
564 return dst
General Comments 0
You need to be logged in to leave comments. Login now