##// END OF EJS Templates
update: specify custom conflict markers for update (BC)...
Durham Goode -
r21537:1ab30e9b default
parent child Browse files
Show More
@@ -1,658 +1,659 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, unionrepo, httppeer, sshpeer, statichttprepo
12 import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo
13 import bookmarks, lock, util, extensions, error, node, scmutil, phases, url
13 import bookmarks, lock, util, extensions, error, node, scmutil, phases, url
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 'union': unionrepo,
67 'union': unionrepo,
68 'file': _local,
68 'file': _local,
69 'http': httppeer,
69 'http': httppeer,
70 'https': httppeer,
70 'https': httppeer,
71 'ssh': sshpeer,
71 'ssh': sshpeer,
72 'static-http': statichttprepo,
72 'static-http': statichttprepo,
73 }
73 }
74
74
75 def _peerlookup(path):
75 def _peerlookup(path):
76 u = util.url(path)
76 u = util.url(path)
77 scheme = u.scheme or 'file'
77 scheme = u.scheme or 'file'
78 thing = schemes.get(scheme) or schemes['file']
78 thing = schemes.get(scheme) or schemes['file']
79 try:
79 try:
80 return thing(path)
80 return thing(path)
81 except TypeError:
81 except TypeError:
82 return thing
82 return thing
83
83
84 def islocal(repo):
84 def islocal(repo):
85 '''return true if repo (or path pointing to repo) is local'''
85 '''return true if repo (or path pointing to repo) is local'''
86 if isinstance(repo, str):
86 if isinstance(repo, str):
87 try:
87 try:
88 return _peerlookup(repo).islocal(repo)
88 return _peerlookup(repo).islocal(repo)
89 except AttributeError:
89 except AttributeError:
90 return False
90 return False
91 return repo.local()
91 return repo.local()
92
92
93 def openpath(ui, path):
93 def openpath(ui, path):
94 '''open path with open if local, url.open if remote'''
94 '''open path with open if local, url.open if remote'''
95 pathurl = util.url(path, parsequery=False, parsefragment=False)
95 pathurl = util.url(path, parsequery=False, parsefragment=False)
96 if pathurl.islocal():
96 if pathurl.islocal():
97 return util.posixfile(pathurl.localpath(), 'rb')
97 return util.posixfile(pathurl.localpath(), 'rb')
98 else:
98 else:
99 return url.open(ui, path)
99 return url.open(ui, path)
100
100
101 # a list of (ui, repo) functions called for wire peer initialization
101 # a list of (ui, repo) functions called for wire peer initialization
102 wirepeersetupfuncs = []
102 wirepeersetupfuncs = []
103
103
104 def _peerorrepo(ui, path, create=False):
104 def _peerorrepo(ui, path, create=False):
105 """return a repository object for the specified path"""
105 """return a repository object for the specified path"""
106 obj = _peerlookup(path).instance(ui, path, create)
106 obj = _peerlookup(path).instance(ui, path, create)
107 ui = getattr(obj, "ui", ui)
107 ui = getattr(obj, "ui", ui)
108 for name, module in extensions.extensions(ui):
108 for name, module in extensions.extensions(ui):
109 hook = getattr(module, 'reposetup', None)
109 hook = getattr(module, 'reposetup', None)
110 if hook:
110 if hook:
111 hook(ui, obj)
111 hook(ui, obj)
112 if not obj.local():
112 if not obj.local():
113 for f in wirepeersetupfuncs:
113 for f in wirepeersetupfuncs:
114 f(ui, obj)
114 f(ui, obj)
115 return obj
115 return obj
116
116
117 def repository(ui, path='', create=False):
117 def repository(ui, path='', create=False):
118 """return a repository object for the specified path"""
118 """return a repository object for the specified path"""
119 peer = _peerorrepo(ui, path, create)
119 peer = _peerorrepo(ui, path, create)
120 repo = peer.local()
120 repo = peer.local()
121 if not repo:
121 if not repo:
122 raise util.Abort(_("repository '%s' is not local") %
122 raise util.Abort(_("repository '%s' is not local") %
123 (path or peer.url()))
123 (path or peer.url()))
124 return repo.filtered('visible')
124 return repo.filtered('visible')
125
125
126 def peer(uiorrepo, opts, path, create=False):
126 def peer(uiorrepo, opts, path, create=False):
127 '''return a repository peer for the specified path'''
127 '''return a repository peer for the specified path'''
128 rui = remoteui(uiorrepo, opts)
128 rui = remoteui(uiorrepo, opts)
129 return _peerorrepo(rui, path, create).peer()
129 return _peerorrepo(rui, path, create).peer()
130
130
131 def defaultdest(source):
131 def defaultdest(source):
132 '''return default destination of clone if none is given
132 '''return default destination of clone if none is given
133
133
134 >>> defaultdest('foo')
134 >>> defaultdest('foo')
135 'foo'
135 'foo'
136 >>> defaultdest('/foo/bar')
136 >>> defaultdest('/foo/bar')
137 'bar'
137 'bar'
138 >>> defaultdest('/')
138 >>> defaultdest('/')
139 ''
139 ''
140 >>> defaultdest('')
140 >>> defaultdest('')
141 ''
141 ''
142 >>> defaultdest('http://example.org/')
142 >>> defaultdest('http://example.org/')
143 ''
143 ''
144 >>> defaultdest('http://example.org/foo/')
144 >>> defaultdest('http://example.org/foo/')
145 'foo'
145 'foo'
146 '''
146 '''
147 path = util.url(source).path
147 path = util.url(source).path
148 if not path:
148 if not path:
149 return ''
149 return ''
150 return os.path.basename(os.path.normpath(path))
150 return os.path.basename(os.path.normpath(path))
151
151
152 def share(ui, source, dest=None, update=True):
152 def share(ui, source, dest=None, update=True):
153 '''create a shared repository'''
153 '''create a shared repository'''
154
154
155 if not islocal(source):
155 if not islocal(source):
156 raise util.Abort(_('can only share local repositories'))
156 raise util.Abort(_('can only share local repositories'))
157
157
158 if not dest:
158 if not dest:
159 dest = defaultdest(source)
159 dest = defaultdest(source)
160 else:
160 else:
161 dest = ui.expandpath(dest)
161 dest = ui.expandpath(dest)
162
162
163 if isinstance(source, str):
163 if isinstance(source, str):
164 origsource = ui.expandpath(source)
164 origsource = ui.expandpath(source)
165 source, branches = parseurl(origsource)
165 source, branches = parseurl(origsource)
166 srcrepo = repository(ui, source)
166 srcrepo = repository(ui, source)
167 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
167 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
168 else:
168 else:
169 srcrepo = source.local()
169 srcrepo = source.local()
170 origsource = source = srcrepo.url()
170 origsource = source = srcrepo.url()
171 checkout = None
171 checkout = None
172
172
173 sharedpath = srcrepo.sharedpath # if our source is already sharing
173 sharedpath = srcrepo.sharedpath # if our source is already sharing
174
174
175 root = os.path.realpath(dest)
175 root = os.path.realpath(dest)
176 roothg = os.path.join(root, '.hg')
176 roothg = os.path.join(root, '.hg')
177
177
178 if os.path.exists(roothg):
178 if os.path.exists(roothg):
179 raise util.Abort(_('destination already exists'))
179 raise util.Abort(_('destination already exists'))
180
180
181 if not os.path.isdir(root):
181 if not os.path.isdir(root):
182 os.mkdir(root)
182 os.mkdir(root)
183 util.makedir(roothg, notindexed=True)
183 util.makedir(roothg, notindexed=True)
184
184
185 requirements = ''
185 requirements = ''
186 try:
186 try:
187 requirements = srcrepo.opener.read('requires')
187 requirements = srcrepo.opener.read('requires')
188 except IOError, inst:
188 except IOError, inst:
189 if inst.errno != errno.ENOENT:
189 if inst.errno != errno.ENOENT:
190 raise
190 raise
191
191
192 requirements += 'shared\n'
192 requirements += 'shared\n'
193 util.writefile(os.path.join(roothg, 'requires'), requirements)
193 util.writefile(os.path.join(roothg, 'requires'), requirements)
194 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
194 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
195
195
196 r = repository(ui, root)
196 r = repository(ui, root)
197
197
198 default = srcrepo.ui.config('paths', 'default')
198 default = srcrepo.ui.config('paths', 'default')
199 if default:
199 if default:
200 fp = r.opener("hgrc", "w", text=True)
200 fp = r.opener("hgrc", "w", text=True)
201 fp.write("[paths]\n")
201 fp.write("[paths]\n")
202 fp.write("default = %s\n" % default)
202 fp.write("default = %s\n" % default)
203 fp.close()
203 fp.close()
204
204
205 if update:
205 if update:
206 r.ui.status(_("updating working directory\n"))
206 r.ui.status(_("updating working directory\n"))
207 if update is not True:
207 if update is not True:
208 checkout = update
208 checkout = update
209 for test in (checkout, 'default', 'tip'):
209 for test in (checkout, 'default', 'tip'):
210 if test is None:
210 if test is None:
211 continue
211 continue
212 try:
212 try:
213 uprev = r.lookup(test)
213 uprev = r.lookup(test)
214 break
214 break
215 except error.RepoLookupError:
215 except error.RepoLookupError:
216 continue
216 continue
217 _update(r, uprev)
217 _update(r, uprev)
218
218
219 def copystore(ui, srcrepo, destpath):
219 def copystore(ui, srcrepo, destpath):
220 '''copy files from store of srcrepo in destpath
220 '''copy files from store of srcrepo in destpath
221
221
222 returns destlock
222 returns destlock
223 '''
223 '''
224 destlock = None
224 destlock = None
225 try:
225 try:
226 hardlink = None
226 hardlink = None
227 num = 0
227 num = 0
228 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
228 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
229 srcvfs = scmutil.vfs(srcrepo.sharedpath)
229 srcvfs = scmutil.vfs(srcrepo.sharedpath)
230 dstvfs = scmutil.vfs(destpath)
230 dstvfs = scmutil.vfs(destpath)
231 for f in srcrepo.store.copylist():
231 for f in srcrepo.store.copylist():
232 if srcpublishing and f.endswith('phaseroots'):
232 if srcpublishing and f.endswith('phaseroots'):
233 continue
233 continue
234 dstbase = os.path.dirname(f)
234 dstbase = os.path.dirname(f)
235 if dstbase and not dstvfs.exists(dstbase):
235 if dstbase and not dstvfs.exists(dstbase):
236 dstvfs.mkdir(dstbase)
236 dstvfs.mkdir(dstbase)
237 if srcvfs.exists(f):
237 if srcvfs.exists(f):
238 if f.endswith('data'):
238 if f.endswith('data'):
239 # 'dstbase' may be empty (e.g. revlog format 0)
239 # 'dstbase' may be empty (e.g. revlog format 0)
240 lockfile = os.path.join(dstbase, "lock")
240 lockfile = os.path.join(dstbase, "lock")
241 # lock to avoid premature writing to the target
241 # lock to avoid premature writing to the target
242 destlock = lock.lock(dstvfs, lockfile)
242 destlock = lock.lock(dstvfs, lockfile)
243 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
243 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
244 hardlink)
244 hardlink)
245 num += n
245 num += n
246 if hardlink:
246 if hardlink:
247 ui.debug("linked %d files\n" % num)
247 ui.debug("linked %d files\n" % num)
248 else:
248 else:
249 ui.debug("copied %d files\n" % num)
249 ui.debug("copied %d files\n" % num)
250 return destlock
250 return destlock
251 except: # re-raises
251 except: # re-raises
252 release(destlock)
252 release(destlock)
253 raise
253 raise
254
254
255 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
255 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
256 update=True, stream=False, branch=None):
256 update=True, stream=False, branch=None):
257 """Make a copy of an existing repository.
257 """Make a copy of an existing repository.
258
258
259 Create a copy of an existing repository in a new directory. The
259 Create a copy of an existing repository in a new directory. The
260 source and destination are URLs, as passed to the repository
260 source and destination are URLs, as passed to the repository
261 function. Returns a pair of repository peers, the source and
261 function. Returns a pair of repository peers, the source and
262 newly created destination.
262 newly created destination.
263
263
264 The location of the source is added to the new repository's
264 The location of the source is added to the new repository's
265 .hg/hgrc file, as the default to be used for future pulls and
265 .hg/hgrc file, as the default to be used for future pulls and
266 pushes.
266 pushes.
267
267
268 If an exception is raised, the partly cloned/updated destination
268 If an exception is raised, the partly cloned/updated destination
269 repository will be deleted.
269 repository will be deleted.
270
270
271 Arguments:
271 Arguments:
272
272
273 source: repository object or URL
273 source: repository object or URL
274
274
275 dest: URL of destination repository to create (defaults to base
275 dest: URL of destination repository to create (defaults to base
276 name of source repository)
276 name of source repository)
277
277
278 pull: always pull from source repository, even in local case
278 pull: always pull from source repository, even in local case
279
279
280 stream: stream raw data uncompressed from repository (fast over
280 stream: stream raw data uncompressed from repository (fast over
281 LAN, slow over WAN)
281 LAN, slow over WAN)
282
282
283 rev: revision to clone up to (implies pull=True)
283 rev: revision to clone up to (implies pull=True)
284
284
285 update: update working directory after clone completes, if
285 update: update working directory after clone completes, if
286 destination is local repository (True means update to default rev,
286 destination is local repository (True means update to default rev,
287 anything else is treated as a revision)
287 anything else is treated as a revision)
288
288
289 branch: branches to clone
289 branch: branches to clone
290 """
290 """
291
291
292 if isinstance(source, str):
292 if isinstance(source, str):
293 origsource = ui.expandpath(source)
293 origsource = ui.expandpath(source)
294 source, branch = parseurl(origsource, branch)
294 source, branch = parseurl(origsource, branch)
295 srcpeer = peer(ui, peeropts, source)
295 srcpeer = peer(ui, peeropts, source)
296 else:
296 else:
297 srcpeer = source.peer() # in case we were called with a localrepo
297 srcpeer = source.peer() # in case we were called with a localrepo
298 branch = (None, branch or [])
298 branch = (None, branch or [])
299 origsource = source = srcpeer.url()
299 origsource = source = srcpeer.url()
300 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
300 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
301
301
302 if dest is None:
302 if dest is None:
303 dest = defaultdest(source)
303 dest = defaultdest(source)
304 if dest:
304 if dest:
305 ui.status(_("destination directory: %s\n") % dest)
305 ui.status(_("destination directory: %s\n") % dest)
306 else:
306 else:
307 dest = ui.expandpath(dest)
307 dest = ui.expandpath(dest)
308
308
309 dest = util.urllocalpath(dest)
309 dest = util.urllocalpath(dest)
310 source = util.urllocalpath(source)
310 source = util.urllocalpath(source)
311
311
312 if not dest:
312 if not dest:
313 raise util.Abort(_("empty destination path is not valid"))
313 raise util.Abort(_("empty destination path is not valid"))
314 if os.path.exists(dest):
314 if os.path.exists(dest):
315 if not os.path.isdir(dest):
315 if not os.path.isdir(dest):
316 raise util.Abort(_("destination '%s' already exists") % dest)
316 raise util.Abort(_("destination '%s' already exists") % dest)
317 elif os.listdir(dest):
317 elif os.listdir(dest):
318 raise util.Abort(_("destination '%s' is not empty") % dest)
318 raise util.Abort(_("destination '%s' is not empty") % dest)
319
319
320 srclock = destlock = cleandir = None
320 srclock = destlock = cleandir = None
321 srcrepo = srcpeer.local()
321 srcrepo = srcpeer.local()
322 try:
322 try:
323 abspath = origsource
323 abspath = origsource
324 if islocal(origsource):
324 if islocal(origsource):
325 abspath = os.path.abspath(util.urllocalpath(origsource))
325 abspath = os.path.abspath(util.urllocalpath(origsource))
326
326
327 if islocal(dest):
327 if islocal(dest):
328 cleandir = dest
328 cleandir = dest
329
329
330 copy = False
330 copy = False
331 if (srcrepo and srcrepo.cancopy() and islocal(dest)
331 if (srcrepo and srcrepo.cancopy() and islocal(dest)
332 and not phases.hassecret(srcrepo)):
332 and not phases.hassecret(srcrepo)):
333 copy = not pull and not rev
333 copy = not pull and not rev
334
334
335 if copy:
335 if copy:
336 try:
336 try:
337 # we use a lock here because if we race with commit, we
337 # we use a lock here because if we race with commit, we
338 # can end up with extra data in the cloned revlogs that's
338 # can end up with extra data in the cloned revlogs that's
339 # not pointed to by changesets, thus causing verify to
339 # not pointed to by changesets, thus causing verify to
340 # fail
340 # fail
341 srclock = srcrepo.lock(wait=False)
341 srclock = srcrepo.lock(wait=False)
342 except error.LockError:
342 except error.LockError:
343 copy = False
343 copy = False
344
344
345 if copy:
345 if copy:
346 srcrepo.hook('preoutgoing', throw=True, source='clone')
346 srcrepo.hook('preoutgoing', throw=True, source='clone')
347 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
347 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
348 if not os.path.exists(dest):
348 if not os.path.exists(dest):
349 os.mkdir(dest)
349 os.mkdir(dest)
350 else:
350 else:
351 # only clean up directories we create ourselves
351 # only clean up directories we create ourselves
352 cleandir = hgdir
352 cleandir = hgdir
353 try:
353 try:
354 destpath = hgdir
354 destpath = hgdir
355 util.makedir(destpath, notindexed=True)
355 util.makedir(destpath, notindexed=True)
356 except OSError, inst:
356 except OSError, inst:
357 if inst.errno == errno.EEXIST:
357 if inst.errno == errno.EEXIST:
358 cleandir = None
358 cleandir = None
359 raise util.Abort(_("destination '%s' already exists")
359 raise util.Abort(_("destination '%s' already exists")
360 % dest)
360 % dest)
361 raise
361 raise
362
362
363 destlock = copystore(ui, srcrepo, destpath)
363 destlock = copystore(ui, srcrepo, destpath)
364
364
365 # Recomputing branch cache might be slow on big repos,
365 # Recomputing branch cache might be slow on big repos,
366 # so just copy it
366 # so just copy it
367 dstcachedir = os.path.join(destpath, 'cache')
367 dstcachedir = os.path.join(destpath, 'cache')
368 srcbranchcache = srcrepo.sjoin('cache/branch2')
368 srcbranchcache = srcrepo.sjoin('cache/branch2')
369 dstbranchcache = os.path.join(dstcachedir, 'branch2')
369 dstbranchcache = os.path.join(dstcachedir, 'branch2')
370 if os.path.exists(srcbranchcache):
370 if os.path.exists(srcbranchcache):
371 if not os.path.exists(dstcachedir):
371 if not os.path.exists(dstcachedir):
372 os.mkdir(dstcachedir)
372 os.mkdir(dstcachedir)
373 util.copyfile(srcbranchcache, dstbranchcache)
373 util.copyfile(srcbranchcache, dstbranchcache)
374
374
375 # we need to re-init the repo after manually copying the data
375 # we need to re-init the repo after manually copying the data
376 # into it
376 # into it
377 destpeer = peer(srcrepo, peeropts, dest)
377 destpeer = peer(srcrepo, peeropts, dest)
378 srcrepo.hook('outgoing', source='clone',
378 srcrepo.hook('outgoing', source='clone',
379 node=node.hex(node.nullid))
379 node=node.hex(node.nullid))
380 else:
380 else:
381 try:
381 try:
382 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
382 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
383 # only pass ui when no srcrepo
383 # only pass ui when no srcrepo
384 except OSError, inst:
384 except OSError, inst:
385 if inst.errno == errno.EEXIST:
385 if inst.errno == errno.EEXIST:
386 cleandir = None
386 cleandir = None
387 raise util.Abort(_("destination '%s' already exists")
387 raise util.Abort(_("destination '%s' already exists")
388 % dest)
388 % dest)
389 raise
389 raise
390
390
391 revs = None
391 revs = None
392 if rev:
392 if rev:
393 if not srcpeer.capable('lookup'):
393 if not srcpeer.capable('lookup'):
394 raise util.Abort(_("src repository does not support "
394 raise util.Abort(_("src repository does not support "
395 "revision lookup and so doesn't "
395 "revision lookup and so doesn't "
396 "support clone by revision"))
396 "support clone by revision"))
397 revs = [srcpeer.lookup(r) for r in rev]
397 revs = [srcpeer.lookup(r) for r in rev]
398 checkout = revs[0]
398 checkout = revs[0]
399 if destpeer.local():
399 if destpeer.local():
400 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
400 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
401 elif srcrepo:
401 elif srcrepo:
402 srcrepo.push(destpeer, revs=revs)
402 srcrepo.push(destpeer, revs=revs)
403 else:
403 else:
404 raise util.Abort(_("clone from remote to remote not supported"))
404 raise util.Abort(_("clone from remote to remote not supported"))
405
405
406 cleandir = None
406 cleandir = None
407
407
408 # clone all bookmarks except divergent ones
408 # clone all bookmarks except divergent ones
409 destrepo = destpeer.local()
409 destrepo = destpeer.local()
410 if destrepo and srcpeer.capable("pushkey"):
410 if destrepo and srcpeer.capable("pushkey"):
411 rb = srcpeer.listkeys('bookmarks')
411 rb = srcpeer.listkeys('bookmarks')
412 marks = destrepo._bookmarks
412 marks = destrepo._bookmarks
413 for k, n in rb.iteritems():
413 for k, n in rb.iteritems():
414 try:
414 try:
415 m = destrepo.lookup(n)
415 m = destrepo.lookup(n)
416 marks[k] = m
416 marks[k] = m
417 except error.RepoLookupError:
417 except error.RepoLookupError:
418 pass
418 pass
419 if rb:
419 if rb:
420 marks.write()
420 marks.write()
421 elif srcrepo and destpeer.capable("pushkey"):
421 elif srcrepo and destpeer.capable("pushkey"):
422 for k, n in srcrepo._bookmarks.iteritems():
422 for k, n in srcrepo._bookmarks.iteritems():
423 destpeer.pushkey('bookmarks', k, '', hex(n))
423 destpeer.pushkey('bookmarks', k, '', hex(n))
424
424
425 if destrepo:
425 if destrepo:
426 fp = destrepo.opener("hgrc", "w", text=True)
426 fp = destrepo.opener("hgrc", "w", text=True)
427 fp.write("[paths]\n")
427 fp.write("[paths]\n")
428 u = util.url(abspath)
428 u = util.url(abspath)
429 u.passwd = None
429 u.passwd = None
430 defaulturl = str(u)
430 defaulturl = str(u)
431 fp.write("default = %s\n" % defaulturl)
431 fp.write("default = %s\n" % defaulturl)
432 fp.close()
432 fp.close()
433
433
434 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
434 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
435
435
436 if update:
436 if update:
437 if update is not True:
437 if update is not True:
438 checkout = srcpeer.lookup(update)
438 checkout = srcpeer.lookup(update)
439 uprev = None
439 uprev = None
440 status = None
440 status = None
441 if checkout is not None:
441 if checkout is not None:
442 try:
442 try:
443 uprev = destrepo.lookup(checkout)
443 uprev = destrepo.lookup(checkout)
444 except error.RepoLookupError:
444 except error.RepoLookupError:
445 pass
445 pass
446 if uprev is None:
446 if uprev is None:
447 try:
447 try:
448 uprev = destrepo._bookmarks['@']
448 uprev = destrepo._bookmarks['@']
449 update = '@'
449 update = '@'
450 bn = destrepo[uprev].branch()
450 bn = destrepo[uprev].branch()
451 if bn == 'default':
451 if bn == 'default':
452 status = _("updating to bookmark @\n")
452 status = _("updating to bookmark @\n")
453 else:
453 else:
454 status = (_("updating to bookmark @ on branch %s\n")
454 status = (_("updating to bookmark @ on branch %s\n")
455 % bn)
455 % bn)
456 except KeyError:
456 except KeyError:
457 try:
457 try:
458 uprev = destrepo.branchtip('default')
458 uprev = destrepo.branchtip('default')
459 except error.RepoLookupError:
459 except error.RepoLookupError:
460 uprev = destrepo.lookup('tip')
460 uprev = destrepo.lookup('tip')
461 if not status:
461 if not status:
462 bn = destrepo[uprev].branch()
462 bn = destrepo[uprev].branch()
463 status = _("updating to branch %s\n") % bn
463 status = _("updating to branch %s\n") % bn
464 destrepo.ui.status(status)
464 destrepo.ui.status(status)
465 _update(destrepo, uprev)
465 _update(destrepo, uprev)
466 if update in destrepo._bookmarks:
466 if update in destrepo._bookmarks:
467 bookmarks.setcurrent(destrepo, update)
467 bookmarks.setcurrent(destrepo, update)
468 finally:
468 finally:
469 release(srclock, destlock)
469 release(srclock, destlock)
470 if cleandir is not None:
470 if cleandir is not None:
471 shutil.rmtree(cleandir, True)
471 shutil.rmtree(cleandir, True)
472 if srcpeer is not None:
472 if srcpeer is not None:
473 srcpeer.close()
473 srcpeer.close()
474 return srcpeer, destpeer
474 return srcpeer, destpeer
475
475
476 def _showstats(repo, stats):
476 def _showstats(repo, stats):
477 repo.ui.status(_("%d files updated, %d files merged, "
477 repo.ui.status(_("%d files updated, %d files merged, "
478 "%d files removed, %d files unresolved\n") % stats)
478 "%d files removed, %d files unresolved\n") % stats)
479
479
480 def updaterepo(repo, node, overwrite):
480 def updaterepo(repo, node, overwrite):
481 """Update the working directory to node.
481 """Update the working directory to node.
482
482
483 When overwrite is set, changes are clobbered, merged else
483 When overwrite is set, changes are clobbered, merged else
484
484
485 returns stats (see pydoc mercurial.merge.applyupdates)"""
485 returns stats (see pydoc mercurial.merge.applyupdates)"""
486 return mergemod.update(repo, node, False, overwrite, None)
486 return mergemod.update(repo, node, False, overwrite, None,
487 labels=['working copy', 'destination'])
487
488
488 def update(repo, node):
489 def update(repo, node):
489 """update the working directory to node, merging linear changes"""
490 """update the working directory to node, merging linear changes"""
490 stats = updaterepo(repo, node, False)
491 stats = updaterepo(repo, node, False)
491 _showstats(repo, stats)
492 _showstats(repo, stats)
492 if stats[3]:
493 if stats[3]:
493 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
494 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
494 return stats[3] > 0
495 return stats[3] > 0
495
496
496 # naming conflict in clone()
497 # naming conflict in clone()
497 _update = update
498 _update = update
498
499
499 def clean(repo, node, show_stats=True):
500 def clean(repo, node, show_stats=True):
500 """forcibly switch the working directory to node, clobbering changes"""
501 """forcibly switch the working directory to node, clobbering changes"""
501 stats = updaterepo(repo, node, True)
502 stats = updaterepo(repo, node, True)
502 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
503 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
503 if show_stats:
504 if show_stats:
504 _showstats(repo, stats)
505 _showstats(repo, stats)
505 return stats[3] > 0
506 return stats[3] > 0
506
507
507 def merge(repo, node, force=None, remind=True):
508 def merge(repo, node, force=None, remind=True):
508 """Branch merge with node, resolving changes. Return true if any
509 """Branch merge with node, resolving changes. Return true if any
509 unresolved conflicts."""
510 unresolved conflicts."""
510 stats = mergemod.update(repo, node, True, force, False)
511 stats = mergemod.update(repo, node, True, force, False)
511 _showstats(repo, stats)
512 _showstats(repo, stats)
512 if stats[3]:
513 if stats[3]:
513 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
514 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
514 "or 'hg update -C .' to abandon\n"))
515 "or 'hg update -C .' to abandon\n"))
515 elif remind:
516 elif remind:
516 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
517 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
517 return stats[3] > 0
518 return stats[3] > 0
518
519
519 def _incoming(displaychlist, subreporecurse, ui, repo, source,
520 def _incoming(displaychlist, subreporecurse, ui, repo, source,
520 opts, buffered=False):
521 opts, buffered=False):
521 """
522 """
522 Helper for incoming / gincoming.
523 Helper for incoming / gincoming.
523 displaychlist gets called with
524 displaychlist gets called with
524 (remoterepo, incomingchangesetlist, displayer) parameters,
525 (remoterepo, incomingchangesetlist, displayer) parameters,
525 and is supposed to contain only code that can't be unified.
526 and is supposed to contain only code that can't be unified.
526 """
527 """
527 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
528 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
528 other = peer(repo, opts, source)
529 other = peer(repo, opts, source)
529 ui.status(_('comparing with %s\n') % util.hidepassword(source))
530 ui.status(_('comparing with %s\n') % util.hidepassword(source))
530 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
531 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
531
532
532 if revs:
533 if revs:
533 revs = [other.lookup(rev) for rev in revs]
534 revs = [other.lookup(rev) for rev in revs]
534 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
535 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
535 revs, opts["bundle"], opts["force"])
536 revs, opts["bundle"], opts["force"])
536 try:
537 try:
537 if not chlist:
538 if not chlist:
538 ui.status(_("no changes found\n"))
539 ui.status(_("no changes found\n"))
539 return subreporecurse()
540 return subreporecurse()
540
541
541 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
542 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
542 displaychlist(other, chlist, displayer)
543 displaychlist(other, chlist, displayer)
543 displayer.close()
544 displayer.close()
544 finally:
545 finally:
545 cleanupfn()
546 cleanupfn()
546 subreporecurse()
547 subreporecurse()
547 return 0 # exit code is zero since we found incoming changes
548 return 0 # exit code is zero since we found incoming changes
548
549
549 def incoming(ui, repo, source, opts):
550 def incoming(ui, repo, source, opts):
550 def subreporecurse():
551 def subreporecurse():
551 ret = 1
552 ret = 1
552 if opts.get('subrepos'):
553 if opts.get('subrepos'):
553 ctx = repo[None]
554 ctx = repo[None]
554 for subpath in sorted(ctx.substate):
555 for subpath in sorted(ctx.substate):
555 sub = ctx.sub(subpath)
556 sub = ctx.sub(subpath)
556 ret = min(ret, sub.incoming(ui, source, opts))
557 ret = min(ret, sub.incoming(ui, source, opts))
557 return ret
558 return ret
558
559
559 def display(other, chlist, displayer):
560 def display(other, chlist, displayer):
560 limit = cmdutil.loglimit(opts)
561 limit = cmdutil.loglimit(opts)
561 if opts.get('newest_first'):
562 if opts.get('newest_first'):
562 chlist.reverse()
563 chlist.reverse()
563 count = 0
564 count = 0
564 for n in chlist:
565 for n in chlist:
565 if limit is not None and count >= limit:
566 if limit is not None and count >= limit:
566 break
567 break
567 parents = [p for p in other.changelog.parents(n) if p != nullid]
568 parents = [p for p in other.changelog.parents(n) if p != nullid]
568 if opts.get('no_merges') and len(parents) == 2:
569 if opts.get('no_merges') and len(parents) == 2:
569 continue
570 continue
570 count += 1
571 count += 1
571 displayer.show(other[n])
572 displayer.show(other[n])
572 return _incoming(display, subreporecurse, ui, repo, source, opts)
573 return _incoming(display, subreporecurse, ui, repo, source, opts)
573
574
574 def _outgoing(ui, repo, dest, opts):
575 def _outgoing(ui, repo, dest, opts):
575 dest = ui.expandpath(dest or 'default-push', dest or 'default')
576 dest = ui.expandpath(dest or 'default-push', dest or 'default')
576 dest, branches = parseurl(dest, opts.get('branch'))
577 dest, branches = parseurl(dest, opts.get('branch'))
577 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
578 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
578 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
579 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
579 if revs:
580 if revs:
580 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
581 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
581
582
582 other = peer(repo, opts, dest)
583 other = peer(repo, opts, dest)
583 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
584 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
584 force=opts.get('force'))
585 force=opts.get('force'))
585 o = outgoing.missing
586 o = outgoing.missing
586 if not o:
587 if not o:
587 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
588 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
588 return o, other
589 return o, other
589
590
590 def outgoing(ui, repo, dest, opts):
591 def outgoing(ui, repo, dest, opts):
591 def recurse():
592 def recurse():
592 ret = 1
593 ret = 1
593 if opts.get('subrepos'):
594 if opts.get('subrepos'):
594 ctx = repo[None]
595 ctx = repo[None]
595 for subpath in sorted(ctx.substate):
596 for subpath in sorted(ctx.substate):
596 sub = ctx.sub(subpath)
597 sub = ctx.sub(subpath)
597 ret = min(ret, sub.outgoing(ui, dest, opts))
598 ret = min(ret, sub.outgoing(ui, dest, opts))
598 return ret
599 return ret
599
600
600 limit = cmdutil.loglimit(opts)
601 limit = cmdutil.loglimit(opts)
601 o, other = _outgoing(ui, repo, dest, opts)
602 o, other = _outgoing(ui, repo, dest, opts)
602 if not o:
603 if not o:
603 cmdutil.outgoinghooks(ui, repo, other, opts, o)
604 cmdutil.outgoinghooks(ui, repo, other, opts, o)
604 return recurse()
605 return recurse()
605
606
606 if opts.get('newest_first'):
607 if opts.get('newest_first'):
607 o.reverse()
608 o.reverse()
608 displayer = cmdutil.show_changeset(ui, repo, opts)
609 displayer = cmdutil.show_changeset(ui, repo, opts)
609 count = 0
610 count = 0
610 for n in o:
611 for n in o:
611 if limit is not None and count >= limit:
612 if limit is not None and count >= limit:
612 break
613 break
613 parents = [p for p in repo.changelog.parents(n) if p != nullid]
614 parents = [p for p in repo.changelog.parents(n) if p != nullid]
614 if opts.get('no_merges') and len(parents) == 2:
615 if opts.get('no_merges') and len(parents) == 2:
615 continue
616 continue
616 count += 1
617 count += 1
617 displayer.show(repo[n])
618 displayer.show(repo[n])
618 displayer.close()
619 displayer.close()
619 cmdutil.outgoinghooks(ui, repo, other, opts, o)
620 cmdutil.outgoinghooks(ui, repo, other, opts, o)
620 recurse()
621 recurse()
621 return 0 # exit code is zero since we found outgoing changes
622 return 0 # exit code is zero since we found outgoing changes
622
623
623 def revert(repo, node, choose):
624 def revert(repo, node, choose):
624 """revert changes to revision in node without updating dirstate"""
625 """revert changes to revision in node without updating dirstate"""
625 return mergemod.update(repo, node, False, True, choose)[3] > 0
626 return mergemod.update(repo, node, False, True, choose)[3] > 0
626
627
627 def verify(repo):
628 def verify(repo):
628 """verify the consistency of a repository"""
629 """verify the consistency of a repository"""
629 return verifymod.verify(repo)
630 return verifymod.verify(repo)
630
631
631 def remoteui(src, opts):
632 def remoteui(src, opts):
632 'build a remote ui from ui or repo and opts'
633 'build a remote ui from ui or repo and opts'
633 if util.safehasattr(src, 'baseui'): # looks like a repository
634 if util.safehasattr(src, 'baseui'): # looks like a repository
634 dst = src.baseui.copy() # drop repo-specific config
635 dst = src.baseui.copy() # drop repo-specific config
635 src = src.ui # copy target options from repo
636 src = src.ui # copy target options from repo
636 else: # assume it's a global ui object
637 else: # assume it's a global ui object
637 dst = src.copy() # keep all global options
638 dst = src.copy() # keep all global options
638
639
639 # copy ssh-specific options
640 # copy ssh-specific options
640 for o in 'ssh', 'remotecmd':
641 for o in 'ssh', 'remotecmd':
641 v = opts.get(o) or src.config('ui', o)
642 v = opts.get(o) or src.config('ui', o)
642 if v:
643 if v:
643 dst.setconfig("ui", o, v, 'copied')
644 dst.setconfig("ui", o, v, 'copied')
644
645
645 # copy bundle-specific options
646 # copy bundle-specific options
646 r = src.config('bundle', 'mainreporoot')
647 r = src.config('bundle', 'mainreporoot')
647 if r:
648 if r:
648 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
649 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
649
650
650 # copy selected local settings to the remote ui
651 # copy selected local settings to the remote ui
651 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
652 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
652 for key, val in src.configitems(sect):
653 for key, val in src.configitems(sect):
653 dst.setconfig(sect, key, val, 'copied')
654 dst.setconfig(sect, key, val, 'copied')
654 v = src.config('web', 'cacerts')
655 v = src.config('web', 'cacerts')
655 if v:
656 if v:
656 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
657 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
657
658
658 return dst
659 return dst
@@ -1,94 +1,94 b''
1 $ hg init
1 $ hg init
2
2
3 $ echo "added file1" > file1
3 $ echo "added file1" > file1
4 $ echo "another line of text" >> file1
4 $ echo "another line of text" >> file1
5 $ echo "added file2" > file2
5 $ echo "added file2" > file2
6 $ hg add file1 file2
6 $ hg add file1 file2
7 $ hg commit -m "added file1 and file2"
7 $ hg commit -m "added file1 and file2"
8
8
9 $ echo "changed file1" >> file1
9 $ echo "changed file1" >> file1
10 $ hg commit -m "changed file1"
10 $ hg commit -m "changed file1"
11
11
12 $ hg -q log
12 $ hg -q log
13 1:dfab7f3c2efb
13 1:dfab7f3c2efb
14 0:c3fa057dd86f
14 0:c3fa057dd86f
15 $ hg id
15 $ hg id
16 dfab7f3c2efb tip
16 dfab7f3c2efb tip
17
17
18 $ hg update -C 0
18 $ hg update -C 0
19 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 $ hg id
20 $ hg id
21 c3fa057dd86f
21 c3fa057dd86f
22
22
23 $ echo "changed file1" >> file1
23 $ echo "changed file1" >> file1
24 $ hg id
24 $ hg id
25 c3fa057dd86f+
25 c3fa057dd86f+
26
26
27 $ hg revert --no-backup --all
27 $ hg revert --no-backup --all
28 reverting file1
28 reverting file1
29 $ hg diff
29 $ hg diff
30 $ hg status
30 $ hg status
31 $ hg id
31 $ hg id
32 c3fa057dd86f
32 c3fa057dd86f
33
33
34 $ hg update
34 $ hg update
35 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ hg diff
36 $ hg diff
37 $ hg status
37 $ hg status
38 $ hg id
38 $ hg id
39 dfab7f3c2efb tip
39 dfab7f3c2efb tip
40
40
41 $ hg update -C 0
41 $ hg update -C 0
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ echo "changed file1 different" >> file1
43 $ echo "changed file1 different" >> file1
44
44
45 $ hg update
45 $ hg update
46 merging file1
46 merging file1
47 warning: conflicts during merge.
47 warning: conflicts during merge.
48 merging file1 incomplete! (edit conflicts, then use 'hg resolve --mark')
48 merging file1 incomplete! (edit conflicts, then use 'hg resolve --mark')
49 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
49 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
50 use 'hg resolve' to retry unresolved file merges
50 use 'hg resolve' to retry unresolved file merges
51 [1]
51 [1]
52
52
53 $ hg diff --nodates
53 $ hg diff --nodates
54 diff -r dfab7f3c2efb file1
54 diff -r dfab7f3c2efb file1
55 --- a/file1
55 --- a/file1
56 +++ b/file1
56 +++ b/file1
57 @@ -1,3 +1,7 @@
57 @@ -1,3 +1,7 @@
58 added file1
58 added file1
59 another line of text
59 another line of text
60 +<<<<<<< local: c3fa057dd86f - test: "added file1 and file2"
60 +<<<<<<< working copy: c3fa057dd86f - test: "added file1 and file2"
61 +changed file1 different
61 +changed file1 different
62 +=======
62 +=======
63 changed file1
63 changed file1
64 +>>>>>>> other: dfab7f3c2efb - test: "changed file1"
64 +>>>>>>> destination: dfab7f3c2efb - test: "changed file1"
65
65
66 $ hg status
66 $ hg status
67 M file1
67 M file1
68 ? file1.orig
68 ? file1.orig
69 $ hg id
69 $ hg id
70 dfab7f3c2efb+ tip
70 dfab7f3c2efb+ tip
71
71
72 $ hg revert --no-backup --all
72 $ hg revert --no-backup --all
73 reverting file1
73 reverting file1
74 $ hg diff
74 $ hg diff
75 $ hg status
75 $ hg status
76 ? file1.orig
76 ? file1.orig
77 $ hg id
77 $ hg id
78 dfab7f3c2efb tip
78 dfab7f3c2efb tip
79
79
80 $ hg revert -r tip --no-backup --all
80 $ hg revert -r tip --no-backup --all
81 $ hg diff
81 $ hg diff
82 $ hg status
82 $ hg status
83 ? file1.orig
83 ? file1.orig
84 $ hg id
84 $ hg id
85 dfab7f3c2efb tip
85 dfab7f3c2efb tip
86
86
87 $ hg update -C
87 $ hg update -C
88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 $ hg diff
89 $ hg diff
90 $ hg status
90 $ hg status
91 ? file1.orig
91 ? file1.orig
92 $ hg id
92 $ hg id
93 dfab7f3c2efb tip
93 dfab7f3c2efb tip
94
94
General Comments 0
You need to be logged in to leave comments. Login now