##// END OF EJS Templates
merge with crew-stable
Thomas Arendsen Hein -
r17704:3d1a781b merge default
parent child Browse files
Show More
@@ -1,598 +1,600 b''
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, httppeer, sshpeer, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
13 import lock, util, extensions, error, node, scmutil, phases
13 import lock, util, extensions, error, node, scmutil, phases
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.urllocalpath(path))
20 path = util.expandpath(util.urllocalpath(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, other, branches, revs):
23 def addbranchrevs(lrepo, other, branches, revs):
24 peer = other.peer() # a courtesy to callers using a localrepo for other
24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 hashbranch, branches = branches
25 hashbranch, branches = branches
26 if not hashbranch and not branches:
26 if not hashbranch and not branches:
27 return revs or None, revs and revs[0] or None
27 return revs or None, revs and revs[0] or None
28 revs = revs and list(revs) or []
28 revs = revs and list(revs) or []
29 if not peer.capable('branchmap'):
29 if not peer.capable('branchmap'):
30 if branches:
30 if branches:
31 raise util.Abort(_("remote branch lookup not supported"))
31 raise util.Abort(_("remote branch lookup not supported"))
32 revs.append(hashbranch)
32 revs.append(hashbranch)
33 return revs, revs[0]
33 return revs, revs[0]
34 branchmap = peer.branchmap()
34 branchmap = peer.branchmap()
35
35
36 def primary(branch):
36 def primary(branch):
37 if branch == '.':
37 if branch == '.':
38 if not lrepo:
38 if not lrepo:
39 raise util.Abort(_("dirstate branch not accessible"))
39 raise util.Abort(_("dirstate branch not accessible"))
40 branch = lrepo.dirstate.branch()
40 branch = lrepo.dirstate.branch()
41 if branch in branchmap:
41 if branch in branchmap:
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 return True
43 return True
44 else:
44 else:
45 return False
45 return False
46
46
47 for branch in branches:
47 for branch in branches:
48 if not primary(branch):
48 if not primary(branch):
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 if hashbranch:
50 if hashbranch:
51 if not primary(hashbranch):
51 if not primary(hashbranch):
52 revs.append(hashbranch)
52 revs.append(hashbranch)
53 return revs, revs[0]
53 return revs, revs[0]
54
54
55 def parseurl(path, branches=None):
55 def parseurl(path, branches=None):
56 '''parse url#branch, returning (url, (branch, branches))'''
56 '''parse url#branch, returning (url, (branch, branches))'''
57
57
58 u = util.url(path)
58 u = util.url(path)
59 branch = None
59 branch = None
60 if u.fragment:
60 if u.fragment:
61 branch = u.fragment
61 branch = u.fragment
62 u.fragment = None
62 u.fragment = None
63 return str(u), (branch, branches or [])
63 return str(u), (branch, branches or [])
64
64
65 schemes = {
65 schemes = {
66 'bundle': bundlerepo,
66 'bundle': bundlerepo,
67 'file': _local,
67 'file': _local,
68 'http': httppeer,
68 'http': httppeer,
69 'https': httppeer,
69 'https': httppeer,
70 'ssh': sshpeer,
70 'ssh': sshpeer,
71 'static-http': statichttprepo,
71 'static-http': statichttprepo,
72 }
72 }
73
73
74 def _peerlookup(path):
74 def _peerlookup(path):
75 u = util.url(path)
75 u = util.url(path)
76 scheme = u.scheme or 'file'
76 scheme = u.scheme or 'file'
77 thing = schemes.get(scheme) or schemes['file']
77 thing = schemes.get(scheme) or schemes['file']
78 try:
78 try:
79 return thing(path)
79 return thing(path)
80 except TypeError:
80 except TypeError:
81 return thing
81 return thing
82
82
83 def islocal(repo):
83 def islocal(repo):
84 '''return true if repo or path is local'''
84 '''return true if repo or path is local'''
85 if isinstance(repo, str):
85 if isinstance(repo, str):
86 try:
86 try:
87 return _peerlookup(repo).islocal(repo)
87 return _peerlookup(repo).islocal(repo)
88 except AttributeError:
88 except AttributeError:
89 return False
89 return False
90 return repo.local()
90 return repo.local()
91
91
92 def _peerorrepo(ui, path, create=False):
92 def _peerorrepo(ui, path, create=False):
93 """return a repository object for the specified path"""
93 """return a repository object for the specified path"""
94 obj = _peerlookup(path).instance(ui, path, create)
94 obj = _peerlookup(path).instance(ui, path, create)
95 ui = getattr(obj, "ui", ui)
95 ui = getattr(obj, "ui", ui)
96 for name, module in extensions.extensions():
96 for name, module in extensions.extensions():
97 hook = getattr(module, 'reposetup', None)
97 hook = getattr(module, 'reposetup', None)
98 if hook:
98 if hook:
99 hook(ui, obj)
99 hook(ui, obj)
100 return obj
100 return obj
101
101
102 def repository(ui, path='', create=False):
102 def repository(ui, path='', create=False):
103 """return a repository object for the specified path"""
103 """return a repository object for the specified path"""
104 peer = _peerorrepo(ui, path, create)
104 peer = _peerorrepo(ui, path, create)
105 repo = peer.local()
105 repo = peer.local()
106 if not repo:
106 if not repo:
107 raise util.Abort(_("repository '%s' is not local") %
107 raise util.Abort(_("repository '%s' is not local") %
108 (path or peer.url()))
108 (path or peer.url()))
109 return repo
109 return repo
110
110
111 def peer(uiorrepo, opts, path, create=False):
111 def peer(uiorrepo, opts, path, create=False):
112 '''return a repository peer for the specified path'''
112 '''return a repository peer for the specified path'''
113 rui = remoteui(uiorrepo, opts)
113 rui = remoteui(uiorrepo, opts)
114 return _peerorrepo(rui, path, create).peer()
114 return _peerorrepo(rui, path, create).peer()
115
115
116 def defaultdest(source):
116 def defaultdest(source):
117 '''return default destination of clone if none is given'''
117 '''return default destination of clone if none is given'''
118 return os.path.basename(os.path.normpath(source))
118 return os.path.basename(os.path.normpath(source))
119
119
120 def share(ui, source, dest=None, update=True):
120 def share(ui, source, dest=None, update=True):
121 '''create a shared repository'''
121 '''create a shared repository'''
122
122
123 if not islocal(source):
123 if not islocal(source):
124 raise util.Abort(_('can only share local repositories'))
124 raise util.Abort(_('can only share local repositories'))
125
125
126 if not dest:
126 if not dest:
127 dest = defaultdest(source)
127 dest = defaultdest(source)
128 else:
128 else:
129 dest = ui.expandpath(dest)
129 dest = ui.expandpath(dest)
130
130
131 if isinstance(source, str):
131 if isinstance(source, str):
132 origsource = ui.expandpath(source)
132 origsource = ui.expandpath(source)
133 source, branches = parseurl(origsource)
133 source, branches = parseurl(origsource)
134 srcrepo = repository(ui, source)
134 srcrepo = repository(ui, source)
135 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
135 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
136 else:
136 else:
137 srcrepo = source.local()
137 srcrepo = source.local()
138 origsource = source = srcrepo.url()
138 origsource = source = srcrepo.url()
139 checkout = None
139 checkout = None
140
140
141 sharedpath = srcrepo.sharedpath # if our source is already sharing
141 sharedpath = srcrepo.sharedpath # if our source is already sharing
142
142
143 root = os.path.realpath(dest)
143 root = os.path.realpath(dest)
144 roothg = os.path.join(root, '.hg')
144 roothg = os.path.join(root, '.hg')
145
145
146 if os.path.exists(roothg):
146 if os.path.exists(roothg):
147 raise util.Abort(_('destination already exists'))
147 raise util.Abort(_('destination already exists'))
148
148
149 if not os.path.isdir(root):
149 if not os.path.isdir(root):
150 os.mkdir(root)
150 os.mkdir(root)
151 util.makedir(roothg, notindexed=True)
151 util.makedir(roothg, notindexed=True)
152
152
153 requirements = ''
153 requirements = ''
154 try:
154 try:
155 requirements = srcrepo.opener.read('requires')
155 requirements = srcrepo.opener.read('requires')
156 except IOError, inst:
156 except IOError, inst:
157 if inst.errno != errno.ENOENT:
157 if inst.errno != errno.ENOENT:
158 raise
158 raise
159
159
160 requirements += 'shared\n'
160 requirements += 'shared\n'
161 util.writefile(os.path.join(roothg, 'requires'), requirements)
161 util.writefile(os.path.join(roothg, 'requires'), requirements)
162 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
162 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
163
163
164 r = repository(ui, root)
164 r = repository(ui, root)
165
165
166 default = srcrepo.ui.config('paths', 'default')
166 default = srcrepo.ui.config('paths', 'default')
167 if default:
167 if default:
168 fp = r.opener("hgrc", "w", text=True)
168 fp = r.opener("hgrc", "w", text=True)
169 fp.write("[paths]\n")
169 fp.write("[paths]\n")
170 fp.write("default = %s\n" % default)
170 fp.write("default = %s\n" % default)
171 fp.close()
171 fp.close()
172
172
173 if update:
173 if update:
174 r.ui.status(_("updating working directory\n"))
174 r.ui.status(_("updating working directory\n"))
175 if update is not True:
175 if update is not True:
176 checkout = update
176 checkout = update
177 for test in (checkout, 'default', 'tip'):
177 for test in (checkout, 'default', 'tip'):
178 if test is None:
178 if test is None:
179 continue
179 continue
180 try:
180 try:
181 uprev = r.lookup(test)
181 uprev = r.lookup(test)
182 break
182 break
183 except error.RepoLookupError:
183 except error.RepoLookupError:
184 continue
184 continue
185 _update(r, uprev)
185 _update(r, uprev)
186
186
187 def copystore(ui, srcrepo, destpath):
187 def copystore(ui, srcrepo, destpath):
188 '''copy files from store of srcrepo in destpath
188 '''copy files from store of srcrepo in destpath
189
189
190 returns destlock
190 returns destlock
191 '''
191 '''
192 destlock = None
192 destlock = None
193 try:
193 try:
194 hardlink = None
194 hardlink = None
195 num = 0
195 num = 0
196 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
196 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
197 for f in srcrepo.store.copylist():
197 for f in srcrepo.store.copylist():
198 if srcpublishing and f.endswith('phaseroots'):
198 if srcpublishing and f.endswith('phaseroots'):
199 continue
199 continue
200 src = os.path.join(srcrepo.sharedpath, f)
200 src = os.path.join(srcrepo.sharedpath, f)
201 dst = os.path.join(destpath, f)
201 dst = os.path.join(destpath, f)
202 dstbase = os.path.dirname(dst)
202 dstbase = os.path.dirname(dst)
203 if dstbase and not os.path.exists(dstbase):
203 if dstbase and not os.path.exists(dstbase):
204 os.mkdir(dstbase)
204 os.mkdir(dstbase)
205 if os.path.exists(src):
205 if os.path.exists(src):
206 if dst.endswith('data'):
206 if dst.endswith('data'):
207 # lock to avoid premature writing to the target
207 # lock to avoid premature writing to the target
208 destlock = lock.lock(os.path.join(dstbase, "lock"))
208 destlock = lock.lock(os.path.join(dstbase, "lock"))
209 hardlink, n = util.copyfiles(src, dst, hardlink)
209 hardlink, n = util.copyfiles(src, dst, hardlink)
210 num += n
210 num += n
211 if hardlink:
211 if hardlink:
212 ui.debug("linked %d files\n" % num)
212 ui.debug("linked %d files\n" % num)
213 else:
213 else:
214 ui.debug("copied %d files\n" % num)
214 ui.debug("copied %d files\n" % num)
215 return destlock
215 return destlock
216 except: # re-raises
216 except: # re-raises
217 release(destlock)
217 release(destlock)
218 raise
218 raise
219
219
220 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
220 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
221 update=True, stream=False, branch=None):
221 update=True, stream=False, branch=None):
222 """Make a copy of an existing repository.
222 """Make a copy of an existing repository.
223
223
224 Create a copy of an existing repository in a new directory. The
224 Create a copy of an existing repository in a new directory. The
225 source and destination are URLs, as passed to the repository
225 source and destination are URLs, as passed to the repository
226 function. Returns a pair of repository peers, the source and
226 function. Returns a pair of repository peers, the source and
227 newly created destination.
227 newly created destination.
228
228
229 The location of the source is added to the new repository's
229 The location of the source is added to the new repository's
230 .hg/hgrc file, as the default to be used for future pulls and
230 .hg/hgrc file, as the default to be used for future pulls and
231 pushes.
231 pushes.
232
232
233 If an exception is raised, the partly cloned/updated destination
233 If an exception is raised, the partly cloned/updated destination
234 repository will be deleted.
234 repository will be deleted.
235
235
236 Arguments:
236 Arguments:
237
237
238 source: repository object or URL
238 source: repository object or URL
239
239
240 dest: URL of destination repository to create (defaults to base
240 dest: URL of destination repository to create (defaults to base
241 name of source repository)
241 name of source repository)
242
242
243 pull: always pull from source repository, even in local case
243 pull: always pull from source repository, even in local case
244
244
245 stream: stream raw data uncompressed from repository (fast over
245 stream: stream raw data uncompressed from repository (fast over
246 LAN, slow over WAN)
246 LAN, slow over WAN)
247
247
248 rev: revision to clone up to (implies pull=True)
248 rev: revision to clone up to (implies pull=True)
249
249
250 update: update working directory after clone completes, if
250 update: update working directory after clone completes, if
251 destination is local repository (True means update to default rev,
251 destination is local repository (True means update to default rev,
252 anything else is treated as a revision)
252 anything else is treated as a revision)
253
253
254 branch: branches to clone
254 branch: branches to clone
255 """
255 """
256
256
257 if isinstance(source, str):
257 if isinstance(source, str):
258 origsource = ui.expandpath(source)
258 origsource = ui.expandpath(source)
259 source, branch = parseurl(origsource, branch)
259 source, branch = parseurl(origsource, branch)
260 srcpeer = peer(ui, peeropts, source)
260 srcpeer = peer(ui, peeropts, source)
261 else:
261 else:
262 srcpeer = source.peer() # in case we were called with a localrepo
262 srcpeer = source.peer() # in case we were called with a localrepo
263 branch = (None, branch or [])
263 branch = (None, branch or [])
264 origsource = source = srcpeer.url()
264 origsource = source = srcpeer.url()
265 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
265 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
266
266
267 if dest is None:
267 if dest is None:
268 dest = defaultdest(source)
268 dest = defaultdest(source)
269 ui.status(_("destination directory: %s\n") % dest)
269 ui.status(_("destination directory: %s\n") % dest)
270 else:
270 else:
271 dest = ui.expandpath(dest)
271 dest = ui.expandpath(dest)
272
272
273 dest = util.urllocalpath(dest)
273 dest = util.urllocalpath(dest)
274 source = util.urllocalpath(source)
274 source = util.urllocalpath(source)
275
275
276 if not dest:
276 if not dest:
277 raise util.Abort(_("empty destination path is not valid"))
277 raise util.Abort(_("empty destination path is not valid"))
278 if os.path.exists(dest):
278 if os.path.exists(dest):
279 if not os.path.isdir(dest):
279 if not os.path.isdir(dest):
280 raise util.Abort(_("destination '%s' already exists") % dest)
280 raise util.Abort(_("destination '%s' already exists") % dest)
281 elif os.listdir(dest):
281 elif os.listdir(dest):
282 raise util.Abort(_("destination '%s' is not empty") % dest)
282 raise util.Abort(_("destination '%s' is not empty") % dest)
283
283
284 class DirCleanup(object):
284 class DirCleanup(object):
285 def __init__(self, dir_):
285 def __init__(self, dir_):
286 self.rmtree = shutil.rmtree
286 self.rmtree = shutil.rmtree
287 self.dir_ = dir_
287 self.dir_ = dir_
288 def close(self):
288 def close(self):
289 self.dir_ = None
289 self.dir_ = None
290 def cleanup(self):
290 def cleanup(self):
291 if self.dir_:
291 if self.dir_:
292 self.rmtree(self.dir_, True)
292 self.rmtree(self.dir_, True)
293
293
294 srclock = destlock = dircleanup = None
294 srclock = destlock = dircleanup = None
295 srcrepo = srcpeer.local()
295 srcrepo = srcpeer.local()
296 try:
296 try:
297 abspath = origsource
297 abspath = origsource
298 if islocal(origsource):
298 if islocal(origsource):
299 abspath = os.path.abspath(util.urllocalpath(origsource))
299 abspath = os.path.abspath(util.urllocalpath(origsource))
300
300
301 if islocal(dest):
301 if islocal(dest):
302 dircleanup = DirCleanup(dest)
302 dircleanup = DirCleanup(dest)
303
303
304 copy = False
304 copy = False
305 if (srcrepo and srcrepo.cancopy() and islocal(dest)
305 if (srcrepo and srcrepo.cancopy() and islocal(dest)
306 and not phases.hassecret(srcrepo)):
306 and not phases.hassecret(srcrepo)):
307 copy = not pull and not rev
307 copy = not pull and not rev
308
308
309 if copy:
309 if copy:
310 try:
310 try:
311 # we use a lock here because if we race with commit, we
311 # we use a lock here because if we race with commit, we
312 # can end up with extra data in the cloned revlogs that's
312 # can end up with extra data in the cloned revlogs that's
313 # not pointed to by changesets, thus causing verify to
313 # not pointed to by changesets, thus causing verify to
314 # fail
314 # fail
315 srclock = srcrepo.lock(wait=False)
315 srclock = srcrepo.lock(wait=False)
316 except error.LockError:
316 except error.LockError:
317 copy = False
317 copy = False
318
318
319 if copy:
319 if copy:
320 srcrepo.hook('preoutgoing', throw=True, source='clone')
320 srcrepo.hook('preoutgoing', throw=True, source='clone')
321 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
321 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
322 if not os.path.exists(dest):
322 if not os.path.exists(dest):
323 os.mkdir(dest)
323 os.mkdir(dest)
324 else:
324 else:
325 # only clean up directories we create ourselves
325 # only clean up directories we create ourselves
326 dircleanup.dir_ = hgdir
326 dircleanup.dir_ = hgdir
327 try:
327 try:
328 destpath = hgdir
328 destpath = hgdir
329 util.makedir(destpath, notindexed=True)
329 util.makedir(destpath, notindexed=True)
330 except OSError, inst:
330 except OSError, inst:
331 if inst.errno == errno.EEXIST:
331 if inst.errno == errno.EEXIST:
332 dircleanup.close()
332 dircleanup.close()
333 raise util.Abort(_("destination '%s' already exists")
333 raise util.Abort(_("destination '%s' already exists")
334 % dest)
334 % dest)
335 raise
335 raise
336
336
337 destlock = copystore(ui, srcrepo, destpath)
337 destlock = copystore(ui, srcrepo, destpath)
338
338
339 # we need to re-init the repo after manually copying the data
339 # we need to re-init the repo after manually copying the data
340 # into it
340 # into it
341 destpeer = peer(ui, peeropts, dest)
341 destpeer = peer(ui, peeropts, dest)
342 srcrepo.hook('outgoing', source='clone',
342 srcrepo.hook('outgoing', source='clone',
343 node=node.hex(node.nullid))
343 node=node.hex(node.nullid))
344 else:
344 else:
345 try:
345 try:
346 destpeer = peer(ui, peeropts, dest, create=True)
346 destpeer = peer(ui, peeropts, dest, create=True)
347 except OSError, inst:
347 except OSError, inst:
348 if inst.errno == errno.EEXIST:
348 if inst.errno == errno.EEXIST:
349 dircleanup.close()
349 dircleanup.close()
350 raise util.Abort(_("destination '%s' already exists")
350 raise util.Abort(_("destination '%s' already exists")
351 % dest)
351 % dest)
352 raise
352 raise
353
353
354 revs = None
354 revs = None
355 if rev:
355 if rev:
356 if not srcpeer.capable('lookup'):
356 if not srcpeer.capable('lookup'):
357 raise util.Abort(_("src repository does not support "
357 raise util.Abort(_("src repository does not support "
358 "revision lookup and so doesn't "
358 "revision lookup and so doesn't "
359 "support clone by revision"))
359 "support clone by revision"))
360 revs = [srcpeer.lookup(r) for r in rev]
360 revs = [srcpeer.lookup(r) for r in rev]
361 checkout = revs[0]
361 checkout = revs[0]
362 if destpeer.local():
362 if destpeer.local():
363 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
363 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
364 elif srcrepo:
364 elif srcrepo:
365 srcrepo.push(destpeer, revs=revs)
365 srcrepo.push(destpeer, revs=revs)
366 else:
366 else:
367 raise util.Abort(_("clone from remote to remote not supported"))
367 raise util.Abort(_("clone from remote to remote not supported"))
368
368
369 if dircleanup:
369 if dircleanup:
370 dircleanup.close()
370 dircleanup.close()
371
371
372 # clone all bookmarks except divergent ones
372 # clone all bookmarks except divergent ones
373 destrepo = destpeer.local()
373 destrepo = destpeer.local()
374 if destrepo and srcpeer.capable("pushkey"):
374 if destrepo and srcpeer.capable("pushkey"):
375 rb = srcpeer.listkeys('bookmarks')
375 rb = srcpeer.listkeys('bookmarks')
376 for k, n in rb.iteritems():
376 for k, n in rb.iteritems():
377 try:
377 try:
378 m = destrepo.lookup(n)
378 m = destrepo.lookup(n)
379 destrepo._bookmarks[k] = m
379 destrepo._bookmarks[k] = m
380 except error.RepoLookupError:
380 except error.RepoLookupError:
381 pass
381 pass
382 if rb:
382 if rb:
383 bookmarks.write(destrepo)
383 bookmarks.write(destrepo)
384 elif srcrepo and destpeer.capable("pushkey"):
384 elif srcrepo and destpeer.capable("pushkey"):
385 for k, n in srcrepo._bookmarks.iteritems():
385 for k, n in srcrepo._bookmarks.iteritems():
386 destpeer.pushkey('bookmarks', k, '', hex(n))
386 destpeer.pushkey('bookmarks', k, '', hex(n))
387
387
388 if destrepo:
388 if destrepo:
389 fp = destrepo.opener("hgrc", "w", text=True)
389 fp = destrepo.opener("hgrc", "w", text=True)
390 fp.write("[paths]\n")
390 fp.write("[paths]\n")
391 u = util.url(abspath)
391 u = util.url(abspath)
392 u.passwd = None
392 u.passwd = None
393 defaulturl = str(u)
393 defaulturl = str(u)
394 fp.write("default = %s\n" % defaulturl)
394 fp.write("default = %s\n" % defaulturl)
395 fp.close()
395 fp.close()
396
396
397 destrepo.ui.setconfig('paths', 'default', defaulturl)
397 destrepo.ui.setconfig('paths', 'default', defaulturl)
398
398
399 if update:
399 if update:
400 if update is not True:
400 if update is not True:
401 checkout = srcpeer.lookup(update)
401 checkout = srcpeer.lookup(update)
402 for test in (checkout, 'default', 'tip'):
402 for test in (checkout, 'default', 'tip'):
403 if test is None:
403 if test is None:
404 continue
404 continue
405 try:
405 try:
406 uprev = destrepo.lookup(test)
406 uprev = destrepo.lookup(test)
407 break
407 break
408 except error.RepoLookupError:
408 except error.RepoLookupError:
409 continue
409 continue
410 bn = destrepo[uprev].branch()
410 bn = destrepo[uprev].branch()
411 destrepo.ui.status(_("updating to branch %s\n") % bn)
411 destrepo.ui.status(_("updating to branch %s\n") % bn)
412 _update(destrepo, uprev)
412 _update(destrepo, uprev)
413 if update in destrepo._bookmarks:
414 bookmarks.setcurrent(destrepo, update)
413
415
414 return srcpeer, destpeer
416 return srcpeer, destpeer
415 finally:
417 finally:
416 release(srclock, destlock)
418 release(srclock, destlock)
417 if dircleanup is not None:
419 if dircleanup is not None:
418 dircleanup.cleanup()
420 dircleanup.cleanup()
419 if srcpeer is not None:
421 if srcpeer is not None:
420 srcpeer.close()
422 srcpeer.close()
421
423
422 def _showstats(repo, stats):
424 def _showstats(repo, stats):
423 repo.ui.status(_("%d files updated, %d files merged, "
425 repo.ui.status(_("%d files updated, %d files merged, "
424 "%d files removed, %d files unresolved\n") % stats)
426 "%d files removed, %d files unresolved\n") % stats)
425
427
426 def update(repo, node):
428 def update(repo, node):
427 """update the working directory to node, merging linear changes"""
429 """update the working directory to node, merging linear changes"""
428 stats = mergemod.update(repo, node, False, False, None)
430 stats = mergemod.update(repo, node, False, False, None)
429 _showstats(repo, stats)
431 _showstats(repo, stats)
430 if stats[3]:
432 if stats[3]:
431 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
433 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
432 return stats[3] > 0
434 return stats[3] > 0
433
435
434 # naming conflict in clone()
436 # naming conflict in clone()
435 _update = update
437 _update = update
436
438
437 def clean(repo, node, show_stats=True):
439 def clean(repo, node, show_stats=True):
438 """forcibly switch the working directory to node, clobbering changes"""
440 """forcibly switch the working directory to node, clobbering changes"""
439 stats = mergemod.update(repo, node, False, True, None)
441 stats = mergemod.update(repo, node, False, True, None)
440 if show_stats:
442 if show_stats:
441 _showstats(repo, stats)
443 _showstats(repo, stats)
442 return stats[3] > 0
444 return stats[3] > 0
443
445
444 def merge(repo, node, force=None, remind=True):
446 def merge(repo, node, force=None, remind=True):
445 """Branch merge with node, resolving changes. Return true if any
447 """Branch merge with node, resolving changes. Return true if any
446 unresolved conflicts."""
448 unresolved conflicts."""
447 stats = mergemod.update(repo, node, True, force, False)
449 stats = mergemod.update(repo, node, True, force, False)
448 _showstats(repo, stats)
450 _showstats(repo, stats)
449 if stats[3]:
451 if stats[3]:
450 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
452 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
451 "or 'hg update -C .' to abandon\n"))
453 "or 'hg update -C .' to abandon\n"))
452 elif remind:
454 elif remind:
453 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
455 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
454 return stats[3] > 0
456 return stats[3] > 0
455
457
456 def _incoming(displaychlist, subreporecurse, ui, repo, source,
458 def _incoming(displaychlist, subreporecurse, ui, repo, source,
457 opts, buffered=False):
459 opts, buffered=False):
458 """
460 """
459 Helper for incoming / gincoming.
461 Helper for incoming / gincoming.
460 displaychlist gets called with
462 displaychlist gets called with
461 (remoterepo, incomingchangesetlist, displayer) parameters,
463 (remoterepo, incomingchangesetlist, displayer) parameters,
462 and is supposed to contain only code that can't be unified.
464 and is supposed to contain only code that can't be unified.
463 """
465 """
464 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
466 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
465 other = peer(repo, opts, source)
467 other = peer(repo, opts, source)
466 ui.status(_('comparing with %s\n') % util.hidepassword(source))
468 ui.status(_('comparing with %s\n') % util.hidepassword(source))
467 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
469 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
468
470
469 if revs:
471 if revs:
470 revs = [other.lookup(rev) for rev in revs]
472 revs = [other.lookup(rev) for rev in revs]
471 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
473 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
472 revs, opts["bundle"], opts["force"])
474 revs, opts["bundle"], opts["force"])
473 try:
475 try:
474 if not chlist:
476 if not chlist:
475 ui.status(_("no changes found\n"))
477 ui.status(_("no changes found\n"))
476 return subreporecurse()
478 return subreporecurse()
477
479
478 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
480 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
479
481
480 # XXX once graphlog extension makes it into core,
482 # XXX once graphlog extension makes it into core,
481 # should be replaced by a if graph/else
483 # should be replaced by a if graph/else
482 displaychlist(other, chlist, displayer)
484 displaychlist(other, chlist, displayer)
483
485
484 displayer.close()
486 displayer.close()
485 finally:
487 finally:
486 cleanupfn()
488 cleanupfn()
487 subreporecurse()
489 subreporecurse()
488 return 0 # exit code is zero since we found incoming changes
490 return 0 # exit code is zero since we found incoming changes
489
491
490 def incoming(ui, repo, source, opts):
492 def incoming(ui, repo, source, opts):
491 def subreporecurse():
493 def subreporecurse():
492 ret = 1
494 ret = 1
493 if opts.get('subrepos'):
495 if opts.get('subrepos'):
494 ctx = repo[None]
496 ctx = repo[None]
495 for subpath in sorted(ctx.substate):
497 for subpath in sorted(ctx.substate):
496 sub = ctx.sub(subpath)
498 sub = ctx.sub(subpath)
497 ret = min(ret, sub.incoming(ui, source, opts))
499 ret = min(ret, sub.incoming(ui, source, opts))
498 return ret
500 return ret
499
501
500 def display(other, chlist, displayer):
502 def display(other, chlist, displayer):
501 limit = cmdutil.loglimit(opts)
503 limit = cmdutil.loglimit(opts)
502 if opts.get('newest_first'):
504 if opts.get('newest_first'):
503 chlist.reverse()
505 chlist.reverse()
504 count = 0
506 count = 0
505 for n in chlist:
507 for n in chlist:
506 if limit is not None and count >= limit:
508 if limit is not None and count >= limit:
507 break
509 break
508 parents = [p for p in other.changelog.parents(n) if p != nullid]
510 parents = [p for p in other.changelog.parents(n) if p != nullid]
509 if opts.get('no_merges') and len(parents) == 2:
511 if opts.get('no_merges') and len(parents) == 2:
510 continue
512 continue
511 count += 1
513 count += 1
512 displayer.show(other[n])
514 displayer.show(other[n])
513 return _incoming(display, subreporecurse, ui, repo, source, opts)
515 return _incoming(display, subreporecurse, ui, repo, source, opts)
514
516
515 def _outgoing(ui, repo, dest, opts):
517 def _outgoing(ui, repo, dest, opts):
516 dest = ui.expandpath(dest or 'default-push', dest or 'default')
518 dest = ui.expandpath(dest or 'default-push', dest or 'default')
517 dest, branches = parseurl(dest, opts.get('branch'))
519 dest, branches = parseurl(dest, opts.get('branch'))
518 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
520 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
519 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
521 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
520 if revs:
522 if revs:
521 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
523 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
522
524
523 other = peer(repo, opts, dest)
525 other = peer(repo, opts, dest)
524 outgoing = discovery.findcommonoutgoing(repo, other, revs,
526 outgoing = discovery.findcommonoutgoing(repo, other, revs,
525 force=opts.get('force'))
527 force=opts.get('force'))
526 o = outgoing.missing
528 o = outgoing.missing
527 if not o:
529 if not o:
528 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
530 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
529 return None
531 return None
530 return o
532 return o
531
533
532 def outgoing(ui, repo, dest, opts):
534 def outgoing(ui, repo, dest, opts):
533 def recurse():
535 def recurse():
534 ret = 1
536 ret = 1
535 if opts.get('subrepos'):
537 if opts.get('subrepos'):
536 ctx = repo[None]
538 ctx = repo[None]
537 for subpath in sorted(ctx.substate):
539 for subpath in sorted(ctx.substate):
538 sub = ctx.sub(subpath)
540 sub = ctx.sub(subpath)
539 ret = min(ret, sub.outgoing(ui, dest, opts))
541 ret = min(ret, sub.outgoing(ui, dest, opts))
540 return ret
542 return ret
541
543
542 limit = cmdutil.loglimit(opts)
544 limit = cmdutil.loglimit(opts)
543 o = _outgoing(ui, repo, dest, opts)
545 o = _outgoing(ui, repo, dest, opts)
544 if o is None:
546 if o is None:
545 return recurse()
547 return recurse()
546
548
547 if opts.get('newest_first'):
549 if opts.get('newest_first'):
548 o.reverse()
550 o.reverse()
549 displayer = cmdutil.show_changeset(ui, repo, opts)
551 displayer = cmdutil.show_changeset(ui, repo, opts)
550 count = 0
552 count = 0
551 for n in o:
553 for n in o:
552 if limit is not None and count >= limit:
554 if limit is not None and count >= limit:
553 break
555 break
554 parents = [p for p in repo.changelog.parents(n) if p != nullid]
556 parents = [p for p in repo.changelog.parents(n) if p != nullid]
555 if opts.get('no_merges') and len(parents) == 2:
557 if opts.get('no_merges') and len(parents) == 2:
556 continue
558 continue
557 count += 1
559 count += 1
558 displayer.show(repo[n])
560 displayer.show(repo[n])
559 displayer.close()
561 displayer.close()
560 recurse()
562 recurse()
561 return 0 # exit code is zero since we found outgoing changes
563 return 0 # exit code is zero since we found outgoing changes
562
564
563 def revert(repo, node, choose):
565 def revert(repo, node, choose):
564 """revert changes to revision in node without updating dirstate"""
566 """revert changes to revision in node without updating dirstate"""
565 return mergemod.update(repo, node, False, True, choose)[3] > 0
567 return mergemod.update(repo, node, False, True, choose)[3] > 0
566
568
567 def verify(repo):
569 def verify(repo):
568 """verify the consistency of a repository"""
570 """verify the consistency of a repository"""
569 return verifymod.verify(repo)
571 return verifymod.verify(repo)
570
572
571 def remoteui(src, opts):
573 def remoteui(src, opts):
572 'build a remote ui from ui or repo and opts'
574 'build a remote ui from ui or repo and opts'
573 if util.safehasattr(src, 'baseui'): # looks like a repository
575 if util.safehasattr(src, 'baseui'): # looks like a repository
574 dst = src.baseui.copy() # drop repo-specific config
576 dst = src.baseui.copy() # drop repo-specific config
575 src = src.ui # copy target options from repo
577 src = src.ui # copy target options from repo
576 else: # assume it's a global ui object
578 else: # assume it's a global ui object
577 dst = src.copy() # keep all global options
579 dst = src.copy() # keep all global options
578
580
579 # copy ssh-specific options
581 # copy ssh-specific options
580 for o in 'ssh', 'remotecmd':
582 for o in 'ssh', 'remotecmd':
581 v = opts.get(o) or src.config('ui', o)
583 v = opts.get(o) or src.config('ui', o)
582 if v:
584 if v:
583 dst.setconfig("ui", o, v)
585 dst.setconfig("ui", o, v)
584
586
585 # copy bundle-specific options
587 # copy bundle-specific options
586 r = src.config('bundle', 'mainreporoot')
588 r = src.config('bundle', 'mainreporoot')
587 if r:
589 if r:
588 dst.setconfig('bundle', 'mainreporoot', r)
590 dst.setconfig('bundle', 'mainreporoot', r)
589
591
590 # copy selected local settings to the remote ui
592 # copy selected local settings to the remote ui
591 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
593 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
592 for key, val in src.configitems(sect):
594 for key, val in src.configitems(sect):
593 dst.setconfig(sect, key, val)
595 dst.setconfig(sect, key, val)
594 v = src.config('web', 'cacerts')
596 v = src.config('web', 'cacerts')
595 if v:
597 if v:
596 dst.setconfig('web', 'cacerts', util.expandpath(v))
598 dst.setconfig('web', 'cacerts', util.expandpath(v))
597
599
598 return dst
600 return dst
@@ -1,472 +1,483 b''
1 $ hg init
1 $ hg init
2
2
3 no bookmarks
3 no bookmarks
4
4
5 $ hg bookmarks
5 $ hg bookmarks
6 no bookmarks set
6 no bookmarks set
7
7
8 bookmark rev -1
8 bookmark rev -1
9
9
10 $ hg bookmark X
10 $ hg bookmark X
11
11
12 list bookmarks
12 list bookmarks
13
13
14 $ hg bookmarks
14 $ hg bookmarks
15 * X -1:000000000000
15 * X -1:000000000000
16
16
17 list bookmarks with color
17 list bookmarks with color
18
18
19 $ hg --config extensions.color= --config color.mode=ansi \
19 $ hg --config extensions.color= --config color.mode=ansi \
20 > bookmarks --color=always
20 > bookmarks --color=always
21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
22
22
23 $ echo a > a
23 $ echo a > a
24 $ hg add a
24 $ hg add a
25 $ hg commit -m 0
25 $ hg commit -m 0
26
26
27 bookmark X moved to rev 0
27 bookmark X moved to rev 0
28
28
29 $ hg bookmarks
29 $ hg bookmarks
30 * X 0:f7b1eb17ad24
30 * X 0:f7b1eb17ad24
31
31
32 look up bookmark
32 look up bookmark
33
33
34 $ hg log -r X
34 $ hg log -r X
35 changeset: 0:f7b1eb17ad24
35 changeset: 0:f7b1eb17ad24
36 bookmark: X
36 bookmark: X
37 tag: tip
37 tag: tip
38 user: test
38 user: test
39 date: Thu Jan 01 00:00:00 1970 +0000
39 date: Thu Jan 01 00:00:00 1970 +0000
40 summary: 0
40 summary: 0
41
41
42
42
43 second bookmark for rev 0
43 second bookmark for rev 0
44
44
45 $ hg bookmark X2
45 $ hg bookmark X2
46
46
47 bookmark rev -1 again
47 bookmark rev -1 again
48
48
49 $ hg bookmark -r null Y
49 $ hg bookmark -r null Y
50
50
51 list bookmarks
51 list bookmarks
52
52
53 $ hg bookmarks
53 $ hg bookmarks
54 X 0:f7b1eb17ad24
54 X 0:f7b1eb17ad24
55 * X2 0:f7b1eb17ad24
55 * X2 0:f7b1eb17ad24
56 Y -1:000000000000
56 Y -1:000000000000
57
57
58 $ echo b > b
58 $ echo b > b
59 $ hg add b
59 $ hg add b
60 $ hg commit -m 1
60 $ hg commit -m 1
61
61
62 bookmarks revset
62 bookmarks revset
63
63
64 $ hg log -r 'bookmark()'
64 $ hg log -r 'bookmark()'
65 changeset: 0:f7b1eb17ad24
65 changeset: 0:f7b1eb17ad24
66 bookmark: X
66 bookmark: X
67 user: test
67 user: test
68 date: Thu Jan 01 00:00:00 1970 +0000
68 date: Thu Jan 01 00:00:00 1970 +0000
69 summary: 0
69 summary: 0
70
70
71 changeset: 1:925d80f479bb
71 changeset: 1:925d80f479bb
72 bookmark: X2
72 bookmark: X2
73 tag: tip
73 tag: tip
74 user: test
74 user: test
75 date: Thu Jan 01 00:00:00 1970 +0000
75 date: Thu Jan 01 00:00:00 1970 +0000
76 summary: 1
76 summary: 1
77
77
78 $ hg log -r 'bookmark(Y)'
78 $ hg log -r 'bookmark(Y)'
79 $ hg log -r 'bookmark(X2)'
79 $ hg log -r 'bookmark(X2)'
80 changeset: 1:925d80f479bb
80 changeset: 1:925d80f479bb
81 bookmark: X2
81 bookmark: X2
82 tag: tip
82 tag: tip
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:00 1970 +0000
84 date: Thu Jan 01 00:00:00 1970 +0000
85 summary: 1
85 summary: 1
86
86
87 $ hg log -r 'bookmark("re:X")'
87 $ hg log -r 'bookmark("re:X")'
88 changeset: 0:f7b1eb17ad24
88 changeset: 0:f7b1eb17ad24
89 bookmark: X
89 bookmark: X
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:00 1970 +0000
91 date: Thu Jan 01 00:00:00 1970 +0000
92 summary: 0
92 summary: 0
93
93
94 changeset: 1:925d80f479bb
94 changeset: 1:925d80f479bb
95 bookmark: X2
95 bookmark: X2
96 tag: tip
96 tag: tip
97 user: test
97 user: test
98 date: Thu Jan 01 00:00:00 1970 +0000
98 date: Thu Jan 01 00:00:00 1970 +0000
99 summary: 1
99 summary: 1
100
100
101 $ hg log -r 'bookmark(unknown)'
101 $ hg log -r 'bookmark(unknown)'
102 abort: bookmark 'unknown' does not exist
102 abort: bookmark 'unknown' does not exist
103 [255]
103 [255]
104
104
105 $ hg help revsets | grep 'bookmark('
105 $ hg help revsets | grep 'bookmark('
106 "bookmark([name])"
106 "bookmark([name])"
107
107
108 bookmarks X and X2 moved to rev 1, Y at rev -1
108 bookmarks X and X2 moved to rev 1, Y at rev -1
109
109
110 $ hg bookmarks
110 $ hg bookmarks
111 X 0:f7b1eb17ad24
111 X 0:f7b1eb17ad24
112 * X2 1:925d80f479bb
112 * X2 1:925d80f479bb
113 Y -1:000000000000
113 Y -1:000000000000
114
114
115 bookmark rev 0 again
115 bookmark rev 0 again
116
116
117 $ hg bookmark -r 0 Z
117 $ hg bookmark -r 0 Z
118
118
119 $ hg update X
119 $ hg update X
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 $ echo c > c
121 $ echo c > c
122 $ hg add c
122 $ hg add c
123 $ hg commit -m 2
123 $ hg commit -m 2
124 created new head
124 created new head
125
125
126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
127
127
128 $ hg bookmarks
128 $ hg bookmarks
129 * X 2:db815d6d32e6
129 * X 2:db815d6d32e6
130 X2 1:925d80f479bb
130 X2 1:925d80f479bb
131 Y -1:000000000000
131 Y -1:000000000000
132 Z 0:f7b1eb17ad24
132 Z 0:f7b1eb17ad24
133
133
134 rename nonexistent bookmark
134 rename nonexistent bookmark
135
135
136 $ hg bookmark -m A B
136 $ hg bookmark -m A B
137 abort: bookmark 'A' does not exist
137 abort: bookmark 'A' does not exist
138 [255]
138 [255]
139
139
140 rename to existent bookmark
140 rename to existent bookmark
141
141
142 $ hg bookmark -m X Y
142 $ hg bookmark -m X Y
143 abort: bookmark 'Y' already exists (use -f to force)
143 abort: bookmark 'Y' already exists (use -f to force)
144 [255]
144 [255]
145
145
146 force rename to existent bookmark
146 force rename to existent bookmark
147
147
148 $ hg bookmark -f -m X Y
148 $ hg bookmark -f -m X Y
149
149
150 list bookmarks
150 list bookmarks
151
151
152 $ hg bookmark
152 $ hg bookmark
153 X2 1:925d80f479bb
153 X2 1:925d80f479bb
154 * Y 2:db815d6d32e6
154 * Y 2:db815d6d32e6
155 Z 0:f7b1eb17ad24
155 Z 0:f7b1eb17ad24
156
156
157 bookmarks from a revset
157 bookmarks from a revset
158 $ hg bookmark -r '.^1' REVSET
158 $ hg bookmark -r '.^1' REVSET
159 $ hg bookmark -r ':tip' TIP
159 $ hg bookmark -r ':tip' TIP
160 $ hg bookmarks
160 $ hg bookmarks
161 REVSET 0:f7b1eb17ad24
161 REVSET 0:f7b1eb17ad24
162 * TIP 2:db815d6d32e6
162 * TIP 2:db815d6d32e6
163 X2 1:925d80f479bb
163 X2 1:925d80f479bb
164 Y 2:db815d6d32e6
164 Y 2:db815d6d32e6
165 Z 0:f7b1eb17ad24
165 Z 0:f7b1eb17ad24
166
166
167 $ hg bookmark -d REVSET
167 $ hg bookmark -d REVSET
168 $ hg bookmark -d TIP
168 $ hg bookmark -d TIP
169
169
170 rename without new name
170 rename without new name
171
171
172 $ hg bookmark -m Y
172 $ hg bookmark -m Y
173 abort: new bookmark name required
173 abort: new bookmark name required
174 [255]
174 [255]
175
175
176 delete without name
176 delete without name
177
177
178 $ hg bookmark -d
178 $ hg bookmark -d
179 abort: bookmark name required
179 abort: bookmark name required
180 [255]
180 [255]
181
181
182 delete nonexistent bookmark
182 delete nonexistent bookmark
183
183
184 $ hg bookmark -d A
184 $ hg bookmark -d A
185 abort: bookmark 'A' does not exist
185 abort: bookmark 'A' does not exist
186 [255]
186 [255]
187
187
188 bookmark name with spaces should be stripped
188 bookmark name with spaces should be stripped
189
189
190 $ hg bookmark ' x y '
190 $ hg bookmark ' x y '
191
191
192 list bookmarks
192 list bookmarks
193
193
194 $ hg bookmarks
194 $ hg bookmarks
195 X2 1:925d80f479bb
195 X2 1:925d80f479bb
196 Y 2:db815d6d32e6
196 Y 2:db815d6d32e6
197 Z 0:f7b1eb17ad24
197 Z 0:f7b1eb17ad24
198 * x y 2:db815d6d32e6
198 * x y 2:db815d6d32e6
199
199
200 look up stripped bookmark name
200 look up stripped bookmark name
201
201
202 $ hg log -r '"x y"'
202 $ hg log -r '"x y"'
203 changeset: 2:db815d6d32e6
203 changeset: 2:db815d6d32e6
204 bookmark: Y
204 bookmark: Y
205 bookmark: x y
205 bookmark: x y
206 tag: tip
206 tag: tip
207 parent: 0:f7b1eb17ad24
207 parent: 0:f7b1eb17ad24
208 user: test
208 user: test
209 date: Thu Jan 01 00:00:00 1970 +0000
209 date: Thu Jan 01 00:00:00 1970 +0000
210 summary: 2
210 summary: 2
211
211
212
212
213 reject bookmark name with newline
213 reject bookmark name with newline
214
214
215 $ hg bookmark '
215 $ hg bookmark '
216 > '
216 > '
217 abort: bookmark name cannot contain newlines
217 abort: bookmark name cannot contain newlines
218 [255]
218 [255]
219
219
220 bookmark with existing name
220 bookmark with existing name
221
221
222 $ hg bookmark Z
222 $ hg bookmark Z
223 abort: bookmark 'Z' already exists (use -f to force)
223 abort: bookmark 'Z' already exists (use -f to force)
224 [255]
224 [255]
225
225
226 force bookmark with existing name
226 force bookmark with existing name
227
227
228 $ hg bookmark -f Z
228 $ hg bookmark -f Z
229
229
230 list bookmarks
230 list bookmarks
231
231
232 $ hg bookmark
232 $ hg bookmark
233 X2 1:925d80f479bb
233 X2 1:925d80f479bb
234 Y 2:db815d6d32e6
234 Y 2:db815d6d32e6
235 * Z 2:db815d6d32e6
235 * Z 2:db815d6d32e6
236 x y 2:db815d6d32e6
236 x y 2:db815d6d32e6
237
237
238 revision but no bookmark name
238 revision but no bookmark name
239
239
240 $ hg bookmark -r .
240 $ hg bookmark -r .
241 abort: bookmark name required
241 abort: bookmark name required
242 [255]
242 [255]
243
243
244 bookmark name with whitespace only
244 bookmark name with whitespace only
245
245
246 $ hg bookmark ' '
246 $ hg bookmark ' '
247 abort: bookmark names cannot consist entirely of whitespace
247 abort: bookmark names cannot consist entirely of whitespace
248 [255]
248 [255]
249
249
250 invalid bookmark
250 invalid bookmark
251
251
252 $ hg bookmark 'foo:bar'
252 $ hg bookmark 'foo:bar'
253 abort: bookmark 'foo:bar' contains illegal character
253 abort: bookmark 'foo:bar' contains illegal character
254 [255]
254 [255]
255
255
256 the bookmark extension should be ignored now that it is part of core
256 the bookmark extension should be ignored now that it is part of core
257
257
258 $ echo "[extensions]" >> $HGRCPATH
258 $ echo "[extensions]" >> $HGRCPATH
259 $ echo "bookmarks=" >> $HGRCPATH
259 $ echo "bookmarks=" >> $HGRCPATH
260 $ hg bookmarks
260 $ hg bookmarks
261 X2 1:925d80f479bb
261 X2 1:925d80f479bb
262 Y 2:db815d6d32e6
262 Y 2:db815d6d32e6
263 * Z 2:db815d6d32e6
263 * Z 2:db815d6d32e6
264 x y 2:db815d6d32e6
264 x y 2:db815d6d32e6
265
265
266 test summary
266 test summary
267
267
268 $ hg summary
268 $ hg summary
269 parent: 2:db815d6d32e6 tip
269 parent: 2:db815d6d32e6 tip
270 2
270 2
271 branch: default
271 branch: default
272 bookmarks: *Z Y x y
272 bookmarks: *Z Y x y
273 commit: (clean)
273 commit: (clean)
274 update: 1 new changesets, 2 branch heads (merge)
274 update: 1 new changesets, 2 branch heads (merge)
275
275
276 test id
276 test id
277
277
278 $ hg id
278 $ hg id
279 db815d6d32e6 tip Y/Z/x y
279 db815d6d32e6 tip Y/Z/x y
280
280
281 test rollback
281 test rollback
282
282
283 $ echo foo > f1
283 $ echo foo > f1
284 $ hg ci -Amr
284 $ hg ci -Amr
285 adding f1
285 adding f1
286 $ hg bookmark -f Y -r 1
286 $ hg bookmark -f Y -r 1
287 $ hg bookmark -f Z -r 1
287 $ hg bookmark -f Z -r 1
288 $ hg rollback
288 $ hg rollback
289 repository tip rolled back to revision 2 (undo commit)
289 repository tip rolled back to revision 2 (undo commit)
290 working directory now based on revision 2
290 working directory now based on revision 2
291 $ hg bookmarks
291 $ hg bookmarks
292 X2 1:925d80f479bb
292 X2 1:925d80f479bb
293 Y 2:db815d6d32e6
293 Y 2:db815d6d32e6
294 * Z 2:db815d6d32e6
294 * Z 2:db815d6d32e6
295 x y 2:db815d6d32e6
295 x y 2:db815d6d32e6
296
296
297 test clone
297 test clone
298
298
299 $ hg bookmark -r 2 -i @
299 $ hg bookmark -r 2 -i @
300 $ hg bookmark -r 2 -i a@
300 $ hg bookmark -r 2 -i a@
301 $ hg bookmarks
301 $ hg bookmarks
302 @ 2:db815d6d32e6
302 @ 2:db815d6d32e6
303 X2 1:925d80f479bb
303 X2 1:925d80f479bb
304 Y 2:db815d6d32e6
304 Y 2:db815d6d32e6
305 * Z 2:db815d6d32e6
305 * Z 2:db815d6d32e6
306 a@ 2:db815d6d32e6
306 a@ 2:db815d6d32e6
307 x y 2:db815d6d32e6
307 x y 2:db815d6d32e6
308 $ hg clone . cloned-bookmarks
308 $ hg clone . cloned-bookmarks
309 updating to branch default
309 updating to branch default
310 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
310 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 $ hg -R cloned-bookmarks bookmarks
311 $ hg -R cloned-bookmarks bookmarks
312 @ 2:db815d6d32e6
312 @ 2:db815d6d32e6
313 X2 1:925d80f479bb
313 X2 1:925d80f479bb
314 Y 2:db815d6d32e6
314 Y 2:db815d6d32e6
315 Z 2:db815d6d32e6
315 Z 2:db815d6d32e6
316 a@ 2:db815d6d32e6
316 a@ 2:db815d6d32e6
317 x y 2:db815d6d32e6
317 x y 2:db815d6d32e6
318
318
319 test clone with pull protocol
319 test clone with pull protocol
320
320
321 $ hg clone --pull . cloned-bookmarks-pull
321 $ hg clone --pull . cloned-bookmarks-pull
322 requesting all changes
322 requesting all changes
323 adding changesets
323 adding changesets
324 adding manifests
324 adding manifests
325 adding file changes
325 adding file changes
326 added 3 changesets with 3 changes to 3 files (+1 heads)
326 added 3 changesets with 3 changes to 3 files (+1 heads)
327 updating to branch default
327 updating to branch default
328 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
328 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
329 $ hg -R cloned-bookmarks-pull bookmarks
329 $ hg -R cloned-bookmarks-pull bookmarks
330 @ 2:db815d6d32e6
330 @ 2:db815d6d32e6
331 X2 1:925d80f479bb
331 X2 1:925d80f479bb
332 Y 2:db815d6d32e6
332 Y 2:db815d6d32e6
333 Z 2:db815d6d32e6
333 Z 2:db815d6d32e6
334 a@ 2:db815d6d32e6
334 a@ 2:db815d6d32e6
335 x y 2:db815d6d32e6
335 x y 2:db815d6d32e6
336
336
337 $ hg bookmark -d @
337 $ hg bookmark -d @
338 $ hg bookmark -d a@
338 $ hg bookmark -d a@
339
339
340 test clone with a specific revision
340 test clone with a specific revision
341
341
342 $ hg clone -r 925d80 . cloned-bookmarks-rev
342 $ hg clone -r 925d80 . cloned-bookmarks-rev
343 adding changesets
343 adding changesets
344 adding manifests
344 adding manifests
345 adding file changes
345 adding file changes
346 added 2 changesets with 2 changes to 2 files
346 added 2 changesets with 2 changes to 2 files
347 updating to branch default
347 updating to branch default
348 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 $ hg -R cloned-bookmarks-rev bookmarks
349 $ hg -R cloned-bookmarks-rev bookmarks
350 X2 1:925d80f479bb
350 X2 1:925d80f479bb
351
351
352 test clone with update to a bookmark
353
354 $ hg clone -u Z . cloned-bookmarks-update
355 updating to branch default
356 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
357 $ hg -R cloned-bookmarks-update bookmarks
358 X2 1:925d80f479bb
359 Y 2:db815d6d32e6
360 * Z 2:db815d6d32e6
361 x y 2:db815d6d32e6
362
352 create bundle with two heads
363 create bundle with two heads
353
364
354 $ hg clone . tobundle
365 $ hg clone . tobundle
355 updating to branch default
366 updating to branch default
356 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
357 $ echo x > tobundle/x
368 $ echo x > tobundle/x
358 $ hg -R tobundle add tobundle/x
369 $ hg -R tobundle add tobundle/x
359 $ hg -R tobundle commit -m'x'
370 $ hg -R tobundle commit -m'x'
360 $ hg -R tobundle update -r -2
371 $ hg -R tobundle update -r -2
361 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
372 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
362 $ echo y > tobundle/y
373 $ echo y > tobundle/y
363 $ hg -R tobundle branch test
374 $ hg -R tobundle branch test
364 marked working directory as branch test
375 marked working directory as branch test
365 (branches are permanent and global, did you want a bookmark?)
376 (branches are permanent and global, did you want a bookmark?)
366 $ hg -R tobundle add tobundle/y
377 $ hg -R tobundle add tobundle/y
367 $ hg -R tobundle commit -m'y'
378 $ hg -R tobundle commit -m'y'
368 $ hg -R tobundle bundle tobundle.hg
379 $ hg -R tobundle bundle tobundle.hg
369 searching for changes
380 searching for changes
370 2 changesets found
381 2 changesets found
371 $ hg unbundle tobundle.hg
382 $ hg unbundle tobundle.hg
372 adding changesets
383 adding changesets
373 adding manifests
384 adding manifests
374 adding file changes
385 adding file changes
375 added 2 changesets with 2 changes to 2 files (+1 heads)
386 added 2 changesets with 2 changes to 2 files (+1 heads)
376 (run 'hg heads' to see heads, 'hg merge' to merge)
387 (run 'hg heads' to see heads, 'hg merge' to merge)
377 $ hg update
388 $ hg update
378 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
389 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 $ hg bookmarks
390 $ hg bookmarks
380 X2 1:925d80f479bb
391 X2 1:925d80f479bb
381 Y 2:db815d6d32e6
392 Y 2:db815d6d32e6
382 * Z 3:125c9a1d6df6
393 * Z 3:125c9a1d6df6
383 x y 2:db815d6d32e6
394 x y 2:db815d6d32e6
384
395
385 test wrongly formated bookmark
396 test wrongly formated bookmark
386
397
387 $ echo '' >> .hg/bookmarks
398 $ echo '' >> .hg/bookmarks
388 $ hg bookmarks
399 $ hg bookmarks
389 X2 1:925d80f479bb
400 X2 1:925d80f479bb
390 Y 2:db815d6d32e6
401 Y 2:db815d6d32e6
391 * Z 3:125c9a1d6df6
402 * Z 3:125c9a1d6df6
392 x y 2:db815d6d32e6
403 x y 2:db815d6d32e6
393 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
404 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
394 $ hg bookmarks
405 $ hg bookmarks
395 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
406 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
396 X2 1:925d80f479bb
407 X2 1:925d80f479bb
397 Y 2:db815d6d32e6
408 Y 2:db815d6d32e6
398 * Z 3:125c9a1d6df6
409 * Z 3:125c9a1d6df6
399 x y 2:db815d6d32e6
410 x y 2:db815d6d32e6
400
411
401 test missing revisions
412 test missing revisions
402
413
403 $ echo "925d80f479bc z" > .hg/bookmarks
414 $ echo "925d80f479bc z" > .hg/bookmarks
404 $ hg book
415 $ hg book
405 no bookmarks set
416 no bookmarks set
406
417
407 test stripping a non-checked-out but bookmarked revision
418 test stripping a non-checked-out but bookmarked revision
408
419
409 $ hg --config extensions.graphlog= log --graph
420 $ hg --config extensions.graphlog= log --graph
410 o changeset: 4:9ba5f110a0b3
421 o changeset: 4:9ba5f110a0b3
411 | branch: test
422 | branch: test
412 | tag: tip
423 | tag: tip
413 | parent: 2:db815d6d32e6
424 | parent: 2:db815d6d32e6
414 | user: test
425 | user: test
415 | date: Thu Jan 01 00:00:00 1970 +0000
426 | date: Thu Jan 01 00:00:00 1970 +0000
416 | summary: y
427 | summary: y
417 |
428 |
418 | @ changeset: 3:125c9a1d6df6
429 | @ changeset: 3:125c9a1d6df6
419 |/ user: test
430 |/ user: test
420 | date: Thu Jan 01 00:00:00 1970 +0000
431 | date: Thu Jan 01 00:00:00 1970 +0000
421 | summary: x
432 | summary: x
422 |
433 |
423 o changeset: 2:db815d6d32e6
434 o changeset: 2:db815d6d32e6
424 | parent: 0:f7b1eb17ad24
435 | parent: 0:f7b1eb17ad24
425 | user: test
436 | user: test
426 | date: Thu Jan 01 00:00:00 1970 +0000
437 | date: Thu Jan 01 00:00:00 1970 +0000
427 | summary: 2
438 | summary: 2
428 |
439 |
429 | o changeset: 1:925d80f479bb
440 | o changeset: 1:925d80f479bb
430 |/ user: test
441 |/ user: test
431 | date: Thu Jan 01 00:00:00 1970 +0000
442 | date: Thu Jan 01 00:00:00 1970 +0000
432 | summary: 1
443 | summary: 1
433 |
444 |
434 o changeset: 0:f7b1eb17ad24
445 o changeset: 0:f7b1eb17ad24
435 user: test
446 user: test
436 date: Thu Jan 01 00:00:00 1970 +0000
447 date: Thu Jan 01 00:00:00 1970 +0000
437 summary: 0
448 summary: 0
438
449
439 $ hg book should-end-on-two
450 $ hg book should-end-on-two
440 $ hg co --clean 4
451 $ hg co --clean 4
441 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
452 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
442 $ hg book four
453 $ hg book four
443 $ hg --config extensions.mq= strip 3
454 $ hg --config extensions.mq= strip 3
444 saved backup bundle to * (glob)
455 saved backup bundle to * (glob)
445 should-end-on-two should end up pointing to revision 2, as that's the
456 should-end-on-two should end up pointing to revision 2, as that's the
446 tipmost surviving ancestor of the stripped revision.
457 tipmost surviving ancestor of the stripped revision.
447 $ hg --config extensions.graphlog= log --graph
458 $ hg --config extensions.graphlog= log --graph
448 @ changeset: 3:9ba5f110a0b3
459 @ changeset: 3:9ba5f110a0b3
449 | branch: test
460 | branch: test
450 | bookmark: four
461 | bookmark: four
451 | tag: tip
462 | tag: tip
452 | user: test
463 | user: test
453 | date: Thu Jan 01 00:00:00 1970 +0000
464 | date: Thu Jan 01 00:00:00 1970 +0000
454 | summary: y
465 | summary: y
455 |
466 |
456 o changeset: 2:db815d6d32e6
467 o changeset: 2:db815d6d32e6
457 | bookmark: should-end-on-two
468 | bookmark: should-end-on-two
458 | parent: 0:f7b1eb17ad24
469 | parent: 0:f7b1eb17ad24
459 | user: test
470 | user: test
460 | date: Thu Jan 01 00:00:00 1970 +0000
471 | date: Thu Jan 01 00:00:00 1970 +0000
461 | summary: 2
472 | summary: 2
462 |
473 |
463 | o changeset: 1:925d80f479bb
474 | o changeset: 1:925d80f479bb
464 |/ user: test
475 |/ user: test
465 | date: Thu Jan 01 00:00:00 1970 +0000
476 | date: Thu Jan 01 00:00:00 1970 +0000
466 | summary: 1
477 | summary: 1
467 |
478 |
468 o changeset: 0:f7b1eb17ad24
479 o changeset: 0:f7b1eb17ad24
469 user: test
480 user: test
470 date: Thu Jan 01 00:00:00 1970 +0000
481 date: Thu Jan 01 00:00:00 1970 +0000
471 summary: 0
482 summary: 0
472
483
General Comments 0
You need to be logged in to leave comments. Login now