##// END OF EJS Templates
Merge with stable
Martin Geisler -
r11780:3cdd6fdc merge default
parent child Browse files
Show More
@@ -1,1160 +1,1161 b''
1 # Subversion 1.4/1.5 Python API backend
1 # Subversion 1.4/1.5 Python API backend
2 #
2 #
3 # Copyright(C) 2007 Daniel Holth et al
3 # Copyright(C) 2007 Daniel Holth et al
4
4
5 import os
5 import os
6 import re
6 import re
7 import sys
7 import sys
8 import cPickle as pickle
8 import cPickle as pickle
9 import tempfile
9 import tempfile
10 import urllib
10 import urllib
11 import urllib2
11 import urllib2
12
12
13 from mercurial import strutil, util, encoding
13 from mercurial import strutil, util, encoding
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15
15
16 # Subversion stuff. Works best with very recent Python SVN bindings
16 # Subversion stuff. Works best with very recent Python SVN bindings
17 # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
17 # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
18 # these bindings.
18 # these bindings.
19
19
20 from cStringIO import StringIO
20 from cStringIO import StringIO
21
21
22 from common import NoRepo, MissingTool, commit, encodeargs, decodeargs
22 from common import NoRepo, MissingTool, commit, encodeargs, decodeargs
23 from common import commandline, converter_source, converter_sink, mapfile
23 from common import commandline, converter_source, converter_sink, mapfile
24
24
25 try:
25 try:
26 from svn.core import SubversionException, Pool
26 from svn.core import SubversionException, Pool
27 import svn
27 import svn
28 import svn.client
28 import svn.client
29 import svn.core
29 import svn.core
30 import svn.ra
30 import svn.ra
31 import svn.delta
31 import svn.delta
32 import transport
32 import transport
33 import warnings
33 import warnings
34 warnings.filterwarnings('ignore',
34 warnings.filterwarnings('ignore',
35 module='svn.core',
35 module='svn.core',
36 category=DeprecationWarning)
36 category=DeprecationWarning)
37
37
38 except ImportError:
38 except ImportError:
39 pass
39 pass
40
40
41 class SvnPathNotFound(Exception):
41 class SvnPathNotFound(Exception):
42 pass
42 pass
43
43
44 def geturl(path):
44 def geturl(path):
45 try:
45 try:
46 return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
46 return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
47 except SubversionException:
47 except SubversionException:
48 pass
48 pass
49 if os.path.isdir(path):
49 if os.path.isdir(path):
50 path = os.path.normpath(os.path.abspath(path))
50 path = os.path.normpath(os.path.abspath(path))
51 if os.name == 'nt':
51 if os.name == 'nt':
52 path = '/' + util.normpath(path)
52 path = '/' + util.normpath(path)
53 # Module URL is later compared with the repository URL returned
53 # Module URL is later compared with the repository URL returned
54 # by svn API, which is UTF-8.
54 # by svn API, which is UTF-8.
55 path = encoding.tolocal(path)
55 path = encoding.tolocal(path)
56 return 'file://%s' % urllib.quote(path)
56 return 'file://%s' % urllib.quote(path)
57 return path
57 return path
58
58
59 def optrev(number):
59 def optrev(number):
60 optrev = svn.core.svn_opt_revision_t()
60 optrev = svn.core.svn_opt_revision_t()
61 optrev.kind = svn.core.svn_opt_revision_number
61 optrev.kind = svn.core.svn_opt_revision_number
62 optrev.value.number = number
62 optrev.value.number = number
63 return optrev
63 return optrev
64
64
65 class changedpath(object):
65 class changedpath(object):
66 def __init__(self, p):
66 def __init__(self, p):
67 self.copyfrom_path = p.copyfrom_path
67 self.copyfrom_path = p.copyfrom_path
68 self.copyfrom_rev = p.copyfrom_rev
68 self.copyfrom_rev = p.copyfrom_rev
69 self.action = p.action
69 self.action = p.action
70
70
71 def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
71 def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
72 strict_node_history=False):
72 strict_node_history=False):
73 protocol = -1
73 protocol = -1
74 def receiver(orig_paths, revnum, author, date, message, pool):
74 def receiver(orig_paths, revnum, author, date, message, pool):
75 if orig_paths is not None:
75 if orig_paths is not None:
76 for k, v in orig_paths.iteritems():
76 for k, v in orig_paths.iteritems():
77 orig_paths[k] = changedpath(v)
77 orig_paths[k] = changedpath(v)
78 pickle.dump((orig_paths, revnum, author, date, message),
78 pickle.dump((orig_paths, revnum, author, date, message),
79 fp, protocol)
79 fp, protocol)
80
80
81 try:
81 try:
82 # Use an ra of our own so that our parent can consume
82 # Use an ra of our own so that our parent can consume
83 # our results without confusing the server.
83 # our results without confusing the server.
84 t = transport.SvnRaTransport(url=url)
84 t = transport.SvnRaTransport(url=url)
85 svn.ra.get_log(t.ra, paths, start, end, limit,
85 svn.ra.get_log(t.ra, paths, start, end, limit,
86 discover_changed_paths,
86 discover_changed_paths,
87 strict_node_history,
87 strict_node_history,
88 receiver)
88 receiver)
89 except SubversionException, (inst, num):
89 except SubversionException, (inst, num):
90 pickle.dump(num, fp, protocol)
90 pickle.dump(num, fp, protocol)
91 except IOError:
91 except IOError:
92 # Caller may interrupt the iteration
92 # Caller may interrupt the iteration
93 pickle.dump(None, fp, protocol)
93 pickle.dump(None, fp, protocol)
94 else:
94 else:
95 pickle.dump(None, fp, protocol)
95 pickle.dump(None, fp, protocol)
96 fp.close()
96 fp.close()
97 # With large history, cleanup process goes crazy and suddenly
97 # With large history, cleanup process goes crazy and suddenly
98 # consumes *huge* amount of memory. The output file being closed,
98 # consumes *huge* amount of memory. The output file being closed,
99 # there is no need for clean termination.
99 # there is no need for clean termination.
100 os._exit(0)
100 os._exit(0)
101
101
102 def debugsvnlog(ui, **opts):
102 def debugsvnlog(ui, **opts):
103 """Fetch SVN log in a subprocess and channel them back to parent to
103 """Fetch SVN log in a subprocess and channel them back to parent to
104 avoid memory collection issues.
104 avoid memory collection issues.
105 """
105 """
106 util.set_binary(sys.stdin)
106 util.set_binary(sys.stdin)
107 util.set_binary(sys.stdout)
107 util.set_binary(sys.stdout)
108 args = decodeargs(sys.stdin.read())
108 args = decodeargs(sys.stdin.read())
109 get_log_child(sys.stdout, *args)
109 get_log_child(sys.stdout, *args)
110
110
111 class logstream(object):
111 class logstream(object):
112 """Interruptible revision log iterator."""
112 """Interruptible revision log iterator."""
113 def __init__(self, stdout):
113 def __init__(self, stdout):
114 self._stdout = stdout
114 self._stdout = stdout
115
115
116 def __iter__(self):
116 def __iter__(self):
117 while True:
117 while True:
118 try:
118 try:
119 entry = pickle.load(self._stdout)
119 entry = pickle.load(self._stdout)
120 except EOFError:
120 except EOFError:
121 raise util.Abort(_('Mercurial failed to run itself, check'
121 raise util.Abort(_('Mercurial failed to run itself, check'
122 ' hg executable is in PATH'))
122 ' hg executable is in PATH'))
123 try:
123 try:
124 orig_paths, revnum, author, date, message = entry
124 orig_paths, revnum, author, date, message = entry
125 except:
125 except:
126 if entry is None:
126 if entry is None:
127 break
127 break
128 raise SubversionException("child raised exception", entry)
128 raise SubversionException("child raised exception", entry)
129 yield entry
129 yield entry
130
130
131 def close(self):
131 def close(self):
132 if self._stdout:
132 if self._stdout:
133 self._stdout.close()
133 self._stdout.close()
134 self._stdout = None
134 self._stdout = None
135
135
136
136
137 # Check to see if the given path is a local Subversion repo. Verify this by
137 # Check to see if the given path is a local Subversion repo. Verify this by
138 # looking for several svn-specific files and directories in the given
138 # looking for several svn-specific files and directories in the given
139 # directory.
139 # directory.
140 def filecheck(ui, path, proto):
140 def filecheck(ui, path, proto):
141 for x in ('locks', 'hooks', 'format', 'db'):
141 for x in ('locks', 'hooks', 'format', 'db'):
142 if not os.path.exists(os.path.join(path, x)):
142 if not os.path.exists(os.path.join(path, x)):
143 return False
143 return False
144 return True
144 return True
145
145
146 # Check to see if a given path is the root of an svn repo over http. We verify
146 # Check to see if a given path is the root of an svn repo over http. We verify
147 # this by requesting a version-controlled URL we know can't exist and looking
147 # this by requesting a version-controlled URL we know can't exist and looking
148 # for the svn-specific "not found" XML.
148 # for the svn-specific "not found" XML.
149 def httpcheck(ui, path, proto):
149 def httpcheck(ui, path, proto):
150 try:
150 try:
151 opener = urllib2.build_opener()
151 opener = urllib2.build_opener()
152 rsp = opener.open('%s://%s/!svn/ver/0/.svn' % (proto, path))
152 rsp = opener.open('%s://%s/!svn/ver/0/.svn' % (proto, path))
153 data = rsp.read()
153 data = rsp.read()
154 except urllib2.HTTPError, inst:
154 except urllib2.HTTPError, inst:
155 if inst.code != 404:
155 if inst.code != 404:
156 # Except for 404 we cannot know for sure this is not an svn repo
156 # Except for 404 we cannot know for sure this is not an svn repo
157 ui.warn(_('svn: cannot probe remote repository, assume it could '
157 ui.warn(_('svn: cannot probe remote repository, assume it could '
158 'be a subversion repository. Use --source-type if you '
158 'be a subversion repository. Use --source-type if you '
159 'know better.\n'))
159 'know better.\n'))
160 return True
160 return True
161 data = inst.fp.read()
161 data = inst.fp.read()
162 except:
162 except:
163 # Could be urllib2.URLError if the URL is invalid or anything else.
163 # Could be urllib2.URLError if the URL is invalid or anything else.
164 return False
164 return False
165 return '<m:human-readable errcode="160013">' in data
165 return '<m:human-readable errcode="160013">' in data
166
166
167 protomap = {'http': httpcheck,
167 protomap = {'http': httpcheck,
168 'https': httpcheck,
168 'https': httpcheck,
169 'file': filecheck,
169 'file': filecheck,
170 }
170 }
171 def issvnurl(ui, url):
171 def issvnurl(ui, url):
172 try:
172 try:
173 proto, path = url.split('://', 1)
173 proto, path = url.split('://', 1)
174 if proto == 'file':
174 if proto == 'file':
175 path = urllib.url2pathname(path)
175 path = urllib.url2pathname(path)
176 except ValueError:
176 except ValueError:
177 proto = 'file'
177 proto = 'file'
178 path = os.path.abspath(url)
178 path = os.path.abspath(url)
179 if proto == 'file':
179 if proto == 'file':
180 path = path.replace(os.sep, '/')
180 path = path.replace(os.sep, '/')
181 check = protomap.get(proto, lambda *args: False)
181 check = protomap.get(proto, lambda *args: False)
182 while '/' in path:
182 while '/' in path:
183 if check(ui, path, proto):
183 if check(ui, path, proto):
184 return True
184 return True
185 path = path.rsplit('/', 1)[0]
185 path = path.rsplit('/', 1)[0]
186 return False
186 return False
187
187
188 # SVN conversion code stolen from bzr-svn and tailor
188 # SVN conversion code stolen from bzr-svn and tailor
189 #
189 #
190 # Subversion looks like a versioned filesystem, branches structures
190 # Subversion looks like a versioned filesystem, branches structures
191 # are defined by conventions and not enforced by the tool. First,
191 # are defined by conventions and not enforced by the tool. First,
192 # we define the potential branches (modules) as "trunk" and "branches"
192 # we define the potential branches (modules) as "trunk" and "branches"
193 # children directories. Revisions are then identified by their
193 # children directories. Revisions are then identified by their
194 # module and revision number (and a repository identifier).
194 # module and revision number (and a repository identifier).
195 #
195 #
196 # The revision graph is really a tree (or a forest). By default, a
196 # The revision graph is really a tree (or a forest). By default, a
197 # revision parent is the previous revision in the same module. If the
197 # revision parent is the previous revision in the same module. If the
198 # module directory is copied/moved from another module then the
198 # module directory is copied/moved from another module then the
199 # revision is the module root and its parent the source revision in
199 # revision is the module root and its parent the source revision in
200 # the parent module. A revision has at most one parent.
200 # the parent module. A revision has at most one parent.
201 #
201 #
202 class svn_source(converter_source):
202 class svn_source(converter_source):
203 def __init__(self, ui, url, rev=None):
203 def __init__(self, ui, url, rev=None):
204 super(svn_source, self).__init__(ui, url, rev=rev)
204 super(svn_source, self).__init__(ui, url, rev=rev)
205
205
206 if not (url.startswith('svn://') or url.startswith('svn+ssh://') or
206 if not (url.startswith('svn://') or url.startswith('svn+ssh://') or
207 (os.path.exists(url) and
207 (os.path.exists(url) and
208 os.path.exists(os.path.join(url, '.svn'))) or
208 os.path.exists(os.path.join(url, '.svn'))) or
209 issvnurl(ui, url)):
209 issvnurl(ui, url)):
210 raise NoRepo(_("%s does not look like a Subversion repository")
210 raise NoRepo(_("%s does not look like a Subversion repository")
211 % url)
211 % url)
212
212
213 try:
213 try:
214 SubversionException
214 SubversionException
215 except NameError:
215 except NameError:
216 raise MissingTool(_('Subversion python bindings could not be loaded'))
216 raise MissingTool(_('Subversion python bindings could not be loaded'))
217
217
218 try:
218 try:
219 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
219 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
220 if version < (1, 4):
220 if version < (1, 4):
221 raise MissingTool(_('Subversion python bindings %d.%d found, '
221 raise MissingTool(_('Subversion python bindings %d.%d found, '
222 '1.4 or later required') % version)
222 '1.4 or later required') % version)
223 except AttributeError:
223 except AttributeError:
224 raise MissingTool(_('Subversion python bindings are too old, 1.4 '
224 raise MissingTool(_('Subversion python bindings are too old, 1.4 '
225 'or later required'))
225 'or later required'))
226
226
227 self.lastrevs = {}
227 self.lastrevs = {}
228
228
229 latest = None
229 latest = None
230 try:
230 try:
231 # Support file://path@rev syntax. Useful e.g. to convert
231 # Support file://path@rev syntax. Useful e.g. to convert
232 # deleted branches.
232 # deleted branches.
233 at = url.rfind('@')
233 at = url.rfind('@')
234 if at >= 0:
234 if at >= 0:
235 latest = int(url[at + 1:])
235 latest = int(url[at + 1:])
236 url = url[:at]
236 url = url[:at]
237 except ValueError:
237 except ValueError:
238 pass
238 pass
239 self.url = geturl(url)
239 self.url = geturl(url)
240 self.encoding = 'UTF-8' # Subversion is always nominal UTF-8
240 self.encoding = 'UTF-8' # Subversion is always nominal UTF-8
241 try:
241 try:
242 self.transport = transport.SvnRaTransport(url=self.url)
242 self.transport = transport.SvnRaTransport(url=self.url)
243 self.ra = self.transport.ra
243 self.ra = self.transport.ra
244 self.ctx = self.transport.client
244 self.ctx = self.transport.client
245 self.baseurl = svn.ra.get_repos_root(self.ra)
245 self.baseurl = svn.ra.get_repos_root(self.ra)
246 # Module is either empty or a repository path starting with
246 # Module is either empty or a repository path starting with
247 # a slash and not ending with a slash.
247 # a slash and not ending with a slash.
248 self.module = urllib.unquote(self.url[len(self.baseurl):])
248 self.module = urllib.unquote(self.url[len(self.baseurl):])
249 self.prevmodule = None
249 self.prevmodule = None
250 self.rootmodule = self.module
250 self.rootmodule = self.module
251 self.commits = {}
251 self.commits = {}
252 self.paths = {}
252 self.paths = {}
253 self.uuid = svn.ra.get_uuid(self.ra)
253 self.uuid = svn.ra.get_uuid(self.ra)
254 except SubversionException:
254 except SubversionException:
255 ui.traceback()
255 ui.traceback()
256 raise NoRepo(_("%s does not look like a Subversion repository")
256 raise NoRepo(_("%s does not look like a Subversion repository")
257 % self.url)
257 % self.url)
258
258
259 if rev:
259 if rev:
260 try:
260 try:
261 latest = int(rev)
261 latest = int(rev)
262 except ValueError:
262 except ValueError:
263 raise util.Abort(_('svn: revision %s is not an integer') % rev)
263 raise util.Abort(_('svn: revision %s is not an integer') % rev)
264
264
265 self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
265 self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
266 try:
266 try:
267 self.startrev = int(self.startrev)
267 self.startrev = int(self.startrev)
268 if self.startrev < 0:
268 if self.startrev < 0:
269 self.startrev = 0
269 self.startrev = 0
270 except ValueError:
270 except ValueError:
271 raise util.Abort(_('svn: start revision %s is not an integer')
271 raise util.Abort(_('svn: start revision %s is not an integer')
272 % self.startrev)
272 % self.startrev)
273
273
274 self.head = self.latest(self.module, latest)
274 self.head = self.latest(self.module, latest)
275 if not self.head:
275 if not self.head:
276 raise util.Abort(_('no revision found in module %s')
276 raise util.Abort(_('no revision found in module %s')
277 % self.module)
277 % self.module)
278 self.last_changed = self.revnum(self.head)
278 self.last_changed = self.revnum(self.head)
279
279
280 self._changescache = None
280 self._changescache = None
281
281
282 if os.path.exists(os.path.join(url, '.svn/entries')):
282 if os.path.exists(os.path.join(url, '.svn/entries')):
283 self.wc = url
283 self.wc = url
284 else:
284 else:
285 self.wc = None
285 self.wc = None
286 self.convertfp = None
286 self.convertfp = None
287
287
288 def setrevmap(self, revmap):
288 def setrevmap(self, revmap):
289 lastrevs = {}
289 lastrevs = {}
290 for revid in revmap.iterkeys():
290 for revid in revmap.iterkeys():
291 uuid, module, revnum = self.revsplit(revid)
291 uuid, module, revnum = self.revsplit(revid)
292 lastrevnum = lastrevs.setdefault(module, revnum)
292 lastrevnum = lastrevs.setdefault(module, revnum)
293 if revnum > lastrevnum:
293 if revnum > lastrevnum:
294 lastrevs[module] = revnum
294 lastrevs[module] = revnum
295 self.lastrevs = lastrevs
295 self.lastrevs = lastrevs
296
296
297 def exists(self, path, optrev):
297 def exists(self, path, optrev):
298 try:
298 try:
299 svn.client.ls(self.url.rstrip('/') + '/' + urllib.quote(path),
299 svn.client.ls(self.url.rstrip('/') + '/' + urllib.quote(path),
300 optrev, False, self.ctx)
300 optrev, False, self.ctx)
301 return True
301 return True
302 except SubversionException:
302 except SubversionException:
303 return False
303 return False
304
304
305 def getheads(self):
305 def getheads(self):
306
306
307 def isdir(path, revnum):
307 def isdir(path, revnum):
308 kind = self._checkpath(path, revnum)
308 kind = self._checkpath(path, revnum)
309 return kind == svn.core.svn_node_dir
309 return kind == svn.core.svn_node_dir
310
310
311 def getcfgpath(name, rev):
311 def getcfgpath(name, rev):
312 cfgpath = self.ui.config('convert', 'svn.' + name)
312 cfgpath = self.ui.config('convert', 'svn.' + name)
313 if cfgpath is not None and cfgpath.strip() == '':
313 if cfgpath is not None and cfgpath.strip() == '':
314 return None
314 return None
315 path = (cfgpath or name).strip('/')
315 path = (cfgpath or name).strip('/')
316 if not self.exists(path, rev):
316 if not self.exists(path, rev):
317 if cfgpath:
317 if cfgpath:
318 raise util.Abort(_('expected %s to be at %r, but not found')
318 raise util.Abort(_('expected %s to be at %r, but not found')
319 % (name, path))
319 % (name, path))
320 return None
320 return None
321 self.ui.note(_('found %s at %r\n') % (name, path))
321 self.ui.note(_('found %s at %r\n') % (name, path))
322 return path
322 return path
323
323
324 rev = optrev(self.last_changed)
324 rev = optrev(self.last_changed)
325 oldmodule = ''
325 oldmodule = ''
326 trunk = getcfgpath('trunk', rev)
326 trunk = getcfgpath('trunk', rev)
327 self.tags = getcfgpath('tags', rev)
327 self.tags = getcfgpath('tags', rev)
328 branches = getcfgpath('branches', rev)
328 branches = getcfgpath('branches', rev)
329
329
330 # If the project has a trunk or branches, we will extract heads
330 # If the project has a trunk or branches, we will extract heads
331 # from them. We keep the project root otherwise.
331 # from them. We keep the project root otherwise.
332 if trunk:
332 if trunk:
333 oldmodule = self.module or ''
333 oldmodule = self.module or ''
334 self.module += '/' + trunk
334 self.module += '/' + trunk
335 self.head = self.latest(self.module, self.last_changed)
335 self.head = self.latest(self.module, self.last_changed)
336 if not self.head:
336 if not self.head:
337 raise util.Abort(_('no revision found in module %s')
337 raise util.Abort(_('no revision found in module %s')
338 % self.module)
338 % self.module)
339
339
340 # First head in the list is the module's head
340 # First head in the list is the module's head
341 self.heads = [self.head]
341 self.heads = [self.head]
342 if self.tags is not None:
342 if self.tags is not None:
343 self.tags = '%s/%s' % (oldmodule , (self.tags or 'tags'))
343 self.tags = '%s/%s' % (oldmodule , (self.tags or 'tags'))
344
344
345 # Check if branches bring a few more heads to the list
345 # Check if branches bring a few more heads to the list
346 if branches:
346 if branches:
347 rpath = self.url.strip('/')
347 rpath = self.url.strip('/')
348 branchnames = svn.client.ls(rpath + '/' + urllib.quote(branches),
348 branchnames = svn.client.ls(rpath + '/' + urllib.quote(branches),
349 rev, False, self.ctx)
349 rev, False, self.ctx)
350 for branch in branchnames.keys():
350 for branch in branchnames.keys():
351 module = '%s/%s/%s' % (oldmodule, branches, branch)
351 module = '%s/%s/%s' % (oldmodule, branches, branch)
352 if not isdir(module, self.last_changed):
352 if not isdir(module, self.last_changed):
353 continue
353 continue
354 brevid = self.latest(module, self.last_changed)
354 brevid = self.latest(module, self.last_changed)
355 if not brevid:
355 if not brevid:
356 self.ui.note(_('ignoring empty branch %s\n') % branch)
356 self.ui.note(_('ignoring empty branch %s\n') % branch)
357 continue
357 continue
358 self.ui.note(_('found branch %s at %d\n') %
358 self.ui.note(_('found branch %s at %d\n') %
359 (branch, self.revnum(brevid)))
359 (branch, self.revnum(brevid)))
360 self.heads.append(brevid)
360 self.heads.append(brevid)
361
361
362 if self.startrev and self.heads:
362 if self.startrev and self.heads:
363 if len(self.heads) > 1:
363 if len(self.heads) > 1:
364 raise util.Abort(_('svn: start revision is not supported '
364 raise util.Abort(_('svn: start revision is not supported '
365 'with more than one branch'))
365 'with more than one branch'))
366 revnum = self.revnum(self.heads[0])
366 revnum = self.revnum(self.heads[0])
367 if revnum < self.startrev:
367 if revnum < self.startrev:
368 raise util.Abort(
368 raise util.Abort(
369 _('svn: no revision found after start revision %d')
369 _('svn: no revision found after start revision %d')
370 % self.startrev)
370 % self.startrev)
371
371
372 return self.heads
372 return self.heads
373
373
374 def getchanges(self, rev):
374 def getchanges(self, rev):
375 if self._changescache and self._changescache[0] == rev:
375 if self._changescache and self._changescache[0] == rev:
376 return self._changescache[1]
376 return self._changescache[1]
377 self._changescache = None
377 self._changescache = None
378 (paths, parents) = self.paths[rev]
378 (paths, parents) = self.paths[rev]
379 if parents:
379 if parents:
380 files, self.removed, copies = self.expandpaths(rev, paths, parents)
380 files, self.removed, copies = self.expandpaths(rev, paths, parents)
381 else:
381 else:
382 # Perform a full checkout on roots
382 # Perform a full checkout on roots
383 uuid, module, revnum = self.revsplit(rev)
383 uuid, module, revnum = self.revsplit(rev)
384 entries = svn.client.ls(self.baseurl + urllib.quote(module),
384 entries = svn.client.ls(self.baseurl + urllib.quote(module),
385 optrev(revnum), True, self.ctx)
385 optrev(revnum), True, self.ctx)
386 files = [n for n, e in entries.iteritems()
386 files = [n for n, e in entries.iteritems()
387 if e.kind == svn.core.svn_node_file]
387 if e.kind == svn.core.svn_node_file]
388 copies = {}
388 copies = {}
389 self.removed = set()
389 self.removed = set()
390
390
391 files.sort()
391 files.sort()
392 files = zip(files, [rev] * len(files))
392 files = zip(files, [rev] * len(files))
393
393
394 # caller caches the result, so free it here to release memory
394 # caller caches the result, so free it here to release memory
395 del self.paths[rev]
395 del self.paths[rev]
396 return (files, copies)
396 return (files, copies)
397
397
398 def getchangedfiles(self, rev, i):
398 def getchangedfiles(self, rev, i):
399 changes = self.getchanges(rev)
399 changes = self.getchanges(rev)
400 self._changescache = (rev, changes)
400 self._changescache = (rev, changes)
401 return [f[0] for f in changes[0]]
401 return [f[0] for f in changes[0]]
402
402
403 def getcommit(self, rev):
403 def getcommit(self, rev):
404 if rev not in self.commits:
404 if rev not in self.commits:
405 uuid, module, revnum = self.revsplit(rev)
405 uuid, module, revnum = self.revsplit(rev)
406 self.module = module
406 self.module = module
407 self.reparent(module)
407 self.reparent(module)
408 # We assume that:
408 # We assume that:
409 # - requests for revisions after "stop" come from the
409 # - requests for revisions after "stop" come from the
410 # revision graph backward traversal. Cache all of them
410 # revision graph backward traversal. Cache all of them
411 # down to stop, they will be used eventually.
411 # down to stop, they will be used eventually.
412 # - requests for revisions before "stop" come to get
412 # - requests for revisions before "stop" come to get
413 # isolated branches parents. Just fetch what is needed.
413 # isolated branches parents. Just fetch what is needed.
414 stop = self.lastrevs.get(module, 0)
414 stop = self.lastrevs.get(module, 0)
415 if revnum < stop:
415 if revnum < stop:
416 stop = revnum + 1
416 stop = revnum + 1
417 self._fetch_revisions(revnum, stop)
417 self._fetch_revisions(revnum, stop)
418 commit = self.commits[rev]
418 commit = self.commits[rev]
419 # caller caches the result, so free it here to release memory
419 # caller caches the result, so free it here to release memory
420 del self.commits[rev]
420 del self.commits[rev]
421 return commit
421 return commit
422
422
423 def gettags(self):
423 def gettags(self):
424 tags = {}
424 tags = {}
425 if self.tags is None:
425 if self.tags is None:
426 return tags
426 return tags
427
427
428 # svn tags are just a convention, project branches left in a
428 # svn tags are just a convention, project branches left in a
429 # 'tags' directory. There is no other relationship than
429 # 'tags' directory. There is no other relationship than
430 # ancestry, which is expensive to discover and makes them hard
430 # ancestry, which is expensive to discover and makes them hard
431 # to update incrementally. Worse, past revisions may be
431 # to update incrementally. Worse, past revisions may be
432 # referenced by tags far away in the future, requiring a deep
432 # referenced by tags far away in the future, requiring a deep
433 # history traversal on every calculation. Current code
433 # history traversal on every calculation. Current code
434 # performs a single backward traversal, tracking moves within
434 # performs a single backward traversal, tracking moves within
435 # the tags directory (tag renaming) and recording a new tag
435 # the tags directory (tag renaming) and recording a new tag
436 # everytime a project is copied from outside the tags
436 # everytime a project is copied from outside the tags
437 # directory. It also lists deleted tags, this behaviour may
437 # directory. It also lists deleted tags, this behaviour may
438 # change in the future.
438 # change in the future.
439 pendings = []
439 pendings = []
440 tagspath = self.tags
440 tagspath = self.tags
441 start = svn.ra.get_latest_revnum(self.ra)
441 start = svn.ra.get_latest_revnum(self.ra)
442 stream = self._getlog([self.tags], start, self.startrev)
442 stream = self._getlog([self.tags], start, self.startrev)
443 try:
443 try:
444 for entry in stream:
444 for entry in stream:
445 origpaths, revnum, author, date, message = entry
445 origpaths, revnum, author, date, message = entry
446 copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p, e
446 copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p, e
447 in origpaths.iteritems() if e.copyfrom_path]
447 in origpaths.iteritems() if e.copyfrom_path]
448 # Apply moves/copies from more specific to general
448 # Apply moves/copies from more specific to general
449 copies.sort(reverse=True)
449 copies.sort(reverse=True)
450
450
451 srctagspath = tagspath
451 srctagspath = tagspath
452 if copies and copies[-1][2] == tagspath:
452 if copies and copies[-1][2] == tagspath:
453 # Track tags directory moves
453 # Track tags directory moves
454 srctagspath = copies.pop()[0]
454 srctagspath = copies.pop()[0]
455
455
456 for source, sourcerev, dest in copies:
456 for source, sourcerev, dest in copies:
457 if not dest.startswith(tagspath + '/'):
457 if not dest.startswith(tagspath + '/'):
458 continue
458 continue
459 for tag in pendings:
459 for tag in pendings:
460 if tag[0].startswith(dest):
460 if tag[0].startswith(dest):
461 tagpath = source + tag[0][len(dest):]
461 tagpath = source + tag[0][len(dest):]
462 tag[:2] = [tagpath, sourcerev]
462 tag[:2] = [tagpath, sourcerev]
463 break
463 break
464 else:
464 else:
465 pendings.append([source, sourcerev, dest])
465 pendings.append([source, sourcerev, dest])
466
466
467 # Filter out tags with children coming from different
467 # Filter out tags with children coming from different
468 # parts of the repository like:
468 # parts of the repository like:
469 # /tags/tag.1 (from /trunk:10)
469 # /tags/tag.1 (from /trunk:10)
470 # /tags/tag.1/foo (from /branches/foo:12)
470 # /tags/tag.1/foo (from /branches/foo:12)
471 # Here/tags/tag.1 discarded as well as its children.
471 # Here/tags/tag.1 discarded as well as its children.
472 # It happens with tools like cvs2svn. Such tags cannot
472 # It happens with tools like cvs2svn. Such tags cannot
473 # be represented in mercurial.
473 # be represented in mercurial.
474 addeds = dict((p, e.copyfrom_path) for p, e
474 addeds = dict((p, e.copyfrom_path) for p, e
475 in origpaths.iteritems()
475 in origpaths.iteritems()
476 if e.action == 'A' and e.copyfrom_path)
476 if e.action == 'A' and e.copyfrom_path)
477 badroots = set()
477 badroots = set()
478 for destroot in addeds:
478 for destroot in addeds:
479 for source, sourcerev, dest in pendings:
479 for source, sourcerev, dest in pendings:
480 if (not dest.startswith(destroot + '/')
480 if (not dest.startswith(destroot + '/')
481 or source.startswith(addeds[destroot] + '/')):
481 or source.startswith(addeds[destroot] + '/')):
482 continue
482 continue
483 badroots.add(destroot)
483 badroots.add(destroot)
484 break
484 break
485
485
486 for badroot in badroots:
486 for badroot in badroots:
487 pendings = [p for p in pendings if p[2] != badroot
487 pendings = [p for p in pendings if p[2] != badroot
488 and not p[2].startswith(badroot + '/')]
488 and not p[2].startswith(badroot + '/')]
489
489
490 # Tell tag renamings from tag creations
490 # Tell tag renamings from tag creations
491 remainings = []
491 remainings = []
492 for source, sourcerev, dest in pendings:
492 for source, sourcerev, dest in pendings:
493 tagname = dest.split('/')[-1]
493 tagname = dest.split('/')[-1]
494 if source.startswith(srctagspath):
494 if source.startswith(srctagspath):
495 remainings.append([source, sourcerev, tagname])
495 remainings.append([source, sourcerev, tagname])
496 continue
496 continue
497 if tagname in tags:
497 if tagname in tags:
498 # Keep the latest tag value
498 # Keep the latest tag value
499 continue
499 continue
500 # From revision may be fake, get one with changes
500 # From revision may be fake, get one with changes
501 try:
501 try:
502 tagid = self.latest(source, sourcerev)
502 tagid = self.latest(source, sourcerev)
503 if tagid and tagname not in tags:
503 if tagid and tagname not in tags:
504 tags[tagname] = tagid
504 tags[tagname] = tagid
505 except SvnPathNotFound:
505 except SvnPathNotFound:
506 # It happens when we are following directories
506 # It happens when we are following directories
507 # we assumed were copied with their parents
507 # we assumed were copied with their parents
508 # but were really created in the tag
508 # but were really created in the tag
509 # directory.
509 # directory.
510 pass
510 pass
511 pendings = remainings
511 pendings = remainings
512 tagspath = srctagspath
512 tagspath = srctagspath
513 finally:
513 finally:
514 stream.close()
514 stream.close()
515 return tags
515 return tags
516
516
517 def converted(self, rev, destrev):
517 def converted(self, rev, destrev):
518 if not self.wc:
518 if not self.wc:
519 return
519 return
520 if self.convertfp is None:
520 if self.convertfp is None:
521 self.convertfp = open(os.path.join(self.wc, '.svn', 'hg-shamap'),
521 self.convertfp = open(os.path.join(self.wc, '.svn', 'hg-shamap'),
522 'a')
522 'a')
523 self.convertfp.write('%s %d\n' % (destrev, self.revnum(rev)))
523 self.convertfp.write('%s %d\n' % (destrev, self.revnum(rev)))
524 self.convertfp.flush()
524 self.convertfp.flush()
525
525
526 def revid(self, revnum, module=None):
526 def revid(self, revnum, module=None):
527 return 'svn:%s%s@%s' % (self.uuid, module or self.module, revnum)
527 return 'svn:%s%s@%s' % (self.uuid, module or self.module, revnum)
528
528
529 def revnum(self, rev):
529 def revnum(self, rev):
530 return int(rev.split('@')[-1])
530 return int(rev.split('@')[-1])
531
531
532 def revsplit(self, rev):
532 def revsplit(self, rev):
533 url, revnum = rev.rsplit('@', 1)
533 url, revnum = rev.rsplit('@', 1)
534 revnum = int(revnum)
534 revnum = int(revnum)
535 parts = url.split('/', 1)
535 parts = url.split('/', 1)
536 uuid = parts.pop(0)[4:]
536 uuid = parts.pop(0)[4:]
537 mod = ''
537 mod = ''
538 if parts:
538 if parts:
539 mod = '/' + parts[0]
539 mod = '/' + parts[0]
540 return uuid, mod, revnum
540 return uuid, mod, revnum
541
541
542 def latest(self, path, stop=0):
542 def latest(self, path, stop=0):
543 """Find the latest revid affecting path, up to stop. It may return
543 """Find the latest revid affecting path, up to stop. It may return
544 a revision in a different module, since a branch may be moved without
544 a revision in a different module, since a branch may be moved without
545 a change being reported. Return None if computed module does not
545 a change being reported. Return None if computed module does not
546 belong to rootmodule subtree.
546 belong to rootmodule subtree.
547 """
547 """
548 if not path.startswith(self.rootmodule):
548 if not path.startswith(self.rootmodule):
549 # Requests on foreign branches may be forbidden at server level
549 # Requests on foreign branches may be forbidden at server level
550 self.ui.debug('ignoring foreign branch %r\n' % path)
550 self.ui.debug('ignoring foreign branch %r\n' % path)
551 return None
551 return None
552
552
553 if not stop:
553 if not stop:
554 stop = svn.ra.get_latest_revnum(self.ra)
554 stop = svn.ra.get_latest_revnum(self.ra)
555 try:
555 try:
556 prevmodule = self.reparent('')
556 prevmodule = self.reparent('')
557 dirent = svn.ra.stat(self.ra, path.strip('/'), stop)
557 dirent = svn.ra.stat(self.ra, path.strip('/'), stop)
558 self.reparent(prevmodule)
558 self.reparent(prevmodule)
559 except SubversionException:
559 except SubversionException:
560 dirent = None
560 dirent = None
561 if not dirent:
561 if not dirent:
562 raise SvnPathNotFound(_('%s not found up to revision %d')
562 raise SvnPathNotFound(_('%s not found up to revision %d')
563 % (path, stop))
563 % (path, stop))
564
564
565 # stat() gives us the previous revision on this line of
565 # stat() gives us the previous revision on this line of
566 # development, but it might be in *another module*. Fetch the
566 # development, but it might be in *another module*. Fetch the
567 # log and detect renames down to the latest revision.
567 # log and detect renames down to the latest revision.
568 stream = self._getlog([path], stop, dirent.created_rev)
568 stream = self._getlog([path], stop, dirent.created_rev)
569 try:
569 try:
570 for entry in stream:
570 for entry in stream:
571 paths, revnum, author, date, message = entry
571 paths, revnum, author, date, message = entry
572 if revnum <= dirent.created_rev:
572 if revnum <= dirent.created_rev:
573 break
573 break
574
574
575 for p in paths:
575 for p in paths:
576 if not path.startswith(p) or not paths[p].copyfrom_path:
576 if not path.startswith(p) or not paths[p].copyfrom_path:
577 continue
577 continue
578 newpath = paths[p].copyfrom_path + path[len(p):]
578 newpath = paths[p].copyfrom_path + path[len(p):]
579 self.ui.debug("branch renamed from %s to %s at %d\n" %
579 self.ui.debug("branch renamed from %s to %s at %d\n" %
580 (path, newpath, revnum))
580 (path, newpath, revnum))
581 path = newpath
581 path = newpath
582 break
582 break
583 finally:
583 finally:
584 stream.close()
584 stream.close()
585
585
586 if not path.startswith(self.rootmodule):
586 if not path.startswith(self.rootmodule):
587 self.ui.debug('ignoring foreign branch %r\n' % path)
587 self.ui.debug('ignoring foreign branch %r\n' % path)
588 return None
588 return None
589 return self.revid(dirent.created_rev, path)
589 return self.revid(dirent.created_rev, path)
590
590
591 def reparent(self, module):
591 def reparent(self, module):
592 """Reparent the svn transport and return the previous parent."""
592 """Reparent the svn transport and return the previous parent."""
593 if self.prevmodule == module:
593 if self.prevmodule == module:
594 return module
594 return module
595 svnurl = self.baseurl + urllib.quote(module)
595 svnurl = self.baseurl + urllib.quote(module)
596 prevmodule = self.prevmodule
596 prevmodule = self.prevmodule
597 if prevmodule is None:
597 if prevmodule is None:
598 prevmodule = ''
598 prevmodule = ''
599 self.ui.debug("reparent to %s\n" % svnurl)
599 self.ui.debug("reparent to %s\n" % svnurl)
600 svn.ra.reparent(self.ra, svnurl)
600 svn.ra.reparent(self.ra, svnurl)
601 self.prevmodule = module
601 self.prevmodule = module
602 return prevmodule
602 return prevmodule
603
603
604 def expandpaths(self, rev, paths, parents):
604 def expandpaths(self, rev, paths, parents):
605 changed, removed = set(), set()
605 changed, removed = set(), set()
606 copies = {}
606 copies = {}
607
607
608 new_module, revnum = self.revsplit(rev)[1:]
608 new_module, revnum = self.revsplit(rev)[1:]
609 if new_module != self.module:
609 if new_module != self.module:
610 self.module = new_module
610 self.module = new_module
611 self.reparent(self.module)
611 self.reparent(self.module)
612
612
613 for i, (path, ent) in enumerate(paths):
613 for i, (path, ent) in enumerate(paths):
614 self.ui.progress(_('scanning paths'), i, item=path,
614 self.ui.progress(_('scanning paths'), i, item=path,
615 total=len(paths))
615 total=len(paths))
616 entrypath = self.getrelpath(path)
616 entrypath = self.getrelpath(path)
617
617
618 kind = self._checkpath(entrypath, revnum)
618 kind = self._checkpath(entrypath, revnum)
619 if kind == svn.core.svn_node_file:
619 if kind == svn.core.svn_node_file:
620 changed.add(self.recode(entrypath))
620 changed.add(self.recode(entrypath))
621 if not ent.copyfrom_path or not parents:
621 if not ent.copyfrom_path or not parents:
622 continue
622 continue
623 # Copy sources not in parent revisions cannot be
623 # Copy sources not in parent revisions cannot be
624 # represented, ignore their origin for now
624 # represented, ignore their origin for now
625 pmodule, prevnum = self.revsplit(parents[0])[1:]
625 pmodule, prevnum = self.revsplit(parents[0])[1:]
626 if ent.copyfrom_rev < prevnum:
626 if ent.copyfrom_rev < prevnum:
627 continue
627 continue
628 copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
628 copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
629 if not copyfrom_path:
629 if not copyfrom_path:
630 continue
630 continue
631 self.ui.debug("copied to %s from %s@%s\n" %
631 self.ui.debug("copied to %s from %s@%s\n" %
632 (entrypath, copyfrom_path, ent.copyfrom_rev))
632 (entrypath, copyfrom_path, ent.copyfrom_rev))
633 copies[self.recode(entrypath)] = self.recode(copyfrom_path)
633 copies[self.recode(entrypath)] = self.recode(copyfrom_path)
634 elif kind == 0: # gone, but had better be a deleted *file*
634 elif kind == 0: # gone, but had better be a deleted *file*
635 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
635 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
636 pmodule, prevnum = self.revsplit(parents[0])[1:]
636 pmodule, prevnum = self.revsplit(parents[0])[1:]
637 parentpath = pmodule + "/" + entrypath
637 parentpath = pmodule + "/" + entrypath
638 fromkind = self._checkpath(entrypath, prevnum, pmodule)
638 fromkind = self._checkpath(entrypath, prevnum, pmodule)
639
639
640 if fromkind == svn.core.svn_node_file:
640 if fromkind == svn.core.svn_node_file:
641 removed.add(self.recode(entrypath))
641 removed.add(self.recode(entrypath))
642 elif fromkind == svn.core.svn_node_dir:
642 elif fromkind == svn.core.svn_node_dir:
643 oroot = parentpath.strip('/')
643 oroot = parentpath.strip('/')
644 nroot = path.strip('/')
644 nroot = path.strip('/')
645 children = self._iterfiles(oroot, prevnum)
645 children = self._iterfiles(oroot, prevnum)
646 for childpath in children:
646 for childpath in children:
647 childpath = childpath.replace(oroot, nroot)
647 childpath = childpath.replace(oroot, nroot)
648 childpath = self.getrelpath("/" + childpath, pmodule)
648 childpath = self.getrelpath("/" + childpath, pmodule)
649 if childpath:
649 if childpath:
650 removed.add(self.recode(childpath))
650 removed.add(self.recode(childpath))
651 else:
651 else:
652 self.ui.debug('unknown path in revision %d: %s\n' % \
652 self.ui.debug('unknown path in revision %d: %s\n' % \
653 (revnum, path))
653 (revnum, path))
654 elif kind == svn.core.svn_node_dir:
654 elif kind == svn.core.svn_node_dir:
655 if ent.action == 'M':
655 if ent.action == 'M':
656 # If the directory just had a prop change,
656 # If the directory just had a prop change,
657 # then we shouldn't need to look for its children.
657 # then we shouldn't need to look for its children.
658 continue
658 continue
659 elif ent.action == 'R' and parents:
659 elif ent.action == 'R' and parents:
660 # If a directory is replacing a file, mark the previous
660 # If a directory is replacing a file, mark the previous
661 # file as deleted
661 # file as deleted
662 pmodule, prevnum = self.revsplit(parents[0])[1:]
662 pmodule, prevnum = self.revsplit(parents[0])[1:]
663 pkind = self._checkpath(entrypath, prevnum, pmodule)
663 pkind = self._checkpath(entrypath, prevnum, pmodule)
664 if pkind == svn.core.svn_node_file:
664 if pkind == svn.core.svn_node_file:
665 removed.add(self.recode(entrypath))
665 removed.add(self.recode(entrypath))
666
666
667 for childpath in self._iterfiles(path, revnum):
667 for childpath in self._iterfiles(path, revnum):
668 childpath = self.getrelpath("/" + childpath)
668 childpath = self.getrelpath("/" + childpath)
669 if childpath:
669 if childpath:
670 changed.add(self.recode(childpath))
670 changed.add(self.recode(childpath))
671
671
672 # Handle directory copies
672 # Handle directory copies
673 if not ent.copyfrom_path or not parents:
673 if not ent.copyfrom_path or not parents:
674 continue
674 continue
675 # Copy sources not in parent revisions cannot be
675 # Copy sources not in parent revisions cannot be
676 # represented, ignore their origin for now
676 # represented, ignore their origin for now
677 pmodule, prevnum = self.revsplit(parents[0])[1:]
677 pmodule, prevnum = self.revsplit(parents[0])[1:]
678 if ent.copyfrom_rev < prevnum:
678 if ent.copyfrom_rev < prevnum:
679 continue
679 continue
680 copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
680 copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
681 if not copyfrompath:
681 if not copyfrompath:
682 continue
682 continue
683 self.ui.debug("mark %s came from %s:%d\n"
683 self.ui.debug("mark %s came from %s:%d\n"
684 % (path, copyfrompath, ent.copyfrom_rev))
684 % (path, copyfrompath, ent.copyfrom_rev))
685 children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev)
685 children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev)
686 for childpath in children:
686 for childpath in children:
687 childpath = self.getrelpath("/" + childpath, pmodule)
687 childpath = self.getrelpath("/" + childpath, pmodule)
688 if not childpath:
688 if not childpath:
689 continue
689 continue
690 copytopath = path + childpath[len(copyfrompath):]
690 copytopath = path + childpath[len(copyfrompath):]
691 copytopath = self.getrelpath(copytopath)
691 copytopath = self.getrelpath(copytopath)
692 copies[self.recode(copytopath)] = self.recode(childpath)
692 copies[self.recode(copytopath)] = self.recode(childpath)
693
693
694 self.ui.progress(_('scanning paths'), None)
694 self.ui.progress(_('scanning paths'), None)
695 changed.update(removed)
695 changed.update(removed)
696 return (list(changed), removed, copies)
696 return (list(changed), removed, copies)
697
697
698 def _fetch_revisions(self, from_revnum, to_revnum):
698 def _fetch_revisions(self, from_revnum, to_revnum):
699 if from_revnum < to_revnum:
699 if from_revnum < to_revnum:
700 from_revnum, to_revnum = to_revnum, from_revnum
700 from_revnum, to_revnum = to_revnum, from_revnum
701
701
702 self.child_cset = None
702 self.child_cset = None
703
703
704 def parselogentry(orig_paths, revnum, author, date, message):
704 def parselogentry(orig_paths, revnum, author, date, message):
705 """Return the parsed commit object or None, and True if
705 """Return the parsed commit object or None, and True if
706 the revision is a branch root.
706 the revision is a branch root.
707 """
707 """
708 self.ui.debug("parsing revision %d (%d changes)\n" %
708 self.ui.debug("parsing revision %d (%d changes)\n" %
709 (revnum, len(orig_paths)))
709 (revnum, len(orig_paths)))
710
710
711 branched = False
711 branched = False
712 rev = self.revid(revnum)
712 rev = self.revid(revnum)
713 # branch log might return entries for a parent we already have
713 # branch log might return entries for a parent we already have
714
714
715 if rev in self.commits or revnum < to_revnum:
715 if rev in self.commits or revnum < to_revnum:
716 return None, branched
716 return None, branched
717
717
718 parents = []
718 parents = []
719 # check whether this revision is the start of a branch or part
719 # check whether this revision is the start of a branch or part
720 # of a branch renaming
720 # of a branch renaming
721 orig_paths = sorted(orig_paths.iteritems())
721 orig_paths = sorted(orig_paths.iteritems())
722 root_paths = [(p, e) for p, e in orig_paths
722 root_paths = [(p, e) for p, e in orig_paths
723 if self.module.startswith(p)]
723 if self.module.startswith(p)]
724 if root_paths:
724 if root_paths:
725 path, ent = root_paths[-1]
725 path, ent = root_paths[-1]
726 if ent.copyfrom_path:
726 if ent.copyfrom_path:
727 branched = True
727 branched = True
728 newpath = ent.copyfrom_path + self.module[len(path):]
728 newpath = ent.copyfrom_path + self.module[len(path):]
729 # ent.copyfrom_rev may not be the actual last revision
729 # ent.copyfrom_rev may not be the actual last revision
730 previd = self.latest(newpath, ent.copyfrom_rev)
730 previd = self.latest(newpath, ent.copyfrom_rev)
731 if previd is not None:
731 if previd is not None:
732 prevmodule, prevnum = self.revsplit(previd)[1:]
732 prevmodule, prevnum = self.revsplit(previd)[1:]
733 if prevnum >= self.startrev:
733 if prevnum >= self.startrev:
734 parents = [previd]
734 parents = [previd]
735 self.ui.note(
735 self.ui.note(
736 _('found parent of branch %s at %d: %s\n') %
736 _('found parent of branch %s at %d: %s\n') %
737 (self.module, prevnum, prevmodule))
737 (self.module, prevnum, prevmodule))
738 else:
738 else:
739 self.ui.debug("no copyfrom path, don't know what to do.\n")
739 self.ui.debug("no copyfrom path, don't know what to do.\n")
740
740
741 paths = []
741 paths = []
742 # filter out unrelated paths
742 # filter out unrelated paths
743 for path, ent in orig_paths:
743 for path, ent in orig_paths:
744 if self.getrelpath(path) is None:
744 if self.getrelpath(path) is None:
745 continue
745 continue
746 paths.append((path, ent))
746 paths.append((path, ent))
747
747
748 # Example SVN datetime. Includes microseconds.
748 # Example SVN datetime. Includes microseconds.
749 # ISO-8601 conformant
749 # ISO-8601 conformant
750 # '2007-01-04T17:35:00.902377Z'
750 # '2007-01-04T17:35:00.902377Z'
751 date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
751 date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
752
752
753 log = message and self.recode(message) or ''
753 log = message and self.recode(message) or ''
754 author = author and self.recode(author) or ''
754 author = author and self.recode(author) or ''
755 try:
755 try:
756 branch = self.module.split("/")[-1]
756 branch = self.module.split("/")[-1]
757 if branch == 'trunk':
757 if branch == 'trunk':
758 branch = ''
758 branch = ''
759 except IndexError:
759 except IndexError:
760 branch = None
760 branch = None
761
761
762 cset = commit(author=author,
762 cset = commit(author=author,
763 date=util.datestr(date),
763 date=util.datestr(date),
764 desc=log,
764 desc=log,
765 parents=parents,
765 parents=parents,
766 branch=branch,
766 branch=branch,
767 rev=rev)
767 rev=rev)
768
768
769 self.commits[rev] = cset
769 self.commits[rev] = cset
770 # The parents list is *shared* among self.paths and the
770 # The parents list is *shared* among self.paths and the
771 # commit object. Both will be updated below.
771 # commit object. Both will be updated below.
772 self.paths[rev] = (paths, cset.parents)
772 self.paths[rev] = (paths, cset.parents)
773 if self.child_cset and not self.child_cset.parents:
773 if self.child_cset and not self.child_cset.parents:
774 self.child_cset.parents[:] = [rev]
774 self.child_cset.parents[:] = [rev]
775 self.child_cset = cset
775 self.child_cset = cset
776 return cset, branched
776 return cset, branched
777
777
778 self.ui.note(_('fetching revision log for "%s" from %d to %d\n') %
778 self.ui.note(_('fetching revision log for "%s" from %d to %d\n') %
779 (self.module, from_revnum, to_revnum))
779 (self.module, from_revnum, to_revnum))
780
780
781 try:
781 try:
782 firstcset = None
782 firstcset = None
783 lastonbranch = False
783 lastonbranch = False
784 stream = self._getlog([self.module], from_revnum, to_revnum)
784 stream = self._getlog([self.module], from_revnum, to_revnum)
785 try:
785 try:
786 for entry in stream:
786 for entry in stream:
787 paths, revnum, author, date, message = entry
787 paths, revnum, author, date, message = entry
788 if revnum < self.startrev:
788 if revnum < self.startrev:
789 lastonbranch = True
789 lastonbranch = True
790 break
790 break
791 if not paths:
791 if not paths:
792 self.ui.debug('revision %d has no entries\n' % revnum)
792 self.ui.debug('revision %d has no entries\n' % revnum)
793 # If we ever leave the loop on an empty
793 # If we ever leave the loop on an empty
794 # revision, do not try to get a parent branch
794 # revision, do not try to get a parent branch
795 lastonbranch = lastonbranch or revnum == 0
795 lastonbranch = lastonbranch or revnum == 0
796 continue
796 continue
797 cset, lastonbranch = parselogentry(paths, revnum, author,
797 cset, lastonbranch = parselogentry(paths, revnum, author,
798 date, message)
798 date, message)
799 if cset:
799 if cset:
800 firstcset = cset
800 firstcset = cset
801 if lastonbranch:
801 if lastonbranch:
802 break
802 break
803 finally:
803 finally:
804 stream.close()
804 stream.close()
805
805
806 if not lastonbranch and firstcset and not firstcset.parents:
806 if not lastonbranch and firstcset and not firstcset.parents:
807 # The first revision of the sequence (the last fetched one)
807 # The first revision of the sequence (the last fetched one)
808 # has invalid parents if not a branch root. Find the parent
808 # has invalid parents if not a branch root. Find the parent
809 # revision now, if any.
809 # revision now, if any.
810 try:
810 try:
811 firstrevnum = self.revnum(firstcset.rev)
811 firstrevnum = self.revnum(firstcset.rev)
812 if firstrevnum > 1:
812 if firstrevnum > 1:
813 latest = self.latest(self.module, firstrevnum - 1)
813 latest = self.latest(self.module, firstrevnum - 1)
814 if latest:
814 if latest:
815 firstcset.parents.append(latest)
815 firstcset.parents.append(latest)
816 except SvnPathNotFound:
816 except SvnPathNotFound:
817 pass
817 pass
818 except SubversionException, (inst, num):
818 except SubversionException, (inst, num):
819 if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
819 if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
820 raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
820 raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
821 raise
821 raise
822
822
823 def getfile(self, file, rev):
823 def getfile(self, file, rev):
824 # TODO: ra.get_file transmits the whole file instead of diffs.
824 # TODO: ra.get_file transmits the whole file instead of diffs.
825 if file in self.removed:
825 if file in self.removed:
826 raise IOError()
826 raise IOError()
827 mode = ''
827 mode = ''
828 try:
828 try:
829 new_module, revnum = self.revsplit(rev)[1:]
829 new_module, revnum = self.revsplit(rev)[1:]
830 if self.module != new_module:
830 if self.module != new_module:
831 self.module = new_module
831 self.module = new_module
832 self.reparent(self.module)
832 self.reparent(self.module)
833 io = StringIO()
833 io = StringIO()
834 info = svn.ra.get_file(self.ra, file, revnum, io)
834 info = svn.ra.get_file(self.ra, file, revnum, io)
835 data = io.getvalue()
835 data = io.getvalue()
836 # ra.get_files() seems to keep a reference on the input buffer
836 # ra.get_files() seems to keep a reference on the input buffer
837 # preventing collection. Release it explicitely.
837 # preventing collection. Release it explicitely.
838 io.close()
838 io.close()
839 if isinstance(info, list):
839 if isinstance(info, list):
840 info = info[-1]
840 info = info[-1]
841 mode = ("svn:executable" in info) and 'x' or ''
841 mode = ("svn:executable" in info) and 'x' or ''
842 mode = ("svn:special" in info) and 'l' or mode
842 mode = ("svn:special" in info) and 'l' or mode
843 except SubversionException, e:
843 except SubversionException, e:
844 notfound = (svn.core.SVN_ERR_FS_NOT_FOUND,
844 notfound = (svn.core.SVN_ERR_FS_NOT_FOUND,
845 svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND)
845 svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND)
846 if e.apr_err in notfound: # File not found
846 if e.apr_err in notfound: # File not found
847 raise IOError()
847 raise IOError()
848 raise
848 raise
849 if mode == 'l':
849 if mode == 'l':
850 link_prefix = "link "
850 link_prefix = "link "
851 if data.startswith(link_prefix):
851 if data.startswith(link_prefix):
852 data = data[len(link_prefix):]
852 data = data[len(link_prefix):]
853 return data, mode
853 return data, mode
854
854
855 def _iterfiles(self, path, revnum):
855 def _iterfiles(self, path, revnum):
856 """Enumerate all files in path at revnum, recursively."""
856 """Enumerate all files in path at revnum, recursively."""
857 path = path.strip('/')
857 path = path.strip('/')
858 pool = Pool()
858 pool = Pool()
859 rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/')
859 rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/')
860 entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool)
860 entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool)
861 return ((path + '/' + p) for p, e in entries.iteritems()
861 return ((path + '/' + p) for p, e in entries.iteritems()
862 if e.kind == svn.core.svn_node_file)
862 if e.kind == svn.core.svn_node_file)
863
863
864 def getrelpath(self, path, module=None):
864 def getrelpath(self, path, module=None):
865 if module is None:
865 if module is None:
866 module = self.module
866 module = self.module
867 # Given the repository url of this wc, say
867 # Given the repository url of this wc, say
868 # "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
868 # "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
869 # extract the "entry" portion (a relative path) from what
869 # extract the "entry" portion (a relative path) from what
870 # svn log --xml says, ie
870 # svn log --xml says, ie
871 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
871 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
872 # that is to say "tests/PloneTestCase.py"
872 # that is to say "tests/PloneTestCase.py"
873 if path.startswith(module):
873 if path.startswith(module):
874 relative = path.rstrip('/')[len(module):]
874 relative = path.rstrip('/')[len(module):]
875 if relative.startswith('/'):
875 if relative.startswith('/'):
876 return relative[1:]
876 return relative[1:]
877 elif relative == '':
877 elif relative == '':
878 return relative
878 return relative
879
879
880 # The path is outside our tracked tree...
880 # The path is outside our tracked tree...
881 self.ui.debug('%r is not under %r, ignoring\n' % (path, module))
881 self.ui.debug('%r is not under %r, ignoring\n' % (path, module))
882 return None
882 return None
883
883
884 def _checkpath(self, path, revnum, module=None):
884 def _checkpath(self, path, revnum, module=None):
885 if module is not None:
885 if module is not None:
886 prevmodule = self.reparent('')
886 prevmodule = self.reparent('')
887 path = module + '/' + path
887 path = module + '/' + path
888 try:
888 try:
889 # ra.check_path does not like leading slashes very much, it leads
889 # ra.check_path does not like leading slashes very much, it leads
890 # to PROPFIND subversion errors
890 # to PROPFIND subversion errors
891 return svn.ra.check_path(self.ra, path.strip('/'), revnum)
891 return svn.ra.check_path(self.ra, path.strip('/'), revnum)
892 finally:
892 finally:
893 if module is not None:
893 if module is not None:
894 self.reparent(prevmodule)
894 self.reparent(prevmodule)
895
895
896 def _getlog(self, paths, start, end, limit=0, discover_changed_paths=True,
896 def _getlog(self, paths, start, end, limit=0, discover_changed_paths=True,
897 strict_node_history=False):
897 strict_node_history=False):
898 # Normalize path names, svn >= 1.5 only wants paths relative to
898 # Normalize path names, svn >= 1.5 only wants paths relative to
899 # supplied URL
899 # supplied URL
900 relpaths = []
900 relpaths = []
901 for p in paths:
901 for p in paths:
902 if not p.startswith('/'):
902 if not p.startswith('/'):
903 p = self.module + '/' + p
903 p = self.module + '/' + p
904 relpaths.append(p.strip('/'))
904 relpaths.append(p.strip('/'))
905 args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
905 args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
906 strict_node_history]
906 strict_node_history]
907 arg = encodeargs(args)
907 arg = encodeargs(args)
908 hgexe = util.hgexecutable()
908 hgexe = util.hgexecutable()
909 cmd = '%s debugsvnlog' % util.shellquote(hgexe)
909 cmd = '%s debugsvnlog' % util.shellquote(hgexe)
910 stdin, stdout = util.popen2(cmd)
910 stdin, stdout = util.popen2(cmd)
911 stdin.write(arg)
911 stdin.write(arg)
912 try:
912 try:
913 stdin.close()
913 stdin.close()
914 except IOError:
914 except IOError:
915 raise util.Abort(_('Mercurial failed to run itself, check'
915 raise util.Abort(_('Mercurial failed to run itself, check'
916 ' hg executable is in PATH'))
916 ' hg executable is in PATH'))
917 return logstream(stdout)
917 return logstream(stdout)
918
918
919 pre_revprop_change = '''#!/bin/sh
919 pre_revprop_change = '''#!/bin/sh
920
920
921 REPOS="$1"
921 REPOS="$1"
922 REV="$2"
922 REV="$2"
923 USER="$3"
923 USER="$3"
924 PROPNAME="$4"
924 PROPNAME="$4"
925 ACTION="$5"
925 ACTION="$5"
926
926
927 if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
927 if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
928 if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi
928 if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi
929 if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi
929 if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi
930
930
931 echo "Changing prohibited revision property" >&2
931 echo "Changing prohibited revision property" >&2
932 exit 1
932 exit 1
933 '''
933 '''
934
934
935 class svn_sink(converter_sink, commandline):
935 class svn_sink(converter_sink, commandline):
936 commit_re = re.compile(r'Committed revision (\d+).', re.M)
936 commit_re = re.compile(r'Committed revision (\d+).', re.M)
937
937
938 def prerun(self):
938 def prerun(self):
939 if self.wc:
939 if self.wc:
940 os.chdir(self.wc)
940 os.chdir(self.wc)
941
941
942 def postrun(self):
942 def postrun(self):
943 if self.wc:
943 if self.wc:
944 os.chdir(self.cwd)
944 os.chdir(self.cwd)
945
945
946 def join(self, name):
946 def join(self, name):
947 return os.path.join(self.wc, '.svn', name)
947 return os.path.join(self.wc, '.svn', name)
948
948
949 def revmapfile(self):
949 def revmapfile(self):
950 return self.join('hg-shamap')
950 return self.join('hg-shamap')
951
951
952 def authorfile(self):
952 def authorfile(self):
953 return self.join('hg-authormap')
953 return self.join('hg-authormap')
954
954
955 def __init__(self, ui, path):
955 def __init__(self, ui, path):
956 converter_sink.__init__(self, ui, path)
956 converter_sink.__init__(self, ui, path)
957 commandline.__init__(self, ui, 'svn')
957 commandline.__init__(self, ui, 'svn')
958 self.delete = []
958 self.delete = []
959 self.setexec = []
959 self.setexec = []
960 self.delexec = []
960 self.delexec = []
961 self.copies = []
961 self.copies = []
962 self.wc = None
962 self.wc = None
963 self.cwd = os.getcwd()
963 self.cwd = os.getcwd()
964
964
965 path = os.path.realpath(path)
965 path = os.path.realpath(path)
966
966
967 created = False
967 created = False
968 if os.path.isfile(os.path.join(path, '.svn', 'entries')):
968 if os.path.isfile(os.path.join(path, '.svn', 'entries')):
969 self.wc = path
969 self.wc = path
970 self.run0('update')
970 self.run0('update')
971 else:
971 else:
972 wcpath = os.path.join(os.getcwd(), os.path.basename(path) + '-wc')
972 wcpath = os.path.join(os.getcwd(), os.path.basename(path) + '-wc')
973
973
974 if os.path.isdir(os.path.dirname(path)):
974 if os.path.isdir(os.path.dirname(path)):
975 if not os.path.exists(os.path.join(path, 'db', 'fs-type')):
975 if not os.path.exists(os.path.join(path, 'db', 'fs-type')):
976 ui.status(_('initializing svn repository %r\n') %
976 ui.status(_('initializing svn repository %r\n') %
977 os.path.basename(path))
977 os.path.basename(path))
978 commandline(ui, 'svnadmin').run0('create', path)
978 commandline(ui, 'svnadmin').run0('create', path)
979 created = path
979 created = path
980 path = util.normpath(path)
980 path = util.normpath(path)
981 if not path.startswith('/'):
981 if not path.startswith('/'):
982 path = '/' + path
982 path = '/' + path
983 path = 'file://' + path
983 path = 'file://' + path
984
984
985 ui.status(_('initializing svn working copy %r\n')
985 ui.status(_('initializing svn working copy %r\n')
986 % os.path.basename(wcpath))
986 % os.path.basename(wcpath))
987 self.run0('checkout', path, wcpath)
987 self.run0('checkout', path, wcpath)
988
988
989 self.wc = wcpath
989 self.wc = wcpath
990 self.opener = util.opener(self.wc)
990 self.opener = util.opener(self.wc)
991 self.wopener = util.opener(self.wc)
991 self.wopener = util.opener(self.wc)
992 self.childmap = mapfile(ui, self.join('hg-childmap'))
992 self.childmap = mapfile(ui, self.join('hg-childmap'))
993 self.is_exec = util.checkexec(self.wc) and util.is_exec or None
993 self.is_exec = util.checkexec(self.wc) and util.is_exec or None
994
994
995 if created:
995 if created:
996 hook = os.path.join(created, 'hooks', 'pre-revprop-change')
996 hook = os.path.join(created, 'hooks', 'pre-revprop-change')
997 fp = open(hook, 'w')
997 fp = open(hook, 'w')
998 fp.write(pre_revprop_change)
998 fp.write(pre_revprop_change)
999 fp.close()
999 fp.close()
1000 util.set_flags(hook, False, True)
1000 util.set_flags(hook, False, True)
1001
1001
1002 xport = transport.SvnRaTransport(url=geturl(path))
1002 xport = transport.SvnRaTransport(url=geturl(path))
1003 self.uuid = svn.ra.get_uuid(xport.ra)
1003 self.uuid = svn.ra.get_uuid(xport.ra)
1004
1004
1005 def wjoin(self, *names):
1005 def wjoin(self, *names):
1006 return os.path.join(self.wc, *names)
1006 return os.path.join(self.wc, *names)
1007
1007
1008 def putfile(self, filename, flags, data):
1008 def putfile(self, filename, flags, data):
1009 if 'l' in flags:
1009 if 'l' in flags:
1010 self.wopener.symlink(data, filename)
1010 self.wopener.symlink(data, filename)
1011 else:
1011 else:
1012 try:
1012 try:
1013 if os.path.islink(self.wjoin(filename)):
1013 if os.path.islink(self.wjoin(filename)):
1014 os.unlink(filename)
1014 os.unlink(filename)
1015 except OSError:
1015 except OSError:
1016 pass
1016 pass
1017 self.wopener(filename, 'w').write(data)
1017 self.wopener(filename, 'w').write(data)
1018
1018
1019 if self.is_exec:
1019 if self.is_exec:
1020 was_exec = self.is_exec(self.wjoin(filename))
1020 was_exec = self.is_exec(self.wjoin(filename))
1021 else:
1021 else:
1022 # On filesystems not supporting execute-bit, there is no way
1022 # On filesystems not supporting execute-bit, there is no way
1023 # to know if it is set but asking subversion. Setting it
1023 # to know if it is set but asking subversion. Setting it
1024 # systematically is just as expensive and much simpler.
1024 # systematically is just as expensive and much simpler.
1025 was_exec = 'x' not in flags
1025 was_exec = 'x' not in flags
1026
1026
1027 util.set_flags(self.wjoin(filename), False, 'x' in flags)
1027 util.set_flags(self.wjoin(filename), False, 'x' in flags)
1028 if was_exec:
1028 if was_exec:
1029 if 'x' not in flags:
1029 if 'x' not in flags:
1030 self.delexec.append(filename)
1030 self.delexec.append(filename)
1031 else:
1031 else:
1032 if 'x' in flags:
1032 if 'x' in flags:
1033 self.setexec.append(filename)
1033 self.setexec.append(filename)
1034
1034
1035 def _copyfile(self, source, dest):
1035 def _copyfile(self, source, dest):
1036 # SVN's copy command pukes if the destination file exists, but
1036 # SVN's copy command pukes if the destination file exists, but
1037 # our copyfile method expects to record a copy that has
1037 # our copyfile method expects to record a copy that has
1038 # already occurred. Cross the semantic gap.
1038 # already occurred. Cross the semantic gap.
1039 wdest = self.wjoin(dest)
1039 wdest = self.wjoin(dest)
1040 exists = os.path.exists(wdest)
1040 exists = os.path.exists(wdest)
1041 if exists:
1041 if exists:
1042 fd, tempname = tempfile.mkstemp(
1042 fd, tempname = tempfile.mkstemp(
1043 prefix='hg-copy-', dir=os.path.dirname(wdest))
1043 prefix='hg-copy-', dir=os.path.dirname(wdest))
1044 os.close(fd)
1044 os.close(fd)
1045 os.unlink(tempname)
1045 os.unlink(tempname)
1046 os.rename(wdest, tempname)
1046 os.rename(wdest, tempname)
1047 try:
1047 try:
1048 self.run0('copy', source, dest)
1048 self.run0('copy', source, dest)
1049 finally:
1049 finally:
1050 if exists:
1050 if exists:
1051 try:
1051 try:
1052 os.unlink(wdest)
1052 os.unlink(wdest)
1053 except OSError:
1053 except OSError:
1054 pass
1054 pass
1055 os.rename(tempname, wdest)
1055 os.rename(tempname, wdest)
1056
1056
1057 def dirs_of(self, files):
1057 def dirs_of(self, files):
1058 dirs = set()
1058 dirs = set()
1059 for f in files:
1059 for f in files:
1060 if os.path.isdir(self.wjoin(f)):
1060 if os.path.isdir(self.wjoin(f)):
1061 dirs.add(f)
1061 dirs.add(f)
1062 for i in strutil.rfindall(f, '/'):
1062 for i in strutil.rfindall(f, '/'):
1063 dirs.add(f[:i])
1063 dirs.add(f[:i])
1064 return dirs
1064 return dirs
1065
1065
1066 def add_dirs(self, files):
1066 def add_dirs(self, files):
1067 add_dirs = [d for d in sorted(self.dirs_of(files))
1067 add_dirs = [d for d in sorted(self.dirs_of(files))
1068 if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
1068 if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
1069 if add_dirs:
1069 if add_dirs:
1070 self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
1070 self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
1071 return add_dirs
1071 return add_dirs
1072
1072
1073 def add_files(self, files):
1073 def add_files(self, files):
1074 if files:
1074 if files:
1075 self.xargs(files, 'add', quiet=True)
1075 self.xargs(files, 'add', quiet=True)
1076 return files
1076 return files
1077
1077
1078 def tidy_dirs(self, names):
1078 def tidy_dirs(self, names):
1079 deleted = []
1079 deleted = []
1080 for d in sorted(self.dirs_of(names), reverse=True):
1080 for d in sorted(self.dirs_of(names), reverse=True):
1081 wd = self.wjoin(d)
1081 wd = self.wjoin(d)
1082 if os.listdir(wd) == '.svn':
1082 if os.listdir(wd) == '.svn':
1083 self.run0('delete', d)
1083 self.run0('delete', d)
1084 deleted.append(d)
1084 deleted.append(d)
1085 return deleted
1085 return deleted
1086
1086
1087 def addchild(self, parent, child):
1087 def addchild(self, parent, child):
1088 self.childmap[parent] = child
1088 self.childmap[parent] = child
1089
1089
1090 def revid(self, rev):
1090 def revid(self, rev):
1091 return u"svn:%s@%s" % (self.uuid, rev)
1091 return u"svn:%s@%s" % (self.uuid, rev)
1092
1092
1093 def putcommit(self, files, copies, parents, commit, source, revmap):
1093 def putcommit(self, files, copies, parents, commit, source, revmap):
1094 # Apply changes to working copy
1094 # Apply changes to working copy
1095 for f, v in files:
1095 for f, v in files:
1096 try:
1096 try:
1097 data, mode = source.getfile(f, v)
1097 data, mode = source.getfile(f, v)
1098 except IOError:
1098 except IOError:
1099 self.delete.append(f)
1099 self.delete.append(f)
1100 else:
1100 else:
1101 self.putfile(f, mode, data)
1101 self.putfile(f, mode, data)
1102 if f in copies:
1102 if f in copies:
1103 self.copies.append([copies[f], f])
1103 self.copies.append([copies[f], f])
1104 files = [f[0] for f in files]
1104 files = [f[0] for f in files]
1105
1105
1106 for parent in parents:
1106 for parent in parents:
1107 try:
1107 try:
1108 return self.revid(self.childmap[parent])
1108 return self.revid(self.childmap[parent])
1109 except KeyError:
1109 except KeyError:
1110 pass
1110 pass
1111 entries = set(self.delete)
1111 entries = set(self.delete)
1112 files = frozenset(files)
1112 files = frozenset(files)
1113 entries.update(self.add_dirs(files.difference(entries)))
1113 entries.update(self.add_dirs(files.difference(entries)))
1114 if self.copies:
1114 if self.copies:
1115 for s, d in self.copies:
1115 for s, d in self.copies:
1116 self._copyfile(s, d)
1116 self._copyfile(s, d)
1117 self.copies = []
1117 self.copies = []
1118 if self.delete:
1118 if self.delete:
1119 self.xargs(self.delete, 'delete')
1119 self.xargs(self.delete, 'delete')
1120 self.delete = []
1120 self.delete = []
1121 entries.update(self.add_files(files.difference(entries)))
1121 entries.update(self.add_files(files.difference(entries)))
1122 entries.update(self.tidy_dirs(entries))
1122 entries.update(self.tidy_dirs(entries))
1123 if self.delexec:
1123 if self.delexec:
1124 self.xargs(self.delexec, 'propdel', 'svn:executable')
1124 self.xargs(self.delexec, 'propdel', 'svn:executable')
1125 self.delexec = []
1125 self.delexec = []
1126 if self.setexec:
1126 if self.setexec:
1127 self.xargs(self.setexec, 'propset', 'svn:executable', '*')
1127 self.xargs(self.setexec, 'propset', 'svn:executable', '*')
1128 self.setexec = []
1128 self.setexec = []
1129
1129
1130 fd, messagefile = tempfile.mkstemp(prefix='hg-convert-')
1130 fd, messagefile = tempfile.mkstemp(prefix='hg-convert-')
1131 fp = os.fdopen(fd, 'w')
1131 fp = os.fdopen(fd, 'w')
1132 fp.write(commit.desc)
1132 fp.write(commit.desc)
1133 fp.close()
1133 fp.close()
1134 try:
1134 try:
1135 output = self.run0('commit',
1135 output = self.run0('commit',
1136 username=util.shortuser(commit.author),
1136 username=util.shortuser(commit.author),
1137 file=messagefile,
1137 file=messagefile,
1138 encoding='utf-8')
1138 encoding='utf-8')
1139 try:
1139 try:
1140 rev = self.commit_re.search(output).group(1)
1140 rev = self.commit_re.search(output).group(1)
1141 except AttributeError:
1141 except AttributeError:
1142 if not files:
1142 if not files:
1143 return parents[0]
1143 return parents[0]
1144 self.ui.warn(_('unexpected svn output:\n'))
1144 self.ui.warn(_('unexpected svn output:\n'))
1145 self.ui.warn(output)
1145 self.ui.warn(output)
1146 raise util.Abort(_('unable to cope with svn output'))
1146 raise util.Abort(_('unable to cope with svn output'))
1147 if commit.rev:
1147 if commit.rev:
1148 self.run('propset', 'hg:convert-rev', commit.rev,
1148 self.run('propset', 'hg:convert-rev', commit.rev,
1149 revprop=True, revision=rev)
1149 revprop=True, revision=rev)
1150 if commit.branch and commit.branch != 'default':
1150 if commit.branch and commit.branch != 'default':
1151 self.run('propset', 'hg:convert-branch', commit.branch,
1151 self.run('propset', 'hg:convert-branch', commit.branch,
1152 revprop=True, revision=rev)
1152 revprop=True, revision=rev)
1153 for parent in parents:
1153 for parent in parents:
1154 self.addchild(parent, rev)
1154 self.addchild(parent, rev)
1155 return self.revid(rev)
1155 return self.revid(rev)
1156 finally:
1156 finally:
1157 os.unlink(messagefile)
1157 os.unlink(messagefile)
1158
1158
1159 def puttags(self, tags):
1159 def puttags(self, tags):
1160 self.ui.warn(_('XXX TAGS NOT IMPLEMENTED YET\n'))
1160 self.ui.warn(_('writing Subversion tags is not yet implemented\n'))
1161 return None, None
@@ -1,382 +1,382 b''
1 # ASCII graph log extension for Mercurial
1 # ASCII graph log extension for Mercurial
2 #
2 #
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to view revision graphs from a shell
8 '''command to view revision graphs from a shell
9
9
10 This extension adds a --graph option to the incoming, outgoing and log
10 This extension adds a --graph option to the incoming, outgoing and log
11 commands. When this options is given, an ASCII representation of the
11 commands. When this options is given, an ASCII representation of the
12 revision graph is also shown.
12 revision graph is also shown.
13 '''
13 '''
14
14
15 import os
15 import os
16 from mercurial.cmdutil import revrange, show_changeset
16 from mercurial.cmdutil import revrange, show_changeset
17 from mercurial.commands import templateopts
17 from mercurial.commands import templateopts
18 from mercurial.i18n import _
18 from mercurial.i18n import _
19 from mercurial.node import nullrev
19 from mercurial.node import nullrev
20 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
20 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
21 from mercurial import hg, url, util, graphmod, discovery
21 from mercurial import hg, url, util, graphmod, discovery
22
22
23 ASCIIDATA = 'ASC'
23 ASCIIDATA = 'ASC'
24
24
25 def asciiedges(seen, rev, parents):
25 def asciiedges(seen, rev, parents):
26 """adds edge info to changelog DAG walk suitable for ascii()"""
26 """adds edge info to changelog DAG walk suitable for ascii()"""
27 if rev not in seen:
27 if rev not in seen:
28 seen.append(rev)
28 seen.append(rev)
29 nodeidx = seen.index(rev)
29 nodeidx = seen.index(rev)
30
30
31 knownparents = []
31 knownparents = []
32 newparents = []
32 newparents = []
33 for parent in parents:
33 for parent in parents:
34 if parent in seen:
34 if parent in seen:
35 knownparents.append(parent)
35 knownparents.append(parent)
36 else:
36 else:
37 newparents.append(parent)
37 newparents.append(parent)
38
38
39 ncols = len(seen)
39 ncols = len(seen)
40 seen[nodeidx:nodeidx + 1] = newparents
40 seen[nodeidx:nodeidx + 1] = newparents
41 edges = [(nodeidx, seen.index(p)) for p in knownparents]
41 edges = [(nodeidx, seen.index(p)) for p in knownparents]
42
42
43 if len(newparents) > 0:
43 if len(newparents) > 0:
44 edges.append((nodeidx, nodeidx))
44 edges.append((nodeidx, nodeidx))
45 if len(newparents) > 1:
45 if len(newparents) > 1:
46 edges.append((nodeidx, nodeidx + 1))
46 edges.append((nodeidx, nodeidx + 1))
47
47
48 nmorecols = len(seen) - ncols
48 nmorecols = len(seen) - ncols
49 return nodeidx, edges, ncols, nmorecols
49 return nodeidx, edges, ncols, nmorecols
50
50
51 def fix_long_right_edges(edges):
51 def fix_long_right_edges(edges):
52 for (i, (start, end)) in enumerate(edges):
52 for (i, (start, end)) in enumerate(edges):
53 if end > start:
53 if end > start:
54 edges[i] = (start, end + 1)
54 edges[i] = (start, end + 1)
55
55
56 def get_nodeline_edges_tail(
56 def get_nodeline_edges_tail(
57 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
57 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
58 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
58 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
59 # Still going in the same non-vertical direction.
59 # Still going in the same non-vertical direction.
60 if n_columns_diff == -1:
60 if n_columns_diff == -1:
61 start = max(node_index + 1, p_node_index)
61 start = max(node_index + 1, p_node_index)
62 tail = ["|", " "] * (start - node_index - 1)
62 tail = ["|", " "] * (start - node_index - 1)
63 tail.extend(["/", " "] * (n_columns - start))
63 tail.extend(["/", " "] * (n_columns - start))
64 return tail
64 return tail
65 else:
65 else:
66 return ["\\", " "] * (n_columns - node_index - 1)
66 return ["\\", " "] * (n_columns - node_index - 1)
67 else:
67 else:
68 return ["|", " "] * (n_columns - node_index - 1)
68 return ["|", " "] * (n_columns - node_index - 1)
69
69
70 def draw_edges(edges, nodeline, interline):
70 def draw_edges(edges, nodeline, interline):
71 for (start, end) in edges:
71 for (start, end) in edges:
72 if start == end + 1:
72 if start == end + 1:
73 interline[2 * end + 1] = "/"
73 interline[2 * end + 1] = "/"
74 elif start == end - 1:
74 elif start == end - 1:
75 interline[2 * start + 1] = "\\"
75 interline[2 * start + 1] = "\\"
76 elif start == end:
76 elif start == end:
77 interline[2 * start] = "|"
77 interline[2 * start] = "|"
78 else:
78 else:
79 nodeline[2 * end] = "+"
79 nodeline[2 * end] = "+"
80 if start > end:
80 if start > end:
81 (start, end) = (end, start)
81 (start, end) = (end, start)
82 for i in range(2 * start + 1, 2 * end):
82 for i in range(2 * start + 1, 2 * end):
83 if nodeline[i] != "+":
83 if nodeline[i] != "+":
84 nodeline[i] = "-"
84 nodeline[i] = "-"
85
85
86 def get_padding_line(ni, n_columns, edges):
86 def get_padding_line(ni, n_columns, edges):
87 line = []
87 line = []
88 line.extend(["|", " "] * ni)
88 line.extend(["|", " "] * ni)
89 if (ni, ni - 1) in edges or (ni, ni) in edges:
89 if (ni, ni - 1) in edges or (ni, ni) in edges:
90 # (ni, ni - 1) (ni, ni)
90 # (ni, ni - 1) (ni, ni)
91 # | | | | | | | |
91 # | | | | | | | |
92 # +---o | | o---+
92 # +---o | | o---+
93 # | | c | | c | |
93 # | | c | | c | |
94 # | |/ / | |/ /
94 # | |/ / | |/ /
95 # | | | | | |
95 # | | | | | |
96 c = "|"
96 c = "|"
97 else:
97 else:
98 c = " "
98 c = " "
99 line.extend([c, " "])
99 line.extend([c, " "])
100 line.extend(["|", " "] * (n_columns - ni - 1))
100 line.extend(["|", " "] * (n_columns - ni - 1))
101 return line
101 return line
102
102
103 def asciistate():
103 def asciistate():
104 """returns the initial value for the "state" argument to ascii()"""
104 """returns the initial value for the "state" argument to ascii()"""
105 return [0, 0]
105 return [0, 0]
106
106
107 def ascii(ui, state, type, char, text, coldata):
107 def ascii(ui, state, type, char, text, coldata):
108 """prints an ASCII graph of the DAG
108 """prints an ASCII graph of the DAG
109
109
110 takes the following arguments (one call per node in the graph):
110 takes the following arguments (one call per node in the graph):
111
111
112 - ui to write to
112 - ui to write to
113 - Somewhere to keep the needed state in (init to asciistate())
113 - Somewhere to keep the needed state in (init to asciistate())
114 - Column of the current node in the set of ongoing edges.
114 - Column of the current node in the set of ongoing edges.
115 - Type indicator of node data == ASCIIDATA.
115 - Type indicator of node data == ASCIIDATA.
116 - Payload: (char, lines):
116 - Payload: (char, lines):
117 - Character to use as node's symbol.
117 - Character to use as node's symbol.
118 - List of lines to display as the node's text.
118 - List of lines to display as the node's text.
119 - Edges; a list of (col, next_col) indicating the edges between
119 - Edges; a list of (col, next_col) indicating the edges between
120 the current node and its parents.
120 the current node and its parents.
121 - Number of columns (ongoing edges) in the current revision.
121 - Number of columns (ongoing edges) in the current revision.
122 - The difference between the number of columns (ongoing edges)
122 - The difference between the number of columns (ongoing edges)
123 in the next revision and the number of columns (ongoing edges)
123 in the next revision and the number of columns (ongoing edges)
124 in the current revision. That is: -1 means one column removed;
124 in the current revision. That is: -1 means one column removed;
125 0 means no columns added or removed; 1 means one column added.
125 0 means no columns added or removed; 1 means one column added.
126 """
126 """
127
127
128 idx, edges, ncols, coldiff = coldata
128 idx, edges, ncols, coldiff = coldata
129 assert -2 < coldiff < 2
129 assert -2 < coldiff < 2
130 if coldiff == -1:
130 if coldiff == -1:
131 # Transform
131 # Transform
132 #
132 #
133 # | | | | | |
133 # | | | | | |
134 # o | | into o---+
134 # o | | into o---+
135 # |X / |/ /
135 # |X / |/ /
136 # | | | |
136 # | | | |
137 fix_long_right_edges(edges)
137 fix_long_right_edges(edges)
138
138
139 # add_padding_line says whether to rewrite
139 # add_padding_line says whether to rewrite
140 #
140 #
141 # | | | | | | | |
141 # | | | | | | | |
142 # | o---+ into | o---+
142 # | o---+ into | o---+
143 # | / / | | | # <--- padding line
143 # | / / | | | # <--- padding line
144 # o | | | / /
144 # o | | | / /
145 # o | |
145 # o | |
146 add_padding_line = (len(text) > 2 and coldiff == -1 and
146 add_padding_line = (len(text) > 2 and coldiff == -1 and
147 [x for (x, y) in edges if x + 1 < y])
147 [x for (x, y) in edges if x + 1 < y])
148
148
149 # fix_nodeline_tail says whether to rewrite
149 # fix_nodeline_tail says whether to rewrite
150 #
150 #
151 # | | o | | | | o | |
151 # | | o | | | | o | |
152 # | | |/ / | | |/ /
152 # | | |/ / | | |/ /
153 # | o | | into | o / / # <--- fixed nodeline tail
153 # | o | | into | o / / # <--- fixed nodeline tail
154 # | |/ / | |/ /
154 # | |/ / | |/ /
155 # o | | o | |
155 # o | | o | |
156 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
156 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
157
157
158 # nodeline is the line containing the node character (typically o)
158 # nodeline is the line containing the node character (typically o)
159 nodeline = ["|", " "] * idx
159 nodeline = ["|", " "] * idx
160 nodeline.extend([char, " "])
160 nodeline.extend([char, " "])
161
161
162 nodeline.extend(
162 nodeline.extend(
163 get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
163 get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
164 state[0], fix_nodeline_tail))
164 state[0], fix_nodeline_tail))
165
165
166 # shift_interline is the line containing the non-vertical
166 # shift_interline is the line containing the non-vertical
167 # edges between this entry and the next
167 # edges between this entry and the next
168 shift_interline = ["|", " "] * idx
168 shift_interline = ["|", " "] * idx
169 if coldiff == -1:
169 if coldiff == -1:
170 n_spaces = 1
170 n_spaces = 1
171 edge_ch = "/"
171 edge_ch = "/"
172 elif coldiff == 0:
172 elif coldiff == 0:
173 n_spaces = 2
173 n_spaces = 2
174 edge_ch = "|"
174 edge_ch = "|"
175 else:
175 else:
176 n_spaces = 3
176 n_spaces = 3
177 edge_ch = "\\"
177 edge_ch = "\\"
178 shift_interline.extend(n_spaces * [" "])
178 shift_interline.extend(n_spaces * [" "])
179 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
179 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
180
180
181 # draw edges from the current node to its parents
181 # draw edges from the current node to its parents
182 draw_edges(edges, nodeline, shift_interline)
182 draw_edges(edges, nodeline, shift_interline)
183
183
184 # lines is the list of all graph lines to print
184 # lines is the list of all graph lines to print
185 lines = [nodeline]
185 lines = [nodeline]
186 if add_padding_line:
186 if add_padding_line:
187 lines.append(get_padding_line(idx, ncols, edges))
187 lines.append(get_padding_line(idx, ncols, edges))
188 lines.append(shift_interline)
188 lines.append(shift_interline)
189
189
190 # make sure that there are as many graph lines as there are
190 # make sure that there are as many graph lines as there are
191 # log strings
191 # log strings
192 while len(text) < len(lines):
192 while len(text) < len(lines):
193 text.append("")
193 text.append("")
194 if len(lines) < len(text):
194 if len(lines) < len(text):
195 extra_interline = ["|", " "] * (ncols + coldiff)
195 extra_interline = ["|", " "] * (ncols + coldiff)
196 while len(lines) < len(text):
196 while len(lines) < len(text):
197 lines.append(extra_interline)
197 lines.append(extra_interline)
198
198
199 # print lines
199 # print lines
200 indentation_level = max(ncols, ncols + coldiff)
200 indentation_level = max(ncols, ncols + coldiff)
201 for (line, logstr) in zip(lines, text):
201 for (line, logstr) in zip(lines, text):
202 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
202 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
203 ui.write(ln.rstrip() + '\n')
203 ui.write(ln.rstrip() + '\n')
204
204
205 # ... and start over
205 # ... and start over
206 state[0] = coldiff
206 state[0] = coldiff
207 state[1] = idx
207 state[1] = idx
208
208
209 def get_revs(repo, rev_opt):
209 def get_revs(repo, rev_opt):
210 if rev_opt:
210 if rev_opt:
211 revs = revrange(repo, rev_opt)
211 revs = revrange(repo, rev_opt)
212 if len(revs) == 0:
212 if len(revs) == 0:
213 return (nullrev, nullrev)
213 return (nullrev, nullrev)
214 return (max(revs), min(revs))
214 return (max(revs), min(revs))
215 else:
215 else:
216 return (len(repo) - 1, 0)
216 return (len(repo) - 1, 0)
217
217
218 def check_unsupported_flags(opts):
218 def check_unsupported_flags(opts):
219 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
219 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
220 "only_merges", "user", "only_branch", "prune", "newest_first",
220 "only_merges", "user", "branch", "only_branch", "prune",
221 "no_merges", "include", "exclude"]:
221 "newest_first", "no_merges", "include", "exclude"]:
222 if op in opts and opts[op]:
222 if op in opts and opts[op]:
223 raise util.Abort(_("--graph option is incompatible with --%s")
223 raise util.Abort(_("--graph option is incompatible with --%s")
224 % op.replace("_", "-"))
224 % op.replace("_", "-"))
225
225
226 def generate(ui, dag, displayer, showparents, edgefn):
226 def generate(ui, dag, displayer, showparents, edgefn):
227 seen, state = [], asciistate()
227 seen, state = [], asciistate()
228 for rev, type, ctx, parents in dag:
228 for rev, type, ctx, parents in dag:
229 char = ctx.node() in showparents and '@' or 'o'
229 char = ctx.node() in showparents and '@' or 'o'
230 displayer.show(ctx)
230 displayer.show(ctx)
231 lines = displayer.hunk.pop(rev).split('\n')[:-1]
231 lines = displayer.hunk.pop(rev).split('\n')[:-1]
232 ascii(ui, state, type, char, lines, edgefn(seen, rev, parents))
232 ascii(ui, state, type, char, lines, edgefn(seen, rev, parents))
233
233
234 def graphlog(ui, repo, path=None, **opts):
234 def graphlog(ui, repo, path=None, **opts):
235 """show revision history alongside an ASCII revision graph
235 """show revision history alongside an ASCII revision graph
236
236
237 Print a revision history alongside a revision graph drawn with
237 Print a revision history alongside a revision graph drawn with
238 ASCII characters.
238 ASCII characters.
239
239
240 Nodes printed as an @ character are parents of the working
240 Nodes printed as an @ character are parents of the working
241 directory.
241 directory.
242 """
242 """
243
243
244 check_unsupported_flags(opts)
244 check_unsupported_flags(opts)
245 limit = cmdutil.loglimit(opts)
245 limit = cmdutil.loglimit(opts)
246 start, stop = get_revs(repo, opts["rev"])
246 start, stop = get_revs(repo, opts["rev"])
247 if start == nullrev:
247 if start == nullrev:
248 return
248 return
249
249
250 if path:
250 if path:
251 path = util.canonpath(repo.root, os.getcwd(), path)
251 path = util.canonpath(repo.root, os.getcwd(), path)
252 if path: # could be reset in canonpath
252 if path: # could be reset in canonpath
253 revdag = graphmod.filerevs(repo, path, start, stop, limit)
253 revdag = graphmod.filerevs(repo, path, start, stop, limit)
254 else:
254 else:
255 if limit is not None:
255 if limit is not None:
256 stop = max(stop, start - limit + 1)
256 stop = max(stop, start - limit + 1)
257 revdag = graphmod.revisions(repo, start, stop)
257 revdag = graphmod.revisions(repo, start, stop)
258
258
259 displayer = show_changeset(ui, repo, opts, buffered=True)
259 displayer = show_changeset(ui, repo, opts, buffered=True)
260 showparents = [ctx.node() for ctx in repo[None].parents()]
260 showparents = [ctx.node() for ctx in repo[None].parents()]
261 generate(ui, revdag, displayer, showparents, asciiedges)
261 generate(ui, revdag, displayer, showparents, asciiedges)
262
262
263 def graphrevs(repo, nodes, opts):
263 def graphrevs(repo, nodes, opts):
264 limit = cmdutil.loglimit(opts)
264 limit = cmdutil.loglimit(opts)
265 nodes.reverse()
265 nodes.reverse()
266 if limit is not None:
266 if limit is not None:
267 nodes = nodes[:limit]
267 nodes = nodes[:limit]
268 return graphmod.nodes(repo, nodes)
268 return graphmod.nodes(repo, nodes)
269
269
270 def goutgoing(ui, repo, dest=None, **opts):
270 def goutgoing(ui, repo, dest=None, **opts):
271 """show the outgoing changesets alongside an ASCII revision graph
271 """show the outgoing changesets alongside an ASCII revision graph
272
272
273 Print the outgoing changesets alongside a revision graph drawn with
273 Print the outgoing changesets alongside a revision graph drawn with
274 ASCII characters.
274 ASCII characters.
275
275
276 Nodes printed as an @ character are parents of the working
276 Nodes printed as an @ character are parents of the working
277 directory.
277 directory.
278 """
278 """
279
279
280 check_unsupported_flags(opts)
280 check_unsupported_flags(opts)
281 dest = ui.expandpath(dest or 'default-push', dest or 'default')
281 dest = ui.expandpath(dest or 'default-push', dest or 'default')
282 dest, branches = hg.parseurl(dest, opts.get('branch'))
282 dest, branches = hg.parseurl(dest, opts.get('branch'))
283 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
283 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
284 other = hg.repository(hg.remoteui(ui, opts), dest)
284 other = hg.repository(hg.remoteui(ui, opts), dest)
285 if revs:
285 if revs:
286 revs = [repo.lookup(rev) for rev in revs]
286 revs = [repo.lookup(rev) for rev in revs]
287 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
287 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
288 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
288 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
289 if not o:
289 if not o:
290 ui.status(_("no changes found\n"))
290 ui.status(_("no changes found\n"))
291 return
291 return
292
292
293 o = repo.changelog.nodesbetween(o, revs)[0]
293 o = repo.changelog.nodesbetween(o, revs)[0]
294 revdag = graphrevs(repo, o, opts)
294 revdag = graphrevs(repo, o, opts)
295 displayer = show_changeset(ui, repo, opts, buffered=True)
295 displayer = show_changeset(ui, repo, opts, buffered=True)
296 showparents = [ctx.node() for ctx in repo[None].parents()]
296 showparents = [ctx.node() for ctx in repo[None].parents()]
297 generate(ui, revdag, displayer, showparents, asciiedges)
297 generate(ui, revdag, displayer, showparents, asciiedges)
298
298
299 def gincoming(ui, repo, source="default", **opts):
299 def gincoming(ui, repo, source="default", **opts):
300 """show the incoming changesets alongside an ASCII revision graph
300 """show the incoming changesets alongside an ASCII revision graph
301
301
302 Print the incoming changesets alongside a revision graph drawn with
302 Print the incoming changesets alongside a revision graph drawn with
303 ASCII characters.
303 ASCII characters.
304
304
305 Nodes printed as an @ character are parents of the working
305 Nodes printed as an @ character are parents of the working
306 directory.
306 directory.
307 """
307 """
308
308
309 check_unsupported_flags(opts)
309 check_unsupported_flags(opts)
310 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
310 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
311 other = hg.repository(hg.remoteui(repo, opts), source)
311 other = hg.repository(hg.remoteui(repo, opts), source)
312 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
312 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
313 ui.status(_('comparing with %s\n') % url.hidepassword(source))
313 ui.status(_('comparing with %s\n') % url.hidepassword(source))
314 if revs:
314 if revs:
315 revs = [other.lookup(rev) for rev in revs]
315 revs = [other.lookup(rev) for rev in revs]
316 incoming = discovery.findincoming(repo, other, heads=revs,
316 incoming = discovery.findincoming(repo, other, heads=revs,
317 force=opts["force"])
317 force=opts["force"])
318 if not incoming:
318 if not incoming:
319 try:
319 try:
320 os.unlink(opts["bundle"])
320 os.unlink(opts["bundle"])
321 except:
321 except:
322 pass
322 pass
323 ui.status(_("no changes found\n"))
323 ui.status(_("no changes found\n"))
324 return
324 return
325
325
326 cleanup = None
326 cleanup = None
327 try:
327 try:
328
328
329 fname = opts["bundle"]
329 fname = opts["bundle"]
330 if fname or not other.local():
330 if fname or not other.local():
331 # create a bundle (uncompressed if other repo is not local)
331 # create a bundle (uncompressed if other repo is not local)
332 if revs is None:
332 if revs is None:
333 cg = other.changegroup(incoming, "incoming")
333 cg = other.changegroup(incoming, "incoming")
334 else:
334 else:
335 cg = other.changegroupsubset(incoming, revs, 'incoming')
335 cg = other.changegroupsubset(incoming, revs, 'incoming')
336 bundletype = other.local() and "HG10BZ" or "HG10UN"
336 bundletype = other.local() and "HG10BZ" or "HG10UN"
337 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
337 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
338 # keep written bundle?
338 # keep written bundle?
339 if opts["bundle"]:
339 if opts["bundle"]:
340 cleanup = None
340 cleanup = None
341 if not other.local():
341 if not other.local():
342 # use the created uncompressed bundlerepo
342 # use the created uncompressed bundlerepo
343 other = bundlerepo.bundlerepository(ui, repo.root, fname)
343 other = bundlerepo.bundlerepository(ui, repo.root, fname)
344
344
345 chlist = other.changelog.nodesbetween(incoming, revs)[0]
345 chlist = other.changelog.nodesbetween(incoming, revs)[0]
346 revdag = graphrevs(other, chlist, opts)
346 revdag = graphrevs(other, chlist, opts)
347 displayer = show_changeset(ui, other, opts, buffered=True)
347 displayer = show_changeset(ui, other, opts, buffered=True)
348 showparents = [ctx.node() for ctx in repo[None].parents()]
348 showparents = [ctx.node() for ctx in repo[None].parents()]
349 generate(ui, revdag, displayer, showparents, asciiedges)
349 generate(ui, revdag, displayer, showparents, asciiedges)
350
350
351 finally:
351 finally:
352 if hasattr(other, 'close'):
352 if hasattr(other, 'close'):
353 other.close()
353 other.close()
354 if cleanup:
354 if cleanup:
355 os.unlink(cleanup)
355 os.unlink(cleanup)
356
356
357 def uisetup(ui):
357 def uisetup(ui):
358 '''Initialize the extension.'''
358 '''Initialize the extension.'''
359 _wrapcmd(ui, 'log', commands.table, graphlog)
359 _wrapcmd(ui, 'log', commands.table, graphlog)
360 _wrapcmd(ui, 'incoming', commands.table, gincoming)
360 _wrapcmd(ui, 'incoming', commands.table, gincoming)
361 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
361 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
362
362
363 def _wrapcmd(ui, cmd, table, wrapfn):
363 def _wrapcmd(ui, cmd, table, wrapfn):
364 '''wrap the command'''
364 '''wrap the command'''
365 def graph(orig, *args, **kwargs):
365 def graph(orig, *args, **kwargs):
366 if kwargs['graph']:
366 if kwargs['graph']:
367 return wrapfn(*args, **kwargs)
367 return wrapfn(*args, **kwargs)
368 return orig(*args, **kwargs)
368 return orig(*args, **kwargs)
369 entry = extensions.wrapcommand(table, cmd, graph)
369 entry = extensions.wrapcommand(table, cmd, graph)
370 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
370 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
371
371
372 cmdtable = {
372 cmdtable = {
373 "glog":
373 "glog":
374 (graphlog,
374 (graphlog,
375 [('l', 'limit', '',
375 [('l', 'limit', '',
376 _('limit number of changes displayed'), _('NUM')),
376 _('limit number of changes displayed'), _('NUM')),
377 ('p', 'patch', False, _('show patch')),
377 ('p', 'patch', False, _('show patch')),
378 ('r', 'rev', [],
378 ('r', 'rev', [],
379 _('show the specified revision or range'), _('REV')),
379 _('show the specified revision or range'), _('REV')),
380 ] + templateopts,
380 ] + templateopts,
381 _('hg glog [OPTION]... [FILE]')),
381 _('hg glog [OPTION]... [FILE]')),
382 }
382 }
@@ -1,4462 +1,4462 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw, discovery
13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset
16 import minirst, revset
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 bad = []
49 bad = []
50 names = []
50 names = []
51 m = cmdutil.match(repo, pats, opts)
51 m = cmdutil.match(repo, pats, opts)
52 oldbad = m.bad
52 oldbad = m.bad
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
54
54
55 for f in repo.walk(m):
55 for f in repo.walk(m):
56 exact = m.exact(f)
56 exact = m.exact(f)
57 if exact or f not in repo.dirstate:
57 if exact or f not in repo.dirstate:
58 names.append(f)
58 names.append(f)
59 if ui.verbose or not exact:
59 if ui.verbose or not exact:
60 ui.status(_('adding %s\n') % m.rel(f))
60 ui.status(_('adding %s\n') % m.rel(f))
61 if not opts.get('dry_run'):
61 if not opts.get('dry_run'):
62 bad += [f for f in repo[None].add(names) if f in m.files()]
62 bad += [f for f in repo[None].add(names) if f in m.files()]
63 return bad and 1 or 0
63 return bad and 1 or 0
64
64
65 def addremove(ui, repo, *pats, **opts):
65 def addremove(ui, repo, *pats, **opts):
66 """add all new files, delete all missing files
66 """add all new files, delete all missing files
67
67
68 Add all new files and remove all missing files from the
68 Add all new files and remove all missing files from the
69 repository.
69 repository.
70
70
71 New files are ignored if they match any of the patterns in
71 New files are ignored if they match any of the patterns in
72 .hgignore. As with add, these changes take effect at the next
72 .hgignore. As with add, these changes take effect at the next
73 commit.
73 commit.
74
74
75 Use the -s/--similarity option to detect renamed files. With a
75 Use the -s/--similarity option to detect renamed files. With a
76 parameter greater than 0, this compares every removed file with
76 parameter greater than 0, this compares every removed file with
77 every added file and records those similar enough as renames. This
77 every added file and records those similar enough as renames. This
78 option takes a percentage between 0 (disabled) and 100 (files must
78 option takes a percentage between 0 (disabled) and 100 (files must
79 be identical) as its parameter. Detecting renamed files this way
79 be identical) as its parameter. Detecting renamed files this way
80 can be expensive. After using this option, :hg:`status -C` can be
80 can be expensive. After using this option, :hg:`status -C` can be
81 used to check which files were identified as moved or renamed.
81 used to check which files were identified as moved or renamed.
82
82
83 Returns 0 if all files are successfully added.
83 Returns 0 if all files are successfully added.
84 """
84 """
85 try:
85 try:
86 sim = float(opts.get('similarity') or 100)
86 sim = float(opts.get('similarity') or 100)
87 except ValueError:
87 except ValueError:
88 raise util.Abort(_('similarity must be a number'))
88 raise util.Abort(_('similarity must be a number'))
89 if sim < 0 or sim > 100:
89 if sim < 0 or sim > 100:
90 raise util.Abort(_('similarity must be between 0 and 100'))
90 raise util.Abort(_('similarity must be between 0 and 100'))
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
92
92
93 def annotate(ui, repo, *pats, **opts):
93 def annotate(ui, repo, *pats, **opts):
94 """show changeset information by line for each file
94 """show changeset information by line for each file
95
95
96 List changes in files, showing the revision id responsible for
96 List changes in files, showing the revision id responsible for
97 each line
97 each line
98
98
99 This command is useful for discovering when a change was made and
99 This command is useful for discovering when a change was made and
100 by whom.
100 by whom.
101
101
102 Without the -a/--text option, annotate will avoid processing files
102 Without the -a/--text option, annotate will avoid processing files
103 it detects as binary. With -a, annotate will annotate the file
103 it detects as binary. With -a, annotate will annotate the file
104 anyway, although the results will probably be neither useful
104 anyway, although the results will probably be neither useful
105 nor desirable.
105 nor desirable.
106
106
107 Returns 0 on success.
107 Returns 0 on success.
108 """
108 """
109 if opts.get('follow'):
109 if opts.get('follow'):
110 # --follow is deprecated and now just an alias for -f/--file
110 # --follow is deprecated and now just an alias for -f/--file
111 # to mimic the behavior of Mercurial before version 1.5
111 # to mimic the behavior of Mercurial before version 1.5
112 opts['file'] = 1
112 opts['file'] = 1
113
113
114 datefunc = ui.quiet and util.shortdate or util.datestr
114 datefunc = ui.quiet and util.shortdate or util.datestr
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
116
116
117 if not pats:
117 if not pats:
118 raise util.Abort(_('at least one filename or pattern is required'))
118 raise util.Abort(_('at least one filename or pattern is required'))
119
119
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
121 ('number', lambda x: str(x[0].rev())),
121 ('number', lambda x: str(x[0].rev())),
122 ('changeset', lambda x: short(x[0].node())),
122 ('changeset', lambda x: short(x[0].node())),
123 ('date', getdate),
123 ('date', getdate),
124 ('file', lambda x: x[0].path()),
124 ('file', lambda x: x[0].path()),
125 ]
125 ]
126
126
127 if (not opts.get('user') and not opts.get('changeset')
127 if (not opts.get('user') and not opts.get('changeset')
128 and not opts.get('date') and not opts.get('file')):
128 and not opts.get('date') and not opts.get('file')):
129 opts['number'] = 1
129 opts['number'] = 1
130
130
131 linenumber = opts.get('line_number') is not None
131 linenumber = opts.get('line_number') is not None
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
134
134
135 funcmap = [func for op, func in opmap if opts.get(op)]
135 funcmap = [func for op, func in opmap if opts.get(op)]
136 if linenumber:
136 if linenumber:
137 lastfunc = funcmap[-1]
137 lastfunc = funcmap[-1]
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
139
139
140 ctx = repo[opts.get('rev')]
140 ctx = repo[opts.get('rev')]
141 m = cmdutil.match(repo, pats, opts)
141 m = cmdutil.match(repo, pats, opts)
142 follow = not opts.get('no_follow')
142 follow = not opts.get('no_follow')
143 for abs in ctx.walk(m):
143 for abs in ctx.walk(m):
144 fctx = ctx[abs]
144 fctx = ctx[abs]
145 if not opts.get('text') and util.binary(fctx.data()):
145 if not opts.get('text') and util.binary(fctx.data()):
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
147 continue
147 continue
148
148
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
150 pieces = []
150 pieces = []
151
151
152 for f in funcmap:
152 for f in funcmap:
153 l = [f(n) for n, dummy in lines]
153 l = [f(n) for n, dummy in lines]
154 if l:
154 if l:
155 sized = [(x, encoding.colwidth(x)) for x in l]
155 sized = [(x, encoding.colwidth(x)) for x in l]
156 ml = max([w for x, w in sized])
156 ml = max([w for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
158
158
159 if pieces:
159 if pieces:
160 for p, l in zip(zip(*pieces), lines):
160 for p, l in zip(zip(*pieces), lines):
161 ui.write("%s: %s" % (" ".join(p), l[1]))
161 ui.write("%s: %s" % (" ".join(p), l[1]))
162
162
163 def archive(ui, repo, dest, **opts):
163 def archive(ui, repo, dest, **opts):
164 '''create an unversioned archive of a repository revision
164 '''create an unversioned archive of a repository revision
165
165
166 By default, the revision used is the parent of the working
166 By default, the revision used is the parent of the working
167 directory; use -r/--rev to specify a different revision.
167 directory; use -r/--rev to specify a different revision.
168
168
169 The archive type is automatically detected based on file
169 The archive type is automatically detected based on file
170 extension (or override using -t/--type).
170 extension (or override using -t/--type).
171
171
172 Valid types are:
172 Valid types are:
173
173
174 :``files``: a directory full of files (default)
174 :``files``: a directory full of files (default)
175 :``tar``: tar archive, uncompressed
175 :``tar``: tar archive, uncompressed
176 :``tbz2``: tar archive, compressed using bzip2
176 :``tbz2``: tar archive, compressed using bzip2
177 :``tgz``: tar archive, compressed using gzip
177 :``tgz``: tar archive, compressed using gzip
178 :``uzip``: zip archive, uncompressed
178 :``uzip``: zip archive, uncompressed
179 :``zip``: zip archive, compressed using deflate
179 :``zip``: zip archive, compressed using deflate
180
180
181 The exact name of the destination archive or directory is given
181 The exact name of the destination archive or directory is given
182 using a format string; see :hg:`help export` for details.
182 using a format string; see :hg:`help export` for details.
183
183
184 Each member added to an archive file has a directory prefix
184 Each member added to an archive file has a directory prefix
185 prepended. Use -p/--prefix to specify a format string for the
185 prepended. Use -p/--prefix to specify a format string for the
186 prefix. The default is the basename of the archive, with suffixes
186 prefix. The default is the basename of the archive, with suffixes
187 removed.
187 removed.
188
188
189 Returns 0 on success.
189 Returns 0 on success.
190 '''
190 '''
191
191
192 ctx = repo[opts.get('rev')]
192 ctx = repo[opts.get('rev')]
193 if not ctx:
193 if not ctx:
194 raise util.Abort(_('no working directory: please specify a revision'))
194 raise util.Abort(_('no working directory: please specify a revision'))
195 node = ctx.node()
195 node = ctx.node()
196 dest = cmdutil.make_filename(repo, dest, node)
196 dest = cmdutil.make_filename(repo, dest, node)
197 if os.path.realpath(dest) == repo.root:
197 if os.path.realpath(dest) == repo.root:
198 raise util.Abort(_('repository root cannot be destination'))
198 raise util.Abort(_('repository root cannot be destination'))
199
199
200 kind = opts.get('type') or archival.guesskind(dest) or 'files'
200 kind = opts.get('type') or archival.guesskind(dest) or 'files'
201 prefix = opts.get('prefix')
201 prefix = opts.get('prefix')
202
202
203 if dest == '-':
203 if dest == '-':
204 if kind == 'files':
204 if kind == 'files':
205 raise util.Abort(_('cannot archive plain files to stdout'))
205 raise util.Abort(_('cannot archive plain files to stdout'))
206 dest = sys.stdout
206 dest = sys.stdout
207 if not prefix:
207 if not prefix:
208 prefix = os.path.basename(repo.root) + '-%h'
208 prefix = os.path.basename(repo.root) + '-%h'
209
209
210 prefix = cmdutil.make_filename(repo, prefix, node)
210 prefix = cmdutil.make_filename(repo, prefix, node)
211 matchfn = cmdutil.match(repo, [], opts)
211 matchfn = cmdutil.match(repo, [], opts)
212 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
212 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
213 matchfn, prefix)
213 matchfn, prefix)
214
214
215 def backout(ui, repo, node=None, rev=None, **opts):
215 def backout(ui, repo, node=None, rev=None, **opts):
216 '''reverse effect of earlier changeset
216 '''reverse effect of earlier changeset
217
217
218 Commit the backed out changes as a new changeset. The new
218 Commit the backed out changes as a new changeset. The new
219 changeset is a child of the backed out changeset.
219 changeset is a child of the backed out changeset.
220
220
221 If you backout a changeset other than the tip, a new head is
221 If you backout a changeset other than the tip, a new head is
222 created. This head will be the new tip and you should merge this
222 created. This head will be the new tip and you should merge this
223 backout changeset with another head.
223 backout changeset with another head.
224
224
225 The --merge option remembers the parent of the working directory
225 The --merge option remembers the parent of the working directory
226 before starting the backout, then merges the new head with that
226 before starting the backout, then merges the new head with that
227 changeset afterwards. This saves you from doing the merge by hand.
227 changeset afterwards. This saves you from doing the merge by hand.
228 The result of this merge is not committed, as with a normal merge.
228 The result of this merge is not committed, as with a normal merge.
229
229
230 See :hg:`help dates` for a list of formats valid for -d/--date.
230 See :hg:`help dates` for a list of formats valid for -d/--date.
231
231
232 Returns 0 on success.
232 Returns 0 on success.
233 '''
233 '''
234 if rev and node:
234 if rev and node:
235 raise util.Abort(_("please specify just one revision"))
235 raise util.Abort(_("please specify just one revision"))
236
236
237 if not rev:
237 if not rev:
238 rev = node
238 rev = node
239
239
240 if not rev:
240 if not rev:
241 raise util.Abort(_("please specify a revision to backout"))
241 raise util.Abort(_("please specify a revision to backout"))
242
242
243 date = opts.get('date')
243 date = opts.get('date')
244 if date:
244 if date:
245 opts['date'] = util.parsedate(date)
245 opts['date'] = util.parsedate(date)
246
246
247 cmdutil.bail_if_changed(repo)
247 cmdutil.bail_if_changed(repo)
248 node = repo.lookup(rev)
248 node = repo.lookup(rev)
249
249
250 op1, op2 = repo.dirstate.parents()
250 op1, op2 = repo.dirstate.parents()
251 a = repo.changelog.ancestor(op1, node)
251 a = repo.changelog.ancestor(op1, node)
252 if a != node:
252 if a != node:
253 raise util.Abort(_('cannot backout change on a different branch'))
253 raise util.Abort(_('cannot backout change on a different branch'))
254
254
255 p1, p2 = repo.changelog.parents(node)
255 p1, p2 = repo.changelog.parents(node)
256 if p1 == nullid:
256 if p1 == nullid:
257 raise util.Abort(_('cannot backout a change with no parents'))
257 raise util.Abort(_('cannot backout a change with no parents'))
258 if p2 != nullid:
258 if p2 != nullid:
259 if not opts.get('parent'):
259 if not opts.get('parent'):
260 raise util.Abort(_('cannot backout a merge changeset without '
260 raise util.Abort(_('cannot backout a merge changeset without '
261 '--parent'))
261 '--parent'))
262 p = repo.lookup(opts['parent'])
262 p = repo.lookup(opts['parent'])
263 if p not in (p1, p2):
263 if p not in (p1, p2):
264 raise util.Abort(_('%s is not a parent of %s') %
264 raise util.Abort(_('%s is not a parent of %s') %
265 (short(p), short(node)))
265 (short(p), short(node)))
266 parent = p
266 parent = p
267 else:
267 else:
268 if opts.get('parent'):
268 if opts.get('parent'):
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
270 parent = p1
270 parent = p1
271
271
272 # the backout should appear on the same branch
272 # the backout should appear on the same branch
273 branch = repo.dirstate.branch()
273 branch = repo.dirstate.branch()
274 hg.clean(repo, node, show_stats=False)
274 hg.clean(repo, node, show_stats=False)
275 repo.dirstate.setbranch(branch)
275 repo.dirstate.setbranch(branch)
276 revert_opts = opts.copy()
276 revert_opts = opts.copy()
277 revert_opts['date'] = None
277 revert_opts['date'] = None
278 revert_opts['all'] = True
278 revert_opts['all'] = True
279 revert_opts['rev'] = hex(parent)
279 revert_opts['rev'] = hex(parent)
280 revert_opts['no_backup'] = None
280 revert_opts['no_backup'] = None
281 revert(ui, repo, **revert_opts)
281 revert(ui, repo, **revert_opts)
282 commit_opts = opts.copy()
282 commit_opts = opts.copy()
283 commit_opts['addremove'] = False
283 commit_opts['addremove'] = False
284 if not commit_opts['message'] and not commit_opts['logfile']:
284 if not commit_opts['message'] and not commit_opts['logfile']:
285 # we don't translate commit messages
285 # we don't translate commit messages
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
287 commit_opts['force_editor'] = True
287 commit_opts['force_editor'] = True
288 commit(ui, repo, **commit_opts)
288 commit(ui, repo, **commit_opts)
289 def nice(node):
289 def nice(node):
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
291 ui.status(_('changeset %s backs out changeset %s\n') %
291 ui.status(_('changeset %s backs out changeset %s\n') %
292 (nice(repo.changelog.tip()), nice(node)))
292 (nice(repo.changelog.tip()), nice(node)))
293 if op1 != node:
293 if op1 != node:
294 hg.clean(repo, op1, show_stats=False)
294 hg.clean(repo, op1, show_stats=False)
295 if opts.get('merge'):
295 if opts.get('merge'):
296 ui.status(_('merging with changeset %s\n')
296 ui.status(_('merging with changeset %s\n')
297 % nice(repo.changelog.tip()))
297 % nice(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
299 else:
299 else:
300 ui.status(_('the backout changeset is a new head - '
300 ui.status(_('the backout changeset is a new head - '
301 'do not forget to merge\n'))
301 'do not forget to merge\n'))
302 ui.status(_('(use "backout --merge" '
302 ui.status(_('(use "backout --merge" '
303 'if you want to auto-merge)\n'))
303 'if you want to auto-merge)\n'))
304
304
305 def bisect(ui, repo, rev=None, extra=None, command=None,
305 def bisect(ui, repo, rev=None, extra=None, command=None,
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
307 """subdivision search of changesets
307 """subdivision search of changesets
308
308
309 This command helps to find changesets which introduce problems. To
309 This command helps to find changesets which introduce problems. To
310 use, mark the earliest changeset you know exhibits the problem as
310 use, mark the earliest changeset you know exhibits the problem as
311 bad, then mark the latest changeset which is free from the problem
311 bad, then mark the latest changeset which is free from the problem
312 as good. Bisect will update your working directory to a revision
312 as good. Bisect will update your working directory to a revision
313 for testing (unless the -U/--noupdate option is specified). Once
313 for testing (unless the -U/--noupdate option is specified). Once
314 you have performed tests, mark the working directory as good or
314 you have performed tests, mark the working directory as good or
315 bad, and bisect will either update to another candidate changeset
315 bad, and bisect will either update to another candidate changeset
316 or announce that it has found the bad revision.
316 or announce that it has found the bad revision.
317
317
318 As a shortcut, you can also use the revision argument to mark a
318 As a shortcut, you can also use the revision argument to mark a
319 revision as good or bad without checking it out first.
319 revision as good or bad without checking it out first.
320
320
321 If you supply a command, it will be used for automatic bisection.
321 If you supply a command, it will be used for automatic bisection.
322 Its exit status will be used to mark revisions as good or bad:
322 Its exit status will be used to mark revisions as good or bad:
323 status 0 means good, 125 means to skip the revision, 127
323 status 0 means good, 125 means to skip the revision, 127
324 (command not found) will abort the bisection, and any other
324 (command not found) will abort the bisection, and any other
325 non-zero exit status means the revision is bad.
325 non-zero exit status means the revision is bad.
326
326
327 Returns 0 on success.
327 Returns 0 on success.
328 """
328 """
329 def print_result(nodes, good):
329 def print_result(nodes, good):
330 displayer = cmdutil.show_changeset(ui, repo, {})
330 displayer = cmdutil.show_changeset(ui, repo, {})
331 if len(nodes) == 1:
331 if len(nodes) == 1:
332 # narrowed it down to a single revision
332 # narrowed it down to a single revision
333 if good:
333 if good:
334 ui.write(_("The first good revision is:\n"))
334 ui.write(_("The first good revision is:\n"))
335 else:
335 else:
336 ui.write(_("The first bad revision is:\n"))
336 ui.write(_("The first bad revision is:\n"))
337 displayer.show(repo[nodes[0]])
337 displayer.show(repo[nodes[0]])
338 else:
338 else:
339 # multiple possible revisions
339 # multiple possible revisions
340 if good:
340 if good:
341 ui.write(_("Due to skipped revisions, the first "
341 ui.write(_("Due to skipped revisions, the first "
342 "good revision could be any of:\n"))
342 "good revision could be any of:\n"))
343 else:
343 else:
344 ui.write(_("Due to skipped revisions, the first "
344 ui.write(_("Due to skipped revisions, the first "
345 "bad revision could be any of:\n"))
345 "bad revision could be any of:\n"))
346 for n in nodes:
346 for n in nodes:
347 displayer.show(repo[n])
347 displayer.show(repo[n])
348 displayer.close()
348 displayer.close()
349
349
350 def check_state(state, interactive=True):
350 def check_state(state, interactive=True):
351 if not state['good'] or not state['bad']:
351 if not state['good'] or not state['bad']:
352 if (good or bad or skip or reset) and interactive:
352 if (good or bad or skip or reset) and interactive:
353 return
353 return
354 if not state['good']:
354 if not state['good']:
355 raise util.Abort(_('cannot bisect (no known good revisions)'))
355 raise util.Abort(_('cannot bisect (no known good revisions)'))
356 else:
356 else:
357 raise util.Abort(_('cannot bisect (no known bad revisions)'))
357 raise util.Abort(_('cannot bisect (no known bad revisions)'))
358 return True
358 return True
359
359
360 # backward compatibility
360 # backward compatibility
361 if rev in "good bad reset init".split():
361 if rev in "good bad reset init".split():
362 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
362 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
363 cmd, rev, extra = rev, extra, None
363 cmd, rev, extra = rev, extra, None
364 if cmd == "good":
364 if cmd == "good":
365 good = True
365 good = True
366 elif cmd == "bad":
366 elif cmd == "bad":
367 bad = True
367 bad = True
368 else:
368 else:
369 reset = True
369 reset = True
370 elif extra or good + bad + skip + reset + bool(command) > 1:
370 elif extra or good + bad + skip + reset + bool(command) > 1:
371 raise util.Abort(_('incompatible arguments'))
371 raise util.Abort(_('incompatible arguments'))
372
372
373 if reset:
373 if reset:
374 p = repo.join("bisect.state")
374 p = repo.join("bisect.state")
375 if os.path.exists(p):
375 if os.path.exists(p):
376 os.unlink(p)
376 os.unlink(p)
377 return
377 return
378
378
379 state = hbisect.load_state(repo)
379 state = hbisect.load_state(repo)
380
380
381 if command:
381 if command:
382 changesets = 1
382 changesets = 1
383 try:
383 try:
384 while changesets:
384 while changesets:
385 # update state
385 # update state
386 status = util.system(command)
386 status = util.system(command)
387 if status == 125:
387 if status == 125:
388 transition = "skip"
388 transition = "skip"
389 elif status == 0:
389 elif status == 0:
390 transition = "good"
390 transition = "good"
391 # status < 0 means process was killed
391 # status < 0 means process was killed
392 elif status == 127:
392 elif status == 127:
393 raise util.Abort(_("failed to execute %s") % command)
393 raise util.Abort(_("failed to execute %s") % command)
394 elif status < 0:
394 elif status < 0:
395 raise util.Abort(_("%s killed") % command)
395 raise util.Abort(_("%s killed") % command)
396 else:
396 else:
397 transition = "bad"
397 transition = "bad"
398 ctx = repo[rev or '.']
398 ctx = repo[rev or '.']
399 state[transition].append(ctx.node())
399 state[transition].append(ctx.node())
400 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
400 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
401 check_state(state, interactive=False)
401 check_state(state, interactive=False)
402 # bisect
402 # bisect
403 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
403 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
404 # update to next check
404 # update to next check
405 cmdutil.bail_if_changed(repo)
405 cmdutil.bail_if_changed(repo)
406 hg.clean(repo, nodes[0], show_stats=False)
406 hg.clean(repo, nodes[0], show_stats=False)
407 finally:
407 finally:
408 hbisect.save_state(repo, state)
408 hbisect.save_state(repo, state)
409 print_result(nodes, good)
409 print_result(nodes, good)
410 return
410 return
411
411
412 # update state
412 # update state
413 node = repo.lookup(rev or '.')
413 node = repo.lookup(rev or '.')
414 if good or bad or skip:
414 if good or bad or skip:
415 if good:
415 if good:
416 state['good'].append(node)
416 state['good'].append(node)
417 elif bad:
417 elif bad:
418 state['bad'].append(node)
418 state['bad'].append(node)
419 elif skip:
419 elif skip:
420 state['skip'].append(node)
420 state['skip'].append(node)
421 hbisect.save_state(repo, state)
421 hbisect.save_state(repo, state)
422
422
423 if not check_state(state):
423 if not check_state(state):
424 return
424 return
425
425
426 # actually bisect
426 # actually bisect
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
428 if changesets == 0:
428 if changesets == 0:
429 print_result(nodes, good)
429 print_result(nodes, good)
430 else:
430 else:
431 assert len(nodes) == 1 # only a single node can be tested next
431 assert len(nodes) == 1 # only a single node can be tested next
432 node = nodes[0]
432 node = nodes[0]
433 # compute the approximate number of remaining tests
433 # compute the approximate number of remaining tests
434 tests, size = 0, 2
434 tests, size = 0, 2
435 while size <= changesets:
435 while size <= changesets:
436 tests, size = tests + 1, size * 2
436 tests, size = tests + 1, size * 2
437 rev = repo.changelog.rev(node)
437 rev = repo.changelog.rev(node)
438 ui.write(_("Testing changeset %d:%s "
438 ui.write(_("Testing changeset %d:%s "
439 "(%d changesets remaining, ~%d tests)\n")
439 "(%d changesets remaining, ~%d tests)\n")
440 % (rev, short(node), changesets, tests))
440 % (rev, short(node), changesets, tests))
441 if not noupdate:
441 if not noupdate:
442 cmdutil.bail_if_changed(repo)
442 cmdutil.bail_if_changed(repo)
443 return hg.clean(repo, node)
443 return hg.clean(repo, node)
444
444
445 def branch(ui, repo, label=None, **opts):
445 def branch(ui, repo, label=None, **opts):
446 """set or show the current branch name
446 """set or show the current branch name
447
447
448 With no argument, show the current branch name. With one argument,
448 With no argument, show the current branch name. With one argument,
449 set the working directory branch name (the branch will not exist
449 set the working directory branch name (the branch will not exist
450 in the repository until the next commit). Standard practice
450 in the repository until the next commit). Standard practice
451 recommends that primary development take place on the 'default'
451 recommends that primary development take place on the 'default'
452 branch.
452 branch.
453
453
454 Unless -f/--force is specified, branch will not let you set a
454 Unless -f/--force is specified, branch will not let you set a
455 branch name that already exists, even if it's inactive.
455 branch name that already exists, even if it's inactive.
456
456
457 Use -C/--clean to reset the working directory branch to that of
457 Use -C/--clean to reset the working directory branch to that of
458 the parent of the working directory, negating a previous branch
458 the parent of the working directory, negating a previous branch
459 change.
459 change.
460
460
461 Use the command :hg:`update` to switch to an existing branch. Use
461 Use the command :hg:`update` to switch to an existing branch. Use
462 :hg:`commit --close-branch` to mark this branch as closed.
462 :hg:`commit --close-branch` to mark this branch as closed.
463
463
464 Returns 0 on success.
464 Returns 0 on success.
465 """
465 """
466
466
467 if opts.get('clean'):
467 if opts.get('clean'):
468 label = repo[None].parents()[0].branch()
468 label = repo[None].parents()[0].branch()
469 repo.dirstate.setbranch(label)
469 repo.dirstate.setbranch(label)
470 ui.status(_('reset working directory to branch %s\n') % label)
470 ui.status(_('reset working directory to branch %s\n') % label)
471 elif label:
471 elif label:
472 utflabel = encoding.fromlocal(label)
472 utflabel = encoding.fromlocal(label)
473 if not opts.get('force') and utflabel in repo.branchtags():
473 if not opts.get('force') and utflabel in repo.branchtags():
474 if label not in [p.branch() for p in repo.parents()]:
474 if label not in [p.branch() for p in repo.parents()]:
475 raise util.Abort(_('a branch of the same name already exists'
475 raise util.Abort(_('a branch of the same name already exists'
476 " (use 'hg update' to switch to it)"))
476 " (use 'hg update' to switch to it)"))
477 repo.dirstate.setbranch(utflabel)
477 repo.dirstate.setbranch(utflabel)
478 ui.status(_('marked working directory as branch %s\n') % label)
478 ui.status(_('marked working directory as branch %s\n') % label)
479 else:
479 else:
480 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
480 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
481
481
482 def branches(ui, repo, active=False, closed=False):
482 def branches(ui, repo, active=False, closed=False):
483 """list repository named branches
483 """list repository named branches
484
484
485 List the repository's named branches, indicating which ones are
485 List the repository's named branches, indicating which ones are
486 inactive. If -c/--closed is specified, also list branches which have
486 inactive. If -c/--closed is specified, also list branches which have
487 been marked closed (see :hg:`commit --close-branch`).
487 been marked closed (see :hg:`commit --close-branch`).
488
488
489 If -a/--active is specified, only show active branches. A branch
489 If -a/--active is specified, only show active branches. A branch
490 is considered active if it contains repository heads.
490 is considered active if it contains repository heads.
491
491
492 Use the command :hg:`update` to switch to an existing branch.
492 Use the command :hg:`update` to switch to an existing branch.
493
493
494 Returns 0.
494 Returns 0.
495 """
495 """
496
496
497 hexfunc = ui.debugflag and hex or short
497 hexfunc = ui.debugflag and hex or short
498 activebranches = [repo[n].branch() for n in repo.heads()]
498 activebranches = [repo[n].branch() for n in repo.heads()]
499 def testactive(tag, node):
499 def testactive(tag, node):
500 realhead = tag in activebranches
500 realhead = tag in activebranches
501 open = node in repo.branchheads(tag, closed=False)
501 open = node in repo.branchheads(tag, closed=False)
502 return realhead and open
502 return realhead and open
503 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
503 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
504 for tag, node in repo.branchtags().items()],
504 for tag, node in repo.branchtags().items()],
505 reverse=True)
505 reverse=True)
506
506
507 for isactive, node, tag in branches:
507 for isactive, node, tag in branches:
508 if (not active) or isactive:
508 if (not active) or isactive:
509 encodedtag = encoding.tolocal(tag)
509 encodedtag = encoding.tolocal(tag)
510 if ui.quiet:
510 if ui.quiet:
511 ui.write("%s\n" % encodedtag)
511 ui.write("%s\n" % encodedtag)
512 else:
512 else:
513 hn = repo.lookup(node)
513 hn = repo.lookup(node)
514 if isactive:
514 if isactive:
515 notice = ''
515 notice = ''
516 elif hn not in repo.branchheads(tag, closed=False):
516 elif hn not in repo.branchheads(tag, closed=False):
517 if not closed:
517 if not closed:
518 continue
518 continue
519 notice = _(' (closed)')
519 notice = _(' (closed)')
520 else:
520 else:
521 notice = _(' (inactive)')
521 notice = _(' (inactive)')
522 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
522 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
523 data = encodedtag, rev, hexfunc(hn), notice
523 data = encodedtag, rev, hexfunc(hn), notice
524 ui.write("%s %s:%s%s\n" % data)
524 ui.write("%s %s:%s%s\n" % data)
525
525
526 def bundle(ui, repo, fname, dest=None, **opts):
526 def bundle(ui, repo, fname, dest=None, **opts):
527 """create a changegroup file
527 """create a changegroup file
528
528
529 Generate a compressed changegroup file collecting changesets not
529 Generate a compressed changegroup file collecting changesets not
530 known to be in another repository.
530 known to be in another repository.
531
531
532 If you omit the destination repository, then hg assumes the
532 If you omit the destination repository, then hg assumes the
533 destination will have all the nodes you specify with --base
533 destination will have all the nodes you specify with --base
534 parameters. To create a bundle containing all changesets, use
534 parameters. To create a bundle containing all changesets, use
535 -a/--all (or --base null).
535 -a/--all (or --base null).
536
536
537 You can change compression method with the -t/--type option.
537 You can change compression method with the -t/--type option.
538 The available compression methods are: none, bzip2, and
538 The available compression methods are: none, bzip2, and
539 gzip (by default, bundles are compressed using bzip2).
539 gzip (by default, bundles are compressed using bzip2).
540
540
541 The bundle file can then be transferred using conventional means
541 The bundle file can then be transferred using conventional means
542 and applied to another repository with the unbundle or pull
542 and applied to another repository with the unbundle or pull
543 command. This is useful when direct push and pull are not
543 command. This is useful when direct push and pull are not
544 available or when exporting an entire repository is undesirable.
544 available or when exporting an entire repository is undesirable.
545
545
546 Applying bundles preserves all changeset contents including
546 Applying bundles preserves all changeset contents including
547 permissions, copy/rename information, and revision history.
547 permissions, copy/rename information, and revision history.
548
548
549 Returns 0 on success, 1 if no changes found.
549 Returns 0 on success, 1 if no changes found.
550 """
550 """
551 revs = opts.get('rev') or None
551 revs = opts.get('rev') or None
552 if opts.get('all'):
552 if opts.get('all'):
553 base = ['null']
553 base = ['null']
554 else:
554 else:
555 base = opts.get('base')
555 base = opts.get('base')
556 if base:
556 if base:
557 if dest:
557 if dest:
558 raise util.Abort(_("--base is incompatible with specifying "
558 raise util.Abort(_("--base is incompatible with specifying "
559 "a destination"))
559 "a destination"))
560 base = [repo.lookup(rev) for rev in base]
560 base = [repo.lookup(rev) for rev in base]
561 # create the right base
561 # create the right base
562 # XXX: nodesbetween / changegroup* should be "fixed" instead
562 # XXX: nodesbetween / changegroup* should be "fixed" instead
563 o = []
563 o = []
564 has = set((nullid,))
564 has = set((nullid,))
565 for n in base:
565 for n in base:
566 has.update(repo.changelog.reachable(n))
566 has.update(repo.changelog.reachable(n))
567 if revs:
567 if revs:
568 revs = [repo.lookup(rev) for rev in revs]
568 revs = [repo.lookup(rev) for rev in revs]
569 visit = revs[:]
569 visit = revs[:]
570 has.difference_update(visit)
570 has.difference_update(visit)
571 else:
571 else:
572 visit = repo.changelog.heads()
572 visit = repo.changelog.heads()
573 seen = {}
573 seen = {}
574 while visit:
574 while visit:
575 n = visit.pop(0)
575 n = visit.pop(0)
576 parents = [p for p in repo.changelog.parents(n) if p not in has]
576 parents = [p for p in repo.changelog.parents(n) if p not in has]
577 if len(parents) == 0:
577 if len(parents) == 0:
578 if n not in has:
578 if n not in has:
579 o.append(n)
579 o.append(n)
580 else:
580 else:
581 for p in parents:
581 for p in parents:
582 if p not in seen:
582 if p not in seen:
583 seen[p] = 1
583 seen[p] = 1
584 visit.append(p)
584 visit.append(p)
585 else:
585 else:
586 dest = ui.expandpath(dest or 'default-push', dest or 'default')
586 dest = ui.expandpath(dest or 'default-push', dest or 'default')
587 dest, branches = hg.parseurl(dest, opts.get('branch'))
587 dest, branches = hg.parseurl(dest, opts.get('branch'))
588 other = hg.repository(hg.remoteui(repo, opts), dest)
588 other = hg.repository(hg.remoteui(repo, opts), dest)
589 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
589 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
590 if revs:
590 if revs:
591 revs = [repo.lookup(rev) for rev in revs]
591 revs = [repo.lookup(rev) for rev in revs]
592 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
592 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
593
593
594 if not o:
594 if not o:
595 ui.status(_("no changes found\n"))
595 ui.status(_("no changes found\n"))
596 return 1
596 return 1
597
597
598 if revs:
598 if revs:
599 cg = repo.changegroupsubset(o, revs, 'bundle')
599 cg = repo.changegroupsubset(o, revs, 'bundle')
600 else:
600 else:
601 cg = repo.changegroup(o, 'bundle')
601 cg = repo.changegroup(o, 'bundle')
602
602
603 bundletype = opts.get('type', 'bzip2').lower()
603 bundletype = opts.get('type', 'bzip2').lower()
604 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
604 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
605 bundletype = btypes.get(bundletype)
605 bundletype = btypes.get(bundletype)
606 if bundletype not in changegroup.bundletypes:
606 if bundletype not in changegroup.bundletypes:
607 raise util.Abort(_('unknown bundle type specified with --type'))
607 raise util.Abort(_('unknown bundle type specified with --type'))
608
608
609 changegroup.writebundle(cg, fname, bundletype)
609 changegroup.writebundle(cg, fname, bundletype)
610
610
611 def cat(ui, repo, file1, *pats, **opts):
611 def cat(ui, repo, file1, *pats, **opts):
612 """output the current or given revision of files
612 """output the current or given revision of files
613
613
614 Print the specified files as they were at the given revision. If
614 Print the specified files as they were at the given revision. If
615 no revision is given, the parent of the working directory is used,
615 no revision is given, the parent of the working directory is used,
616 or tip if no revision is checked out.
616 or tip if no revision is checked out.
617
617
618 Output may be to a file, in which case the name of the file is
618 Output may be to a file, in which case the name of the file is
619 given using a format string. The formatting rules are the same as
619 given using a format string. The formatting rules are the same as
620 for the export command, with the following additions:
620 for the export command, with the following additions:
621
621
622 :``%s``: basename of file being printed
622 :``%s``: basename of file being printed
623 :``%d``: dirname of file being printed, or '.' if in repository root
623 :``%d``: dirname of file being printed, or '.' if in repository root
624 :``%p``: root-relative path name of file being printed
624 :``%p``: root-relative path name of file being printed
625
625
626 Returns 0 on success.
626 Returns 0 on success.
627 """
627 """
628 ctx = repo[opts.get('rev')]
628 ctx = repo[opts.get('rev')]
629 err = 1
629 err = 1
630 m = cmdutil.match(repo, (file1,) + pats, opts)
630 m = cmdutil.match(repo, (file1,) + pats, opts)
631 for abs in ctx.walk(m):
631 for abs in ctx.walk(m):
632 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
632 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
633 data = ctx[abs].data()
633 data = ctx[abs].data()
634 if opts.get('decode'):
634 if opts.get('decode'):
635 data = repo.wwritedata(abs, data)
635 data = repo.wwritedata(abs, data)
636 fp.write(data)
636 fp.write(data)
637 err = 0
637 err = 0
638 return err
638 return err
639
639
640 def clone(ui, source, dest=None, **opts):
640 def clone(ui, source, dest=None, **opts):
641 """make a copy of an existing repository
641 """make a copy of an existing repository
642
642
643 Create a copy of an existing repository in a new directory.
643 Create a copy of an existing repository in a new directory.
644
644
645 If no destination directory name is specified, it defaults to the
645 If no destination directory name is specified, it defaults to the
646 basename of the source.
646 basename of the source.
647
647
648 The location of the source is added to the new repository's
648 The location of the source is added to the new repository's
649 .hg/hgrc file, as the default to be used for future pulls.
649 .hg/hgrc file, as the default to be used for future pulls.
650
650
651 See :hg:`help urls` for valid source format details.
651 See :hg:`help urls` for valid source format details.
652
652
653 It is possible to specify an ``ssh://`` URL as the destination, but no
653 It is possible to specify an ``ssh://`` URL as the destination, but no
654 .hg/hgrc and working directory will be created on the remote side.
654 .hg/hgrc and working directory will be created on the remote side.
655 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
655 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
656
656
657 A set of changesets (tags, or branch names) to pull may be specified
657 A set of changesets (tags, or branch names) to pull may be specified
658 by listing each changeset (tag, or branch name) with -r/--rev.
658 by listing each changeset (tag, or branch name) with -r/--rev.
659 If -r/--rev is used, the cloned repository will contain only a subset
659 If -r/--rev is used, the cloned repository will contain only a subset
660 of the changesets of the source repository. Only the set of changesets
660 of the changesets of the source repository. Only the set of changesets
661 defined by all -r/--rev options (including all their ancestors)
661 defined by all -r/--rev options (including all their ancestors)
662 will be pulled into the destination repository.
662 will be pulled into the destination repository.
663 No subsequent changesets (including subsequent tags) will be present
663 No subsequent changesets (including subsequent tags) will be present
664 in the destination.
664 in the destination.
665
665
666 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
666 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
667 local source repositories.
667 local source repositories.
668
668
669 For efficiency, hardlinks are used for cloning whenever the source
669 For efficiency, hardlinks are used for cloning whenever the source
670 and destination are on the same filesystem (note this applies only
670 and destination are on the same filesystem (note this applies only
671 to the repository data, not to the working directory). Some
671 to the repository data, not to the working directory). Some
672 filesystems, such as AFS, implement hardlinking incorrectly, but
672 filesystems, such as AFS, implement hardlinking incorrectly, but
673 do not report errors. In these cases, use the --pull option to
673 do not report errors. In these cases, use the --pull option to
674 avoid hardlinking.
674 avoid hardlinking.
675
675
676 In some cases, you can clone repositories and the working directory
676 In some cases, you can clone repositories and the working directory
677 using full hardlinks with ::
677 using full hardlinks with ::
678
678
679 $ cp -al REPO REPOCLONE
679 $ cp -al REPO REPOCLONE
680
680
681 This is the fastest way to clone, but it is not always safe. The
681 This is the fastest way to clone, but it is not always safe. The
682 operation is not atomic (making sure REPO is not modified during
682 operation is not atomic (making sure REPO is not modified during
683 the operation is up to you) and you have to make sure your editor
683 the operation is up to you) and you have to make sure your editor
684 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
684 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
685 this is not compatible with certain extensions that place their
685 this is not compatible with certain extensions that place their
686 metadata under the .hg directory, such as mq.
686 metadata under the .hg directory, such as mq.
687
687
688 Mercurial will update the working directory to the first applicable
688 Mercurial will update the working directory to the first applicable
689 revision from this list:
689 revision from this list:
690
690
691 a) null if -U or the source repository has no changesets
691 a) null if -U or the source repository has no changesets
692 b) if -u . and the source repository is local, the first parent of
692 b) if -u . and the source repository is local, the first parent of
693 the source repository's working directory
693 the source repository's working directory
694 c) the changeset specified with -u (if a branch name, this means the
694 c) the changeset specified with -u (if a branch name, this means the
695 latest head of that branch)
695 latest head of that branch)
696 d) the changeset specified with -r
696 d) the changeset specified with -r
697 e) the tipmost head specified with -b
697 e) the tipmost head specified with -b
698 f) the tipmost head specified with the url#branch source syntax
698 f) the tipmost head specified with the url#branch source syntax
699 g) the tipmost head of the default branch
699 g) the tipmost head of the default branch
700 h) tip
700 h) tip
701
701
702 Returns 0 on success.
702 Returns 0 on success.
703 """
703 """
704 if opts.get('noupdate') and opts.get('updaterev'):
704 if opts.get('noupdate') and opts.get('updaterev'):
705 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
705 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
706
706
707 r = hg.clone(hg.remoteui(ui, opts), source, dest,
707 r = hg.clone(hg.remoteui(ui, opts), source, dest,
708 pull=opts.get('pull'),
708 pull=opts.get('pull'),
709 stream=opts.get('uncompressed'),
709 stream=opts.get('uncompressed'),
710 rev=opts.get('rev'),
710 rev=opts.get('rev'),
711 update=opts.get('updaterev') or not opts.get('noupdate'),
711 update=opts.get('updaterev') or not opts.get('noupdate'),
712 branch=opts.get('branch'))
712 branch=opts.get('branch'))
713
713
714 return r is None
714 return r is None
715
715
716 def commit(ui, repo, *pats, **opts):
716 def commit(ui, repo, *pats, **opts):
717 """commit the specified files or all outstanding changes
717 """commit the specified files or all outstanding changes
718
718
719 Commit changes to the given files into the repository. Unlike a
719 Commit changes to the given files into the repository. Unlike a
720 centralized RCS, this operation is a local operation. See
720 centralized RCS, this operation is a local operation. See
721 :hg:`push` for a way to actively distribute your changes.
721 :hg:`push` for a way to actively distribute your changes.
722
722
723 If a list of files is omitted, all changes reported by :hg:`status`
723 If a list of files is omitted, all changes reported by :hg:`status`
724 will be committed.
724 will be committed.
725
725
726 If you are committing the result of a merge, do not provide any
726 If you are committing the result of a merge, do not provide any
727 filenames or -I/-X filters.
727 filenames or -I/-X filters.
728
728
729 If no commit message is specified, the configured editor is
729 If no commit message is specified, the configured editor is
730 started to prompt you for a message.
730 started to prompt you for a message.
731
731
732 See :hg:`help dates` for a list of formats valid for -d/--date.
732 See :hg:`help dates` for a list of formats valid for -d/--date.
733
733
734 Returns 0 on success, 1 if nothing changed.
734 Returns 0 on success, 1 if nothing changed.
735 """
735 """
736 extra = {}
736 extra = {}
737 if opts.get('close_branch'):
737 if opts.get('close_branch'):
738 if repo['.'].node() not in repo.branchheads():
738 if repo['.'].node() not in repo.branchheads():
739 # The topo heads set is included in the branch heads set of the
739 # The topo heads set is included in the branch heads set of the
740 # current branch, so it's sufficient to test branchheads
740 # current branch, so it's sufficient to test branchheads
741 raise util.Abort(_('can only close branch heads'))
741 raise util.Abort(_('can only close branch heads'))
742 extra['close'] = 1
742 extra['close'] = 1
743 e = cmdutil.commiteditor
743 e = cmdutil.commiteditor
744 if opts.get('force_editor'):
744 if opts.get('force_editor'):
745 e = cmdutil.commitforceeditor
745 e = cmdutil.commitforceeditor
746
746
747 def commitfunc(ui, repo, message, match, opts):
747 def commitfunc(ui, repo, message, match, opts):
748 return repo.commit(message, opts.get('user'), opts.get('date'), match,
748 return repo.commit(message, opts.get('user'), opts.get('date'), match,
749 editor=e, extra=extra)
749 editor=e, extra=extra)
750
750
751 branch = repo[None].branch()
751 branch = repo[None].branch()
752 bheads = repo.branchheads(branch)
752 bheads = repo.branchheads(branch)
753
753
754 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
754 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
755 if not node:
755 if not node:
756 ui.status(_("nothing changed\n"))
756 ui.status(_("nothing changed\n"))
757 return 1
757 return 1
758
758
759 ctx = repo[node]
759 ctx = repo[node]
760 parents = ctx.parents()
760 parents = ctx.parents()
761
761
762 if bheads and not [x for x in parents
762 if bheads and not [x for x in parents
763 if x.node() in bheads and x.branch() == branch]:
763 if x.node() in bheads and x.branch() == branch]:
764 ui.status(_('created new head\n'))
764 ui.status(_('created new head\n'))
765 # The message is not printed for initial roots. For the other
765 # The message is not printed for initial roots. For the other
766 # changesets, it is printed in the following situations:
766 # changesets, it is printed in the following situations:
767 #
767 #
768 # Par column: for the 2 parents with ...
768 # Par column: for the 2 parents with ...
769 # N: null or no parent
769 # N: null or no parent
770 # B: parent is on another named branch
770 # B: parent is on another named branch
771 # C: parent is a regular non head changeset
771 # C: parent is a regular non head changeset
772 # H: parent was a branch head of the current branch
772 # H: parent was a branch head of the current branch
773 # Msg column: whether we print "created new head" message
773 # Msg column: whether we print "created new head" message
774 # In the following, it is assumed that there already exists some
774 # In the following, it is assumed that there already exists some
775 # initial branch heads of the current branch, otherwise nothing is
775 # initial branch heads of the current branch, otherwise nothing is
776 # printed anyway.
776 # printed anyway.
777 #
777 #
778 # Par Msg Comment
778 # Par Msg Comment
779 # NN y additional topo root
779 # NN y additional topo root
780 #
780 #
781 # BN y additional branch root
781 # BN y additional branch root
782 # CN y additional topo head
782 # CN y additional topo head
783 # HN n usual case
783 # HN n usual case
784 #
784 #
785 # BB y weird additional branch root
785 # BB y weird additional branch root
786 # CB y branch merge
786 # CB y branch merge
787 # HB n merge with named branch
787 # HB n merge with named branch
788 #
788 #
789 # CC y additional head from merge
789 # CC y additional head from merge
790 # CH n merge with a head
790 # CH n merge with a head
791 #
791 #
792 # HH n head merge: head count decreases
792 # HH n head merge: head count decreases
793
793
794 if not opts.get('close_branch'):
794 if not opts.get('close_branch'):
795 for r in parents:
795 for r in parents:
796 if r.extra().get('close') and r.branch() == branch:
796 if r.extra().get('close') and r.branch() == branch:
797 ui.status(_('reopening closed branch head %d\n') % r)
797 ui.status(_('reopening closed branch head %d\n') % r)
798
798
799 if ui.debugflag:
799 if ui.debugflag:
800 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
800 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
801 elif ui.verbose:
801 elif ui.verbose:
802 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
802 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
803
803
804 def copy(ui, repo, *pats, **opts):
804 def copy(ui, repo, *pats, **opts):
805 """mark files as copied for the next commit
805 """mark files as copied for the next commit
806
806
807 Mark dest as having copies of source files. If dest is a
807 Mark dest as having copies of source files. If dest is a
808 directory, copies are put in that directory. If dest is a file,
808 directory, copies are put in that directory. If dest is a file,
809 the source must be a single file.
809 the source must be a single file.
810
810
811 By default, this command copies the contents of files as they
811 By default, this command copies the contents of files as they
812 exist in the working directory. If invoked with -A/--after, the
812 exist in the working directory. If invoked with -A/--after, the
813 operation is recorded, but no copying is performed.
813 operation is recorded, but no copying is performed.
814
814
815 This command takes effect with the next commit. To undo a copy
815 This command takes effect with the next commit. To undo a copy
816 before that, see :hg:`revert`.
816 before that, see :hg:`revert`.
817
817
818 Returns 0 on success, 1 if errors are encountered.
818 Returns 0 on success, 1 if errors are encountered.
819 """
819 """
820 wlock = repo.wlock(False)
820 wlock = repo.wlock(False)
821 try:
821 try:
822 return cmdutil.copy(ui, repo, pats, opts)
822 return cmdutil.copy(ui, repo, pats, opts)
823 finally:
823 finally:
824 wlock.release()
824 wlock.release()
825
825
826 def debugancestor(ui, repo, *args):
826 def debugancestor(ui, repo, *args):
827 """find the ancestor revision of two revisions in a given index"""
827 """find the ancestor revision of two revisions in a given index"""
828 if len(args) == 3:
828 if len(args) == 3:
829 index, rev1, rev2 = args
829 index, rev1, rev2 = args
830 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
830 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
831 lookup = r.lookup
831 lookup = r.lookup
832 elif len(args) == 2:
832 elif len(args) == 2:
833 if not repo:
833 if not repo:
834 raise util.Abort(_("There is no Mercurial repository here "
834 raise util.Abort(_("There is no Mercurial repository here "
835 "(.hg not found)"))
835 "(.hg not found)"))
836 rev1, rev2 = args
836 rev1, rev2 = args
837 r = repo.changelog
837 r = repo.changelog
838 lookup = repo.lookup
838 lookup = repo.lookup
839 else:
839 else:
840 raise util.Abort(_('either two or three arguments required'))
840 raise util.Abort(_('either two or three arguments required'))
841 a = r.ancestor(lookup(rev1), lookup(rev2))
841 a = r.ancestor(lookup(rev1), lookup(rev2))
842 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
842 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
843
843
844 def debugbuilddag(ui, repo, text,
844 def debugbuilddag(ui, repo, text,
845 mergeable_file=False,
845 mergeable_file=False,
846 appended_file=False,
846 appended_file=False,
847 overwritten_file=False,
847 overwritten_file=False,
848 new_file=False):
848 new_file=False):
849 """builds a repo with a given dag from scratch in the current empty repo
849 """builds a repo with a given dag from scratch in the current empty repo
850
850
851 Elements:
851 Elements:
852
852
853 - "+n" is a linear run of n nodes based on the current default parent
853 - "+n" is a linear run of n nodes based on the current default parent
854 - "." is a single node based on the current default parent
854 - "." is a single node based on the current default parent
855 - "$" resets the default parent to null (implied at the start);
855 - "$" resets the default parent to null (implied at the start);
856 otherwise the default parent is always the last node created
856 otherwise the default parent is always the last node created
857 - "<p" sets the default parent to the backref p
857 - "<p" sets the default parent to the backref p
858 - "*p" is a fork at parent p, which is a backref
858 - "*p" is a fork at parent p, which is a backref
859 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
859 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
860 - "/p2" is a merge of the preceding node and p2
860 - "/p2" is a merge of the preceding node and p2
861 - ":tag" defines a local tag for the preceding node
861 - ":tag" defines a local tag for the preceding node
862 - "@branch" sets the named branch for subsequent nodes
862 - "@branch" sets the named branch for subsequent nodes
863 - "!command" runs the command using your shell
863 - "!command" runs the command using your shell
864 - "!!my command\\n" is like "!", but to the end of the line
864 - "!!my command\\n" is like "!", but to the end of the line
865 - "#...\\n" is a comment up to the end of the line
865 - "#...\\n" is a comment up to the end of the line
866
866
867 Whitespace between the above elements is ignored.
867 Whitespace between the above elements is ignored.
868
868
869 A backref is either
869 A backref is either
870
870
871 - a number n, which references the node curr-n, where curr is the current
871 - a number n, which references the node curr-n, where curr is the current
872 node, or
872 node, or
873 - the name of a local tag you placed earlier using ":tag", or
873 - the name of a local tag you placed earlier using ":tag", or
874 - empty to denote the default parent.
874 - empty to denote the default parent.
875
875
876 All string valued-elements are either strictly alphanumeric, or must
876 All string valued-elements are either strictly alphanumeric, or must
877 be enclosed in double quotes ("..."), with "\" as escape character.
877 be enclosed in double quotes ("..."), with "\" as escape character.
878
878
879 Note that the --overwritten-file and --appended-file options imply the
879 Note that the --overwritten-file and --appended-file options imply the
880 use of "HGMERGE=internal:local" during DAG buildup.
880 use of "HGMERGE=internal:local" during DAG buildup.
881 """
881 """
882
882
883 if not (mergeable_file or appended_file or overwritten_file or new_file):
883 if not (mergeable_file or appended_file or overwritten_file or new_file):
884 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
884 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
885
885
886 if len(repo.changelog) > 0:
886 if len(repo.changelog) > 0:
887 raise util.Abort(_('repository is not empty'))
887 raise util.Abort(_('repository is not empty'))
888
888
889 if overwritten_file or appended_file:
889 if overwritten_file or appended_file:
890 # we don't want to fail in merges during buildup
890 # we don't want to fail in merges during buildup
891 os.environ['HGMERGE'] = 'internal:local'
891 os.environ['HGMERGE'] = 'internal:local'
892
892
893 def writefile(fname, text, fmode="wb"):
893 def writefile(fname, text, fmode="wb"):
894 f = open(fname, fmode)
894 f = open(fname, fmode)
895 try:
895 try:
896 f.write(text)
896 f.write(text)
897 finally:
897 finally:
898 f.close()
898 f.close()
899
899
900 if mergeable_file:
900 if mergeable_file:
901 linesperrev = 2
901 linesperrev = 2
902 # determine number of revs in DAG
902 # determine number of revs in DAG
903 n = 0
903 n = 0
904 for type, data in dagparser.parsedag(text):
904 for type, data in dagparser.parsedag(text):
905 if type == 'n':
905 if type == 'n':
906 n += 1
906 n += 1
907 # make a file with k lines per rev
907 # make a file with k lines per rev
908 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
908 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
909 + "\n")
909 + "\n")
910
910
911 at = -1
911 at = -1
912 atbranch = 'default'
912 atbranch = 'default'
913 for type, data in dagparser.parsedag(text):
913 for type, data in dagparser.parsedag(text):
914 if type == 'n':
914 if type == 'n':
915 ui.status('node %s\n' % str(data))
915 ui.status('node %s\n' % str(data))
916 id, ps = data
916 id, ps = data
917 p1 = ps[0]
917 p1 = ps[0]
918 if p1 != at:
918 if p1 != at:
919 update(ui, repo, node=p1, clean=True)
919 update(ui, repo, node=p1, clean=True)
920 at = p1
920 at = p1
921 if repo.dirstate.branch() != atbranch:
921 if repo.dirstate.branch() != atbranch:
922 branch(ui, repo, atbranch, force=True)
922 branch(ui, repo, atbranch, force=True)
923 if len(ps) > 1:
923 if len(ps) > 1:
924 p2 = ps[1]
924 p2 = ps[1]
925 merge(ui, repo, node=p2)
925 merge(ui, repo, node=p2)
926
926
927 if mergeable_file:
927 if mergeable_file:
928 f = open("mf", "rb+")
928 f = open("mf", "rb+")
929 try:
929 try:
930 lines = f.read().split("\n")
930 lines = f.read().split("\n")
931 lines[id * linesperrev] += " r%i" % id
931 lines[id * linesperrev] += " r%i" % id
932 f.seek(0)
932 f.seek(0)
933 f.write("\n".join(lines))
933 f.write("\n".join(lines))
934 finally:
934 finally:
935 f.close()
935 f.close()
936
936
937 if appended_file:
937 if appended_file:
938 writefile("af", "r%i\n" % id, "ab")
938 writefile("af", "r%i\n" % id, "ab")
939
939
940 if overwritten_file:
940 if overwritten_file:
941 writefile("of", "r%i\n" % id)
941 writefile("of", "r%i\n" % id)
942
942
943 if new_file:
943 if new_file:
944 writefile("nf%i" % id, "r%i\n" % id)
944 writefile("nf%i" % id, "r%i\n" % id)
945
945
946 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
946 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
947 at = id
947 at = id
948 elif type == 'l':
948 elif type == 'l':
949 id, name = data
949 id, name = data
950 ui.status('tag %s\n' % name)
950 ui.status('tag %s\n' % name)
951 tag(ui, repo, name, local=True)
951 tag(ui, repo, name, local=True)
952 elif type == 'a':
952 elif type == 'a':
953 ui.status('branch %s\n' % data)
953 ui.status('branch %s\n' % data)
954 atbranch = data
954 atbranch = data
955 elif type in 'cC':
955 elif type in 'cC':
956 r = util.system(data, cwd=repo.root)
956 r = util.system(data, cwd=repo.root)
957 if r:
957 if r:
958 desc, r = util.explain_exit(r)
958 desc, r = util.explain_exit(r)
959 raise util.Abort(_('%s command %s') % (data, desc))
959 raise util.Abort(_('%s command %s') % (data, desc))
960
960
961 def debugcommands(ui, cmd='', *args):
961 def debugcommands(ui, cmd='', *args):
962 """list all available commands and options"""
962 """list all available commands and options"""
963 for cmd, vals in sorted(table.iteritems()):
963 for cmd, vals in sorted(table.iteritems()):
964 cmd = cmd.split('|')[0].strip('^')
964 cmd = cmd.split('|')[0].strip('^')
965 opts = ', '.join([i[1] for i in vals[1]])
965 opts = ', '.join([i[1] for i in vals[1]])
966 ui.write('%s: %s\n' % (cmd, opts))
966 ui.write('%s: %s\n' % (cmd, opts))
967
967
968 def debugcomplete(ui, cmd='', **opts):
968 def debugcomplete(ui, cmd='', **opts):
969 """returns the completion list associated with the given command"""
969 """returns the completion list associated with the given command"""
970
970
971 if opts.get('options'):
971 if opts.get('options'):
972 options = []
972 options = []
973 otables = [globalopts]
973 otables = [globalopts]
974 if cmd:
974 if cmd:
975 aliases, entry = cmdutil.findcmd(cmd, table, False)
975 aliases, entry = cmdutil.findcmd(cmd, table, False)
976 otables.append(entry[1])
976 otables.append(entry[1])
977 for t in otables:
977 for t in otables:
978 for o in t:
978 for o in t:
979 if "(DEPRECATED)" in o[3]:
979 if "(DEPRECATED)" in o[3]:
980 continue
980 continue
981 if o[0]:
981 if o[0]:
982 options.append('-%s' % o[0])
982 options.append('-%s' % o[0])
983 options.append('--%s' % o[1])
983 options.append('--%s' % o[1])
984 ui.write("%s\n" % "\n".join(options))
984 ui.write("%s\n" % "\n".join(options))
985 return
985 return
986
986
987 cmdlist = cmdutil.findpossible(cmd, table)
987 cmdlist = cmdutil.findpossible(cmd, table)
988 if ui.verbose:
988 if ui.verbose:
989 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
989 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
990 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
990 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
991
991
992 def debugfsinfo(ui, path = "."):
992 def debugfsinfo(ui, path = "."):
993 """show information detected about current filesystem"""
993 """show information detected about current filesystem"""
994 open('.debugfsinfo', 'w').write('')
994 open('.debugfsinfo', 'w').write('')
995 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
995 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
996 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
996 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
997 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
997 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
998 and 'yes' or 'no'))
998 and 'yes' or 'no'))
999 os.unlink('.debugfsinfo')
999 os.unlink('.debugfsinfo')
1000
1000
1001 def debugrebuildstate(ui, repo, rev="tip"):
1001 def debugrebuildstate(ui, repo, rev="tip"):
1002 """rebuild the dirstate as it would look like for the given revision"""
1002 """rebuild the dirstate as it would look like for the given revision"""
1003 ctx = repo[rev]
1003 ctx = repo[rev]
1004 wlock = repo.wlock()
1004 wlock = repo.wlock()
1005 try:
1005 try:
1006 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1006 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1007 finally:
1007 finally:
1008 wlock.release()
1008 wlock.release()
1009
1009
1010 def debugcheckstate(ui, repo):
1010 def debugcheckstate(ui, repo):
1011 """validate the correctness of the current dirstate"""
1011 """validate the correctness of the current dirstate"""
1012 parent1, parent2 = repo.dirstate.parents()
1012 parent1, parent2 = repo.dirstate.parents()
1013 m1 = repo[parent1].manifest()
1013 m1 = repo[parent1].manifest()
1014 m2 = repo[parent2].manifest()
1014 m2 = repo[parent2].manifest()
1015 errors = 0
1015 errors = 0
1016 for f in repo.dirstate:
1016 for f in repo.dirstate:
1017 state = repo.dirstate[f]
1017 state = repo.dirstate[f]
1018 if state in "nr" and f not in m1:
1018 if state in "nr" and f not in m1:
1019 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1019 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1020 errors += 1
1020 errors += 1
1021 if state in "a" and f in m1:
1021 if state in "a" and f in m1:
1022 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1022 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1023 errors += 1
1023 errors += 1
1024 if state in "m" and f not in m1 and f not in m2:
1024 if state in "m" and f not in m1 and f not in m2:
1025 ui.warn(_("%s in state %s, but not in either manifest\n") %
1025 ui.warn(_("%s in state %s, but not in either manifest\n") %
1026 (f, state))
1026 (f, state))
1027 errors += 1
1027 errors += 1
1028 for f in m1:
1028 for f in m1:
1029 state = repo.dirstate[f]
1029 state = repo.dirstate[f]
1030 if state not in "nrm":
1030 if state not in "nrm":
1031 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1031 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1032 errors += 1
1032 errors += 1
1033 if errors:
1033 if errors:
1034 error = _(".hg/dirstate inconsistent with current parent's manifest")
1034 error = _(".hg/dirstate inconsistent with current parent's manifest")
1035 raise util.Abort(error)
1035 raise util.Abort(error)
1036
1036
1037 def showconfig(ui, repo, *values, **opts):
1037 def showconfig(ui, repo, *values, **opts):
1038 """show combined config settings from all hgrc files
1038 """show combined config settings from all hgrc files
1039
1039
1040 With no arguments, print names and values of all config items.
1040 With no arguments, print names and values of all config items.
1041
1041
1042 With one argument of the form section.name, print just the value
1042 With one argument of the form section.name, print just the value
1043 of that config item.
1043 of that config item.
1044
1044
1045 With multiple arguments, print names and values of all config
1045 With multiple arguments, print names and values of all config
1046 items with matching section names.
1046 items with matching section names.
1047
1047
1048 With --debug, the source (filename and line number) is printed
1048 With --debug, the source (filename and line number) is printed
1049 for each config item.
1049 for each config item.
1050
1050
1051 Returns 0 on success.
1051 Returns 0 on success.
1052 """
1052 """
1053
1053
1054 for f in util.rcpath():
1054 for f in util.rcpath():
1055 ui.debug(_('read config from: %s\n') % f)
1055 ui.debug(_('read config from: %s\n') % f)
1056 untrusted = bool(opts.get('untrusted'))
1056 untrusted = bool(opts.get('untrusted'))
1057 if values:
1057 if values:
1058 if len([v for v in values if '.' in v]) > 1:
1058 if len([v for v in values if '.' in v]) > 1:
1059 raise util.Abort(_('only one config item permitted'))
1059 raise util.Abort(_('only one config item permitted'))
1060 for section, name, value in ui.walkconfig(untrusted=untrusted):
1060 for section, name, value in ui.walkconfig(untrusted=untrusted):
1061 sectname = section + '.' + name
1061 sectname = section + '.' + name
1062 if values:
1062 if values:
1063 for v in values:
1063 for v in values:
1064 if v == section:
1064 if v == section:
1065 ui.debug('%s: ' %
1065 ui.debug('%s: ' %
1066 ui.configsource(section, name, untrusted))
1066 ui.configsource(section, name, untrusted))
1067 ui.write('%s=%s\n' % (sectname, value))
1067 ui.write('%s=%s\n' % (sectname, value))
1068 elif v == sectname:
1068 elif v == sectname:
1069 ui.debug('%s: ' %
1069 ui.debug('%s: ' %
1070 ui.configsource(section, name, untrusted))
1070 ui.configsource(section, name, untrusted))
1071 ui.write(value, '\n')
1071 ui.write(value, '\n')
1072 else:
1072 else:
1073 ui.debug('%s: ' %
1073 ui.debug('%s: ' %
1074 ui.configsource(section, name, untrusted))
1074 ui.configsource(section, name, untrusted))
1075 ui.write('%s=%s\n' % (sectname, value))
1075 ui.write('%s=%s\n' % (sectname, value))
1076
1076
1077 def debugpushkey(ui, repopath, namespace, *keyinfo):
1077 def debugpushkey(ui, repopath, namespace, *keyinfo):
1078 '''access the pushkey key/value protocol
1078 '''access the pushkey key/value protocol
1079
1079
1080 With two args, list the keys in the given namespace.
1080 With two args, list the keys in the given namespace.
1081
1081
1082 With five args, set a key to new if it currently is set to old.
1082 With five args, set a key to new if it currently is set to old.
1083 Reports success or failure.
1083 Reports success or failure.
1084 '''
1084 '''
1085
1085
1086 target = hg.repository(ui, repopath)
1086 target = hg.repository(ui, repopath)
1087 if keyinfo:
1087 if keyinfo:
1088 key, old, new = keyinfo
1088 key, old, new = keyinfo
1089 r = target.pushkey(namespace, key, old, new)
1089 r = target.pushkey(namespace, key, old, new)
1090 ui.status(str(r) + '\n')
1090 ui.status(str(r) + '\n')
1091 return not(r)
1091 return not(r)
1092 else:
1092 else:
1093 for k, v in target.listkeys(namespace).iteritems():
1093 for k, v in target.listkeys(namespace).iteritems():
1094 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1094 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1095 v.encode('string-escape')))
1095 v.encode('string-escape')))
1096
1096
1097 def debugrevspec(ui, repo, expr):
1097 def debugrevspec(ui, repo, expr):
1098 '''parse and apply a revision specification'''
1098 '''parse and apply a revision specification'''
1099 if ui.verbose:
1099 if ui.verbose:
1100 tree = revset.parse(expr)
1100 tree = revset.parse(expr)
1101 ui.note(tree, "\n")
1101 ui.note(tree, "\n")
1102 func = revset.match(expr)
1102 func = revset.match(expr)
1103 for c in func(repo, range(len(repo))):
1103 for c in func(repo, range(len(repo))):
1104 ui.write("%s\n" % c)
1104 ui.write("%s\n" % c)
1105
1105
1106 def debugsetparents(ui, repo, rev1, rev2=None):
1106 def debugsetparents(ui, repo, rev1, rev2=None):
1107 """manually set the parents of the current working directory
1107 """manually set the parents of the current working directory
1108
1108
1109 This is useful for writing repository conversion tools, but should
1109 This is useful for writing repository conversion tools, but should
1110 be used with care.
1110 be used with care.
1111
1111
1112 Returns 0 on success.
1112 Returns 0 on success.
1113 """
1113 """
1114
1114
1115 if not rev2:
1115 if not rev2:
1116 rev2 = hex(nullid)
1116 rev2 = hex(nullid)
1117
1117
1118 wlock = repo.wlock()
1118 wlock = repo.wlock()
1119 try:
1119 try:
1120 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1120 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1121 finally:
1121 finally:
1122 wlock.release()
1122 wlock.release()
1123
1123
1124 def debugstate(ui, repo, nodates=None):
1124 def debugstate(ui, repo, nodates=None):
1125 """show the contents of the current dirstate"""
1125 """show the contents of the current dirstate"""
1126 timestr = ""
1126 timestr = ""
1127 showdate = not nodates
1127 showdate = not nodates
1128 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1128 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1129 if showdate:
1129 if showdate:
1130 if ent[3] == -1:
1130 if ent[3] == -1:
1131 # Pad or slice to locale representation
1131 # Pad or slice to locale representation
1132 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1132 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1133 time.localtime(0)))
1133 time.localtime(0)))
1134 timestr = 'unset'
1134 timestr = 'unset'
1135 timestr = (timestr[:locale_len] +
1135 timestr = (timestr[:locale_len] +
1136 ' ' * (locale_len - len(timestr)))
1136 ' ' * (locale_len - len(timestr)))
1137 else:
1137 else:
1138 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1138 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1139 time.localtime(ent[3]))
1139 time.localtime(ent[3]))
1140 if ent[1] & 020000:
1140 if ent[1] & 020000:
1141 mode = 'lnk'
1141 mode = 'lnk'
1142 else:
1142 else:
1143 mode = '%3o' % (ent[1] & 0777)
1143 mode = '%3o' % (ent[1] & 0777)
1144 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1144 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1145 for f in repo.dirstate.copies():
1145 for f in repo.dirstate.copies():
1146 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1146 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1147
1147
1148 def debugsub(ui, repo, rev=None):
1148 def debugsub(ui, repo, rev=None):
1149 if rev == '':
1149 if rev == '':
1150 rev = None
1150 rev = None
1151 for k, v in sorted(repo[rev].substate.items()):
1151 for k, v in sorted(repo[rev].substate.items()):
1152 ui.write('path %s\n' % k)
1152 ui.write('path %s\n' % k)
1153 ui.write(' source %s\n' % v[0])
1153 ui.write(' source %s\n' % v[0])
1154 ui.write(' revision %s\n' % v[1])
1154 ui.write(' revision %s\n' % v[1])
1155
1155
1156 def debugdag(ui, repo, file_=None, *revs, **opts):
1156 def debugdag(ui, repo, file_=None, *revs, **opts):
1157 """format the changelog or an index DAG as a concise textual description
1157 """format the changelog or an index DAG as a concise textual description
1158
1158
1159 If you pass a revlog index, the revlog's DAG is emitted. If you list
1159 If you pass a revlog index, the revlog's DAG is emitted. If you list
1160 revision numbers, they get labelled in the output as rN.
1160 revision numbers, they get labelled in the output as rN.
1161
1161
1162 Otherwise, the changelog DAG of the current repo is emitted.
1162 Otherwise, the changelog DAG of the current repo is emitted.
1163 """
1163 """
1164 spaces = opts.get('spaces')
1164 spaces = opts.get('spaces')
1165 dots = opts.get('dots')
1165 dots = opts.get('dots')
1166 if file_:
1166 if file_:
1167 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1167 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1168 revs = set((int(r) for r in revs))
1168 revs = set((int(r) for r in revs))
1169 def events():
1169 def events():
1170 for r in rlog:
1170 for r in rlog:
1171 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1171 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1172 if r in revs:
1172 if r in revs:
1173 yield 'l', (r, "r%i" % r)
1173 yield 'l', (r, "r%i" % r)
1174 elif repo:
1174 elif repo:
1175 cl = repo.changelog
1175 cl = repo.changelog
1176 tags = opts.get('tags')
1176 tags = opts.get('tags')
1177 branches = opts.get('branches')
1177 branches = opts.get('branches')
1178 if tags:
1178 if tags:
1179 labels = {}
1179 labels = {}
1180 for l, n in repo.tags().items():
1180 for l, n in repo.tags().items():
1181 labels.setdefault(cl.rev(n), []).append(l)
1181 labels.setdefault(cl.rev(n), []).append(l)
1182 def events():
1182 def events():
1183 b = "default"
1183 b = "default"
1184 for r in cl:
1184 for r in cl:
1185 if branches:
1185 if branches:
1186 newb = cl.read(cl.node(r))[5]['branch']
1186 newb = cl.read(cl.node(r))[5]['branch']
1187 if newb != b:
1187 if newb != b:
1188 yield 'a', newb
1188 yield 'a', newb
1189 b = newb
1189 b = newb
1190 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1190 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1191 if tags:
1191 if tags:
1192 ls = labels.get(r)
1192 ls = labels.get(r)
1193 if ls:
1193 if ls:
1194 for l in ls:
1194 for l in ls:
1195 yield 'l', (r, l)
1195 yield 'l', (r, l)
1196 else:
1196 else:
1197 raise util.Abort(_('need repo for changelog dag'))
1197 raise util.Abort(_('need repo for changelog dag'))
1198
1198
1199 for line in dagparser.dagtextlines(events(),
1199 for line in dagparser.dagtextlines(events(),
1200 addspaces=spaces,
1200 addspaces=spaces,
1201 wraplabels=True,
1201 wraplabels=True,
1202 wrapannotations=True,
1202 wrapannotations=True,
1203 wrapnonlinear=dots,
1203 wrapnonlinear=dots,
1204 usedots=dots,
1204 usedots=dots,
1205 maxlinewidth=70):
1205 maxlinewidth=70):
1206 ui.write(line)
1206 ui.write(line)
1207 ui.write("\n")
1207 ui.write("\n")
1208
1208
1209 def debugdata(ui, file_, rev):
1209 def debugdata(ui, file_, rev):
1210 """dump the contents of a data file revision"""
1210 """dump the contents of a data file revision"""
1211 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1211 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1212 try:
1212 try:
1213 ui.write(r.revision(r.lookup(rev)))
1213 ui.write(r.revision(r.lookup(rev)))
1214 except KeyError:
1214 except KeyError:
1215 raise util.Abort(_('invalid revision identifier %s') % rev)
1215 raise util.Abort(_('invalid revision identifier %s') % rev)
1216
1216
1217 def debugdate(ui, date, range=None, **opts):
1217 def debugdate(ui, date, range=None, **opts):
1218 """parse and display a date"""
1218 """parse and display a date"""
1219 if opts["extended"]:
1219 if opts["extended"]:
1220 d = util.parsedate(date, util.extendeddateformats)
1220 d = util.parsedate(date, util.extendeddateformats)
1221 else:
1221 else:
1222 d = util.parsedate(date)
1222 d = util.parsedate(date)
1223 ui.write("internal: %s %s\n" % d)
1223 ui.write("internal: %s %s\n" % d)
1224 ui.write("standard: %s\n" % util.datestr(d))
1224 ui.write("standard: %s\n" % util.datestr(d))
1225 if range:
1225 if range:
1226 m = util.matchdate(range)
1226 m = util.matchdate(range)
1227 ui.write("match: %s\n" % m(d[0]))
1227 ui.write("match: %s\n" % m(d[0]))
1228
1228
1229 def debugindex(ui, file_):
1229 def debugindex(ui, file_):
1230 """dump the contents of an index file"""
1230 """dump the contents of an index file"""
1231 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1231 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1232 ui.write(" rev offset length base linkrev"
1232 ui.write(" rev offset length base linkrev"
1233 " nodeid p1 p2\n")
1233 " nodeid p1 p2\n")
1234 for i in r:
1234 for i in r:
1235 node = r.node(i)
1235 node = r.node(i)
1236 try:
1236 try:
1237 pp = r.parents(node)
1237 pp = r.parents(node)
1238 except:
1238 except:
1239 pp = [nullid, nullid]
1239 pp = [nullid, nullid]
1240 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1240 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1241 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1241 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1242 short(node), short(pp[0]), short(pp[1])))
1242 short(node), short(pp[0]), short(pp[1])))
1243
1243
1244 def debugindexdot(ui, file_):
1244 def debugindexdot(ui, file_):
1245 """dump an index DAG as a graphviz dot file"""
1245 """dump an index DAG as a graphviz dot file"""
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1247 ui.write("digraph G {\n")
1247 ui.write("digraph G {\n")
1248 for i in r:
1248 for i in r:
1249 node = r.node(i)
1249 node = r.node(i)
1250 pp = r.parents(node)
1250 pp = r.parents(node)
1251 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1251 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1252 if pp[1] != nullid:
1252 if pp[1] != nullid:
1253 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1253 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1254 ui.write("}\n")
1254 ui.write("}\n")
1255
1255
1256 def debuginstall(ui):
1256 def debuginstall(ui):
1257 '''test Mercurial installation
1257 '''test Mercurial installation
1258
1258
1259 Returns 0 on success.
1259 Returns 0 on success.
1260 '''
1260 '''
1261
1261
1262 def writetemp(contents):
1262 def writetemp(contents):
1263 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1263 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1264 f = os.fdopen(fd, "wb")
1264 f = os.fdopen(fd, "wb")
1265 f.write(contents)
1265 f.write(contents)
1266 f.close()
1266 f.close()
1267 return name
1267 return name
1268
1268
1269 problems = 0
1269 problems = 0
1270
1270
1271 # encoding
1271 # encoding
1272 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1272 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1273 try:
1273 try:
1274 encoding.fromlocal("test")
1274 encoding.fromlocal("test")
1275 except util.Abort, inst:
1275 except util.Abort, inst:
1276 ui.write(" %s\n" % inst)
1276 ui.write(" %s\n" % inst)
1277 ui.write(_(" (check that your locale is properly set)\n"))
1277 ui.write(_(" (check that your locale is properly set)\n"))
1278 problems += 1
1278 problems += 1
1279
1279
1280 # compiled modules
1280 # compiled modules
1281 ui.status(_("Checking extensions...\n"))
1281 ui.status(_("Checking extensions...\n"))
1282 try:
1282 try:
1283 import bdiff, mpatch, base85
1283 import bdiff, mpatch, base85
1284 except Exception, inst:
1284 except Exception, inst:
1285 ui.write(" %s\n" % inst)
1285 ui.write(" %s\n" % inst)
1286 ui.write(_(" One or more extensions could not be found"))
1286 ui.write(_(" One or more extensions could not be found"))
1287 ui.write(_(" (check that you compiled the extensions)\n"))
1287 ui.write(_(" (check that you compiled the extensions)\n"))
1288 problems += 1
1288 problems += 1
1289
1289
1290 # templates
1290 # templates
1291 ui.status(_("Checking templates...\n"))
1291 ui.status(_("Checking templates...\n"))
1292 try:
1292 try:
1293 import templater
1293 import templater
1294 templater.templater(templater.templatepath("map-cmdline.default"))
1294 templater.templater(templater.templatepath("map-cmdline.default"))
1295 except Exception, inst:
1295 except Exception, inst:
1296 ui.write(" %s\n" % inst)
1296 ui.write(" %s\n" % inst)
1297 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1297 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1298 problems += 1
1298 problems += 1
1299
1299
1300 # patch
1300 # patch
1301 ui.status(_("Checking patch...\n"))
1301 ui.status(_("Checking patch...\n"))
1302 patchproblems = 0
1302 patchproblems = 0
1303 a = "1\n2\n3\n4\n"
1303 a = "1\n2\n3\n4\n"
1304 b = "1\n2\n3\ninsert\n4\n"
1304 b = "1\n2\n3\ninsert\n4\n"
1305 fa = writetemp(a)
1305 fa = writetemp(a)
1306 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1306 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1307 os.path.basename(fa))
1307 os.path.basename(fa))
1308 fd = writetemp(d)
1308 fd = writetemp(d)
1309
1309
1310 files = {}
1310 files = {}
1311 try:
1311 try:
1312 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1312 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1313 except util.Abort, e:
1313 except util.Abort, e:
1314 ui.write(_(" patch call failed:\n"))
1314 ui.write(_(" patch call failed:\n"))
1315 ui.write(" " + str(e) + "\n")
1315 ui.write(" " + str(e) + "\n")
1316 patchproblems += 1
1316 patchproblems += 1
1317 else:
1317 else:
1318 if list(files) != [os.path.basename(fa)]:
1318 if list(files) != [os.path.basename(fa)]:
1319 ui.write(_(" unexpected patch output!\n"))
1319 ui.write(_(" unexpected patch output!\n"))
1320 patchproblems += 1
1320 patchproblems += 1
1321 a = open(fa).read()
1321 a = open(fa).read()
1322 if a != b:
1322 if a != b:
1323 ui.write(_(" patch test failed!\n"))
1323 ui.write(_(" patch test failed!\n"))
1324 patchproblems += 1
1324 patchproblems += 1
1325
1325
1326 if patchproblems:
1326 if patchproblems:
1327 if ui.config('ui', 'patch'):
1327 if ui.config('ui', 'patch'):
1328 ui.write(_(" (Current patch tool may be incompatible with patch,"
1328 ui.write(_(" (Current patch tool may be incompatible with patch,"
1329 " or misconfigured. Please check your .hgrc file)\n"))
1329 " or misconfigured. Please check your .hgrc file)\n"))
1330 else:
1330 else:
1331 ui.write(_(" Internal patcher failure, please report this error"
1331 ui.write(_(" Internal patcher failure, please report this error"
1332 " to http://mercurial.selenic.com/bts/\n"))
1332 " to http://mercurial.selenic.com/bts/\n"))
1333 problems += patchproblems
1333 problems += patchproblems
1334
1334
1335 os.unlink(fa)
1335 os.unlink(fa)
1336 os.unlink(fd)
1336 os.unlink(fd)
1337
1337
1338 # editor
1338 # editor
1339 ui.status(_("Checking commit editor...\n"))
1339 ui.status(_("Checking commit editor...\n"))
1340 editor = ui.geteditor()
1340 editor = ui.geteditor()
1341 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1341 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1342 if not cmdpath:
1342 if not cmdpath:
1343 if editor == 'vi':
1343 if editor == 'vi':
1344 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1344 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1345 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1345 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1346 else:
1346 else:
1347 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1347 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1348 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1348 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1349 problems += 1
1349 problems += 1
1350
1350
1351 # check username
1351 # check username
1352 ui.status(_("Checking username...\n"))
1352 ui.status(_("Checking username...\n"))
1353 try:
1353 try:
1354 user = ui.username()
1354 user = ui.username()
1355 except util.Abort, e:
1355 except util.Abort, e:
1356 ui.write(" %s\n" % e)
1356 ui.write(" %s\n" % e)
1357 ui.write(_(" (specify a username in your .hgrc file)\n"))
1357 ui.write(_(" (specify a username in your .hgrc file)\n"))
1358 problems += 1
1358 problems += 1
1359
1359
1360 if not problems:
1360 if not problems:
1361 ui.status(_("No problems detected\n"))
1361 ui.status(_("No problems detected\n"))
1362 else:
1362 else:
1363 ui.write(_("%s problems detected,"
1363 ui.write(_("%s problems detected,"
1364 " please check your install!\n") % problems)
1364 " please check your install!\n") % problems)
1365
1365
1366 return problems
1366 return problems
1367
1367
1368 def debugrename(ui, repo, file1, *pats, **opts):
1368 def debugrename(ui, repo, file1, *pats, **opts):
1369 """dump rename information"""
1369 """dump rename information"""
1370
1370
1371 ctx = repo[opts.get('rev')]
1371 ctx = repo[opts.get('rev')]
1372 m = cmdutil.match(repo, (file1,) + pats, opts)
1372 m = cmdutil.match(repo, (file1,) + pats, opts)
1373 for abs in ctx.walk(m):
1373 for abs in ctx.walk(m):
1374 fctx = ctx[abs]
1374 fctx = ctx[abs]
1375 o = fctx.filelog().renamed(fctx.filenode())
1375 o = fctx.filelog().renamed(fctx.filenode())
1376 rel = m.rel(abs)
1376 rel = m.rel(abs)
1377 if o:
1377 if o:
1378 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1378 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1379 else:
1379 else:
1380 ui.write(_("%s not renamed\n") % rel)
1380 ui.write(_("%s not renamed\n") % rel)
1381
1381
1382 def debugwalk(ui, repo, *pats, **opts):
1382 def debugwalk(ui, repo, *pats, **opts):
1383 """show how files match on given patterns"""
1383 """show how files match on given patterns"""
1384 m = cmdutil.match(repo, pats, opts)
1384 m = cmdutil.match(repo, pats, opts)
1385 items = list(repo.walk(m))
1385 items = list(repo.walk(m))
1386 if not items:
1386 if not items:
1387 return
1387 return
1388 fmt = 'f %%-%ds %%-%ds %%s' % (
1388 fmt = 'f %%-%ds %%-%ds %%s' % (
1389 max([len(abs) for abs in items]),
1389 max([len(abs) for abs in items]),
1390 max([len(m.rel(abs)) for abs in items]))
1390 max([len(m.rel(abs)) for abs in items]))
1391 for abs in items:
1391 for abs in items:
1392 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1392 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1393 ui.write("%s\n" % line.rstrip())
1393 ui.write("%s\n" % line.rstrip())
1394
1394
1395 def diff(ui, repo, *pats, **opts):
1395 def diff(ui, repo, *pats, **opts):
1396 """diff repository (or selected files)
1396 """diff repository (or selected files)
1397
1397
1398 Show differences between revisions for the specified files.
1398 Show differences between revisions for the specified files.
1399
1399
1400 Differences between files are shown using the unified diff format.
1400 Differences between files are shown using the unified diff format.
1401
1401
1402 NOTE: diff may generate unexpected results for merges, as it will
1402 NOTE: diff may generate unexpected results for merges, as it will
1403 default to comparing against the working directory's first parent
1403 default to comparing against the working directory's first parent
1404 changeset if no revisions are specified.
1404 changeset if no revisions are specified.
1405
1405
1406 When two revision arguments are given, then changes are shown
1406 When two revision arguments are given, then changes are shown
1407 between those revisions. If only one revision is specified then
1407 between those revisions. If only one revision is specified then
1408 that revision is compared to the working directory, and, when no
1408 that revision is compared to the working directory, and, when no
1409 revisions are specified, the working directory files are compared
1409 revisions are specified, the working directory files are compared
1410 to its parent.
1410 to its parent.
1411
1411
1412 Alternatively you can specify -c/--change with a revision to see
1412 Alternatively you can specify -c/--change with a revision to see
1413 the changes in that changeset relative to its first parent.
1413 the changes in that changeset relative to its first parent.
1414
1414
1415 Without the -a/--text option, diff will avoid generating diffs of
1415 Without the -a/--text option, diff will avoid generating diffs of
1416 files it detects as binary. With -a, diff will generate a diff
1416 files it detects as binary. With -a, diff will generate a diff
1417 anyway, probably with undesirable results.
1417 anyway, probably with undesirable results.
1418
1418
1419 Use the -g/--git option to generate diffs in the git extended diff
1419 Use the -g/--git option to generate diffs in the git extended diff
1420 format. For more information, read :hg:`help diffs`.
1420 format. For more information, read :hg:`help diffs`.
1421
1421
1422 Returns 0 on success.
1422 Returns 0 on success.
1423 """
1423 """
1424
1424
1425 revs = opts.get('rev')
1425 revs = opts.get('rev')
1426 change = opts.get('change')
1426 change = opts.get('change')
1427 stat = opts.get('stat')
1427 stat = opts.get('stat')
1428 reverse = opts.get('reverse')
1428 reverse = opts.get('reverse')
1429
1429
1430 if revs and change:
1430 if revs and change:
1431 msg = _('cannot specify --rev and --change at the same time')
1431 msg = _('cannot specify --rev and --change at the same time')
1432 raise util.Abort(msg)
1432 raise util.Abort(msg)
1433 elif change:
1433 elif change:
1434 node2 = repo.lookup(change)
1434 node2 = repo.lookup(change)
1435 node1 = repo[node2].parents()[0].node()
1435 node1 = repo[node2].parents()[0].node()
1436 else:
1436 else:
1437 node1, node2 = cmdutil.revpair(repo, revs)
1437 node1, node2 = cmdutil.revpair(repo, revs)
1438
1438
1439 if reverse:
1439 if reverse:
1440 node1, node2 = node2, node1
1440 node1, node2 = node2, node1
1441
1441
1442 diffopts = patch.diffopts(ui, opts)
1442 diffopts = patch.diffopts(ui, opts)
1443 m = cmdutil.match(repo, pats, opts)
1443 m = cmdutil.match(repo, pats, opts)
1444 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1444 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1445
1445
1446 def export(ui, repo, *changesets, **opts):
1446 def export(ui, repo, *changesets, **opts):
1447 """dump the header and diffs for one or more changesets
1447 """dump the header and diffs for one or more changesets
1448
1448
1449 Print the changeset header and diffs for one or more revisions.
1449 Print the changeset header and diffs for one or more revisions.
1450
1450
1451 The information shown in the changeset header is: author, date,
1451 The information shown in the changeset header is: author, date,
1452 branch name (if non-default), changeset hash, parent(s) and commit
1452 branch name (if non-default), changeset hash, parent(s) and commit
1453 comment.
1453 comment.
1454
1454
1455 NOTE: export may generate unexpected diff output for merge
1455 NOTE: export may generate unexpected diff output for merge
1456 changesets, as it will compare the merge changeset against its
1456 changesets, as it will compare the merge changeset against its
1457 first parent only.
1457 first parent only.
1458
1458
1459 Output may be to a file, in which case the name of the file is
1459 Output may be to a file, in which case the name of the file is
1460 given using a format string. The formatting rules are as follows:
1460 given using a format string. The formatting rules are as follows:
1461
1461
1462 :``%%``: literal "%" character
1462 :``%%``: literal "%" character
1463 :``%H``: changeset hash (40 hexadecimal digits)
1463 :``%H``: changeset hash (40 hexadecimal digits)
1464 :``%N``: number of patches being generated
1464 :``%N``: number of patches being generated
1465 :``%R``: changeset revision number
1465 :``%R``: changeset revision number
1466 :``%b``: basename of the exporting repository
1466 :``%b``: basename of the exporting repository
1467 :``%h``: short-form changeset hash (12 hexadecimal digits)
1467 :``%h``: short-form changeset hash (12 hexadecimal digits)
1468 :``%n``: zero-padded sequence number, starting at 1
1468 :``%n``: zero-padded sequence number, starting at 1
1469 :``%r``: zero-padded changeset revision number
1469 :``%r``: zero-padded changeset revision number
1470
1470
1471 Without the -a/--text option, export will avoid generating diffs
1471 Without the -a/--text option, export will avoid generating diffs
1472 of files it detects as binary. With -a, export will generate a
1472 of files it detects as binary. With -a, export will generate a
1473 diff anyway, probably with undesirable results.
1473 diff anyway, probably with undesirable results.
1474
1474
1475 Use the -g/--git option to generate diffs in the git extended diff
1475 Use the -g/--git option to generate diffs in the git extended diff
1476 format. See :hg:`help diffs` for more information.
1476 format. See :hg:`help diffs` for more information.
1477
1477
1478 With the --switch-parent option, the diff will be against the
1478 With the --switch-parent option, the diff will be against the
1479 second parent. It can be useful to review a merge.
1479 second parent. It can be useful to review a merge.
1480
1480
1481 Returns 0 on success.
1481 Returns 0 on success.
1482 """
1482 """
1483 changesets += tuple(opts.get('rev', []))
1483 changesets += tuple(opts.get('rev', []))
1484 if not changesets:
1484 if not changesets:
1485 raise util.Abort(_("export requires at least one changeset"))
1485 raise util.Abort(_("export requires at least one changeset"))
1486 revs = cmdutil.revrange(repo, changesets)
1486 revs = cmdutil.revrange(repo, changesets)
1487 if len(revs) > 1:
1487 if len(revs) > 1:
1488 ui.note(_('exporting patches:\n'))
1488 ui.note(_('exporting patches:\n'))
1489 else:
1489 else:
1490 ui.note(_('exporting patch:\n'))
1490 ui.note(_('exporting patch:\n'))
1491 cmdutil.export(repo, revs, template=opts.get('output'),
1491 cmdutil.export(repo, revs, template=opts.get('output'),
1492 switch_parent=opts.get('switch_parent'),
1492 switch_parent=opts.get('switch_parent'),
1493 opts=patch.diffopts(ui, opts))
1493 opts=patch.diffopts(ui, opts))
1494
1494
1495 def forget(ui, repo, *pats, **opts):
1495 def forget(ui, repo, *pats, **opts):
1496 """forget the specified files on the next commit
1496 """forget the specified files on the next commit
1497
1497
1498 Mark the specified files so they will no longer be tracked
1498 Mark the specified files so they will no longer be tracked
1499 after the next commit.
1499 after the next commit.
1500
1500
1501 This only removes files from the current branch, not from the
1501 This only removes files from the current branch, not from the
1502 entire project history, and it does not delete them from the
1502 entire project history, and it does not delete them from the
1503 working directory.
1503 working directory.
1504
1504
1505 To undo a forget before the next commit, see :hg:`add`.
1505 To undo a forget before the next commit, see :hg:`add`.
1506
1506
1507 Returns 0 on success.
1507 Returns 0 on success.
1508 """
1508 """
1509
1509
1510 if not pats:
1510 if not pats:
1511 raise util.Abort(_('no files specified'))
1511 raise util.Abort(_('no files specified'))
1512
1512
1513 m = cmdutil.match(repo, pats, opts)
1513 m = cmdutil.match(repo, pats, opts)
1514 s = repo.status(match=m, clean=True)
1514 s = repo.status(match=m, clean=True)
1515 forget = sorted(s[0] + s[1] + s[3] + s[6])
1515 forget = sorted(s[0] + s[1] + s[3] + s[6])
1516 errs = 0
1516 errs = 0
1517
1517
1518 for f in m.files():
1518 for f in m.files():
1519 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1519 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1520 ui.warn(_('not removing %s: file is already untracked\n')
1520 ui.warn(_('not removing %s: file is already untracked\n')
1521 % m.rel(f))
1521 % m.rel(f))
1522 errs = 1
1522 errs = 1
1523
1523
1524 for f in forget:
1524 for f in forget:
1525 if ui.verbose or not m.exact(f):
1525 if ui.verbose or not m.exact(f):
1526 ui.status(_('removing %s\n') % m.rel(f))
1526 ui.status(_('removing %s\n') % m.rel(f))
1527
1527
1528 repo[None].remove(forget, unlink=False)
1528 repo[None].remove(forget, unlink=False)
1529 return errs
1529 return errs
1530
1530
1531 def grep(ui, repo, pattern, *pats, **opts):
1531 def grep(ui, repo, pattern, *pats, **opts):
1532 """search for a pattern in specified files and revisions
1532 """search for a pattern in specified files and revisions
1533
1533
1534 Search revisions of files for a regular expression.
1534 Search revisions of files for a regular expression.
1535
1535
1536 This command behaves differently than Unix grep. It only accepts
1536 This command behaves differently than Unix grep. It only accepts
1537 Python/Perl regexps. It searches repository history, not the
1537 Python/Perl regexps. It searches repository history, not the
1538 working directory. It always prints the revision number in which a
1538 working directory. It always prints the revision number in which a
1539 match appears.
1539 match appears.
1540
1540
1541 By default, grep only prints output for the first revision of a
1541 By default, grep only prints output for the first revision of a
1542 file in which it finds a match. To get it to print every revision
1542 file in which it finds a match. To get it to print every revision
1543 that contains a change in match status ("-" for a match that
1543 that contains a change in match status ("-" for a match that
1544 becomes a non-match, or "+" for a non-match that becomes a match),
1544 becomes a non-match, or "+" for a non-match that becomes a match),
1545 use the --all flag.
1545 use the --all flag.
1546
1546
1547 Returns 0 if a match is found, 1 otherwise.
1547 Returns 0 if a match is found, 1 otherwise.
1548 """
1548 """
1549 reflags = 0
1549 reflags = 0
1550 if opts.get('ignore_case'):
1550 if opts.get('ignore_case'):
1551 reflags |= re.I
1551 reflags |= re.I
1552 try:
1552 try:
1553 regexp = re.compile(pattern, reflags)
1553 regexp = re.compile(pattern, reflags)
1554 except Exception, inst:
1554 except Exception, inst:
1555 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1555 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1556 return 1
1556 return 1
1557 sep, eol = ':', '\n'
1557 sep, eol = ':', '\n'
1558 if opts.get('print0'):
1558 if opts.get('print0'):
1559 sep = eol = '\0'
1559 sep = eol = '\0'
1560
1560
1561 getfile = util.lrucachefunc(repo.file)
1561 getfile = util.lrucachefunc(repo.file)
1562
1562
1563 def matchlines(body):
1563 def matchlines(body):
1564 begin = 0
1564 begin = 0
1565 linenum = 0
1565 linenum = 0
1566 while True:
1566 while True:
1567 match = regexp.search(body, begin)
1567 match = regexp.search(body, begin)
1568 if not match:
1568 if not match:
1569 break
1569 break
1570 mstart, mend = match.span()
1570 mstart, mend = match.span()
1571 linenum += body.count('\n', begin, mstart) + 1
1571 linenum += body.count('\n', begin, mstart) + 1
1572 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1572 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1573 begin = body.find('\n', mend) + 1 or len(body)
1573 begin = body.find('\n', mend) + 1 or len(body)
1574 lend = begin - 1
1574 lend = begin - 1
1575 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1575 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1576
1576
1577 class linestate(object):
1577 class linestate(object):
1578 def __init__(self, line, linenum, colstart, colend):
1578 def __init__(self, line, linenum, colstart, colend):
1579 self.line = line
1579 self.line = line
1580 self.linenum = linenum
1580 self.linenum = linenum
1581 self.colstart = colstart
1581 self.colstart = colstart
1582 self.colend = colend
1582 self.colend = colend
1583
1583
1584 def __hash__(self):
1584 def __hash__(self):
1585 return hash((self.linenum, self.line))
1585 return hash((self.linenum, self.line))
1586
1586
1587 def __eq__(self, other):
1587 def __eq__(self, other):
1588 return self.line == other.line
1588 return self.line == other.line
1589
1589
1590 matches = {}
1590 matches = {}
1591 copies = {}
1591 copies = {}
1592 def grepbody(fn, rev, body):
1592 def grepbody(fn, rev, body):
1593 matches[rev].setdefault(fn, [])
1593 matches[rev].setdefault(fn, [])
1594 m = matches[rev][fn]
1594 m = matches[rev][fn]
1595 for lnum, cstart, cend, line in matchlines(body):
1595 for lnum, cstart, cend, line in matchlines(body):
1596 s = linestate(line, lnum, cstart, cend)
1596 s = linestate(line, lnum, cstart, cend)
1597 m.append(s)
1597 m.append(s)
1598
1598
1599 def difflinestates(a, b):
1599 def difflinestates(a, b):
1600 sm = difflib.SequenceMatcher(None, a, b)
1600 sm = difflib.SequenceMatcher(None, a, b)
1601 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1601 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1602 if tag == 'insert':
1602 if tag == 'insert':
1603 for i in xrange(blo, bhi):
1603 for i in xrange(blo, bhi):
1604 yield ('+', b[i])
1604 yield ('+', b[i])
1605 elif tag == 'delete':
1605 elif tag == 'delete':
1606 for i in xrange(alo, ahi):
1606 for i in xrange(alo, ahi):
1607 yield ('-', a[i])
1607 yield ('-', a[i])
1608 elif tag == 'replace':
1608 elif tag == 'replace':
1609 for i in xrange(alo, ahi):
1609 for i in xrange(alo, ahi):
1610 yield ('-', a[i])
1610 yield ('-', a[i])
1611 for i in xrange(blo, bhi):
1611 for i in xrange(blo, bhi):
1612 yield ('+', b[i])
1612 yield ('+', b[i])
1613
1613
1614 def display(fn, ctx, pstates, states):
1614 def display(fn, ctx, pstates, states):
1615 rev = ctx.rev()
1615 rev = ctx.rev()
1616 datefunc = ui.quiet and util.shortdate or util.datestr
1616 datefunc = ui.quiet and util.shortdate or util.datestr
1617 found = False
1617 found = False
1618 filerevmatches = {}
1618 filerevmatches = {}
1619 if opts.get('all'):
1619 if opts.get('all'):
1620 iter = difflinestates(pstates, states)
1620 iter = difflinestates(pstates, states)
1621 else:
1621 else:
1622 iter = [('', l) for l in states]
1622 iter = [('', l) for l in states]
1623 for change, l in iter:
1623 for change, l in iter:
1624 cols = [fn, str(rev)]
1624 cols = [fn, str(rev)]
1625 before, match, after = None, None, None
1625 before, match, after = None, None, None
1626 if opts.get('line_number'):
1626 if opts.get('line_number'):
1627 cols.append(str(l.linenum))
1627 cols.append(str(l.linenum))
1628 if opts.get('all'):
1628 if opts.get('all'):
1629 cols.append(change)
1629 cols.append(change)
1630 if opts.get('user'):
1630 if opts.get('user'):
1631 cols.append(ui.shortuser(ctx.user()))
1631 cols.append(ui.shortuser(ctx.user()))
1632 if opts.get('date'):
1632 if opts.get('date'):
1633 cols.append(datefunc(ctx.date()))
1633 cols.append(datefunc(ctx.date()))
1634 if opts.get('files_with_matches'):
1634 if opts.get('files_with_matches'):
1635 c = (fn, rev)
1635 c = (fn, rev)
1636 if c in filerevmatches:
1636 if c in filerevmatches:
1637 continue
1637 continue
1638 filerevmatches[c] = 1
1638 filerevmatches[c] = 1
1639 else:
1639 else:
1640 before = l.line[:l.colstart]
1640 before = l.line[:l.colstart]
1641 match = l.line[l.colstart:l.colend]
1641 match = l.line[l.colstart:l.colend]
1642 after = l.line[l.colend:]
1642 after = l.line[l.colend:]
1643 ui.write(sep.join(cols))
1643 ui.write(sep.join(cols))
1644 if before is not None:
1644 if before is not None:
1645 ui.write(sep + before)
1645 ui.write(sep + before)
1646 ui.write(match, label='grep.match')
1646 ui.write(match, label='grep.match')
1647 ui.write(after)
1647 ui.write(after)
1648 ui.write(eol)
1648 ui.write(eol)
1649 found = True
1649 found = True
1650 return found
1650 return found
1651
1651
1652 skip = {}
1652 skip = {}
1653 revfiles = {}
1653 revfiles = {}
1654 matchfn = cmdutil.match(repo, pats, opts)
1654 matchfn = cmdutil.match(repo, pats, opts)
1655 found = False
1655 found = False
1656 follow = opts.get('follow')
1656 follow = opts.get('follow')
1657
1657
1658 def prep(ctx, fns):
1658 def prep(ctx, fns):
1659 rev = ctx.rev()
1659 rev = ctx.rev()
1660 pctx = ctx.parents()[0]
1660 pctx = ctx.parents()[0]
1661 parent = pctx.rev()
1661 parent = pctx.rev()
1662 matches.setdefault(rev, {})
1662 matches.setdefault(rev, {})
1663 matches.setdefault(parent, {})
1663 matches.setdefault(parent, {})
1664 files = revfiles.setdefault(rev, [])
1664 files = revfiles.setdefault(rev, [])
1665 for fn in fns:
1665 for fn in fns:
1666 flog = getfile(fn)
1666 flog = getfile(fn)
1667 try:
1667 try:
1668 fnode = ctx.filenode(fn)
1668 fnode = ctx.filenode(fn)
1669 except error.LookupError:
1669 except error.LookupError:
1670 continue
1670 continue
1671
1671
1672 copied = flog.renamed(fnode)
1672 copied = flog.renamed(fnode)
1673 copy = follow and copied and copied[0]
1673 copy = follow and copied and copied[0]
1674 if copy:
1674 if copy:
1675 copies.setdefault(rev, {})[fn] = copy
1675 copies.setdefault(rev, {})[fn] = copy
1676 if fn in skip:
1676 if fn in skip:
1677 if copy:
1677 if copy:
1678 skip[copy] = True
1678 skip[copy] = True
1679 continue
1679 continue
1680 files.append(fn)
1680 files.append(fn)
1681
1681
1682 if fn not in matches[rev]:
1682 if fn not in matches[rev]:
1683 grepbody(fn, rev, flog.read(fnode))
1683 grepbody(fn, rev, flog.read(fnode))
1684
1684
1685 pfn = copy or fn
1685 pfn = copy or fn
1686 if pfn not in matches[parent]:
1686 if pfn not in matches[parent]:
1687 try:
1687 try:
1688 fnode = pctx.filenode(pfn)
1688 fnode = pctx.filenode(pfn)
1689 grepbody(pfn, parent, flog.read(fnode))
1689 grepbody(pfn, parent, flog.read(fnode))
1690 except error.LookupError:
1690 except error.LookupError:
1691 pass
1691 pass
1692
1692
1693 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1693 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1694 rev = ctx.rev()
1694 rev = ctx.rev()
1695 parent = ctx.parents()[0].rev()
1695 parent = ctx.parents()[0].rev()
1696 for fn in sorted(revfiles.get(rev, [])):
1696 for fn in sorted(revfiles.get(rev, [])):
1697 states = matches[rev][fn]
1697 states = matches[rev][fn]
1698 copy = copies.get(rev, {}).get(fn)
1698 copy = copies.get(rev, {}).get(fn)
1699 if fn in skip:
1699 if fn in skip:
1700 if copy:
1700 if copy:
1701 skip[copy] = True
1701 skip[copy] = True
1702 continue
1702 continue
1703 pstates = matches.get(parent, {}).get(copy or fn, [])
1703 pstates = matches.get(parent, {}).get(copy or fn, [])
1704 if pstates or states:
1704 if pstates or states:
1705 r = display(fn, ctx, pstates, states)
1705 r = display(fn, ctx, pstates, states)
1706 found = found or r
1706 found = found or r
1707 if r and not opts.get('all'):
1707 if r and not opts.get('all'):
1708 skip[fn] = True
1708 skip[fn] = True
1709 if copy:
1709 if copy:
1710 skip[copy] = True
1710 skip[copy] = True
1711 del matches[rev]
1711 del matches[rev]
1712 del revfiles[rev]
1712 del revfiles[rev]
1713
1713
1714 return not found
1714 return not found
1715
1715
1716 def heads(ui, repo, *branchrevs, **opts):
1716 def heads(ui, repo, *branchrevs, **opts):
1717 """show current repository heads or show branch heads
1717 """show current repository heads or show branch heads
1718
1718
1719 With no arguments, show all repository branch heads.
1719 With no arguments, show all repository branch heads.
1720
1720
1721 Repository "heads" are changesets with no child changesets. They are
1721 Repository "heads" are changesets with no child changesets. They are
1722 where development generally takes place and are the usual targets
1722 where development generally takes place and are the usual targets
1723 for update and merge operations. Branch heads are changesets that have
1723 for update and merge operations. Branch heads are changesets that have
1724 no child changeset on the same branch.
1724 no child changeset on the same branch.
1725
1725
1726 If one or more REVs are given, only branch heads on the branches
1726 If one or more REVs are given, only branch heads on the branches
1727 associated with the specified changesets are shown.
1727 associated with the specified changesets are shown.
1728
1728
1729 If -c/--closed is specified, also show branch heads marked closed
1729 If -c/--closed is specified, also show branch heads marked closed
1730 (see :hg:`commit --close-branch`).
1730 (see :hg:`commit --close-branch`).
1731
1731
1732 If STARTREV is specified, only those heads that are descendants of
1732 If STARTREV is specified, only those heads that are descendants of
1733 STARTREV will be displayed.
1733 STARTREV will be displayed.
1734
1734
1735 If -t/--topo is specified, named branch mechanics will be ignored and only
1735 If -t/--topo is specified, named branch mechanics will be ignored and only
1736 changesets without children will be shown.
1736 changesets without children will be shown.
1737
1737
1738 Returns 0 if matching heads are found, 1 if not.
1738 Returns 0 if matching heads are found, 1 if not.
1739 """
1739 """
1740
1740
1741 if opts.get('rev'):
1741 if opts.get('rev'):
1742 start = repo.lookup(opts['rev'])
1742 start = repo.lookup(opts['rev'])
1743 else:
1743 else:
1744 start = None
1744 start = None
1745
1745
1746 if opts.get('topo'):
1746 if opts.get('topo'):
1747 heads = [repo[h] for h in repo.heads(start)]
1747 heads = [repo[h] for h in repo.heads(start)]
1748 else:
1748 else:
1749 heads = []
1749 heads = []
1750 for b, ls in repo.branchmap().iteritems():
1750 for b, ls in repo.branchmap().iteritems():
1751 if start is None:
1751 if start is None:
1752 heads += [repo[h] for h in ls]
1752 heads += [repo[h] for h in ls]
1753 continue
1753 continue
1754 startrev = repo.changelog.rev(start)
1754 startrev = repo.changelog.rev(start)
1755 descendants = set(repo.changelog.descendants(startrev))
1755 descendants = set(repo.changelog.descendants(startrev))
1756 descendants.add(startrev)
1756 descendants.add(startrev)
1757 rev = repo.changelog.rev
1757 rev = repo.changelog.rev
1758 heads += [repo[h] for h in ls if rev(h) in descendants]
1758 heads += [repo[h] for h in ls if rev(h) in descendants]
1759
1759
1760 if branchrevs:
1760 if branchrevs:
1761 decode, encode = encoding.fromlocal, encoding.tolocal
1761 decode, encode = encoding.fromlocal, encoding.tolocal
1762 branches = set(repo[decode(br)].branch() for br in branchrevs)
1762 branches = set(repo[decode(br)].branch() for br in branchrevs)
1763 heads = [h for h in heads if h.branch() in branches]
1763 heads = [h for h in heads if h.branch() in branches]
1764
1764
1765 if not opts.get('closed'):
1765 if not opts.get('closed'):
1766 heads = [h for h in heads if not h.extra().get('close')]
1766 heads = [h for h in heads if not h.extra().get('close')]
1767
1767
1768 if opts.get('active') and branchrevs:
1768 if opts.get('active') and branchrevs:
1769 dagheads = repo.heads(start)
1769 dagheads = repo.heads(start)
1770 heads = [h for h in heads if h.node() in dagheads]
1770 heads = [h for h in heads if h.node() in dagheads]
1771
1771
1772 if branchrevs:
1772 if branchrevs:
1773 haveheads = set(h.branch() for h in heads)
1773 haveheads = set(h.branch() for h in heads)
1774 if branches - haveheads:
1774 if branches - haveheads:
1775 headless = ', '.join(encode(b) for b in branches - haveheads)
1775 headless = ', '.join(encode(b) for b in branches - haveheads)
1776 msg = _('no open branch heads found on branches %s')
1776 msg = _('no open branch heads found on branches %s')
1777 if opts.get('rev'):
1777 if opts.get('rev'):
1778 msg += _(' (started at %s)' % opts['rev'])
1778 msg += _(' (started at %s)' % opts['rev'])
1779 ui.warn((msg + '\n') % headless)
1779 ui.warn((msg + '\n') % headless)
1780
1780
1781 if not heads:
1781 if not heads:
1782 return 1
1782 return 1
1783
1783
1784 heads = sorted(heads, key=lambda x: -x.rev())
1784 heads = sorted(heads, key=lambda x: -x.rev())
1785 displayer = cmdutil.show_changeset(ui, repo, opts)
1785 displayer = cmdutil.show_changeset(ui, repo, opts)
1786 for ctx in heads:
1786 for ctx in heads:
1787 displayer.show(ctx)
1787 displayer.show(ctx)
1788 displayer.close()
1788 displayer.close()
1789
1789
1790 def help_(ui, name=None, with_version=False, unknowncmd=False):
1790 def help_(ui, name=None, with_version=False, unknowncmd=False):
1791 """show help for a given topic or a help overview
1791 """show help for a given topic or a help overview
1792
1792
1793 With no arguments, print a list of commands with short help messages.
1793 With no arguments, print a list of commands with short help messages.
1794
1794
1795 Given a topic, extension, or command name, print help for that
1795 Given a topic, extension, or command name, print help for that
1796 topic.
1796 topic.
1797
1797
1798 Returns 0 if successful.
1798 Returns 0 if successful.
1799 """
1799 """
1800 option_lists = []
1800 option_lists = []
1801 textwidth = util.termwidth() - 2
1801 textwidth = util.termwidth() - 2
1802
1802
1803 def addglobalopts(aliases):
1803 def addglobalopts(aliases):
1804 if ui.verbose:
1804 if ui.verbose:
1805 option_lists.append((_("global options:"), globalopts))
1805 option_lists.append((_("global options:"), globalopts))
1806 if name == 'shortlist':
1806 if name == 'shortlist':
1807 option_lists.append((_('use "hg help" for the full list '
1807 option_lists.append((_('use "hg help" for the full list '
1808 'of commands'), ()))
1808 'of commands'), ()))
1809 else:
1809 else:
1810 if name == 'shortlist':
1810 if name == 'shortlist':
1811 msg = _('use "hg help" for the full list of commands '
1811 msg = _('use "hg help" for the full list of commands '
1812 'or "hg -v" for details')
1812 'or "hg -v" for details')
1813 elif aliases:
1813 elif aliases:
1814 msg = _('use "hg -v help%s" to show aliases and '
1814 msg = _('use "hg -v help%s" to show aliases and '
1815 'global options') % (name and " " + name or "")
1815 'global options') % (name and " " + name or "")
1816 else:
1816 else:
1817 msg = _('use "hg -v help %s" to show global options') % name
1817 msg = _('use "hg -v help %s" to show global options') % name
1818 option_lists.append((msg, ()))
1818 option_lists.append((msg, ()))
1819
1819
1820 def helpcmd(name):
1820 def helpcmd(name):
1821 if with_version:
1821 if with_version:
1822 version_(ui)
1822 version_(ui)
1823 ui.write('\n')
1823 ui.write('\n')
1824
1824
1825 try:
1825 try:
1826 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1826 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1827 except error.AmbiguousCommand, inst:
1827 except error.AmbiguousCommand, inst:
1828 # py3k fix: except vars can't be used outside the scope of the
1828 # py3k fix: except vars can't be used outside the scope of the
1829 # except block, nor can be used inside a lambda. python issue4617
1829 # except block, nor can be used inside a lambda. python issue4617
1830 prefix = inst.args[0]
1830 prefix = inst.args[0]
1831 select = lambda c: c.lstrip('^').startswith(prefix)
1831 select = lambda c: c.lstrip('^').startswith(prefix)
1832 helplist(_('list of commands:\n\n'), select)
1832 helplist(_('list of commands:\n\n'), select)
1833 return
1833 return
1834
1834
1835 # check if it's an invalid alias and display its error if it is
1835 # check if it's an invalid alias and display its error if it is
1836 if getattr(entry[0], 'badalias', False):
1836 if getattr(entry[0], 'badalias', False):
1837 if not unknowncmd:
1837 if not unknowncmd:
1838 entry[0](ui)
1838 entry[0](ui)
1839 return
1839 return
1840
1840
1841 # synopsis
1841 # synopsis
1842 if len(entry) > 2:
1842 if len(entry) > 2:
1843 if entry[2].startswith('hg'):
1843 if entry[2].startswith('hg'):
1844 ui.write("%s\n" % entry[2])
1844 ui.write("%s\n" % entry[2])
1845 else:
1845 else:
1846 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1846 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1847 else:
1847 else:
1848 ui.write('hg %s\n' % aliases[0])
1848 ui.write('hg %s\n' % aliases[0])
1849
1849
1850 # aliases
1850 # aliases
1851 if not ui.quiet and len(aliases) > 1:
1851 if not ui.quiet and len(aliases) > 1:
1852 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1852 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1853
1853
1854 # description
1854 # description
1855 doc = gettext(entry[0].__doc__)
1855 doc = gettext(entry[0].__doc__)
1856 if not doc:
1856 if not doc:
1857 doc = _("(no help text available)")
1857 doc = _("(no help text available)")
1858 if hasattr(entry[0], 'definition'): # aliased command
1858 if hasattr(entry[0], 'definition'): # aliased command
1859 if entry[0].definition.startswith('!'): # shell alias
1859 if entry[0].definition.startswith('!'): # shell alias
1860 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1860 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1861 else:
1861 else:
1862 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1862 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1863 if ui.quiet:
1863 if ui.quiet:
1864 doc = doc.splitlines()[0]
1864 doc = doc.splitlines()[0]
1865 keep = ui.verbose and ['verbose'] or []
1865 keep = ui.verbose and ['verbose'] or []
1866 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1866 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1867 ui.write("\n%s\n" % formatted)
1867 ui.write("\n%s\n" % formatted)
1868 if pruned:
1868 if pruned:
1869 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1869 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1870
1870
1871 if not ui.quiet:
1871 if not ui.quiet:
1872 # options
1872 # options
1873 if entry[1]:
1873 if entry[1]:
1874 option_lists.append((_("options:\n"), entry[1]))
1874 option_lists.append((_("options:\n"), entry[1]))
1875
1875
1876 addglobalopts(False)
1876 addglobalopts(False)
1877
1877
1878 def helplist(header, select=None):
1878 def helplist(header, select=None):
1879 h = {}
1879 h = {}
1880 cmds = {}
1880 cmds = {}
1881 for c, e in table.iteritems():
1881 for c, e in table.iteritems():
1882 f = c.split("|", 1)[0]
1882 f = c.split("|", 1)[0]
1883 if select and not select(f):
1883 if select and not select(f):
1884 continue
1884 continue
1885 if (not select and name != 'shortlist' and
1885 if (not select and name != 'shortlist' and
1886 e[0].__module__ != __name__):
1886 e[0].__module__ != __name__):
1887 continue
1887 continue
1888 if name == "shortlist" and not f.startswith("^"):
1888 if name == "shortlist" and not f.startswith("^"):
1889 continue
1889 continue
1890 f = f.lstrip("^")
1890 f = f.lstrip("^")
1891 if not ui.debugflag and f.startswith("debug"):
1891 if not ui.debugflag and f.startswith("debug"):
1892 continue
1892 continue
1893 doc = e[0].__doc__
1893 doc = e[0].__doc__
1894 if doc and 'DEPRECATED' in doc and not ui.verbose:
1894 if doc and 'DEPRECATED' in doc and not ui.verbose:
1895 continue
1895 continue
1896 doc = gettext(doc)
1896 doc = gettext(doc)
1897 if not doc:
1897 if not doc:
1898 doc = _("(no help text available)")
1898 doc = _("(no help text available)")
1899 h[f] = doc.splitlines()[0].rstrip()
1899 h[f] = doc.splitlines()[0].rstrip()
1900 cmds[f] = c.lstrip("^")
1900 cmds[f] = c.lstrip("^")
1901
1901
1902 if not h:
1902 if not h:
1903 ui.status(_('no commands defined\n'))
1903 ui.status(_('no commands defined\n'))
1904 return
1904 return
1905
1905
1906 ui.status(header)
1906 ui.status(header)
1907 fns = sorted(h)
1907 fns = sorted(h)
1908 m = max(map(len, fns))
1908 m = max(map(len, fns))
1909 for f in fns:
1909 for f in fns:
1910 if ui.verbose:
1910 if ui.verbose:
1911 commands = cmds[f].replace("|",", ")
1911 commands = cmds[f].replace("|",", ")
1912 ui.write(" %s:\n %s\n"%(commands, h[f]))
1912 ui.write(" %s:\n %s\n"%(commands, h[f]))
1913 else:
1913 else:
1914 ui.write('%s\n' % (util.wrap(h[f],
1914 ui.write('%s\n' % (util.wrap(h[f],
1915 initindent=' %-*s ' % (m, f),
1915 initindent=' %-*s ' % (m, f),
1916 hangindent=' ' * (m + 4))))
1916 hangindent=' ' * (m + 4))))
1917
1917
1918 if not ui.quiet:
1918 if not ui.quiet:
1919 addglobalopts(True)
1919 addglobalopts(True)
1920
1920
1921 def helptopic(name):
1921 def helptopic(name):
1922 for names, header, doc in help.helptable:
1922 for names, header, doc in help.helptable:
1923 if name in names:
1923 if name in names:
1924 break
1924 break
1925 else:
1925 else:
1926 raise error.UnknownCommand(name)
1926 raise error.UnknownCommand(name)
1927
1927
1928 # description
1928 # description
1929 if not doc:
1929 if not doc:
1930 doc = _("(no help text available)")
1930 doc = _("(no help text available)")
1931 if hasattr(doc, '__call__'):
1931 if hasattr(doc, '__call__'):
1932 doc = doc()
1932 doc = doc()
1933
1933
1934 ui.write("%s\n\n" % header)
1934 ui.write("%s\n\n" % header)
1935 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1935 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1936
1936
1937 def helpext(name):
1937 def helpext(name):
1938 try:
1938 try:
1939 mod = extensions.find(name)
1939 mod = extensions.find(name)
1940 doc = gettext(mod.__doc__) or _('no help text available')
1940 doc = gettext(mod.__doc__) or _('no help text available')
1941 except KeyError:
1941 except KeyError:
1942 mod = None
1942 mod = None
1943 doc = extensions.disabledext(name)
1943 doc = extensions.disabledext(name)
1944 if not doc:
1944 if not doc:
1945 raise error.UnknownCommand(name)
1945 raise error.UnknownCommand(name)
1946
1946
1947 if '\n' not in doc:
1947 if '\n' not in doc:
1948 head, tail = doc, ""
1948 head, tail = doc, ""
1949 else:
1949 else:
1950 head, tail = doc.split('\n', 1)
1950 head, tail = doc.split('\n', 1)
1951 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1951 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1952 if tail:
1952 if tail:
1953 ui.write(minirst.format(tail, textwidth))
1953 ui.write(minirst.format(tail, textwidth))
1954 ui.status('\n\n')
1954 ui.status('\n\n')
1955
1955
1956 if mod:
1956 if mod:
1957 try:
1957 try:
1958 ct = mod.cmdtable
1958 ct = mod.cmdtable
1959 except AttributeError:
1959 except AttributeError:
1960 ct = {}
1960 ct = {}
1961 modcmds = set([c.split('|', 1)[0] for c in ct])
1961 modcmds = set([c.split('|', 1)[0] for c in ct])
1962 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1962 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1963 else:
1963 else:
1964 ui.write(_('use "hg help extensions" for information on enabling '
1964 ui.write(_('use "hg help extensions" for information on enabling '
1965 'extensions\n'))
1965 'extensions\n'))
1966
1966
1967 def helpextcmd(name):
1967 def helpextcmd(name):
1968 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1968 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1969 doc = gettext(mod.__doc__).splitlines()[0]
1969 doc = gettext(mod.__doc__).splitlines()[0]
1970
1970
1971 msg = help.listexts(_("'%s' is provided by the following "
1971 msg = help.listexts(_("'%s' is provided by the following "
1972 "extension:") % cmd, {ext: doc}, len(ext),
1972 "extension:") % cmd, {ext: doc}, len(ext),
1973 indent=4)
1973 indent=4)
1974 ui.write(minirst.format(msg, textwidth))
1974 ui.write(minirst.format(msg, textwidth))
1975 ui.write('\n\n')
1975 ui.write('\n\n')
1976 ui.write(_('use "hg help extensions" for information on enabling '
1976 ui.write(_('use "hg help extensions" for information on enabling '
1977 'extensions\n'))
1977 'extensions\n'))
1978
1978
1979 if name and name != 'shortlist':
1979 if name and name != 'shortlist':
1980 i = None
1980 i = None
1981 if unknowncmd:
1981 if unknowncmd:
1982 queries = (helpextcmd,)
1982 queries = (helpextcmd,)
1983 else:
1983 else:
1984 queries = (helptopic, helpcmd, helpext, helpextcmd)
1984 queries = (helptopic, helpcmd, helpext, helpextcmd)
1985 for f in queries:
1985 for f in queries:
1986 try:
1986 try:
1987 f(name)
1987 f(name)
1988 i = None
1988 i = None
1989 break
1989 break
1990 except error.UnknownCommand, inst:
1990 except error.UnknownCommand, inst:
1991 i = inst
1991 i = inst
1992 if i:
1992 if i:
1993 raise i
1993 raise i
1994
1994
1995 else:
1995 else:
1996 # program name
1996 # program name
1997 if ui.verbose or with_version:
1997 if ui.verbose or with_version:
1998 version_(ui)
1998 version_(ui)
1999 else:
1999 else:
2000 ui.status(_("Mercurial Distributed SCM\n"))
2000 ui.status(_("Mercurial Distributed SCM\n"))
2001 ui.status('\n')
2001 ui.status('\n')
2002
2002
2003 # list of commands
2003 # list of commands
2004 if name == "shortlist":
2004 if name == "shortlist":
2005 header = _('basic commands:\n\n')
2005 header = _('basic commands:\n\n')
2006 else:
2006 else:
2007 header = _('list of commands:\n\n')
2007 header = _('list of commands:\n\n')
2008
2008
2009 helplist(header)
2009 helplist(header)
2010 if name != 'shortlist':
2010 if name != 'shortlist':
2011 exts, maxlength = extensions.enabled()
2011 exts, maxlength = extensions.enabled()
2012 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2012 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2013 if text:
2013 if text:
2014 ui.write("\n%s\n" % minirst.format(text, textwidth))
2014 ui.write("\n%s\n" % minirst.format(text, textwidth))
2015
2015
2016 # list all option lists
2016 # list all option lists
2017 opt_output = []
2017 opt_output = []
2018 multioccur = False
2018 multioccur = False
2019 for title, options in option_lists:
2019 for title, options in option_lists:
2020 opt_output.append(("\n%s" % title, None))
2020 opt_output.append(("\n%s" % title, None))
2021 for option in options:
2021 for option in options:
2022 if len(option) == 5:
2022 if len(option) == 5:
2023 shortopt, longopt, default, desc, optlabel = option
2023 shortopt, longopt, default, desc, optlabel = option
2024 else:
2024 else:
2025 shortopt, longopt, default, desc = option
2025 shortopt, longopt, default, desc = option
2026 optlabel = _("VALUE") # default label
2026 optlabel = _("VALUE") # default label
2027
2027
2028 if _("DEPRECATED") in desc and not ui.verbose:
2028 if _("DEPRECATED") in desc and not ui.verbose:
2029 continue
2029 continue
2030 if isinstance(default, list):
2030 if isinstance(default, list):
2031 numqualifier = " %s [+]" % optlabel
2031 numqualifier = " %s [+]" % optlabel
2032 multioccur = True
2032 multioccur = True
2033 elif (default is not None) and not isinstance(default, bool):
2033 elif (default is not None) and not isinstance(default, bool):
2034 numqualifier = " %s" % optlabel
2034 numqualifier = " %s" % optlabel
2035 else:
2035 else:
2036 numqualifier = ""
2036 numqualifier = ""
2037 opt_output.append(("%2s%s" %
2037 opt_output.append(("%2s%s" %
2038 (shortopt and "-%s" % shortopt,
2038 (shortopt and "-%s" % shortopt,
2039 longopt and " --%s%s" %
2039 longopt and " --%s%s" %
2040 (longopt, numqualifier)),
2040 (longopt, numqualifier)),
2041 "%s%s" % (desc,
2041 "%s%s" % (desc,
2042 default
2042 default
2043 and _(" (default: %s)") % default
2043 and _(" (default: %s)") % default
2044 or "")))
2044 or "")))
2045 if multioccur:
2045 if multioccur:
2046 msg = _("\n[+] marked option can be specified multiple times")
2046 msg = _("\n[+] marked option can be specified multiple times")
2047 if ui.verbose and name != 'shortlist':
2047 if ui.verbose and name != 'shortlist':
2048 opt_output.append((msg, None))
2048 opt_output.append((msg, None))
2049 else:
2049 else:
2050 opt_output.insert(-1, (msg, None))
2050 opt_output.insert(-1, (msg, None))
2051
2051
2052 if not name:
2052 if not name:
2053 ui.write(_("\nadditional help topics:\n\n"))
2053 ui.write(_("\nadditional help topics:\n\n"))
2054 topics = []
2054 topics = []
2055 for names, header, doc in help.helptable:
2055 for names, header, doc in help.helptable:
2056 topics.append((sorted(names, key=len, reverse=True)[0], header))
2056 topics.append((sorted(names, key=len, reverse=True)[0], header))
2057 topics_len = max([len(s[0]) for s in topics])
2057 topics_len = max([len(s[0]) for s in topics])
2058 for t, desc in topics:
2058 for t, desc in topics:
2059 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2059 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2060
2060
2061 if opt_output:
2061 if opt_output:
2062 colwidth = encoding.colwidth
2062 colwidth = encoding.colwidth
2063 # normalize: (opt or message, desc or None, width of opt)
2063 # normalize: (opt or message, desc or None, width of opt)
2064 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2064 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2065 for opt, desc in opt_output]
2065 for opt, desc in opt_output]
2066 hanging = max([e[2] for e in entries])
2066 hanging = max([e[2] for e in entries])
2067 for opt, desc, width in entries:
2067 for opt, desc, width in entries:
2068 if desc:
2068 if desc:
2069 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2069 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2070 hangindent = ' ' * (hanging + 3)
2070 hangindent = ' ' * (hanging + 3)
2071 ui.write('%s\n' % (util.wrap(desc,
2071 ui.write('%s\n' % (util.wrap(desc,
2072 initindent=initindent,
2072 initindent=initindent,
2073 hangindent=hangindent)))
2073 hangindent=hangindent)))
2074 else:
2074 else:
2075 ui.write("%s\n" % opt)
2075 ui.write("%s\n" % opt)
2076
2076
2077 def identify(ui, repo, source=None,
2077 def identify(ui, repo, source=None,
2078 rev=None, num=None, id=None, branch=None, tags=None):
2078 rev=None, num=None, id=None, branch=None, tags=None):
2079 """identify the working copy or specified revision
2079 """identify the working copy or specified revision
2080
2080
2081 With no revision, print a summary of the current state of the
2081 With no revision, print a summary of the current state of the
2082 repository.
2082 repository.
2083
2083
2084 Specifying a path to a repository root or Mercurial bundle will
2084 Specifying a path to a repository root or Mercurial bundle will
2085 cause lookup to operate on that repository/bundle.
2085 cause lookup to operate on that repository/bundle.
2086
2086
2087 This summary identifies the repository state using one or two
2087 This summary identifies the repository state using one or two
2088 parent hash identifiers, followed by a "+" if there are
2088 parent hash identifiers, followed by a "+" if there are
2089 uncommitted changes in the working directory, a list of tags for
2089 uncommitted changes in the working directory, a list of tags for
2090 this revision and a branch name for non-default branches.
2090 this revision and a branch name for non-default branches.
2091
2091
2092 Returns 0 if successful.
2092 Returns 0 if successful.
2093 """
2093 """
2094
2094
2095 if not repo and not source:
2095 if not repo and not source:
2096 raise util.Abort(_("There is no Mercurial repository here "
2096 raise util.Abort(_("There is no Mercurial repository here "
2097 "(.hg not found)"))
2097 "(.hg not found)"))
2098
2098
2099 hexfunc = ui.debugflag and hex or short
2099 hexfunc = ui.debugflag and hex or short
2100 default = not (num or id or branch or tags)
2100 default = not (num or id or branch or tags)
2101 output = []
2101 output = []
2102
2102
2103 revs = []
2103 revs = []
2104 if source:
2104 if source:
2105 source, branches = hg.parseurl(ui.expandpath(source))
2105 source, branches = hg.parseurl(ui.expandpath(source))
2106 repo = hg.repository(ui, source)
2106 repo = hg.repository(ui, source)
2107 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2107 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2108
2108
2109 if not repo.local():
2109 if not repo.local():
2110 if not rev and revs:
2110 if not rev and revs:
2111 rev = revs[0]
2111 rev = revs[0]
2112 if not rev:
2112 if not rev:
2113 rev = "tip"
2113 rev = "tip"
2114 if num or branch or tags:
2114 if num or branch or tags:
2115 raise util.Abort(
2115 raise util.Abort(
2116 "can't query remote revision number, branch, or tags")
2116 "can't query remote revision number, branch, or tags")
2117 output = [hexfunc(repo.lookup(rev))]
2117 output = [hexfunc(repo.lookup(rev))]
2118 elif not rev:
2118 elif not rev:
2119 ctx = repo[None]
2119 ctx = repo[None]
2120 parents = ctx.parents()
2120 parents = ctx.parents()
2121 changed = False
2121 changed = False
2122 if default or id or num:
2122 if default or id or num:
2123 changed = util.any(repo.status())
2123 changed = util.any(repo.status())
2124 if default or id:
2124 if default or id:
2125 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2125 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2126 (changed) and "+" or "")]
2126 (changed) and "+" or "")]
2127 if num:
2127 if num:
2128 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2128 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2129 (changed) and "+" or ""))
2129 (changed) and "+" or ""))
2130 else:
2130 else:
2131 ctx = repo[rev]
2131 ctx = repo[rev]
2132 if default or id:
2132 if default or id:
2133 output = [hexfunc(ctx.node())]
2133 output = [hexfunc(ctx.node())]
2134 if num:
2134 if num:
2135 output.append(str(ctx.rev()))
2135 output.append(str(ctx.rev()))
2136
2136
2137 if repo.local() and default and not ui.quiet:
2137 if repo.local() and default and not ui.quiet:
2138 b = encoding.tolocal(ctx.branch())
2138 b = encoding.tolocal(ctx.branch())
2139 if b != 'default':
2139 if b != 'default':
2140 output.append("(%s)" % b)
2140 output.append("(%s)" % b)
2141
2141
2142 # multiple tags for a single parent separated by '/'
2142 # multiple tags for a single parent separated by '/'
2143 t = "/".join(ctx.tags())
2143 t = "/".join(ctx.tags())
2144 if t:
2144 if t:
2145 output.append(t)
2145 output.append(t)
2146
2146
2147 if branch:
2147 if branch:
2148 output.append(encoding.tolocal(ctx.branch()))
2148 output.append(encoding.tolocal(ctx.branch()))
2149
2149
2150 if tags:
2150 if tags:
2151 output.extend(ctx.tags())
2151 output.extend(ctx.tags())
2152
2152
2153 ui.write("%s\n" % ' '.join(output))
2153 ui.write("%s\n" % ' '.join(output))
2154
2154
2155 def import_(ui, repo, patch1, *patches, **opts):
2155 def import_(ui, repo, patch1, *patches, **opts):
2156 """import an ordered set of patches
2156 """import an ordered set of patches
2157
2157
2158 Import a list of patches and commit them individually (unless
2158 Import a list of patches and commit them individually (unless
2159 --no-commit is specified).
2159 --no-commit is specified).
2160
2160
2161 If there are outstanding changes in the working directory, import
2161 If there are outstanding changes in the working directory, import
2162 will abort unless given the -f/--force flag.
2162 will abort unless given the -f/--force flag.
2163
2163
2164 You can import a patch straight from a mail message. Even patches
2164 You can import a patch straight from a mail message. Even patches
2165 as attachments work (to use the body part, it must have type
2165 as attachments work (to use the body part, it must have type
2166 text/plain or text/x-patch). From and Subject headers of email
2166 text/plain or text/x-patch). From and Subject headers of email
2167 message are used as default committer and commit message. All
2167 message are used as default committer and commit message. All
2168 text/plain body parts before first diff are added to commit
2168 text/plain body parts before first diff are added to commit
2169 message.
2169 message.
2170
2170
2171 If the imported patch was generated by :hg:`export`, user and
2171 If the imported patch was generated by :hg:`export`, user and
2172 description from patch override values from message headers and
2172 description from patch override values from message headers and
2173 body. Values given on command line with -m/--message and -u/--user
2173 body. Values given on command line with -m/--message and -u/--user
2174 override these.
2174 override these.
2175
2175
2176 If --exact is specified, import will set the working directory to
2176 If --exact is specified, import will set the working directory to
2177 the parent of each patch before applying it, and will abort if the
2177 the parent of each patch before applying it, and will abort if the
2178 resulting changeset has a different ID than the one recorded in
2178 resulting changeset has a different ID than the one recorded in
2179 the patch. This may happen due to character set problems or other
2179 the patch. This may happen due to character set problems or other
2180 deficiencies in the text patch format.
2180 deficiencies in the text patch format.
2181
2181
2182 With -s/--similarity, hg will attempt to discover renames and
2182 With -s/--similarity, hg will attempt to discover renames and
2183 copies in the patch in the same way as 'addremove'.
2183 copies in the patch in the same way as 'addremove'.
2184
2184
2185 To read a patch from standard input, use "-" as the patch name. If
2185 To read a patch from standard input, use "-" as the patch name. If
2186 a URL is specified, the patch will be downloaded from it.
2186 a URL is specified, the patch will be downloaded from it.
2187 See :hg:`help dates` for a list of formats valid for -d/--date.
2187 See :hg:`help dates` for a list of formats valid for -d/--date.
2188
2188
2189 Returns 0 on success.
2189 Returns 0 on success.
2190 """
2190 """
2191 patches = (patch1,) + patches
2191 patches = (patch1,) + patches
2192
2192
2193 date = opts.get('date')
2193 date = opts.get('date')
2194 if date:
2194 if date:
2195 opts['date'] = util.parsedate(date)
2195 opts['date'] = util.parsedate(date)
2196
2196
2197 try:
2197 try:
2198 sim = float(opts.get('similarity') or 0)
2198 sim = float(opts.get('similarity') or 0)
2199 except ValueError:
2199 except ValueError:
2200 raise util.Abort(_('similarity must be a number'))
2200 raise util.Abort(_('similarity must be a number'))
2201 if sim < 0 or sim > 100:
2201 if sim < 0 or sim > 100:
2202 raise util.Abort(_('similarity must be between 0 and 100'))
2202 raise util.Abort(_('similarity must be between 0 and 100'))
2203
2203
2204 if opts.get('exact') or not opts.get('force'):
2204 if opts.get('exact') or not opts.get('force'):
2205 cmdutil.bail_if_changed(repo)
2205 cmdutil.bail_if_changed(repo)
2206
2206
2207 d = opts["base"]
2207 d = opts["base"]
2208 strip = opts["strip"]
2208 strip = opts["strip"]
2209 wlock = lock = None
2209 wlock = lock = None
2210
2210
2211 def tryone(ui, hunk):
2211 def tryone(ui, hunk):
2212 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2212 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2213 patch.extract(ui, hunk)
2213 patch.extract(ui, hunk)
2214
2214
2215 if not tmpname:
2215 if not tmpname:
2216 return None
2216 return None
2217 commitid = _('to working directory')
2217 commitid = _('to working directory')
2218
2218
2219 try:
2219 try:
2220 cmdline_message = cmdutil.logmessage(opts)
2220 cmdline_message = cmdutil.logmessage(opts)
2221 if cmdline_message:
2221 if cmdline_message:
2222 # pickup the cmdline msg
2222 # pickup the cmdline msg
2223 message = cmdline_message
2223 message = cmdline_message
2224 elif message:
2224 elif message:
2225 # pickup the patch msg
2225 # pickup the patch msg
2226 message = message.strip()
2226 message = message.strip()
2227 else:
2227 else:
2228 # launch the editor
2228 # launch the editor
2229 message = None
2229 message = None
2230 ui.debug('message:\n%s\n' % message)
2230 ui.debug('message:\n%s\n' % message)
2231
2231
2232 wp = repo.parents()
2232 wp = repo.parents()
2233 if opts.get('exact'):
2233 if opts.get('exact'):
2234 if not nodeid or not p1:
2234 if not nodeid or not p1:
2235 raise util.Abort(_('not a Mercurial patch'))
2235 raise util.Abort(_('not a Mercurial patch'))
2236 p1 = repo.lookup(p1)
2236 p1 = repo.lookup(p1)
2237 p2 = repo.lookup(p2 or hex(nullid))
2237 p2 = repo.lookup(p2 or hex(nullid))
2238
2238
2239 if p1 != wp[0].node():
2239 if p1 != wp[0].node():
2240 hg.clean(repo, p1)
2240 hg.clean(repo, p1)
2241 repo.dirstate.setparents(p1, p2)
2241 repo.dirstate.setparents(p1, p2)
2242 elif p2:
2242 elif p2:
2243 try:
2243 try:
2244 p1 = repo.lookup(p1)
2244 p1 = repo.lookup(p1)
2245 p2 = repo.lookup(p2)
2245 p2 = repo.lookup(p2)
2246 if p1 == wp[0].node():
2246 if p1 == wp[0].node():
2247 repo.dirstate.setparents(p1, p2)
2247 repo.dirstate.setparents(p1, p2)
2248 except error.RepoError:
2248 except error.RepoError:
2249 pass
2249 pass
2250 if opts.get('exact') or opts.get('import_branch'):
2250 if opts.get('exact') or opts.get('import_branch'):
2251 repo.dirstate.setbranch(branch or 'default')
2251 repo.dirstate.setbranch(branch or 'default')
2252
2252
2253 files = {}
2253 files = {}
2254 try:
2254 try:
2255 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2255 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2256 files=files, eolmode=None)
2256 files=files, eolmode=None)
2257 finally:
2257 finally:
2258 files = patch.updatedir(ui, repo, files,
2258 files = patch.updatedir(ui, repo, files,
2259 similarity=sim / 100.0)
2259 similarity=sim / 100.0)
2260 if not opts.get('no_commit'):
2260 if not opts.get('no_commit'):
2261 if opts.get('exact'):
2261 if opts.get('exact'):
2262 m = None
2262 m = None
2263 else:
2263 else:
2264 m = cmdutil.matchfiles(repo, files or [])
2264 m = cmdutil.matchfiles(repo, files or [])
2265 n = repo.commit(message, opts.get('user') or user,
2265 n = repo.commit(message, opts.get('user') or user,
2266 opts.get('date') or date, match=m,
2266 opts.get('date') or date, match=m,
2267 editor=cmdutil.commiteditor)
2267 editor=cmdutil.commiteditor)
2268 if opts.get('exact'):
2268 if opts.get('exact'):
2269 if hex(n) != nodeid:
2269 if hex(n) != nodeid:
2270 repo.rollback()
2270 repo.rollback()
2271 raise util.Abort(_('patch is damaged'
2271 raise util.Abort(_('patch is damaged'
2272 ' or loses information'))
2272 ' or loses information'))
2273 # Force a dirstate write so that the next transaction
2273 # Force a dirstate write so that the next transaction
2274 # backups an up-do-date file.
2274 # backups an up-do-date file.
2275 repo.dirstate.write()
2275 repo.dirstate.write()
2276 if n:
2276 if n:
2277 commitid = short(n)
2277 commitid = short(n)
2278
2278
2279 return commitid
2279 return commitid
2280 finally:
2280 finally:
2281 os.unlink(tmpname)
2281 os.unlink(tmpname)
2282
2282
2283 try:
2283 try:
2284 wlock = repo.wlock()
2284 wlock = repo.wlock()
2285 lock = repo.lock()
2285 lock = repo.lock()
2286 lastcommit = None
2286 lastcommit = None
2287 for p in patches:
2287 for p in patches:
2288 pf = os.path.join(d, p)
2288 pf = os.path.join(d, p)
2289
2289
2290 if pf == '-':
2290 if pf == '-':
2291 ui.status(_("applying patch from stdin\n"))
2291 ui.status(_("applying patch from stdin\n"))
2292 pf = sys.stdin
2292 pf = sys.stdin
2293 else:
2293 else:
2294 ui.status(_("applying %s\n") % p)
2294 ui.status(_("applying %s\n") % p)
2295 pf = url.open(ui, pf)
2295 pf = url.open(ui, pf)
2296
2296
2297 haspatch = False
2297 haspatch = False
2298 for hunk in patch.split(pf):
2298 for hunk in patch.split(pf):
2299 commitid = tryone(ui, hunk)
2299 commitid = tryone(ui, hunk)
2300 if commitid:
2300 if commitid:
2301 haspatch = True
2301 haspatch = True
2302 if lastcommit:
2302 if lastcommit:
2303 ui.status(_('applied %s\n') % lastcommit)
2303 ui.status(_('applied %s\n') % lastcommit)
2304 lastcommit = commitid
2304 lastcommit = commitid
2305
2305
2306 if not haspatch:
2306 if not haspatch:
2307 raise util.Abort(_('no diffs found'))
2307 raise util.Abort(_('no diffs found'))
2308
2308
2309 finally:
2309 finally:
2310 release(lock, wlock)
2310 release(lock, wlock)
2311
2311
2312 def incoming(ui, repo, source="default", **opts):
2312 def incoming(ui, repo, source="default", **opts):
2313 """show new changesets found in source
2313 """show new changesets found in source
2314
2314
2315 Show new changesets found in the specified path/URL or the default
2315 Show new changesets found in the specified path/URL or the default
2316 pull location. These are the changesets that would have been pulled
2316 pull location. These are the changesets that would have been pulled
2317 if a pull at the time you issued this command.
2317 if a pull at the time you issued this command.
2318
2318
2319 For remote repository, using --bundle avoids downloading the
2319 For remote repository, using --bundle avoids downloading the
2320 changesets twice if the incoming is followed by a pull.
2320 changesets twice if the incoming is followed by a pull.
2321
2321
2322 See pull for valid source format details.
2322 See pull for valid source format details.
2323
2323
2324 Returns 0 if there are incoming changes, 1 otherwise.
2324 Returns 0 if there are incoming changes, 1 otherwise.
2325 """
2325 """
2326 limit = cmdutil.loglimit(opts)
2326 limit = cmdutil.loglimit(opts)
2327 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2327 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2328 other = hg.repository(hg.remoteui(repo, opts), source)
2328 other = hg.repository(hg.remoteui(repo, opts), source)
2329 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2329 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2330 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2330 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2331 if revs:
2331 if revs:
2332 revs = [other.lookup(rev) for rev in revs]
2332 revs = [other.lookup(rev) for rev in revs]
2333
2333
2334 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2334 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2335 force=opts.get('force'))
2335 force=opts.get('force'))
2336 common, incoming, rheads = tmp
2336 common, incoming, rheads = tmp
2337 if not incoming:
2337 if not incoming:
2338 try:
2338 try:
2339 os.unlink(opts["bundle"])
2339 os.unlink(opts["bundle"])
2340 except:
2340 except:
2341 pass
2341 pass
2342 ui.status(_("no changes found\n"))
2342 ui.status(_("no changes found\n"))
2343 return 1
2343 return 1
2344
2344
2345 cleanup = None
2345 cleanup = None
2346 try:
2346 try:
2347 fname = opts["bundle"]
2347 fname = opts["bundle"]
2348 if fname or not other.local():
2348 if fname or not other.local():
2349 # create a bundle (uncompressed if other repo is not local)
2349 # create a bundle (uncompressed if other repo is not local)
2350
2350
2351 if revs is None and other.capable('changegroupsubset'):
2351 if revs is None and other.capable('changegroupsubset'):
2352 revs = rheads
2352 revs = rheads
2353
2353
2354 if revs is None:
2354 if revs is None:
2355 cg = other.changegroup(incoming, "incoming")
2355 cg = other.changegroup(incoming, "incoming")
2356 else:
2356 else:
2357 cg = other.changegroupsubset(incoming, revs, 'incoming')
2357 cg = other.changegroupsubset(incoming, revs, 'incoming')
2358 bundletype = other.local() and "HG10BZ" or "HG10UN"
2358 bundletype = other.local() and "HG10BZ" or "HG10UN"
2359 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2359 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2360 # keep written bundle?
2360 # keep written bundle?
2361 if opts["bundle"]:
2361 if opts["bundle"]:
2362 cleanup = None
2362 cleanup = None
2363 if not other.local():
2363 if not other.local():
2364 # use the created uncompressed bundlerepo
2364 # use the created uncompressed bundlerepo
2365 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2365 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2366
2366
2367 o = other.changelog.nodesbetween(incoming, revs)[0]
2367 o = other.changelog.nodesbetween(incoming, revs)[0]
2368 if opts.get('newest_first'):
2368 if opts.get('newest_first'):
2369 o.reverse()
2369 o.reverse()
2370 displayer = cmdutil.show_changeset(ui, other, opts)
2370 displayer = cmdutil.show_changeset(ui, other, opts)
2371 count = 0
2371 count = 0
2372 for n in o:
2372 for n in o:
2373 if limit is not None and count >= limit:
2373 if limit is not None and count >= limit:
2374 break
2374 break
2375 parents = [p for p in other.changelog.parents(n) if p != nullid]
2375 parents = [p for p in other.changelog.parents(n) if p != nullid]
2376 if opts.get('no_merges') and len(parents) == 2:
2376 if opts.get('no_merges') and len(parents) == 2:
2377 continue
2377 continue
2378 count += 1
2378 count += 1
2379 displayer.show(other[n])
2379 displayer.show(other[n])
2380 displayer.close()
2380 displayer.close()
2381 finally:
2381 finally:
2382 if hasattr(other, 'close'):
2382 if hasattr(other, 'close'):
2383 other.close()
2383 other.close()
2384 if cleanup:
2384 if cleanup:
2385 os.unlink(cleanup)
2385 os.unlink(cleanup)
2386
2386
2387 def init(ui, dest=".", **opts):
2387 def init(ui, dest=".", **opts):
2388 """create a new repository in the given directory
2388 """create a new repository in the given directory
2389
2389
2390 Initialize a new repository in the given directory. If the given
2390 Initialize a new repository in the given directory. If the given
2391 directory does not exist, it will be created.
2391 directory does not exist, it will be created.
2392
2392
2393 If no directory is given, the current directory is used.
2393 If no directory is given, the current directory is used.
2394
2394
2395 It is possible to specify an ``ssh://`` URL as the destination.
2395 It is possible to specify an ``ssh://`` URL as the destination.
2396 See :hg:`help urls` for more information.
2396 See :hg:`help urls` for more information.
2397
2397
2398 Returns 0 on success.
2398 Returns 0 on success.
2399 """
2399 """
2400 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2400 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2401
2401
2402 def locate(ui, repo, *pats, **opts):
2402 def locate(ui, repo, *pats, **opts):
2403 """locate files matching specific patterns
2403 """locate files matching specific patterns
2404
2404
2405 Print files under Mercurial control in the working directory whose
2405 Print files under Mercurial control in the working directory whose
2406 names match the given patterns.
2406 names match the given patterns.
2407
2407
2408 By default, this command searches all directories in the working
2408 By default, this command searches all directories in the working
2409 directory. To search just the current directory and its
2409 directory. To search just the current directory and its
2410 subdirectories, use "--include .".
2410 subdirectories, use "--include .".
2411
2411
2412 If no patterns are given to match, this command prints the names
2412 If no patterns are given to match, this command prints the names
2413 of all files under Mercurial control in the working directory.
2413 of all files under Mercurial control in the working directory.
2414
2414
2415 If you want to feed the output of this command into the "xargs"
2415 If you want to feed the output of this command into the "xargs"
2416 command, use the -0 option to both this command and "xargs". This
2416 command, use the -0 option to both this command and "xargs". This
2417 will avoid the problem of "xargs" treating single filenames that
2417 will avoid the problem of "xargs" treating single filenames that
2418 contain whitespace as multiple filenames.
2418 contain whitespace as multiple filenames.
2419
2419
2420 Returns 0 if a match is found, 1 otherwise.
2420 Returns 0 if a match is found, 1 otherwise.
2421 """
2421 """
2422 end = opts.get('print0') and '\0' or '\n'
2422 end = opts.get('print0') and '\0' or '\n'
2423 rev = opts.get('rev') or None
2423 rev = opts.get('rev') or None
2424
2424
2425 ret = 1
2425 ret = 1
2426 m = cmdutil.match(repo, pats, opts, default='relglob')
2426 m = cmdutil.match(repo, pats, opts, default='relglob')
2427 m.bad = lambda x, y: False
2427 m.bad = lambda x, y: False
2428 for abs in repo[rev].walk(m):
2428 for abs in repo[rev].walk(m):
2429 if not rev and abs not in repo.dirstate:
2429 if not rev and abs not in repo.dirstate:
2430 continue
2430 continue
2431 if opts.get('fullpath'):
2431 if opts.get('fullpath'):
2432 ui.write(repo.wjoin(abs), end)
2432 ui.write(repo.wjoin(abs), end)
2433 else:
2433 else:
2434 ui.write(((pats and m.rel(abs)) or abs), end)
2434 ui.write(((pats and m.rel(abs)) or abs), end)
2435 ret = 0
2435 ret = 0
2436
2436
2437 return ret
2437 return ret
2438
2438
2439 def log(ui, repo, *pats, **opts):
2439 def log(ui, repo, *pats, **opts):
2440 """show revision history of entire repository or files
2440 """show revision history of entire repository or files
2441
2441
2442 Print the revision history of the specified files or the entire
2442 Print the revision history of the specified files or the entire
2443 project.
2443 project.
2444
2444
2445 File history is shown without following rename or copy history of
2445 File history is shown without following rename or copy history of
2446 files. Use -f/--follow with a filename to follow history across
2446 files. Use -f/--follow with a filename to follow history across
2447 renames and copies. --follow without a filename will only show
2447 renames and copies. --follow without a filename will only show
2448 ancestors or descendants of the starting revision. --follow-first
2448 ancestors or descendants of the starting revision. --follow-first
2449 only follows the first parent of merge revisions.
2449 only follows the first parent of merge revisions.
2450
2450
2451 If no revision range is specified, the default is tip:0 unless
2451 If no revision range is specified, the default is tip:0 unless
2452 --follow is set, in which case the working directory parent is
2452 --follow is set, in which case the working directory parent is
2453 used as the starting revision. You can specify a revision set for
2453 used as the starting revision. You can specify a revision set for
2454 log, see :hg:`help revsets` for more information.
2454 log, see :hg:`help revsets` for more information.
2455
2455
2456 See :hg:`help dates` for a list of formats valid for -d/--date.
2456 See :hg:`help dates` for a list of formats valid for -d/--date.
2457
2457
2458 By default this command prints revision number and changeset id,
2458 By default this command prints revision number and changeset id,
2459 tags, non-trivial parents, user, date and time, and a summary for
2459 tags, non-trivial parents, user, date and time, and a summary for
2460 each commit. When the -v/--verbose switch is used, the list of
2460 each commit. When the -v/--verbose switch is used, the list of
2461 changed files and full commit message are shown.
2461 changed files and full commit message are shown.
2462
2462
2463 NOTE: log -p/--patch may generate unexpected diff output for merge
2463 NOTE: log -p/--patch may generate unexpected diff output for merge
2464 changesets, as it will only compare the merge changeset against
2464 changesets, as it will only compare the merge changeset against
2465 its first parent. Also, only files different from BOTH parents
2465 its first parent. Also, only files different from BOTH parents
2466 will appear in files:.
2466 will appear in files:.
2467
2467
2468 Returns 0 on success.
2468 Returns 0 on success.
2469 """
2469 """
2470
2470
2471 matchfn = cmdutil.match(repo, pats, opts)
2471 matchfn = cmdutil.match(repo, pats, opts)
2472 limit = cmdutil.loglimit(opts)
2472 limit = cmdutil.loglimit(opts)
2473 count = 0
2473 count = 0
2474
2474
2475 endrev = None
2475 endrev = None
2476 if opts.get('copies') and opts.get('rev'):
2476 if opts.get('copies') and opts.get('rev'):
2477 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2477 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2478
2478
2479 df = False
2479 df = False
2480 if opts["date"]:
2480 if opts["date"]:
2481 df = util.matchdate(opts["date"])
2481 df = util.matchdate(opts["date"])
2482
2482
2483 branches = opts.get('branch', []) + opts.get('only_branch', [])
2483 branches = opts.get('branch', []) + opts.get('only_branch', [])
2484 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2484 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2485
2485
2486 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2486 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2487 def prep(ctx, fns):
2487 def prep(ctx, fns):
2488 rev = ctx.rev()
2488 rev = ctx.rev()
2489 parents = [p for p in repo.changelog.parentrevs(rev)
2489 parents = [p for p in repo.changelog.parentrevs(rev)
2490 if p != nullrev]
2490 if p != nullrev]
2491 if opts.get('no_merges') and len(parents) == 2:
2491 if opts.get('no_merges') and len(parents) == 2:
2492 return
2492 return
2493 if opts.get('only_merges') and len(parents) != 2:
2493 if opts.get('only_merges') and len(parents) != 2:
2494 return
2494 return
2495 if opts.get('branch') and ctx.branch() not in opts['branch']:
2495 if opts.get('branch') and ctx.branch() not in opts['branch']:
2496 return
2496 return
2497 if df and not df(ctx.date()[0]):
2497 if df and not df(ctx.date()[0]):
2498 return
2498 return
2499 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2499 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2500 return
2500 return
2501 if opts.get('keyword'):
2501 if opts.get('keyword'):
2502 for k in [kw.lower() for kw in opts['keyword']]:
2502 for k in [kw.lower() for kw in opts['keyword']]:
2503 if (k in ctx.user().lower() or
2503 if (k in ctx.user().lower() or
2504 k in ctx.description().lower() or
2504 k in ctx.description().lower() or
2505 k in " ".join(ctx.files()).lower()):
2505 k in " ".join(ctx.files()).lower()):
2506 break
2506 break
2507 else:
2507 else:
2508 return
2508 return
2509
2509
2510 copies = None
2510 copies = None
2511 if opts.get('copies') and rev:
2511 if opts.get('copies') and rev:
2512 copies = []
2512 copies = []
2513 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2513 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2514 for fn in ctx.files():
2514 for fn in ctx.files():
2515 rename = getrenamed(fn, rev)
2515 rename = getrenamed(fn, rev)
2516 if rename:
2516 if rename:
2517 copies.append((fn, rename[0]))
2517 copies.append((fn, rename[0]))
2518
2518
2519 revmatchfn = None
2519 revmatchfn = None
2520 if opts.get('patch') or opts.get('stat'):
2520 if opts.get('patch') or opts.get('stat'):
2521 revmatchfn = cmdutil.match(repo, fns, default='path')
2521 revmatchfn = cmdutil.match(repo, fns, default='path')
2522
2522
2523 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2523 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2524
2524
2525 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2525 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2526 if count == limit:
2526 if count == limit:
2527 break
2527 break
2528 if displayer.flush(ctx.rev()):
2528 if displayer.flush(ctx.rev()):
2529 count += 1
2529 count += 1
2530 displayer.close()
2530 displayer.close()
2531
2531
2532 def manifest(ui, repo, node=None, rev=None):
2532 def manifest(ui, repo, node=None, rev=None):
2533 """output the current or given revision of the project manifest
2533 """output the current or given revision of the project manifest
2534
2534
2535 Print a list of version controlled files for the given revision.
2535 Print a list of version controlled files for the given revision.
2536 If no revision is given, the first parent of the working directory
2536 If no revision is given, the first parent of the working directory
2537 is used, or the null revision if no revision is checked out.
2537 is used, or the null revision if no revision is checked out.
2538
2538
2539 With -v, print file permissions, symlink and executable bits.
2539 With -v, print file permissions, symlink and executable bits.
2540 With --debug, print file revision hashes.
2540 With --debug, print file revision hashes.
2541
2541
2542 Returns 0 on success.
2542 Returns 0 on success.
2543 """
2543 """
2544
2544
2545 if rev and node:
2545 if rev and node:
2546 raise util.Abort(_("please specify just one revision"))
2546 raise util.Abort(_("please specify just one revision"))
2547
2547
2548 if not node:
2548 if not node:
2549 node = rev
2549 node = rev
2550
2550
2551 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2551 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2552 ctx = repo[node]
2552 ctx = repo[node]
2553 for f in ctx:
2553 for f in ctx:
2554 if ui.debugflag:
2554 if ui.debugflag:
2555 ui.write("%40s " % hex(ctx.manifest()[f]))
2555 ui.write("%40s " % hex(ctx.manifest()[f]))
2556 if ui.verbose:
2556 if ui.verbose:
2557 ui.write(decor[ctx.flags(f)])
2557 ui.write(decor[ctx.flags(f)])
2558 ui.write("%s\n" % f)
2558 ui.write("%s\n" % f)
2559
2559
2560 def merge(ui, repo, node=None, **opts):
2560 def merge(ui, repo, node=None, **opts):
2561 """merge working directory with another revision
2561 """merge working directory with another revision
2562
2562
2563 The current working directory is updated with all changes made in
2563 The current working directory is updated with all changes made in
2564 the requested revision since the last common predecessor revision.
2564 the requested revision since the last common predecessor revision.
2565
2565
2566 Files that changed between either parent are marked as changed for
2566 Files that changed between either parent are marked as changed for
2567 the next commit and a commit must be performed before any further
2567 the next commit and a commit must be performed before any further
2568 updates to the repository are allowed. The next commit will have
2568 updates to the repository are allowed. The next commit will have
2569 two parents.
2569 two parents.
2570
2570
2571 If no revision is specified, the working directory's parent is a
2571 If no revision is specified, the working directory's parent is a
2572 head revision, and the current branch contains exactly one other
2572 head revision, and the current branch contains exactly one other
2573 head, the other head is merged with by default. Otherwise, an
2573 head, the other head is merged with by default. Otherwise, an
2574 explicit revision with which to merge with must be provided.
2574 explicit revision with which to merge with must be provided.
2575
2575
2576 To undo an uncommitted merge, use :hg:`update --clean .` which
2576 To undo an uncommitted merge, use :hg:`update --clean .` which
2577 will check out a clean copy of the original merge parent, losing
2577 will check out a clean copy of the original merge parent, losing
2578 all changes.
2578 all changes.
2579
2579
2580 Returns 0 on success, 1 if there are unresolved files.
2580 Returns 0 on success, 1 if there are unresolved files.
2581 """
2581 """
2582
2582
2583 if opts.get('rev') and node:
2583 if opts.get('rev') and node:
2584 raise util.Abort(_("please specify just one revision"))
2584 raise util.Abort(_("please specify just one revision"))
2585 if not node:
2585 if not node:
2586 node = opts.get('rev')
2586 node = opts.get('rev')
2587
2587
2588 if not node:
2588 if not node:
2589 branch = repo.changectx(None).branch()
2589 branch = repo.changectx(None).branch()
2590 bheads = repo.branchheads(branch)
2590 bheads = repo.branchheads(branch)
2591 if len(bheads) > 2:
2591 if len(bheads) > 2:
2592 raise util.Abort(_(
2592 raise util.Abort(_(
2593 'branch \'%s\' has %d heads - '
2593 'branch \'%s\' has %d heads - '
2594 'please merge with an explicit rev\n'
2594 'please merge with an explicit rev\n'
2595 '(run \'hg heads .\' to see heads)')
2595 '(run \'hg heads .\' to see heads)')
2596 % (branch, len(bheads)))
2596 % (branch, len(bheads)))
2597
2597
2598 parent = repo.dirstate.parents()[0]
2598 parent = repo.dirstate.parents()[0]
2599 if len(bheads) == 1:
2599 if len(bheads) == 1:
2600 if len(repo.heads()) > 1:
2600 if len(repo.heads()) > 1:
2601 raise util.Abort(_(
2601 raise util.Abort(_(
2602 'branch \'%s\' has one head - '
2602 'branch \'%s\' has one head - '
2603 'please merge with an explicit rev\n'
2603 'please merge with an explicit rev\n'
2604 '(run \'hg heads\' to see all heads)')
2604 '(run \'hg heads\' to see all heads)')
2605 % branch)
2605 % branch)
2606 msg = _('there is nothing to merge')
2606 msg = _('there is nothing to merge')
2607 if parent != repo.lookup(repo[None].branch()):
2607 if parent != repo.lookup(repo[None].branch()):
2608 msg = _('%s - use "hg update" instead') % msg
2608 msg = _('%s - use "hg update" instead') % msg
2609 raise util.Abort(msg)
2609 raise util.Abort(msg)
2610
2610
2611 if parent not in bheads:
2611 if parent not in bheads:
2612 raise util.Abort(_('working dir not at a head rev - '
2612 raise util.Abort(_('working dir not at a head rev - '
2613 'use "hg update" or merge with an explicit rev'))
2613 'use "hg update" or merge with an explicit rev'))
2614 node = parent == bheads[0] and bheads[-1] or bheads[0]
2614 node = parent == bheads[0] and bheads[-1] or bheads[0]
2615
2615
2616 if opts.get('preview'):
2616 if opts.get('preview'):
2617 # find nodes that are ancestors of p2 but not of p1
2617 # find nodes that are ancestors of p2 but not of p1
2618 p1 = repo.lookup('.')
2618 p1 = repo.lookup('.')
2619 p2 = repo.lookup(node)
2619 p2 = repo.lookup(node)
2620 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2620 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2621
2621
2622 displayer = cmdutil.show_changeset(ui, repo, opts)
2622 displayer = cmdutil.show_changeset(ui, repo, opts)
2623 for node in nodes:
2623 for node in nodes:
2624 displayer.show(repo[node])
2624 displayer.show(repo[node])
2625 displayer.close()
2625 displayer.close()
2626 return 0
2626 return 0
2627
2627
2628 return hg.merge(repo, node, force=opts.get('force'))
2628 return hg.merge(repo, node, force=opts.get('force'))
2629
2629
2630 def outgoing(ui, repo, dest=None, **opts):
2630 def outgoing(ui, repo, dest=None, **opts):
2631 """show changesets not found in the destination
2631 """show changesets not found in the destination
2632
2632
2633 Show changesets not found in the specified destination repository
2633 Show changesets not found in the specified destination repository
2634 or the default push location. These are the changesets that would
2634 or the default push location. These are the changesets that would
2635 be pushed if a push was requested.
2635 be pushed if a push was requested.
2636
2636
2637 See pull for details of valid destination formats.
2637 See pull for details of valid destination formats.
2638
2638
2639 Returns 0 if there are outgoing changes, 1 otherwise.
2639 Returns 0 if there are outgoing changes, 1 otherwise.
2640 """
2640 """
2641 limit = cmdutil.loglimit(opts)
2641 limit = cmdutil.loglimit(opts)
2642 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2642 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2643 dest, branches = hg.parseurl(dest, opts.get('branch'))
2643 dest, branches = hg.parseurl(dest, opts.get('branch'))
2644 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2644 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2645 if revs:
2645 if revs:
2646 revs = [repo.lookup(rev) for rev in revs]
2646 revs = [repo.lookup(rev) for rev in revs]
2647
2647
2648 other = hg.repository(hg.remoteui(repo, opts), dest)
2648 other = hg.repository(hg.remoteui(repo, opts), dest)
2649 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2649 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2650 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2650 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2651 if not o:
2651 if not o:
2652 ui.status(_("no changes found\n"))
2652 ui.status(_("no changes found\n"))
2653 return 1
2653 return 1
2654 o = repo.changelog.nodesbetween(o, revs)[0]
2654 o = repo.changelog.nodesbetween(o, revs)[0]
2655 if opts.get('newest_first'):
2655 if opts.get('newest_first'):
2656 o.reverse()
2656 o.reverse()
2657 displayer = cmdutil.show_changeset(ui, repo, opts)
2657 displayer = cmdutil.show_changeset(ui, repo, opts)
2658 count = 0
2658 count = 0
2659 for n in o:
2659 for n in o:
2660 if limit is not None and count >= limit:
2660 if limit is not None and count >= limit:
2661 break
2661 break
2662 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2662 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2663 if opts.get('no_merges') and len(parents) == 2:
2663 if opts.get('no_merges') and len(parents) == 2:
2664 continue
2664 continue
2665 count += 1
2665 count += 1
2666 displayer.show(repo[n])
2666 displayer.show(repo[n])
2667 displayer.close()
2667 displayer.close()
2668
2668
2669 def parents(ui, repo, file_=None, **opts):
2669 def parents(ui, repo, file_=None, **opts):
2670 """show the parents of the working directory or revision
2670 """show the parents of the working directory or revision
2671
2671
2672 Print the working directory's parent revisions. If a revision is
2672 Print the working directory's parent revisions. If a revision is
2673 given via -r/--rev, the parent of that revision will be printed.
2673 given via -r/--rev, the parent of that revision will be printed.
2674 If a file argument is given, the revision in which the file was
2674 If a file argument is given, the revision in which the file was
2675 last changed (before the working directory revision or the
2675 last changed (before the working directory revision or the
2676 argument to --rev if given) is printed.
2676 argument to --rev if given) is printed.
2677
2677
2678 Returns 0 on success.
2678 Returns 0 on success.
2679 """
2679 """
2680 rev = opts.get('rev')
2680 rev = opts.get('rev')
2681 if rev:
2681 if rev:
2682 ctx = repo[rev]
2682 ctx = repo[rev]
2683 else:
2683 else:
2684 ctx = repo[None]
2684 ctx = repo[None]
2685
2685
2686 if file_:
2686 if file_:
2687 m = cmdutil.match(repo, (file_,), opts)
2687 m = cmdutil.match(repo, (file_,), opts)
2688 if m.anypats() or len(m.files()) != 1:
2688 if m.anypats() or len(m.files()) != 1:
2689 raise util.Abort(_('can only specify an explicit filename'))
2689 raise util.Abort(_('can only specify an explicit filename'))
2690 file_ = m.files()[0]
2690 file_ = m.files()[0]
2691 filenodes = []
2691 filenodes = []
2692 for cp in ctx.parents():
2692 for cp in ctx.parents():
2693 if not cp:
2693 if not cp:
2694 continue
2694 continue
2695 try:
2695 try:
2696 filenodes.append(cp.filenode(file_))
2696 filenodes.append(cp.filenode(file_))
2697 except error.LookupError:
2697 except error.LookupError:
2698 pass
2698 pass
2699 if not filenodes:
2699 if not filenodes:
2700 raise util.Abort(_("'%s' not found in manifest!") % file_)
2700 raise util.Abort(_("'%s' not found in manifest!") % file_)
2701 fl = repo.file(file_)
2701 fl = repo.file(file_)
2702 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2702 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2703 else:
2703 else:
2704 p = [cp.node() for cp in ctx.parents()]
2704 p = [cp.node() for cp in ctx.parents()]
2705
2705
2706 displayer = cmdutil.show_changeset(ui, repo, opts)
2706 displayer = cmdutil.show_changeset(ui, repo, opts)
2707 for n in p:
2707 for n in p:
2708 if n != nullid:
2708 if n != nullid:
2709 displayer.show(repo[n])
2709 displayer.show(repo[n])
2710 displayer.close()
2710 displayer.close()
2711
2711
2712 def paths(ui, repo, search=None):
2712 def paths(ui, repo, search=None):
2713 """show aliases for remote repositories
2713 """show aliases for remote repositories
2714
2714
2715 Show definition of symbolic path name NAME. If no name is given,
2715 Show definition of symbolic path name NAME. If no name is given,
2716 show definition of all available names.
2716 show definition of all available names.
2717
2717
2718 Path names are defined in the [paths] section of
2718 Path names are defined in the [paths] section of
2719 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2719 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2720 repository, ``.hg/hgrc`` is used, too.
2720 repository, ``.hg/hgrc`` is used, too.
2721
2721
2722 The path names ``default`` and ``default-push`` have a special
2722 The path names ``default`` and ``default-push`` have a special
2723 meaning. When performing a push or pull operation, they are used
2723 meaning. When performing a push or pull operation, they are used
2724 as fallbacks if no location is specified on the command-line.
2724 as fallbacks if no location is specified on the command-line.
2725 When ``default-push`` is set, it will be used for push and
2725 When ``default-push`` is set, it will be used for push and
2726 ``default`` will be used for pull; otherwise ``default`` is used
2726 ``default`` will be used for pull; otherwise ``default`` is used
2727 as the fallback for both. When cloning a repository, the clone
2727 as the fallback for both. When cloning a repository, the clone
2728 source is written as ``default`` in ``.hg/hgrc``. Note that
2728 source is written as ``default`` in ``.hg/hgrc``. Note that
2729 ``default`` and ``default-push`` apply to all inbound (e.g.
2729 ``default`` and ``default-push`` apply to all inbound (e.g.
2730 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2730 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2731 :hg:`bundle`) operations.
2731 :hg:`bundle`) operations.
2732
2732
2733 See :hg:`help urls` for more information.
2733 See :hg:`help urls` for more information.
2734
2734
2735 Returns 0 on success.
2735 Returns 0 on success.
2736 """
2736 """
2737 if search:
2737 if search:
2738 for name, path in ui.configitems("paths"):
2738 for name, path in ui.configitems("paths"):
2739 if name == search:
2739 if name == search:
2740 ui.write("%s\n" % url.hidepassword(path))
2740 ui.write("%s\n" % url.hidepassword(path))
2741 return
2741 return
2742 ui.warn(_("not found!\n"))
2742 ui.warn(_("not found!\n"))
2743 return 1
2743 return 1
2744 else:
2744 else:
2745 for name, path in ui.configitems("paths"):
2745 for name, path in ui.configitems("paths"):
2746 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2746 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2747
2747
2748 def postincoming(ui, repo, modheads, optupdate, checkout):
2748 def postincoming(ui, repo, modheads, optupdate, checkout):
2749 if modheads == 0:
2749 if modheads == 0:
2750 return
2750 return
2751 if optupdate:
2751 if optupdate:
2752 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2752 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2753 return hg.update(repo, checkout)
2753 return hg.update(repo, checkout)
2754 else:
2754 else:
2755 ui.status(_("not updating, since new heads added\n"))
2755 ui.status(_("not updating, since new heads added\n"))
2756 if modheads > 1:
2756 if modheads > 1:
2757 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2757 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2758 else:
2758 else:
2759 ui.status(_("(run 'hg update' to get a working copy)\n"))
2759 ui.status(_("(run 'hg update' to get a working copy)\n"))
2760
2760
2761 def pull(ui, repo, source="default", **opts):
2761 def pull(ui, repo, source="default", **opts):
2762 """pull changes from the specified source
2762 """pull changes from the specified source
2763
2763
2764 Pull changes from a remote repository to a local one.
2764 Pull changes from a remote repository to a local one.
2765
2765
2766 This finds all changes from the repository at the specified path
2766 This finds all changes from the repository at the specified path
2767 or URL and adds them to a local repository (the current one unless
2767 or URL and adds them to a local repository (the current one unless
2768 -R is specified). By default, this does not update the copy of the
2768 -R is specified). By default, this does not update the copy of the
2769 project in the working directory.
2769 project in the working directory.
2770
2770
2771 Use :hg:`incoming` if you want to see what would have been added
2771 Use :hg:`incoming` if you want to see what would have been added
2772 by a pull at the time you issued this command. If you then decide
2772 by a pull at the time you issued this command. If you then decide
2773 to add those changes to the repository, you should use :hg:`pull
2773 to add those changes to the repository, you should use :hg:`pull
2774 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2774 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2775
2775
2776 If SOURCE is omitted, the 'default' path will be used.
2776 If SOURCE is omitted, the 'default' path will be used.
2777 See :hg:`help urls` for more information.
2777 See :hg:`help urls` for more information.
2778
2778
2779 Returns 0 on success, 1 if an update had unresolved files.
2779 Returns 0 on success, 1 if an update had unresolved files.
2780 """
2780 """
2781 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2781 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2782 other = hg.repository(hg.remoteui(repo, opts), source)
2782 other = hg.repository(hg.remoteui(repo, opts), source)
2783 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2783 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2784 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2784 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2785 if revs:
2785 if revs:
2786 try:
2786 try:
2787 revs = [other.lookup(rev) for rev in revs]
2787 revs = [other.lookup(rev) for rev in revs]
2788 except error.CapabilityError:
2788 except error.CapabilityError:
2789 err = _("Other repository doesn't support revision lookup, "
2789 err = _("Other repository doesn't support revision lookup, "
2790 "so a rev cannot be specified.")
2790 "so a rev cannot be specified.")
2791 raise util.Abort(err)
2791 raise util.Abort(err)
2792
2792
2793 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2793 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2794 if checkout:
2794 if checkout:
2795 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2795 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2796 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2796 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2797
2797
2798 def push(ui, repo, dest=None, **opts):
2798 def push(ui, repo, dest=None, **opts):
2799 """push changes to the specified destination
2799 """push changes to the specified destination
2800
2800
2801 Push changesets from the local repository to the specified
2801 Push changesets from the local repository to the specified
2802 destination.
2802 destination.
2803
2803
2804 This operation is symmetrical to pull: it is identical to a pull
2804 This operation is symmetrical to pull: it is identical to a pull
2805 in the destination repository from the current one.
2805 in the destination repository from the current one.
2806
2806
2807 By default, push will not allow creation of new heads at the
2807 By default, push will not allow creation of new heads at the
2808 destination, since multiple heads would make it unclear which head
2808 destination, since multiple heads would make it unclear which head
2809 to use. In this situation, it is recommended to pull and merge
2809 to use. In this situation, it is recommended to pull and merge
2810 before pushing.
2810 before pushing.
2811
2811
2812 Use --new-branch if you want to allow push to create a new named
2812 Use --new-branch if you want to allow push to create a new named
2813 branch that is not present at the destination. This allows you to
2813 branch that is not present at the destination. This allows you to
2814 only create a new branch without forcing other changes.
2814 only create a new branch without forcing other changes.
2815
2815
2816 Use -f/--force to override the default behavior and push all
2816 Use -f/--force to override the default behavior and push all
2817 changesets on all branches.
2817 changesets on all branches.
2818
2818
2819 If -r/--rev is used, the specified revision and all its ancestors
2819 If -r/--rev is used, the specified revision and all its ancestors
2820 will be pushed to the remote repository.
2820 will be pushed to the remote repository.
2821
2821
2822 Please see :hg:`help urls` for important details about ``ssh://``
2822 Please see :hg:`help urls` for important details about ``ssh://``
2823 URLs. If DESTINATION is omitted, a default path will be used.
2823 URLs. If DESTINATION is omitted, a default path will be used.
2824
2824
2825 Returns 0 if push was successful, 1 if nothing to push.
2825 Returns 0 if push was successful, 1 if nothing to push.
2826 """
2826 """
2827 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2827 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2828 dest, branches = hg.parseurl(dest, opts.get('branch'))
2828 dest, branches = hg.parseurl(dest, opts.get('branch'))
2829 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2829 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2830 other = hg.repository(hg.remoteui(repo, opts), dest)
2830 other = hg.repository(hg.remoteui(repo, opts), dest)
2831 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2831 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2832 if revs:
2832 if revs:
2833 revs = [repo.lookup(rev) for rev in revs]
2833 revs = [repo.lookup(rev) for rev in revs]
2834
2834
2835 # push subrepos depth-first for coherent ordering
2835 # push subrepos depth-first for coherent ordering
2836 c = repo['']
2836 c = repo['']
2837 subs = c.substate # only repos that are committed
2837 subs = c.substate # only repos that are committed
2838 for s in sorted(subs):
2838 for s in sorted(subs):
2839 if not c.sub(s).push(opts.get('force')):
2839 if not c.sub(s).push(opts.get('force')):
2840 return False
2840 return False
2841
2841
2842 r = repo.push(other, opts.get('force'), revs=revs,
2842 r = repo.push(other, opts.get('force'), revs=revs,
2843 newbranch=opts.get('new_branch'))
2843 newbranch=opts.get('new_branch'))
2844 return r == 0
2844 return r == 0
2845
2845
2846 def recover(ui, repo):
2846 def recover(ui, repo):
2847 """roll back an interrupted transaction
2847 """roll back an interrupted transaction
2848
2848
2849 Recover from an interrupted commit or pull.
2849 Recover from an interrupted commit or pull.
2850
2850
2851 This command tries to fix the repository status after an
2851 This command tries to fix the repository status after an
2852 interrupted operation. It should only be necessary when Mercurial
2852 interrupted operation. It should only be necessary when Mercurial
2853 suggests it.
2853 suggests it.
2854
2854
2855 Returns 0 if successful, 1 if nothing to recover or verify fails.
2855 Returns 0 if successful, 1 if nothing to recover or verify fails.
2856 """
2856 """
2857 if repo.recover():
2857 if repo.recover():
2858 return hg.verify(repo)
2858 return hg.verify(repo)
2859 return 1
2859 return 1
2860
2860
2861 def remove(ui, repo, *pats, **opts):
2861 def remove(ui, repo, *pats, **opts):
2862 """remove the specified files on the next commit
2862 """remove the specified files on the next commit
2863
2863
2864 Schedule the indicated files for removal from the repository.
2864 Schedule the indicated files for removal from the repository.
2865
2865
2866 This only removes files from the current branch, not from the
2866 This only removes files from the current branch, not from the
2867 entire project history. -A/--after can be used to remove only
2867 entire project history. -A/--after can be used to remove only
2868 files that have already been deleted, -f/--force can be used to
2868 files that have already been deleted, -f/--force can be used to
2869 force deletion, and -Af can be used to remove files from the next
2869 force deletion, and -Af can be used to remove files from the next
2870 revision without deleting them from the working directory.
2870 revision without deleting them from the working directory.
2871
2871
2872 The following table details the behavior of remove for different
2872 The following table details the behavior of remove for different
2873 file states (columns) and option combinations (rows). The file
2873 file states (columns) and option combinations (rows). The file
2874 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2874 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2875 reported by :hg:`status`). The actions are Warn, Remove (from
2875 reported by :hg:`status`). The actions are Warn, Remove (from
2876 branch) and Delete (from disk)::
2876 branch) and Delete (from disk)::
2877
2877
2878 A C M !
2878 A C M !
2879 none W RD W R
2879 none W RD W R
2880 -f R RD RD R
2880 -f R RD RD R
2881 -A W W W R
2881 -A W W W R
2882 -Af R R R R
2882 -Af R R R R
2883
2883
2884 This command schedules the files to be removed at the next commit.
2884 This command schedules the files to be removed at the next commit.
2885 To undo a remove before that, see :hg:`revert`.
2885 To undo a remove before that, see :hg:`revert`.
2886
2886
2887 Returns 0 on success, 1 if any warnings encountered.
2887 Returns 0 on success, 1 if any warnings encountered.
2888 """
2888 """
2889
2889
2890 ret = 0
2890 ret = 0
2891 after, force = opts.get('after'), opts.get('force')
2891 after, force = opts.get('after'), opts.get('force')
2892 if not pats and not after:
2892 if not pats and not after:
2893 raise util.Abort(_('no files specified'))
2893 raise util.Abort(_('no files specified'))
2894
2894
2895 m = cmdutil.match(repo, pats, opts)
2895 m = cmdutil.match(repo, pats, opts)
2896 s = repo.status(match=m, clean=True)
2896 s = repo.status(match=m, clean=True)
2897 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2897 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2898
2898
2899 for f in m.files():
2899 for f in m.files():
2900 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2900 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2901 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2901 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2902 ret = 1
2902 ret = 1
2903
2903
2904 def warn(files, reason):
2904 def warn(files, reason):
2905 for f in files:
2905 for f in files:
2906 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2906 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2907 % (m.rel(f), reason))
2907 % (m.rel(f), reason))
2908 ret = 1
2908 ret = 1
2909
2909
2910 if force:
2910 if force:
2911 remove, forget = modified + deleted + clean, added
2911 remove, forget = modified + deleted + clean, added
2912 elif after:
2912 elif after:
2913 remove, forget = deleted, []
2913 remove, forget = deleted, []
2914 warn(modified + added + clean, _('still exists'))
2914 warn(modified + added + clean, _('still exists'))
2915 else:
2915 else:
2916 remove, forget = deleted + clean, []
2916 remove, forget = deleted + clean, []
2917 warn(modified, _('is modified'))
2917 warn(modified, _('is modified'))
2918 warn(added, _('has been marked for add'))
2918 warn(added, _('has been marked for add'))
2919
2919
2920 for f in sorted(remove + forget):
2920 for f in sorted(remove + forget):
2921 if ui.verbose or not m.exact(f):
2921 if ui.verbose or not m.exact(f):
2922 ui.status(_('removing %s\n') % m.rel(f))
2922 ui.status(_('removing %s\n') % m.rel(f))
2923
2923
2924 repo[None].forget(forget)
2924 repo[None].forget(forget)
2925 repo[None].remove(remove, unlink=not after)
2925 repo[None].remove(remove, unlink=not after)
2926 return ret
2926 return ret
2927
2927
2928 def rename(ui, repo, *pats, **opts):
2928 def rename(ui, repo, *pats, **opts):
2929 """rename files; equivalent of copy + remove
2929 """rename files; equivalent of copy + remove
2930
2930
2931 Mark dest as copies of sources; mark sources for deletion. If dest
2931 Mark dest as copies of sources; mark sources for deletion. If dest
2932 is a directory, copies are put in that directory. If dest is a
2932 is a directory, copies are put in that directory. If dest is a
2933 file, there can only be one source.
2933 file, there can only be one source.
2934
2934
2935 By default, this command copies the contents of files as they
2935 By default, this command copies the contents of files as they
2936 exist in the working directory. If invoked with -A/--after, the
2936 exist in the working directory. If invoked with -A/--after, the
2937 operation is recorded, but no copying is performed.
2937 operation is recorded, but no copying is performed.
2938
2938
2939 This command takes effect at the next commit. To undo a rename
2939 This command takes effect at the next commit. To undo a rename
2940 before that, see :hg:`revert`.
2940 before that, see :hg:`revert`.
2941
2941
2942 Returns 0 on success, 1 if errors are encountered.
2942 Returns 0 on success, 1 if errors are encountered.
2943 """
2943 """
2944 wlock = repo.wlock(False)
2944 wlock = repo.wlock(False)
2945 try:
2945 try:
2946 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2946 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2947 finally:
2947 finally:
2948 wlock.release()
2948 wlock.release()
2949
2949
2950 def resolve(ui, repo, *pats, **opts):
2950 def resolve(ui, repo, *pats, **opts):
2951 """various operations to help finish a merge
2951 """various operations to help finish a merge
2952
2952
2953 This command includes several actions that are often useful while
2953 This command includes several actions that are often useful while
2954 performing a merge, after running ``merge`` but before running
2954 performing a merge, after running ``merge`` but before running
2955 ``commit``. (It is only meaningful if your working directory has
2955 ``commit``. (It is only meaningful if your working directory has
2956 two parents.) It is most relevant for merges with unresolved
2956 two parents.) It is most relevant for merges with unresolved
2957 conflicts, which are typically a result of non-interactive merging with
2957 conflicts, which are typically a result of non-interactive merging with
2958 ``internal:merge`` or a command-line merge tool like ``diff3``.
2958 ``internal:merge`` or a command-line merge tool like ``diff3``.
2959
2959
2960 The available actions are:
2960 The available actions are:
2961
2961
2962 1) list files that were merged with conflicts (U, for unresolved)
2962 1) list files that were merged with conflicts (U, for unresolved)
2963 and without conflicts (R, for resolved): ``hg resolve -l``
2963 and without conflicts (R, for resolved): ``hg resolve -l``
2964 (this is like ``status`` for merges)
2964 (this is like ``status`` for merges)
2965 2) record that you have resolved conflicts in certain files:
2965 2) record that you have resolved conflicts in certain files:
2966 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2966 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2967 3) forget that you have resolved conflicts in certain files:
2967 3) forget that you have resolved conflicts in certain files:
2968 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2968 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2969 4) discard your current attempt(s) at resolving conflicts and
2969 4) discard your current attempt(s) at resolving conflicts and
2970 restart the merge from scratch: ``hg resolve file...``
2970 restart the merge from scratch: ``hg resolve file...``
2971 (or ``-a`` for all unresolved files)
2971 (or ``-a`` for all unresolved files)
2972
2972
2973 Note that Mercurial will not let you commit files with unresolved merge
2973 Note that Mercurial will not let you commit files with unresolved merge
2974 conflicts. You must use ``hg resolve -m ...`` before you can commit
2974 conflicts. You must use ``hg resolve -m ...`` before you can commit
2975 after a conflicting merge.
2975 after a conflicting merge.
2976
2976
2977 Returns 0 on success, 1 if any files fail a resolve attempt.
2977 Returns 0 on success, 1 if any files fail a resolve attempt.
2978 """
2978 """
2979
2979
2980 all, mark, unmark, show, nostatus = \
2980 all, mark, unmark, show, nostatus = \
2981 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2981 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2982
2982
2983 if (show and (mark or unmark)) or (mark and unmark):
2983 if (show and (mark or unmark)) or (mark and unmark):
2984 raise util.Abort(_("too many options specified"))
2984 raise util.Abort(_("too many options specified"))
2985 if pats and all:
2985 if pats and all:
2986 raise util.Abort(_("can't specify --all and patterns"))
2986 raise util.Abort(_("can't specify --all and patterns"))
2987 if not (all or pats or show or mark or unmark):
2987 if not (all or pats or show or mark or unmark):
2988 raise util.Abort(_('no files or directories specified; '
2988 raise util.Abort(_('no files or directories specified; '
2989 'use --all to remerge all files'))
2989 'use --all to remerge all files'))
2990
2990
2991 ms = mergemod.mergestate(repo)
2991 ms = mergemod.mergestate(repo)
2992 m = cmdutil.match(repo, pats, opts)
2992 m = cmdutil.match(repo, pats, opts)
2993 ret = 0
2993 ret = 0
2994
2994
2995 for f in ms:
2995 for f in ms:
2996 if m(f):
2996 if m(f):
2997 if show:
2997 if show:
2998 if nostatus:
2998 if nostatus:
2999 ui.write("%s\n" % f)
2999 ui.write("%s\n" % f)
3000 else:
3000 else:
3001 ui.write("%s %s\n" % (ms[f].upper(), f),
3001 ui.write("%s %s\n" % (ms[f].upper(), f),
3002 label='resolve.' +
3002 label='resolve.' +
3003 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3003 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3004 elif mark:
3004 elif mark:
3005 ms.mark(f, "r")
3005 ms.mark(f, "r")
3006 elif unmark:
3006 elif unmark:
3007 ms.mark(f, "u")
3007 ms.mark(f, "u")
3008 else:
3008 else:
3009 wctx = repo[None]
3009 wctx = repo[None]
3010 mctx = wctx.parents()[-1]
3010 mctx = wctx.parents()[-1]
3011
3011
3012 # backup pre-resolve (merge uses .orig for its own purposes)
3012 # backup pre-resolve (merge uses .orig for its own purposes)
3013 a = repo.wjoin(f)
3013 a = repo.wjoin(f)
3014 util.copyfile(a, a + ".resolve")
3014 util.copyfile(a, a + ".resolve")
3015
3015
3016 # resolve file
3016 # resolve file
3017 if ms.resolve(f, wctx, mctx):
3017 if ms.resolve(f, wctx, mctx):
3018 ret = 1
3018 ret = 1
3019
3019
3020 # replace filemerge's .orig file with our resolve file
3020 # replace filemerge's .orig file with our resolve file
3021 util.rename(a + ".resolve", a + ".orig")
3021 util.rename(a + ".resolve", a + ".orig")
3022 return ret
3022 return ret
3023
3023
3024 def revert(ui, repo, *pats, **opts):
3024 def revert(ui, repo, *pats, **opts):
3025 """restore individual files or directories to an earlier state
3025 """restore individual files or directories to an earlier state
3026
3026
3027 NOTE: This command is most likely not what you are looking for. revert
3027 NOTE: This command is most likely not what you are looking for. revert
3028 will partially overwrite content in the working directory without changing
3028 will partially overwrite content in the working directory without changing
3029 the working directory parents. Use :hg:`update -r rev` to check out earlier
3029 the working directory parents. Use :hg:`update -r rev` to check out earlier
3030 revisions, or :hg:`update --clean .` to undo a merge which has added
3030 revisions, or :hg:`update --clean .` to undo a merge which has added
3031 another parent.
3031 another parent.
3032
3032
3033 With no revision specified, revert the named files or directories
3033 With no revision specified, revert the named files or directories
3034 to the contents they had in the parent of the working directory.
3034 to the contents they had in the parent of the working directory.
3035 This restores the contents of the affected files to an unmodified
3035 This restores the contents of the affected files to an unmodified
3036 state and unschedules adds, removes, copies, and renames. If the
3036 state and unschedules adds, removes, copies, and renames. If the
3037 working directory has two parents, you must explicitly specify a
3037 working directory has two parents, you must explicitly specify a
3038 revision.
3038 revision.
3039
3039
3040 Using the -r/--rev option, revert the given files or directories
3040 Using the -r/--rev option, revert the given files or directories
3041 to their contents as of a specific revision. This can be helpful
3041 to their contents as of a specific revision. This can be helpful
3042 to "roll back" some or all of an earlier change. See :hg:`help
3042 to "roll back" some or all of an earlier change. See :hg:`help
3043 dates` for a list of formats valid for -d/--date.
3043 dates` for a list of formats valid for -d/--date.
3044
3044
3045 Revert modifies the working directory. It does not commit any
3045 Revert modifies the working directory. It does not commit any
3046 changes, or change the parent of the working directory. If you
3046 changes, or change the parent of the working directory. If you
3047 revert to a revision other than the parent of the working
3047 revert to a revision other than the parent of the working
3048 directory, the reverted files will thus appear modified
3048 directory, the reverted files will thus appear modified
3049 afterwards.
3049 afterwards.
3050
3050
3051 If a file has been deleted, it is restored. If the executable mode
3051 If a file has been deleted, it is restored. If the executable mode
3052 of a file was changed, it is reset.
3052 of a file was changed, it is reset.
3053
3053
3054 If names are given, all files matching the names are reverted.
3054 If names are given, all files matching the names are reverted.
3055 If no arguments are given, no files are reverted.
3055 If no arguments are given, no files are reverted.
3056
3056
3057 Modified files are saved with a .orig suffix before reverting.
3057 Modified files are saved with a .orig suffix before reverting.
3058 To disable these backups, use --no-backup.
3058 To disable these backups, use --no-backup.
3059
3059
3060 Returns 0 on success.
3060 Returns 0 on success.
3061 """
3061 """
3062
3062
3063 if opts["date"]:
3063 if opts["date"]:
3064 if opts["rev"]:
3064 if opts["rev"]:
3065 raise util.Abort(_("you can't specify a revision and a date"))
3065 raise util.Abort(_("you can't specify a revision and a date"))
3066 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3066 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3067
3067
3068 if not pats and not opts.get('all'):
3068 if not pats and not opts.get('all'):
3069 raise util.Abort(_('no files or directories specified; '
3069 raise util.Abort(_('no files or directories specified; '
3070 'use --all to revert the whole repo'))
3070 'use --all to revert the whole repo'))
3071
3071
3072 parent, p2 = repo.dirstate.parents()
3072 parent, p2 = repo.dirstate.parents()
3073 if not opts.get('rev') and p2 != nullid:
3073 if not opts.get('rev') and p2 != nullid:
3074 raise util.Abort(_('uncommitted merge - please provide a '
3074 raise util.Abort(_('uncommitted merge - please provide a '
3075 'specific revision'))
3075 'specific revision'))
3076 ctx = repo[opts.get('rev')]
3076 ctx = repo[opts.get('rev')]
3077 node = ctx.node()
3077 node = ctx.node()
3078 mf = ctx.manifest()
3078 mf = ctx.manifest()
3079 if node == parent:
3079 if node == parent:
3080 pmf = mf
3080 pmf = mf
3081 else:
3081 else:
3082 pmf = None
3082 pmf = None
3083
3083
3084 # need all matching names in dirstate and manifest of target rev,
3084 # need all matching names in dirstate and manifest of target rev,
3085 # so have to walk both. do not print errors if files exist in one
3085 # so have to walk both. do not print errors if files exist in one
3086 # but not other.
3086 # but not other.
3087
3087
3088 names = {}
3088 names = {}
3089
3089
3090 wlock = repo.wlock()
3090 wlock = repo.wlock()
3091 try:
3091 try:
3092 # walk dirstate.
3092 # walk dirstate.
3093
3093
3094 m = cmdutil.match(repo, pats, opts)
3094 m = cmdutil.match(repo, pats, opts)
3095 m.bad = lambda x, y: False
3095 m.bad = lambda x, y: False
3096 for abs in repo.walk(m):
3096 for abs in repo.walk(m):
3097 names[abs] = m.rel(abs), m.exact(abs)
3097 names[abs] = m.rel(abs), m.exact(abs)
3098
3098
3099 # walk target manifest.
3099 # walk target manifest.
3100
3100
3101 def badfn(path, msg):
3101 def badfn(path, msg):
3102 if path in names:
3102 if path in names:
3103 return
3103 return
3104 path_ = path + '/'
3104 path_ = path + '/'
3105 for f in names:
3105 for f in names:
3106 if f.startswith(path_):
3106 if f.startswith(path_):
3107 return
3107 return
3108 ui.warn("%s: %s\n" % (m.rel(path), msg))
3108 ui.warn("%s: %s\n" % (m.rel(path), msg))
3109
3109
3110 m = cmdutil.match(repo, pats, opts)
3110 m = cmdutil.match(repo, pats, opts)
3111 m.bad = badfn
3111 m.bad = badfn
3112 for abs in repo[node].walk(m):
3112 for abs in repo[node].walk(m):
3113 if abs not in names:
3113 if abs not in names:
3114 names[abs] = m.rel(abs), m.exact(abs)
3114 names[abs] = m.rel(abs), m.exact(abs)
3115
3115
3116 m = cmdutil.matchfiles(repo, names)
3116 m = cmdutil.matchfiles(repo, names)
3117 changes = repo.status(match=m)[:4]
3117 changes = repo.status(match=m)[:4]
3118 modified, added, removed, deleted = map(set, changes)
3118 modified, added, removed, deleted = map(set, changes)
3119
3119
3120 # if f is a rename, also revert the source
3120 # if f is a rename, also revert the source
3121 cwd = repo.getcwd()
3121 cwd = repo.getcwd()
3122 for f in added:
3122 for f in added:
3123 src = repo.dirstate.copied(f)
3123 src = repo.dirstate.copied(f)
3124 if src and src not in names and repo.dirstate[src] == 'r':
3124 if src and src not in names and repo.dirstate[src] == 'r':
3125 removed.add(src)
3125 removed.add(src)
3126 names[src] = (repo.pathto(src, cwd), True)
3126 names[src] = (repo.pathto(src, cwd), True)
3127
3127
3128 def removeforget(abs):
3128 def removeforget(abs):
3129 if repo.dirstate[abs] == 'a':
3129 if repo.dirstate[abs] == 'a':
3130 return _('forgetting %s\n')
3130 return _('forgetting %s\n')
3131 return _('removing %s\n')
3131 return _('removing %s\n')
3132
3132
3133 revert = ([], _('reverting %s\n'))
3133 revert = ([], _('reverting %s\n'))
3134 add = ([], _('adding %s\n'))
3134 add = ([], _('adding %s\n'))
3135 remove = ([], removeforget)
3135 remove = ([], removeforget)
3136 undelete = ([], _('undeleting %s\n'))
3136 undelete = ([], _('undeleting %s\n'))
3137
3137
3138 disptable = (
3138 disptable = (
3139 # dispatch table:
3139 # dispatch table:
3140 # file state
3140 # file state
3141 # action if in target manifest
3141 # action if in target manifest
3142 # action if not in target manifest
3142 # action if not in target manifest
3143 # make backup if in target manifest
3143 # make backup if in target manifest
3144 # make backup if not in target manifest
3144 # make backup if not in target manifest
3145 (modified, revert, remove, True, True),
3145 (modified, revert, remove, True, True),
3146 (added, revert, remove, True, False),
3146 (added, revert, remove, True, False),
3147 (removed, undelete, None, False, False),
3147 (removed, undelete, None, False, False),
3148 (deleted, revert, remove, False, False),
3148 (deleted, revert, remove, False, False),
3149 )
3149 )
3150
3150
3151 for abs, (rel, exact) in sorted(names.items()):
3151 for abs, (rel, exact) in sorted(names.items()):
3152 mfentry = mf.get(abs)
3152 mfentry = mf.get(abs)
3153 target = repo.wjoin(abs)
3153 target = repo.wjoin(abs)
3154 def handle(xlist, dobackup):
3154 def handle(xlist, dobackup):
3155 xlist[0].append(abs)
3155 xlist[0].append(abs)
3156 if dobackup and not opts.get('no_backup') and util.lexists(target):
3156 if dobackup and not opts.get('no_backup') and util.lexists(target):
3157 bakname = "%s.orig" % rel
3157 bakname = "%s.orig" % rel
3158 ui.note(_('saving current version of %s as %s\n') %
3158 ui.note(_('saving current version of %s as %s\n') %
3159 (rel, bakname))
3159 (rel, bakname))
3160 if not opts.get('dry_run'):
3160 if not opts.get('dry_run'):
3161 util.rename(target, bakname)
3161 util.rename(target, bakname)
3162 if ui.verbose or not exact:
3162 if ui.verbose or not exact:
3163 msg = xlist[1]
3163 msg = xlist[1]
3164 if not isinstance(msg, basestring):
3164 if not isinstance(msg, basestring):
3165 msg = msg(abs)
3165 msg = msg(abs)
3166 ui.status(msg % rel)
3166 ui.status(msg % rel)
3167 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3167 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3168 if abs not in table:
3168 if abs not in table:
3169 continue
3169 continue
3170 # file has changed in dirstate
3170 # file has changed in dirstate
3171 if mfentry:
3171 if mfentry:
3172 handle(hitlist, backuphit)
3172 handle(hitlist, backuphit)
3173 elif misslist is not None:
3173 elif misslist is not None:
3174 handle(misslist, backupmiss)
3174 handle(misslist, backupmiss)
3175 break
3175 break
3176 else:
3176 else:
3177 if abs not in repo.dirstate:
3177 if abs not in repo.dirstate:
3178 if mfentry:
3178 if mfentry:
3179 handle(add, True)
3179 handle(add, True)
3180 elif exact:
3180 elif exact:
3181 ui.warn(_('file not managed: %s\n') % rel)
3181 ui.warn(_('file not managed: %s\n') % rel)
3182 continue
3182 continue
3183 # file has not changed in dirstate
3183 # file has not changed in dirstate
3184 if node == parent:
3184 if node == parent:
3185 if exact:
3185 if exact:
3186 ui.warn(_('no changes needed to %s\n') % rel)
3186 ui.warn(_('no changes needed to %s\n') % rel)
3187 continue
3187 continue
3188 if pmf is None:
3188 if pmf is None:
3189 # only need parent manifest in this unlikely case,
3189 # only need parent manifest in this unlikely case,
3190 # so do not read by default
3190 # so do not read by default
3191 pmf = repo[parent].manifest()
3191 pmf = repo[parent].manifest()
3192 if abs in pmf:
3192 if abs in pmf:
3193 if mfentry:
3193 if mfentry:
3194 # if version of file is same in parent and target
3194 # if version of file is same in parent and target
3195 # manifests, do nothing
3195 # manifests, do nothing
3196 if (pmf[abs] != mfentry or
3196 if (pmf[abs] != mfentry or
3197 pmf.flags(abs) != mf.flags(abs)):
3197 pmf.flags(abs) != mf.flags(abs)):
3198 handle(revert, False)
3198 handle(revert, False)
3199 else:
3199 else:
3200 handle(remove, False)
3200 handle(remove, False)
3201
3201
3202 if not opts.get('dry_run'):
3202 if not opts.get('dry_run'):
3203 def checkout(f):
3203 def checkout(f):
3204 fc = ctx[f]
3204 fc = ctx[f]
3205 repo.wwrite(f, fc.data(), fc.flags())
3205 repo.wwrite(f, fc.data(), fc.flags())
3206
3206
3207 audit_path = util.path_auditor(repo.root)
3207 audit_path = util.path_auditor(repo.root)
3208 for f in remove[0]:
3208 for f in remove[0]:
3209 if repo.dirstate[f] == 'a':
3209 if repo.dirstate[f] == 'a':
3210 repo.dirstate.forget(f)
3210 repo.dirstate.forget(f)
3211 continue
3211 continue
3212 audit_path(f)
3212 audit_path(f)
3213 try:
3213 try:
3214 util.unlink(repo.wjoin(f))
3214 util.unlink(repo.wjoin(f))
3215 except OSError:
3215 except OSError:
3216 pass
3216 pass
3217 repo.dirstate.remove(f)
3217 repo.dirstate.remove(f)
3218
3218
3219 normal = None
3219 normal = None
3220 if node == parent:
3220 if node == parent:
3221 # We're reverting to our parent. If possible, we'd like status
3221 # We're reverting to our parent. If possible, we'd like status
3222 # to report the file as clean. We have to use normallookup for
3222 # to report the file as clean. We have to use normallookup for
3223 # merges to avoid losing information about merged/dirty files.
3223 # merges to avoid losing information about merged/dirty files.
3224 if p2 != nullid:
3224 if p2 != nullid:
3225 normal = repo.dirstate.normallookup
3225 normal = repo.dirstate.normallookup
3226 else:
3226 else:
3227 normal = repo.dirstate.normal
3227 normal = repo.dirstate.normal
3228 for f in revert[0]:
3228 for f in revert[0]:
3229 checkout(f)
3229 checkout(f)
3230 if normal:
3230 if normal:
3231 normal(f)
3231 normal(f)
3232
3232
3233 for f in add[0]:
3233 for f in add[0]:
3234 checkout(f)
3234 checkout(f)
3235 repo.dirstate.add(f)
3235 repo.dirstate.add(f)
3236
3236
3237 normal = repo.dirstate.normallookup
3237 normal = repo.dirstate.normallookup
3238 if node == parent and p2 == nullid:
3238 if node == parent and p2 == nullid:
3239 normal = repo.dirstate.normal
3239 normal = repo.dirstate.normal
3240 for f in undelete[0]:
3240 for f in undelete[0]:
3241 checkout(f)
3241 checkout(f)
3242 normal(f)
3242 normal(f)
3243
3243
3244 finally:
3244 finally:
3245 wlock.release()
3245 wlock.release()
3246
3246
3247 def rollback(ui, repo, **opts):
3247 def rollback(ui, repo, **opts):
3248 """roll back the last transaction (dangerous)
3248 """roll back the last transaction (dangerous)
3249
3249
3250 This command should be used with care. There is only one level of
3250 This command should be used with care. There is only one level of
3251 rollback, and there is no way to undo a rollback. It will also
3251 rollback, and there is no way to undo a rollback. It will also
3252 restore the dirstate at the time of the last transaction, losing
3252 restore the dirstate at the time of the last transaction, losing
3253 any dirstate changes since that time. This command does not alter
3253 any dirstate changes since that time. This command does not alter
3254 the working directory.
3254 the working directory.
3255
3255
3256 Transactions are used to encapsulate the effects of all commands
3256 Transactions are used to encapsulate the effects of all commands
3257 that create new changesets or propagate existing changesets into a
3257 that create new changesets or propagate existing changesets into a
3258 repository. For example, the following commands are transactional,
3258 repository. For example, the following commands are transactional,
3259 and their effects can be rolled back:
3259 and their effects can be rolled back:
3260
3260
3261 - commit
3261 - commit
3262 - import
3262 - import
3263 - pull
3263 - pull
3264 - push (with this repository as the destination)
3264 - push (with this repository as the destination)
3265 - unbundle
3265 - unbundle
3266
3266
3267 This command is not intended for use on public repositories. Once
3267 This command is not intended for use on public repositories. Once
3268 changes are visible for pull by other users, rolling a transaction
3268 changes are visible for pull by other users, rolling a transaction
3269 back locally is ineffective (someone else may already have pulled
3269 back locally is ineffective (someone else may already have pulled
3270 the changes). Furthermore, a race is possible with readers of the
3270 the changes). Furthermore, a race is possible with readers of the
3271 repository; for example an in-progress pull from the repository
3271 repository; for example an in-progress pull from the repository
3272 may fail if a rollback is performed.
3272 may fail if a rollback is performed.
3273
3273
3274 Returns 0 on success, 1 if no rollback data is available.
3274 Returns 0 on success, 1 if no rollback data is available.
3275 """
3275 """
3276 return repo.rollback(opts.get('dry_run'))
3276 return repo.rollback(opts.get('dry_run'))
3277
3277
3278 def root(ui, repo):
3278 def root(ui, repo):
3279 """print the root (top) of the current working directory
3279 """print the root (top) of the current working directory
3280
3280
3281 Print the root directory of the current repository.
3281 Print the root directory of the current repository.
3282
3282
3283 Returns 0 on success.
3283 Returns 0 on success.
3284 """
3284 """
3285 ui.write(repo.root + "\n")
3285 ui.write(repo.root + "\n")
3286
3286
3287 def serve(ui, repo, **opts):
3287 def serve(ui, repo, **opts):
3288 """start stand-alone webserver
3288 """start stand-alone webserver
3289
3289
3290 Start a local HTTP repository browser and pull server. You can use
3290 Start a local HTTP repository browser and pull server. You can use
3291 this for ad-hoc sharing and browing of repositories. It is
3291 this for ad-hoc sharing and browing of repositories. It is
3292 recommended to use a real web server to serve a repository for
3292 recommended to use a real web server to serve a repository for
3293 longer periods of time.
3293 longer periods of time.
3294
3294
3295 Please note that the server does not implement access control.
3295 Please note that the server does not implement access control.
3296 This means that, by default, anybody can read from the server and
3296 This means that, by default, anybody can read from the server and
3297 nobody can write to it by default. Set the ``web.allow_push``
3297 nobody can write to it by default. Set the ``web.allow_push``
3298 option to ``*`` to allow everybody to push to the server. You
3298 option to ``*`` to allow everybody to push to the server. You
3299 should use a real web server if you need to authenticate users.
3299 should use a real web server if you need to authenticate users.
3300
3300
3301 By default, the server logs accesses to stdout and errors to
3301 By default, the server logs accesses to stdout and errors to
3302 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3302 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3303 files.
3303 files.
3304
3304
3305 To have the server choose a free port number to listen on, specify
3305 To have the server choose a free port number to listen on, specify
3306 a port number of 0; in this case, the server will print the port
3306 a port number of 0; in this case, the server will print the port
3307 number it uses.
3307 number it uses.
3308
3308
3309 Returns 0 on success.
3309 Returns 0 on success.
3310 """
3310 """
3311
3311
3312 if opts["stdio"]:
3312 if opts["stdio"]:
3313 if repo is None:
3313 if repo is None:
3314 raise error.RepoError(_("There is no Mercurial repository here"
3314 raise error.RepoError(_("There is no Mercurial repository here"
3315 " (.hg not found)"))
3315 " (.hg not found)"))
3316 s = sshserver.sshserver(ui, repo)
3316 s = sshserver.sshserver(ui, repo)
3317 s.serve_forever()
3317 s.serve_forever()
3318
3318
3319 # this way we can check if something was given in the command-line
3319 # this way we can check if something was given in the command-line
3320 if opts.get('port'):
3320 if opts.get('port'):
3321 opts['port'] = int(opts.get('port'))
3321 opts['port'] = int(opts.get('port'))
3322
3322
3323 baseui = repo and repo.baseui or ui
3323 baseui = repo and repo.baseui or ui
3324 optlist = ("name templates style address port prefix ipv6"
3324 optlist = ("name templates style address port prefix ipv6"
3325 " accesslog errorlog certificate encoding")
3325 " accesslog errorlog certificate encoding")
3326 for o in optlist.split():
3326 for o in optlist.split():
3327 val = opts.get(o, '')
3327 val = opts.get(o, '')
3328 if val in (None, ''): # should check against default options instead
3328 if val in (None, ''): # should check against default options instead
3329 continue
3329 continue
3330 baseui.setconfig("web", o, val)
3330 baseui.setconfig("web", o, val)
3331 if repo and repo.ui != baseui:
3331 if repo and repo.ui != baseui:
3332 repo.ui.setconfig("web", o, val)
3332 repo.ui.setconfig("web", o, val)
3333
3333
3334 o = opts.get('web_conf') or opts.get('webdir_conf')
3334 o = opts.get('web_conf') or opts.get('webdir_conf')
3335 if not o:
3335 if not o:
3336 if not repo:
3336 if not repo:
3337 raise error.RepoError(_("There is no Mercurial repository"
3337 raise error.RepoError(_("There is no Mercurial repository"
3338 " here (.hg not found)"))
3338 " here (.hg not found)"))
3339 o = repo.root
3339 o = repo.root
3340
3340
3341 app = hgweb.hgweb(o, baseui=ui)
3341 app = hgweb.hgweb(o, baseui=ui)
3342
3342
3343 class service(object):
3343 class service(object):
3344 def init(self):
3344 def init(self):
3345 util.set_signal_handler()
3345 util.set_signal_handler()
3346 self.httpd = hgweb.server.create_server(ui, app)
3346 self.httpd = hgweb.server.create_server(ui, app)
3347
3347
3348 if opts['port'] and not ui.verbose:
3348 if opts['port'] and not ui.verbose:
3349 return
3349 return
3350
3350
3351 if self.httpd.prefix:
3351 if self.httpd.prefix:
3352 prefix = self.httpd.prefix.strip('/') + '/'
3352 prefix = self.httpd.prefix.strip('/') + '/'
3353 else:
3353 else:
3354 prefix = ''
3354 prefix = ''
3355
3355
3356 port = ':%d' % self.httpd.port
3356 port = ':%d' % self.httpd.port
3357 if port == ':80':
3357 if port == ':80':
3358 port = ''
3358 port = ''
3359
3359
3360 bindaddr = self.httpd.addr
3360 bindaddr = self.httpd.addr
3361 if bindaddr == '0.0.0.0':
3361 if bindaddr == '0.0.0.0':
3362 bindaddr = '*'
3362 bindaddr = '*'
3363 elif ':' in bindaddr: # IPv6
3363 elif ':' in bindaddr: # IPv6
3364 bindaddr = '[%s]' % bindaddr
3364 bindaddr = '[%s]' % bindaddr
3365
3365
3366 fqaddr = self.httpd.fqaddr
3366 fqaddr = self.httpd.fqaddr
3367 if ':' in fqaddr:
3367 if ':' in fqaddr:
3368 fqaddr = '[%s]' % fqaddr
3368 fqaddr = '[%s]' % fqaddr
3369 if opts['port']:
3369 if opts['port']:
3370 write = ui.status
3370 write = ui.status
3371 else:
3371 else:
3372 write = ui.write
3372 write = ui.write
3373 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3373 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3374 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3374 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3375
3375
3376 def run(self):
3376 def run(self):
3377 self.httpd.serve_forever()
3377 self.httpd.serve_forever()
3378
3378
3379 service = service()
3379 service = service()
3380
3380
3381 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3381 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3382
3382
3383 def status(ui, repo, *pats, **opts):
3383 def status(ui, repo, *pats, **opts):
3384 """show changed files in the working directory
3384 """show changed files in the working directory
3385
3385
3386 Show status of files in the repository. If names are given, only
3386 Show status of files in the repository. If names are given, only
3387 files that match are shown. Files that are clean or ignored or
3387 files that match are shown. Files that are clean or ignored or
3388 the source of a copy/move operation, are not listed unless
3388 the source of a copy/move operation, are not listed unless
3389 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3389 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3390 Unless options described with "show only ..." are given, the
3390 Unless options described with "show only ..." are given, the
3391 options -mardu are used.
3391 options -mardu are used.
3392
3392
3393 Option -q/--quiet hides untracked (unknown and ignored) files
3393 Option -q/--quiet hides untracked (unknown and ignored) files
3394 unless explicitly requested with -u/--unknown or -i/--ignored.
3394 unless explicitly requested with -u/--unknown or -i/--ignored.
3395
3395
3396 NOTE: status may appear to disagree with diff if permissions have
3396 NOTE: status may appear to disagree with diff if permissions have
3397 changed or a merge has occurred. The standard diff format does not
3397 changed or a merge has occurred. The standard diff format does not
3398 report permission changes and diff only reports changes relative
3398 report permission changes and diff only reports changes relative
3399 to one merge parent.
3399 to one merge parent.
3400
3400
3401 If one revision is given, it is used as the base revision.
3401 If one revision is given, it is used as the base revision.
3402 If two revisions are given, the differences between them are
3402 If two revisions are given, the differences between them are
3403 shown. The --change option can also be used as a shortcut to list
3403 shown. The --change option can also be used as a shortcut to list
3404 the changed files of a revision from its first parent.
3404 the changed files of a revision from its first parent.
3405
3405
3406 The codes used to show the status of files are::
3406 The codes used to show the status of files are::
3407
3407
3408 M = modified
3408 M = modified
3409 A = added
3409 A = added
3410 R = removed
3410 R = removed
3411 C = clean
3411 C = clean
3412 ! = missing (deleted by non-hg command, but still tracked)
3412 ! = missing (deleted by non-hg command, but still tracked)
3413 ? = not tracked
3413 ? = not tracked
3414 I = ignored
3414 I = ignored
3415 = origin of the previous file listed as A (added)
3415 = origin of the previous file listed as A (added)
3416
3416
3417 Returns 0 on success.
3417 Returns 0 on success.
3418 """
3418 """
3419
3419
3420 revs = opts.get('rev')
3420 revs = opts.get('rev')
3421 change = opts.get('change')
3421 change = opts.get('change')
3422
3422
3423 if revs and change:
3423 if revs and change:
3424 msg = _('cannot specify --rev and --change at the same time')
3424 msg = _('cannot specify --rev and --change at the same time')
3425 raise util.Abort(msg)
3425 raise util.Abort(msg)
3426 elif change:
3426 elif change:
3427 node2 = repo.lookup(change)
3427 node2 = repo.lookup(change)
3428 node1 = repo[node2].parents()[0].node()
3428 node1 = repo[node2].parents()[0].node()
3429 else:
3429 else:
3430 node1, node2 = cmdutil.revpair(repo, revs)
3430 node1, node2 = cmdutil.revpair(repo, revs)
3431
3431
3432 cwd = (pats and repo.getcwd()) or ''
3432 cwd = (pats and repo.getcwd()) or ''
3433 end = opts.get('print0') and '\0' or '\n'
3433 end = opts.get('print0') and '\0' or '\n'
3434 copy = {}
3434 copy = {}
3435 states = 'modified added removed deleted unknown ignored clean'.split()
3435 states = 'modified added removed deleted unknown ignored clean'.split()
3436 show = [k for k in states if opts.get(k)]
3436 show = [k for k in states if opts.get(k)]
3437 if opts.get('all'):
3437 if opts.get('all'):
3438 show += ui.quiet and (states[:4] + ['clean']) or states
3438 show += ui.quiet and (states[:4] + ['clean']) or states
3439 if not show:
3439 if not show:
3440 show = ui.quiet and states[:4] or states[:5]
3440 show = ui.quiet and states[:4] or states[:5]
3441
3441
3442 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3442 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3443 'ignored' in show, 'clean' in show, 'unknown' in show)
3443 'ignored' in show, 'clean' in show, 'unknown' in show)
3444 changestates = zip(states, 'MAR!?IC', stat)
3444 changestates = zip(states, 'MAR!?IC', stat)
3445
3445
3446 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3446 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3447 ctxn = repo[nullid]
3447 ctxn = repo[nullid]
3448 ctx1 = repo[node1]
3448 ctx1 = repo[node1]
3449 ctx2 = repo[node2]
3449 ctx2 = repo[node2]
3450 added = stat[1]
3450 added = stat[1]
3451 if node2 is None:
3451 if node2 is None:
3452 added = stat[0] + stat[1] # merged?
3452 added = stat[0] + stat[1] # merged?
3453
3453
3454 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3454 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3455 if k in added:
3455 if k in added:
3456 copy[k] = v
3456 copy[k] = v
3457 elif v in added:
3457 elif v in added:
3458 copy[v] = k
3458 copy[v] = k
3459
3459
3460 for state, char, files in changestates:
3460 for state, char, files in changestates:
3461 if state in show:
3461 if state in show:
3462 format = "%s %%s%s" % (char, end)
3462 format = "%s %%s%s" % (char, end)
3463 if opts.get('no_status'):
3463 if opts.get('no_status'):
3464 format = "%%s%s" % end
3464 format = "%%s%s" % end
3465
3465
3466 for f in files:
3466 for f in files:
3467 ui.write(format % repo.pathto(f, cwd),
3467 ui.write(format % repo.pathto(f, cwd),
3468 label='status.' + state)
3468 label='status.' + state)
3469 if f in copy:
3469 if f in copy:
3470 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3470 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3471 label='status.copied')
3471 label='status.copied')
3472
3472
3473 def summary(ui, repo, **opts):
3473 def summary(ui, repo, **opts):
3474 """summarize working directory state
3474 """summarize working directory state
3475
3475
3476 This generates a brief summary of the working directory state,
3476 This generates a brief summary of the working directory state,
3477 including parents, branch, commit status, and available updates.
3477 including parents, branch, commit status, and available updates.
3478
3478
3479 With the --remote option, this will check the default paths for
3479 With the --remote option, this will check the default paths for
3480 incoming and outgoing changes. This can be time-consuming.
3480 incoming and outgoing changes. This can be time-consuming.
3481
3481
3482 Returns 0 on success.
3482 Returns 0 on success.
3483 """
3483 """
3484
3484
3485 ctx = repo[None]
3485 ctx = repo[None]
3486 parents = ctx.parents()
3486 parents = ctx.parents()
3487 pnode = parents[0].node()
3487 pnode = parents[0].node()
3488
3488
3489 for p in parents:
3489 for p in parents:
3490 # label with log.changeset (instead of log.parent) since this
3490 # label with log.changeset (instead of log.parent) since this
3491 # shows a working directory parent *changeset*:
3491 # shows a working directory parent *changeset*:
3492 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3492 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3493 label='log.changeset')
3493 label='log.changeset')
3494 ui.write(' '.join(p.tags()), label='log.tag')
3494 ui.write(' '.join(p.tags()), label='log.tag')
3495 if p.rev() == -1:
3495 if p.rev() == -1:
3496 if not len(repo):
3496 if not len(repo):
3497 ui.write(_(' (empty repository)'))
3497 ui.write(_(' (empty repository)'))
3498 else:
3498 else:
3499 ui.write(_(' (no revision checked out)'))
3499 ui.write(_(' (no revision checked out)'))
3500 ui.write('\n')
3500 ui.write('\n')
3501 if p.description():
3501 if p.description():
3502 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3502 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3503 label='log.summary')
3503 label='log.summary')
3504
3504
3505 branch = ctx.branch()
3505 branch = ctx.branch()
3506 bheads = repo.branchheads(branch)
3506 bheads = repo.branchheads(branch)
3507 m = _('branch: %s\n') % branch
3507 m = _('branch: %s\n') % branch
3508 if branch != 'default':
3508 if branch != 'default':
3509 ui.write(m, label='log.branch')
3509 ui.write(m, label='log.branch')
3510 else:
3510 else:
3511 ui.status(m, label='log.branch')
3511 ui.status(m, label='log.branch')
3512
3512
3513 st = list(repo.status(unknown=True))[:6]
3513 st = list(repo.status(unknown=True))[:6]
3514
3514
3515 c = repo.dirstate.copies()
3515 c = repo.dirstate.copies()
3516 copied, renamed = [], []
3516 copied, renamed = [], []
3517 for d, s in c.iteritems():
3517 for d, s in c.iteritems():
3518 if s in st[2]:
3518 if s in st[2]:
3519 st[2].remove(s)
3519 st[2].remove(s)
3520 renamed.append(d)
3520 renamed.append(d)
3521 else:
3521 else:
3522 copied.append(d)
3522 copied.append(d)
3523 if d in st[1]:
3523 if d in st[1]:
3524 st[1].remove(d)
3524 st[1].remove(d)
3525 st.insert(3, renamed)
3525 st.insert(3, renamed)
3526 st.insert(4, copied)
3526 st.insert(4, copied)
3527
3527
3528 ms = mergemod.mergestate(repo)
3528 ms = mergemod.mergestate(repo)
3529 st.append([f for f in ms if ms[f] == 'u'])
3529 st.append([f for f in ms if ms[f] == 'u'])
3530
3530
3531 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3531 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3532 st.append(subs)
3532 st.append(subs)
3533
3533
3534 labels = [ui.label(_('%d modified'), 'status.modified'),
3534 labels = [ui.label(_('%d modified'), 'status.modified'),
3535 ui.label(_('%d added'), 'status.added'),
3535 ui.label(_('%d added'), 'status.added'),
3536 ui.label(_('%d removed'), 'status.removed'),
3536 ui.label(_('%d removed'), 'status.removed'),
3537 ui.label(_('%d renamed'), 'status.copied'),
3537 ui.label(_('%d renamed'), 'status.copied'),
3538 ui.label(_('%d copied'), 'status.copied'),
3538 ui.label(_('%d copied'), 'status.copied'),
3539 ui.label(_('%d deleted'), 'status.deleted'),
3539 ui.label(_('%d deleted'), 'status.deleted'),
3540 ui.label(_('%d unknown'), 'status.unknown'),
3540 ui.label(_('%d unknown'), 'status.unknown'),
3541 ui.label(_('%d ignored'), 'status.ignored'),
3541 ui.label(_('%d ignored'), 'status.ignored'),
3542 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3542 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3543 ui.label(_('%d subrepos'), 'status.modified')]
3543 ui.label(_('%d subrepos'), 'status.modified')]
3544 t = []
3544 t = []
3545 for s, l in zip(st, labels):
3545 for s, l in zip(st, labels):
3546 if s:
3546 if s:
3547 t.append(l % len(s))
3547 t.append(l % len(s))
3548
3548
3549 t = ', '.join(t)
3549 t = ', '.join(t)
3550 cleanworkdir = False
3550 cleanworkdir = False
3551
3551
3552 if len(parents) > 1:
3552 if len(parents) > 1:
3553 t += _(' (merge)')
3553 t += _(' (merge)')
3554 elif branch != parents[0].branch():
3554 elif branch != parents[0].branch():
3555 t += _(' (new branch)')
3555 t += _(' (new branch)')
3556 elif (parents[0].extra().get('close') and
3556 elif (parents[0].extra().get('close') and
3557 pnode in repo.branchheads(branch, closed=True)):
3557 pnode in repo.branchheads(branch, closed=True)):
3558 t += _(' (head closed)')
3558 t += _(' (head closed)')
3559 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3559 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3560 t += _(' (clean)')
3560 t += _(' (clean)')
3561 cleanworkdir = True
3561 cleanworkdir = True
3562 elif pnode not in bheads:
3562 elif pnode not in bheads:
3563 t += _(' (new branch head)')
3563 t += _(' (new branch head)')
3564
3564
3565 if cleanworkdir:
3565 if cleanworkdir:
3566 ui.status(_('commit: %s\n') % t.strip())
3566 ui.status(_('commit: %s\n') % t.strip())
3567 else:
3567 else:
3568 ui.write(_('commit: %s\n') % t.strip())
3568 ui.write(_('commit: %s\n') % t.strip())
3569
3569
3570 # all ancestors of branch heads - all ancestors of parent = new csets
3570 # all ancestors of branch heads - all ancestors of parent = new csets
3571 new = [0] * len(repo)
3571 new = [0] * len(repo)
3572 cl = repo.changelog
3572 cl = repo.changelog
3573 for a in [cl.rev(n) for n in bheads]:
3573 for a in [cl.rev(n) for n in bheads]:
3574 new[a] = 1
3574 new[a] = 1
3575 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3575 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3576 new[a] = 1
3576 new[a] = 1
3577 for a in [p.rev() for p in parents]:
3577 for a in [p.rev() for p in parents]:
3578 if a >= 0:
3578 if a >= 0:
3579 new[a] = 0
3579 new[a] = 0
3580 for a in cl.ancestors(*[p.rev() for p in parents]):
3580 for a in cl.ancestors(*[p.rev() for p in parents]):
3581 new[a] = 0
3581 new[a] = 0
3582 new = sum(new)
3582 new = sum(new)
3583
3583
3584 if new == 0:
3584 if new == 0:
3585 ui.status(_('update: (current)\n'))
3585 ui.status(_('update: (current)\n'))
3586 elif pnode not in bheads:
3586 elif pnode not in bheads:
3587 ui.write(_('update: %d new changesets (update)\n') % new)
3587 ui.write(_('update: %d new changesets (update)\n') % new)
3588 else:
3588 else:
3589 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3589 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3590 (new, len(bheads)))
3590 (new, len(bheads)))
3591
3591
3592 if opts.get('remote'):
3592 if opts.get('remote'):
3593 t = []
3593 t = []
3594 source, branches = hg.parseurl(ui.expandpath('default'))
3594 source, branches = hg.parseurl(ui.expandpath('default'))
3595 other = hg.repository(hg.remoteui(repo, {}), source)
3595 other = hg.repository(hg.remoteui(repo, {}), source)
3596 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3596 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3597 ui.debug('comparing with %s\n' % url.hidepassword(source))
3597 ui.debug('comparing with %s\n' % url.hidepassword(source))
3598 repo.ui.pushbuffer()
3598 repo.ui.pushbuffer()
3599 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3599 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3600 repo.ui.popbuffer()
3600 repo.ui.popbuffer()
3601 if incoming:
3601 if incoming:
3602 t.append(_('1 or more incoming'))
3602 t.append(_('1 or more incoming'))
3603
3603
3604 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3604 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3605 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3605 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3606 other = hg.repository(hg.remoteui(repo, {}), dest)
3606 other = hg.repository(hg.remoteui(repo, {}), dest)
3607 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3607 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3608 repo.ui.pushbuffer()
3608 repo.ui.pushbuffer()
3609 o = discovery.findoutgoing(repo, other)
3609 o = discovery.findoutgoing(repo, other)
3610 repo.ui.popbuffer()
3610 repo.ui.popbuffer()
3611 o = repo.changelog.nodesbetween(o, None)[0]
3611 o = repo.changelog.nodesbetween(o, None)[0]
3612 if o:
3612 if o:
3613 t.append(_('%d outgoing') % len(o))
3613 t.append(_('%d outgoing') % len(o))
3614
3614
3615 if t:
3615 if t:
3616 ui.write(_('remote: %s\n') % (', '.join(t)))
3616 ui.write(_('remote: %s\n') % (', '.join(t)))
3617 else:
3617 else:
3618 ui.status(_('remote: (synced)\n'))
3618 ui.status(_('remote: (synced)\n'))
3619
3619
3620 def tag(ui, repo, name1, *names, **opts):
3620 def tag(ui, repo, name1, *names, **opts):
3621 """add one or more tags for the current or given revision
3621 """add one or more tags for the current or given revision
3622
3622
3623 Name a particular revision using <name>.
3623 Name a particular revision using <name>.
3624
3624
3625 Tags are used to name particular revisions of the repository and are
3625 Tags are used to name particular revisions of the repository and are
3626 very useful to compare different revisions, to go back to significant
3626 very useful to compare different revisions, to go back to significant
3627 earlier versions or to mark branch points as releases, etc.
3627 earlier versions or to mark branch points as releases, etc.
3628
3628
3629 If no revision is given, the parent of the working directory is
3629 If no revision is given, the parent of the working directory is
3630 used, or tip if no revision is checked out.
3630 used, or tip if no revision is checked out.
3631
3631
3632 To facilitate version control, distribution, and merging of tags,
3632 To facilitate version control, distribution, and merging of tags,
3633 they are stored as a file named ".hgtags" which is managed
3633 they are stored as a file named ".hgtags" which is managed
3634 similarly to other project files and can be hand-edited if
3634 similarly to other project files and can be hand-edited if
3635 necessary. The file '.hg/localtags' is used for local tags (not
3635 necessary. The file '.hg/localtags' is used for local tags (not
3636 shared among repositories).
3636 shared among repositories).
3637
3637
3638 See :hg:`help dates` for a list of formats valid for -d/--date.
3638 See :hg:`help dates` for a list of formats valid for -d/--date.
3639
3639
3640 Since tag names have priority over branch names during revision
3640 Since tag names have priority over branch names during revision
3641 lookup, using an existing branch name as a tag name is discouraged.
3641 lookup, using an existing branch name as a tag name is discouraged.
3642
3642
3643 Returns 0 on success.
3643 Returns 0 on success.
3644 """
3644 """
3645
3645
3646 rev_ = "."
3646 rev_ = "."
3647 names = [t.strip() for t in (name1,) + names]
3647 names = [t.strip() for t in (name1,) + names]
3648 if len(names) != len(set(names)):
3648 if len(names) != len(set(names)):
3649 raise util.Abort(_('tag names must be unique'))
3649 raise util.Abort(_('tag names must be unique'))
3650 for n in names:
3650 for n in names:
3651 if n in ['tip', '.', 'null']:
3651 if n in ['tip', '.', 'null']:
3652 raise util.Abort(_('the name \'%s\' is reserved') % n)
3652 raise util.Abort(_('the name \'%s\' is reserved') % n)
3653 if not n:
3653 if not n:
3654 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3654 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3655 if opts.get('rev') and opts.get('remove'):
3655 if opts.get('rev') and opts.get('remove'):
3656 raise util.Abort(_("--rev and --remove are incompatible"))
3656 raise util.Abort(_("--rev and --remove are incompatible"))
3657 if opts.get('rev'):
3657 if opts.get('rev'):
3658 rev_ = opts['rev']
3658 rev_ = opts['rev']
3659 message = opts.get('message')
3659 message = opts.get('message')
3660 if opts.get('remove'):
3660 if opts.get('remove'):
3661 expectedtype = opts.get('local') and 'local' or 'global'
3661 expectedtype = opts.get('local') and 'local' or 'global'
3662 for n in names:
3662 for n in names:
3663 if not repo.tagtype(n):
3663 if not repo.tagtype(n):
3664 raise util.Abort(_('tag \'%s\' does not exist') % n)
3664 raise util.Abort(_('tag \'%s\' does not exist') % n)
3665 if repo.tagtype(n) != expectedtype:
3665 if repo.tagtype(n) != expectedtype:
3666 if expectedtype == 'global':
3666 if expectedtype == 'global':
3667 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3667 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3668 else:
3668 else:
3669 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3669 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3670 rev_ = nullid
3670 rev_ = nullid
3671 if not message:
3671 if not message:
3672 # we don't translate commit messages
3672 # we don't translate commit messages
3673 message = 'Removed tag %s' % ', '.join(names)
3673 message = 'Removed tag %s' % ', '.join(names)
3674 elif not opts.get('force'):
3674 elif not opts.get('force'):
3675 for n in names:
3675 for n in names:
3676 if n in repo.tags():
3676 if n in repo.tags():
3677 raise util.Abort(_('tag \'%s\' already exists '
3677 raise util.Abort(_('tag \'%s\' already exists '
3678 '(use -f to force)') % n)
3678 '(use -f to force)') % n)
3679 if not rev_ and repo.dirstate.parents()[1] != nullid:
3679 if not rev_ and repo.dirstate.parents()[1] != nullid:
3680 raise util.Abort(_('uncommitted merge - please provide a '
3680 raise util.Abort(_('uncommitted merge - please provide a '
3681 'specific revision'))
3681 'specific revision'))
3682 r = repo[rev_].node()
3682 r = repo[rev_].node()
3683
3683
3684 if not message:
3684 if not message:
3685 # we don't translate commit messages
3685 # we don't translate commit messages
3686 message = ('Added tag %s for changeset %s' %
3686 message = ('Added tag %s for changeset %s' %
3687 (', '.join(names), short(r)))
3687 (', '.join(names), short(r)))
3688
3688
3689 date = opts.get('date')
3689 date = opts.get('date')
3690 if date:
3690 if date:
3691 date = util.parsedate(date)
3691 date = util.parsedate(date)
3692
3692
3693 if opts.get('edit'):
3693 if opts.get('edit'):
3694 message = ui.edit(message, ui.username())
3694 message = ui.edit(message, ui.username())
3695
3695
3696 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3696 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3697
3697
3698 def tags(ui, repo):
3698 def tags(ui, repo):
3699 """list repository tags
3699 """list repository tags
3700
3700
3701 This lists both regular and local tags. When the -v/--verbose
3701 This lists both regular and local tags. When the -v/--verbose
3702 switch is used, a third column "local" is printed for local tags.
3702 switch is used, a third column "local" is printed for local tags.
3703
3703
3704 Returns 0 on success.
3704 Returns 0 on success.
3705 """
3705 """
3706
3706
3707 hexfunc = ui.debugflag and hex or short
3707 hexfunc = ui.debugflag and hex or short
3708 tagtype = ""
3708 tagtype = ""
3709
3709
3710 for t, n in reversed(repo.tagslist()):
3710 for t, n in reversed(repo.tagslist()):
3711 if ui.quiet:
3711 if ui.quiet:
3712 ui.write("%s\n" % t)
3712 ui.write("%s\n" % t)
3713 continue
3713 continue
3714
3714
3715 try:
3715 try:
3716 hn = hexfunc(n)
3716 hn = hexfunc(n)
3717 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3717 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3718 except error.LookupError:
3718 except error.LookupError:
3719 r = " ?:%s" % hn
3719 r = " ?:%s" % hn
3720 else:
3720 else:
3721 spaces = " " * (30 - encoding.colwidth(t))
3721 spaces = " " * (30 - encoding.colwidth(t))
3722 if ui.verbose:
3722 if ui.verbose:
3723 if repo.tagtype(t) == 'local':
3723 if repo.tagtype(t) == 'local':
3724 tagtype = " local"
3724 tagtype = " local"
3725 else:
3725 else:
3726 tagtype = ""
3726 tagtype = ""
3727 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3727 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3728
3728
3729 def tip(ui, repo, **opts):
3729 def tip(ui, repo, **opts):
3730 """show the tip revision
3730 """show the tip revision
3731
3731
3732 The tip revision (usually just called the tip) is the changeset
3732 The tip revision (usually just called the tip) is the changeset
3733 most recently added to the repository (and therefore the most
3733 most recently added to the repository (and therefore the most
3734 recently changed head).
3734 recently changed head).
3735
3735
3736 If you have just made a commit, that commit will be the tip. If
3736 If you have just made a commit, that commit will be the tip. If
3737 you have just pulled changes from another repository, the tip of
3737 you have just pulled changes from another repository, the tip of
3738 that repository becomes the current tip. The "tip" tag is special
3738 that repository becomes the current tip. The "tip" tag is special
3739 and cannot be renamed or assigned to a different changeset.
3739 and cannot be renamed or assigned to a different changeset.
3740
3740
3741 Returns 0 on success.
3741 Returns 0 on success.
3742 """
3742 """
3743 displayer = cmdutil.show_changeset(ui, repo, opts)
3743 displayer = cmdutil.show_changeset(ui, repo, opts)
3744 displayer.show(repo[len(repo) - 1])
3744 displayer.show(repo[len(repo) - 1])
3745 displayer.close()
3745 displayer.close()
3746
3746
3747 def unbundle(ui, repo, fname1, *fnames, **opts):
3747 def unbundle(ui, repo, fname1, *fnames, **opts):
3748 """apply one or more changegroup files
3748 """apply one or more changegroup files
3749
3749
3750 Apply one or more compressed changegroup files generated by the
3750 Apply one or more compressed changegroup files generated by the
3751 bundle command.
3751 bundle command.
3752
3752
3753 Returns 0 on success, 1 if an update has unresolved files.
3753 Returns 0 on success, 1 if an update has unresolved files.
3754 """
3754 """
3755 fnames = (fname1,) + fnames
3755 fnames = (fname1,) + fnames
3756
3756
3757 lock = repo.lock()
3757 lock = repo.lock()
3758 try:
3758 try:
3759 for fname in fnames:
3759 for fname in fnames:
3760 f = url.open(ui, fname)
3760 f = url.open(ui, fname)
3761 gen = changegroup.readbundle(f, fname)
3761 gen = changegroup.readbundle(f, fname)
3762 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3762 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3763 lock=lock)
3763 lock=lock)
3764 finally:
3764 finally:
3765 lock.release()
3765 lock.release()
3766
3766
3767 return postincoming(ui, repo, modheads, opts.get('update'), None)
3767 return postincoming(ui, repo, modheads, opts.get('update'), None)
3768
3768
3769 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3769 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3770 """update working directory (or switch revisions)
3770 """update working directory (or switch revisions)
3771
3771
3772 Update the repository's working directory to the specified
3772 Update the repository's working directory to the specified
3773 changeset.
3773 changeset.
3774
3774
3775 If no changeset is specified, attempt to update to the tip of the
3775 If no changeset is specified, attempt to update to the tip of the
3776 current branch. If this changeset is a descendant of the working
3776 current branch. If this changeset is a descendant of the working
3777 directory's parent, update to it, otherwise abort.
3777 directory's parent, update to it, otherwise abort.
3778
3778
3779 The following rules apply when the working directory contains
3779 The following rules apply when the working directory contains
3780 uncommitted changes:
3780 uncommitted changes:
3781
3781
3782 1. If neither -c/--check nor -C/--clean is specified, and if
3782 1. If neither -c/--check nor -C/--clean is specified, and if
3783 the requested changeset is an ancestor or descendant of
3783 the requested changeset is an ancestor or descendant of
3784 the working directory's parent, the uncommitted changes
3784 the working directory's parent, the uncommitted changes
3785 are merged into the requested changeset and the merged
3785 are merged into the requested changeset and the merged
3786 result is left uncommitted. If the requested changeset is
3786 result is left uncommitted. If the requested changeset is
3787 not an ancestor or descendant (that is, it is on another
3787 not an ancestor or descendant (that is, it is on another
3788 branch), the update is aborted and the uncommitted changes
3788 branch), the update is aborted and the uncommitted changes
3789 are preserved.
3789 are preserved.
3790
3790
3791 2. With the -c/--check option, the update is aborted and the
3791 2. With the -c/--check option, the update is aborted and the
3792 uncommitted changes are preserved.
3792 uncommitted changes are preserved.
3793
3793
3794 3. With the -C/--clean option, uncommitted changes are discarded and
3794 3. With the -C/--clean option, uncommitted changes are discarded and
3795 the working directory is updated to the requested changeset.
3795 the working directory is updated to the requested changeset.
3796
3796
3797 Use null as the changeset to remove the working directory (like
3797 Use null as the changeset to remove the working directory (like
3798 :hg:`clone -U`).
3798 :hg:`clone -U`).
3799
3799
3800 If you want to update just one file to an older changeset, use :hg:`revert`.
3800 If you want to update just one file to an older changeset, use :hg:`revert`.
3801
3801
3802 See :hg:`help dates` for a list of formats valid for -d/--date.
3802 See :hg:`help dates` for a list of formats valid for -d/--date.
3803
3803
3804 Returns 0 on success, 1 if there are unresolved files.
3804 Returns 0 on success, 1 if there are unresolved files.
3805 """
3805 """
3806 if rev and node:
3806 if rev and node:
3807 raise util.Abort(_("please specify just one revision"))
3807 raise util.Abort(_("please specify just one revision"))
3808
3808
3809 if not rev:
3809 if not rev:
3810 rev = node
3810 rev = node
3811
3811
3812 if check and clean:
3812 if check and clean:
3813 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3813 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3814
3814
3815 if check:
3815 if check:
3816 # we could use dirty() but we can ignore merge and branch trivia
3816 # we could use dirty() but we can ignore merge and branch trivia
3817 c = repo[None]
3817 c = repo[None]
3818 if c.modified() or c.added() or c.removed():
3818 if c.modified() or c.added() or c.removed():
3819 raise util.Abort(_("uncommitted local changes"))
3819 raise util.Abort(_("uncommitted local changes"))
3820
3820
3821 if date:
3821 if date:
3822 if rev:
3822 if rev:
3823 raise util.Abort(_("you can't specify a revision and a date"))
3823 raise util.Abort(_("you can't specify a revision and a date"))
3824 rev = cmdutil.finddate(ui, repo, date)
3824 rev = cmdutil.finddate(ui, repo, date)
3825
3825
3826 if clean or check:
3826 if clean or check:
3827 return hg.clean(repo, rev)
3827 return hg.clean(repo, rev)
3828 else:
3828 else:
3829 return hg.update(repo, rev)
3829 return hg.update(repo, rev)
3830
3830
3831 def verify(ui, repo):
3831 def verify(ui, repo):
3832 """verify the integrity of the repository
3832 """verify the integrity of the repository
3833
3833
3834 Verify the integrity of the current repository.
3834 Verify the integrity of the current repository.
3835
3835
3836 This will perform an extensive check of the repository's
3836 This will perform an extensive check of the repository's
3837 integrity, validating the hashes and checksums of each entry in
3837 integrity, validating the hashes and checksums of each entry in
3838 the changelog, manifest, and tracked files, as well as the
3838 the changelog, manifest, and tracked files, as well as the
3839 integrity of their crosslinks and indices.
3839 integrity of their crosslinks and indices.
3840
3840
3841 Returns 0 on success, 1 if errors are encountered.
3841 Returns 0 on success, 1 if errors are encountered.
3842 """
3842 """
3843 return hg.verify(repo)
3843 return hg.verify(repo)
3844
3844
3845 def version_(ui):
3845 def version_(ui):
3846 """output version and copyright information"""
3846 """output version and copyright information"""
3847 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3847 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3848 % util.version())
3848 % util.version())
3849 ui.status(_(
3849 ui.status(_(
3850 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3850 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3851 "This is free software; see the source for copying conditions. "
3851 "This is free software; see the source for copying conditions. "
3852 "There is NO\nwarranty; "
3852 "There is NO\nwarranty; "
3853 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3853 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3854 ))
3854 ))
3855
3855
3856 # Command options and aliases are listed here, alphabetically
3856 # Command options and aliases are listed here, alphabetically
3857
3857
3858 globalopts = [
3858 globalopts = [
3859 ('R', 'repository', '',
3859 ('R', 'repository', '',
3860 _('repository root directory or name of overlay bundle file'),
3860 _('repository root directory or name of overlay bundle file'),
3861 _('REPO')),
3861 _('REPO')),
3862 ('', 'cwd', '',
3862 ('', 'cwd', '',
3863 _('change working directory'), _('DIR')),
3863 _('change working directory'), _('DIR')),
3864 ('y', 'noninteractive', None,
3864 ('y', 'noninteractive', None,
3865 _('do not prompt, assume \'yes\' for any required answers')),
3865 _('do not prompt, assume \'yes\' for any required answers')),
3866 ('q', 'quiet', None, _('suppress output')),
3866 ('q', 'quiet', None, _('suppress output')),
3867 ('v', 'verbose', None, _('enable additional output')),
3867 ('v', 'verbose', None, _('enable additional output')),
3868 ('', 'config', [],
3868 ('', 'config', [],
3869 _('set/override config option (use \'section.name=value\')'),
3869 _('set/override config option (use \'section.name=value\')'),
3870 _('CONFIG')),
3870 _('CONFIG')),
3871 ('', 'debug', None, _('enable debugging output')),
3871 ('', 'debug', None, _('enable debugging output')),
3872 ('', 'debugger', None, _('start debugger')),
3872 ('', 'debugger', None, _('start debugger')),
3873 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3873 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3874 _('ENCODE')),
3874 _('ENCODE')),
3875 ('', 'encodingmode', encoding.encodingmode,
3875 ('', 'encodingmode', encoding.encodingmode,
3876 _('set the charset encoding mode'), _('MODE')),
3876 _('set the charset encoding mode'), _('MODE')),
3877 ('', 'traceback', None, _('always print a traceback on exception')),
3877 ('', 'traceback', None, _('always print a traceback on exception')),
3878 ('', 'time', None, _('time how long the command takes')),
3878 ('', 'time', None, _('time how long the command takes')),
3879 ('', 'profile', None, _('print command execution profile')),
3879 ('', 'profile', None, _('print command execution profile')),
3880 ('', 'version', None, _('output version information and exit')),
3880 ('', 'version', None, _('output version information and exit')),
3881 ('h', 'help', None, _('display help and exit')),
3881 ('h', 'help', None, _('display help and exit')),
3882 ]
3882 ]
3883
3883
3884 dryrunopts = [('n', 'dry-run', None,
3884 dryrunopts = [('n', 'dry-run', None,
3885 _('do not perform actions, just print output'))]
3885 _('do not perform actions, just print output'))]
3886
3886
3887 remoteopts = [
3887 remoteopts = [
3888 ('e', 'ssh', '',
3888 ('e', 'ssh', '',
3889 _('specify ssh command to use'), _('CMD')),
3889 _('specify ssh command to use'), _('CMD')),
3890 ('', 'remotecmd', '',
3890 ('', 'remotecmd', '',
3891 _('specify hg command to run on the remote side'), _('CMD')),
3891 _('specify hg command to run on the remote side'), _('CMD')),
3892 ]
3892 ]
3893
3893
3894 walkopts = [
3894 walkopts = [
3895 ('I', 'include', [],
3895 ('I', 'include', [],
3896 _('include names matching the given patterns'), _('PATTERN')),
3896 _('include names matching the given patterns'), _('PATTERN')),
3897 ('X', 'exclude', [],
3897 ('X', 'exclude', [],
3898 _('exclude names matching the given patterns'), _('PATTERN')),
3898 _('exclude names matching the given patterns'), _('PATTERN')),
3899 ]
3899 ]
3900
3900
3901 commitopts = [
3901 commitopts = [
3902 ('m', 'message', '',
3902 ('m', 'message', '',
3903 _('use text as commit message'), _('TEXT')),
3903 _('use text as commit message'), _('TEXT')),
3904 ('l', 'logfile', '',
3904 ('l', 'logfile', '',
3905 _('read commit message from file'), _('FILE')),
3905 _('read commit message from file'), _('FILE')),
3906 ]
3906 ]
3907
3907
3908 commitopts2 = [
3908 commitopts2 = [
3909 ('d', 'date', '',
3909 ('d', 'date', '',
3910 _('record datecode as commit date'), _('DATE')),
3910 _('record datecode as commit date'), _('DATE')),
3911 ('u', 'user', '',
3911 ('u', 'user', '',
3912 _('record the specified user as committer'), _('USER')),
3912 _('record the specified user as committer'), _('USER')),
3913 ]
3913 ]
3914
3914
3915 templateopts = [
3915 templateopts = [
3916 ('', 'style', '',
3916 ('', 'style', '',
3917 _('display using template map file'), _('STYLE')),
3917 _('display using template map file'), _('STYLE')),
3918 ('', 'template', '',
3918 ('', 'template', '',
3919 _('display with template'), _('TEMPLATE')),
3919 _('display with template'), _('TEMPLATE')),
3920 ]
3920 ]
3921
3921
3922 logopts = [
3922 logopts = [
3923 ('p', 'patch', None, _('show patch')),
3923 ('p', 'patch', None, _('show patch')),
3924 ('g', 'git', None, _('use git extended diff format')),
3924 ('g', 'git', None, _('use git extended diff format')),
3925 ('l', 'limit', '',
3925 ('l', 'limit', '',
3926 _('limit number of changes displayed'), _('NUM')),
3926 _('limit number of changes displayed'), _('NUM')),
3927 ('M', 'no-merges', None, _('do not show merges')),
3927 ('M', 'no-merges', None, _('do not show merges')),
3928 ('', 'stat', None, _('output diffstat-style summary of changes')),
3928 ('', 'stat', None, _('output diffstat-style summary of changes')),
3929 ] + templateopts
3929 ] + templateopts
3930
3930
3931 diffopts = [
3931 diffopts = [
3932 ('a', 'text', None, _('treat all files as text')),
3932 ('a', 'text', None, _('treat all files as text')),
3933 ('g', 'git', None, _('use git extended diff format')),
3933 ('g', 'git', None, _('use git extended diff format')),
3934 ('', 'nodates', None, _('omit dates from diff headers'))
3934 ('', 'nodates', None, _('omit dates from diff headers'))
3935 ]
3935 ]
3936
3936
3937 diffopts2 = [
3937 diffopts2 = [
3938 ('p', 'show-function', None, _('show which function each change is in')),
3938 ('p', 'show-function', None, _('show which function each change is in')),
3939 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3939 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3940 ('w', 'ignore-all-space', None,
3940 ('w', 'ignore-all-space', None,
3941 _('ignore white space when comparing lines')),
3941 _('ignore white space when comparing lines')),
3942 ('b', 'ignore-space-change', None,
3942 ('b', 'ignore-space-change', None,
3943 _('ignore changes in the amount of white space')),
3943 _('ignore changes in the amount of white space')),
3944 ('B', 'ignore-blank-lines', None,
3944 ('B', 'ignore-blank-lines', None,
3945 _('ignore changes whose lines are all blank')),
3945 _('ignore changes whose lines are all blank')),
3946 ('U', 'unified', '',
3946 ('U', 'unified', '',
3947 _('number of lines of context to show'), _('NUM')),
3947 _('number of lines of context to show'), _('NUM')),
3948 ('', 'stat', None, _('output diffstat-style summary of changes')),
3948 ('', 'stat', None, _('output diffstat-style summary of changes')),
3949 ]
3949 ]
3950
3950
3951 similarityopts = [
3951 similarityopts = [
3952 ('s', 'similarity', '',
3952 ('s', 'similarity', '',
3953 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3953 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3954 ]
3954 ]
3955
3955
3956 table = {
3956 table = {
3957 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3957 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3958 "addremove":
3958 "addremove":
3959 (addremove, similarityopts + walkopts + dryrunopts,
3959 (addremove, similarityopts + walkopts + dryrunopts,
3960 _('[OPTION]... [FILE]...')),
3960 _('[OPTION]... [FILE]...')),
3961 "^annotate|blame":
3961 "^annotate|blame":
3962 (annotate,
3962 (annotate,
3963 [('r', 'rev', '',
3963 [('r', 'rev', '',
3964 _('annotate the specified revision'), _('REV')),
3964 _('annotate the specified revision'), _('REV')),
3965 ('', 'follow', None,
3965 ('', 'follow', None,
3966 _('follow copies/renames and list the filename (DEPRECATED)')),
3966 _('follow copies/renames and list the filename (DEPRECATED)')),
3967 ('', 'no-follow', None, _("don't follow copies and renames")),
3967 ('', 'no-follow', None, _("don't follow copies and renames")),
3968 ('a', 'text', None, _('treat all files as text')),
3968 ('a', 'text', None, _('treat all files as text')),
3969 ('u', 'user', None, _('list the author (long with -v)')),
3969 ('u', 'user', None, _('list the author (long with -v)')),
3970 ('f', 'file', None, _('list the filename')),
3970 ('f', 'file', None, _('list the filename')),
3971 ('d', 'date', None, _('list the date (short with -q)')),
3971 ('d', 'date', None, _('list the date (short with -q)')),
3972 ('n', 'number', None, _('list the revision number (default)')),
3972 ('n', 'number', None, _('list the revision number (default)')),
3973 ('c', 'changeset', None, _('list the changeset')),
3973 ('c', 'changeset', None, _('list the changeset')),
3974 ('l', 'line-number', None,
3974 ('l', 'line-number', None,
3975 _('show line number at the first appearance'))
3975 _('show line number at the first appearance'))
3976 ] + walkopts,
3976 ] + walkopts,
3977 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3977 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3978 "archive":
3978 "archive":
3979 (archive,
3979 (archive,
3980 [('', 'no-decode', None, _('do not pass files through decoders')),
3980 [('', 'no-decode', None, _('do not pass files through decoders')),
3981 ('p', 'prefix', '',
3981 ('p', 'prefix', '',
3982 _('directory prefix for files in archive'), _('PREFIX')),
3982 _('directory prefix for files in archive'), _('PREFIX')),
3983 ('r', 'rev', '',
3983 ('r', 'rev', '',
3984 _('revision to distribute'), _('REV')),
3984 _('revision to distribute'), _('REV')),
3985 ('t', 'type', '',
3985 ('t', 'type', '',
3986 _('type of distribution to create'), _('TYPE')),
3986 _('type of distribution to create'), _('TYPE')),
3987 ] + walkopts,
3987 ] + walkopts,
3988 _('[OPTION]... DEST')),
3988 _('[OPTION]... DEST')),
3989 "backout":
3989 "backout":
3990 (backout,
3990 (backout,
3991 [('', 'merge', None,
3991 [('', 'merge', None,
3992 _('merge with old dirstate parent after backout')),
3992 _('merge with old dirstate parent after backout')),
3993 ('', 'parent', '',
3993 ('', 'parent', '',
3994 _('parent to choose when backing out merge'), _('REV')),
3994 _('parent to choose when backing out merge'), _('REV')),
3995 ('r', 'rev', '',
3995 ('r', 'rev', '',
3996 _('revision to backout'), _('REV')),
3996 _('revision to backout'), _('REV')),
3997 ] + walkopts + commitopts + commitopts2,
3997 ] + walkopts + commitopts + commitopts2,
3998 _('[OPTION]... [-r] REV')),
3998 _('[OPTION]... [-r] REV')),
3999 "bisect":
3999 "bisect":
4000 (bisect,
4000 (bisect,
4001 [('r', 'reset', False, _('reset bisect state')),
4001 [('r', 'reset', False, _('reset bisect state')),
4002 ('g', 'good', False, _('mark changeset good')),
4002 ('g', 'good', False, _('mark changeset good')),
4003 ('b', 'bad', False, _('mark changeset bad')),
4003 ('b', 'bad', False, _('mark changeset bad')),
4004 ('s', 'skip', False, _('skip testing changeset')),
4004 ('s', 'skip', False, _('skip testing changeset')),
4005 ('c', 'command', '',
4005 ('c', 'command', '',
4006 _('use command to check changeset state'), _('CMD')),
4006 _('use command to check changeset state'), _('CMD')),
4007 ('U', 'noupdate', False, _('do not update to target'))],
4007 ('U', 'noupdate', False, _('do not update to target'))],
4008 _("[-gbsr] [-U] [-c CMD] [REV]")),
4008 _("[-gbsr] [-U] [-c CMD] [REV]")),
4009 "branch":
4009 "branch":
4010 (branch,
4010 (branch,
4011 [('f', 'force', None,
4011 [('f', 'force', None,
4012 _('set branch name even if it shadows an existing branch')),
4012 _('set branch name even if it shadows an existing branch')),
4013 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4013 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4014 _('[-fC] [NAME]')),
4014 _('[-fC] [NAME]')),
4015 "branches":
4015 "branches":
4016 (branches,
4016 (branches,
4017 [('a', 'active', False,
4017 [('a', 'active', False,
4018 _('show only branches that have unmerged heads')),
4018 _('show only branches that have unmerged heads')),
4019 ('c', 'closed', False,
4019 ('c', 'closed', False,
4020 _('show normal and closed branches'))],
4020 _('show normal and closed branches'))],
4021 _('[-ac]')),
4021 _('[-ac]')),
4022 "bundle":
4022 "bundle":
4023 (bundle,
4023 (bundle,
4024 [('f', 'force', None,
4024 [('f', 'force', None,
4025 _('run even when the destination is unrelated')),
4025 _('run even when the destination is unrelated')),
4026 ('r', 'rev', [],
4026 ('r', 'rev', [],
4027 _('a changeset intended to be added to the destination'),
4027 _('a changeset intended to be added to the destination'),
4028 _('REV')),
4028 _('REV')),
4029 ('b', 'branch', [],
4029 ('b', 'branch', [],
4030 _('a specific branch you would like to bundle'),
4030 _('a specific branch you would like to bundle'),
4031 _('BRANCH')),
4031 _('BRANCH')),
4032 ('', 'base', [],
4032 ('', 'base', [],
4033 _('a base changeset assumed to be available at the destination'),
4033 _('a base changeset assumed to be available at the destination'),
4034 _('REV')),
4034 _('REV')),
4035 ('a', 'all', None, _('bundle all changesets in the repository')),
4035 ('a', 'all', None, _('bundle all changesets in the repository')),
4036 ('t', 'type', 'bzip2',
4036 ('t', 'type', 'bzip2',
4037 _('bundle compression type to use'), _('TYPE')),
4037 _('bundle compression type to use'), _('TYPE')),
4038 ] + remoteopts,
4038 ] + remoteopts,
4039 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4039 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4040 "cat":
4040 "cat":
4041 (cat,
4041 (cat,
4042 [('o', 'output', '',
4042 [('o', 'output', '',
4043 _('print output to file with formatted name'), _('FORMAT')),
4043 _('print output to file with formatted name'), _('FORMAT')),
4044 ('r', 'rev', '',
4044 ('r', 'rev', '',
4045 _('print the given revision'), _('REV')),
4045 _('print the given revision'), _('REV')),
4046 ('', 'decode', None, _('apply any matching decode filter')),
4046 ('', 'decode', None, _('apply any matching decode filter')),
4047 ] + walkopts,
4047 ] + walkopts,
4048 _('[OPTION]... FILE...')),
4048 _('[OPTION]... FILE...')),
4049 "^clone":
4049 "^clone":
4050 (clone,
4050 (clone,
4051 [('U', 'noupdate', None,
4051 [('U', 'noupdate', None,
4052 _('the clone will include an empty working copy (only a repository)')),
4052 _('the clone will include an empty working copy (only a repository)')),
4053 ('u', 'updaterev', '',
4053 ('u', 'updaterev', '',
4054 _('revision, tag or branch to check out'), _('REV')),
4054 _('revision, tag or branch to check out'), _('REV')),
4055 ('r', 'rev', [],
4055 ('r', 'rev', [],
4056 _('include the specified changeset'), _('REV')),
4056 _('include the specified changeset'), _('REV')),
4057 ('b', 'branch', [],
4057 ('b', 'branch', [],
4058 _('clone only the specified branch'), _('BRANCH')),
4058 _('clone only the specified branch'), _('BRANCH')),
4059 ('', 'pull', None, _('use pull protocol to copy metadata')),
4059 ('', 'pull', None, _('use pull protocol to copy metadata')),
4060 ('', 'uncompressed', None,
4060 ('', 'uncompressed', None,
4061 _('use uncompressed transfer (fast over LAN)')),
4061 _('use uncompressed transfer (fast over LAN)')),
4062 ] + remoteopts,
4062 ] + remoteopts,
4063 _('[OPTION]... SOURCE [DEST]')),
4063 _('[OPTION]... SOURCE [DEST]')),
4064 "^commit|ci":
4064 "^commit|ci":
4065 (commit,
4065 (commit,
4066 [('A', 'addremove', None,
4066 [('A', 'addremove', None,
4067 _('mark new/missing files as added/removed before committing')),
4067 _('mark new/missing files as added/removed before committing')),
4068 ('', 'close-branch', None,
4068 ('', 'close-branch', None,
4069 _('mark a branch as closed, hiding it from the branch list')),
4069 _('mark a branch as closed, hiding it from the branch list')),
4070 ] + walkopts + commitopts + commitopts2,
4070 ] + walkopts + commitopts + commitopts2,
4071 _('[OPTION]... [FILE]...')),
4071 _('[OPTION]... [FILE]...')),
4072 "copy|cp":
4072 "copy|cp":
4073 (copy,
4073 (copy,
4074 [('A', 'after', None, _('record a copy that has already occurred')),
4074 [('A', 'after', None, _('record a copy that has already occurred')),
4075 ('f', 'force', None,
4075 ('f', 'force', None,
4076 _('forcibly copy over an existing managed file')),
4076 _('forcibly copy over an existing managed file')),
4077 ] + walkopts + dryrunopts,
4077 ] + walkopts + dryrunopts,
4078 _('[OPTION]... [SOURCE]... DEST')),
4078 _('[OPTION]... [SOURCE]... DEST')),
4079 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4079 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4080 "debugbuilddag":
4080 "debugbuilddag":
4081 (debugbuilddag,
4081 (debugbuilddag,
4082 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4082 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4083 ('a', 'appended-file', None, _('add single file all revs append to')),
4083 ('a', 'appended-file', None, _('add single file all revs append to')),
4084 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4084 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4085 ('n', 'new-file', None, _('add new file at each rev')),
4085 ('n', 'new-file', None, _('add new file at each rev')),
4086 ],
4086 ],
4087 _('[OPTION]... TEXT')),
4087 _('[OPTION]... TEXT')),
4088 "debugcheckstate": (debugcheckstate, [], ''),
4088 "debugcheckstate": (debugcheckstate, [], ''),
4089 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4089 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4090 "debugcomplete":
4090 "debugcomplete":
4091 (debugcomplete,
4091 (debugcomplete,
4092 [('o', 'options', None, _('show the command options'))],
4092 [('o', 'options', None, _('show the command options'))],
4093 _('[-o] CMD')),
4093 _('[-o] CMD')),
4094 "debugdag":
4094 "debugdag":
4095 (debugdag,
4095 (debugdag,
4096 [('t', 'tags', None, _('use tags as labels')),
4096 [('t', 'tags', None, _('use tags as labels')),
4097 ('b', 'branches', None, _('annotate with branch names')),
4097 ('b', 'branches', None, _('annotate with branch names')),
4098 ('', 'dots', None, _('use dots for runs')),
4098 ('', 'dots', None, _('use dots for runs')),
4099 ('s', 'spaces', None, _('separate elements by spaces')),
4099 ('s', 'spaces', None, _('separate elements by spaces')),
4100 ],
4100 ],
4101 _('[OPTION]... [FILE [REV]...]')),
4101 _('[OPTION]... [FILE [REV]...]')),
4102 "debugdate":
4102 "debugdate":
4103 (debugdate,
4103 (debugdate,
4104 [('e', 'extended', None, _('try extended date formats'))],
4104 [('e', 'extended', None, _('try extended date formats'))],
4105 _('[-e] DATE [RANGE]')),
4105 _('[-e] DATE [RANGE]')),
4106 "debugdata": (debugdata, [], _('FILE REV')),
4106 "debugdata": (debugdata, [], _('FILE REV')),
4107 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4107 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4108 "debugindex": (debugindex, [], _('FILE')),
4108 "debugindex": (debugindex, [], _('FILE')),
4109 "debugindexdot": (debugindexdot, [], _('FILE')),
4109 "debugindexdot": (debugindexdot, [], _('FILE')),
4110 "debuginstall": (debuginstall, [], ''),
4110 "debuginstall": (debuginstall, [], ''),
4111 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4111 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4112 "debugrebuildstate":
4112 "debugrebuildstate":
4113 (debugrebuildstate,
4113 (debugrebuildstate,
4114 [('r', 'rev', '',
4114 [('r', 'rev', '',
4115 _('revision to rebuild to'), _('REV'))],
4115 _('revision to rebuild to'), _('REV'))],
4116 _('[-r REV] [REV]')),
4116 _('[-r REV] [REV]')),
4117 "debugrename":
4117 "debugrename":
4118 (debugrename,
4118 (debugrename,
4119 [('r', 'rev', '',
4119 [('r', 'rev', '',
4120 _('revision to debug'), _('REV'))],
4120 _('revision to debug'), _('REV'))],
4121 _('[-r REV] FILE')),
4121 _('[-r REV] FILE')),
4122 "debugrevspec":
4122 "debugrevspec":
4123 (debugrevspec, [], ('REVSPEC')),
4123 (debugrevspec, [], ('REVSPEC')),
4124 "debugsetparents":
4124 "debugsetparents":
4125 (debugsetparents, [], _('REV1 [REV2]')),
4125 (debugsetparents, [], _('REV1 [REV2]')),
4126 "debugstate":
4126 "debugstate":
4127 (debugstate,
4127 (debugstate,
4128 [('', 'nodates', None, _('do not display the saved mtime'))],
4128 [('', 'nodates', None, _('do not display the saved mtime'))],
4129 _('[OPTION]...')),
4129 _('[OPTION]...')),
4130 "debugsub":
4130 "debugsub":
4131 (debugsub,
4131 (debugsub,
4132 [('r', 'rev', '',
4132 [('r', 'rev', '',
4133 _('revision to check'), _('REV'))],
4133 _('revision to check'), _('REV'))],
4134 _('[-r REV] [REV]')),
4134 _('[-r REV] [REV]')),
4135 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4135 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4136 "^diff":
4136 "^diff":
4137 (diff,
4137 (diff,
4138 [('r', 'rev', [],
4138 [('r', 'rev', [],
4139 _('revision'), _('REV')),
4139 _('revision'), _('REV')),
4140 ('c', 'change', '',
4140 ('c', 'change', '',
4141 _('change made by revision'), _('REV'))
4141 _('change made by revision'), _('REV'))
4142 ] + diffopts + diffopts2 + walkopts,
4142 ] + diffopts + diffopts2 + walkopts,
4143 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4143 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4144 "^export":
4144 "^export":
4145 (export,
4145 (export,
4146 [('o', 'output', '',
4146 [('o', 'output', '',
4147 _('print output to file with formatted name'), _('FORMAT')),
4147 _('print output to file with formatted name'), _('FORMAT')),
4148 ('', 'switch-parent', None, _('diff against the second parent')),
4148 ('', 'switch-parent', None, _('diff against the second parent')),
4149 ('r', 'rev', [],
4149 ('r', 'rev', [],
4150 _('revisions to export'), _('REV')),
4150 _('revisions to export'), _('REV')),
4151 ] + diffopts,
4151 ] + diffopts,
4152 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4152 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4153 "^forget":
4153 "^forget":
4154 (forget,
4154 (forget,
4155 [] + walkopts,
4155 [] + walkopts,
4156 _('[OPTION]... FILE...')),
4156 _('[OPTION]... FILE...')),
4157 "grep":
4157 "grep":
4158 (grep,
4158 (grep,
4159 [('0', 'print0', None, _('end fields with NUL')),
4159 [('0', 'print0', None, _('end fields with NUL')),
4160 ('', 'all', None, _('print all revisions that match')),
4160 ('', 'all', None, _('print all revisions that match')),
4161 ('f', 'follow', None,
4161 ('f', 'follow', None,
4162 _('follow changeset history,'
4162 _('follow changeset history,'
4163 ' or file history across copies and renames')),
4163 ' or file history across copies and renames')),
4164 ('i', 'ignore-case', None, _('ignore case when matching')),
4164 ('i', 'ignore-case', None, _('ignore case when matching')),
4165 ('l', 'files-with-matches', None,
4165 ('l', 'files-with-matches', None,
4166 _('print only filenames and revisions that match')),
4166 _('print only filenames and revisions that match')),
4167 ('n', 'line-number', None, _('print matching line numbers')),
4167 ('n', 'line-number', None, _('print matching line numbers')),
4168 ('r', 'rev', [],
4168 ('r', 'rev', [],
4169 _('only search files changed within revision range'), _('REV')),
4169 _('only search files changed within revision range'), _('REV')),
4170 ('u', 'user', None, _('list the author (long with -v)')),
4170 ('u', 'user', None, _('list the author (long with -v)')),
4171 ('d', 'date', None, _('list the date (short with -q)')),
4171 ('d', 'date', None, _('list the date (short with -q)')),
4172 ] + walkopts,
4172 ] + walkopts,
4173 _('[OPTION]... PATTERN [FILE]...')),
4173 _('[OPTION]... PATTERN [FILE]...')),
4174 "heads":
4174 "heads":
4175 (heads,
4175 (heads,
4176 [('r', 'rev', '',
4176 [('r', 'rev', '',
4177 _('show only heads which are descendants of REV'), _('REV')),
4177 _('show only heads which are descendants of REV'), _('REV')),
4178 ('t', 'topo', False, _('show topological heads only')),
4178 ('t', 'topo', False, _('show topological heads only')),
4179 ('a', 'active', False,
4179 ('a', 'active', False,
4180 _('show active branchheads only [DEPRECATED]')),
4180 _('show active branchheads only (DEPRECATED)')),
4181 ('c', 'closed', False,
4181 ('c', 'closed', False,
4182 _('show normal and closed branch heads')),
4182 _('show normal and closed branch heads')),
4183 ] + templateopts,
4183 ] + templateopts,
4184 _('[-ac] [-r REV] [REV]...')),
4184 _('[-ac] [-r REV] [REV]...')),
4185 "help": (help_, [], _('[TOPIC]')),
4185 "help": (help_, [], _('[TOPIC]')),
4186 "identify|id":
4186 "identify|id":
4187 (identify,
4187 (identify,
4188 [('r', 'rev', '',
4188 [('r', 'rev', '',
4189 _('identify the specified revision'), _('REV')),
4189 _('identify the specified revision'), _('REV')),
4190 ('n', 'num', None, _('show local revision number')),
4190 ('n', 'num', None, _('show local revision number')),
4191 ('i', 'id', None, _('show global revision id')),
4191 ('i', 'id', None, _('show global revision id')),
4192 ('b', 'branch', None, _('show branch')),
4192 ('b', 'branch', None, _('show branch')),
4193 ('t', 'tags', None, _('show tags'))],
4193 ('t', 'tags', None, _('show tags'))],
4194 _('[-nibt] [-r REV] [SOURCE]')),
4194 _('[-nibt] [-r REV] [SOURCE]')),
4195 "import|patch":
4195 "import|patch":
4196 (import_,
4196 (import_,
4197 [('p', 'strip', 1,
4197 [('p', 'strip', 1,
4198 _('directory strip option for patch. This has the same '
4198 _('directory strip option for patch. This has the same '
4199 'meaning as the corresponding patch option'),
4199 'meaning as the corresponding patch option'),
4200 _('NUM')),
4200 _('NUM')),
4201 ('b', 'base', '',
4201 ('b', 'base', '',
4202 _('base path'), _('PATH')),
4202 _('base path'), _('PATH')),
4203 ('f', 'force', None,
4203 ('f', 'force', None,
4204 _('skip check for outstanding uncommitted changes')),
4204 _('skip check for outstanding uncommitted changes')),
4205 ('', 'no-commit', None,
4205 ('', 'no-commit', None,
4206 _("don't commit, just update the working directory")),
4206 _("don't commit, just update the working directory")),
4207 ('', 'exact', None,
4207 ('', 'exact', None,
4208 _('apply patch to the nodes from which it was generated')),
4208 _('apply patch to the nodes from which it was generated')),
4209 ('', 'import-branch', None,
4209 ('', 'import-branch', None,
4210 _('use any branch information in patch (implied by --exact)'))] +
4210 _('use any branch information in patch (implied by --exact)'))] +
4211 commitopts + commitopts2 + similarityopts,
4211 commitopts + commitopts2 + similarityopts,
4212 _('[OPTION]... PATCH...')),
4212 _('[OPTION]... PATCH...')),
4213 "incoming|in":
4213 "incoming|in":
4214 (incoming,
4214 (incoming,
4215 [('f', 'force', None,
4215 [('f', 'force', None,
4216 _('run even if remote repository is unrelated')),
4216 _('run even if remote repository is unrelated')),
4217 ('n', 'newest-first', None, _('show newest record first')),
4217 ('n', 'newest-first', None, _('show newest record first')),
4218 ('', 'bundle', '',
4218 ('', 'bundle', '',
4219 _('file to store the bundles into'), _('FILE')),
4219 _('file to store the bundles into'), _('FILE')),
4220 ('r', 'rev', [],
4220 ('r', 'rev', [],
4221 _('a remote changeset intended to be added'), _('REV')),
4221 _('a remote changeset intended to be added'), _('REV')),
4222 ('b', 'branch', [],
4222 ('b', 'branch', [],
4223 _('a specific branch you would like to pull'), _('BRANCH')),
4223 _('a specific branch you would like to pull'), _('BRANCH')),
4224 ] + logopts + remoteopts,
4224 ] + logopts + remoteopts,
4225 _('[-p] [-n] [-M] [-f] [-r REV]...'
4225 _('[-p] [-n] [-M] [-f] [-r REV]...'
4226 ' [--bundle FILENAME] [SOURCE]')),
4226 ' [--bundle FILENAME] [SOURCE]')),
4227 "^init":
4227 "^init":
4228 (init,
4228 (init,
4229 remoteopts,
4229 remoteopts,
4230 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4230 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4231 "locate":
4231 "locate":
4232 (locate,
4232 (locate,
4233 [('r', 'rev', '',
4233 [('r', 'rev', '',
4234 _('search the repository as it is in REV'), _('REV')),
4234 _('search the repository as it is in REV'), _('REV')),
4235 ('0', 'print0', None,
4235 ('0', 'print0', None,
4236 _('end filenames with NUL, for use with xargs')),
4236 _('end filenames with NUL, for use with xargs')),
4237 ('f', 'fullpath', None,
4237 ('f', 'fullpath', None,
4238 _('print complete paths from the filesystem root')),
4238 _('print complete paths from the filesystem root')),
4239 ] + walkopts,
4239 ] + walkopts,
4240 _('[OPTION]... [PATTERN]...')),
4240 _('[OPTION]... [PATTERN]...')),
4241 "^log|history":
4241 "^log|history":
4242 (log,
4242 (log,
4243 [('f', 'follow', None,
4243 [('f', 'follow', None,
4244 _('follow changeset history,'
4244 _('follow changeset history,'
4245 ' or file history across copies and renames')),
4245 ' or file history across copies and renames')),
4246 ('', 'follow-first', None,
4246 ('', 'follow-first', None,
4247 _('only follow the first parent of merge changesets')),
4247 _('only follow the first parent of merge changesets')),
4248 ('d', 'date', '',
4248 ('d', 'date', '',
4249 _('show revisions matching date spec'), _('DATE')),
4249 _('show revisions matching date spec'), _('DATE')),
4250 ('C', 'copies', None, _('show copied files')),
4250 ('C', 'copies', None, _('show copied files')),
4251 ('k', 'keyword', [],
4251 ('k', 'keyword', [],
4252 _('do case-insensitive search for a given text'), _('TEXT')),
4252 _('do case-insensitive search for a given text'), _('TEXT')),
4253 ('r', 'rev', [],
4253 ('r', 'rev', [],
4254 _('show the specified revision or range'), _('REV')),
4254 _('show the specified revision or range'), _('REV')),
4255 ('', 'removed', None, _('include revisions where files were removed')),
4255 ('', 'removed', None, _('include revisions where files were removed')),
4256 ('m', 'only-merges', None, _('show only merges')),
4256 ('m', 'only-merges', None, _('show only merges')),
4257 ('u', 'user', [],
4257 ('u', 'user', [],
4258 _('revisions committed by user'), _('USER')),
4258 _('revisions committed by user'), _('USER')),
4259 ('', 'only-branch', [],
4259 ('', 'only-branch', [],
4260 _('show only changesets within the given named branch (DEPRECATED)'),
4260 _('show only changesets within the given named branch (DEPRECATED)'),
4261 _('BRANCH')),
4261 _('BRANCH')),
4262 ('b', 'branch', [],
4262 ('b', 'branch', [],
4263 _('show changesets within the given named branch'), _('BRANCH')),
4263 _('show changesets within the given named branch'), _('BRANCH')),
4264 ('P', 'prune', [],
4264 ('P', 'prune', [],
4265 _('do not display revision or any of its ancestors'), _('REV')),
4265 _('do not display revision or any of its ancestors'), _('REV')),
4266 ] + logopts + walkopts,
4266 ] + logopts + walkopts,
4267 _('[OPTION]... [FILE]')),
4267 _('[OPTION]... [FILE]')),
4268 "manifest":
4268 "manifest":
4269 (manifest,
4269 (manifest,
4270 [('r', 'rev', '',
4270 [('r', 'rev', '',
4271 _('revision to display'), _('REV'))],
4271 _('revision to display'), _('REV'))],
4272 _('[-r REV]')),
4272 _('[-r REV]')),
4273 "^merge":
4273 "^merge":
4274 (merge,
4274 (merge,
4275 [('f', 'force', None, _('force a merge with outstanding changes')),
4275 [('f', 'force', None, _('force a merge with outstanding changes')),
4276 ('r', 'rev', '',
4276 ('r', 'rev', '',
4277 _('revision to merge'), _('REV')),
4277 _('revision to merge'), _('REV')),
4278 ('P', 'preview', None,
4278 ('P', 'preview', None,
4279 _('review revisions to merge (no merge is performed)'))],
4279 _('review revisions to merge (no merge is performed)'))],
4280 _('[-P] [-f] [[-r] REV]')),
4280 _('[-P] [-f] [[-r] REV]')),
4281 "outgoing|out":
4281 "outgoing|out":
4282 (outgoing,
4282 (outgoing,
4283 [('f', 'force', None,
4283 [('f', 'force', None,
4284 _('run even when the destination is unrelated')),
4284 _('run even when the destination is unrelated')),
4285 ('r', 'rev', [],
4285 ('r', 'rev', [],
4286 _('a changeset intended to be included in the destination'),
4286 _('a changeset intended to be included in the destination'),
4287 _('REV')),
4287 _('REV')),
4288 ('n', 'newest-first', None, _('show newest record first')),
4288 ('n', 'newest-first', None, _('show newest record first')),
4289 ('b', 'branch', [],
4289 ('b', 'branch', [],
4290 _('a specific branch you would like to push'), _('BRANCH')),
4290 _('a specific branch you would like to push'), _('BRANCH')),
4291 ] + logopts + remoteopts,
4291 ] + logopts + remoteopts,
4292 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4292 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4293 "parents":
4293 "parents":
4294 (parents,
4294 (parents,
4295 [('r', 'rev', '',
4295 [('r', 'rev', '',
4296 _('show parents of the specified revision'), _('REV')),
4296 _('show parents of the specified revision'), _('REV')),
4297 ] + templateopts,
4297 ] + templateopts,
4298 _('[-r REV] [FILE]')),
4298 _('[-r REV] [FILE]')),
4299 "paths": (paths, [], _('[NAME]')),
4299 "paths": (paths, [], _('[NAME]')),
4300 "^pull":
4300 "^pull":
4301 (pull,
4301 (pull,
4302 [('u', 'update', None,
4302 [('u', 'update', None,
4303 _('update to new branch head if changesets were pulled')),
4303 _('update to new branch head if changesets were pulled')),
4304 ('f', 'force', None,
4304 ('f', 'force', None,
4305 _('run even when remote repository is unrelated')),
4305 _('run even when remote repository is unrelated')),
4306 ('r', 'rev', [],
4306 ('r', 'rev', [],
4307 _('a remote changeset intended to be added'), _('REV')),
4307 _('a remote changeset intended to be added'), _('REV')),
4308 ('b', 'branch', [],
4308 ('b', 'branch', [],
4309 _('a specific branch you would like to pull'), _('BRANCH')),
4309 _('a specific branch you would like to pull'), _('BRANCH')),
4310 ] + remoteopts,
4310 ] + remoteopts,
4311 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4311 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4312 "^push":
4312 "^push":
4313 (push,
4313 (push,
4314 [('f', 'force', None, _('force push')),
4314 [('f', 'force', None, _('force push')),
4315 ('r', 'rev', [],
4315 ('r', 'rev', [],
4316 _('a changeset intended to be included in the destination'),
4316 _('a changeset intended to be included in the destination'),
4317 _('REV')),
4317 _('REV')),
4318 ('b', 'branch', [],
4318 ('b', 'branch', [],
4319 _('a specific branch you would like to push'), _('BRANCH')),
4319 _('a specific branch you would like to push'), _('BRANCH')),
4320 ('', 'new-branch', False, _('allow pushing a new branch')),
4320 ('', 'new-branch', False, _('allow pushing a new branch')),
4321 ] + remoteopts,
4321 ] + remoteopts,
4322 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4322 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4323 "recover": (recover, []),
4323 "recover": (recover, []),
4324 "^remove|rm":
4324 "^remove|rm":
4325 (remove,
4325 (remove,
4326 [('A', 'after', None, _('record delete for missing files')),
4326 [('A', 'after', None, _('record delete for missing files')),
4327 ('f', 'force', None,
4327 ('f', 'force', None,
4328 _('remove (and delete) file even if added or modified')),
4328 _('remove (and delete) file even if added or modified')),
4329 ] + walkopts,
4329 ] + walkopts,
4330 _('[OPTION]... FILE...')),
4330 _('[OPTION]... FILE...')),
4331 "rename|mv":
4331 "rename|mv":
4332 (rename,
4332 (rename,
4333 [('A', 'after', None, _('record a rename that has already occurred')),
4333 [('A', 'after', None, _('record a rename that has already occurred')),
4334 ('f', 'force', None,
4334 ('f', 'force', None,
4335 _('forcibly copy over an existing managed file')),
4335 _('forcibly copy over an existing managed file')),
4336 ] + walkopts + dryrunopts,
4336 ] + walkopts + dryrunopts,
4337 _('[OPTION]... SOURCE... DEST')),
4337 _('[OPTION]... SOURCE... DEST')),
4338 "resolve":
4338 "resolve":
4339 (resolve,
4339 (resolve,
4340 [('a', 'all', None, _('select all unresolved files')),
4340 [('a', 'all', None, _('select all unresolved files')),
4341 ('l', 'list', None, _('list state of files needing merge')),
4341 ('l', 'list', None, _('list state of files needing merge')),
4342 ('m', 'mark', None, _('mark files as resolved')),
4342 ('m', 'mark', None, _('mark files as resolved')),
4343 ('u', 'unmark', None, _('unmark files as resolved')),
4343 ('u', 'unmark', None, _('unmark files as resolved')),
4344 ('n', 'no-status', None, _('hide status prefix'))]
4344 ('n', 'no-status', None, _('hide status prefix'))]
4345 + walkopts,
4345 + walkopts,
4346 _('[OPTION]... [FILE]...')),
4346 _('[OPTION]... [FILE]...')),
4347 "revert":
4347 "revert":
4348 (revert,
4348 (revert,
4349 [('a', 'all', None, _('revert all changes when no arguments given')),
4349 [('a', 'all', None, _('revert all changes when no arguments given')),
4350 ('d', 'date', '',
4350 ('d', 'date', '',
4351 _('tipmost revision matching date'), _('DATE')),
4351 _('tipmost revision matching date'), _('DATE')),
4352 ('r', 'rev', '',
4352 ('r', 'rev', '',
4353 _('revert to the specified revision'), _('REV')),
4353 _('revert to the specified revision'), _('REV')),
4354 ('', 'no-backup', None, _('do not save backup copies of files')),
4354 ('', 'no-backup', None, _('do not save backup copies of files')),
4355 ] + walkopts + dryrunopts,
4355 ] + walkopts + dryrunopts,
4356 _('[OPTION]... [-r REV] [NAME]...')),
4356 _('[OPTION]... [-r REV] [NAME]...')),
4357 "rollback": (rollback, dryrunopts),
4357 "rollback": (rollback, dryrunopts),
4358 "root": (root, []),
4358 "root": (root, []),
4359 "^serve":
4359 "^serve":
4360 (serve,
4360 (serve,
4361 [('A', 'accesslog', '',
4361 [('A', 'accesslog', '',
4362 _('name of access log file to write to'), _('FILE')),
4362 _('name of access log file to write to'), _('FILE')),
4363 ('d', 'daemon', None, _('run server in background')),
4363 ('d', 'daemon', None, _('run server in background')),
4364 ('', 'daemon-pipefds', '',
4364 ('', 'daemon-pipefds', '',
4365 _('used internally by daemon mode'), _('NUM')),
4365 _('used internally by daemon mode'), _('NUM')),
4366 ('E', 'errorlog', '',
4366 ('E', 'errorlog', '',
4367 _('name of error log file to write to'), _('FILE')),
4367 _('name of error log file to write to'), _('FILE')),
4368 # use string type, then we can check if something was passed
4368 # use string type, then we can check if something was passed
4369 ('p', 'port', '',
4369 ('p', 'port', '',
4370 _('port to listen on (default: 8000)'), _('PORT')),
4370 _('port to listen on (default: 8000)'), _('PORT')),
4371 ('a', 'address', '',
4371 ('a', 'address', '',
4372 _('address to listen on (default: all interfaces)'), _('ADDR')),
4372 _('address to listen on (default: all interfaces)'), _('ADDR')),
4373 ('', 'prefix', '',
4373 ('', 'prefix', '',
4374 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4374 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4375 ('n', 'name', '',
4375 ('n', 'name', '',
4376 _('name to show in web pages (default: working directory)'),
4376 _('name to show in web pages (default: working directory)'),
4377 _('NAME')),
4377 _('NAME')),
4378 ('', 'web-conf', '',
4378 ('', 'web-conf', '',
4379 _('name of the hgweb config file (serve more than one repository)'),
4379 _('name of the hgweb config file (serve more than one repository)'),
4380 _('FILE')),
4380 _('FILE')),
4381 ('', 'webdir-conf', '',
4381 ('', 'webdir-conf', '',
4382 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4382 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4383 ('', 'pid-file', '',
4383 ('', 'pid-file', '',
4384 _('name of file to write process ID to'), _('FILE')),
4384 _('name of file to write process ID to'), _('FILE')),
4385 ('', 'stdio', None, _('for remote clients')),
4385 ('', 'stdio', None, _('for remote clients')),
4386 ('t', 'templates', '',
4386 ('t', 'templates', '',
4387 _('web templates to use'), _('TEMPLATE')),
4387 _('web templates to use'), _('TEMPLATE')),
4388 ('', 'style', '',
4388 ('', 'style', '',
4389 _('template style to use'), _('STYLE')),
4389 _('template style to use'), _('STYLE')),
4390 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4390 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4391 ('', 'certificate', '',
4391 ('', 'certificate', '',
4392 _('SSL certificate file'), _('FILE'))],
4392 _('SSL certificate file'), _('FILE'))],
4393 _('[OPTION]...')),
4393 _('[OPTION]...')),
4394 "showconfig|debugconfig":
4394 "showconfig|debugconfig":
4395 (showconfig,
4395 (showconfig,
4396 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4396 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4397 _('[-u] [NAME]...')),
4397 _('[-u] [NAME]...')),
4398 "^summary|sum":
4398 "^summary|sum":
4399 (summary,
4399 (summary,
4400 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4400 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4401 "^status|st":
4401 "^status|st":
4402 (status,
4402 (status,
4403 [('A', 'all', None, _('show status of all files')),
4403 [('A', 'all', None, _('show status of all files')),
4404 ('m', 'modified', None, _('show only modified files')),
4404 ('m', 'modified', None, _('show only modified files')),
4405 ('a', 'added', None, _('show only added files')),
4405 ('a', 'added', None, _('show only added files')),
4406 ('r', 'removed', None, _('show only removed files')),
4406 ('r', 'removed', None, _('show only removed files')),
4407 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4407 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4408 ('c', 'clean', None, _('show only files without changes')),
4408 ('c', 'clean', None, _('show only files without changes')),
4409 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4409 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4410 ('i', 'ignored', None, _('show only ignored files')),
4410 ('i', 'ignored', None, _('show only ignored files')),
4411 ('n', 'no-status', None, _('hide status prefix')),
4411 ('n', 'no-status', None, _('hide status prefix')),
4412 ('C', 'copies', None, _('show source of copied files')),
4412 ('C', 'copies', None, _('show source of copied files')),
4413 ('0', 'print0', None,
4413 ('0', 'print0', None,
4414 _('end filenames with NUL, for use with xargs')),
4414 _('end filenames with NUL, for use with xargs')),
4415 ('', 'rev', [],
4415 ('', 'rev', [],
4416 _('show difference from revision'), _('REV')),
4416 _('show difference from revision'), _('REV')),
4417 ('', 'change', '',
4417 ('', 'change', '',
4418 _('list the changed files of a revision'), _('REV')),
4418 _('list the changed files of a revision'), _('REV')),
4419 ] + walkopts,
4419 ] + walkopts,
4420 _('[OPTION]... [FILE]...')),
4420 _('[OPTION]... [FILE]...')),
4421 "tag":
4421 "tag":
4422 (tag,
4422 (tag,
4423 [('f', 'force', None, _('replace existing tag')),
4423 [('f', 'force', None, _('replace existing tag')),
4424 ('l', 'local', None, _('make the tag local')),
4424 ('l', 'local', None, _('make the tag local')),
4425 ('r', 'rev', '',
4425 ('r', 'rev', '',
4426 _('revision to tag'), _('REV')),
4426 _('revision to tag'), _('REV')),
4427 ('', 'remove', None, _('remove a tag')),
4427 ('', 'remove', None, _('remove a tag')),
4428 # -l/--local is already there, commitopts cannot be used
4428 # -l/--local is already there, commitopts cannot be used
4429 ('e', 'edit', None, _('edit commit message')),
4429 ('e', 'edit', None, _('edit commit message')),
4430 ('m', 'message', '',
4430 ('m', 'message', '',
4431 _('use <text> as commit message'), _('TEXT')),
4431 _('use <text> as commit message'), _('TEXT')),
4432 ] + commitopts2,
4432 ] + commitopts2,
4433 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4433 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4434 "tags": (tags, [], ''),
4434 "tags": (tags, [], ''),
4435 "tip":
4435 "tip":
4436 (tip,
4436 (tip,
4437 [('p', 'patch', None, _('show patch')),
4437 [('p', 'patch', None, _('show patch')),
4438 ('g', 'git', None, _('use git extended diff format')),
4438 ('g', 'git', None, _('use git extended diff format')),
4439 ] + templateopts,
4439 ] + templateopts,
4440 _('[-p] [-g]')),
4440 _('[-p] [-g]')),
4441 "unbundle":
4441 "unbundle":
4442 (unbundle,
4442 (unbundle,
4443 [('u', 'update', None,
4443 [('u', 'update', None,
4444 _('update to new branch head if changesets were unbundled'))],
4444 _('update to new branch head if changesets were unbundled'))],
4445 _('[-u] FILE...')),
4445 _('[-u] FILE...')),
4446 "^update|up|checkout|co":
4446 "^update|up|checkout|co":
4447 (update,
4447 (update,
4448 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4448 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4449 ('c', 'check', None, _('check for uncommitted changes')),
4449 ('c', 'check', None, _('check for uncommitted changes')),
4450 ('d', 'date', '',
4450 ('d', 'date', '',
4451 _('tipmost revision matching date'), _('DATE')),
4451 _('tipmost revision matching date'), _('DATE')),
4452 ('r', 'rev', '',
4452 ('r', 'rev', '',
4453 _('revision'), _('REV'))],
4453 _('revision'), _('REV'))],
4454 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4454 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4455 "verify": (verify, []),
4455 "verify": (verify, []),
4456 "version": (version_, []),
4456 "version": (version_, []),
4457 }
4457 }
4458
4458
4459 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4459 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4460 " debugindex debugindexdot debugdate debuginstall debugfsinfo"
4460 " debugindex debugindexdot debugdate debuginstall debugfsinfo"
4461 " debugpushkey")
4461 " debugpushkey")
4462 optionalrepo = ("identify paths serve showconfig debugancestor debugdag")
4462 optionalrepo = ("identify paths serve showconfig debugancestor debugdag")
General Comments 0
You need to be logged in to leave comments. Login now