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