##// END OF EJS Templates
util: add copyfile function
Matt Mackall -
r3629:4cfb72bc default
parent child Browse files
Show More
@@ -1,3541 +1,3537 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
11 demandload(globals(), "os re sys signal imp urllib pdb shlex")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "difflib patch tempfile time")
13 demandload(globals(), "difflib patch tempfile time")
14 demandload(globals(), "traceback errno version atexit sets bz2")
14 demandload(globals(), "traceback errno version atexit sets bz2")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def relpath(repo, args):
27 def relpath(repo, args):
28 cwd = repo.getcwd()
28 cwd = repo.getcwd()
29 if cwd:
29 if cwd:
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return args
31 return args
32
32
33 def logmessage(opts):
33 def logmessage(opts):
34 """ get the log message according to -m and -l option """
34 """ get the log message according to -m and -l option """
35 message = opts['message']
35 message = opts['message']
36 logfile = opts['logfile']
36 logfile = opts['logfile']
37
37
38 if message and logfile:
38 if message and logfile:
39 raise util.Abort(_('options --message and --logfile are mutually '
39 raise util.Abort(_('options --message and --logfile are mutually '
40 'exclusive'))
40 'exclusive'))
41 if not message and logfile:
41 if not message and logfile:
42 try:
42 try:
43 if logfile == '-':
43 if logfile == '-':
44 message = sys.stdin.read()
44 message = sys.stdin.read()
45 else:
45 else:
46 message = open(logfile).read()
46 message = open(logfile).read()
47 except IOError, inst:
47 except IOError, inst:
48 raise util.Abort(_("can't read commit message '%s': %s") %
48 raise util.Abort(_("can't read commit message '%s': %s") %
49 (logfile, inst.strerror))
49 (logfile, inst.strerror))
50 return message
50 return message
51
51
52 def walkchangerevs(ui, repo, pats, change, opts):
52 def walkchangerevs(ui, repo, pats, change, opts):
53 '''Iterate over files and the revs they changed in.
53 '''Iterate over files and the revs they changed in.
54
54
55 Callers most commonly need to iterate backwards over the history
55 Callers most commonly need to iterate backwards over the history
56 it is interested in. Doing so has awful (quadratic-looking)
56 it is interested in. Doing so has awful (quadratic-looking)
57 performance, so we use iterators in a "windowed" way.
57 performance, so we use iterators in a "windowed" way.
58
58
59 We walk a window of revisions in the desired order. Within the
59 We walk a window of revisions in the desired order. Within the
60 window, we first walk forwards to gather data, then in the desired
60 window, we first walk forwards to gather data, then in the desired
61 order (usually backwards) to display it.
61 order (usually backwards) to display it.
62
62
63 This function returns an (iterator, matchfn) tuple. The iterator
63 This function returns an (iterator, matchfn) tuple. The iterator
64 yields 3-tuples. They will be of one of the following forms:
64 yields 3-tuples. They will be of one of the following forms:
65
65
66 "window", incrementing, lastrev: stepping through a window,
66 "window", incrementing, lastrev: stepping through a window,
67 positive if walking forwards through revs, last rev in the
67 positive if walking forwards through revs, last rev in the
68 sequence iterated over - use to reset state for the current window
68 sequence iterated over - use to reset state for the current window
69
69
70 "add", rev, fns: out-of-order traversal of the given file names
70 "add", rev, fns: out-of-order traversal of the given file names
71 fns, which changed during revision rev - use to gather data for
71 fns, which changed during revision rev - use to gather data for
72 possible display
72 possible display
73
73
74 "iter", rev, None: in-order traversal of the revs earlier iterated
74 "iter", rev, None: in-order traversal of the revs earlier iterated
75 over with "add" - use to display data'''
75 over with "add" - use to display data'''
76
76
77 def increasing_windows(start, end, windowsize=8, sizelimit=512):
77 def increasing_windows(start, end, windowsize=8, sizelimit=512):
78 if start < end:
78 if start < end:
79 while start < end:
79 while start < end:
80 yield start, min(windowsize, end-start)
80 yield start, min(windowsize, end-start)
81 start += windowsize
81 start += windowsize
82 if windowsize < sizelimit:
82 if windowsize < sizelimit:
83 windowsize *= 2
83 windowsize *= 2
84 else:
84 else:
85 while start > end:
85 while start > end:
86 yield start, min(windowsize, start-end-1)
86 yield start, min(windowsize, start-end-1)
87 start -= windowsize
87 start -= windowsize
88 if windowsize < sizelimit:
88 if windowsize < sizelimit:
89 windowsize *= 2
89 windowsize *= 2
90
90
91 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
91 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
92 follow = opts.get('follow') or opts.get('follow_first')
92 follow = opts.get('follow') or opts.get('follow_first')
93
93
94 if repo.changelog.count() == 0:
94 if repo.changelog.count() == 0:
95 return [], matchfn
95 return [], matchfn
96
96
97 if follow:
97 if follow:
98 defrange = '%s:0' % repo.changectx().rev()
98 defrange = '%s:0' % repo.changectx().rev()
99 else:
99 else:
100 defrange = 'tip:0'
100 defrange = 'tip:0'
101 revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange])
101 revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange])
102 wanted = {}
102 wanted = {}
103 slowpath = anypats
103 slowpath = anypats
104 fncache = {}
104 fncache = {}
105
105
106 if not slowpath and not files:
106 if not slowpath and not files:
107 # No files, no patterns. Display all revs.
107 # No files, no patterns. Display all revs.
108 wanted = dict.fromkeys(revs)
108 wanted = dict.fromkeys(revs)
109 copies = []
109 copies = []
110 if not slowpath:
110 if not slowpath:
111 # Only files, no patterns. Check the history of each file.
111 # Only files, no patterns. Check the history of each file.
112 def filerevgen(filelog, node):
112 def filerevgen(filelog, node):
113 cl_count = repo.changelog.count()
113 cl_count = repo.changelog.count()
114 if node is None:
114 if node is None:
115 last = filelog.count() - 1
115 last = filelog.count() - 1
116 else:
116 else:
117 last = filelog.rev(node)
117 last = filelog.rev(node)
118 for i, window in increasing_windows(last, nullrev):
118 for i, window in increasing_windows(last, nullrev):
119 revs = []
119 revs = []
120 for j in xrange(i - window, i + 1):
120 for j in xrange(i - window, i + 1):
121 n = filelog.node(j)
121 n = filelog.node(j)
122 revs.append((filelog.linkrev(n),
122 revs.append((filelog.linkrev(n),
123 follow and filelog.renamed(n)))
123 follow and filelog.renamed(n)))
124 revs.reverse()
124 revs.reverse()
125 for rev in revs:
125 for rev in revs:
126 # only yield rev for which we have the changelog, it can
126 # only yield rev for which we have the changelog, it can
127 # happen while doing "hg log" during a pull or commit
127 # happen while doing "hg log" during a pull or commit
128 if rev[0] < cl_count:
128 if rev[0] < cl_count:
129 yield rev
129 yield rev
130 def iterfiles():
130 def iterfiles():
131 for filename in files:
131 for filename in files:
132 yield filename, None
132 yield filename, None
133 for filename_node in copies:
133 for filename_node in copies:
134 yield filename_node
134 yield filename_node
135 minrev, maxrev = min(revs), max(revs)
135 minrev, maxrev = min(revs), max(revs)
136 for file_, node in iterfiles():
136 for file_, node in iterfiles():
137 filelog = repo.file(file_)
137 filelog = repo.file(file_)
138 # A zero count may be a directory or deleted file, so
138 # A zero count may be a directory or deleted file, so
139 # try to find matching entries on the slow path.
139 # try to find matching entries on the slow path.
140 if filelog.count() == 0:
140 if filelog.count() == 0:
141 slowpath = True
141 slowpath = True
142 break
142 break
143 for rev, copied in filerevgen(filelog, node):
143 for rev, copied in filerevgen(filelog, node):
144 if rev <= maxrev:
144 if rev <= maxrev:
145 if rev < minrev:
145 if rev < minrev:
146 break
146 break
147 fncache.setdefault(rev, [])
147 fncache.setdefault(rev, [])
148 fncache[rev].append(file_)
148 fncache[rev].append(file_)
149 wanted[rev] = 1
149 wanted[rev] = 1
150 if follow and copied:
150 if follow and copied:
151 copies.append(copied)
151 copies.append(copied)
152 if slowpath:
152 if slowpath:
153 if follow:
153 if follow:
154 raise util.Abort(_('can only follow copies/renames for explicit '
154 raise util.Abort(_('can only follow copies/renames for explicit '
155 'file names'))
155 'file names'))
156
156
157 # The slow path checks files modified in every changeset.
157 # The slow path checks files modified in every changeset.
158 def changerevgen():
158 def changerevgen():
159 for i, window in increasing_windows(repo.changelog.count()-1,
159 for i, window in increasing_windows(repo.changelog.count()-1,
160 nullrev):
160 nullrev):
161 for j in xrange(i - window, i + 1):
161 for j in xrange(i - window, i + 1):
162 yield j, change(j)[3]
162 yield j, change(j)[3]
163
163
164 for rev, changefiles in changerevgen():
164 for rev, changefiles in changerevgen():
165 matches = filter(matchfn, changefiles)
165 matches = filter(matchfn, changefiles)
166 if matches:
166 if matches:
167 fncache[rev] = matches
167 fncache[rev] = matches
168 wanted[rev] = 1
168 wanted[rev] = 1
169
169
170 class followfilter:
170 class followfilter:
171 def __init__(self, onlyfirst=False):
171 def __init__(self, onlyfirst=False):
172 self.startrev = nullrev
172 self.startrev = nullrev
173 self.roots = []
173 self.roots = []
174 self.onlyfirst = onlyfirst
174 self.onlyfirst = onlyfirst
175
175
176 def match(self, rev):
176 def match(self, rev):
177 def realparents(rev):
177 def realparents(rev):
178 if self.onlyfirst:
178 if self.onlyfirst:
179 return repo.changelog.parentrevs(rev)[0:1]
179 return repo.changelog.parentrevs(rev)[0:1]
180 else:
180 else:
181 return filter(lambda x: x != nullrev,
181 return filter(lambda x: x != nullrev,
182 repo.changelog.parentrevs(rev))
182 repo.changelog.parentrevs(rev))
183
183
184 if self.startrev == nullrev:
184 if self.startrev == nullrev:
185 self.startrev = rev
185 self.startrev = rev
186 return True
186 return True
187
187
188 if rev > self.startrev:
188 if rev > self.startrev:
189 # forward: all descendants
189 # forward: all descendants
190 if not self.roots:
190 if not self.roots:
191 self.roots.append(self.startrev)
191 self.roots.append(self.startrev)
192 for parent in realparents(rev):
192 for parent in realparents(rev):
193 if parent in self.roots:
193 if parent in self.roots:
194 self.roots.append(rev)
194 self.roots.append(rev)
195 return True
195 return True
196 else:
196 else:
197 # backwards: all parents
197 # backwards: all parents
198 if not self.roots:
198 if not self.roots:
199 self.roots.extend(realparents(self.startrev))
199 self.roots.extend(realparents(self.startrev))
200 if rev in self.roots:
200 if rev in self.roots:
201 self.roots.remove(rev)
201 self.roots.remove(rev)
202 self.roots.extend(realparents(rev))
202 self.roots.extend(realparents(rev))
203 return True
203 return True
204
204
205 return False
205 return False
206
206
207 # it might be worthwhile to do this in the iterator if the rev range
207 # it might be worthwhile to do this in the iterator if the rev range
208 # is descending and the prune args are all within that range
208 # is descending and the prune args are all within that range
209 for rev in opts.get('prune', ()):
209 for rev in opts.get('prune', ()):
210 rev = repo.changelog.rev(repo.lookup(rev))
210 rev = repo.changelog.rev(repo.lookup(rev))
211 ff = followfilter()
211 ff = followfilter()
212 stop = min(revs[0], revs[-1])
212 stop = min(revs[0], revs[-1])
213 for x in xrange(rev, stop-1, -1):
213 for x in xrange(rev, stop-1, -1):
214 if ff.match(x) and x in wanted:
214 if ff.match(x) and x in wanted:
215 del wanted[x]
215 del wanted[x]
216
216
217 def iterate():
217 def iterate():
218 if follow and not files:
218 if follow and not files:
219 ff = followfilter(onlyfirst=opts.get('follow_first'))
219 ff = followfilter(onlyfirst=opts.get('follow_first'))
220 def want(rev):
220 def want(rev):
221 if ff.match(rev) and rev in wanted:
221 if ff.match(rev) and rev in wanted:
222 return True
222 return True
223 return False
223 return False
224 else:
224 else:
225 def want(rev):
225 def want(rev):
226 return rev in wanted
226 return rev in wanted
227
227
228 for i, window in increasing_windows(0, len(revs)):
228 for i, window in increasing_windows(0, len(revs)):
229 yield 'window', revs[0] < revs[-1], revs[-1]
229 yield 'window', revs[0] < revs[-1], revs[-1]
230 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
230 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
231 srevs = list(nrevs)
231 srevs = list(nrevs)
232 srevs.sort()
232 srevs.sort()
233 for rev in srevs:
233 for rev in srevs:
234 fns = fncache.get(rev)
234 fns = fncache.get(rev)
235 if not fns:
235 if not fns:
236 def fns_generator():
236 def fns_generator():
237 for f in change(rev)[3]:
237 for f in change(rev)[3]:
238 if matchfn(f):
238 if matchfn(f):
239 yield f
239 yield f
240 fns = fns_generator()
240 fns = fns_generator()
241 yield 'add', rev, fns
241 yield 'add', rev, fns
242 for rev in nrevs:
242 for rev in nrevs:
243 yield 'iter', rev, None
243 yield 'iter', rev, None
244 return iterate(), matchfn
244 return iterate(), matchfn
245
245
246 def write_bundle(cg, filename=None, compress=True):
246 def write_bundle(cg, filename=None, compress=True):
247 """Write a bundle file and return its filename.
247 """Write a bundle file and return its filename.
248
248
249 Existing files will not be overwritten.
249 Existing files will not be overwritten.
250 If no filename is specified, a temporary file is created.
250 If no filename is specified, a temporary file is created.
251 bz2 compression can be turned off.
251 bz2 compression can be turned off.
252 The bundle file will be deleted in case of errors.
252 The bundle file will be deleted in case of errors.
253 """
253 """
254 class nocompress(object):
254 class nocompress(object):
255 def compress(self, x):
255 def compress(self, x):
256 return x
256 return x
257 def flush(self):
257 def flush(self):
258 return ""
258 return ""
259
259
260 fh = None
260 fh = None
261 cleanup = None
261 cleanup = None
262 try:
262 try:
263 if filename:
263 if filename:
264 if os.path.exists(filename):
264 if os.path.exists(filename):
265 raise util.Abort(_("file '%s' already exists") % filename)
265 raise util.Abort(_("file '%s' already exists") % filename)
266 fh = open(filename, "wb")
266 fh = open(filename, "wb")
267 else:
267 else:
268 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
268 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
269 fh = os.fdopen(fd, "wb")
269 fh = os.fdopen(fd, "wb")
270 cleanup = filename
270 cleanup = filename
271
271
272 if compress:
272 if compress:
273 fh.write("HG10")
273 fh.write("HG10")
274 z = bz2.BZ2Compressor(9)
274 z = bz2.BZ2Compressor(9)
275 else:
275 else:
276 fh.write("HG10UN")
276 fh.write("HG10UN")
277 z = nocompress()
277 z = nocompress()
278 # parse the changegroup data, otherwise we will block
278 # parse the changegroup data, otherwise we will block
279 # in case of sshrepo because we don't know the end of the stream
279 # in case of sshrepo because we don't know the end of the stream
280
280
281 # an empty chunkiter is the end of the changegroup
281 # an empty chunkiter is the end of the changegroup
282 empty = False
282 empty = False
283 while not empty:
283 while not empty:
284 empty = True
284 empty = True
285 for chunk in changegroup.chunkiter(cg):
285 for chunk in changegroup.chunkiter(cg):
286 empty = False
286 empty = False
287 fh.write(z.compress(changegroup.genchunk(chunk)))
287 fh.write(z.compress(changegroup.genchunk(chunk)))
288 fh.write(z.compress(changegroup.closechunk()))
288 fh.write(z.compress(changegroup.closechunk()))
289 fh.write(z.flush())
289 fh.write(z.flush())
290 cleanup = None
290 cleanup = None
291 return filename
291 return filename
292 finally:
292 finally:
293 if fh is not None:
293 if fh is not None:
294 fh.close()
294 fh.close()
295 if cleanup is not None:
295 if cleanup is not None:
296 os.unlink(cleanup)
296 os.unlink(cleanup)
297
297
298 class changeset_printer(object):
298 class changeset_printer(object):
299 '''show changeset information when templating not requested.'''
299 '''show changeset information when templating not requested.'''
300
300
301 def __init__(self, ui, repo):
301 def __init__(self, ui, repo):
302 self.ui = ui
302 self.ui = ui
303 self.repo = repo
303 self.repo = repo
304
304
305 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
305 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
306 '''show a single changeset or file revision'''
306 '''show a single changeset or file revision'''
307 log = self.repo.changelog
307 log = self.repo.changelog
308 if changenode is None:
308 if changenode is None:
309 changenode = log.node(rev)
309 changenode = log.node(rev)
310 elif not rev:
310 elif not rev:
311 rev = log.rev(changenode)
311 rev = log.rev(changenode)
312
312
313 if self.ui.quiet:
313 if self.ui.quiet:
314 self.ui.write("%d:%s\n" % (rev, short(changenode)))
314 self.ui.write("%d:%s\n" % (rev, short(changenode)))
315 return
315 return
316
316
317 changes = log.read(changenode)
317 changes = log.read(changenode)
318 date = util.datestr(changes[2])
318 date = util.datestr(changes[2])
319 extra = changes[5]
319 extra = changes[5]
320 branch = extra.get("branch")
320 branch = extra.get("branch")
321
321
322 hexfunc = self.ui.debugflag and hex or short
322 hexfunc = self.ui.debugflag and hex or short
323
323
324 parents = log.parentrevs(rev)
324 parents = log.parentrevs(rev)
325 if not self.ui.debugflag:
325 if not self.ui.debugflag:
326 if parents[1] == nullrev:
326 if parents[1] == nullrev:
327 if parents[0] >= rev - 1:
327 if parents[0] >= rev - 1:
328 parents = []
328 parents = []
329 else:
329 else:
330 parents = [parents[0]]
330 parents = [parents[0]]
331 parents = [(p, hexfunc(log.node(p))) for p in parents]
331 parents = [(p, hexfunc(log.node(p))) for p in parents]
332
332
333 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
333 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
334
334
335 if branch:
335 if branch:
336 self.ui.write(_("branch: %s\n") % branch)
336 self.ui.write(_("branch: %s\n") % branch)
337 for tag in self.repo.nodetags(changenode):
337 for tag in self.repo.nodetags(changenode):
338 self.ui.write(_("tag: %s\n") % tag)
338 self.ui.write(_("tag: %s\n") % tag)
339 for parent in parents:
339 for parent in parents:
340 self.ui.write(_("parent: %d:%s\n") % parent)
340 self.ui.write(_("parent: %d:%s\n") % parent)
341
341
342 if brinfo and changenode in brinfo:
342 if brinfo and changenode in brinfo:
343 br = brinfo[changenode]
343 br = brinfo[changenode]
344 self.ui.write(_("branch: %s\n") % " ".join(br))
344 self.ui.write(_("branch: %s\n") % " ".join(br))
345
345
346 if self.ui.debugflag:
346 if self.ui.debugflag:
347 self.ui.write(_("manifest: %d:%s\n") %
347 self.ui.write(_("manifest: %d:%s\n") %
348 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
348 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
349 self.ui.write(_("user: %s\n") % changes[1])
349 self.ui.write(_("user: %s\n") % changes[1])
350 self.ui.write(_("date: %s\n") % date)
350 self.ui.write(_("date: %s\n") % date)
351
351
352 if self.ui.debugflag:
352 if self.ui.debugflag:
353 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
353 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
354 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
354 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
355 files):
355 files):
356 if value:
356 if value:
357 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
357 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
358 elif changes[3] and self.ui.verbose:
358 elif changes[3] and self.ui.verbose:
359 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
359 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
360 if copies and self.ui.verbose:
360 if copies and self.ui.verbose:
361 copies = ['%s (%s)' % c for c in copies]
361 copies = ['%s (%s)' % c for c in copies]
362 self.ui.write(_("copies: %s\n") % ' '.join(copies))
362 self.ui.write(_("copies: %s\n") % ' '.join(copies))
363
363
364 if extra and self.ui.debugflag:
364 if extra and self.ui.debugflag:
365 extraitems = extra.items()
365 extraitems = extra.items()
366 extraitems.sort()
366 extraitems.sort()
367 for key, value in extraitems:
367 for key, value in extraitems:
368 self.ui.write(_("extra: %s=%s\n")
368 self.ui.write(_("extra: %s=%s\n")
369 % (key, value.encode('string_escape')))
369 % (key, value.encode('string_escape')))
370
370
371 description = changes[4].strip()
371 description = changes[4].strip()
372 if description:
372 if description:
373 if self.ui.verbose:
373 if self.ui.verbose:
374 self.ui.write(_("description:\n"))
374 self.ui.write(_("description:\n"))
375 self.ui.write(description)
375 self.ui.write(description)
376 self.ui.write("\n\n")
376 self.ui.write("\n\n")
377 else:
377 else:
378 self.ui.write(_("summary: %s\n") %
378 self.ui.write(_("summary: %s\n") %
379 description.splitlines()[0])
379 description.splitlines()[0])
380 self.ui.write("\n")
380 self.ui.write("\n")
381
381
382 def show_changeset(ui, repo, opts):
382 def show_changeset(ui, repo, opts):
383 """show one changeset using template or regular display.
383 """show one changeset using template or regular display.
384
384
385 Display format will be the first non-empty hit of:
385 Display format will be the first non-empty hit of:
386 1. option 'template'
386 1. option 'template'
387 2. option 'style'
387 2. option 'style'
388 3. [ui] setting 'logtemplate'
388 3. [ui] setting 'logtemplate'
389 4. [ui] setting 'style'
389 4. [ui] setting 'style'
390 If all of these values are either the unset or the empty string,
390 If all of these values are either the unset or the empty string,
391 regular display via changeset_printer() is done.
391 regular display via changeset_printer() is done.
392 """
392 """
393 # options
393 # options
394 tmpl = opts.get('template')
394 tmpl = opts.get('template')
395 mapfile = None
395 mapfile = None
396 if tmpl:
396 if tmpl:
397 tmpl = templater.parsestring(tmpl, quoted=False)
397 tmpl = templater.parsestring(tmpl, quoted=False)
398 else:
398 else:
399 mapfile = opts.get('style')
399 mapfile = opts.get('style')
400 # ui settings
400 # ui settings
401 if not mapfile:
401 if not mapfile:
402 tmpl = ui.config('ui', 'logtemplate')
402 tmpl = ui.config('ui', 'logtemplate')
403 if tmpl:
403 if tmpl:
404 tmpl = templater.parsestring(tmpl)
404 tmpl = templater.parsestring(tmpl)
405 else:
405 else:
406 mapfile = ui.config('ui', 'style')
406 mapfile = ui.config('ui', 'style')
407
407
408 if tmpl or mapfile:
408 if tmpl or mapfile:
409 if mapfile:
409 if mapfile:
410 if not os.path.split(mapfile)[0]:
410 if not os.path.split(mapfile)[0]:
411 mapname = (templater.templatepath('map-cmdline.' + mapfile)
411 mapname = (templater.templatepath('map-cmdline.' + mapfile)
412 or templater.templatepath(mapfile))
412 or templater.templatepath(mapfile))
413 if mapname: mapfile = mapname
413 if mapname: mapfile = mapname
414 try:
414 try:
415 t = templater.changeset_templater(ui, repo, mapfile)
415 t = templater.changeset_templater(ui, repo, mapfile)
416 except SyntaxError, inst:
416 except SyntaxError, inst:
417 raise util.Abort(inst.args[0])
417 raise util.Abort(inst.args[0])
418 if tmpl: t.use_template(tmpl)
418 if tmpl: t.use_template(tmpl)
419 return t
419 return t
420 return changeset_printer(ui, repo)
420 return changeset_printer(ui, repo)
421
421
422 def setremoteconfig(ui, opts):
422 def setremoteconfig(ui, opts):
423 "copy remote options to ui tree"
423 "copy remote options to ui tree"
424 if opts.get('ssh'):
424 if opts.get('ssh'):
425 ui.setconfig("ui", "ssh", opts['ssh'])
425 ui.setconfig("ui", "ssh", opts['ssh'])
426 if opts.get('remotecmd'):
426 if opts.get('remotecmd'):
427 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
427 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
428
428
429 def show_version(ui):
429 def show_version(ui):
430 """output version and copyright information"""
430 """output version and copyright information"""
431 ui.write(_("Mercurial Distributed SCM (version %s)\n")
431 ui.write(_("Mercurial Distributed SCM (version %s)\n")
432 % version.get_version())
432 % version.get_version())
433 ui.status(_(
433 ui.status(_(
434 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
434 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
435 "This is free software; see the source for copying conditions. "
435 "This is free software; see the source for copying conditions. "
436 "There is NO\nwarranty; "
436 "There is NO\nwarranty; "
437 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
437 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
438 ))
438 ))
439
439
440 def help_(ui, name=None, with_version=False):
440 def help_(ui, name=None, with_version=False):
441 """show help for a command, extension, or list of commands
441 """show help for a command, extension, or list of commands
442
442
443 With no arguments, print a list of commands and short help.
443 With no arguments, print a list of commands and short help.
444
444
445 Given a command name, print help for that command.
445 Given a command name, print help for that command.
446
446
447 Given an extension name, print help for that extension, and the
447 Given an extension name, print help for that extension, and the
448 commands it provides."""
448 commands it provides."""
449 option_lists = []
449 option_lists = []
450
450
451 def helpcmd(name):
451 def helpcmd(name):
452 if with_version:
452 if with_version:
453 show_version(ui)
453 show_version(ui)
454 ui.write('\n')
454 ui.write('\n')
455 aliases, i = findcmd(ui, name)
455 aliases, i = findcmd(ui, name)
456 # synopsis
456 # synopsis
457 ui.write("%s\n\n" % i[2])
457 ui.write("%s\n\n" % i[2])
458
458
459 # description
459 # description
460 doc = i[0].__doc__
460 doc = i[0].__doc__
461 if not doc:
461 if not doc:
462 doc = _("(No help text available)")
462 doc = _("(No help text available)")
463 if ui.quiet:
463 if ui.quiet:
464 doc = doc.splitlines(0)[0]
464 doc = doc.splitlines(0)[0]
465 ui.write("%s\n" % doc.rstrip())
465 ui.write("%s\n" % doc.rstrip())
466
466
467 if not ui.quiet:
467 if not ui.quiet:
468 # aliases
468 # aliases
469 if len(aliases) > 1:
469 if len(aliases) > 1:
470 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
470 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
471
471
472 # options
472 # options
473 if i[1]:
473 if i[1]:
474 option_lists.append(("options", i[1]))
474 option_lists.append(("options", i[1]))
475
475
476 def helplist(select=None):
476 def helplist(select=None):
477 h = {}
477 h = {}
478 cmds = {}
478 cmds = {}
479 for c, e in table.items():
479 for c, e in table.items():
480 f = c.split("|", 1)[0]
480 f = c.split("|", 1)[0]
481 if select and not select(f):
481 if select and not select(f):
482 continue
482 continue
483 if name == "shortlist" and not f.startswith("^"):
483 if name == "shortlist" and not f.startswith("^"):
484 continue
484 continue
485 f = f.lstrip("^")
485 f = f.lstrip("^")
486 if not ui.debugflag and f.startswith("debug"):
486 if not ui.debugflag and f.startswith("debug"):
487 continue
487 continue
488 doc = e[0].__doc__
488 doc = e[0].__doc__
489 if not doc:
489 if not doc:
490 doc = _("(No help text available)")
490 doc = _("(No help text available)")
491 h[f] = doc.splitlines(0)[0].rstrip()
491 h[f] = doc.splitlines(0)[0].rstrip()
492 cmds[f] = c.lstrip("^")
492 cmds[f] = c.lstrip("^")
493
493
494 fns = h.keys()
494 fns = h.keys()
495 fns.sort()
495 fns.sort()
496 m = max(map(len, fns))
496 m = max(map(len, fns))
497 for f in fns:
497 for f in fns:
498 if ui.verbose:
498 if ui.verbose:
499 commands = cmds[f].replace("|",", ")
499 commands = cmds[f].replace("|",", ")
500 ui.write(" %s:\n %s\n"%(commands, h[f]))
500 ui.write(" %s:\n %s\n"%(commands, h[f]))
501 else:
501 else:
502 ui.write(' %-*s %s\n' % (m, f, h[f]))
502 ui.write(' %-*s %s\n' % (m, f, h[f]))
503
503
504 def helpext(name):
504 def helpext(name):
505 try:
505 try:
506 mod = findext(name)
506 mod = findext(name)
507 except KeyError:
507 except KeyError:
508 raise UnknownCommand(name)
508 raise UnknownCommand(name)
509
509
510 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
510 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
511 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
511 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
512 for d in doc[1:]:
512 for d in doc[1:]:
513 ui.write(d, '\n')
513 ui.write(d, '\n')
514
514
515 ui.status('\n')
515 ui.status('\n')
516 if ui.verbose:
516 if ui.verbose:
517 ui.status(_('list of commands:\n\n'))
517 ui.status(_('list of commands:\n\n'))
518 else:
518 else:
519 ui.status(_('list of commands (use "hg help -v %s" '
519 ui.status(_('list of commands (use "hg help -v %s" '
520 'to show aliases and global options):\n\n') % name)
520 'to show aliases and global options):\n\n') % name)
521
521
522 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
522 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
523 helplist(modcmds.has_key)
523 helplist(modcmds.has_key)
524
524
525 if name and name != 'shortlist':
525 if name and name != 'shortlist':
526 try:
526 try:
527 helpcmd(name)
527 helpcmd(name)
528 except UnknownCommand:
528 except UnknownCommand:
529 helpext(name)
529 helpext(name)
530
530
531 else:
531 else:
532 # program name
532 # program name
533 if ui.verbose or with_version:
533 if ui.verbose or with_version:
534 show_version(ui)
534 show_version(ui)
535 else:
535 else:
536 ui.status(_("Mercurial Distributed SCM\n"))
536 ui.status(_("Mercurial Distributed SCM\n"))
537 ui.status('\n')
537 ui.status('\n')
538
538
539 # list of commands
539 # list of commands
540 if name == "shortlist":
540 if name == "shortlist":
541 ui.status(_('basic commands (use "hg help" '
541 ui.status(_('basic commands (use "hg help" '
542 'for the full list or option "-v" for details):\n\n'))
542 'for the full list or option "-v" for details):\n\n'))
543 elif ui.verbose:
543 elif ui.verbose:
544 ui.status(_('list of commands:\n\n'))
544 ui.status(_('list of commands:\n\n'))
545 else:
545 else:
546 ui.status(_('list of commands (use "hg help -v" '
546 ui.status(_('list of commands (use "hg help -v" '
547 'to show aliases and global options):\n\n'))
547 'to show aliases and global options):\n\n'))
548
548
549 helplist()
549 helplist()
550
550
551 # global options
551 # global options
552 if ui.verbose:
552 if ui.verbose:
553 option_lists.append(("global options", globalopts))
553 option_lists.append(("global options", globalopts))
554
554
555 # list all option lists
555 # list all option lists
556 opt_output = []
556 opt_output = []
557 for title, options in option_lists:
557 for title, options in option_lists:
558 opt_output.append(("\n%s:\n" % title, None))
558 opt_output.append(("\n%s:\n" % title, None))
559 for shortopt, longopt, default, desc in options:
559 for shortopt, longopt, default, desc in options:
560 if "DEPRECATED" in desc and not ui.verbose: continue
560 if "DEPRECATED" in desc and not ui.verbose: continue
561 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
561 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
562 longopt and " --%s" % longopt),
562 longopt and " --%s" % longopt),
563 "%s%s" % (desc,
563 "%s%s" % (desc,
564 default
564 default
565 and _(" (default: %s)") % default
565 and _(" (default: %s)") % default
566 or "")))
566 or "")))
567
567
568 if opt_output:
568 if opt_output:
569 opts_len = max([len(line[0]) for line in opt_output if line[1]])
569 opts_len = max([len(line[0]) for line in opt_output if line[1]])
570 for first, second in opt_output:
570 for first, second in opt_output:
571 if second:
571 if second:
572 ui.write(" %-*s %s\n" % (opts_len, first, second))
572 ui.write(" %-*s %s\n" % (opts_len, first, second))
573 else:
573 else:
574 ui.write("%s\n" % first)
574 ui.write("%s\n" % first)
575
575
576 # Commands start here, listed alphabetically
576 # Commands start here, listed alphabetically
577
577
578 def add(ui, repo, *pats, **opts):
578 def add(ui, repo, *pats, **opts):
579 """add the specified files on the next commit
579 """add the specified files on the next commit
580
580
581 Schedule files to be version controlled and added to the repository.
581 Schedule files to be version controlled and added to the repository.
582
582
583 The files will be added to the repository at the next commit.
583 The files will be added to the repository at the next commit.
584
584
585 If no names are given, add all files in the repository.
585 If no names are given, add all files in the repository.
586 """
586 """
587
587
588 names = []
588 names = []
589 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
589 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
590 if exact:
590 if exact:
591 if ui.verbose:
591 if ui.verbose:
592 ui.status(_('adding %s\n') % rel)
592 ui.status(_('adding %s\n') % rel)
593 names.append(abs)
593 names.append(abs)
594 elif repo.dirstate.state(abs) == '?':
594 elif repo.dirstate.state(abs) == '?':
595 ui.status(_('adding %s\n') % rel)
595 ui.status(_('adding %s\n') % rel)
596 names.append(abs)
596 names.append(abs)
597 if not opts.get('dry_run'):
597 if not opts.get('dry_run'):
598 repo.add(names)
598 repo.add(names)
599
599
600 def addremove(ui, repo, *pats, **opts):
600 def addremove(ui, repo, *pats, **opts):
601 """add all new files, delete all missing files
601 """add all new files, delete all missing files
602
602
603 Add all new files and remove all missing files from the repository.
603 Add all new files and remove all missing files from the repository.
604
604
605 New files are ignored if they match any of the patterns in .hgignore. As
605 New files are ignored if they match any of the patterns in .hgignore. As
606 with add, these changes take effect at the next commit.
606 with add, these changes take effect at the next commit.
607
607
608 Use the -s option to detect renamed files. With a parameter > 0,
608 Use the -s option to detect renamed files. With a parameter > 0,
609 this compares every removed file with every added file and records
609 this compares every removed file with every added file and records
610 those similar enough as renames. This option takes a percentage
610 those similar enough as renames. This option takes a percentage
611 between 0 (disabled) and 100 (files must be identical) as its
611 between 0 (disabled) and 100 (files must be identical) as its
612 parameter. Detecting renamed files this way can be expensive.
612 parameter. Detecting renamed files this way can be expensive.
613 """
613 """
614 sim = float(opts.get('similarity') or 0)
614 sim = float(opts.get('similarity') or 0)
615 if sim < 0 or sim > 100:
615 if sim < 0 or sim > 100:
616 raise util.Abort(_('similarity must be between 0 and 100'))
616 raise util.Abort(_('similarity must be between 0 and 100'))
617 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
617 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
618
618
619 def annotate(ui, repo, *pats, **opts):
619 def annotate(ui, repo, *pats, **opts):
620 """show changeset information per file line
620 """show changeset information per file line
621
621
622 List changes in files, showing the revision id responsible for each line
622 List changes in files, showing the revision id responsible for each line
623
623
624 This command is useful to discover who did a change or when a change took
624 This command is useful to discover who did a change or when a change took
625 place.
625 place.
626
626
627 Without the -a option, annotate will avoid processing files it
627 Without the -a option, annotate will avoid processing files it
628 detects as binary. With -a, annotate will generate an annotation
628 detects as binary. With -a, annotate will generate an annotation
629 anyway, probably with undesirable results.
629 anyway, probably with undesirable results.
630 """
630 """
631 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
631 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
632
632
633 if not pats:
633 if not pats:
634 raise util.Abort(_('at least one file name or pattern required'))
634 raise util.Abort(_('at least one file name or pattern required'))
635
635
636 opmap = [['user', lambda x: ui.shortuser(x.user())],
636 opmap = [['user', lambda x: ui.shortuser(x.user())],
637 ['number', lambda x: str(x.rev())],
637 ['number', lambda x: str(x.rev())],
638 ['changeset', lambda x: short(x.node())],
638 ['changeset', lambda x: short(x.node())],
639 ['date', getdate], ['follow', lambda x: x.path()]]
639 ['date', getdate], ['follow', lambda x: x.path()]]
640 if (not opts['user'] and not opts['changeset'] and not opts['date']
640 if (not opts['user'] and not opts['changeset'] and not opts['date']
641 and not opts['follow']):
641 and not opts['follow']):
642 opts['number'] = 1
642 opts['number'] = 1
643
643
644 ctx = repo.changectx(opts['rev'])
644 ctx = repo.changectx(opts['rev'])
645
645
646 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
646 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
647 node=ctx.node()):
647 node=ctx.node()):
648 fctx = ctx.filectx(abs)
648 fctx = ctx.filectx(abs)
649 if not opts['text'] and util.binary(fctx.data()):
649 if not opts['text'] and util.binary(fctx.data()):
650 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
650 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
651 continue
651 continue
652
652
653 lines = fctx.annotate(follow=opts.get('follow'))
653 lines = fctx.annotate(follow=opts.get('follow'))
654 pieces = []
654 pieces = []
655
655
656 for o, f in opmap:
656 for o, f in opmap:
657 if opts[o]:
657 if opts[o]:
658 l = [f(n) for n, dummy in lines]
658 l = [f(n) for n, dummy in lines]
659 if l:
659 if l:
660 m = max(map(len, l))
660 m = max(map(len, l))
661 pieces.append(["%*s" % (m, x) for x in l])
661 pieces.append(["%*s" % (m, x) for x in l])
662
662
663 if pieces:
663 if pieces:
664 for p, l in zip(zip(*pieces), lines):
664 for p, l in zip(zip(*pieces), lines):
665 ui.write("%s: %s" % (" ".join(p), l[1]))
665 ui.write("%s: %s" % (" ".join(p), l[1]))
666
666
667 def archive(ui, repo, dest, **opts):
667 def archive(ui, repo, dest, **opts):
668 '''create unversioned archive of a repository revision
668 '''create unversioned archive of a repository revision
669
669
670 By default, the revision used is the parent of the working
670 By default, the revision used is the parent of the working
671 directory; use "-r" to specify a different revision.
671 directory; use "-r" to specify a different revision.
672
672
673 To specify the type of archive to create, use "-t". Valid
673 To specify the type of archive to create, use "-t". Valid
674 types are:
674 types are:
675
675
676 "files" (default): a directory full of files
676 "files" (default): a directory full of files
677 "tar": tar archive, uncompressed
677 "tar": tar archive, uncompressed
678 "tbz2": tar archive, compressed using bzip2
678 "tbz2": tar archive, compressed using bzip2
679 "tgz": tar archive, compressed using gzip
679 "tgz": tar archive, compressed using gzip
680 "uzip": zip archive, uncompressed
680 "uzip": zip archive, uncompressed
681 "zip": zip archive, compressed using deflate
681 "zip": zip archive, compressed using deflate
682
682
683 The exact name of the destination archive or directory is given
683 The exact name of the destination archive or directory is given
684 using a format string; see "hg help export" for details.
684 using a format string; see "hg help export" for details.
685
685
686 Each member added to an archive file has a directory prefix
686 Each member added to an archive file has a directory prefix
687 prepended. Use "-p" to specify a format string for the prefix.
687 prepended. Use "-p" to specify a format string for the prefix.
688 The default is the basename of the archive, with suffixes removed.
688 The default is the basename of the archive, with suffixes removed.
689 '''
689 '''
690
690
691 node = repo.changectx(opts['rev']).node()
691 node = repo.changectx(opts['rev']).node()
692 dest = cmdutil.make_filename(repo, dest, node)
692 dest = cmdutil.make_filename(repo, dest, node)
693 if os.path.realpath(dest) == repo.root:
693 if os.path.realpath(dest) == repo.root:
694 raise util.Abort(_('repository root cannot be destination'))
694 raise util.Abort(_('repository root cannot be destination'))
695 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
695 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
696 kind = opts.get('type') or 'files'
696 kind = opts.get('type') or 'files'
697 prefix = opts['prefix']
697 prefix = opts['prefix']
698 if dest == '-':
698 if dest == '-':
699 if kind == 'files':
699 if kind == 'files':
700 raise util.Abort(_('cannot archive plain files to stdout'))
700 raise util.Abort(_('cannot archive plain files to stdout'))
701 dest = sys.stdout
701 dest = sys.stdout
702 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
702 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
703 prefix = cmdutil.make_filename(repo, prefix, node)
703 prefix = cmdutil.make_filename(repo, prefix, node)
704 archival.archive(repo, dest, node, kind, not opts['no_decode'],
704 archival.archive(repo, dest, node, kind, not opts['no_decode'],
705 matchfn, prefix)
705 matchfn, prefix)
706
706
707 def backout(ui, repo, rev, **opts):
707 def backout(ui, repo, rev, **opts):
708 '''reverse effect of earlier changeset
708 '''reverse effect of earlier changeset
709
709
710 Commit the backed out changes as a new changeset. The new
710 Commit the backed out changes as a new changeset. The new
711 changeset is a child of the backed out changeset.
711 changeset is a child of the backed out changeset.
712
712
713 If you back out a changeset other than the tip, a new head is
713 If you back out a changeset other than the tip, a new head is
714 created. This head is the parent of the working directory. If
714 created. This head is the parent of the working directory. If
715 you back out an old changeset, your working directory will appear
715 you back out an old changeset, your working directory will appear
716 old after the backout. You should merge the backout changeset
716 old after the backout. You should merge the backout changeset
717 with another head.
717 with another head.
718
718
719 The --merge option remembers the parent of the working directory
719 The --merge option remembers the parent of the working directory
720 before starting the backout, then merges the new head with that
720 before starting the backout, then merges the new head with that
721 changeset afterwards. This saves you from doing the merge by
721 changeset afterwards. This saves you from doing the merge by
722 hand. The result of this merge is not committed, as for a normal
722 hand. The result of this merge is not committed, as for a normal
723 merge.'''
723 merge.'''
724
724
725 bail_if_changed(repo)
725 bail_if_changed(repo)
726 op1, op2 = repo.dirstate.parents()
726 op1, op2 = repo.dirstate.parents()
727 if op2 != nullid:
727 if op2 != nullid:
728 raise util.Abort(_('outstanding uncommitted merge'))
728 raise util.Abort(_('outstanding uncommitted merge'))
729 node = repo.lookup(rev)
729 node = repo.lookup(rev)
730 p1, p2 = repo.changelog.parents(node)
730 p1, p2 = repo.changelog.parents(node)
731 if p1 == nullid:
731 if p1 == nullid:
732 raise util.Abort(_('cannot back out a change with no parents'))
732 raise util.Abort(_('cannot back out a change with no parents'))
733 if p2 != nullid:
733 if p2 != nullid:
734 if not opts['parent']:
734 if not opts['parent']:
735 raise util.Abort(_('cannot back out a merge changeset without '
735 raise util.Abort(_('cannot back out a merge changeset without '
736 '--parent'))
736 '--parent'))
737 p = repo.lookup(opts['parent'])
737 p = repo.lookup(opts['parent'])
738 if p not in (p1, p2):
738 if p not in (p1, p2):
739 raise util.Abort(_('%s is not a parent of %s' %
739 raise util.Abort(_('%s is not a parent of %s' %
740 (short(p), short(node))))
740 (short(p), short(node))))
741 parent = p
741 parent = p
742 else:
742 else:
743 if opts['parent']:
743 if opts['parent']:
744 raise util.Abort(_('cannot use --parent on non-merge changeset'))
744 raise util.Abort(_('cannot use --parent on non-merge changeset'))
745 parent = p1
745 parent = p1
746 hg.clean(repo, node, show_stats=False)
746 hg.clean(repo, node, show_stats=False)
747 revert_opts = opts.copy()
747 revert_opts = opts.copy()
748 revert_opts['all'] = True
748 revert_opts['all'] = True
749 revert_opts['rev'] = hex(parent)
749 revert_opts['rev'] = hex(parent)
750 revert(ui, repo, **revert_opts)
750 revert(ui, repo, **revert_opts)
751 commit_opts = opts.copy()
751 commit_opts = opts.copy()
752 commit_opts['addremove'] = False
752 commit_opts['addremove'] = False
753 if not commit_opts['message'] and not commit_opts['logfile']:
753 if not commit_opts['message'] and not commit_opts['logfile']:
754 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
754 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
755 commit_opts['force_editor'] = True
755 commit_opts['force_editor'] = True
756 commit(ui, repo, **commit_opts)
756 commit(ui, repo, **commit_opts)
757 def nice(node):
757 def nice(node):
758 return '%d:%s' % (repo.changelog.rev(node), short(node))
758 return '%d:%s' % (repo.changelog.rev(node), short(node))
759 ui.status(_('changeset %s backs out changeset %s\n') %
759 ui.status(_('changeset %s backs out changeset %s\n') %
760 (nice(repo.changelog.tip()), nice(node)))
760 (nice(repo.changelog.tip()), nice(node)))
761 if op1 != node:
761 if op1 != node:
762 if opts['merge']:
762 if opts['merge']:
763 ui.status(_('merging with changeset %s\n') % nice(op1))
763 ui.status(_('merging with changeset %s\n') % nice(op1))
764 n = _lookup(repo, hex(op1))
764 n = _lookup(repo, hex(op1))
765 hg.merge(repo, n)
765 hg.merge(repo, n)
766 else:
766 else:
767 ui.status(_('the backout changeset is a new head - '
767 ui.status(_('the backout changeset is a new head - '
768 'do not forget to merge\n'))
768 'do not forget to merge\n'))
769 ui.status(_('(use "backout --merge" '
769 ui.status(_('(use "backout --merge" '
770 'if you want to auto-merge)\n'))
770 'if you want to auto-merge)\n'))
771
771
772 def branch(ui, repo, label=None):
772 def branch(ui, repo, label=None):
773 """set or show the current branch name
773 """set or show the current branch name
774
774
775 With <name>, set the current branch name. Otherwise, show the
775 With <name>, set the current branch name. Otherwise, show the
776 current branch name.
776 current branch name.
777 """
777 """
778
778
779 if label is not None:
779 if label is not None:
780 repo.opener("branch", "w").write(label)
780 repo.opener("branch", "w").write(label)
781 else:
781 else:
782 b = repo.workingctx().branch()
782 b = repo.workingctx().branch()
783 if b:
783 if b:
784 ui.write("%s\n" % b)
784 ui.write("%s\n" % b)
785
785
786 def branches(ui, repo):
786 def branches(ui, repo):
787 """list repository named branches
787 """list repository named branches
788
788
789 List the repository's named branches.
789 List the repository's named branches.
790 """
790 """
791 b = repo.branchtags()
791 b = repo.branchtags()
792 l = [(-repo.changelog.rev(n), n, t) for t,n in b.items()]
792 l = [(-repo.changelog.rev(n), n, t) for t,n in b.items()]
793 l.sort()
793 l.sort()
794 for r, n, t in l:
794 for r, n, t in l:
795 hexfunc = ui.debugflag and hex or short
795 hexfunc = ui.debugflag and hex or short
796 if ui.quiet:
796 if ui.quiet:
797 ui.write("%s\n" % t)
797 ui.write("%s\n" % t)
798 else:
798 else:
799 ui.write("%-30s %s:%s\n" % (t, -r, hexfunc(n)))
799 ui.write("%-30s %s:%s\n" % (t, -r, hexfunc(n)))
800
800
801 def bundle(ui, repo, fname, dest=None, **opts):
801 def bundle(ui, repo, fname, dest=None, **opts):
802 """create a changegroup file
802 """create a changegroup file
803
803
804 Generate a compressed changegroup file collecting changesets not
804 Generate a compressed changegroup file collecting changesets not
805 found in the other repository.
805 found in the other repository.
806
806
807 If no destination repository is specified the destination is assumed
807 If no destination repository is specified the destination is assumed
808 to have all the nodes specified by one or more --base parameters.
808 to have all the nodes specified by one or more --base parameters.
809
809
810 The bundle file can then be transferred using conventional means and
810 The bundle file can then be transferred using conventional means and
811 applied to another repository with the unbundle or pull command.
811 applied to another repository with the unbundle or pull command.
812 This is useful when direct push and pull are not available or when
812 This is useful when direct push and pull are not available or when
813 exporting an entire repository is undesirable.
813 exporting an entire repository is undesirable.
814
814
815 Applying bundles preserves all changeset contents including
815 Applying bundles preserves all changeset contents including
816 permissions, copy/rename information, and revision history.
816 permissions, copy/rename information, and revision history.
817 """
817 """
818 revs = opts.get('rev') or None
818 revs = opts.get('rev') or None
819 if revs:
819 if revs:
820 revs = [repo.lookup(rev) for rev in revs]
820 revs = [repo.lookup(rev) for rev in revs]
821 base = opts.get('base')
821 base = opts.get('base')
822 if base:
822 if base:
823 if dest:
823 if dest:
824 raise util.Abort(_("--base is incompatible with specifiying "
824 raise util.Abort(_("--base is incompatible with specifiying "
825 "a destination"))
825 "a destination"))
826 base = [repo.lookup(rev) for rev in base]
826 base = [repo.lookup(rev) for rev in base]
827 # create the right base
827 # create the right base
828 # XXX: nodesbetween / changegroup* should be "fixed" instead
828 # XXX: nodesbetween / changegroup* should be "fixed" instead
829 o = []
829 o = []
830 has_set = sets.Set(base)
830 has_set = sets.Set(base)
831 for n in base:
831 for n in base:
832 has_set.update(repo.changelog.reachable(n))
832 has_set.update(repo.changelog.reachable(n))
833 if revs:
833 if revs:
834 visit = list(revs)
834 visit = list(revs)
835 else:
835 else:
836 visit = repo.changelog.heads()
836 visit = repo.changelog.heads()
837 seen = sets.Set(visit)
837 seen = sets.Set(visit)
838 while visit:
838 while visit:
839 n = visit.pop(0)
839 n = visit.pop(0)
840 parents = [p for p in repo.changelog.parents(n)
840 parents = [p for p in repo.changelog.parents(n)
841 if p != nullid and p not in has_set]
841 if p != nullid and p not in has_set]
842 if len(parents) == 0:
842 if len(parents) == 0:
843 o.insert(0, n)
843 o.insert(0, n)
844 else:
844 else:
845 for p in parents:
845 for p in parents:
846 if p not in seen:
846 if p not in seen:
847 seen.add(p)
847 seen.add(p)
848 visit.append(p)
848 visit.append(p)
849 else:
849 else:
850 setremoteconfig(ui, opts)
850 setremoteconfig(ui, opts)
851 dest = ui.expandpath(dest or 'default-push', dest or 'default')
851 dest = ui.expandpath(dest or 'default-push', dest or 'default')
852 other = hg.repository(ui, dest)
852 other = hg.repository(ui, dest)
853 o = repo.findoutgoing(other, force=opts['force'])
853 o = repo.findoutgoing(other, force=opts['force'])
854
854
855 if revs:
855 if revs:
856 cg = repo.changegroupsubset(o, revs, 'bundle')
856 cg = repo.changegroupsubset(o, revs, 'bundle')
857 else:
857 else:
858 cg = repo.changegroup(o, 'bundle')
858 cg = repo.changegroup(o, 'bundle')
859 write_bundle(cg, fname)
859 write_bundle(cg, fname)
860
860
861 def cat(ui, repo, file1, *pats, **opts):
861 def cat(ui, repo, file1, *pats, **opts):
862 """output the latest or given revisions of files
862 """output the latest or given revisions of files
863
863
864 Print the specified files as they were at the given revision.
864 Print the specified files as they were at the given revision.
865 If no revision is given then working dir parent is used, or tip
865 If no revision is given then working dir parent is used, or tip
866 if no revision is checked out.
866 if no revision is checked out.
867
867
868 Output may be to a file, in which case the name of the file is
868 Output may be to a file, in which case the name of the file is
869 given using a format string. The formatting rules are the same as
869 given using a format string. The formatting rules are the same as
870 for the export command, with the following additions:
870 for the export command, with the following additions:
871
871
872 %s basename of file being printed
872 %s basename of file being printed
873 %d dirname of file being printed, or '.' if in repo root
873 %d dirname of file being printed, or '.' if in repo root
874 %p root-relative path name of file being printed
874 %p root-relative path name of file being printed
875 """
875 """
876 ctx = repo.changectx(opts['rev'])
876 ctx = repo.changectx(opts['rev'])
877 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
877 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
878 ctx.node()):
878 ctx.node()):
879 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
879 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
880 fp.write(ctx.filectx(abs).data())
880 fp.write(ctx.filectx(abs).data())
881
881
882 def clone(ui, source, dest=None, **opts):
882 def clone(ui, source, dest=None, **opts):
883 """make a copy of an existing repository
883 """make a copy of an existing repository
884
884
885 Create a copy of an existing repository in a new directory.
885 Create a copy of an existing repository in a new directory.
886
886
887 If no destination directory name is specified, it defaults to the
887 If no destination directory name is specified, it defaults to the
888 basename of the source.
888 basename of the source.
889
889
890 The location of the source is added to the new repository's
890 The location of the source is added to the new repository's
891 .hg/hgrc file, as the default to be used for future pulls.
891 .hg/hgrc file, as the default to be used for future pulls.
892
892
893 For efficiency, hardlinks are used for cloning whenever the source
893 For efficiency, hardlinks are used for cloning whenever the source
894 and destination are on the same filesystem (note this applies only
894 and destination are on the same filesystem (note this applies only
895 to the repository data, not to the checked out files). Some
895 to the repository data, not to the checked out files). Some
896 filesystems, such as AFS, implement hardlinking incorrectly, but
896 filesystems, such as AFS, implement hardlinking incorrectly, but
897 do not report errors. In these cases, use the --pull option to
897 do not report errors. In these cases, use the --pull option to
898 avoid hardlinking.
898 avoid hardlinking.
899
899
900 You can safely clone repositories and checked out files using full
900 You can safely clone repositories and checked out files using full
901 hardlinks with
901 hardlinks with
902
902
903 $ cp -al REPO REPOCLONE
903 $ cp -al REPO REPOCLONE
904
904
905 which is the fastest way to clone. However, the operation is not
905 which is the fastest way to clone. However, the operation is not
906 atomic (making sure REPO is not modified during the operation is
906 atomic (making sure REPO is not modified during the operation is
907 up to you) and you have to make sure your editor breaks hardlinks
907 up to you) and you have to make sure your editor breaks hardlinks
908 (Emacs and most Linux Kernel tools do so).
908 (Emacs and most Linux Kernel tools do so).
909
909
910 If you use the -r option to clone up to a specific revision, no
910 If you use the -r option to clone up to a specific revision, no
911 subsequent revisions will be present in the cloned repository.
911 subsequent revisions will be present in the cloned repository.
912 This option implies --pull, even on local repositories.
912 This option implies --pull, even on local repositories.
913
913
914 See pull for valid source format details.
914 See pull for valid source format details.
915
915
916 It is possible to specify an ssh:// URL as the destination, but no
916 It is possible to specify an ssh:// URL as the destination, but no
917 .hg/hgrc and working directory will be created on the remote side.
917 .hg/hgrc and working directory will be created on the remote side.
918 Look at the help text for the pull command for important details
918 Look at the help text for the pull command for important details
919 about ssh:// URLs.
919 about ssh:// URLs.
920 """
920 """
921 setremoteconfig(ui, opts)
921 setremoteconfig(ui, opts)
922 hg.clone(ui, ui.expandpath(source), dest,
922 hg.clone(ui, ui.expandpath(source), dest,
923 pull=opts['pull'],
923 pull=opts['pull'],
924 stream=opts['uncompressed'],
924 stream=opts['uncompressed'],
925 rev=opts['rev'],
925 rev=opts['rev'],
926 update=not opts['noupdate'])
926 update=not opts['noupdate'])
927
927
928 def commit(ui, repo, *pats, **opts):
928 def commit(ui, repo, *pats, **opts):
929 """commit the specified files or all outstanding changes
929 """commit the specified files or all outstanding changes
930
930
931 Commit changes to the given files into the repository.
931 Commit changes to the given files into the repository.
932
932
933 If a list of files is omitted, all changes reported by "hg status"
933 If a list of files is omitted, all changes reported by "hg status"
934 will be committed.
934 will be committed.
935
935
936 If no commit message is specified, the editor configured in your hgrc
936 If no commit message is specified, the editor configured in your hgrc
937 or in the EDITOR environment variable is started to enter a message.
937 or in the EDITOR environment variable is started to enter a message.
938 """
938 """
939 message = logmessage(opts)
939 message = logmessage(opts)
940
940
941 if opts['addremove']:
941 if opts['addremove']:
942 cmdutil.addremove(repo, pats, opts)
942 cmdutil.addremove(repo, pats, opts)
943 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
943 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
944 if pats:
944 if pats:
945 modified, added, removed = repo.status(files=fns, match=match)[:3]
945 modified, added, removed = repo.status(files=fns, match=match)[:3]
946 files = modified + added + removed
946 files = modified + added + removed
947 else:
947 else:
948 files = []
948 files = []
949 try:
949 try:
950 repo.commit(files, message, opts['user'], opts['date'], match,
950 repo.commit(files, message, opts['user'], opts['date'], match,
951 force_editor=opts.get('force_editor'))
951 force_editor=opts.get('force_editor'))
952 except ValueError, inst:
952 except ValueError, inst:
953 raise util.Abort(str(inst))
953 raise util.Abort(str(inst))
954
954
955 def docopy(ui, repo, pats, opts, wlock):
955 def docopy(ui, repo, pats, opts, wlock):
956 # called with the repo lock held
956 # called with the repo lock held
957 cwd = repo.getcwd()
957 cwd = repo.getcwd()
958 errors = 0
958 errors = 0
959 copied = []
959 copied = []
960 targets = {}
960 targets = {}
961
961
962 def okaytocopy(abs, rel, exact):
962 def okaytocopy(abs, rel, exact):
963 reasons = {'?': _('is not managed'),
963 reasons = {'?': _('is not managed'),
964 'a': _('has been marked for add'),
964 'a': _('has been marked for add'),
965 'r': _('has been marked for remove')}
965 'r': _('has been marked for remove')}
966 state = repo.dirstate.state(abs)
966 state = repo.dirstate.state(abs)
967 reason = reasons.get(state)
967 reason = reasons.get(state)
968 if reason:
968 if reason:
969 if state == 'a':
969 if state == 'a':
970 origsrc = repo.dirstate.copied(abs)
970 origsrc = repo.dirstate.copied(abs)
971 if origsrc is not None:
971 if origsrc is not None:
972 return origsrc
972 return origsrc
973 if exact:
973 if exact:
974 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
974 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
975 else:
975 else:
976 return abs
976 return abs
977
977
978 def copy(origsrc, abssrc, relsrc, target, exact):
978 def copy(origsrc, abssrc, relsrc, target, exact):
979 abstarget = util.canonpath(repo.root, cwd, target)
979 abstarget = util.canonpath(repo.root, cwd, target)
980 reltarget = util.pathto(cwd, abstarget)
980 reltarget = util.pathto(cwd, abstarget)
981 prevsrc = targets.get(abstarget)
981 prevsrc = targets.get(abstarget)
982 if prevsrc is not None:
982 if prevsrc is not None:
983 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
983 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
984 (reltarget, abssrc, prevsrc))
984 (reltarget, abssrc, prevsrc))
985 return
985 return
986 if (not opts['after'] and os.path.exists(reltarget) or
986 if (not opts['after'] and os.path.exists(reltarget) or
987 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
987 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
988 if not opts['force']:
988 if not opts['force']:
989 ui.warn(_('%s: not overwriting - file exists\n') %
989 ui.warn(_('%s: not overwriting - file exists\n') %
990 reltarget)
990 reltarget)
991 return
991 return
992 if not opts['after'] and not opts.get('dry_run'):
992 if not opts['after'] and not opts.get('dry_run'):
993 os.unlink(reltarget)
993 os.unlink(reltarget)
994 if opts['after']:
994 if opts['after']:
995 if not os.path.exists(reltarget):
995 if not os.path.exists(reltarget):
996 return
996 return
997 else:
997 else:
998 targetdir = os.path.dirname(reltarget) or '.'
998 targetdir = os.path.dirname(reltarget) or '.'
999 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
999 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1000 os.makedirs(targetdir)
1000 os.makedirs(targetdir)
1001 try:
1001 try:
1002 restore = repo.dirstate.state(abstarget) == 'r'
1002 restore = repo.dirstate.state(abstarget) == 'r'
1003 if restore and not opts.get('dry_run'):
1003 if restore and not opts.get('dry_run'):
1004 repo.undelete([abstarget], wlock)
1004 repo.undelete([abstarget], wlock)
1005 try:
1005 try:
1006 if not opts.get('dry_run'):
1006 if not opts.get('dry_run'):
1007 shutil.copyfile(relsrc, reltarget)
1007 util.copyfile(relsrc, reltarget)
1008 shutil.copymode(relsrc, reltarget)
1009 restore = False
1008 restore = False
1010 finally:
1009 finally:
1011 if restore:
1010 if restore:
1012 repo.remove([abstarget], wlock)
1011 repo.remove([abstarget], wlock)
1013 except shutil.Error, inst:
1014 raise util.Abort(str(inst))
1015 except IOError, inst:
1012 except IOError, inst:
1016 if inst.errno == errno.ENOENT:
1013 if inst.errno == errno.ENOENT:
1017 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1014 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1018 else:
1015 else:
1019 ui.warn(_('%s: cannot copy - %s\n') %
1016 ui.warn(_('%s: cannot copy - %s\n') %
1020 (relsrc, inst.strerror))
1017 (relsrc, inst.strerror))
1021 errors += 1
1018 errors += 1
1022 return
1019 return
1023 if ui.verbose or not exact:
1020 if ui.verbose or not exact:
1024 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1021 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1025 targets[abstarget] = abssrc
1022 targets[abstarget] = abssrc
1026 if abstarget != origsrc and not opts.get('dry_run'):
1023 if abstarget != origsrc and not opts.get('dry_run'):
1027 repo.copy(origsrc, abstarget, wlock)
1024 repo.copy(origsrc, abstarget, wlock)
1028 copied.append((abssrc, relsrc, exact))
1025 copied.append((abssrc, relsrc, exact))
1029
1026
1030 def targetpathfn(pat, dest, srcs):
1027 def targetpathfn(pat, dest, srcs):
1031 if os.path.isdir(pat):
1028 if os.path.isdir(pat):
1032 abspfx = util.canonpath(repo.root, cwd, pat)
1029 abspfx = util.canonpath(repo.root, cwd, pat)
1033 if destdirexists:
1030 if destdirexists:
1034 striplen = len(os.path.split(abspfx)[0])
1031 striplen = len(os.path.split(abspfx)[0])
1035 else:
1032 else:
1036 striplen = len(abspfx)
1033 striplen = len(abspfx)
1037 if striplen:
1034 if striplen:
1038 striplen += len(os.sep)
1035 striplen += len(os.sep)
1039 res = lambda p: os.path.join(dest, p[striplen:])
1036 res = lambda p: os.path.join(dest, p[striplen:])
1040 elif destdirexists:
1037 elif destdirexists:
1041 res = lambda p: os.path.join(dest, os.path.basename(p))
1038 res = lambda p: os.path.join(dest, os.path.basename(p))
1042 else:
1039 else:
1043 res = lambda p: dest
1040 res = lambda p: dest
1044 return res
1041 return res
1045
1042
1046 def targetpathafterfn(pat, dest, srcs):
1043 def targetpathafterfn(pat, dest, srcs):
1047 if util.patkind(pat, None)[0]:
1044 if util.patkind(pat, None)[0]:
1048 # a mercurial pattern
1045 # a mercurial pattern
1049 res = lambda p: os.path.join(dest, os.path.basename(p))
1046 res = lambda p: os.path.join(dest, os.path.basename(p))
1050 else:
1047 else:
1051 abspfx = util.canonpath(repo.root, cwd, pat)
1048 abspfx = util.canonpath(repo.root, cwd, pat)
1052 if len(abspfx) < len(srcs[0][0]):
1049 if len(abspfx) < len(srcs[0][0]):
1053 # A directory. Either the target path contains the last
1050 # A directory. Either the target path contains the last
1054 # component of the source path or it does not.
1051 # component of the source path or it does not.
1055 def evalpath(striplen):
1052 def evalpath(striplen):
1056 score = 0
1053 score = 0
1057 for s in srcs:
1054 for s in srcs:
1058 t = os.path.join(dest, s[0][striplen:])
1055 t = os.path.join(dest, s[0][striplen:])
1059 if os.path.exists(t):
1056 if os.path.exists(t):
1060 score += 1
1057 score += 1
1061 return score
1058 return score
1062
1059
1063 striplen = len(abspfx)
1060 striplen = len(abspfx)
1064 if striplen:
1061 if striplen:
1065 striplen += len(os.sep)
1062 striplen += len(os.sep)
1066 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1063 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1067 score = evalpath(striplen)
1064 score = evalpath(striplen)
1068 striplen1 = len(os.path.split(abspfx)[0])
1065 striplen1 = len(os.path.split(abspfx)[0])
1069 if striplen1:
1066 if striplen1:
1070 striplen1 += len(os.sep)
1067 striplen1 += len(os.sep)
1071 if evalpath(striplen1) > score:
1068 if evalpath(striplen1) > score:
1072 striplen = striplen1
1069 striplen = striplen1
1073 res = lambda p: os.path.join(dest, p[striplen:])
1070 res = lambda p: os.path.join(dest, p[striplen:])
1074 else:
1071 else:
1075 # a file
1072 # a file
1076 if destdirexists:
1073 if destdirexists:
1077 res = lambda p: os.path.join(dest, os.path.basename(p))
1074 res = lambda p: os.path.join(dest, os.path.basename(p))
1078 else:
1075 else:
1079 res = lambda p: dest
1076 res = lambda p: dest
1080 return res
1077 return res
1081
1078
1082
1079
1083 pats = list(pats)
1080 pats = list(pats)
1084 if not pats:
1081 if not pats:
1085 raise util.Abort(_('no source or destination specified'))
1082 raise util.Abort(_('no source or destination specified'))
1086 if len(pats) == 1:
1083 if len(pats) == 1:
1087 raise util.Abort(_('no destination specified'))
1084 raise util.Abort(_('no destination specified'))
1088 dest = pats.pop()
1085 dest = pats.pop()
1089 destdirexists = os.path.isdir(dest)
1086 destdirexists = os.path.isdir(dest)
1090 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1087 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1091 raise util.Abort(_('with multiple sources, destination must be an '
1088 raise util.Abort(_('with multiple sources, destination must be an '
1092 'existing directory'))
1089 'existing directory'))
1093 if opts['after']:
1090 if opts['after']:
1094 tfn = targetpathafterfn
1091 tfn = targetpathafterfn
1095 else:
1092 else:
1096 tfn = targetpathfn
1093 tfn = targetpathfn
1097 copylist = []
1094 copylist = []
1098 for pat in pats:
1095 for pat in pats:
1099 srcs = []
1096 srcs = []
1100 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1097 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1101 origsrc = okaytocopy(abssrc, relsrc, exact)
1098 origsrc = okaytocopy(abssrc, relsrc, exact)
1102 if origsrc:
1099 if origsrc:
1103 srcs.append((origsrc, abssrc, relsrc, exact))
1100 srcs.append((origsrc, abssrc, relsrc, exact))
1104 if not srcs:
1101 if not srcs:
1105 continue
1102 continue
1106 copylist.append((tfn(pat, dest, srcs), srcs))
1103 copylist.append((tfn(pat, dest, srcs), srcs))
1107 if not copylist:
1104 if not copylist:
1108 raise util.Abort(_('no files to copy'))
1105 raise util.Abort(_('no files to copy'))
1109
1106
1110 for targetpath, srcs in copylist:
1107 for targetpath, srcs in copylist:
1111 for origsrc, abssrc, relsrc, exact in srcs:
1108 for origsrc, abssrc, relsrc, exact in srcs:
1112 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1109 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1113
1110
1114 if errors:
1111 if errors:
1115 ui.warn(_('(consider using --after)\n'))
1112 ui.warn(_('(consider using --after)\n'))
1116 return errors, copied
1113 return errors, copied
1117
1114
1118 def copy(ui, repo, *pats, **opts):
1115 def copy(ui, repo, *pats, **opts):
1119 """mark files as copied for the next commit
1116 """mark files as copied for the next commit
1120
1117
1121 Mark dest as having copies of source files. If dest is a
1118 Mark dest as having copies of source files. If dest is a
1122 directory, copies are put in that directory. If dest is a file,
1119 directory, copies are put in that directory. If dest is a file,
1123 there can only be one source.
1120 there can only be one source.
1124
1121
1125 By default, this command copies the contents of files as they
1122 By default, this command copies the contents of files as they
1126 stand in the working directory. If invoked with --after, the
1123 stand in the working directory. If invoked with --after, the
1127 operation is recorded, but no copying is performed.
1124 operation is recorded, but no copying is performed.
1128
1125
1129 This command takes effect in the next commit.
1126 This command takes effect in the next commit.
1130
1127
1131 NOTE: This command should be treated as experimental. While it
1128 NOTE: This command should be treated as experimental. While it
1132 should properly record copied files, this information is not yet
1129 should properly record copied files, this information is not yet
1133 fully used by merge, nor fully reported by log.
1130 fully used by merge, nor fully reported by log.
1134 """
1131 """
1135 wlock = repo.wlock(0)
1132 wlock = repo.wlock(0)
1136 errs, copied = docopy(ui, repo, pats, opts, wlock)
1133 errs, copied = docopy(ui, repo, pats, opts, wlock)
1137 return errs
1134 return errs
1138
1135
1139 def debugancestor(ui, index, rev1, rev2):
1136 def debugancestor(ui, index, rev1, rev2):
1140 """find the ancestor revision of two revisions in a given index"""
1137 """find the ancestor revision of two revisions in a given index"""
1141 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1138 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1142 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1139 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1143 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1140 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1144
1141
1145 def debugcomplete(ui, cmd='', **opts):
1142 def debugcomplete(ui, cmd='', **opts):
1146 """returns the completion list associated with the given command"""
1143 """returns the completion list associated with the given command"""
1147
1144
1148 if opts['options']:
1145 if opts['options']:
1149 options = []
1146 options = []
1150 otables = [globalopts]
1147 otables = [globalopts]
1151 if cmd:
1148 if cmd:
1152 aliases, entry = findcmd(ui, cmd)
1149 aliases, entry = findcmd(ui, cmd)
1153 otables.append(entry[1])
1150 otables.append(entry[1])
1154 for t in otables:
1151 for t in otables:
1155 for o in t:
1152 for o in t:
1156 if o[0]:
1153 if o[0]:
1157 options.append('-%s' % o[0])
1154 options.append('-%s' % o[0])
1158 options.append('--%s' % o[1])
1155 options.append('--%s' % o[1])
1159 ui.write("%s\n" % "\n".join(options))
1156 ui.write("%s\n" % "\n".join(options))
1160 return
1157 return
1161
1158
1162 clist = findpossible(ui, cmd).keys()
1159 clist = findpossible(ui, cmd).keys()
1163 clist.sort()
1160 clist.sort()
1164 ui.write("%s\n" % "\n".join(clist))
1161 ui.write("%s\n" % "\n".join(clist))
1165
1162
1166 def debugrebuildstate(ui, repo, rev=None):
1163 def debugrebuildstate(ui, repo, rev=None):
1167 """rebuild the dirstate as it would look like for the given revision"""
1164 """rebuild the dirstate as it would look like for the given revision"""
1168 if not rev:
1165 if not rev:
1169 rev = repo.changelog.tip()
1166 rev = repo.changelog.tip()
1170 else:
1167 else:
1171 rev = repo.lookup(rev)
1168 rev = repo.lookup(rev)
1172 change = repo.changelog.read(rev)
1169 change = repo.changelog.read(rev)
1173 n = change[0]
1170 n = change[0]
1174 files = repo.manifest.read(n)
1171 files = repo.manifest.read(n)
1175 wlock = repo.wlock()
1172 wlock = repo.wlock()
1176 repo.dirstate.rebuild(rev, files)
1173 repo.dirstate.rebuild(rev, files)
1177
1174
1178 def debugcheckstate(ui, repo):
1175 def debugcheckstate(ui, repo):
1179 """validate the correctness of the current dirstate"""
1176 """validate the correctness of the current dirstate"""
1180 parent1, parent2 = repo.dirstate.parents()
1177 parent1, parent2 = repo.dirstate.parents()
1181 repo.dirstate.read()
1178 repo.dirstate.read()
1182 dc = repo.dirstate.map
1179 dc = repo.dirstate.map
1183 keys = dc.keys()
1180 keys = dc.keys()
1184 keys.sort()
1181 keys.sort()
1185 m1n = repo.changelog.read(parent1)[0]
1182 m1n = repo.changelog.read(parent1)[0]
1186 m2n = repo.changelog.read(parent2)[0]
1183 m2n = repo.changelog.read(parent2)[0]
1187 m1 = repo.manifest.read(m1n)
1184 m1 = repo.manifest.read(m1n)
1188 m2 = repo.manifest.read(m2n)
1185 m2 = repo.manifest.read(m2n)
1189 errors = 0
1186 errors = 0
1190 for f in dc:
1187 for f in dc:
1191 state = repo.dirstate.state(f)
1188 state = repo.dirstate.state(f)
1192 if state in "nr" and f not in m1:
1189 if state in "nr" and f not in m1:
1193 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1190 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1194 errors += 1
1191 errors += 1
1195 if state in "a" and f in m1:
1192 if state in "a" and f in m1:
1196 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1193 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1197 errors += 1
1194 errors += 1
1198 if state in "m" and f not in m1 and f not in m2:
1195 if state in "m" and f not in m1 and f not in m2:
1199 ui.warn(_("%s in state %s, but not in either manifest\n") %
1196 ui.warn(_("%s in state %s, but not in either manifest\n") %
1200 (f, state))
1197 (f, state))
1201 errors += 1
1198 errors += 1
1202 for f in m1:
1199 for f in m1:
1203 state = repo.dirstate.state(f)
1200 state = repo.dirstate.state(f)
1204 if state not in "nrm":
1201 if state not in "nrm":
1205 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1202 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1206 errors += 1
1203 errors += 1
1207 if errors:
1204 if errors:
1208 error = _(".hg/dirstate inconsistent with current parent's manifest")
1205 error = _(".hg/dirstate inconsistent with current parent's manifest")
1209 raise util.Abort(error)
1206 raise util.Abort(error)
1210
1207
1211 def showconfig(ui, repo, *values, **opts):
1208 def showconfig(ui, repo, *values, **opts):
1212 """show combined config settings from all hgrc files
1209 """show combined config settings from all hgrc files
1213
1210
1214 With no args, print names and values of all config items.
1211 With no args, print names and values of all config items.
1215
1212
1216 With one arg of the form section.name, print just the value of
1213 With one arg of the form section.name, print just the value of
1217 that config item.
1214 that config item.
1218
1215
1219 With multiple args, print names and values of all config items
1216 With multiple args, print names and values of all config items
1220 with matching section names."""
1217 with matching section names."""
1221
1218
1222 untrusted = bool(opts.get('untrusted'))
1219 untrusted = bool(opts.get('untrusted'))
1223 if values:
1220 if values:
1224 if len([v for v in values if '.' in v]) > 1:
1221 if len([v for v in values if '.' in v]) > 1:
1225 raise util.Abort(_('only one config item permitted'))
1222 raise util.Abort(_('only one config item permitted'))
1226 for section, name, value in ui.walkconfig(untrusted=untrusted):
1223 for section, name, value in ui.walkconfig(untrusted=untrusted):
1227 sectname = section + '.' + name
1224 sectname = section + '.' + name
1228 if values:
1225 if values:
1229 for v in values:
1226 for v in values:
1230 if v == section:
1227 if v == section:
1231 ui.write('%s=%s\n' % (sectname, value))
1228 ui.write('%s=%s\n' % (sectname, value))
1232 elif v == sectname:
1229 elif v == sectname:
1233 ui.write(value, '\n')
1230 ui.write(value, '\n')
1234 else:
1231 else:
1235 ui.write('%s=%s\n' % (sectname, value))
1232 ui.write('%s=%s\n' % (sectname, value))
1236
1233
1237 def debugsetparents(ui, repo, rev1, rev2=None):
1234 def debugsetparents(ui, repo, rev1, rev2=None):
1238 """manually set the parents of the current working directory
1235 """manually set the parents of the current working directory
1239
1236
1240 This is useful for writing repository conversion tools, but should
1237 This is useful for writing repository conversion tools, but should
1241 be used with care.
1238 be used with care.
1242 """
1239 """
1243
1240
1244 if not rev2:
1241 if not rev2:
1245 rev2 = hex(nullid)
1242 rev2 = hex(nullid)
1246
1243
1247 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1244 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1248
1245
1249 def debugstate(ui, repo):
1246 def debugstate(ui, repo):
1250 """show the contents of the current dirstate"""
1247 """show the contents of the current dirstate"""
1251 repo.dirstate.read()
1248 repo.dirstate.read()
1252 dc = repo.dirstate.map
1249 dc = repo.dirstate.map
1253 keys = dc.keys()
1250 keys = dc.keys()
1254 keys.sort()
1251 keys.sort()
1255 for file_ in keys:
1252 for file_ in keys:
1256 ui.write("%c %3o %10d %s %s\n"
1253 ui.write("%c %3o %10d %s %s\n"
1257 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1254 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1258 time.strftime("%x %X",
1255 time.strftime("%x %X",
1259 time.localtime(dc[file_][3])), file_))
1256 time.localtime(dc[file_][3])), file_))
1260 for f in repo.dirstate.copies():
1257 for f in repo.dirstate.copies():
1261 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1258 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1262
1259
1263 def debugdata(ui, file_, rev):
1260 def debugdata(ui, file_, rev):
1264 """dump the contents of an data file revision"""
1261 """dump the contents of an data file revision"""
1265 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1262 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1266 file_[:-2] + ".i", file_, 0)
1263 file_[:-2] + ".i", file_, 0)
1267 try:
1264 try:
1268 ui.write(r.revision(r.lookup(rev)))
1265 ui.write(r.revision(r.lookup(rev)))
1269 except KeyError:
1266 except KeyError:
1270 raise util.Abort(_('invalid revision identifier %s') % rev)
1267 raise util.Abort(_('invalid revision identifier %s') % rev)
1271
1268
1272 def debugindex(ui, file_):
1269 def debugindex(ui, file_):
1273 """dump the contents of an index file"""
1270 """dump the contents of an index file"""
1274 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1271 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1275 ui.write(" rev offset length base linkrev" +
1272 ui.write(" rev offset length base linkrev" +
1276 " nodeid p1 p2\n")
1273 " nodeid p1 p2\n")
1277 for i in xrange(r.count()):
1274 for i in xrange(r.count()):
1278 node = r.node(i)
1275 node = r.node(i)
1279 pp = r.parents(node)
1276 pp = r.parents(node)
1280 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1277 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1281 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1278 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1282 short(node), short(pp[0]), short(pp[1])))
1279 short(node), short(pp[0]), short(pp[1])))
1283
1280
1284 def debugindexdot(ui, file_):
1281 def debugindexdot(ui, file_):
1285 """dump an index DAG as a .dot file"""
1282 """dump an index DAG as a .dot file"""
1286 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1283 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1287 ui.write("digraph G {\n")
1284 ui.write("digraph G {\n")
1288 for i in xrange(r.count()):
1285 for i in xrange(r.count()):
1289 node = r.node(i)
1286 node = r.node(i)
1290 pp = r.parents(node)
1287 pp = r.parents(node)
1291 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1288 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1292 if pp[1] != nullid:
1289 if pp[1] != nullid:
1293 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1290 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1294 ui.write("}\n")
1291 ui.write("}\n")
1295
1292
1296 def debugrename(ui, repo, file, rev=None):
1293 def debugrename(ui, repo, file, rev=None):
1297 """dump rename information"""
1294 """dump rename information"""
1298 r = repo.file(relpath(repo, [file])[0])
1295 r = repo.file(relpath(repo, [file])[0])
1299 if rev:
1296 if rev:
1300 try:
1297 try:
1301 # assume all revision numbers are for changesets
1298 # assume all revision numbers are for changesets
1302 n = repo.lookup(rev)
1299 n = repo.lookup(rev)
1303 change = repo.changelog.read(n)
1300 change = repo.changelog.read(n)
1304 m = repo.manifest.read(change[0])
1301 m = repo.manifest.read(change[0])
1305 n = m[relpath(repo, [file])[0]]
1302 n = m[relpath(repo, [file])[0]]
1306 except (hg.RepoError, KeyError):
1303 except (hg.RepoError, KeyError):
1307 n = r.lookup(rev)
1304 n = r.lookup(rev)
1308 else:
1305 else:
1309 n = r.tip()
1306 n = r.tip()
1310 m = r.renamed(n)
1307 m = r.renamed(n)
1311 if m:
1308 if m:
1312 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1309 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1313 else:
1310 else:
1314 ui.write(_("not renamed\n"))
1311 ui.write(_("not renamed\n"))
1315
1312
1316 def debugwalk(ui, repo, *pats, **opts):
1313 def debugwalk(ui, repo, *pats, **opts):
1317 """show how files match on given patterns"""
1314 """show how files match on given patterns"""
1318 items = list(cmdutil.walk(repo, pats, opts))
1315 items = list(cmdutil.walk(repo, pats, opts))
1319 if not items:
1316 if not items:
1320 return
1317 return
1321 fmt = '%%s %%-%ds %%-%ds %%s' % (
1318 fmt = '%%s %%-%ds %%-%ds %%s' % (
1322 max([len(abs) for (src, abs, rel, exact) in items]),
1319 max([len(abs) for (src, abs, rel, exact) in items]),
1323 max([len(rel) for (src, abs, rel, exact) in items]))
1320 max([len(rel) for (src, abs, rel, exact) in items]))
1324 for src, abs, rel, exact in items:
1321 for src, abs, rel, exact in items:
1325 line = fmt % (src, abs, rel, exact and 'exact' or '')
1322 line = fmt % (src, abs, rel, exact and 'exact' or '')
1326 ui.write("%s\n" % line.rstrip())
1323 ui.write("%s\n" % line.rstrip())
1327
1324
1328 def diff(ui, repo, *pats, **opts):
1325 def diff(ui, repo, *pats, **opts):
1329 """diff repository (or selected files)
1326 """diff repository (or selected files)
1330
1327
1331 Show differences between revisions for the specified files.
1328 Show differences between revisions for the specified files.
1332
1329
1333 Differences between files are shown using the unified diff format.
1330 Differences between files are shown using the unified diff format.
1334
1331
1335 When two revision arguments are given, then changes are shown
1332 When two revision arguments are given, then changes are shown
1336 between those revisions. If only one revision is specified then
1333 between those revisions. If only one revision is specified then
1337 that revision is compared to the working directory, and, when no
1334 that revision is compared to the working directory, and, when no
1338 revisions are specified, the working directory files are compared
1335 revisions are specified, the working directory files are compared
1339 to its parent.
1336 to its parent.
1340
1337
1341 Without the -a option, diff will avoid generating diffs of files
1338 Without the -a option, diff will avoid generating diffs of files
1342 it detects as binary. With -a, diff will generate a diff anyway,
1339 it detects as binary. With -a, diff will generate a diff anyway,
1343 probably with undesirable results.
1340 probably with undesirable results.
1344 """
1341 """
1345 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1342 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1346
1343
1347 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1344 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1348
1345
1349 patch.diff(repo, node1, node2, fns, match=matchfn,
1346 patch.diff(repo, node1, node2, fns, match=matchfn,
1350 opts=patch.diffopts(ui, opts))
1347 opts=patch.diffopts(ui, opts))
1351
1348
1352 def export(ui, repo, *changesets, **opts):
1349 def export(ui, repo, *changesets, **opts):
1353 """dump the header and diffs for one or more changesets
1350 """dump the header and diffs for one or more changesets
1354
1351
1355 Print the changeset header and diffs for one or more revisions.
1352 Print the changeset header and diffs for one or more revisions.
1356
1353
1357 The information shown in the changeset header is: author,
1354 The information shown in the changeset header is: author,
1358 changeset hash, parent and commit comment.
1355 changeset hash, parent and commit comment.
1359
1356
1360 Output may be to a file, in which case the name of the file is
1357 Output may be to a file, in which case the name of the file is
1361 given using a format string. The formatting rules are as follows:
1358 given using a format string. The formatting rules are as follows:
1362
1359
1363 %% literal "%" character
1360 %% literal "%" character
1364 %H changeset hash (40 bytes of hexadecimal)
1361 %H changeset hash (40 bytes of hexadecimal)
1365 %N number of patches being generated
1362 %N number of patches being generated
1366 %R changeset revision number
1363 %R changeset revision number
1367 %b basename of the exporting repository
1364 %b basename of the exporting repository
1368 %h short-form changeset hash (12 bytes of hexadecimal)
1365 %h short-form changeset hash (12 bytes of hexadecimal)
1369 %n zero-padded sequence number, starting at 1
1366 %n zero-padded sequence number, starting at 1
1370 %r zero-padded changeset revision number
1367 %r zero-padded changeset revision number
1371
1368
1372 Without the -a option, export will avoid generating diffs of files
1369 Without the -a option, export will avoid generating diffs of files
1373 it detects as binary. With -a, export will generate a diff anyway,
1370 it detects as binary. With -a, export will generate a diff anyway,
1374 probably with undesirable results.
1371 probably with undesirable results.
1375
1372
1376 With the --switch-parent option, the diff will be against the second
1373 With the --switch-parent option, the diff will be against the second
1377 parent. It can be useful to review a merge.
1374 parent. It can be useful to review a merge.
1378 """
1375 """
1379 if not changesets:
1376 if not changesets:
1380 raise util.Abort(_("export requires at least one changeset"))
1377 raise util.Abort(_("export requires at least one changeset"))
1381 revs = cmdutil.revrange(ui, repo, changesets)
1378 revs = cmdutil.revrange(ui, repo, changesets)
1382 if len(revs) > 1:
1379 if len(revs) > 1:
1383 ui.note(_('exporting patches:\n'))
1380 ui.note(_('exporting patches:\n'))
1384 else:
1381 else:
1385 ui.note(_('exporting patch:\n'))
1382 ui.note(_('exporting patch:\n'))
1386 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1383 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1387 switch_parent=opts['switch_parent'],
1384 switch_parent=opts['switch_parent'],
1388 opts=patch.diffopts(ui, opts))
1385 opts=patch.diffopts(ui, opts))
1389
1386
1390 def grep(ui, repo, pattern, *pats, **opts):
1387 def grep(ui, repo, pattern, *pats, **opts):
1391 """search for a pattern in specified files and revisions
1388 """search for a pattern in specified files and revisions
1392
1389
1393 Search revisions of files for a regular expression.
1390 Search revisions of files for a regular expression.
1394
1391
1395 This command behaves differently than Unix grep. It only accepts
1392 This command behaves differently than Unix grep. It only accepts
1396 Python/Perl regexps. It searches repository history, not the
1393 Python/Perl regexps. It searches repository history, not the
1397 working directory. It always prints the revision number in which
1394 working directory. It always prints the revision number in which
1398 a match appears.
1395 a match appears.
1399
1396
1400 By default, grep only prints output for the first revision of a
1397 By default, grep only prints output for the first revision of a
1401 file in which it finds a match. To get it to print every revision
1398 file in which it finds a match. To get it to print every revision
1402 that contains a change in match status ("-" for a match that
1399 that contains a change in match status ("-" for a match that
1403 becomes a non-match, or "+" for a non-match that becomes a match),
1400 becomes a non-match, or "+" for a non-match that becomes a match),
1404 use the --all flag.
1401 use the --all flag.
1405 """
1402 """
1406 reflags = 0
1403 reflags = 0
1407 if opts['ignore_case']:
1404 if opts['ignore_case']:
1408 reflags |= re.I
1405 reflags |= re.I
1409 regexp = re.compile(pattern, reflags)
1406 regexp = re.compile(pattern, reflags)
1410 sep, eol = ':', '\n'
1407 sep, eol = ':', '\n'
1411 if opts['print0']:
1408 if opts['print0']:
1412 sep = eol = '\0'
1409 sep = eol = '\0'
1413
1410
1414 fcache = {}
1411 fcache = {}
1415 def getfile(fn):
1412 def getfile(fn):
1416 if fn not in fcache:
1413 if fn not in fcache:
1417 fcache[fn] = repo.file(fn)
1414 fcache[fn] = repo.file(fn)
1418 return fcache[fn]
1415 return fcache[fn]
1419
1416
1420 def matchlines(body):
1417 def matchlines(body):
1421 begin = 0
1418 begin = 0
1422 linenum = 0
1419 linenum = 0
1423 while True:
1420 while True:
1424 match = regexp.search(body, begin)
1421 match = regexp.search(body, begin)
1425 if not match:
1422 if not match:
1426 break
1423 break
1427 mstart, mend = match.span()
1424 mstart, mend = match.span()
1428 linenum += body.count('\n', begin, mstart) + 1
1425 linenum += body.count('\n', begin, mstart) + 1
1429 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1426 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1430 lend = body.find('\n', mend)
1427 lend = body.find('\n', mend)
1431 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1428 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1432 begin = lend + 1
1429 begin = lend + 1
1433
1430
1434 class linestate(object):
1431 class linestate(object):
1435 def __init__(self, line, linenum, colstart, colend):
1432 def __init__(self, line, linenum, colstart, colend):
1436 self.line = line
1433 self.line = line
1437 self.linenum = linenum
1434 self.linenum = linenum
1438 self.colstart = colstart
1435 self.colstart = colstart
1439 self.colend = colend
1436 self.colend = colend
1440
1437
1441 def __eq__(self, other):
1438 def __eq__(self, other):
1442 return self.line == other.line
1439 return self.line == other.line
1443
1440
1444 matches = {}
1441 matches = {}
1445 copies = {}
1442 copies = {}
1446 def grepbody(fn, rev, body):
1443 def grepbody(fn, rev, body):
1447 matches[rev].setdefault(fn, [])
1444 matches[rev].setdefault(fn, [])
1448 m = matches[rev][fn]
1445 m = matches[rev][fn]
1449 for lnum, cstart, cend, line in matchlines(body):
1446 for lnum, cstart, cend, line in matchlines(body):
1450 s = linestate(line, lnum, cstart, cend)
1447 s = linestate(line, lnum, cstart, cend)
1451 m.append(s)
1448 m.append(s)
1452
1449
1453 def difflinestates(a, b):
1450 def difflinestates(a, b):
1454 sm = difflib.SequenceMatcher(None, a, b)
1451 sm = difflib.SequenceMatcher(None, a, b)
1455 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1452 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1456 if tag == 'insert':
1453 if tag == 'insert':
1457 for i in xrange(blo, bhi):
1454 for i in xrange(blo, bhi):
1458 yield ('+', b[i])
1455 yield ('+', b[i])
1459 elif tag == 'delete':
1456 elif tag == 'delete':
1460 for i in xrange(alo, ahi):
1457 for i in xrange(alo, ahi):
1461 yield ('-', a[i])
1458 yield ('-', a[i])
1462 elif tag == 'replace':
1459 elif tag == 'replace':
1463 for i in xrange(alo, ahi):
1460 for i in xrange(alo, ahi):
1464 yield ('-', a[i])
1461 yield ('-', a[i])
1465 for i in xrange(blo, bhi):
1462 for i in xrange(blo, bhi):
1466 yield ('+', b[i])
1463 yield ('+', b[i])
1467
1464
1468 prev = {}
1465 prev = {}
1469 def display(fn, rev, states, prevstates):
1466 def display(fn, rev, states, prevstates):
1470 counts = {'-': 0, '+': 0}
1467 counts = {'-': 0, '+': 0}
1471 filerevmatches = {}
1468 filerevmatches = {}
1472 if incrementing or not opts['all']:
1469 if incrementing or not opts['all']:
1473 a, b, r = prevstates, states, rev
1470 a, b, r = prevstates, states, rev
1474 else:
1471 else:
1475 a, b, r = states, prevstates, prev.get(fn, -1)
1472 a, b, r = states, prevstates, prev.get(fn, -1)
1476 for change, l in difflinestates(a, b):
1473 for change, l in difflinestates(a, b):
1477 cols = [fn, str(r)]
1474 cols = [fn, str(r)]
1478 if opts['line_number']:
1475 if opts['line_number']:
1479 cols.append(str(l.linenum))
1476 cols.append(str(l.linenum))
1480 if opts['all']:
1477 if opts['all']:
1481 cols.append(change)
1478 cols.append(change)
1482 if opts['user']:
1479 if opts['user']:
1483 cols.append(ui.shortuser(getchange(r)[1]))
1480 cols.append(ui.shortuser(getchange(r)[1]))
1484 if opts['files_with_matches']:
1481 if opts['files_with_matches']:
1485 c = (fn, r)
1482 c = (fn, r)
1486 if c in filerevmatches:
1483 if c in filerevmatches:
1487 continue
1484 continue
1488 filerevmatches[c] = 1
1485 filerevmatches[c] = 1
1489 else:
1486 else:
1490 cols.append(l.line)
1487 cols.append(l.line)
1491 ui.write(sep.join(cols), eol)
1488 ui.write(sep.join(cols), eol)
1492 counts[change] += 1
1489 counts[change] += 1
1493 return counts['+'], counts['-']
1490 return counts['+'], counts['-']
1494
1491
1495 fstate = {}
1492 fstate = {}
1496 skip = {}
1493 skip = {}
1497 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1494 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1498 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1495 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1499 count = 0
1496 count = 0
1500 incrementing = False
1497 incrementing = False
1501 follow = opts.get('follow')
1498 follow = opts.get('follow')
1502 for st, rev, fns in changeiter:
1499 for st, rev, fns in changeiter:
1503 if st == 'window':
1500 if st == 'window':
1504 incrementing = rev
1501 incrementing = rev
1505 matches.clear()
1502 matches.clear()
1506 elif st == 'add':
1503 elif st == 'add':
1507 mf = repo.changectx(rev).manifest()
1504 mf = repo.changectx(rev).manifest()
1508 matches[rev] = {}
1505 matches[rev] = {}
1509 for fn in fns:
1506 for fn in fns:
1510 if fn in skip:
1507 if fn in skip:
1511 continue
1508 continue
1512 fstate.setdefault(fn, {})
1509 fstate.setdefault(fn, {})
1513 try:
1510 try:
1514 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1511 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1515 if follow:
1512 if follow:
1516 copied = getfile(fn).renamed(mf[fn])
1513 copied = getfile(fn).renamed(mf[fn])
1517 if copied:
1514 if copied:
1518 copies.setdefault(rev, {})[fn] = copied[0]
1515 copies.setdefault(rev, {})[fn] = copied[0]
1519 except KeyError:
1516 except KeyError:
1520 pass
1517 pass
1521 elif st == 'iter':
1518 elif st == 'iter':
1522 states = matches[rev].items()
1519 states = matches[rev].items()
1523 states.sort()
1520 states.sort()
1524 for fn, m in states:
1521 for fn, m in states:
1525 copy = copies.get(rev, {}).get(fn)
1522 copy = copies.get(rev, {}).get(fn)
1526 if fn in skip:
1523 if fn in skip:
1527 if copy:
1524 if copy:
1528 skip[copy] = True
1525 skip[copy] = True
1529 continue
1526 continue
1530 if incrementing or not opts['all'] or fstate[fn]:
1527 if incrementing or not opts['all'] or fstate[fn]:
1531 pos, neg = display(fn, rev, m, fstate[fn])
1528 pos, neg = display(fn, rev, m, fstate[fn])
1532 count += pos + neg
1529 count += pos + neg
1533 if pos and not opts['all']:
1530 if pos and not opts['all']:
1534 skip[fn] = True
1531 skip[fn] = True
1535 if copy:
1532 if copy:
1536 skip[copy] = True
1533 skip[copy] = True
1537 fstate[fn] = m
1534 fstate[fn] = m
1538 if copy:
1535 if copy:
1539 fstate[copy] = m
1536 fstate[copy] = m
1540 prev[fn] = rev
1537 prev[fn] = rev
1541
1538
1542 if not incrementing:
1539 if not incrementing:
1543 fstate = fstate.items()
1540 fstate = fstate.items()
1544 fstate.sort()
1541 fstate.sort()
1545 for fn, state in fstate:
1542 for fn, state in fstate:
1546 if fn in skip:
1543 if fn in skip:
1547 continue
1544 continue
1548 if fn not in copies.get(prev[fn], {}):
1545 if fn not in copies.get(prev[fn], {}):
1549 display(fn, rev, {}, state)
1546 display(fn, rev, {}, state)
1550 return (count == 0 and 1) or 0
1547 return (count == 0 and 1) or 0
1551
1548
1552 def heads(ui, repo, **opts):
1549 def heads(ui, repo, **opts):
1553 """show current repository heads
1550 """show current repository heads
1554
1551
1555 Show all repository head changesets.
1552 Show all repository head changesets.
1556
1553
1557 Repository "heads" are changesets that don't have children
1554 Repository "heads" are changesets that don't have children
1558 changesets. They are where development generally takes place and
1555 changesets. They are where development generally takes place and
1559 are the usual targets for update and merge operations.
1556 are the usual targets for update and merge operations.
1560 """
1557 """
1561 if opts['rev']:
1558 if opts['rev']:
1562 heads = repo.heads(repo.lookup(opts['rev']))
1559 heads = repo.heads(repo.lookup(opts['rev']))
1563 else:
1560 else:
1564 heads = repo.heads()
1561 heads = repo.heads()
1565 br = None
1562 br = None
1566 if opts['branches']:
1563 if opts['branches']:
1567 ui.warn(_("the --branches option is deprecated, "
1564 ui.warn(_("the --branches option is deprecated, "
1568 "please use 'hg branches' instead\n"))
1565 "please use 'hg branches' instead\n"))
1569 br = repo.branchlookup(heads)
1566 br = repo.branchlookup(heads)
1570 displayer = show_changeset(ui, repo, opts)
1567 displayer = show_changeset(ui, repo, opts)
1571 for n in heads:
1568 for n in heads:
1572 displayer.show(changenode=n, brinfo=br)
1569 displayer.show(changenode=n, brinfo=br)
1573
1570
1574 def identify(ui, repo):
1571 def identify(ui, repo):
1575 """print information about the working copy
1572 """print information about the working copy
1576
1573
1577 Print a short summary of the current state of the repo.
1574 Print a short summary of the current state of the repo.
1578
1575
1579 This summary identifies the repository state using one or two parent
1576 This summary identifies the repository state using one or two parent
1580 hash identifiers, followed by a "+" if there are uncommitted changes
1577 hash identifiers, followed by a "+" if there are uncommitted changes
1581 in the working directory, followed by a list of tags for this revision.
1578 in the working directory, followed by a list of tags for this revision.
1582 """
1579 """
1583 parents = [p for p in repo.dirstate.parents() if p != nullid]
1580 parents = [p for p in repo.dirstate.parents() if p != nullid]
1584 if not parents:
1581 if not parents:
1585 ui.write(_("unknown\n"))
1582 ui.write(_("unknown\n"))
1586 return
1583 return
1587
1584
1588 hexfunc = ui.debugflag and hex or short
1585 hexfunc = ui.debugflag and hex or short
1589 modified, added, removed, deleted = repo.status()[:4]
1586 modified, added, removed, deleted = repo.status()[:4]
1590 output = ["%s%s" %
1587 output = ["%s%s" %
1591 ('+'.join([hexfunc(parent) for parent in parents]),
1588 ('+'.join([hexfunc(parent) for parent in parents]),
1592 (modified or added or removed or deleted) and "+" or "")]
1589 (modified or added or removed or deleted) and "+" or "")]
1593
1590
1594 if not ui.quiet:
1591 if not ui.quiet:
1595
1592
1596 branch = repo.workingctx().branch()
1593 branch = repo.workingctx().branch()
1597 if branch:
1594 if branch:
1598 output.append("(%s)" % branch)
1595 output.append("(%s)" % branch)
1599
1596
1600 # multiple tags for a single parent separated by '/'
1597 # multiple tags for a single parent separated by '/'
1601 parenttags = ['/'.join(tags)
1598 parenttags = ['/'.join(tags)
1602 for tags in map(repo.nodetags, parents) if tags]
1599 for tags in map(repo.nodetags, parents) if tags]
1603 # tags for multiple parents separated by ' + '
1600 # tags for multiple parents separated by ' + '
1604 if parenttags:
1601 if parenttags:
1605 output.append(' + '.join(parenttags))
1602 output.append(' + '.join(parenttags))
1606
1603
1607 ui.write("%s\n" % ' '.join(output))
1604 ui.write("%s\n" % ' '.join(output))
1608
1605
1609 def import_(ui, repo, patch1, *patches, **opts):
1606 def import_(ui, repo, patch1, *patches, **opts):
1610 """import an ordered set of patches
1607 """import an ordered set of patches
1611
1608
1612 Import a list of patches and commit them individually.
1609 Import a list of patches and commit them individually.
1613
1610
1614 If there are outstanding changes in the working directory, import
1611 If there are outstanding changes in the working directory, import
1615 will abort unless given the -f flag.
1612 will abort unless given the -f flag.
1616
1613
1617 You can import a patch straight from a mail message. Even patches
1614 You can import a patch straight from a mail message. Even patches
1618 as attachments work (body part must be type text/plain or
1615 as attachments work (body part must be type text/plain or
1619 text/x-patch to be used). From and Subject headers of email
1616 text/x-patch to be used). From and Subject headers of email
1620 message are used as default committer and commit message. All
1617 message are used as default committer and commit message. All
1621 text/plain body parts before first diff are added to commit
1618 text/plain body parts before first diff are added to commit
1622 message.
1619 message.
1623
1620
1624 If imported patch was generated by hg export, user and description
1621 If imported patch was generated by hg export, user and description
1625 from patch override values from message headers and body. Values
1622 from patch override values from message headers and body. Values
1626 given on command line with -m and -u override these.
1623 given on command line with -m and -u override these.
1627
1624
1628 To read a patch from standard input, use patch name "-".
1625 To read a patch from standard input, use patch name "-".
1629 """
1626 """
1630 patches = (patch1,) + patches
1627 patches = (patch1,) + patches
1631
1628
1632 if not opts['force']:
1629 if not opts['force']:
1633 bail_if_changed(repo)
1630 bail_if_changed(repo)
1634
1631
1635 d = opts["base"]
1632 d = opts["base"]
1636 strip = opts["strip"]
1633 strip = opts["strip"]
1637
1634
1638 wlock = repo.wlock()
1635 wlock = repo.wlock()
1639 lock = repo.lock()
1636 lock = repo.lock()
1640
1637
1641 for p in patches:
1638 for p in patches:
1642 pf = os.path.join(d, p)
1639 pf = os.path.join(d, p)
1643
1640
1644 if pf == '-':
1641 if pf == '-':
1645 ui.status(_("applying patch from stdin\n"))
1642 ui.status(_("applying patch from stdin\n"))
1646 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1643 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1647 else:
1644 else:
1648 ui.status(_("applying %s\n") % p)
1645 ui.status(_("applying %s\n") % p)
1649 tmpname, message, user, date = patch.extract(ui, file(pf))
1646 tmpname, message, user, date = patch.extract(ui, file(pf))
1650
1647
1651 if tmpname is None:
1648 if tmpname is None:
1652 raise util.Abort(_('no diffs found'))
1649 raise util.Abort(_('no diffs found'))
1653
1650
1654 try:
1651 try:
1655 if opts['message']:
1652 if opts['message']:
1656 # pickup the cmdline msg
1653 # pickup the cmdline msg
1657 message = opts['message']
1654 message = opts['message']
1658 elif message:
1655 elif message:
1659 # pickup the patch msg
1656 # pickup the patch msg
1660 message = message.strip()
1657 message = message.strip()
1661 else:
1658 else:
1662 # launch the editor
1659 # launch the editor
1663 message = None
1660 message = None
1664 ui.debug(_('message:\n%s\n') % message)
1661 ui.debug(_('message:\n%s\n') % message)
1665
1662
1666 files = {}
1663 files = {}
1667 try:
1664 try:
1668 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1665 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1669 files=files)
1666 files=files)
1670 finally:
1667 finally:
1671 files = patch.updatedir(ui, repo, files, wlock=wlock)
1668 files = patch.updatedir(ui, repo, files, wlock=wlock)
1672 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1669 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1673 finally:
1670 finally:
1674 os.unlink(tmpname)
1671 os.unlink(tmpname)
1675
1672
1676 def incoming(ui, repo, source="default", **opts):
1673 def incoming(ui, repo, source="default", **opts):
1677 """show new changesets found in source
1674 """show new changesets found in source
1678
1675
1679 Show new changesets found in the specified path/URL or the default
1676 Show new changesets found in the specified path/URL or the default
1680 pull location. These are the changesets that would be pulled if a pull
1677 pull location. These are the changesets that would be pulled if a pull
1681 was requested.
1678 was requested.
1682
1679
1683 For remote repository, using --bundle avoids downloading the changesets
1680 For remote repository, using --bundle avoids downloading the changesets
1684 twice if the incoming is followed by a pull.
1681 twice if the incoming is followed by a pull.
1685
1682
1686 See pull for valid source format details.
1683 See pull for valid source format details.
1687 """
1684 """
1688 source = ui.expandpath(source)
1685 source = ui.expandpath(source)
1689 setremoteconfig(ui, opts)
1686 setremoteconfig(ui, opts)
1690
1687
1691 other = hg.repository(ui, source)
1688 other = hg.repository(ui, source)
1692 incoming = repo.findincoming(other, force=opts["force"])
1689 incoming = repo.findincoming(other, force=opts["force"])
1693 if not incoming:
1690 if not incoming:
1694 ui.status(_("no changes found\n"))
1691 ui.status(_("no changes found\n"))
1695 return
1692 return
1696
1693
1697 cleanup = None
1694 cleanup = None
1698 try:
1695 try:
1699 fname = opts["bundle"]
1696 fname = opts["bundle"]
1700 if fname or not other.local():
1697 if fname or not other.local():
1701 # create a bundle (uncompressed if other repo is not local)
1698 # create a bundle (uncompressed if other repo is not local)
1702 cg = other.changegroup(incoming, "incoming")
1699 cg = other.changegroup(incoming, "incoming")
1703 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1700 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1704 # keep written bundle?
1701 # keep written bundle?
1705 if opts["bundle"]:
1702 if opts["bundle"]:
1706 cleanup = None
1703 cleanup = None
1707 if not other.local():
1704 if not other.local():
1708 # use the created uncompressed bundlerepo
1705 # use the created uncompressed bundlerepo
1709 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1706 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1710
1707
1711 revs = None
1708 revs = None
1712 if opts['rev']:
1709 if opts['rev']:
1713 revs = [other.lookup(rev) for rev in opts['rev']]
1710 revs = [other.lookup(rev) for rev in opts['rev']]
1714 o = other.changelog.nodesbetween(incoming, revs)[0]
1711 o = other.changelog.nodesbetween(incoming, revs)[0]
1715 if opts['newest_first']:
1712 if opts['newest_first']:
1716 o.reverse()
1713 o.reverse()
1717 displayer = show_changeset(ui, other, opts)
1714 displayer = show_changeset(ui, other, opts)
1718 for n in o:
1715 for n in o:
1719 parents = [p for p in other.changelog.parents(n) if p != nullid]
1716 parents = [p for p in other.changelog.parents(n) if p != nullid]
1720 if opts['no_merges'] and len(parents) == 2:
1717 if opts['no_merges'] and len(parents) == 2:
1721 continue
1718 continue
1722 displayer.show(changenode=n)
1719 displayer.show(changenode=n)
1723 if opts['patch']:
1720 if opts['patch']:
1724 prev = (parents and parents[0]) or nullid
1721 prev = (parents and parents[0]) or nullid
1725 patch.diff(other, prev, n, fp=repo.ui)
1722 patch.diff(other, prev, n, fp=repo.ui)
1726 ui.write("\n")
1723 ui.write("\n")
1727 finally:
1724 finally:
1728 if hasattr(other, 'close'):
1725 if hasattr(other, 'close'):
1729 other.close()
1726 other.close()
1730 if cleanup:
1727 if cleanup:
1731 os.unlink(cleanup)
1728 os.unlink(cleanup)
1732
1729
1733 def init(ui, dest=".", **opts):
1730 def init(ui, dest=".", **opts):
1734 """create a new repository in the given directory
1731 """create a new repository in the given directory
1735
1732
1736 Initialize a new repository in the given directory. If the given
1733 Initialize a new repository in the given directory. If the given
1737 directory does not exist, it is created.
1734 directory does not exist, it is created.
1738
1735
1739 If no directory is given, the current directory is used.
1736 If no directory is given, the current directory is used.
1740
1737
1741 It is possible to specify an ssh:// URL as the destination.
1738 It is possible to specify an ssh:// URL as the destination.
1742 Look at the help text for the pull command for important details
1739 Look at the help text for the pull command for important details
1743 about ssh:// URLs.
1740 about ssh:// URLs.
1744 """
1741 """
1745 setremoteconfig(ui, opts)
1742 setremoteconfig(ui, opts)
1746 hg.repository(ui, dest, create=1)
1743 hg.repository(ui, dest, create=1)
1747
1744
1748 def locate(ui, repo, *pats, **opts):
1745 def locate(ui, repo, *pats, **opts):
1749 """locate files matching specific patterns
1746 """locate files matching specific patterns
1750
1747
1751 Print all files under Mercurial control whose names match the
1748 Print all files under Mercurial control whose names match the
1752 given patterns.
1749 given patterns.
1753
1750
1754 This command searches the current directory and its
1751 This command searches the current directory and its
1755 subdirectories. To search an entire repository, move to the root
1752 subdirectories. To search an entire repository, move to the root
1756 of the repository.
1753 of the repository.
1757
1754
1758 If no patterns are given to match, this command prints all file
1755 If no patterns are given to match, this command prints all file
1759 names.
1756 names.
1760
1757
1761 If you want to feed the output of this command into the "xargs"
1758 If you want to feed the output of this command into the "xargs"
1762 command, use the "-0" option to both this command and "xargs".
1759 command, use the "-0" option to both this command and "xargs".
1763 This will avoid the problem of "xargs" treating single filenames
1760 This will avoid the problem of "xargs" treating single filenames
1764 that contain white space as multiple filenames.
1761 that contain white space as multiple filenames.
1765 """
1762 """
1766 end = opts['print0'] and '\0' or '\n'
1763 end = opts['print0'] and '\0' or '\n'
1767 rev = opts['rev']
1764 rev = opts['rev']
1768 if rev:
1765 if rev:
1769 node = repo.lookup(rev)
1766 node = repo.lookup(rev)
1770 else:
1767 else:
1771 node = None
1768 node = None
1772
1769
1773 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1770 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1774 head='(?:.*/|)'):
1771 head='(?:.*/|)'):
1775 if not node and repo.dirstate.state(abs) == '?':
1772 if not node and repo.dirstate.state(abs) == '?':
1776 continue
1773 continue
1777 if opts['fullpath']:
1774 if opts['fullpath']:
1778 ui.write(os.path.join(repo.root, abs), end)
1775 ui.write(os.path.join(repo.root, abs), end)
1779 else:
1776 else:
1780 ui.write(((pats and rel) or abs), end)
1777 ui.write(((pats and rel) or abs), end)
1781
1778
1782 def log(ui, repo, *pats, **opts):
1779 def log(ui, repo, *pats, **opts):
1783 """show revision history of entire repository or files
1780 """show revision history of entire repository or files
1784
1781
1785 Print the revision history of the specified files or the entire
1782 Print the revision history of the specified files or the entire
1786 project.
1783 project.
1787
1784
1788 File history is shown without following rename or copy history of
1785 File history is shown without following rename or copy history of
1789 files. Use -f/--follow with a file name to follow history across
1786 files. Use -f/--follow with a file name to follow history across
1790 renames and copies. --follow without a file name will only show
1787 renames and copies. --follow without a file name will only show
1791 ancestors or descendants of the starting revision. --follow-first
1788 ancestors or descendants of the starting revision. --follow-first
1792 only follows the first parent of merge revisions.
1789 only follows the first parent of merge revisions.
1793
1790
1794 If no revision range is specified, the default is tip:0 unless
1791 If no revision range is specified, the default is tip:0 unless
1795 --follow is set, in which case the working directory parent is
1792 --follow is set, in which case the working directory parent is
1796 used as the starting revision.
1793 used as the starting revision.
1797
1794
1798 By default this command outputs: changeset id and hash, tags,
1795 By default this command outputs: changeset id and hash, tags,
1799 non-trivial parents, user, date and time, and a summary for each
1796 non-trivial parents, user, date and time, and a summary for each
1800 commit. When the -v/--verbose switch is used, the list of changed
1797 commit. When the -v/--verbose switch is used, the list of changed
1801 files and full commit message is shown.
1798 files and full commit message is shown.
1802 """
1799 """
1803 class dui(object):
1800 class dui(object):
1804 # Implement and delegate some ui protocol. Save hunks of
1801 # Implement and delegate some ui protocol. Save hunks of
1805 # output for later display in the desired order.
1802 # output for later display in the desired order.
1806 def __init__(self, ui):
1803 def __init__(self, ui):
1807 self.ui = ui
1804 self.ui = ui
1808 self.hunk = {}
1805 self.hunk = {}
1809 self.header = {}
1806 self.header = {}
1810 self.quiet = ui.quiet
1807 self.quiet = ui.quiet
1811 self.verbose = ui.verbose
1808 self.verbose = ui.verbose
1812 self.debugflag = ui.debugflag
1809 self.debugflag = ui.debugflag
1813 def bump(self, rev):
1810 def bump(self, rev):
1814 self.rev = rev
1811 self.rev = rev
1815 self.hunk[rev] = []
1812 self.hunk[rev] = []
1816 self.header[rev] = []
1813 self.header[rev] = []
1817 def note(self, *args):
1814 def note(self, *args):
1818 if self.verbose:
1815 if self.verbose:
1819 self.write(*args)
1816 self.write(*args)
1820 def status(self, *args):
1817 def status(self, *args):
1821 if not self.quiet:
1818 if not self.quiet:
1822 self.write(*args)
1819 self.write(*args)
1823 def write(self, *args):
1820 def write(self, *args):
1824 self.hunk[self.rev].extend(args)
1821 self.hunk[self.rev].extend(args)
1825 def write_header(self, *args):
1822 def write_header(self, *args):
1826 self.header[self.rev].extend(args)
1823 self.header[self.rev].extend(args)
1827 def debug(self, *args):
1824 def debug(self, *args):
1828 if self.debugflag:
1825 if self.debugflag:
1829 self.write(*args)
1826 self.write(*args)
1830 def __getattr__(self, key):
1827 def __getattr__(self, key):
1831 return getattr(self.ui, key)
1828 return getattr(self.ui, key)
1832
1829
1833 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1830 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1834 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1831 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1835
1832
1836 if opts['branches']:
1833 if opts['branches']:
1837 ui.warn(_("the --branches option is deprecated, "
1834 ui.warn(_("the --branches option is deprecated, "
1838 "please use 'hg branches' instead\n"))
1835 "please use 'hg branches' instead\n"))
1839
1836
1840 if opts['limit']:
1837 if opts['limit']:
1841 try:
1838 try:
1842 limit = int(opts['limit'])
1839 limit = int(opts['limit'])
1843 except ValueError:
1840 except ValueError:
1844 raise util.Abort(_('limit must be a positive integer'))
1841 raise util.Abort(_('limit must be a positive integer'))
1845 if limit <= 0: raise util.Abort(_('limit must be positive'))
1842 if limit <= 0: raise util.Abort(_('limit must be positive'))
1846 else:
1843 else:
1847 limit = sys.maxint
1844 limit = sys.maxint
1848 count = 0
1845 count = 0
1849
1846
1850 if opts['copies'] and opts['rev']:
1847 if opts['copies'] and opts['rev']:
1851 endrev = max(cmdutil.revrange(ui, repo, opts['rev'])) + 1
1848 endrev = max(cmdutil.revrange(ui, repo, opts['rev'])) + 1
1852 else:
1849 else:
1853 endrev = repo.changelog.count()
1850 endrev = repo.changelog.count()
1854 rcache = {}
1851 rcache = {}
1855 ncache = {}
1852 ncache = {}
1856 dcache = []
1853 dcache = []
1857 def getrenamed(fn, rev, man):
1854 def getrenamed(fn, rev, man):
1858 '''looks up all renames for a file (up to endrev) the first
1855 '''looks up all renames for a file (up to endrev) the first
1859 time the file is given. It indexes on the changerev and only
1856 time the file is given. It indexes on the changerev and only
1860 parses the manifest if linkrev != changerev.
1857 parses the manifest if linkrev != changerev.
1861 Returns rename info for fn at changerev rev.'''
1858 Returns rename info for fn at changerev rev.'''
1862 if fn not in rcache:
1859 if fn not in rcache:
1863 rcache[fn] = {}
1860 rcache[fn] = {}
1864 ncache[fn] = {}
1861 ncache[fn] = {}
1865 fl = repo.file(fn)
1862 fl = repo.file(fn)
1866 for i in xrange(fl.count()):
1863 for i in xrange(fl.count()):
1867 node = fl.node(i)
1864 node = fl.node(i)
1868 lr = fl.linkrev(node)
1865 lr = fl.linkrev(node)
1869 renamed = fl.renamed(node)
1866 renamed = fl.renamed(node)
1870 rcache[fn][lr] = renamed
1867 rcache[fn][lr] = renamed
1871 if renamed:
1868 if renamed:
1872 ncache[fn][node] = renamed
1869 ncache[fn][node] = renamed
1873 if lr >= endrev:
1870 if lr >= endrev:
1874 break
1871 break
1875 if rev in rcache[fn]:
1872 if rev in rcache[fn]:
1876 return rcache[fn][rev]
1873 return rcache[fn][rev]
1877 mr = repo.manifest.rev(man)
1874 mr = repo.manifest.rev(man)
1878 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1875 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1879 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1876 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1880 if not dcache or dcache[0] != man:
1877 if not dcache or dcache[0] != man:
1881 dcache[:] = [man, repo.manifest.readdelta(man)]
1878 dcache[:] = [man, repo.manifest.readdelta(man)]
1882 if fn in dcache[1]:
1879 if fn in dcache[1]:
1883 return ncache[fn].get(dcache[1][fn])
1880 return ncache[fn].get(dcache[1][fn])
1884 return None
1881 return None
1885
1882
1886 displayer = show_changeset(ui, repo, opts)
1883 displayer = show_changeset(ui, repo, opts)
1887 for st, rev, fns in changeiter:
1884 for st, rev, fns in changeiter:
1888 if st == 'window':
1885 if st == 'window':
1889 du = dui(ui)
1886 du = dui(ui)
1890 displayer.ui = du
1887 displayer.ui = du
1891 elif st == 'add':
1888 elif st == 'add':
1892 du.bump(rev)
1889 du.bump(rev)
1893 changenode = repo.changelog.node(rev)
1890 changenode = repo.changelog.node(rev)
1894 parents = [p for p in repo.changelog.parentrevs(rev)
1891 parents = [p for p in repo.changelog.parentrevs(rev)
1895 if p != nullrev]
1892 if p != nullrev]
1896 if opts['no_merges'] and len(parents) == 2:
1893 if opts['no_merges'] and len(parents) == 2:
1897 continue
1894 continue
1898 if opts['only_merges'] and len(parents) != 2:
1895 if opts['only_merges'] and len(parents) != 2:
1899 continue
1896 continue
1900
1897
1901 if opts['keyword']:
1898 if opts['keyword']:
1902 changes = getchange(rev)
1899 changes = getchange(rev)
1903 miss = 0
1900 miss = 0
1904 for k in [kw.lower() for kw in opts['keyword']]:
1901 for k in [kw.lower() for kw in opts['keyword']]:
1905 if not (k in changes[1].lower() or
1902 if not (k in changes[1].lower() or
1906 k in changes[4].lower() or
1903 k in changes[4].lower() or
1907 k in " ".join(changes[3][:20]).lower()):
1904 k in " ".join(changes[3][:20]).lower()):
1908 miss = 1
1905 miss = 1
1909 break
1906 break
1910 if miss:
1907 if miss:
1911 continue
1908 continue
1912
1909
1913 br = None
1910 br = None
1914 if opts['branches']:
1911 if opts['branches']:
1915 br = repo.branchlookup([repo.changelog.node(rev)])
1912 br = repo.branchlookup([repo.changelog.node(rev)])
1916
1913
1917 copies = []
1914 copies = []
1918 if opts.get('copies') and rev:
1915 if opts.get('copies') and rev:
1919 mf = getchange(rev)[0]
1916 mf = getchange(rev)[0]
1920 for fn in getchange(rev)[3]:
1917 for fn in getchange(rev)[3]:
1921 rename = getrenamed(fn, rev, mf)
1918 rename = getrenamed(fn, rev, mf)
1922 if rename:
1919 if rename:
1923 copies.append((fn, rename[0]))
1920 copies.append((fn, rename[0]))
1924 displayer.show(rev, changenode, brinfo=br, copies=copies)
1921 displayer.show(rev, changenode, brinfo=br, copies=copies)
1925 if opts['patch']:
1922 if opts['patch']:
1926 if parents:
1923 if parents:
1927 prev = parents[0]
1924 prev = parents[0]
1928 else:
1925 else:
1929 prev = nullrev
1926 prev = nullrev
1930 prev = repo.changelog.node(prev)
1927 prev = repo.changelog.node(prev)
1931 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1928 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1932 du.write("\n\n")
1929 du.write("\n\n")
1933 elif st == 'iter':
1930 elif st == 'iter':
1934 if count == limit: break
1931 if count == limit: break
1935 if du.header[rev]:
1932 if du.header[rev]:
1936 ui.write_header(*du.header[rev])
1933 ui.write_header(*du.header[rev])
1937 if du.hunk[rev]:
1934 if du.hunk[rev]:
1938 count += 1
1935 count += 1
1939 ui.write(*du.hunk[rev])
1936 ui.write(*du.hunk[rev])
1940
1937
1941 def manifest(ui, repo, rev=None):
1938 def manifest(ui, repo, rev=None):
1942 """output the latest or given revision of the project manifest
1939 """output the latest or given revision of the project manifest
1943
1940
1944 Print a list of version controlled files for the given revision.
1941 Print a list of version controlled files for the given revision.
1945
1942
1946 The manifest is the list of files being version controlled. If no revision
1943 The manifest is the list of files being version controlled. If no revision
1947 is given then the tip is used.
1944 is given then the tip is used.
1948 """
1945 """
1949 if rev:
1946 if rev:
1950 try:
1947 try:
1951 # assume all revision numbers are for changesets
1948 # assume all revision numbers are for changesets
1952 n = repo.lookup(rev)
1949 n = repo.lookup(rev)
1953 change = repo.changelog.read(n)
1950 change = repo.changelog.read(n)
1954 n = change[0]
1951 n = change[0]
1955 except hg.RepoError:
1952 except hg.RepoError:
1956 n = repo.manifest.lookup(rev)
1953 n = repo.manifest.lookup(rev)
1957 else:
1954 else:
1958 n = repo.manifest.tip()
1955 n = repo.manifest.tip()
1959 m = repo.manifest.read(n)
1956 m = repo.manifest.read(n)
1960 files = m.keys()
1957 files = m.keys()
1961 files.sort()
1958 files.sort()
1962
1959
1963 for f in files:
1960 for f in files:
1964 ui.write("%40s %3s %s\n" % (hex(m[f]),
1961 ui.write("%40s %3s %s\n" % (hex(m[f]),
1965 m.execf(f) and "755" or "644", f))
1962 m.execf(f) and "755" or "644", f))
1966
1963
1967 def merge(ui, repo, node=None, force=None, branch=None):
1964 def merge(ui, repo, node=None, force=None, branch=None):
1968 """Merge working directory with another revision
1965 """Merge working directory with another revision
1969
1966
1970 Merge the contents of the current working directory and the
1967 Merge the contents of the current working directory and the
1971 requested revision. Files that changed between either parent are
1968 requested revision. Files that changed between either parent are
1972 marked as changed for the next commit and a commit must be
1969 marked as changed for the next commit and a commit must be
1973 performed before any further updates are allowed.
1970 performed before any further updates are allowed.
1974
1971
1975 If no revision is specified, the working directory's parent is a
1972 If no revision is specified, the working directory's parent is a
1976 head revision, and the repository contains exactly one other head,
1973 head revision, and the repository contains exactly one other head,
1977 the other head is merged with by default. Otherwise, an explicit
1974 the other head is merged with by default. Otherwise, an explicit
1978 revision to merge with must be provided.
1975 revision to merge with must be provided.
1979 """
1976 """
1980
1977
1981 if node or branch:
1978 if node or branch:
1982 node = _lookup(repo, node, branch)
1979 node = _lookup(repo, node, branch)
1983 else:
1980 else:
1984 heads = repo.heads()
1981 heads = repo.heads()
1985 if len(heads) > 2:
1982 if len(heads) > 2:
1986 raise util.Abort(_('repo has %d heads - '
1983 raise util.Abort(_('repo has %d heads - '
1987 'please merge with an explicit rev') %
1984 'please merge with an explicit rev') %
1988 len(heads))
1985 len(heads))
1989 if len(heads) == 1:
1986 if len(heads) == 1:
1990 raise util.Abort(_('there is nothing to merge - '
1987 raise util.Abort(_('there is nothing to merge - '
1991 'use "hg update" instead'))
1988 'use "hg update" instead'))
1992 parent = repo.dirstate.parents()[0]
1989 parent = repo.dirstate.parents()[0]
1993 if parent not in heads:
1990 if parent not in heads:
1994 raise util.Abort(_('working dir not at a head rev - '
1991 raise util.Abort(_('working dir not at a head rev - '
1995 'use "hg update" or merge with an explicit rev'))
1992 'use "hg update" or merge with an explicit rev'))
1996 node = parent == heads[0] and heads[-1] or heads[0]
1993 node = parent == heads[0] and heads[-1] or heads[0]
1997 return hg.merge(repo, node, force=force)
1994 return hg.merge(repo, node, force=force)
1998
1995
1999 def outgoing(ui, repo, dest=None, **opts):
1996 def outgoing(ui, repo, dest=None, **opts):
2000 """show changesets not found in destination
1997 """show changesets not found in destination
2001
1998
2002 Show changesets not found in the specified destination repository or
1999 Show changesets not found in the specified destination repository or
2003 the default push location. These are the changesets that would be pushed
2000 the default push location. These are the changesets that would be pushed
2004 if a push was requested.
2001 if a push was requested.
2005
2002
2006 See pull for valid destination format details.
2003 See pull for valid destination format details.
2007 """
2004 """
2008 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2005 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2009 setremoteconfig(ui, opts)
2006 setremoteconfig(ui, opts)
2010 revs = None
2007 revs = None
2011 if opts['rev']:
2008 if opts['rev']:
2012 revs = [repo.lookup(rev) for rev in opts['rev']]
2009 revs = [repo.lookup(rev) for rev in opts['rev']]
2013
2010
2014 other = hg.repository(ui, dest)
2011 other = hg.repository(ui, dest)
2015 o = repo.findoutgoing(other, force=opts['force'])
2012 o = repo.findoutgoing(other, force=opts['force'])
2016 if not o:
2013 if not o:
2017 ui.status(_("no changes found\n"))
2014 ui.status(_("no changes found\n"))
2018 return
2015 return
2019 o = repo.changelog.nodesbetween(o, revs)[0]
2016 o = repo.changelog.nodesbetween(o, revs)[0]
2020 if opts['newest_first']:
2017 if opts['newest_first']:
2021 o.reverse()
2018 o.reverse()
2022 displayer = show_changeset(ui, repo, opts)
2019 displayer = show_changeset(ui, repo, opts)
2023 for n in o:
2020 for n in o:
2024 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2021 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2025 if opts['no_merges'] and len(parents) == 2:
2022 if opts['no_merges'] and len(parents) == 2:
2026 continue
2023 continue
2027 displayer.show(changenode=n)
2024 displayer.show(changenode=n)
2028 if opts['patch']:
2025 if opts['patch']:
2029 prev = (parents and parents[0]) or nullid
2026 prev = (parents and parents[0]) or nullid
2030 patch.diff(repo, prev, n)
2027 patch.diff(repo, prev, n)
2031 ui.write("\n")
2028 ui.write("\n")
2032
2029
2033 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2030 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2034 """show the parents of the working dir or revision
2031 """show the parents of the working dir or revision
2035
2032
2036 Print the working directory's parent revisions.
2033 Print the working directory's parent revisions.
2037 """
2034 """
2038 # legacy
2035 # legacy
2039 if file_ and not rev:
2036 if file_ and not rev:
2040 try:
2037 try:
2041 rev = repo.lookup(file_)
2038 rev = repo.lookup(file_)
2042 file_ = None
2039 file_ = None
2043 except hg.RepoError:
2040 except hg.RepoError:
2044 pass
2041 pass
2045 else:
2042 else:
2046 ui.warn(_("'hg parent REV' is deprecated, "
2043 ui.warn(_("'hg parent REV' is deprecated, "
2047 "please use 'hg parents -r REV instead\n"))
2044 "please use 'hg parents -r REV instead\n"))
2048
2045
2049 if rev:
2046 if rev:
2050 if file_:
2047 if file_:
2051 ctx = repo.filectx(file_, changeid=rev)
2048 ctx = repo.filectx(file_, changeid=rev)
2052 else:
2049 else:
2053 ctx = repo.changectx(rev)
2050 ctx = repo.changectx(rev)
2054 p = [cp.node() for cp in ctx.parents()]
2051 p = [cp.node() for cp in ctx.parents()]
2055 else:
2052 else:
2056 p = repo.dirstate.parents()
2053 p = repo.dirstate.parents()
2057
2054
2058 br = None
2055 br = None
2059 if branches is not None:
2056 if branches is not None:
2060 ui.warn(_("the --branches option is deprecated, "
2057 ui.warn(_("the --branches option is deprecated, "
2061 "please use 'hg branches' instead\n"))
2058 "please use 'hg branches' instead\n"))
2062 br = repo.branchlookup(p)
2059 br = repo.branchlookup(p)
2063 displayer = show_changeset(ui, repo, opts)
2060 displayer = show_changeset(ui, repo, opts)
2064 for n in p:
2061 for n in p:
2065 if n != nullid:
2062 if n != nullid:
2066 displayer.show(changenode=n, brinfo=br)
2063 displayer.show(changenode=n, brinfo=br)
2067
2064
2068 def paths(ui, repo, search=None):
2065 def paths(ui, repo, search=None):
2069 """show definition of symbolic path names
2066 """show definition of symbolic path names
2070
2067
2071 Show definition of symbolic path name NAME. If no name is given, show
2068 Show definition of symbolic path name NAME. If no name is given, show
2072 definition of available names.
2069 definition of available names.
2073
2070
2074 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2071 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2075 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2072 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2076 """
2073 """
2077 if search:
2074 if search:
2078 for name, path in ui.configitems("paths"):
2075 for name, path in ui.configitems("paths"):
2079 if name == search:
2076 if name == search:
2080 ui.write("%s\n" % path)
2077 ui.write("%s\n" % path)
2081 return
2078 return
2082 ui.warn(_("not found!\n"))
2079 ui.warn(_("not found!\n"))
2083 return 1
2080 return 1
2084 else:
2081 else:
2085 for name, path in ui.configitems("paths"):
2082 for name, path in ui.configitems("paths"):
2086 ui.write("%s = %s\n" % (name, path))
2083 ui.write("%s = %s\n" % (name, path))
2087
2084
2088 def postincoming(ui, repo, modheads, optupdate):
2085 def postincoming(ui, repo, modheads, optupdate):
2089 if modheads == 0:
2086 if modheads == 0:
2090 return
2087 return
2091 if optupdate:
2088 if optupdate:
2092 if modheads == 1:
2089 if modheads == 1:
2093 return hg.update(repo, repo.changelog.tip()) # update
2090 return hg.update(repo, repo.changelog.tip()) # update
2094 else:
2091 else:
2095 ui.status(_("not updating, since new heads added\n"))
2092 ui.status(_("not updating, since new heads added\n"))
2096 if modheads > 1:
2093 if modheads > 1:
2097 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2094 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2098 else:
2095 else:
2099 ui.status(_("(run 'hg update' to get a working copy)\n"))
2096 ui.status(_("(run 'hg update' to get a working copy)\n"))
2100
2097
2101 def pull(ui, repo, source="default", **opts):
2098 def pull(ui, repo, source="default", **opts):
2102 """pull changes from the specified source
2099 """pull changes from the specified source
2103
2100
2104 Pull changes from a remote repository to a local one.
2101 Pull changes from a remote repository to a local one.
2105
2102
2106 This finds all changes from the repository at the specified path
2103 This finds all changes from the repository at the specified path
2107 or URL and adds them to the local repository. By default, this
2104 or URL and adds them to the local repository. By default, this
2108 does not update the copy of the project in the working directory.
2105 does not update the copy of the project in the working directory.
2109
2106
2110 Valid URLs are of the form:
2107 Valid URLs are of the form:
2111
2108
2112 local/filesystem/path (or file://local/filesystem/path)
2109 local/filesystem/path (or file://local/filesystem/path)
2113 http://[user@]host[:port]/[path]
2110 http://[user@]host[:port]/[path]
2114 https://[user@]host[:port]/[path]
2111 https://[user@]host[:port]/[path]
2115 ssh://[user@]host[:port]/[path]
2112 ssh://[user@]host[:port]/[path]
2116 static-http://host[:port]/[path]
2113 static-http://host[:port]/[path]
2117
2114
2118 Paths in the local filesystem can either point to Mercurial
2115 Paths in the local filesystem can either point to Mercurial
2119 repositories or to bundle files (as created by 'hg bundle' or
2116 repositories or to bundle files (as created by 'hg bundle' or
2120 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2117 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2121 allows access to a Mercurial repository where you simply use a web
2118 allows access to a Mercurial repository where you simply use a web
2122 server to publish the .hg directory as static content.
2119 server to publish the .hg directory as static content.
2123
2120
2124 Some notes about using SSH with Mercurial:
2121 Some notes about using SSH with Mercurial:
2125 - SSH requires an accessible shell account on the destination machine
2122 - SSH requires an accessible shell account on the destination machine
2126 and a copy of hg in the remote path or specified with as remotecmd.
2123 and a copy of hg in the remote path or specified with as remotecmd.
2127 - path is relative to the remote user's home directory by default.
2124 - path is relative to the remote user's home directory by default.
2128 Use an extra slash at the start of a path to specify an absolute path:
2125 Use an extra slash at the start of a path to specify an absolute path:
2129 ssh://example.com//tmp/repository
2126 ssh://example.com//tmp/repository
2130 - Mercurial doesn't use its own compression via SSH; the right thing
2127 - Mercurial doesn't use its own compression via SSH; the right thing
2131 to do is to configure it in your ~/.ssh/config, e.g.:
2128 to do is to configure it in your ~/.ssh/config, e.g.:
2132 Host *.mylocalnetwork.example.com
2129 Host *.mylocalnetwork.example.com
2133 Compression no
2130 Compression no
2134 Host *
2131 Host *
2135 Compression yes
2132 Compression yes
2136 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2133 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2137 with the --ssh command line option.
2134 with the --ssh command line option.
2138 """
2135 """
2139 source = ui.expandpath(source)
2136 source = ui.expandpath(source)
2140 setremoteconfig(ui, opts)
2137 setremoteconfig(ui, opts)
2141
2138
2142 other = hg.repository(ui, source)
2139 other = hg.repository(ui, source)
2143 ui.status(_('pulling from %s\n') % (source))
2140 ui.status(_('pulling from %s\n') % (source))
2144 revs = None
2141 revs = None
2145 if opts['rev']:
2142 if opts['rev']:
2146 if 'lookup' in other.capabilities:
2143 if 'lookup' in other.capabilities:
2147 revs = [other.lookup(rev) for rev in opts['rev']]
2144 revs = [other.lookup(rev) for rev in opts['rev']]
2148 else:
2145 else:
2149 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2146 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2150 raise util.Abort(error)
2147 raise util.Abort(error)
2151 modheads = repo.pull(other, heads=revs, force=opts['force'])
2148 modheads = repo.pull(other, heads=revs, force=opts['force'])
2152 return postincoming(ui, repo, modheads, opts['update'])
2149 return postincoming(ui, repo, modheads, opts['update'])
2153
2150
2154 def push(ui, repo, dest=None, **opts):
2151 def push(ui, repo, dest=None, **opts):
2155 """push changes to the specified destination
2152 """push changes to the specified destination
2156
2153
2157 Push changes from the local repository to the given destination.
2154 Push changes from the local repository to the given destination.
2158
2155
2159 This is the symmetrical operation for pull. It helps to move
2156 This is the symmetrical operation for pull. It helps to move
2160 changes from the current repository to a different one. If the
2157 changes from the current repository to a different one. If the
2161 destination is local this is identical to a pull in that directory
2158 destination is local this is identical to a pull in that directory
2162 from the current one.
2159 from the current one.
2163
2160
2164 By default, push will refuse to run if it detects the result would
2161 By default, push will refuse to run if it detects the result would
2165 increase the number of remote heads. This generally indicates the
2162 increase the number of remote heads. This generally indicates the
2166 the client has forgotten to sync and merge before pushing.
2163 the client has forgotten to sync and merge before pushing.
2167
2164
2168 Valid URLs are of the form:
2165 Valid URLs are of the form:
2169
2166
2170 local/filesystem/path (or file://local/filesystem/path)
2167 local/filesystem/path (or file://local/filesystem/path)
2171 ssh://[user@]host[:port]/[path]
2168 ssh://[user@]host[:port]/[path]
2172 http://[user@]host[:port]/[path]
2169 http://[user@]host[:port]/[path]
2173 https://[user@]host[:port]/[path]
2170 https://[user@]host[:port]/[path]
2174
2171
2175 Look at the help text for the pull command for important details
2172 Look at the help text for the pull command for important details
2176 about ssh:// URLs.
2173 about ssh:// URLs.
2177
2174
2178 Pushing to http:// and https:// URLs is only possible, if this
2175 Pushing to http:// and https:// URLs is only possible, if this
2179 feature is explicitly enabled on the remote Mercurial server.
2176 feature is explicitly enabled on the remote Mercurial server.
2180 """
2177 """
2181 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2178 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2182 setremoteconfig(ui, opts)
2179 setremoteconfig(ui, opts)
2183
2180
2184 other = hg.repository(ui, dest)
2181 other = hg.repository(ui, dest)
2185 ui.status('pushing to %s\n' % (dest))
2182 ui.status('pushing to %s\n' % (dest))
2186 revs = None
2183 revs = None
2187 if opts['rev']:
2184 if opts['rev']:
2188 revs = [repo.lookup(rev) for rev in opts['rev']]
2185 revs = [repo.lookup(rev) for rev in opts['rev']]
2189 r = repo.push(other, opts['force'], revs=revs)
2186 r = repo.push(other, opts['force'], revs=revs)
2190 return r == 0
2187 return r == 0
2191
2188
2192 def rawcommit(ui, repo, *flist, **rc):
2189 def rawcommit(ui, repo, *flist, **rc):
2193 """raw commit interface (DEPRECATED)
2190 """raw commit interface (DEPRECATED)
2194
2191
2195 (DEPRECATED)
2192 (DEPRECATED)
2196 Lowlevel commit, for use in helper scripts.
2193 Lowlevel commit, for use in helper scripts.
2197
2194
2198 This command is not intended to be used by normal users, as it is
2195 This command is not intended to be used by normal users, as it is
2199 primarily useful for importing from other SCMs.
2196 primarily useful for importing from other SCMs.
2200
2197
2201 This command is now deprecated and will be removed in a future
2198 This command is now deprecated and will be removed in a future
2202 release, please use debugsetparents and commit instead.
2199 release, please use debugsetparents and commit instead.
2203 """
2200 """
2204
2201
2205 ui.warn(_("(the rawcommit command is deprecated)\n"))
2202 ui.warn(_("(the rawcommit command is deprecated)\n"))
2206
2203
2207 message = rc['message']
2204 message = rc['message']
2208 if not message and rc['logfile']:
2205 if not message and rc['logfile']:
2209 try:
2206 try:
2210 message = open(rc['logfile']).read()
2207 message = open(rc['logfile']).read()
2211 except IOError:
2208 except IOError:
2212 pass
2209 pass
2213 if not message and not rc['logfile']:
2210 if not message and not rc['logfile']:
2214 raise util.Abort(_("missing commit message"))
2211 raise util.Abort(_("missing commit message"))
2215
2212
2216 files = relpath(repo, list(flist))
2213 files = relpath(repo, list(flist))
2217 if rc['files']:
2214 if rc['files']:
2218 files += open(rc['files']).read().splitlines()
2215 files += open(rc['files']).read().splitlines()
2219
2216
2220 rc['parent'] = map(repo.lookup, rc['parent'])
2217 rc['parent'] = map(repo.lookup, rc['parent'])
2221
2218
2222 try:
2219 try:
2223 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2220 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2224 except ValueError, inst:
2221 except ValueError, inst:
2225 raise util.Abort(str(inst))
2222 raise util.Abort(str(inst))
2226
2223
2227 def recover(ui, repo):
2224 def recover(ui, repo):
2228 """roll back an interrupted transaction
2225 """roll back an interrupted transaction
2229
2226
2230 Recover from an interrupted commit or pull.
2227 Recover from an interrupted commit or pull.
2231
2228
2232 This command tries to fix the repository status after an interrupted
2229 This command tries to fix the repository status after an interrupted
2233 operation. It should only be necessary when Mercurial suggests it.
2230 operation. It should only be necessary when Mercurial suggests it.
2234 """
2231 """
2235 if repo.recover():
2232 if repo.recover():
2236 return hg.verify(repo)
2233 return hg.verify(repo)
2237 return 1
2234 return 1
2238
2235
2239 def remove(ui, repo, *pats, **opts):
2236 def remove(ui, repo, *pats, **opts):
2240 """remove the specified files on the next commit
2237 """remove the specified files on the next commit
2241
2238
2242 Schedule the indicated files for removal from the repository.
2239 Schedule the indicated files for removal from the repository.
2243
2240
2244 This command schedules the files to be removed at the next commit.
2241 This command schedules the files to be removed at the next commit.
2245 This only removes files from the current branch, not from the
2242 This only removes files from the current branch, not from the
2246 entire project history. If the files still exist in the working
2243 entire project history. If the files still exist in the working
2247 directory, they will be deleted from it. If invoked with --after,
2244 directory, they will be deleted from it. If invoked with --after,
2248 files that have been manually deleted are marked as removed.
2245 files that have been manually deleted are marked as removed.
2249
2246
2250 Modified files and added files are not removed by default. To
2247 Modified files and added files are not removed by default. To
2251 remove them, use the -f/--force option.
2248 remove them, use the -f/--force option.
2252 """
2249 """
2253 names = []
2250 names = []
2254 if not opts['after'] and not pats:
2251 if not opts['after'] and not pats:
2255 raise util.Abort(_('no files specified'))
2252 raise util.Abort(_('no files specified'))
2256 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2253 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2257 exact = dict.fromkeys(files)
2254 exact = dict.fromkeys(files)
2258 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2255 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2259 modified, added, removed, deleted, unknown = mardu
2256 modified, added, removed, deleted, unknown = mardu
2260 remove, forget = [], []
2257 remove, forget = [], []
2261 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2258 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2262 reason = None
2259 reason = None
2263 if abs not in deleted and opts['after']:
2260 if abs not in deleted and opts['after']:
2264 reason = _('is still present')
2261 reason = _('is still present')
2265 elif abs in modified and not opts['force']:
2262 elif abs in modified and not opts['force']:
2266 reason = _('is modified (use -f to force removal)')
2263 reason = _('is modified (use -f to force removal)')
2267 elif abs in added:
2264 elif abs in added:
2268 if opts['force']:
2265 if opts['force']:
2269 forget.append(abs)
2266 forget.append(abs)
2270 continue
2267 continue
2271 reason = _('has been marked for add (use -f to force removal)')
2268 reason = _('has been marked for add (use -f to force removal)')
2272 elif abs in unknown:
2269 elif abs in unknown:
2273 reason = _('is not managed')
2270 reason = _('is not managed')
2274 elif abs in removed:
2271 elif abs in removed:
2275 continue
2272 continue
2276 if reason:
2273 if reason:
2277 if exact:
2274 if exact:
2278 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2275 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2279 else:
2276 else:
2280 if ui.verbose or not exact:
2277 if ui.verbose or not exact:
2281 ui.status(_('removing %s\n') % rel)
2278 ui.status(_('removing %s\n') % rel)
2282 remove.append(abs)
2279 remove.append(abs)
2283 repo.forget(forget)
2280 repo.forget(forget)
2284 repo.remove(remove, unlink=not opts['after'])
2281 repo.remove(remove, unlink=not opts['after'])
2285
2282
2286 def rename(ui, repo, *pats, **opts):
2283 def rename(ui, repo, *pats, **opts):
2287 """rename files; equivalent of copy + remove
2284 """rename files; equivalent of copy + remove
2288
2285
2289 Mark dest as copies of sources; mark sources for deletion. If
2286 Mark dest as copies of sources; mark sources for deletion. If
2290 dest is a directory, copies are put in that directory. If dest is
2287 dest is a directory, copies are put in that directory. If dest is
2291 a file, there can only be one source.
2288 a file, there can only be one source.
2292
2289
2293 By default, this command copies the contents of files as they
2290 By default, this command copies the contents of files as they
2294 stand in the working directory. If invoked with --after, the
2291 stand in the working directory. If invoked with --after, the
2295 operation is recorded, but no copying is performed.
2292 operation is recorded, but no copying is performed.
2296
2293
2297 This command takes effect in the next commit.
2294 This command takes effect in the next commit.
2298
2295
2299 NOTE: This command should be treated as experimental. While it
2296 NOTE: This command should be treated as experimental. While it
2300 should properly record rename files, this information is not yet
2297 should properly record rename files, this information is not yet
2301 fully used by merge, nor fully reported by log.
2298 fully used by merge, nor fully reported by log.
2302 """
2299 """
2303 wlock = repo.wlock(0)
2300 wlock = repo.wlock(0)
2304 errs, copied = docopy(ui, repo, pats, opts, wlock)
2301 errs, copied = docopy(ui, repo, pats, opts, wlock)
2305 names = []
2302 names = []
2306 for abs, rel, exact in copied:
2303 for abs, rel, exact in copied:
2307 if ui.verbose or not exact:
2304 if ui.verbose or not exact:
2308 ui.status(_('removing %s\n') % rel)
2305 ui.status(_('removing %s\n') % rel)
2309 names.append(abs)
2306 names.append(abs)
2310 if not opts.get('dry_run'):
2307 if not opts.get('dry_run'):
2311 repo.remove(names, True, wlock)
2308 repo.remove(names, True, wlock)
2312 return errs
2309 return errs
2313
2310
2314 def revert(ui, repo, *pats, **opts):
2311 def revert(ui, repo, *pats, **opts):
2315 """revert files or dirs to their states as of some revision
2312 """revert files or dirs to their states as of some revision
2316
2313
2317 With no revision specified, revert the named files or directories
2314 With no revision specified, revert the named files or directories
2318 to the contents they had in the parent of the working directory.
2315 to the contents they had in the parent of the working directory.
2319 This restores the contents of the affected files to an unmodified
2316 This restores the contents of the affected files to an unmodified
2320 state. If the working directory has two parents, you must
2317 state. If the working directory has two parents, you must
2321 explicitly specify the revision to revert to.
2318 explicitly specify the revision to revert to.
2322
2319
2323 Modified files are saved with a .orig suffix before reverting.
2320 Modified files are saved with a .orig suffix before reverting.
2324 To disable these backups, use --no-backup.
2321 To disable these backups, use --no-backup.
2325
2322
2326 Using the -r option, revert the given files or directories to their
2323 Using the -r option, revert the given files or directories to their
2327 contents as of a specific revision. This can be helpful to "roll
2324 contents as of a specific revision. This can be helpful to "roll
2328 back" some or all of a change that should not have been committed.
2325 back" some or all of a change that should not have been committed.
2329
2326
2330 Revert modifies the working directory. It does not commit any
2327 Revert modifies the working directory. It does not commit any
2331 changes, or change the parent of the working directory. If you
2328 changes, or change the parent of the working directory. If you
2332 revert to a revision other than the parent of the working
2329 revert to a revision other than the parent of the working
2333 directory, the reverted files will thus appear modified
2330 directory, the reverted files will thus appear modified
2334 afterwards.
2331 afterwards.
2335
2332
2336 If a file has been deleted, it is recreated. If the executable
2333 If a file has been deleted, it is recreated. If the executable
2337 mode of a file was changed, it is reset.
2334 mode of a file was changed, it is reset.
2338
2335
2339 If names are given, all files matching the names are reverted.
2336 If names are given, all files matching the names are reverted.
2340
2337
2341 If no arguments are given, no files are reverted.
2338 If no arguments are given, no files are reverted.
2342 """
2339 """
2343
2340
2344 if not pats and not opts['all']:
2341 if not pats and not opts['all']:
2345 raise util.Abort(_('no files or directories specified; '
2342 raise util.Abort(_('no files or directories specified; '
2346 'use --all to revert the whole repo'))
2343 'use --all to revert the whole repo'))
2347
2344
2348 parent, p2 = repo.dirstate.parents()
2345 parent, p2 = repo.dirstate.parents()
2349 if not opts['rev'] and p2 != nullid:
2346 if not opts['rev'] and p2 != nullid:
2350 raise util.Abort(_('uncommitted merge - please provide a '
2347 raise util.Abort(_('uncommitted merge - please provide a '
2351 'specific revision'))
2348 'specific revision'))
2352 node = repo.changectx(opts['rev']).node()
2349 node = repo.changectx(opts['rev']).node()
2353 mf = repo.manifest.read(repo.changelog.read(node)[0])
2350 mf = repo.manifest.read(repo.changelog.read(node)[0])
2354 if node == parent:
2351 if node == parent:
2355 pmf = mf
2352 pmf = mf
2356 else:
2353 else:
2357 pmf = None
2354 pmf = None
2358
2355
2359 wlock = repo.wlock()
2356 wlock = repo.wlock()
2360
2357
2361 # need all matching names in dirstate and manifest of target rev,
2358 # need all matching names in dirstate and manifest of target rev,
2362 # so have to walk both. do not print errors if files exist in one
2359 # so have to walk both. do not print errors if files exist in one
2363 # but not other.
2360 # but not other.
2364
2361
2365 names = {}
2362 names = {}
2366 target_only = {}
2363 target_only = {}
2367
2364
2368 # walk dirstate.
2365 # walk dirstate.
2369
2366
2370 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2367 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2371 badmatch=mf.has_key):
2368 badmatch=mf.has_key):
2372 names[abs] = (rel, exact)
2369 names[abs] = (rel, exact)
2373 if src == 'b':
2370 if src == 'b':
2374 target_only[abs] = True
2371 target_only[abs] = True
2375
2372
2376 # walk target manifest.
2373 # walk target manifest.
2377
2374
2378 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2375 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2379 badmatch=names.has_key):
2376 badmatch=names.has_key):
2380 if abs in names: continue
2377 if abs in names: continue
2381 names[abs] = (rel, exact)
2378 names[abs] = (rel, exact)
2382 target_only[abs] = True
2379 target_only[abs] = True
2383
2380
2384 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2381 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2385 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2382 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2386
2383
2387 revert = ([], _('reverting %s\n'))
2384 revert = ([], _('reverting %s\n'))
2388 add = ([], _('adding %s\n'))
2385 add = ([], _('adding %s\n'))
2389 remove = ([], _('removing %s\n'))
2386 remove = ([], _('removing %s\n'))
2390 forget = ([], _('forgetting %s\n'))
2387 forget = ([], _('forgetting %s\n'))
2391 undelete = ([], _('undeleting %s\n'))
2388 undelete = ([], _('undeleting %s\n'))
2392 update = {}
2389 update = {}
2393
2390
2394 disptable = (
2391 disptable = (
2395 # dispatch table:
2392 # dispatch table:
2396 # file state
2393 # file state
2397 # action if in target manifest
2394 # action if in target manifest
2398 # action if not in target manifest
2395 # action if not in target manifest
2399 # make backup if in target manifest
2396 # make backup if in target manifest
2400 # make backup if not in target manifest
2397 # make backup if not in target manifest
2401 (modified, revert, remove, True, True),
2398 (modified, revert, remove, True, True),
2402 (added, revert, forget, True, False),
2399 (added, revert, forget, True, False),
2403 (removed, undelete, None, False, False),
2400 (removed, undelete, None, False, False),
2404 (deleted, revert, remove, False, False),
2401 (deleted, revert, remove, False, False),
2405 (unknown, add, None, True, False),
2402 (unknown, add, None, True, False),
2406 (target_only, add, None, False, False),
2403 (target_only, add, None, False, False),
2407 )
2404 )
2408
2405
2409 entries = names.items()
2406 entries = names.items()
2410 entries.sort()
2407 entries.sort()
2411
2408
2412 for abs, (rel, exact) in entries:
2409 for abs, (rel, exact) in entries:
2413 mfentry = mf.get(abs)
2410 mfentry = mf.get(abs)
2414 def handle(xlist, dobackup):
2411 def handle(xlist, dobackup):
2415 xlist[0].append(abs)
2412 xlist[0].append(abs)
2416 update[abs] = 1
2413 update[abs] = 1
2417 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2414 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2418 bakname = "%s.orig" % rel
2415 bakname = "%s.orig" % rel
2419 ui.note(_('saving current version of %s as %s\n') %
2416 ui.note(_('saving current version of %s as %s\n') %
2420 (rel, bakname))
2417 (rel, bakname))
2421 if not opts.get('dry_run'):
2418 if not opts.get('dry_run'):
2422 shutil.copyfile(rel, bakname)
2419 util.copyfile(rel, bakname)
2423 shutil.copymode(rel, bakname)
2424 if ui.verbose or not exact:
2420 if ui.verbose or not exact:
2425 ui.status(xlist[1] % rel)
2421 ui.status(xlist[1] % rel)
2426 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2422 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2427 if abs not in table: continue
2423 if abs not in table: continue
2428 # file has changed in dirstate
2424 # file has changed in dirstate
2429 if mfentry:
2425 if mfentry:
2430 handle(hitlist, backuphit)
2426 handle(hitlist, backuphit)
2431 elif misslist is not None:
2427 elif misslist is not None:
2432 handle(misslist, backupmiss)
2428 handle(misslist, backupmiss)
2433 else:
2429 else:
2434 if exact: ui.warn(_('file not managed: %s\n' % rel))
2430 if exact: ui.warn(_('file not managed: %s\n' % rel))
2435 break
2431 break
2436 else:
2432 else:
2437 # file has not changed in dirstate
2433 # file has not changed in dirstate
2438 if node == parent:
2434 if node == parent:
2439 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2435 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2440 continue
2436 continue
2441 if pmf is None:
2437 if pmf is None:
2442 # only need parent manifest in this unlikely case,
2438 # only need parent manifest in this unlikely case,
2443 # so do not read by default
2439 # so do not read by default
2444 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2440 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2445 if abs in pmf:
2441 if abs in pmf:
2446 if mfentry:
2442 if mfentry:
2447 # if version of file is same in parent and target
2443 # if version of file is same in parent and target
2448 # manifests, do nothing
2444 # manifests, do nothing
2449 if pmf[abs] != mfentry:
2445 if pmf[abs] != mfentry:
2450 handle(revert, False)
2446 handle(revert, False)
2451 else:
2447 else:
2452 handle(remove, False)
2448 handle(remove, False)
2453
2449
2454 if not opts.get('dry_run'):
2450 if not opts.get('dry_run'):
2455 repo.dirstate.forget(forget[0])
2451 repo.dirstate.forget(forget[0])
2456 r = hg.revert(repo, node, update.has_key, wlock)
2452 r = hg.revert(repo, node, update.has_key, wlock)
2457 repo.dirstate.update(add[0], 'a')
2453 repo.dirstate.update(add[0], 'a')
2458 repo.dirstate.update(undelete[0], 'n')
2454 repo.dirstate.update(undelete[0], 'n')
2459 repo.dirstate.update(remove[0], 'r')
2455 repo.dirstate.update(remove[0], 'r')
2460 return r
2456 return r
2461
2457
2462 def rollback(ui, repo):
2458 def rollback(ui, repo):
2463 """roll back the last transaction in this repository
2459 """roll back the last transaction in this repository
2464
2460
2465 Roll back the last transaction in this repository, restoring the
2461 Roll back the last transaction in this repository, restoring the
2466 project to its state prior to the transaction.
2462 project to its state prior to the transaction.
2467
2463
2468 Transactions are used to encapsulate the effects of all commands
2464 Transactions are used to encapsulate the effects of all commands
2469 that create new changesets or propagate existing changesets into a
2465 that create new changesets or propagate existing changesets into a
2470 repository. For example, the following commands are transactional,
2466 repository. For example, the following commands are transactional,
2471 and their effects can be rolled back:
2467 and their effects can be rolled back:
2472
2468
2473 commit
2469 commit
2474 import
2470 import
2475 pull
2471 pull
2476 push (with this repository as destination)
2472 push (with this repository as destination)
2477 unbundle
2473 unbundle
2478
2474
2479 This command should be used with care. There is only one level of
2475 This command should be used with care. There is only one level of
2480 rollback, and there is no way to undo a rollback.
2476 rollback, and there is no way to undo a rollback.
2481
2477
2482 This command is not intended for use on public repositories. Once
2478 This command is not intended for use on public repositories. Once
2483 changes are visible for pull by other users, rolling a transaction
2479 changes are visible for pull by other users, rolling a transaction
2484 back locally is ineffective (someone else may already have pulled
2480 back locally is ineffective (someone else may already have pulled
2485 the changes). Furthermore, a race is possible with readers of the
2481 the changes). Furthermore, a race is possible with readers of the
2486 repository; for example an in-progress pull from the repository
2482 repository; for example an in-progress pull from the repository
2487 may fail if a rollback is performed.
2483 may fail if a rollback is performed.
2488 """
2484 """
2489 repo.rollback()
2485 repo.rollback()
2490
2486
2491 def root(ui, repo):
2487 def root(ui, repo):
2492 """print the root (top) of the current working dir
2488 """print the root (top) of the current working dir
2493
2489
2494 Print the root directory of the current repository.
2490 Print the root directory of the current repository.
2495 """
2491 """
2496 ui.write(repo.root + "\n")
2492 ui.write(repo.root + "\n")
2497
2493
2498 def serve(ui, repo, **opts):
2494 def serve(ui, repo, **opts):
2499 """export the repository via HTTP
2495 """export the repository via HTTP
2500
2496
2501 Start a local HTTP repository browser and pull server.
2497 Start a local HTTP repository browser and pull server.
2502
2498
2503 By default, the server logs accesses to stdout and errors to
2499 By default, the server logs accesses to stdout and errors to
2504 stderr. Use the "-A" and "-E" options to log to files.
2500 stderr. Use the "-A" and "-E" options to log to files.
2505 """
2501 """
2506
2502
2507 if opts["stdio"]:
2503 if opts["stdio"]:
2508 if repo is None:
2504 if repo is None:
2509 raise hg.RepoError(_("There is no Mercurial repository here"
2505 raise hg.RepoError(_("There is no Mercurial repository here"
2510 " (.hg not found)"))
2506 " (.hg not found)"))
2511 s = sshserver.sshserver(ui, repo)
2507 s = sshserver.sshserver(ui, repo)
2512 s.serve_forever()
2508 s.serve_forever()
2513
2509
2514 optlist = ("name templates style address port ipv6"
2510 optlist = ("name templates style address port ipv6"
2515 " accesslog errorlog webdir_conf")
2511 " accesslog errorlog webdir_conf")
2516 for o in optlist.split():
2512 for o in optlist.split():
2517 if opts[o]:
2513 if opts[o]:
2518 ui.setconfig("web", o, str(opts[o]))
2514 ui.setconfig("web", o, str(opts[o]))
2519
2515
2520 if repo is None and not ui.config("web", "webdir_conf"):
2516 if repo is None and not ui.config("web", "webdir_conf"):
2521 raise hg.RepoError(_("There is no Mercurial repository here"
2517 raise hg.RepoError(_("There is no Mercurial repository here"
2522 " (.hg not found)"))
2518 " (.hg not found)"))
2523
2519
2524 if opts['daemon'] and not opts['daemon_pipefds']:
2520 if opts['daemon'] and not opts['daemon_pipefds']:
2525 rfd, wfd = os.pipe()
2521 rfd, wfd = os.pipe()
2526 args = sys.argv[:]
2522 args = sys.argv[:]
2527 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2523 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2528 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2524 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2529 args[0], args)
2525 args[0], args)
2530 os.close(wfd)
2526 os.close(wfd)
2531 os.read(rfd, 1)
2527 os.read(rfd, 1)
2532 os._exit(0)
2528 os._exit(0)
2533
2529
2534 httpd = hgweb.server.create_server(ui, repo)
2530 httpd = hgweb.server.create_server(ui, repo)
2535
2531
2536 if ui.verbose:
2532 if ui.verbose:
2537 if httpd.port != 80:
2533 if httpd.port != 80:
2538 ui.status(_('listening at http://%s:%d/\n') %
2534 ui.status(_('listening at http://%s:%d/\n') %
2539 (httpd.addr, httpd.port))
2535 (httpd.addr, httpd.port))
2540 else:
2536 else:
2541 ui.status(_('listening at http://%s/\n') % httpd.addr)
2537 ui.status(_('listening at http://%s/\n') % httpd.addr)
2542
2538
2543 if opts['pid_file']:
2539 if opts['pid_file']:
2544 fp = open(opts['pid_file'], 'w')
2540 fp = open(opts['pid_file'], 'w')
2545 fp.write(str(os.getpid()) + '\n')
2541 fp.write(str(os.getpid()) + '\n')
2546 fp.close()
2542 fp.close()
2547
2543
2548 if opts['daemon_pipefds']:
2544 if opts['daemon_pipefds']:
2549 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2545 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2550 os.close(rfd)
2546 os.close(rfd)
2551 os.write(wfd, 'y')
2547 os.write(wfd, 'y')
2552 os.close(wfd)
2548 os.close(wfd)
2553 sys.stdout.flush()
2549 sys.stdout.flush()
2554 sys.stderr.flush()
2550 sys.stderr.flush()
2555 fd = os.open(util.nulldev, os.O_RDWR)
2551 fd = os.open(util.nulldev, os.O_RDWR)
2556 if fd != 0: os.dup2(fd, 0)
2552 if fd != 0: os.dup2(fd, 0)
2557 if fd != 1: os.dup2(fd, 1)
2553 if fd != 1: os.dup2(fd, 1)
2558 if fd != 2: os.dup2(fd, 2)
2554 if fd != 2: os.dup2(fd, 2)
2559 if fd not in (0, 1, 2): os.close(fd)
2555 if fd not in (0, 1, 2): os.close(fd)
2560
2556
2561 httpd.serve_forever()
2557 httpd.serve_forever()
2562
2558
2563 def status(ui, repo, *pats, **opts):
2559 def status(ui, repo, *pats, **opts):
2564 """show changed files in the working directory
2560 """show changed files in the working directory
2565
2561
2566 Show status of files in the repository. If names are given, only
2562 Show status of files in the repository. If names are given, only
2567 files that match are shown. Files that are clean or ignored, are
2563 files that match are shown. Files that are clean or ignored, are
2568 not listed unless -c (clean), -i (ignored) or -A is given.
2564 not listed unless -c (clean), -i (ignored) or -A is given.
2569
2565
2570 If one revision is given, it is used as the base revision.
2566 If one revision is given, it is used as the base revision.
2571 If two revisions are given, the difference between them is shown.
2567 If two revisions are given, the difference between them is shown.
2572
2568
2573 The codes used to show the status of files are:
2569 The codes used to show the status of files are:
2574 M = modified
2570 M = modified
2575 A = added
2571 A = added
2576 R = removed
2572 R = removed
2577 C = clean
2573 C = clean
2578 ! = deleted, but still tracked
2574 ! = deleted, but still tracked
2579 ? = not tracked
2575 ? = not tracked
2580 I = ignored (not shown by default)
2576 I = ignored (not shown by default)
2581 = the previous added file was copied from here
2577 = the previous added file was copied from here
2582 """
2578 """
2583
2579
2584 all = opts['all']
2580 all = opts['all']
2585 node1, node2 = cmdutil.revpair(ui, repo, opts.get('rev'))
2581 node1, node2 = cmdutil.revpair(ui, repo, opts.get('rev'))
2586
2582
2587 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2583 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2588 cwd = (pats and repo.getcwd()) or ''
2584 cwd = (pats and repo.getcwd()) or ''
2589 modified, added, removed, deleted, unknown, ignored, clean = [
2585 modified, added, removed, deleted, unknown, ignored, clean = [
2590 [util.pathto(cwd, x) for x in n]
2586 [util.pathto(cwd, x) for x in n]
2591 for n in repo.status(node1=node1, node2=node2, files=files,
2587 for n in repo.status(node1=node1, node2=node2, files=files,
2592 match=matchfn,
2588 match=matchfn,
2593 list_ignored=all or opts['ignored'],
2589 list_ignored=all or opts['ignored'],
2594 list_clean=all or opts['clean'])]
2590 list_clean=all or opts['clean'])]
2595
2591
2596 changetypes = (('modified', 'M', modified),
2592 changetypes = (('modified', 'M', modified),
2597 ('added', 'A', added),
2593 ('added', 'A', added),
2598 ('removed', 'R', removed),
2594 ('removed', 'R', removed),
2599 ('deleted', '!', deleted),
2595 ('deleted', '!', deleted),
2600 ('unknown', '?', unknown),
2596 ('unknown', '?', unknown),
2601 ('ignored', 'I', ignored))
2597 ('ignored', 'I', ignored))
2602
2598
2603 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2599 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2604
2600
2605 end = opts['print0'] and '\0' or '\n'
2601 end = opts['print0'] and '\0' or '\n'
2606
2602
2607 for opt, char, changes in ([ct for ct in explicit_changetypes
2603 for opt, char, changes in ([ct for ct in explicit_changetypes
2608 if all or opts[ct[0]]]
2604 if all or opts[ct[0]]]
2609 or changetypes):
2605 or changetypes):
2610 if opts['no_status']:
2606 if opts['no_status']:
2611 format = "%%s%s" % end
2607 format = "%%s%s" % end
2612 else:
2608 else:
2613 format = "%s %%s%s" % (char, end)
2609 format = "%s %%s%s" % (char, end)
2614
2610
2615 for f in changes:
2611 for f in changes:
2616 ui.write(format % f)
2612 ui.write(format % f)
2617 if ((all or opts.get('copies')) and not opts.get('no_status')):
2613 if ((all or opts.get('copies')) and not opts.get('no_status')):
2618 copied = repo.dirstate.copied(f)
2614 copied = repo.dirstate.copied(f)
2619 if copied:
2615 if copied:
2620 ui.write(' %s%s' % (copied, end))
2616 ui.write(' %s%s' % (copied, end))
2621
2617
2622 def tag(ui, repo, name, rev_=None, **opts):
2618 def tag(ui, repo, name, rev_=None, **opts):
2623 """add a tag for the current tip or a given revision
2619 """add a tag for the current tip or a given revision
2624
2620
2625 Name a particular revision using <name>.
2621 Name a particular revision using <name>.
2626
2622
2627 Tags are used to name particular revisions of the repository and are
2623 Tags are used to name particular revisions of the repository and are
2628 very useful to compare different revision, to go back to significant
2624 very useful to compare different revision, to go back to significant
2629 earlier versions or to mark branch points as releases, etc.
2625 earlier versions or to mark branch points as releases, etc.
2630
2626
2631 If no revision is given, the parent of the working directory is used.
2627 If no revision is given, the parent of the working directory is used.
2632
2628
2633 To facilitate version control, distribution, and merging of tags,
2629 To facilitate version control, distribution, and merging of tags,
2634 they are stored as a file named ".hgtags" which is managed
2630 they are stored as a file named ".hgtags" which is managed
2635 similarly to other project files and can be hand-edited if
2631 similarly to other project files and can be hand-edited if
2636 necessary. The file '.hg/localtags' is used for local tags (not
2632 necessary. The file '.hg/localtags' is used for local tags (not
2637 shared among repositories).
2633 shared among repositories).
2638 """
2634 """
2639 if name in ['tip', '.']:
2635 if name in ['tip', '.']:
2640 raise util.Abort(_("the name '%s' is reserved") % name)
2636 raise util.Abort(_("the name '%s' is reserved") % name)
2641 if rev_ is not None:
2637 if rev_ is not None:
2642 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2638 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2643 "please use 'hg tag [-r REV] NAME' instead\n"))
2639 "please use 'hg tag [-r REV] NAME' instead\n"))
2644 if opts['rev']:
2640 if opts['rev']:
2645 raise util.Abort(_("use only one form to specify the revision"))
2641 raise util.Abort(_("use only one form to specify the revision"))
2646 if opts['rev']:
2642 if opts['rev']:
2647 rev_ = opts['rev']
2643 rev_ = opts['rev']
2648 if not rev_ and repo.dirstate.parents()[1] != nullid:
2644 if not rev_ and repo.dirstate.parents()[1] != nullid:
2649 raise util.Abort(_('uncommitted merge - please provide a '
2645 raise util.Abort(_('uncommitted merge - please provide a '
2650 'specific revision'))
2646 'specific revision'))
2651 r = repo.changectx(rev_).node()
2647 r = repo.changectx(rev_).node()
2652
2648
2653 message = opts['message']
2649 message = opts['message']
2654 if not message:
2650 if not message:
2655 message = _('Added tag %s for changeset %s') % (name, short(r))
2651 message = _('Added tag %s for changeset %s') % (name, short(r))
2656
2652
2657 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2653 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2658
2654
2659 def tags(ui, repo):
2655 def tags(ui, repo):
2660 """list repository tags
2656 """list repository tags
2661
2657
2662 List the repository tags.
2658 List the repository tags.
2663
2659
2664 This lists both regular and local tags.
2660 This lists both regular and local tags.
2665 """
2661 """
2666
2662
2667 l = repo.tagslist()
2663 l = repo.tagslist()
2668 l.reverse()
2664 l.reverse()
2669 hexfunc = ui.debugflag and hex or short
2665 hexfunc = ui.debugflag and hex or short
2670 for t, n in l:
2666 for t, n in l:
2671 try:
2667 try:
2672 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2668 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2673 except KeyError:
2669 except KeyError:
2674 r = " ?:?"
2670 r = " ?:?"
2675 if ui.quiet:
2671 if ui.quiet:
2676 ui.write("%s\n" % t)
2672 ui.write("%s\n" % t)
2677 else:
2673 else:
2678 ui.write("%-30s %s\n" % (t, r))
2674 ui.write("%-30s %s\n" % (t, r))
2679
2675
2680 def tip(ui, repo, **opts):
2676 def tip(ui, repo, **opts):
2681 """show the tip revision
2677 """show the tip revision
2682
2678
2683 Show the tip revision.
2679 Show the tip revision.
2684 """
2680 """
2685 n = repo.changelog.tip()
2681 n = repo.changelog.tip()
2686 br = None
2682 br = None
2687 if opts['branches']:
2683 if opts['branches']:
2688 ui.warn(_("the --branches option is deprecated, "
2684 ui.warn(_("the --branches option is deprecated, "
2689 "please use 'hg branches' instead\n"))
2685 "please use 'hg branches' instead\n"))
2690 br = repo.branchlookup([n])
2686 br = repo.branchlookup([n])
2691 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2687 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2692 if opts['patch']:
2688 if opts['patch']:
2693 patch.diff(repo, repo.changelog.parents(n)[0], n)
2689 patch.diff(repo, repo.changelog.parents(n)[0], n)
2694
2690
2695 def unbundle(ui, repo, fname, **opts):
2691 def unbundle(ui, repo, fname, **opts):
2696 """apply a changegroup file
2692 """apply a changegroup file
2697
2693
2698 Apply a compressed changegroup file generated by the bundle
2694 Apply a compressed changegroup file generated by the bundle
2699 command.
2695 command.
2700 """
2696 """
2701 f = urllib.urlopen(fname)
2697 f = urllib.urlopen(fname)
2702
2698
2703 header = f.read(6)
2699 header = f.read(6)
2704 if not header.startswith("HG"):
2700 if not header.startswith("HG"):
2705 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2701 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2706 elif not header.startswith("HG10"):
2702 elif not header.startswith("HG10"):
2707 raise util.Abort(_("%s: unknown bundle version") % fname)
2703 raise util.Abort(_("%s: unknown bundle version") % fname)
2708 elif header == "HG10BZ":
2704 elif header == "HG10BZ":
2709 def generator(f):
2705 def generator(f):
2710 zd = bz2.BZ2Decompressor()
2706 zd = bz2.BZ2Decompressor()
2711 zd.decompress("BZ")
2707 zd.decompress("BZ")
2712 for chunk in f:
2708 for chunk in f:
2713 yield zd.decompress(chunk)
2709 yield zd.decompress(chunk)
2714 elif header == "HG10UN":
2710 elif header == "HG10UN":
2715 def generator(f):
2711 def generator(f):
2716 for chunk in f:
2712 for chunk in f:
2717 yield chunk
2713 yield chunk
2718 else:
2714 else:
2719 raise util.Abort(_("%s: unknown bundle compression type")
2715 raise util.Abort(_("%s: unknown bundle compression type")
2720 % fname)
2716 % fname)
2721 gen = generator(util.filechunkiter(f, 4096))
2717 gen = generator(util.filechunkiter(f, 4096))
2722 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2718 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2723 'bundle:' + fname)
2719 'bundle:' + fname)
2724 return postincoming(ui, repo, modheads, opts['update'])
2720 return postincoming(ui, repo, modheads, opts['update'])
2725
2721
2726 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2722 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2727 branch=None):
2723 branch=None):
2728 """update or merge working directory
2724 """update or merge working directory
2729
2725
2730 Update the working directory to the specified revision.
2726 Update the working directory to the specified revision.
2731
2727
2732 If there are no outstanding changes in the working directory and
2728 If there are no outstanding changes in the working directory and
2733 there is a linear relationship between the current version and the
2729 there is a linear relationship between the current version and the
2734 requested version, the result is the requested version.
2730 requested version, the result is the requested version.
2735
2731
2736 To merge the working directory with another revision, use the
2732 To merge the working directory with another revision, use the
2737 merge command.
2733 merge command.
2738
2734
2739 By default, update will refuse to run if doing so would require
2735 By default, update will refuse to run if doing so would require
2740 merging or discarding local changes.
2736 merging or discarding local changes.
2741 """
2737 """
2742 node = _lookup(repo, node, branch)
2738 node = _lookup(repo, node, branch)
2743 if clean:
2739 if clean:
2744 return hg.clean(repo, node)
2740 return hg.clean(repo, node)
2745 else:
2741 else:
2746 return hg.update(repo, node)
2742 return hg.update(repo, node)
2747
2743
2748 def _lookup(repo, node, branch=None):
2744 def _lookup(repo, node, branch=None):
2749 if branch:
2745 if branch:
2750 repo.ui.warn(_("the --branch option is deprecated, "
2746 repo.ui.warn(_("the --branch option is deprecated, "
2751 "please use 'hg branch' instead\n"))
2747 "please use 'hg branch' instead\n"))
2752 br = repo.branchlookup(branch=branch)
2748 br = repo.branchlookup(branch=branch)
2753 found = []
2749 found = []
2754 for x in br:
2750 for x in br:
2755 if branch in br[x]:
2751 if branch in br[x]:
2756 found.append(x)
2752 found.append(x)
2757 if len(found) > 1:
2753 if len(found) > 1:
2758 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2754 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2759 for x in found:
2755 for x in found:
2760 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2756 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2761 raise util.Abort("")
2757 raise util.Abort("")
2762 if len(found) == 1:
2758 if len(found) == 1:
2763 node = found[0]
2759 node = found[0]
2764 repo.ui.warn(_("Using head %s for branch %s\n")
2760 repo.ui.warn(_("Using head %s for branch %s\n")
2765 % (short(node), branch))
2761 % (short(node), branch))
2766 else:
2762 else:
2767 raise util.Abort(_("branch %s not found") % branch)
2763 raise util.Abort(_("branch %s not found") % branch)
2768 else:
2764 else:
2769 node = node and repo.lookup(node) or repo.changelog.tip()
2765 node = node and repo.lookup(node) or repo.changelog.tip()
2770 return node
2766 return node
2771
2767
2772 def verify(ui, repo):
2768 def verify(ui, repo):
2773 """verify the integrity of the repository
2769 """verify the integrity of the repository
2774
2770
2775 Verify the integrity of the current repository.
2771 Verify the integrity of the current repository.
2776
2772
2777 This will perform an extensive check of the repository's
2773 This will perform an extensive check of the repository's
2778 integrity, validating the hashes and checksums of each entry in
2774 integrity, validating the hashes and checksums of each entry in
2779 the changelog, manifest, and tracked files, as well as the
2775 the changelog, manifest, and tracked files, as well as the
2780 integrity of their crosslinks and indices.
2776 integrity of their crosslinks and indices.
2781 """
2777 """
2782 return hg.verify(repo)
2778 return hg.verify(repo)
2783
2779
2784 # Command options and aliases are listed here, alphabetically
2780 # Command options and aliases are listed here, alphabetically
2785
2781
2786 globalopts = [
2782 globalopts = [
2787 ('R', 'repository', '',
2783 ('R', 'repository', '',
2788 _('repository root directory or symbolic path name')),
2784 _('repository root directory or symbolic path name')),
2789 ('', 'cwd', '', _('change working directory')),
2785 ('', 'cwd', '', _('change working directory')),
2790 ('y', 'noninteractive', None,
2786 ('y', 'noninteractive', None,
2791 _('do not prompt, assume \'yes\' for any required answers')),
2787 _('do not prompt, assume \'yes\' for any required answers')),
2792 ('q', 'quiet', None, _('suppress output')),
2788 ('q', 'quiet', None, _('suppress output')),
2793 ('v', 'verbose', None, _('enable additional output')),
2789 ('v', 'verbose', None, _('enable additional output')),
2794 ('', 'config', [], _('set/override config option')),
2790 ('', 'config', [], _('set/override config option')),
2795 ('', 'debug', None, _('enable debugging output')),
2791 ('', 'debug', None, _('enable debugging output')),
2796 ('', 'debugger', None, _('start debugger')),
2792 ('', 'debugger', None, _('start debugger')),
2797 ('', 'lsprof', None, _('print improved command execution profile')),
2793 ('', 'lsprof', None, _('print improved command execution profile')),
2798 ('', 'traceback', None, _('print traceback on exception')),
2794 ('', 'traceback', None, _('print traceback on exception')),
2799 ('', 'time', None, _('time how long the command takes')),
2795 ('', 'time', None, _('time how long the command takes')),
2800 ('', 'profile', None, _('print command execution profile')),
2796 ('', 'profile', None, _('print command execution profile')),
2801 ('', 'version', None, _('output version information and exit')),
2797 ('', 'version', None, _('output version information and exit')),
2802 ('h', 'help', None, _('display help and exit')),
2798 ('h', 'help', None, _('display help and exit')),
2803 ]
2799 ]
2804
2800
2805 dryrunopts = [('n', 'dry-run', None,
2801 dryrunopts = [('n', 'dry-run', None,
2806 _('do not perform actions, just print output'))]
2802 _('do not perform actions, just print output'))]
2807
2803
2808 remoteopts = [
2804 remoteopts = [
2809 ('e', 'ssh', '', _('specify ssh command to use')),
2805 ('e', 'ssh', '', _('specify ssh command to use')),
2810 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2806 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2811 ]
2807 ]
2812
2808
2813 walkopts = [
2809 walkopts = [
2814 ('I', 'include', [], _('include names matching the given patterns')),
2810 ('I', 'include', [], _('include names matching the given patterns')),
2815 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2811 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2816 ]
2812 ]
2817
2813
2818 table = {
2814 table = {
2819 "^add":
2815 "^add":
2820 (add,
2816 (add,
2821 walkopts + dryrunopts,
2817 walkopts + dryrunopts,
2822 _('hg add [OPTION]... [FILE]...')),
2818 _('hg add [OPTION]... [FILE]...')),
2823 "addremove":
2819 "addremove":
2824 (addremove,
2820 (addremove,
2825 [('s', 'similarity', '',
2821 [('s', 'similarity', '',
2826 _('guess renamed files by similarity (0<=s<=100)')),
2822 _('guess renamed files by similarity (0<=s<=100)')),
2827 ] + walkopts + dryrunopts,
2823 ] + walkopts + dryrunopts,
2828 _('hg addremove [OPTION]... [FILE]...')),
2824 _('hg addremove [OPTION]... [FILE]...')),
2829 "^annotate":
2825 "^annotate":
2830 (annotate,
2826 (annotate,
2831 [('r', 'rev', '', _('annotate the specified revision')),
2827 [('r', 'rev', '', _('annotate the specified revision')),
2832 ('f', 'follow', None, _('follow file copies and renames')),
2828 ('f', 'follow', None, _('follow file copies and renames')),
2833 ('a', 'text', None, _('treat all files as text')),
2829 ('a', 'text', None, _('treat all files as text')),
2834 ('u', 'user', None, _('list the author')),
2830 ('u', 'user', None, _('list the author')),
2835 ('d', 'date', None, _('list the date')),
2831 ('d', 'date', None, _('list the date')),
2836 ('n', 'number', None, _('list the revision number (default)')),
2832 ('n', 'number', None, _('list the revision number (default)')),
2837 ('c', 'changeset', None, _('list the changeset')),
2833 ('c', 'changeset', None, _('list the changeset')),
2838 ] + walkopts,
2834 ] + walkopts,
2839 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2835 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2840 "archive":
2836 "archive":
2841 (archive,
2837 (archive,
2842 [('', 'no-decode', None, _('do not pass files through decoders')),
2838 [('', 'no-decode', None, _('do not pass files through decoders')),
2843 ('p', 'prefix', '', _('directory prefix for files in archive')),
2839 ('p', 'prefix', '', _('directory prefix for files in archive')),
2844 ('r', 'rev', '', _('revision to distribute')),
2840 ('r', 'rev', '', _('revision to distribute')),
2845 ('t', 'type', '', _('type of distribution to create')),
2841 ('t', 'type', '', _('type of distribution to create')),
2846 ] + walkopts,
2842 ] + walkopts,
2847 _('hg archive [OPTION]... DEST')),
2843 _('hg archive [OPTION]... DEST')),
2848 "backout":
2844 "backout":
2849 (backout,
2845 (backout,
2850 [('', 'merge', None,
2846 [('', 'merge', None,
2851 _('merge with old dirstate parent after backout')),
2847 _('merge with old dirstate parent after backout')),
2852 ('m', 'message', '', _('use <text> as commit message')),
2848 ('m', 'message', '', _('use <text> as commit message')),
2853 ('l', 'logfile', '', _('read commit message from <file>')),
2849 ('l', 'logfile', '', _('read commit message from <file>')),
2854 ('d', 'date', '', _('record datecode as commit date')),
2850 ('d', 'date', '', _('record datecode as commit date')),
2855 ('', 'parent', '', _('parent to choose when backing out merge')),
2851 ('', 'parent', '', _('parent to choose when backing out merge')),
2856 ('u', 'user', '', _('record user as committer')),
2852 ('u', 'user', '', _('record user as committer')),
2857 ] + walkopts,
2853 ] + walkopts,
2858 _('hg backout [OPTION]... REV')),
2854 _('hg backout [OPTION]... REV')),
2859 "branch": (branch, [], _('hg branch [NAME]')),
2855 "branch": (branch, [], _('hg branch [NAME]')),
2860 "branches": (branches, [], _('hg branches')),
2856 "branches": (branches, [], _('hg branches')),
2861 "bundle":
2857 "bundle":
2862 (bundle,
2858 (bundle,
2863 [('f', 'force', None,
2859 [('f', 'force', None,
2864 _('run even when remote repository is unrelated')),
2860 _('run even when remote repository is unrelated')),
2865 ('r', 'rev', [],
2861 ('r', 'rev', [],
2866 _('a changeset you would like to bundle')),
2862 _('a changeset you would like to bundle')),
2867 ('', 'base', [],
2863 ('', 'base', [],
2868 _('a base changeset to specify instead of a destination')),
2864 _('a base changeset to specify instead of a destination')),
2869 ] + remoteopts,
2865 ] + remoteopts,
2870 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2866 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2871 "cat":
2867 "cat":
2872 (cat,
2868 (cat,
2873 [('o', 'output', '', _('print output to file with formatted name')),
2869 [('o', 'output', '', _('print output to file with formatted name')),
2874 ('r', 'rev', '', _('print the given revision')),
2870 ('r', 'rev', '', _('print the given revision')),
2875 ] + walkopts,
2871 ] + walkopts,
2876 _('hg cat [OPTION]... FILE...')),
2872 _('hg cat [OPTION]... FILE...')),
2877 "^clone":
2873 "^clone":
2878 (clone,
2874 (clone,
2879 [('U', 'noupdate', None, _('do not update the new working directory')),
2875 [('U', 'noupdate', None, _('do not update the new working directory')),
2880 ('r', 'rev', [],
2876 ('r', 'rev', [],
2881 _('a changeset you would like to have after cloning')),
2877 _('a changeset you would like to have after cloning')),
2882 ('', 'pull', None, _('use pull protocol to copy metadata')),
2878 ('', 'pull', None, _('use pull protocol to copy metadata')),
2883 ('', 'uncompressed', None,
2879 ('', 'uncompressed', None,
2884 _('use uncompressed transfer (fast over LAN)')),
2880 _('use uncompressed transfer (fast over LAN)')),
2885 ] + remoteopts,
2881 ] + remoteopts,
2886 _('hg clone [OPTION]... SOURCE [DEST]')),
2882 _('hg clone [OPTION]... SOURCE [DEST]')),
2887 "^commit|ci":
2883 "^commit|ci":
2888 (commit,
2884 (commit,
2889 [('A', 'addremove', None,
2885 [('A', 'addremove', None,
2890 _('mark new/missing files as added/removed before committing')),
2886 _('mark new/missing files as added/removed before committing')),
2891 ('m', 'message', '', _('use <text> as commit message')),
2887 ('m', 'message', '', _('use <text> as commit message')),
2892 ('l', 'logfile', '', _('read the commit message from <file>')),
2888 ('l', 'logfile', '', _('read the commit message from <file>')),
2893 ('d', 'date', '', _('record datecode as commit date')),
2889 ('d', 'date', '', _('record datecode as commit date')),
2894 ('u', 'user', '', _('record user as commiter')),
2890 ('u', 'user', '', _('record user as commiter')),
2895 ] + walkopts,
2891 ] + walkopts,
2896 _('hg commit [OPTION]... [FILE]...')),
2892 _('hg commit [OPTION]... [FILE]...')),
2897 "copy|cp":
2893 "copy|cp":
2898 (copy,
2894 (copy,
2899 [('A', 'after', None, _('record a copy that has already occurred')),
2895 [('A', 'after', None, _('record a copy that has already occurred')),
2900 ('f', 'force', None,
2896 ('f', 'force', None,
2901 _('forcibly copy over an existing managed file')),
2897 _('forcibly copy over an existing managed file')),
2902 ] + walkopts + dryrunopts,
2898 ] + walkopts + dryrunopts,
2903 _('hg copy [OPTION]... [SOURCE]... DEST')),
2899 _('hg copy [OPTION]... [SOURCE]... DEST')),
2904 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2900 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2905 "debugcomplete":
2901 "debugcomplete":
2906 (debugcomplete,
2902 (debugcomplete,
2907 [('o', 'options', None, _('show the command options'))],
2903 [('o', 'options', None, _('show the command options'))],
2908 _('debugcomplete [-o] CMD')),
2904 _('debugcomplete [-o] CMD')),
2909 "debugrebuildstate":
2905 "debugrebuildstate":
2910 (debugrebuildstate,
2906 (debugrebuildstate,
2911 [('r', 'rev', '', _('revision to rebuild to'))],
2907 [('r', 'rev', '', _('revision to rebuild to'))],
2912 _('debugrebuildstate [-r REV] [REV]')),
2908 _('debugrebuildstate [-r REV] [REV]')),
2913 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2909 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2914 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2910 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2915 "debugstate": (debugstate, [], _('debugstate')),
2911 "debugstate": (debugstate, [], _('debugstate')),
2916 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2912 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2917 "debugindex": (debugindex, [], _('debugindex FILE')),
2913 "debugindex": (debugindex, [], _('debugindex FILE')),
2918 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2914 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2919 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2915 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2920 "debugwalk":
2916 "debugwalk":
2921 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2917 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2922 "^diff":
2918 "^diff":
2923 (diff,
2919 (diff,
2924 [('r', 'rev', [], _('revision')),
2920 [('r', 'rev', [], _('revision')),
2925 ('a', 'text', None, _('treat all files as text')),
2921 ('a', 'text', None, _('treat all files as text')),
2926 ('p', 'show-function', None,
2922 ('p', 'show-function', None,
2927 _('show which function each change is in')),
2923 _('show which function each change is in')),
2928 ('g', 'git', None, _('use git extended diff format')),
2924 ('g', 'git', None, _('use git extended diff format')),
2929 ('', 'nodates', None, _("don't include dates in diff headers")),
2925 ('', 'nodates', None, _("don't include dates in diff headers")),
2930 ('w', 'ignore-all-space', None,
2926 ('w', 'ignore-all-space', None,
2931 _('ignore white space when comparing lines')),
2927 _('ignore white space when comparing lines')),
2932 ('b', 'ignore-space-change', None,
2928 ('b', 'ignore-space-change', None,
2933 _('ignore changes in the amount of white space')),
2929 _('ignore changes in the amount of white space')),
2934 ('B', 'ignore-blank-lines', None,
2930 ('B', 'ignore-blank-lines', None,
2935 _('ignore changes whose lines are all blank')),
2931 _('ignore changes whose lines are all blank')),
2936 ] + walkopts,
2932 ] + walkopts,
2937 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2933 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2938 "^export":
2934 "^export":
2939 (export,
2935 (export,
2940 [('o', 'output', '', _('print output to file with formatted name')),
2936 [('o', 'output', '', _('print output to file with formatted name')),
2941 ('a', 'text', None, _('treat all files as text')),
2937 ('a', 'text', None, _('treat all files as text')),
2942 ('g', 'git', None, _('use git extended diff format')),
2938 ('g', 'git', None, _('use git extended diff format')),
2943 ('', 'nodates', None, _("don't include dates in diff headers")),
2939 ('', 'nodates', None, _("don't include dates in diff headers")),
2944 ('', 'switch-parent', None, _('diff against the second parent'))],
2940 ('', 'switch-parent', None, _('diff against the second parent'))],
2945 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2941 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2946 "grep":
2942 "grep":
2947 (grep,
2943 (grep,
2948 [('0', 'print0', None, _('end fields with NUL')),
2944 [('0', 'print0', None, _('end fields with NUL')),
2949 ('', 'all', None, _('print all revisions that match')),
2945 ('', 'all', None, _('print all revisions that match')),
2950 ('f', 'follow', None,
2946 ('f', 'follow', None,
2951 _('follow changeset history, or file history across copies and renames')),
2947 _('follow changeset history, or file history across copies and renames')),
2952 ('i', 'ignore-case', None, _('ignore case when matching')),
2948 ('i', 'ignore-case', None, _('ignore case when matching')),
2953 ('l', 'files-with-matches', None,
2949 ('l', 'files-with-matches', None,
2954 _('print only filenames and revs that match')),
2950 _('print only filenames and revs that match')),
2955 ('n', 'line-number', None, _('print matching line numbers')),
2951 ('n', 'line-number', None, _('print matching line numbers')),
2956 ('r', 'rev', [], _('search in given revision range')),
2952 ('r', 'rev', [], _('search in given revision range')),
2957 ('u', 'user', None, _('print user who committed change')),
2953 ('u', 'user', None, _('print user who committed change')),
2958 ] + walkopts,
2954 ] + walkopts,
2959 _('hg grep [OPTION]... PATTERN [FILE]...')),
2955 _('hg grep [OPTION]... PATTERN [FILE]...')),
2960 "heads":
2956 "heads":
2961 (heads,
2957 (heads,
2962 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2958 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2963 ('', 'style', '', _('display using template map file')),
2959 ('', 'style', '', _('display using template map file')),
2964 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2960 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2965 ('', 'template', '', _('display with template'))],
2961 ('', 'template', '', _('display with template'))],
2966 _('hg heads [-r REV]')),
2962 _('hg heads [-r REV]')),
2967 "help": (help_, [], _('hg help [COMMAND]')),
2963 "help": (help_, [], _('hg help [COMMAND]')),
2968 "identify|id": (identify, [], _('hg identify')),
2964 "identify|id": (identify, [], _('hg identify')),
2969 "import|patch":
2965 "import|patch":
2970 (import_,
2966 (import_,
2971 [('p', 'strip', 1,
2967 [('p', 'strip', 1,
2972 _('directory strip option for patch. This has the same\n'
2968 _('directory strip option for patch. This has the same\n'
2973 'meaning as the corresponding patch option')),
2969 'meaning as the corresponding patch option')),
2974 ('m', 'message', '', _('use <text> as commit message')),
2970 ('m', 'message', '', _('use <text> as commit message')),
2975 ('b', 'base', '', _('base path (DEPRECATED)')),
2971 ('b', 'base', '', _('base path (DEPRECATED)')),
2976 ('f', 'force', None,
2972 ('f', 'force', None,
2977 _('skip check for outstanding uncommitted changes'))],
2973 _('skip check for outstanding uncommitted changes'))],
2978 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2974 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2979 "incoming|in": (incoming,
2975 "incoming|in": (incoming,
2980 [('M', 'no-merges', None, _('do not show merges')),
2976 [('M', 'no-merges', None, _('do not show merges')),
2981 ('f', 'force', None,
2977 ('f', 'force', None,
2982 _('run even when remote repository is unrelated')),
2978 _('run even when remote repository is unrelated')),
2983 ('', 'style', '', _('display using template map file')),
2979 ('', 'style', '', _('display using template map file')),
2984 ('n', 'newest-first', None, _('show newest record first')),
2980 ('n', 'newest-first', None, _('show newest record first')),
2985 ('', 'bundle', '', _('file to store the bundles into')),
2981 ('', 'bundle', '', _('file to store the bundles into')),
2986 ('p', 'patch', None, _('show patch')),
2982 ('p', 'patch', None, _('show patch')),
2987 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2983 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2988 ('', 'template', '', _('display with template')),
2984 ('', 'template', '', _('display with template')),
2989 ] + remoteopts,
2985 ] + remoteopts,
2990 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2986 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2991 ' [--bundle FILENAME] [SOURCE]')),
2987 ' [--bundle FILENAME] [SOURCE]')),
2992 "^init":
2988 "^init":
2993 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2989 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2994 "locate":
2990 "locate":
2995 (locate,
2991 (locate,
2996 [('r', 'rev', '', _('search the repository as it stood at rev')),
2992 [('r', 'rev', '', _('search the repository as it stood at rev')),
2997 ('0', 'print0', None,
2993 ('0', 'print0', None,
2998 _('end filenames with NUL, for use with xargs')),
2994 _('end filenames with NUL, for use with xargs')),
2999 ('f', 'fullpath', None,
2995 ('f', 'fullpath', None,
3000 _('print complete paths from the filesystem root')),
2996 _('print complete paths from the filesystem root')),
3001 ] + walkopts,
2997 ] + walkopts,
3002 _('hg locate [OPTION]... [PATTERN]...')),
2998 _('hg locate [OPTION]... [PATTERN]...')),
3003 "^log|history":
2999 "^log|history":
3004 (log,
3000 (log,
3005 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3001 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3006 ('f', 'follow', None,
3002 ('f', 'follow', None,
3007 _('follow changeset history, or file history across copies and renames')),
3003 _('follow changeset history, or file history across copies and renames')),
3008 ('', 'follow-first', None,
3004 ('', 'follow-first', None,
3009 _('only follow the first parent of merge changesets')),
3005 _('only follow the first parent of merge changesets')),
3010 ('C', 'copies', None, _('show copied files')),
3006 ('C', 'copies', None, _('show copied files')),
3011 ('k', 'keyword', [], _('search for a keyword')),
3007 ('k', 'keyword', [], _('search for a keyword')),
3012 ('l', 'limit', '', _('limit number of changes displayed')),
3008 ('l', 'limit', '', _('limit number of changes displayed')),
3013 ('r', 'rev', [], _('show the specified revision or range')),
3009 ('r', 'rev', [], _('show the specified revision or range')),
3014 ('M', 'no-merges', None, _('do not show merges')),
3010 ('M', 'no-merges', None, _('do not show merges')),
3015 ('', 'style', '', _('display using template map file')),
3011 ('', 'style', '', _('display using template map file')),
3016 ('m', 'only-merges', None, _('show only merges')),
3012 ('m', 'only-merges', None, _('show only merges')),
3017 ('p', 'patch', None, _('show patch')),
3013 ('p', 'patch', None, _('show patch')),
3018 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3014 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3019 ('', 'template', '', _('display with template')),
3015 ('', 'template', '', _('display with template')),
3020 ] + walkopts,
3016 ] + walkopts,
3021 _('hg log [OPTION]... [FILE]')),
3017 _('hg log [OPTION]... [FILE]')),
3022 "manifest": (manifest, [], _('hg manifest [REV]')),
3018 "manifest": (manifest, [], _('hg manifest [REV]')),
3023 "merge":
3019 "merge":
3024 (merge,
3020 (merge,
3025 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
3021 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
3026 ('f', 'force', None, _('force a merge with outstanding changes'))],
3022 ('f', 'force', None, _('force a merge with outstanding changes'))],
3027 _('hg merge [-f] [REV]')),
3023 _('hg merge [-f] [REV]')),
3028 "outgoing|out": (outgoing,
3024 "outgoing|out": (outgoing,
3029 [('M', 'no-merges', None, _('do not show merges')),
3025 [('M', 'no-merges', None, _('do not show merges')),
3030 ('f', 'force', None,
3026 ('f', 'force', None,
3031 _('run even when remote repository is unrelated')),
3027 _('run even when remote repository is unrelated')),
3032 ('p', 'patch', None, _('show patch')),
3028 ('p', 'patch', None, _('show patch')),
3033 ('', 'style', '', _('display using template map file')),
3029 ('', 'style', '', _('display using template map file')),
3034 ('r', 'rev', [], _('a specific revision you would like to push')),
3030 ('r', 'rev', [], _('a specific revision you would like to push')),
3035 ('n', 'newest-first', None, _('show newest record first')),
3031 ('n', 'newest-first', None, _('show newest record first')),
3036 ('', 'template', '', _('display with template')),
3032 ('', 'template', '', _('display with template')),
3037 ] + remoteopts,
3033 ] + remoteopts,
3038 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3034 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3039 "^parents":
3035 "^parents":
3040 (parents,
3036 (parents,
3041 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3037 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3042 ('r', 'rev', '', _('show parents from the specified rev')),
3038 ('r', 'rev', '', _('show parents from the specified rev')),
3043 ('', 'style', '', _('display using template map file')),
3039 ('', 'style', '', _('display using template map file')),
3044 ('', 'template', '', _('display with template'))],
3040 ('', 'template', '', _('display with template'))],
3045 _('hg parents [-r REV] [FILE]')),
3041 _('hg parents [-r REV] [FILE]')),
3046 "paths": (paths, [], _('hg paths [NAME]')),
3042 "paths": (paths, [], _('hg paths [NAME]')),
3047 "^pull":
3043 "^pull":
3048 (pull,
3044 (pull,
3049 [('u', 'update', None,
3045 [('u', 'update', None,
3050 _('update to new tip if changesets were pulled')),
3046 _('update to new tip if changesets were pulled')),
3051 ('f', 'force', None,
3047 ('f', 'force', None,
3052 _('run even when remote repository is unrelated')),
3048 _('run even when remote repository is unrelated')),
3053 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3049 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3054 ] + remoteopts,
3050 ] + remoteopts,
3055 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3051 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3056 "^push":
3052 "^push":
3057 (push,
3053 (push,
3058 [('f', 'force', None, _('force push')),
3054 [('f', 'force', None, _('force push')),
3059 ('r', 'rev', [], _('a specific revision you would like to push')),
3055 ('r', 'rev', [], _('a specific revision you would like to push')),
3060 ] + remoteopts,
3056 ] + remoteopts,
3061 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3057 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3062 "debugrawcommit|rawcommit":
3058 "debugrawcommit|rawcommit":
3063 (rawcommit,
3059 (rawcommit,
3064 [('p', 'parent', [], _('parent')),
3060 [('p', 'parent', [], _('parent')),
3065 ('d', 'date', '', _('date code')),
3061 ('d', 'date', '', _('date code')),
3066 ('u', 'user', '', _('user')),
3062 ('u', 'user', '', _('user')),
3067 ('F', 'files', '', _('file list')),
3063 ('F', 'files', '', _('file list')),
3068 ('m', 'message', '', _('commit message')),
3064 ('m', 'message', '', _('commit message')),
3069 ('l', 'logfile', '', _('commit message file'))],
3065 ('l', 'logfile', '', _('commit message file'))],
3070 _('hg debugrawcommit [OPTION]... [FILE]...')),
3066 _('hg debugrawcommit [OPTION]... [FILE]...')),
3071 "recover": (recover, [], _('hg recover')),
3067 "recover": (recover, [], _('hg recover')),
3072 "^remove|rm":
3068 "^remove|rm":
3073 (remove,
3069 (remove,
3074 [('A', 'after', None, _('record remove that has already occurred')),
3070 [('A', 'after', None, _('record remove that has already occurred')),
3075 ('f', 'force', None, _('remove file even if modified')),
3071 ('f', 'force', None, _('remove file even if modified')),
3076 ] + walkopts,
3072 ] + walkopts,
3077 _('hg remove [OPTION]... FILE...')),
3073 _('hg remove [OPTION]... FILE...')),
3078 "rename|mv":
3074 "rename|mv":
3079 (rename,
3075 (rename,
3080 [('A', 'after', None, _('record a rename that has already occurred')),
3076 [('A', 'after', None, _('record a rename that has already occurred')),
3081 ('f', 'force', None,
3077 ('f', 'force', None,
3082 _('forcibly copy over an existing managed file')),
3078 _('forcibly copy over an existing managed file')),
3083 ] + walkopts + dryrunopts,
3079 ] + walkopts + dryrunopts,
3084 _('hg rename [OPTION]... SOURCE... DEST')),
3080 _('hg rename [OPTION]... SOURCE... DEST')),
3085 "^revert":
3081 "^revert":
3086 (revert,
3082 (revert,
3087 [('a', 'all', None, _('revert all changes when no arguments given')),
3083 [('a', 'all', None, _('revert all changes when no arguments given')),
3088 ('r', 'rev', '', _('revision to revert to')),
3084 ('r', 'rev', '', _('revision to revert to')),
3089 ('', 'no-backup', None, _('do not save backup copies of files')),
3085 ('', 'no-backup', None, _('do not save backup copies of files')),
3090 ] + walkopts + dryrunopts,
3086 ] + walkopts + dryrunopts,
3091 _('hg revert [-r REV] [NAME]...')),
3087 _('hg revert [-r REV] [NAME]...')),
3092 "rollback": (rollback, [], _('hg rollback')),
3088 "rollback": (rollback, [], _('hg rollback')),
3093 "root": (root, [], _('hg root')),
3089 "root": (root, [], _('hg root')),
3094 "showconfig|debugconfig":
3090 "showconfig|debugconfig":
3095 (showconfig,
3091 (showconfig,
3096 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3092 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3097 _('showconfig [-u] [NAME]...')),
3093 _('showconfig [-u] [NAME]...')),
3098 "^serve":
3094 "^serve":
3099 (serve,
3095 (serve,
3100 [('A', 'accesslog', '', _('name of access log file to write to')),
3096 [('A', 'accesslog', '', _('name of access log file to write to')),
3101 ('d', 'daemon', None, _('run server in background')),
3097 ('d', 'daemon', None, _('run server in background')),
3102 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3098 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3103 ('E', 'errorlog', '', _('name of error log file to write to')),
3099 ('E', 'errorlog', '', _('name of error log file to write to')),
3104 ('p', 'port', 0, _('port to use (default: 8000)')),
3100 ('p', 'port', 0, _('port to use (default: 8000)')),
3105 ('a', 'address', '', _('address to use')),
3101 ('a', 'address', '', _('address to use')),
3106 ('n', 'name', '',
3102 ('n', 'name', '',
3107 _('name to show in web pages (default: working dir)')),
3103 _('name to show in web pages (default: working dir)')),
3108 ('', 'webdir-conf', '', _('name of the webdir config file'
3104 ('', 'webdir-conf', '', _('name of the webdir config file'
3109 ' (serve more than one repo)')),
3105 ' (serve more than one repo)')),
3110 ('', 'pid-file', '', _('name of file to write process ID to')),
3106 ('', 'pid-file', '', _('name of file to write process ID to')),
3111 ('', 'stdio', None, _('for remote clients')),
3107 ('', 'stdio', None, _('for remote clients')),
3112 ('t', 'templates', '', _('web templates to use')),
3108 ('t', 'templates', '', _('web templates to use')),
3113 ('', 'style', '', _('template style to use')),
3109 ('', 'style', '', _('template style to use')),
3114 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3110 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3115 _('hg serve [OPTION]...')),
3111 _('hg serve [OPTION]...')),
3116 "^status|st":
3112 "^status|st":
3117 (status,
3113 (status,
3118 [('A', 'all', None, _('show status of all files')),
3114 [('A', 'all', None, _('show status of all files')),
3119 ('m', 'modified', None, _('show only modified files')),
3115 ('m', 'modified', None, _('show only modified files')),
3120 ('a', 'added', None, _('show only added files')),
3116 ('a', 'added', None, _('show only added files')),
3121 ('r', 'removed', None, _('show only removed files')),
3117 ('r', 'removed', None, _('show only removed files')),
3122 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3118 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3123 ('c', 'clean', None, _('show only files without changes')),
3119 ('c', 'clean', None, _('show only files without changes')),
3124 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3120 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3125 ('i', 'ignored', None, _('show ignored files')),
3121 ('i', 'ignored', None, _('show ignored files')),
3126 ('n', 'no-status', None, _('hide status prefix')),
3122 ('n', 'no-status', None, _('hide status prefix')),
3127 ('C', 'copies', None, _('show source of copied files')),
3123 ('C', 'copies', None, _('show source of copied files')),
3128 ('0', 'print0', None,
3124 ('0', 'print0', None,
3129 _('end filenames with NUL, for use with xargs')),
3125 _('end filenames with NUL, for use with xargs')),
3130 ('', 'rev', [], _('show difference from revision')),
3126 ('', 'rev', [], _('show difference from revision')),
3131 ] + walkopts,
3127 ] + walkopts,
3132 _('hg status [OPTION]... [FILE]...')),
3128 _('hg status [OPTION]... [FILE]...')),
3133 "tag":
3129 "tag":
3134 (tag,
3130 (tag,
3135 [('l', 'local', None, _('make the tag local')),
3131 [('l', 'local', None, _('make the tag local')),
3136 ('m', 'message', '', _('message for tag commit log entry')),
3132 ('m', 'message', '', _('message for tag commit log entry')),
3137 ('d', 'date', '', _('record datecode as commit date')),
3133 ('d', 'date', '', _('record datecode as commit date')),
3138 ('u', 'user', '', _('record user as commiter')),
3134 ('u', 'user', '', _('record user as commiter')),
3139 ('r', 'rev', '', _('revision to tag'))],
3135 ('r', 'rev', '', _('revision to tag'))],
3140 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3136 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3141 "tags": (tags, [], _('hg tags')),
3137 "tags": (tags, [], _('hg tags')),
3142 "tip":
3138 "tip":
3143 (tip,
3139 (tip,
3144 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3140 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3145 ('', 'style', '', _('display using template map file')),
3141 ('', 'style', '', _('display using template map file')),
3146 ('p', 'patch', None, _('show patch')),
3142 ('p', 'patch', None, _('show patch')),
3147 ('', 'template', '', _('display with template'))],
3143 ('', 'template', '', _('display with template'))],
3148 _('hg tip [-p]')),
3144 _('hg tip [-p]')),
3149 "unbundle":
3145 "unbundle":
3150 (unbundle,
3146 (unbundle,
3151 [('u', 'update', None,
3147 [('u', 'update', None,
3152 _('update to new tip if changesets were unbundled'))],
3148 _('update to new tip if changesets were unbundled'))],
3153 _('hg unbundle [-u] FILE')),
3149 _('hg unbundle [-u] FILE')),
3154 "^update|up|checkout|co":
3150 "^update|up|checkout|co":
3155 (update,
3151 (update,
3156 [('b', 'branch', '',
3152 [('b', 'branch', '',
3157 _('checkout the head of a specific branch (DEPRECATED)')),
3153 _('checkout the head of a specific branch (DEPRECATED)')),
3158 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3154 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3159 ('C', 'clean', None, _('overwrite locally modified files')),
3155 ('C', 'clean', None, _('overwrite locally modified files')),
3160 ('f', 'force', None, _('force a merge with outstanding changes'))],
3156 ('f', 'force', None, _('force a merge with outstanding changes'))],
3161 _('hg update [-C] [-f] [REV]')),
3157 _('hg update [-C] [-f] [REV]')),
3162 "verify": (verify, [], _('hg verify')),
3158 "verify": (verify, [], _('hg verify')),
3163 "version": (show_version, [], _('hg version')),
3159 "version": (show_version, [], _('hg version')),
3164 }
3160 }
3165
3161
3166 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3162 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3167 " debugindex debugindexdot")
3163 " debugindex debugindexdot")
3168 optionalrepo = ("paths serve showconfig")
3164 optionalrepo = ("paths serve showconfig")
3169
3165
3170 def findpossible(ui, cmd):
3166 def findpossible(ui, cmd):
3171 """
3167 """
3172 Return cmd -> (aliases, command table entry)
3168 Return cmd -> (aliases, command table entry)
3173 for each matching command.
3169 for each matching command.
3174 Return debug commands (or their aliases) only if no normal command matches.
3170 Return debug commands (or their aliases) only if no normal command matches.
3175 """
3171 """
3176 choice = {}
3172 choice = {}
3177 debugchoice = {}
3173 debugchoice = {}
3178 for e in table.keys():
3174 for e in table.keys():
3179 aliases = e.lstrip("^").split("|")
3175 aliases = e.lstrip("^").split("|")
3180 found = None
3176 found = None
3181 if cmd in aliases:
3177 if cmd in aliases:
3182 found = cmd
3178 found = cmd
3183 elif not ui.config("ui", "strict"):
3179 elif not ui.config("ui", "strict"):
3184 for a in aliases:
3180 for a in aliases:
3185 if a.startswith(cmd):
3181 if a.startswith(cmd):
3186 found = a
3182 found = a
3187 break
3183 break
3188 if found is not None:
3184 if found is not None:
3189 if aliases[0].startswith("debug") or found.startswith("debug"):
3185 if aliases[0].startswith("debug") or found.startswith("debug"):
3190 debugchoice[found] = (aliases, table[e])
3186 debugchoice[found] = (aliases, table[e])
3191 else:
3187 else:
3192 choice[found] = (aliases, table[e])
3188 choice[found] = (aliases, table[e])
3193
3189
3194 if not choice and debugchoice:
3190 if not choice and debugchoice:
3195 choice = debugchoice
3191 choice = debugchoice
3196
3192
3197 return choice
3193 return choice
3198
3194
3199 def findcmd(ui, cmd):
3195 def findcmd(ui, cmd):
3200 """Return (aliases, command table entry) for command string."""
3196 """Return (aliases, command table entry) for command string."""
3201 choice = findpossible(ui, cmd)
3197 choice = findpossible(ui, cmd)
3202
3198
3203 if choice.has_key(cmd):
3199 if choice.has_key(cmd):
3204 return choice[cmd]
3200 return choice[cmd]
3205
3201
3206 if len(choice) > 1:
3202 if len(choice) > 1:
3207 clist = choice.keys()
3203 clist = choice.keys()
3208 clist.sort()
3204 clist.sort()
3209 raise AmbiguousCommand(cmd, clist)
3205 raise AmbiguousCommand(cmd, clist)
3210
3206
3211 if choice:
3207 if choice:
3212 return choice.values()[0]
3208 return choice.values()[0]
3213
3209
3214 raise UnknownCommand(cmd)
3210 raise UnknownCommand(cmd)
3215
3211
3216 def catchterm(*args):
3212 def catchterm(*args):
3217 raise util.SignalInterrupt
3213 raise util.SignalInterrupt
3218
3214
3219 def run():
3215 def run():
3220 sys.exit(dispatch(sys.argv[1:]))
3216 sys.exit(dispatch(sys.argv[1:]))
3221
3217
3222 class ParseError(Exception):
3218 class ParseError(Exception):
3223 """Exception raised on errors in parsing the command line."""
3219 """Exception raised on errors in parsing the command line."""
3224
3220
3225 def parse(ui, args):
3221 def parse(ui, args):
3226 options = {}
3222 options = {}
3227 cmdoptions = {}
3223 cmdoptions = {}
3228
3224
3229 try:
3225 try:
3230 args = fancyopts.fancyopts(args, globalopts, options)
3226 args = fancyopts.fancyopts(args, globalopts, options)
3231 except fancyopts.getopt.GetoptError, inst:
3227 except fancyopts.getopt.GetoptError, inst:
3232 raise ParseError(None, inst)
3228 raise ParseError(None, inst)
3233
3229
3234 if args:
3230 if args:
3235 cmd, args = args[0], args[1:]
3231 cmd, args = args[0], args[1:]
3236 aliases, i = findcmd(ui, cmd)
3232 aliases, i = findcmd(ui, cmd)
3237 cmd = aliases[0]
3233 cmd = aliases[0]
3238 defaults = ui.config("defaults", cmd)
3234 defaults = ui.config("defaults", cmd)
3239 if defaults:
3235 if defaults:
3240 args = shlex.split(defaults) + args
3236 args = shlex.split(defaults) + args
3241 c = list(i[1])
3237 c = list(i[1])
3242 else:
3238 else:
3243 cmd = None
3239 cmd = None
3244 c = []
3240 c = []
3245
3241
3246 # combine global options into local
3242 # combine global options into local
3247 for o in globalopts:
3243 for o in globalopts:
3248 c.append((o[0], o[1], options[o[1]], o[3]))
3244 c.append((o[0], o[1], options[o[1]], o[3]))
3249
3245
3250 try:
3246 try:
3251 args = fancyopts.fancyopts(args, c, cmdoptions)
3247 args = fancyopts.fancyopts(args, c, cmdoptions)
3252 except fancyopts.getopt.GetoptError, inst:
3248 except fancyopts.getopt.GetoptError, inst:
3253 raise ParseError(cmd, inst)
3249 raise ParseError(cmd, inst)
3254
3250
3255 # separate global options back out
3251 # separate global options back out
3256 for o in globalopts:
3252 for o in globalopts:
3257 n = o[1]
3253 n = o[1]
3258 options[n] = cmdoptions[n]
3254 options[n] = cmdoptions[n]
3259 del cmdoptions[n]
3255 del cmdoptions[n]
3260
3256
3261 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3257 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3262
3258
3263 external = {}
3259 external = {}
3264
3260
3265 def findext(name):
3261 def findext(name):
3266 '''return module with given extension name'''
3262 '''return module with given extension name'''
3267 try:
3263 try:
3268 return sys.modules[external[name]]
3264 return sys.modules[external[name]]
3269 except KeyError:
3265 except KeyError:
3270 for k, v in external.iteritems():
3266 for k, v in external.iteritems():
3271 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3267 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3272 return sys.modules[v]
3268 return sys.modules[v]
3273 raise KeyError(name)
3269 raise KeyError(name)
3274
3270
3275 def load_extensions(ui):
3271 def load_extensions(ui):
3276 added = []
3272 added = []
3277 for ext_name, load_from_name in ui.extensions():
3273 for ext_name, load_from_name in ui.extensions():
3278 if ext_name in external:
3274 if ext_name in external:
3279 continue
3275 continue
3280 try:
3276 try:
3281 if load_from_name:
3277 if load_from_name:
3282 # the module will be loaded in sys.modules
3278 # the module will be loaded in sys.modules
3283 # choose an unique name so that it doesn't
3279 # choose an unique name so that it doesn't
3284 # conflicts with other modules
3280 # conflicts with other modules
3285 module_name = "hgext_%s" % ext_name.replace('.', '_')
3281 module_name = "hgext_%s" % ext_name.replace('.', '_')
3286 mod = imp.load_source(module_name, load_from_name)
3282 mod = imp.load_source(module_name, load_from_name)
3287 else:
3283 else:
3288 def importh(name):
3284 def importh(name):
3289 mod = __import__(name)
3285 mod = __import__(name)
3290 components = name.split('.')
3286 components = name.split('.')
3291 for comp in components[1:]:
3287 for comp in components[1:]:
3292 mod = getattr(mod, comp)
3288 mod = getattr(mod, comp)
3293 return mod
3289 return mod
3294 try:
3290 try:
3295 mod = importh("hgext.%s" % ext_name)
3291 mod = importh("hgext.%s" % ext_name)
3296 except ImportError:
3292 except ImportError:
3297 mod = importh(ext_name)
3293 mod = importh(ext_name)
3298 external[ext_name] = mod.__name__
3294 external[ext_name] = mod.__name__
3299 added.append((mod, ext_name))
3295 added.append((mod, ext_name))
3300 except (util.SignalInterrupt, KeyboardInterrupt):
3296 except (util.SignalInterrupt, KeyboardInterrupt):
3301 raise
3297 raise
3302 except Exception, inst:
3298 except Exception, inst:
3303 ui.warn(_("*** failed to import extension %s: %s\n") %
3299 ui.warn(_("*** failed to import extension %s: %s\n") %
3304 (ext_name, inst))
3300 (ext_name, inst))
3305 if ui.print_exc():
3301 if ui.print_exc():
3306 return 1
3302 return 1
3307
3303
3308 for mod, name in added:
3304 for mod, name in added:
3309 uisetup = getattr(mod, 'uisetup', None)
3305 uisetup = getattr(mod, 'uisetup', None)
3310 if uisetup:
3306 if uisetup:
3311 uisetup(ui)
3307 uisetup(ui)
3312 cmdtable = getattr(mod, 'cmdtable', {})
3308 cmdtable = getattr(mod, 'cmdtable', {})
3313 for t in cmdtable:
3309 for t in cmdtable:
3314 if t in table:
3310 if t in table:
3315 ui.warn(_("module %s overrides %s\n") % (name, t))
3311 ui.warn(_("module %s overrides %s\n") % (name, t))
3316 table.update(cmdtable)
3312 table.update(cmdtable)
3317
3313
3318 def parseconfig(config):
3314 def parseconfig(config):
3319 """parse the --config options from the command line"""
3315 """parse the --config options from the command line"""
3320 parsed = []
3316 parsed = []
3321 for cfg in config:
3317 for cfg in config:
3322 try:
3318 try:
3323 name, value = cfg.split('=', 1)
3319 name, value = cfg.split('=', 1)
3324 section, name = name.split('.', 1)
3320 section, name = name.split('.', 1)
3325 if not section or not name:
3321 if not section or not name:
3326 raise IndexError
3322 raise IndexError
3327 parsed.append((section, name, value))
3323 parsed.append((section, name, value))
3328 except (IndexError, ValueError):
3324 except (IndexError, ValueError):
3329 raise util.Abort(_('malformed --config option: %s') % cfg)
3325 raise util.Abort(_('malformed --config option: %s') % cfg)
3330 return parsed
3326 return parsed
3331
3327
3332 def dispatch(args):
3328 def dispatch(args):
3333 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3329 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3334 num = getattr(signal, name, None)
3330 num = getattr(signal, name, None)
3335 if num: signal.signal(num, catchterm)
3331 if num: signal.signal(num, catchterm)
3336
3332
3337 try:
3333 try:
3338 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3334 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3339 except util.Abort, inst:
3335 except util.Abort, inst:
3340 sys.stderr.write(_("abort: %s\n") % inst)
3336 sys.stderr.write(_("abort: %s\n") % inst)
3341 return -1
3337 return -1
3342
3338
3343 load_extensions(u)
3339 load_extensions(u)
3344 u.addreadhook(load_extensions)
3340 u.addreadhook(load_extensions)
3345
3341
3346 try:
3342 try:
3347 cmd, func, args, options, cmdoptions = parse(u, args)
3343 cmd, func, args, options, cmdoptions = parse(u, args)
3348 if options["time"]:
3344 if options["time"]:
3349 def get_times():
3345 def get_times():
3350 t = os.times()
3346 t = os.times()
3351 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3347 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3352 t = (t[0], t[1], t[2], t[3], time.clock())
3348 t = (t[0], t[1], t[2], t[3], time.clock())
3353 return t
3349 return t
3354 s = get_times()
3350 s = get_times()
3355 def print_time():
3351 def print_time():
3356 t = get_times()
3352 t = get_times()
3357 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3353 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3358 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3354 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3359 atexit.register(print_time)
3355 atexit.register(print_time)
3360
3356
3361 # enter the debugger before command execution
3357 # enter the debugger before command execution
3362 if options['debugger']:
3358 if options['debugger']:
3363 pdb.set_trace()
3359 pdb.set_trace()
3364
3360
3365 try:
3361 try:
3366 if options['cwd']:
3362 if options['cwd']:
3367 try:
3363 try:
3368 os.chdir(options['cwd'])
3364 os.chdir(options['cwd'])
3369 except OSError, inst:
3365 except OSError, inst:
3370 raise util.Abort('%s: %s' %
3366 raise util.Abort('%s: %s' %
3371 (options['cwd'], inst.strerror))
3367 (options['cwd'], inst.strerror))
3372
3368
3373 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3369 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3374 not options["noninteractive"], options["traceback"],
3370 not options["noninteractive"], options["traceback"],
3375 parseconfig(options["config"]))
3371 parseconfig(options["config"]))
3376
3372
3377 path = u.expandpath(options["repository"]) or ""
3373 path = u.expandpath(options["repository"]) or ""
3378 repo = path and hg.repository(u, path=path) or None
3374 repo = path and hg.repository(u, path=path) or None
3379 if repo and not repo.local():
3375 if repo and not repo.local():
3380 raise util.Abort(_("repository '%s' is not local") % path)
3376 raise util.Abort(_("repository '%s' is not local") % path)
3381
3377
3382 if options['help']:
3378 if options['help']:
3383 return help_(u, cmd, options['version'])
3379 return help_(u, cmd, options['version'])
3384 elif options['version']:
3380 elif options['version']:
3385 return show_version(u)
3381 return show_version(u)
3386 elif not cmd:
3382 elif not cmd:
3387 return help_(u, 'shortlist')
3383 return help_(u, 'shortlist')
3388
3384
3389 if cmd not in norepo.split():
3385 if cmd not in norepo.split():
3390 try:
3386 try:
3391 if not repo:
3387 if not repo:
3392 repo = hg.repository(u, path=path)
3388 repo = hg.repository(u, path=path)
3393 u = repo.ui
3389 u = repo.ui
3394 for name in external.itervalues():
3390 for name in external.itervalues():
3395 mod = sys.modules[name]
3391 mod = sys.modules[name]
3396 if hasattr(mod, 'reposetup'):
3392 if hasattr(mod, 'reposetup'):
3397 mod.reposetup(u, repo)
3393 mod.reposetup(u, repo)
3398 hg.repo_setup_hooks.append(mod.reposetup)
3394 hg.repo_setup_hooks.append(mod.reposetup)
3399 except hg.RepoError:
3395 except hg.RepoError:
3400 if cmd not in optionalrepo.split():
3396 if cmd not in optionalrepo.split():
3401 raise
3397 raise
3402 d = lambda: func(u, repo, *args, **cmdoptions)
3398 d = lambda: func(u, repo, *args, **cmdoptions)
3403 else:
3399 else:
3404 d = lambda: func(u, *args, **cmdoptions)
3400 d = lambda: func(u, *args, **cmdoptions)
3405
3401
3406 try:
3402 try:
3407 if options['profile']:
3403 if options['profile']:
3408 import hotshot, hotshot.stats
3404 import hotshot, hotshot.stats
3409 prof = hotshot.Profile("hg.prof")
3405 prof = hotshot.Profile("hg.prof")
3410 try:
3406 try:
3411 try:
3407 try:
3412 return prof.runcall(d)
3408 return prof.runcall(d)
3413 except:
3409 except:
3414 try:
3410 try:
3415 u.warn(_('exception raised - generating '
3411 u.warn(_('exception raised - generating '
3416 'profile anyway\n'))
3412 'profile anyway\n'))
3417 except:
3413 except:
3418 pass
3414 pass
3419 raise
3415 raise
3420 finally:
3416 finally:
3421 prof.close()
3417 prof.close()
3422 stats = hotshot.stats.load("hg.prof")
3418 stats = hotshot.stats.load("hg.prof")
3423 stats.strip_dirs()
3419 stats.strip_dirs()
3424 stats.sort_stats('time', 'calls')
3420 stats.sort_stats('time', 'calls')
3425 stats.print_stats(40)
3421 stats.print_stats(40)
3426 elif options['lsprof']:
3422 elif options['lsprof']:
3427 try:
3423 try:
3428 from mercurial import lsprof
3424 from mercurial import lsprof
3429 except ImportError:
3425 except ImportError:
3430 raise util.Abort(_(
3426 raise util.Abort(_(
3431 'lsprof not available - install from '
3427 'lsprof not available - install from '
3432 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3428 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3433 p = lsprof.Profiler()
3429 p = lsprof.Profiler()
3434 p.enable(subcalls=True)
3430 p.enable(subcalls=True)
3435 try:
3431 try:
3436 return d()
3432 return d()
3437 finally:
3433 finally:
3438 p.disable()
3434 p.disable()
3439 stats = lsprof.Stats(p.getstats())
3435 stats = lsprof.Stats(p.getstats())
3440 stats.sort()
3436 stats.sort()
3441 stats.pprint(top=10, file=sys.stderr, climit=5)
3437 stats.pprint(top=10, file=sys.stderr, climit=5)
3442 else:
3438 else:
3443 return d()
3439 return d()
3444 finally:
3440 finally:
3445 u.flush()
3441 u.flush()
3446 except:
3442 except:
3447 # enter the debugger when we hit an exception
3443 # enter the debugger when we hit an exception
3448 if options['debugger']:
3444 if options['debugger']:
3449 pdb.post_mortem(sys.exc_info()[2])
3445 pdb.post_mortem(sys.exc_info()[2])
3450 u.print_exc()
3446 u.print_exc()
3451 raise
3447 raise
3452 except ParseError, inst:
3448 except ParseError, inst:
3453 if inst.args[0]:
3449 if inst.args[0]:
3454 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3450 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3455 help_(u, inst.args[0])
3451 help_(u, inst.args[0])
3456 else:
3452 else:
3457 u.warn(_("hg: %s\n") % inst.args[1])
3453 u.warn(_("hg: %s\n") % inst.args[1])
3458 help_(u, 'shortlist')
3454 help_(u, 'shortlist')
3459 except AmbiguousCommand, inst:
3455 except AmbiguousCommand, inst:
3460 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3456 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3461 (inst.args[0], " ".join(inst.args[1])))
3457 (inst.args[0], " ".join(inst.args[1])))
3462 except UnknownCommand, inst:
3458 except UnknownCommand, inst:
3463 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3459 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3464 help_(u, 'shortlist')
3460 help_(u, 'shortlist')
3465 except hg.RepoError, inst:
3461 except hg.RepoError, inst:
3466 u.warn(_("abort: %s!\n") % inst)
3462 u.warn(_("abort: %s!\n") % inst)
3467 except lock.LockHeld, inst:
3463 except lock.LockHeld, inst:
3468 if inst.errno == errno.ETIMEDOUT:
3464 if inst.errno == errno.ETIMEDOUT:
3469 reason = _('timed out waiting for lock held by %s') % inst.locker
3465 reason = _('timed out waiting for lock held by %s') % inst.locker
3470 else:
3466 else:
3471 reason = _('lock held by %s') % inst.locker
3467 reason = _('lock held by %s') % inst.locker
3472 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3468 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3473 except lock.LockUnavailable, inst:
3469 except lock.LockUnavailable, inst:
3474 u.warn(_("abort: could not lock %s: %s\n") %
3470 u.warn(_("abort: could not lock %s: %s\n") %
3475 (inst.desc or inst.filename, inst.strerror))
3471 (inst.desc or inst.filename, inst.strerror))
3476 except revlog.RevlogError, inst:
3472 except revlog.RevlogError, inst:
3477 u.warn(_("abort: %s!\n") % inst)
3473 u.warn(_("abort: %s!\n") % inst)
3478 except util.SignalInterrupt:
3474 except util.SignalInterrupt:
3479 u.warn(_("killed!\n"))
3475 u.warn(_("killed!\n"))
3480 except KeyboardInterrupt:
3476 except KeyboardInterrupt:
3481 try:
3477 try:
3482 u.warn(_("interrupted!\n"))
3478 u.warn(_("interrupted!\n"))
3483 except IOError, inst:
3479 except IOError, inst:
3484 if inst.errno == errno.EPIPE:
3480 if inst.errno == errno.EPIPE:
3485 if u.debugflag:
3481 if u.debugflag:
3486 u.warn(_("\nbroken pipe\n"))
3482 u.warn(_("\nbroken pipe\n"))
3487 else:
3483 else:
3488 raise
3484 raise
3489 except IOError, inst:
3485 except IOError, inst:
3490 if hasattr(inst, "code"):
3486 if hasattr(inst, "code"):
3491 u.warn(_("abort: %s\n") % inst)
3487 u.warn(_("abort: %s\n") % inst)
3492 elif hasattr(inst, "reason"):
3488 elif hasattr(inst, "reason"):
3493 u.warn(_("abort: error: %s\n") % inst.reason[1])
3489 u.warn(_("abort: error: %s\n") % inst.reason[1])
3494 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3490 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3495 if u.debugflag:
3491 if u.debugflag:
3496 u.warn(_("broken pipe\n"))
3492 u.warn(_("broken pipe\n"))
3497 elif getattr(inst, "strerror", None):
3493 elif getattr(inst, "strerror", None):
3498 if getattr(inst, "filename", None):
3494 if getattr(inst, "filename", None):
3499 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3495 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3500 else:
3496 else:
3501 u.warn(_("abort: %s\n") % inst.strerror)
3497 u.warn(_("abort: %s\n") % inst.strerror)
3502 else:
3498 else:
3503 raise
3499 raise
3504 except OSError, inst:
3500 except OSError, inst:
3505 if getattr(inst, "filename", None):
3501 if getattr(inst, "filename", None):
3506 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3502 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3507 else:
3503 else:
3508 u.warn(_("abort: %s\n") % inst.strerror)
3504 u.warn(_("abort: %s\n") % inst.strerror)
3509 except util.UnexpectedOutput, inst:
3505 except util.UnexpectedOutput, inst:
3510 u.warn(_("abort: %s") % inst[0])
3506 u.warn(_("abort: %s") % inst[0])
3511 if not isinstance(inst[1], basestring):
3507 if not isinstance(inst[1], basestring):
3512 u.warn(" %r\n" % (inst[1],))
3508 u.warn(" %r\n" % (inst[1],))
3513 elif not inst[1]:
3509 elif not inst[1]:
3514 u.warn(_(" empty string\n"))
3510 u.warn(_(" empty string\n"))
3515 else:
3511 else:
3516 u.warn("\n%r%s\n" %
3512 u.warn("\n%r%s\n" %
3517 (inst[1][:400], len(inst[1]) > 400 and '...' or ''))
3513 (inst[1][:400], len(inst[1]) > 400 and '...' or ''))
3518 except util.Abort, inst:
3514 except util.Abort, inst:
3519 u.warn(_("abort: %s\n") % inst)
3515 u.warn(_("abort: %s\n") % inst)
3520 except TypeError, inst:
3516 except TypeError, inst:
3521 # was this an argument error?
3517 # was this an argument error?
3522 tb = traceback.extract_tb(sys.exc_info()[2])
3518 tb = traceback.extract_tb(sys.exc_info()[2])
3523 if len(tb) > 2: # no
3519 if len(tb) > 2: # no
3524 raise
3520 raise
3525 u.debug(inst, "\n")
3521 u.debug(inst, "\n")
3526 u.warn(_("%s: invalid arguments\n") % cmd)
3522 u.warn(_("%s: invalid arguments\n") % cmd)
3527 help_(u, cmd)
3523 help_(u, cmd)
3528 except SystemExit, inst:
3524 except SystemExit, inst:
3529 # Commands shouldn't sys.exit directly, but give a return code.
3525 # Commands shouldn't sys.exit directly, but give a return code.
3530 # Just in case catch this and and pass exit code to caller.
3526 # Just in case catch this and and pass exit code to caller.
3531 return inst.code
3527 return inst.code
3532 except:
3528 except:
3533 u.warn(_("** unknown exception encountered, details follow\n"))
3529 u.warn(_("** unknown exception encountered, details follow\n"))
3534 u.warn(_("** report bug details to "
3530 u.warn(_("** report bug details to "
3535 "http://www.selenic.com/mercurial/bts\n"))
3531 "http://www.selenic.com/mercurial/bts\n"))
3536 u.warn(_("** or mercurial@selenic.com\n"))
3532 u.warn(_("** or mercurial@selenic.com\n"))
3537 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3533 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3538 % version.get_version())
3534 % version.get_version())
3539 raise
3535 raise
3540
3536
3541 return -1
3537 return -1
@@ -1,657 +1,654 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from node import *
10 from node import *
11 demandload(globals(), "base85 cmdutil mdiff util")
11 demandload(globals(), "base85 cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha")
12 demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha")
13 demandload(globals(), "sys tempfile zlib")
13 demandload(globals(), "sys tempfile zlib")
14
14
15 # helper functions
15 # helper functions
16
16
17 def copyfile(src, dst, basedir=None):
17 def copyfile(src, dst, basedir=None):
18 if not basedir:
18 if not basedir:
19 basedir = os.getcwd()
19 basedir = os.getcwd()
20
20
21 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
22 if os.path.exists(absdst):
22 if os.path.exists(absdst):
23 raise util.Abort(_("cannot create %s: destination already exists") %
23 raise util.Abort(_("cannot create %s: destination already exists") %
24 dst)
24 dst)
25
25
26 targetdir = os.path.dirname(absdst)
26 targetdir = os.path.dirname(absdst)
27 if not os.path.isdir(targetdir):
27 if not os.path.isdir(targetdir):
28 os.makedirs(targetdir)
28 os.makedirs(targetdir)
29 try:
29
30 shutil.copyfile(abssrc, absdst)
30 util.copyfile(abssrc, absdst)
31 shutil.copymode(abssrc, absdst)
32 except shutil.Error, inst:
33 raise util.Abort(str(inst))
34
31
35 # public functions
32 # public functions
36
33
37 def extract(ui, fileobj):
34 def extract(ui, fileobj):
38 '''extract patch from data read from fileobj.
35 '''extract patch from data read from fileobj.
39
36
40 patch can be normal patch or contained in email message.
37 patch can be normal patch or contained in email message.
41
38
42 return tuple (filename, message, user, date). any item in returned
39 return tuple (filename, message, user, date). any item in returned
43 tuple can be None. if filename is None, fileobj did not contain
40 tuple can be None. if filename is None, fileobj did not contain
44 patch. caller must unlink filename when done.'''
41 patch. caller must unlink filename when done.'''
45
42
46 # attempt to detect the start of a patch
43 # attempt to detect the start of a patch
47 # (this heuristic is borrowed from quilt)
44 # (this heuristic is borrowed from quilt)
48 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
45 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
49 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
46 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
50 '(---|\*\*\*)[ \t])', re.MULTILINE)
47 '(---|\*\*\*)[ \t])', re.MULTILINE)
51
48
52 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
49 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
53 tmpfp = os.fdopen(fd, 'w')
50 tmpfp = os.fdopen(fd, 'w')
54 try:
51 try:
55 hgpatch = False
52 hgpatch = False
56
53
57 msg = email.Parser.Parser().parse(fileobj)
54 msg = email.Parser.Parser().parse(fileobj)
58
55
59 message = msg['Subject']
56 message = msg['Subject']
60 user = msg['From']
57 user = msg['From']
61 # should try to parse msg['Date']
58 # should try to parse msg['Date']
62 date = None
59 date = None
63
60
64 if message:
61 if message:
65 message = message.replace('\n\t', ' ')
62 message = message.replace('\n\t', ' ')
66 ui.debug('Subject: %s\n' % message)
63 ui.debug('Subject: %s\n' % message)
67 if user:
64 if user:
68 ui.debug('From: %s\n' % user)
65 ui.debug('From: %s\n' % user)
69 diffs_seen = 0
66 diffs_seen = 0
70 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
67 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
71
68
72 for part in msg.walk():
69 for part in msg.walk():
73 content_type = part.get_content_type()
70 content_type = part.get_content_type()
74 ui.debug('Content-Type: %s\n' % content_type)
71 ui.debug('Content-Type: %s\n' % content_type)
75 if content_type not in ok_types:
72 if content_type not in ok_types:
76 continue
73 continue
77 payload = part.get_payload(decode=True)
74 payload = part.get_payload(decode=True)
78 m = diffre.search(payload)
75 m = diffre.search(payload)
79 if m:
76 if m:
80 ui.debug(_('found patch at byte %d\n') % m.start(0))
77 ui.debug(_('found patch at byte %d\n') % m.start(0))
81 diffs_seen += 1
78 diffs_seen += 1
82 cfp = cStringIO.StringIO()
79 cfp = cStringIO.StringIO()
83 if message:
80 if message:
84 cfp.write(message)
81 cfp.write(message)
85 cfp.write('\n')
82 cfp.write('\n')
86 for line in payload[:m.start(0)].splitlines():
83 for line in payload[:m.start(0)].splitlines():
87 if line.startswith('# HG changeset patch'):
84 if line.startswith('# HG changeset patch'):
88 ui.debug(_('patch generated by hg export\n'))
85 ui.debug(_('patch generated by hg export\n'))
89 hgpatch = True
86 hgpatch = True
90 # drop earlier commit message content
87 # drop earlier commit message content
91 cfp.seek(0)
88 cfp.seek(0)
92 cfp.truncate()
89 cfp.truncate()
93 elif hgpatch:
90 elif hgpatch:
94 if line.startswith('# User '):
91 if line.startswith('# User '):
95 user = line[7:]
92 user = line[7:]
96 ui.debug('From: %s\n' % user)
93 ui.debug('From: %s\n' % user)
97 elif line.startswith("# Date "):
94 elif line.startswith("# Date "):
98 date = line[7:]
95 date = line[7:]
99 if not line.startswith('# '):
96 if not line.startswith('# '):
100 cfp.write(line)
97 cfp.write(line)
101 cfp.write('\n')
98 cfp.write('\n')
102 message = cfp.getvalue()
99 message = cfp.getvalue()
103 if tmpfp:
100 if tmpfp:
104 tmpfp.write(payload)
101 tmpfp.write(payload)
105 if not payload.endswith('\n'):
102 if not payload.endswith('\n'):
106 tmpfp.write('\n')
103 tmpfp.write('\n')
107 elif not diffs_seen and message and content_type == 'text/plain':
104 elif not diffs_seen and message and content_type == 'text/plain':
108 message += '\n' + payload
105 message += '\n' + payload
109 except:
106 except:
110 tmpfp.close()
107 tmpfp.close()
111 os.unlink(tmpname)
108 os.unlink(tmpname)
112 raise
109 raise
113
110
114 tmpfp.close()
111 tmpfp.close()
115 if not diffs_seen:
112 if not diffs_seen:
116 os.unlink(tmpname)
113 os.unlink(tmpname)
117 return None, message, user, date
114 return None, message, user, date
118 return tmpname, message, user, date
115 return tmpname, message, user, date
119
116
120 def readgitpatch(patchname):
117 def readgitpatch(patchname):
121 """extract git-style metadata about patches from <patchname>"""
118 """extract git-style metadata about patches from <patchname>"""
122 class gitpatch:
119 class gitpatch:
123 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
120 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
124 def __init__(self, path):
121 def __init__(self, path):
125 self.path = path
122 self.path = path
126 self.oldpath = None
123 self.oldpath = None
127 self.mode = None
124 self.mode = None
128 self.op = 'MODIFY'
125 self.op = 'MODIFY'
129 self.copymod = False
126 self.copymod = False
130 self.lineno = 0
127 self.lineno = 0
131 self.binary = False
128 self.binary = False
132
129
133 # Filter patch for git information
130 # Filter patch for git information
134 gitre = re.compile('diff --git a/(.*) b/(.*)')
131 gitre = re.compile('diff --git a/(.*) b/(.*)')
135 pf = file(patchname)
132 pf = file(patchname)
136 gp = None
133 gp = None
137 gitpatches = []
134 gitpatches = []
138 # Can have a git patch with only metadata, causing patch to complain
135 # Can have a git patch with only metadata, causing patch to complain
139 dopatch = False
136 dopatch = False
140
137
141 lineno = 0
138 lineno = 0
142 for line in pf:
139 for line in pf:
143 lineno += 1
140 lineno += 1
144 if line.startswith('diff --git'):
141 if line.startswith('diff --git'):
145 m = gitre.match(line)
142 m = gitre.match(line)
146 if m:
143 if m:
147 if gp:
144 if gp:
148 gitpatches.append(gp)
145 gitpatches.append(gp)
149 src, dst = m.group(1,2)
146 src, dst = m.group(1,2)
150 gp = gitpatch(dst)
147 gp = gitpatch(dst)
151 gp.lineno = lineno
148 gp.lineno = lineno
152 elif gp:
149 elif gp:
153 if line.startswith('--- '):
150 if line.startswith('--- '):
154 if gp.op in ('COPY', 'RENAME'):
151 if gp.op in ('COPY', 'RENAME'):
155 gp.copymod = True
152 gp.copymod = True
156 dopatch = 'filter'
153 dopatch = 'filter'
157 gitpatches.append(gp)
154 gitpatches.append(gp)
158 gp = None
155 gp = None
159 if not dopatch:
156 if not dopatch:
160 dopatch = True
157 dopatch = True
161 continue
158 continue
162 if line.startswith('rename from '):
159 if line.startswith('rename from '):
163 gp.op = 'RENAME'
160 gp.op = 'RENAME'
164 gp.oldpath = line[12:].rstrip()
161 gp.oldpath = line[12:].rstrip()
165 elif line.startswith('rename to '):
162 elif line.startswith('rename to '):
166 gp.path = line[10:].rstrip()
163 gp.path = line[10:].rstrip()
167 elif line.startswith('copy from '):
164 elif line.startswith('copy from '):
168 gp.op = 'COPY'
165 gp.op = 'COPY'
169 gp.oldpath = line[10:].rstrip()
166 gp.oldpath = line[10:].rstrip()
170 elif line.startswith('copy to '):
167 elif line.startswith('copy to '):
171 gp.path = line[8:].rstrip()
168 gp.path = line[8:].rstrip()
172 elif line.startswith('deleted file'):
169 elif line.startswith('deleted file'):
173 gp.op = 'DELETE'
170 gp.op = 'DELETE'
174 elif line.startswith('new file mode '):
171 elif line.startswith('new file mode '):
175 gp.op = 'ADD'
172 gp.op = 'ADD'
176 gp.mode = int(line.rstrip()[-3:], 8)
173 gp.mode = int(line.rstrip()[-3:], 8)
177 elif line.startswith('new mode '):
174 elif line.startswith('new mode '):
178 gp.mode = int(line.rstrip()[-3:], 8)
175 gp.mode = int(line.rstrip()[-3:], 8)
179 elif line.startswith('GIT binary patch'):
176 elif line.startswith('GIT binary patch'):
180 if not dopatch:
177 if not dopatch:
181 dopatch = 'binary'
178 dopatch = 'binary'
182 gp.binary = True
179 gp.binary = True
183 if gp:
180 if gp:
184 gitpatches.append(gp)
181 gitpatches.append(gp)
185
182
186 if not gitpatches:
183 if not gitpatches:
187 dopatch = True
184 dopatch = True
188
185
189 return (dopatch, gitpatches)
186 return (dopatch, gitpatches)
190
187
191 def dogitpatch(patchname, gitpatches, cwd=None):
188 def dogitpatch(patchname, gitpatches, cwd=None):
192 """Preprocess git patch so that vanilla patch can handle it"""
189 """Preprocess git patch so that vanilla patch can handle it"""
193 def extractbin(fp):
190 def extractbin(fp):
194 line = fp.readline().rstrip()
191 line = fp.readline().rstrip()
195 while line and not line.startswith('literal '):
192 while line and not line.startswith('literal '):
196 line = fp.readline().rstrip()
193 line = fp.readline().rstrip()
197 if not line:
194 if not line:
198 return
195 return
199 size = int(line[8:])
196 size = int(line[8:])
200 dec = []
197 dec = []
201 line = fp.readline().rstrip()
198 line = fp.readline().rstrip()
202 while line:
199 while line:
203 l = line[0]
200 l = line[0]
204 if l <= 'Z' and l >= 'A':
201 if l <= 'Z' and l >= 'A':
205 l = ord(l) - ord('A') + 1
202 l = ord(l) - ord('A') + 1
206 else:
203 else:
207 l = ord(l) - ord('a') + 27
204 l = ord(l) - ord('a') + 27
208 dec.append(base85.b85decode(line[1:])[:l])
205 dec.append(base85.b85decode(line[1:])[:l])
209 line = fp.readline().rstrip()
206 line = fp.readline().rstrip()
210 text = zlib.decompress(''.join(dec))
207 text = zlib.decompress(''.join(dec))
211 if len(text) != size:
208 if len(text) != size:
212 raise util.Abort(_('binary patch is %d bytes, not %d') %
209 raise util.Abort(_('binary patch is %d bytes, not %d') %
213 (len(text), size))
210 (len(text), size))
214 return text
211 return text
215
212
216 pf = file(patchname)
213 pf = file(patchname)
217 pfline = 1
214 pfline = 1
218
215
219 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
216 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
220 tmpfp = os.fdopen(fd, 'w')
217 tmpfp = os.fdopen(fd, 'w')
221
218
222 try:
219 try:
223 for i in xrange(len(gitpatches)):
220 for i in xrange(len(gitpatches)):
224 p = gitpatches[i]
221 p = gitpatches[i]
225 if not p.copymod and not p.binary:
222 if not p.copymod and not p.binary:
226 continue
223 continue
227
224
228 # rewrite patch hunk
225 # rewrite patch hunk
229 while pfline < p.lineno:
226 while pfline < p.lineno:
230 tmpfp.write(pf.readline())
227 tmpfp.write(pf.readline())
231 pfline += 1
228 pfline += 1
232
229
233 if p.binary:
230 if p.binary:
234 text = extractbin(pf)
231 text = extractbin(pf)
235 if not text:
232 if not text:
236 raise util.Abort(_('binary patch extraction failed'))
233 raise util.Abort(_('binary patch extraction failed'))
237 if not cwd:
234 if not cwd:
238 cwd = os.getcwd()
235 cwd = os.getcwd()
239 absdst = os.path.join(cwd, p.path)
236 absdst = os.path.join(cwd, p.path)
240 basedir = os.path.dirname(absdst)
237 basedir = os.path.dirname(absdst)
241 if not os.path.isdir(basedir):
238 if not os.path.isdir(basedir):
242 os.makedirs(basedir)
239 os.makedirs(basedir)
243 out = file(absdst, 'wb')
240 out = file(absdst, 'wb')
244 out.write(text)
241 out.write(text)
245 out.close()
242 out.close()
246 elif p.copymod:
243 elif p.copymod:
247 copyfile(p.oldpath, p.path, basedir=cwd)
244 copyfile(p.oldpath, p.path, basedir=cwd)
248 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
245 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
249 line = pf.readline()
246 line = pf.readline()
250 pfline += 1
247 pfline += 1
251 while not line.startswith('--- a/'):
248 while not line.startswith('--- a/'):
252 tmpfp.write(line)
249 tmpfp.write(line)
253 line = pf.readline()
250 line = pf.readline()
254 pfline += 1
251 pfline += 1
255 tmpfp.write('--- a/%s\n' % p.path)
252 tmpfp.write('--- a/%s\n' % p.path)
256
253
257 line = pf.readline()
254 line = pf.readline()
258 while line:
255 while line:
259 tmpfp.write(line)
256 tmpfp.write(line)
260 line = pf.readline()
257 line = pf.readline()
261 except:
258 except:
262 tmpfp.close()
259 tmpfp.close()
263 os.unlink(patchname)
260 os.unlink(patchname)
264 raise
261 raise
265
262
266 tmpfp.close()
263 tmpfp.close()
267 return patchname
264 return patchname
268
265
269 def patch(patchname, ui, strip=1, cwd=None, files={}):
266 def patch(patchname, ui, strip=1, cwd=None, files={}):
270 """apply the patch <patchname> to the working directory.
267 """apply the patch <patchname> to the working directory.
271 a list of patched files is returned"""
268 a list of patched files is returned"""
272
269
273 # helper function
270 # helper function
274 def __patch(patchname):
271 def __patch(patchname):
275 """patch and updates the files and fuzz variables"""
272 """patch and updates the files and fuzz variables"""
276 fuzz = False
273 fuzz = False
277
274
278 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
275 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
279 'patch')
276 'patch')
280 args = []
277 args = []
281 if cwd:
278 if cwd:
282 args.append('-d %s' % util.shellquote(cwd))
279 args.append('-d %s' % util.shellquote(cwd))
283 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
280 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
284 util.shellquote(patchname)))
281 util.shellquote(patchname)))
285
282
286 for line in fp:
283 for line in fp:
287 line = line.rstrip()
284 line = line.rstrip()
288 ui.note(line + '\n')
285 ui.note(line + '\n')
289 if line.startswith('patching file '):
286 if line.startswith('patching file '):
290 pf = util.parse_patch_output(line)
287 pf = util.parse_patch_output(line)
291 printed_file = False
288 printed_file = False
292 files.setdefault(pf, (None, None))
289 files.setdefault(pf, (None, None))
293 elif line.find('with fuzz') >= 0:
290 elif line.find('with fuzz') >= 0:
294 fuzz = True
291 fuzz = True
295 if not printed_file:
292 if not printed_file:
296 ui.warn(pf + '\n')
293 ui.warn(pf + '\n')
297 printed_file = True
294 printed_file = True
298 ui.warn(line + '\n')
295 ui.warn(line + '\n')
299 elif line.find('saving rejects to file') >= 0:
296 elif line.find('saving rejects to file') >= 0:
300 ui.warn(line + '\n')
297 ui.warn(line + '\n')
301 elif line.find('FAILED') >= 0:
298 elif line.find('FAILED') >= 0:
302 if not printed_file:
299 if not printed_file:
303 ui.warn(pf + '\n')
300 ui.warn(pf + '\n')
304 printed_file = True
301 printed_file = True
305 ui.warn(line + '\n')
302 ui.warn(line + '\n')
306 code = fp.close()
303 code = fp.close()
307 if code:
304 if code:
308 raise util.Abort(_("patch command failed: %s") %
305 raise util.Abort(_("patch command failed: %s") %
309 util.explain_exit(code)[0])
306 util.explain_exit(code)[0])
310 return fuzz
307 return fuzz
311
308
312 (dopatch, gitpatches) = readgitpatch(patchname)
309 (dopatch, gitpatches) = readgitpatch(patchname)
313 for gp in gitpatches:
310 for gp in gitpatches:
314 files[gp.path] = (gp.op, gp)
311 files[gp.path] = (gp.op, gp)
315
312
316 fuzz = False
313 fuzz = False
317 if dopatch:
314 if dopatch:
318 if dopatch in ('filter', 'binary'):
315 if dopatch in ('filter', 'binary'):
319 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
316 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
320 try:
317 try:
321 if dopatch != 'binary':
318 if dopatch != 'binary':
322 fuzz = __patch(patchname)
319 fuzz = __patch(patchname)
323 finally:
320 finally:
324 if dopatch == 'filter':
321 if dopatch == 'filter':
325 os.unlink(patchname)
322 os.unlink(patchname)
326
323
327 return fuzz
324 return fuzz
328
325
329 def diffopts(ui, opts={}, untrusted=False):
326 def diffopts(ui, opts={}, untrusted=False):
330 def get(key, name=None):
327 def get(key, name=None):
331 return (opts.get(key) or
328 return (opts.get(key) or
332 ui.configbool('diff', name or key, None, untrusted=untrusted))
329 ui.configbool('diff', name or key, None, untrusted=untrusted))
333 return mdiff.diffopts(
330 return mdiff.diffopts(
334 text=opts.get('text'),
331 text=opts.get('text'),
335 git=get('git'),
332 git=get('git'),
336 nodates=get('nodates'),
333 nodates=get('nodates'),
337 showfunc=get('show_function', 'showfunc'),
334 showfunc=get('show_function', 'showfunc'),
338 ignorews=get('ignore_all_space', 'ignorews'),
335 ignorews=get('ignore_all_space', 'ignorews'),
339 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
336 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
340 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
337 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
341
338
342 def updatedir(ui, repo, patches, wlock=None):
339 def updatedir(ui, repo, patches, wlock=None):
343 '''Update dirstate after patch application according to metadata'''
340 '''Update dirstate after patch application according to metadata'''
344 if not patches:
341 if not patches:
345 return
342 return
346 copies = []
343 copies = []
347 removes = []
344 removes = []
348 cfiles = patches.keys()
345 cfiles = patches.keys()
349 cwd = repo.getcwd()
346 cwd = repo.getcwd()
350 if cwd:
347 if cwd:
351 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
348 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
352 for f in patches:
349 for f in patches:
353 ctype, gp = patches[f]
350 ctype, gp = patches[f]
354 if ctype == 'RENAME':
351 if ctype == 'RENAME':
355 copies.append((gp.oldpath, gp.path, gp.copymod))
352 copies.append((gp.oldpath, gp.path, gp.copymod))
356 removes.append(gp.oldpath)
353 removes.append(gp.oldpath)
357 elif ctype == 'COPY':
354 elif ctype == 'COPY':
358 copies.append((gp.oldpath, gp.path, gp.copymod))
355 copies.append((gp.oldpath, gp.path, gp.copymod))
359 elif ctype == 'DELETE':
356 elif ctype == 'DELETE':
360 removes.append(gp.path)
357 removes.append(gp.path)
361 for src, dst, after in copies:
358 for src, dst, after in copies:
362 if not after:
359 if not after:
363 copyfile(src, dst, repo.root)
360 copyfile(src, dst, repo.root)
364 repo.copy(src, dst, wlock=wlock)
361 repo.copy(src, dst, wlock=wlock)
365 if removes:
362 if removes:
366 repo.remove(removes, True, wlock=wlock)
363 repo.remove(removes, True, wlock=wlock)
367 for f in patches:
364 for f in patches:
368 ctype, gp = patches[f]
365 ctype, gp = patches[f]
369 if gp and gp.mode:
366 if gp and gp.mode:
370 x = gp.mode & 0100 != 0
367 x = gp.mode & 0100 != 0
371 dst = os.path.join(repo.root, gp.path)
368 dst = os.path.join(repo.root, gp.path)
372 # patch won't create empty files
369 # patch won't create empty files
373 if ctype == 'ADD' and not os.path.exists(dst):
370 if ctype == 'ADD' and not os.path.exists(dst):
374 repo.wwrite(gp.path, '')
371 repo.wwrite(gp.path, '')
375 util.set_exec(dst, x)
372 util.set_exec(dst, x)
376 cmdutil.addremove(repo, cfiles, wlock=wlock)
373 cmdutil.addremove(repo, cfiles, wlock=wlock)
377 files = patches.keys()
374 files = patches.keys()
378 files.extend([r for r in removes if r not in files])
375 files.extend([r for r in removes if r not in files])
379 files.sort()
376 files.sort()
380
377
381 return files
378 return files
382
379
383 def b85diff(fp, to, tn):
380 def b85diff(fp, to, tn):
384 '''print base85-encoded binary diff'''
381 '''print base85-encoded binary diff'''
385 def gitindex(text):
382 def gitindex(text):
386 if not text:
383 if not text:
387 return '0' * 40
384 return '0' * 40
388 l = len(text)
385 l = len(text)
389 s = sha.new('blob %d\0' % l)
386 s = sha.new('blob %d\0' % l)
390 s.update(text)
387 s.update(text)
391 return s.hexdigest()
388 return s.hexdigest()
392
389
393 def fmtline(line):
390 def fmtline(line):
394 l = len(line)
391 l = len(line)
395 if l <= 26:
392 if l <= 26:
396 l = chr(ord('A') + l - 1)
393 l = chr(ord('A') + l - 1)
397 else:
394 else:
398 l = chr(l - 26 + ord('a') - 1)
395 l = chr(l - 26 + ord('a') - 1)
399 return '%c%s\n' % (l, base85.b85encode(line, True))
396 return '%c%s\n' % (l, base85.b85encode(line, True))
400
397
401 def chunk(text, csize=52):
398 def chunk(text, csize=52):
402 l = len(text)
399 l = len(text)
403 i = 0
400 i = 0
404 while i < l:
401 while i < l:
405 yield text[i:i+csize]
402 yield text[i:i+csize]
406 i += csize
403 i += csize
407
404
408 # TODO: deltas
405 # TODO: deltas
409 l = len(tn)
406 l = len(tn)
410 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
407 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
411 (gitindex(to), gitindex(tn), len(tn)))
408 (gitindex(to), gitindex(tn), len(tn)))
412
409
413 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
410 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
414 fp.write(tn)
411 fp.write(tn)
415 fp.write('\n')
412 fp.write('\n')
416
413
417 def diff(repo, node1=None, node2=None, files=None, match=util.always,
414 def diff(repo, node1=None, node2=None, files=None, match=util.always,
418 fp=None, changes=None, opts=None):
415 fp=None, changes=None, opts=None):
419 '''print diff of changes to files between two nodes, or node and
416 '''print diff of changes to files between two nodes, or node and
420 working directory.
417 working directory.
421
418
422 if node1 is None, use first dirstate parent instead.
419 if node1 is None, use first dirstate parent instead.
423 if node2 is None, compare node1 with working directory.'''
420 if node2 is None, compare node1 with working directory.'''
424
421
425 if opts is None:
422 if opts is None:
426 opts = mdiff.defaultopts
423 opts = mdiff.defaultopts
427 if fp is None:
424 if fp is None:
428 fp = repo.ui
425 fp = repo.ui
429
426
430 if not node1:
427 if not node1:
431 node1 = repo.dirstate.parents()[0]
428 node1 = repo.dirstate.parents()[0]
432
429
433 clcache = {}
430 clcache = {}
434 def getchangelog(n):
431 def getchangelog(n):
435 if n not in clcache:
432 if n not in clcache:
436 clcache[n] = repo.changelog.read(n)
433 clcache[n] = repo.changelog.read(n)
437 return clcache[n]
434 return clcache[n]
438 mcache = {}
435 mcache = {}
439 def getmanifest(n):
436 def getmanifest(n):
440 if n not in mcache:
437 if n not in mcache:
441 mcache[n] = repo.manifest.read(n)
438 mcache[n] = repo.manifest.read(n)
442 return mcache[n]
439 return mcache[n]
443 fcache = {}
440 fcache = {}
444 def getfile(f):
441 def getfile(f):
445 if f not in fcache:
442 if f not in fcache:
446 fcache[f] = repo.file(f)
443 fcache[f] = repo.file(f)
447 return fcache[f]
444 return fcache[f]
448
445
449 # reading the data for node1 early allows it to play nicely
446 # reading the data for node1 early allows it to play nicely
450 # with repo.status and the revlog cache.
447 # with repo.status and the revlog cache.
451 change = getchangelog(node1)
448 change = getchangelog(node1)
452 mmap = getmanifest(change[0])
449 mmap = getmanifest(change[0])
453 date1 = util.datestr(change[2])
450 date1 = util.datestr(change[2])
454
451
455 if not changes:
452 if not changes:
456 changes = repo.status(node1, node2, files, match=match)[:5]
453 changes = repo.status(node1, node2, files, match=match)[:5]
457 modified, added, removed, deleted, unknown = changes
454 modified, added, removed, deleted, unknown = changes
458 if files:
455 if files:
459 def filterfiles(filters):
456 def filterfiles(filters):
460 l = [x for x in filters if x in files]
457 l = [x for x in filters if x in files]
461
458
462 for t in files:
459 for t in files:
463 if not t.endswith("/"):
460 if not t.endswith("/"):
464 t += "/"
461 t += "/"
465 l += [x for x in filters if x.startswith(t)]
462 l += [x for x in filters if x.startswith(t)]
466 return l
463 return l
467
464
468 modified, added, removed = map(filterfiles, (modified, added, removed))
465 modified, added, removed = map(filterfiles, (modified, added, removed))
469
466
470 if not modified and not added and not removed:
467 if not modified and not added and not removed:
471 return
468 return
472
469
473 def renamedbetween(f, n1, n2):
470 def renamedbetween(f, n1, n2):
474 r1, r2 = map(repo.changelog.rev, (n1, n2))
471 r1, r2 = map(repo.changelog.rev, (n1, n2))
475 src = None
472 src = None
476 while r2 > r1:
473 while r2 > r1:
477 cl = getchangelog(n2)[0]
474 cl = getchangelog(n2)[0]
478 m = getmanifest(cl)
475 m = getmanifest(cl)
479 try:
476 try:
480 src = getfile(f).renamed(m[f])
477 src = getfile(f).renamed(m[f])
481 except KeyError:
478 except KeyError:
482 return None
479 return None
483 if src:
480 if src:
484 f = src[0]
481 f = src[0]
485 n2 = repo.changelog.parents(n2)[0]
482 n2 = repo.changelog.parents(n2)[0]
486 r2 = repo.changelog.rev(n2)
483 r2 = repo.changelog.rev(n2)
487 return src
484 return src
488
485
489 if node2:
486 if node2:
490 change = getchangelog(node2)
487 change = getchangelog(node2)
491 mmap2 = getmanifest(change[0])
488 mmap2 = getmanifest(change[0])
492 _date2 = util.datestr(change[2])
489 _date2 = util.datestr(change[2])
493 def date2(f):
490 def date2(f):
494 return _date2
491 return _date2
495 def read(f):
492 def read(f):
496 return getfile(f).read(mmap2[f])
493 return getfile(f).read(mmap2[f])
497 def renamed(f):
494 def renamed(f):
498 return renamedbetween(f, node1, node2)
495 return renamedbetween(f, node1, node2)
499 else:
496 else:
500 tz = util.makedate()[1]
497 tz = util.makedate()[1]
501 _date2 = util.datestr()
498 _date2 = util.datestr()
502 def date2(f):
499 def date2(f):
503 try:
500 try:
504 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
501 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
505 except OSError, err:
502 except OSError, err:
506 if err.errno != errno.ENOENT: raise
503 if err.errno != errno.ENOENT: raise
507 return _date2
504 return _date2
508 def read(f):
505 def read(f):
509 return repo.wread(f)
506 return repo.wread(f)
510 def renamed(f):
507 def renamed(f):
511 src = repo.dirstate.copied(f)
508 src = repo.dirstate.copied(f)
512 parent = repo.dirstate.parents()[0]
509 parent = repo.dirstate.parents()[0]
513 if src:
510 if src:
514 f = src[0]
511 f = src[0]
515 of = renamedbetween(f, node1, parent)
512 of = renamedbetween(f, node1, parent)
516 if of:
513 if of:
517 return of
514 return of
518 elif src:
515 elif src:
519 cl = getchangelog(parent)[0]
516 cl = getchangelog(parent)[0]
520 return (src, getmanifest(cl)[src])
517 return (src, getmanifest(cl)[src])
521 else:
518 else:
522 return None
519 return None
523
520
524 if repo.ui.quiet:
521 if repo.ui.quiet:
525 r = None
522 r = None
526 else:
523 else:
527 hexfunc = repo.ui.debugflag and hex or short
524 hexfunc = repo.ui.debugflag and hex or short
528 r = [hexfunc(node) for node in [node1, node2] if node]
525 r = [hexfunc(node) for node in [node1, node2] if node]
529
526
530 if opts.git:
527 if opts.git:
531 copied = {}
528 copied = {}
532 for f in added:
529 for f in added:
533 src = renamed(f)
530 src = renamed(f)
534 if src:
531 if src:
535 copied[f] = src
532 copied[f] = src
536 srcs = [x[1][0] for x in copied.items()]
533 srcs = [x[1][0] for x in copied.items()]
537
534
538 all = modified + added + removed
535 all = modified + added + removed
539 all.sort()
536 all.sort()
540 for f in all:
537 for f in all:
541 to = None
538 to = None
542 tn = None
539 tn = None
543 dodiff = True
540 dodiff = True
544 header = []
541 header = []
545 if f in mmap:
542 if f in mmap:
546 to = getfile(f).read(mmap[f])
543 to = getfile(f).read(mmap[f])
547 if f not in removed:
544 if f not in removed:
548 tn = read(f)
545 tn = read(f)
549 if opts.git:
546 if opts.git:
550 def gitmode(x):
547 def gitmode(x):
551 return x and '100755' or '100644'
548 return x and '100755' or '100644'
552 def addmodehdr(header, omode, nmode):
549 def addmodehdr(header, omode, nmode):
553 if omode != nmode:
550 if omode != nmode:
554 header.append('old mode %s\n' % omode)
551 header.append('old mode %s\n' % omode)
555 header.append('new mode %s\n' % nmode)
552 header.append('new mode %s\n' % nmode)
556
553
557 a, b = f, f
554 a, b = f, f
558 if f in added:
555 if f in added:
559 if node2:
556 if node2:
560 mode = gitmode(mmap2.execf(f))
557 mode = gitmode(mmap2.execf(f))
561 else:
558 else:
562 mode = gitmode(util.is_exec(repo.wjoin(f), None))
559 mode = gitmode(util.is_exec(repo.wjoin(f), None))
563 if f in copied:
560 if f in copied:
564 a, arev = copied[f]
561 a, arev = copied[f]
565 omode = gitmode(mmap.execf(a))
562 omode = gitmode(mmap.execf(a))
566 addmodehdr(header, omode, mode)
563 addmodehdr(header, omode, mode)
567 op = a in removed and 'rename' or 'copy'
564 op = a in removed and 'rename' or 'copy'
568 header.append('%s from %s\n' % (op, a))
565 header.append('%s from %s\n' % (op, a))
569 header.append('%s to %s\n' % (op, f))
566 header.append('%s to %s\n' % (op, f))
570 to = getfile(a).read(arev)
567 to = getfile(a).read(arev)
571 else:
568 else:
572 header.append('new file mode %s\n' % mode)
569 header.append('new file mode %s\n' % mode)
573 if util.binary(tn):
570 if util.binary(tn):
574 dodiff = 'binary'
571 dodiff = 'binary'
575 elif f in removed:
572 elif f in removed:
576 if f in srcs:
573 if f in srcs:
577 dodiff = False
574 dodiff = False
578 else:
575 else:
579 mode = gitmode(mmap.execf(f))
576 mode = gitmode(mmap.execf(f))
580 header.append('deleted file mode %s\n' % mode)
577 header.append('deleted file mode %s\n' % mode)
581 else:
578 else:
582 omode = gitmode(mmap.execf(f))
579 omode = gitmode(mmap.execf(f))
583 if node2:
580 if node2:
584 nmode = gitmode(mmap2.execf(f))
581 nmode = gitmode(mmap2.execf(f))
585 else:
582 else:
586 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
583 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
587 addmodehdr(header, omode, nmode)
584 addmodehdr(header, omode, nmode)
588 if util.binary(to) or util.binary(tn):
585 if util.binary(to) or util.binary(tn):
589 dodiff = 'binary'
586 dodiff = 'binary'
590 r = None
587 r = None
591 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
588 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
592 if dodiff == 'binary':
589 if dodiff == 'binary':
593 fp.write(''.join(header))
590 fp.write(''.join(header))
594 b85diff(fp, to, tn)
591 b85diff(fp, to, tn)
595 elif dodiff:
592 elif dodiff:
596 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
593 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
597 if text or len(header) > 1:
594 if text or len(header) > 1:
598 fp.write(''.join(header))
595 fp.write(''.join(header))
599 fp.write(text)
596 fp.write(text)
600
597
601 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
598 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
602 opts=None):
599 opts=None):
603 '''export changesets as hg patches.'''
600 '''export changesets as hg patches.'''
604
601
605 total = len(revs)
602 total = len(revs)
606 revwidth = max(map(len, revs))
603 revwidth = max(map(len, revs))
607
604
608 def single(node, seqno, fp):
605 def single(node, seqno, fp):
609 parents = [p for p in repo.changelog.parents(node) if p != nullid]
606 parents = [p for p in repo.changelog.parents(node) if p != nullid]
610 if switch_parent:
607 if switch_parent:
611 parents.reverse()
608 parents.reverse()
612 prev = (parents and parents[0]) or nullid
609 prev = (parents and parents[0]) or nullid
613 change = repo.changelog.read(node)
610 change = repo.changelog.read(node)
614
611
615 if not fp:
612 if not fp:
616 fp = cmdutil.make_file(repo, template, node, total=total,
613 fp = cmdutil.make_file(repo, template, node, total=total,
617 seqno=seqno, revwidth=revwidth)
614 seqno=seqno, revwidth=revwidth)
618 if fp not in (sys.stdout, repo.ui):
615 if fp not in (sys.stdout, repo.ui):
619 repo.ui.note("%s\n" % fp.name)
616 repo.ui.note("%s\n" % fp.name)
620
617
621 fp.write("# HG changeset patch\n")
618 fp.write("# HG changeset patch\n")
622 fp.write("# User %s\n" % change[1])
619 fp.write("# User %s\n" % change[1])
623 fp.write("# Date %d %d\n" % change[2])
620 fp.write("# Date %d %d\n" % change[2])
624 fp.write("# Node ID %s\n" % hex(node))
621 fp.write("# Node ID %s\n" % hex(node))
625 fp.write("# Parent %s\n" % hex(prev))
622 fp.write("# Parent %s\n" % hex(prev))
626 if len(parents) > 1:
623 if len(parents) > 1:
627 fp.write("# Parent %s\n" % hex(parents[1]))
624 fp.write("# Parent %s\n" % hex(parents[1]))
628 fp.write(change[4].rstrip())
625 fp.write(change[4].rstrip())
629 fp.write("\n\n")
626 fp.write("\n\n")
630
627
631 diff(repo, prev, node, fp=fp, opts=opts)
628 diff(repo, prev, node, fp=fp, opts=opts)
632 if fp not in (sys.stdout, repo.ui):
629 if fp not in (sys.stdout, repo.ui):
633 fp.close()
630 fp.close()
634
631
635 for seqno, cset in enumerate(revs):
632 for seqno, cset in enumerate(revs):
636 single(cset, seqno, fp)
633 single(cset, seqno, fp)
637
634
638 def diffstat(patchlines):
635 def diffstat(patchlines):
639 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
636 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
640 try:
637 try:
641 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
638 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
642 try:
639 try:
643 for line in patchlines: print >> p.tochild, line
640 for line in patchlines: print >> p.tochild, line
644 p.tochild.close()
641 p.tochild.close()
645 if p.wait(): return
642 if p.wait(): return
646 fp = os.fdopen(fd, 'r')
643 fp = os.fdopen(fd, 'r')
647 stat = []
644 stat = []
648 for line in fp: stat.append(line.lstrip())
645 for line in fp: stat.append(line.lstrip())
649 last = stat.pop()
646 last = stat.pop()
650 stat.insert(0, last)
647 stat.insert(0, last)
651 stat = ''.join(stat)
648 stat = ''.join(stat)
652 if stat.startswith('0 files'): raise ValueError
649 if stat.startswith('0 files'): raise ValueError
653 return stat
650 return stat
654 except: raise
651 except: raise
655 finally:
652 finally:
656 try: os.unlink(name)
653 try: os.unlink(name)
657 except: pass
654 except: pass
@@ -1,1059 +1,1067 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser")
18 demandload(globals(), "os threading time calendar ConfigParser")
19
19
20 # used by parsedate
20 # used by parsedate
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
22 '%a %b %d %H:%M:%S %Y')
22 '%a %b %d %H:%M:%S %Y')
23
23
24 class SignalInterrupt(Exception):
24 class SignalInterrupt(Exception):
25 """Exception raised on SIGTERM and SIGHUP."""
25 """Exception raised on SIGTERM and SIGHUP."""
26
26
27 # like SafeConfigParser but with case-sensitive keys
27 # like SafeConfigParser but with case-sensitive keys
28 class configparser(ConfigParser.SafeConfigParser):
28 class configparser(ConfigParser.SafeConfigParser):
29 def optionxform(self, optionstr):
29 def optionxform(self, optionstr):
30 return optionstr
30 return optionstr
31
31
32 def cachefunc(func):
32 def cachefunc(func):
33 '''cache the result of function calls'''
33 '''cache the result of function calls'''
34 # XXX doesn't handle keywords args
34 # XXX doesn't handle keywords args
35 cache = {}
35 cache = {}
36 if func.func_code.co_argcount == 1:
36 if func.func_code.co_argcount == 1:
37 # we gain a small amount of time because
37 # we gain a small amount of time because
38 # we don't need to pack/unpack the list
38 # we don't need to pack/unpack the list
39 def f(arg):
39 def f(arg):
40 if arg not in cache:
40 if arg not in cache:
41 cache[arg] = func(arg)
41 cache[arg] = func(arg)
42 return cache[arg]
42 return cache[arg]
43 else:
43 else:
44 def f(*args):
44 def f(*args):
45 if args not in cache:
45 if args not in cache:
46 cache[args] = func(*args)
46 cache[args] = func(*args)
47 return cache[args]
47 return cache[args]
48
48
49 return f
49 return f
50
50
51 def pipefilter(s, cmd):
51 def pipefilter(s, cmd):
52 '''filter string S through command CMD, returning its output'''
52 '''filter string S through command CMD, returning its output'''
53 (pout, pin) = popen2.popen2(cmd, -1, 'b')
53 (pout, pin) = popen2.popen2(cmd, -1, 'b')
54 def writer():
54 def writer():
55 try:
55 try:
56 pin.write(s)
56 pin.write(s)
57 pin.close()
57 pin.close()
58 except IOError, inst:
58 except IOError, inst:
59 if inst.errno != errno.EPIPE:
59 if inst.errno != errno.EPIPE:
60 raise
60 raise
61
61
62 # we should use select instead on UNIX, but this will work on most
62 # we should use select instead on UNIX, but this will work on most
63 # systems, including Windows
63 # systems, including Windows
64 w = threading.Thread(target=writer)
64 w = threading.Thread(target=writer)
65 w.start()
65 w.start()
66 f = pout.read()
66 f = pout.read()
67 pout.close()
67 pout.close()
68 w.join()
68 w.join()
69 return f
69 return f
70
70
71 def tempfilter(s, cmd):
71 def tempfilter(s, cmd):
72 '''filter string S through a pair of temporary files with CMD.
72 '''filter string S through a pair of temporary files with CMD.
73 CMD is used as a template to create the real command to be run,
73 CMD is used as a template to create the real command to be run,
74 with the strings INFILE and OUTFILE replaced by the real names of
74 with the strings INFILE and OUTFILE replaced by the real names of
75 the temporary files generated.'''
75 the temporary files generated.'''
76 inname, outname = None, None
76 inname, outname = None, None
77 try:
77 try:
78 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
78 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
79 fp = os.fdopen(infd, 'wb')
79 fp = os.fdopen(infd, 'wb')
80 fp.write(s)
80 fp.write(s)
81 fp.close()
81 fp.close()
82 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
82 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
83 os.close(outfd)
83 os.close(outfd)
84 cmd = cmd.replace('INFILE', inname)
84 cmd = cmd.replace('INFILE', inname)
85 cmd = cmd.replace('OUTFILE', outname)
85 cmd = cmd.replace('OUTFILE', outname)
86 code = os.system(cmd)
86 code = os.system(cmd)
87 if code: raise Abort(_("command '%s' failed: %s") %
87 if code: raise Abort(_("command '%s' failed: %s") %
88 (cmd, explain_exit(code)))
88 (cmd, explain_exit(code)))
89 return open(outname, 'rb').read()
89 return open(outname, 'rb').read()
90 finally:
90 finally:
91 try:
91 try:
92 if inname: os.unlink(inname)
92 if inname: os.unlink(inname)
93 except: pass
93 except: pass
94 try:
94 try:
95 if outname: os.unlink(outname)
95 if outname: os.unlink(outname)
96 except: pass
96 except: pass
97
97
98 filtertable = {
98 filtertable = {
99 'tempfile:': tempfilter,
99 'tempfile:': tempfilter,
100 'pipe:': pipefilter,
100 'pipe:': pipefilter,
101 }
101 }
102
102
103 def filter(s, cmd):
103 def filter(s, cmd):
104 "filter a string through a command that transforms its input to its output"
104 "filter a string through a command that transforms its input to its output"
105 for name, fn in filtertable.iteritems():
105 for name, fn in filtertable.iteritems():
106 if cmd.startswith(name):
106 if cmd.startswith(name):
107 return fn(s, cmd[len(name):].lstrip())
107 return fn(s, cmd[len(name):].lstrip())
108 return pipefilter(s, cmd)
108 return pipefilter(s, cmd)
109
109
110 def find_in_path(name, path, default=None):
110 def find_in_path(name, path, default=None):
111 '''find name in search path. path can be string (will be split
111 '''find name in search path. path can be string (will be split
112 with os.pathsep), or iterable thing that returns strings. if name
112 with os.pathsep), or iterable thing that returns strings. if name
113 found, return path to name. else return default.'''
113 found, return path to name. else return default.'''
114 if isinstance(path, str):
114 if isinstance(path, str):
115 path = path.split(os.pathsep)
115 path = path.split(os.pathsep)
116 for p in path:
116 for p in path:
117 p_name = os.path.join(p, name)
117 p_name = os.path.join(p, name)
118 if os.path.exists(p_name):
118 if os.path.exists(p_name):
119 return p_name
119 return p_name
120 return default
120 return default
121
121
122 def binary(s):
122 def binary(s):
123 """return true if a string is binary data using diff's heuristic"""
123 """return true if a string is binary data using diff's heuristic"""
124 if s and '\0' in s[:4096]:
124 if s and '\0' in s[:4096]:
125 return True
125 return True
126 return False
126 return False
127
127
128 def unique(g):
128 def unique(g):
129 """return the uniq elements of iterable g"""
129 """return the uniq elements of iterable g"""
130 seen = {}
130 seen = {}
131 l = []
131 l = []
132 for f in g:
132 for f in g:
133 if f not in seen:
133 if f not in seen:
134 seen[f] = 1
134 seen[f] = 1
135 l.append(f)
135 l.append(f)
136 return l
136 return l
137
137
138 class Abort(Exception):
138 class Abort(Exception):
139 """Raised if a command needs to print an error and exit."""
139 """Raised if a command needs to print an error and exit."""
140
140
141 class UnexpectedOutput(Abort):
141 class UnexpectedOutput(Abort):
142 """Raised to print an error with part of output and exit."""
142 """Raised to print an error with part of output and exit."""
143
143
144 def always(fn): return True
144 def always(fn): return True
145 def never(fn): return False
145 def never(fn): return False
146
146
147 def patkind(name, dflt_pat='glob'):
147 def patkind(name, dflt_pat='glob'):
148 """Split a string into an optional pattern kind prefix and the
148 """Split a string into an optional pattern kind prefix and the
149 actual pattern."""
149 actual pattern."""
150 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
150 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
151 if name.startswith(prefix + ':'): return name.split(':', 1)
151 if name.startswith(prefix + ':'): return name.split(':', 1)
152 return dflt_pat, name
152 return dflt_pat, name
153
153
154 def globre(pat, head='^', tail='$'):
154 def globre(pat, head='^', tail='$'):
155 "convert a glob pattern into a regexp"
155 "convert a glob pattern into a regexp"
156 i, n = 0, len(pat)
156 i, n = 0, len(pat)
157 res = ''
157 res = ''
158 group = False
158 group = False
159 def peek(): return i < n and pat[i]
159 def peek(): return i < n and pat[i]
160 while i < n:
160 while i < n:
161 c = pat[i]
161 c = pat[i]
162 i = i+1
162 i = i+1
163 if c == '*':
163 if c == '*':
164 if peek() == '*':
164 if peek() == '*':
165 i += 1
165 i += 1
166 res += '.*'
166 res += '.*'
167 else:
167 else:
168 res += '[^/]*'
168 res += '[^/]*'
169 elif c == '?':
169 elif c == '?':
170 res += '.'
170 res += '.'
171 elif c == '[':
171 elif c == '[':
172 j = i
172 j = i
173 if j < n and pat[j] in '!]':
173 if j < n and pat[j] in '!]':
174 j += 1
174 j += 1
175 while j < n and pat[j] != ']':
175 while j < n and pat[j] != ']':
176 j += 1
176 j += 1
177 if j >= n:
177 if j >= n:
178 res += '\\['
178 res += '\\['
179 else:
179 else:
180 stuff = pat[i:j].replace('\\','\\\\')
180 stuff = pat[i:j].replace('\\','\\\\')
181 i = j + 1
181 i = j + 1
182 if stuff[0] == '!':
182 if stuff[0] == '!':
183 stuff = '^' + stuff[1:]
183 stuff = '^' + stuff[1:]
184 elif stuff[0] == '^':
184 elif stuff[0] == '^':
185 stuff = '\\' + stuff
185 stuff = '\\' + stuff
186 res = '%s[%s]' % (res, stuff)
186 res = '%s[%s]' % (res, stuff)
187 elif c == '{':
187 elif c == '{':
188 group = True
188 group = True
189 res += '(?:'
189 res += '(?:'
190 elif c == '}' and group:
190 elif c == '}' and group:
191 res += ')'
191 res += ')'
192 group = False
192 group = False
193 elif c == ',' and group:
193 elif c == ',' and group:
194 res += '|'
194 res += '|'
195 elif c == '\\':
195 elif c == '\\':
196 p = peek()
196 p = peek()
197 if p:
197 if p:
198 i += 1
198 i += 1
199 res += re.escape(p)
199 res += re.escape(p)
200 else:
200 else:
201 res += re.escape(c)
201 res += re.escape(c)
202 else:
202 else:
203 res += re.escape(c)
203 res += re.escape(c)
204 return head + res + tail
204 return head + res + tail
205
205
206 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
206 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
207
207
208 def pathto(n1, n2):
208 def pathto(n1, n2):
209 '''return the relative path from one place to another.
209 '''return the relative path from one place to another.
210 this returns a path in the form used by the local filesystem, not hg.'''
210 this returns a path in the form used by the local filesystem, not hg.'''
211 if not n1: return localpath(n2)
211 if not n1: return localpath(n2)
212 a, b = n1.split('/'), n2.split('/')
212 a, b = n1.split('/'), n2.split('/')
213 a.reverse()
213 a.reverse()
214 b.reverse()
214 b.reverse()
215 while a and b and a[-1] == b[-1]:
215 while a and b and a[-1] == b[-1]:
216 a.pop()
216 a.pop()
217 b.pop()
217 b.pop()
218 b.reverse()
218 b.reverse()
219 return os.sep.join((['..'] * len(a)) + b)
219 return os.sep.join((['..'] * len(a)) + b)
220
220
221 def canonpath(root, cwd, myname):
221 def canonpath(root, cwd, myname):
222 """return the canonical path of myname, given cwd and root"""
222 """return the canonical path of myname, given cwd and root"""
223 if root == os.sep:
223 if root == os.sep:
224 rootsep = os.sep
224 rootsep = os.sep
225 elif root.endswith(os.sep):
225 elif root.endswith(os.sep):
226 rootsep = root
226 rootsep = root
227 else:
227 else:
228 rootsep = root + os.sep
228 rootsep = root + os.sep
229 name = myname
229 name = myname
230 if not os.path.isabs(name):
230 if not os.path.isabs(name):
231 name = os.path.join(root, cwd, name)
231 name = os.path.join(root, cwd, name)
232 name = os.path.normpath(name)
232 name = os.path.normpath(name)
233 if name != rootsep and name.startswith(rootsep):
233 if name != rootsep and name.startswith(rootsep):
234 name = name[len(rootsep):]
234 name = name[len(rootsep):]
235 audit_path(name)
235 audit_path(name)
236 return pconvert(name)
236 return pconvert(name)
237 elif name == root:
237 elif name == root:
238 return ''
238 return ''
239 else:
239 else:
240 # Determine whether `name' is in the hierarchy at or beneath `root',
240 # Determine whether `name' is in the hierarchy at or beneath `root',
241 # by iterating name=dirname(name) until that causes no change (can't
241 # by iterating name=dirname(name) until that causes no change (can't
242 # check name == '/', because that doesn't work on windows). For each
242 # check name == '/', because that doesn't work on windows). For each
243 # `name', compare dev/inode numbers. If they match, the list `rel'
243 # `name', compare dev/inode numbers. If they match, the list `rel'
244 # holds the reversed list of components making up the relative file
244 # holds the reversed list of components making up the relative file
245 # name we want.
245 # name we want.
246 root_st = os.stat(root)
246 root_st = os.stat(root)
247 rel = []
247 rel = []
248 while True:
248 while True:
249 try:
249 try:
250 name_st = os.stat(name)
250 name_st = os.stat(name)
251 except OSError:
251 except OSError:
252 break
252 break
253 if samestat(name_st, root_st):
253 if samestat(name_st, root_st):
254 rel.reverse()
254 rel.reverse()
255 name = os.path.join(*rel)
255 name = os.path.join(*rel)
256 audit_path(name)
256 audit_path(name)
257 return pconvert(name)
257 return pconvert(name)
258 dirname, basename = os.path.split(name)
258 dirname, basename = os.path.split(name)
259 rel.append(basename)
259 rel.append(basename)
260 if dirname == name:
260 if dirname == name:
261 break
261 break
262 name = dirname
262 name = dirname
263
263
264 raise Abort('%s not under root' % myname)
264 raise Abort('%s not under root' % myname)
265
265
266 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
266 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
267 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
267 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
268
268
269 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
269 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
270 if os.name == 'nt':
270 if os.name == 'nt':
271 dflt_pat = 'glob'
271 dflt_pat = 'glob'
272 else:
272 else:
273 dflt_pat = 'relpath'
273 dflt_pat = 'relpath'
274 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
274 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
275
275
276 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
276 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
277 """build a function to match a set of file patterns
277 """build a function to match a set of file patterns
278
278
279 arguments:
279 arguments:
280 canonroot - the canonical root of the tree you're matching against
280 canonroot - the canonical root of the tree you're matching against
281 cwd - the current working directory, if relevant
281 cwd - the current working directory, if relevant
282 names - patterns to find
282 names - patterns to find
283 inc - patterns to include
283 inc - patterns to include
284 exc - patterns to exclude
284 exc - patterns to exclude
285 head - a regex to prepend to patterns to control whether a match is rooted
285 head - a regex to prepend to patterns to control whether a match is rooted
286
286
287 a pattern is one of:
287 a pattern is one of:
288 'glob:<rooted glob>'
288 'glob:<rooted glob>'
289 're:<rooted regexp>'
289 're:<rooted regexp>'
290 'path:<rooted path>'
290 'path:<rooted path>'
291 'relglob:<relative glob>'
291 'relglob:<relative glob>'
292 'relpath:<relative path>'
292 'relpath:<relative path>'
293 'relre:<relative regexp>'
293 'relre:<relative regexp>'
294 '<rooted path or regexp>'
294 '<rooted path or regexp>'
295
295
296 returns:
296 returns:
297 a 3-tuple containing
297 a 3-tuple containing
298 - list of explicit non-pattern names passed in
298 - list of explicit non-pattern names passed in
299 - a bool match(filename) function
299 - a bool match(filename) function
300 - a bool indicating if any patterns were passed in
300 - a bool indicating if any patterns were passed in
301
301
302 todo:
302 todo:
303 make head regex a rooted bool
303 make head regex a rooted bool
304 """
304 """
305
305
306 def contains_glob(name):
306 def contains_glob(name):
307 for c in name:
307 for c in name:
308 if c in _globchars: return True
308 if c in _globchars: return True
309 return False
309 return False
310
310
311 def regex(kind, name, tail):
311 def regex(kind, name, tail):
312 '''convert a pattern into a regular expression'''
312 '''convert a pattern into a regular expression'''
313 if kind == 're':
313 if kind == 're':
314 return name
314 return name
315 elif kind == 'path':
315 elif kind == 'path':
316 return '^' + re.escape(name) + '(?:/|$)'
316 return '^' + re.escape(name) + '(?:/|$)'
317 elif kind == 'relglob':
317 elif kind == 'relglob':
318 return head + globre(name, '(?:|.*/)', tail)
318 return head + globre(name, '(?:|.*/)', tail)
319 elif kind == 'relpath':
319 elif kind == 'relpath':
320 return head + re.escape(name) + tail
320 return head + re.escape(name) + tail
321 elif kind == 'relre':
321 elif kind == 'relre':
322 if name.startswith('^'):
322 if name.startswith('^'):
323 return name
323 return name
324 return '.*' + name
324 return '.*' + name
325 return head + globre(name, '', tail)
325 return head + globre(name, '', tail)
326
326
327 def matchfn(pats, tail):
327 def matchfn(pats, tail):
328 """build a matching function from a set of patterns"""
328 """build a matching function from a set of patterns"""
329 if not pats:
329 if not pats:
330 return
330 return
331 matches = []
331 matches = []
332 for k, p in pats:
332 for k, p in pats:
333 try:
333 try:
334 pat = '(?:%s)' % regex(k, p, tail)
334 pat = '(?:%s)' % regex(k, p, tail)
335 matches.append(re.compile(pat).match)
335 matches.append(re.compile(pat).match)
336 except re.error:
336 except re.error:
337 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
337 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
338 else: raise Abort("invalid pattern (%s): %s" % (k, p))
338 else: raise Abort("invalid pattern (%s): %s" % (k, p))
339
339
340 def buildfn(text):
340 def buildfn(text):
341 for m in matches:
341 for m in matches:
342 r = m(text)
342 r = m(text)
343 if r:
343 if r:
344 return r
344 return r
345
345
346 return buildfn
346 return buildfn
347
347
348 def globprefix(pat):
348 def globprefix(pat):
349 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
349 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
350 root = []
350 root = []
351 for p in pat.split(os.sep):
351 for p in pat.split(os.sep):
352 if contains_glob(p): break
352 if contains_glob(p): break
353 root.append(p)
353 root.append(p)
354 return '/'.join(root)
354 return '/'.join(root)
355
355
356 pats = []
356 pats = []
357 files = []
357 files = []
358 roots = []
358 roots = []
359 for kind, name in [patkind(p, dflt_pat) for p in names]:
359 for kind, name in [patkind(p, dflt_pat) for p in names]:
360 if kind in ('glob', 'relpath'):
360 if kind in ('glob', 'relpath'):
361 name = canonpath(canonroot, cwd, name)
361 name = canonpath(canonroot, cwd, name)
362 if name == '':
362 if name == '':
363 kind, name = 'glob', '**'
363 kind, name = 'glob', '**'
364 if kind in ('glob', 'path', 're'):
364 if kind in ('glob', 'path', 're'):
365 pats.append((kind, name))
365 pats.append((kind, name))
366 if kind == 'glob':
366 if kind == 'glob':
367 root = globprefix(name)
367 root = globprefix(name)
368 if root: roots.append(root)
368 if root: roots.append(root)
369 elif kind == 'relpath':
369 elif kind == 'relpath':
370 files.append((kind, name))
370 files.append((kind, name))
371 roots.append(name)
371 roots.append(name)
372
372
373 patmatch = matchfn(pats, '$') or always
373 patmatch = matchfn(pats, '$') or always
374 filematch = matchfn(files, '(?:/|$)') or always
374 filematch = matchfn(files, '(?:/|$)') or always
375 incmatch = always
375 incmatch = always
376 if inc:
376 if inc:
377 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
377 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
378 incmatch = matchfn(inckinds, '(?:/|$)')
378 incmatch = matchfn(inckinds, '(?:/|$)')
379 excmatch = lambda fn: False
379 excmatch = lambda fn: False
380 if exc:
380 if exc:
381 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
381 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
382 excmatch = matchfn(exckinds, '(?:/|$)')
382 excmatch = matchfn(exckinds, '(?:/|$)')
383
383
384 return (roots,
384 return (roots,
385 lambda fn: (incmatch(fn) and not excmatch(fn) and
385 lambda fn: (incmatch(fn) and not excmatch(fn) and
386 (fn.endswith('/') or
386 (fn.endswith('/') or
387 (not pats and not files) or
387 (not pats and not files) or
388 (pats and patmatch(fn)) or
388 (pats and patmatch(fn)) or
389 (files and filematch(fn)))),
389 (files and filematch(fn)))),
390 (inc or exc or (pats and pats != [('glob', '**')])) and True)
390 (inc or exc or (pats and pats != [('glob', '**')])) and True)
391
391
392 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
392 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
393 '''enhanced shell command execution.
393 '''enhanced shell command execution.
394 run with environment maybe modified, maybe in different dir.
394 run with environment maybe modified, maybe in different dir.
395
395
396 if command fails and onerr is None, return status. if ui object,
396 if command fails and onerr is None, return status. if ui object,
397 print error message and return status, else raise onerr object as
397 print error message and return status, else raise onerr object as
398 exception.'''
398 exception.'''
399 def py2shell(val):
399 def py2shell(val):
400 'convert python object into string that is useful to shell'
400 'convert python object into string that is useful to shell'
401 if val in (None, False):
401 if val in (None, False):
402 return '0'
402 return '0'
403 if val == True:
403 if val == True:
404 return '1'
404 return '1'
405 return str(val)
405 return str(val)
406 oldenv = {}
406 oldenv = {}
407 for k in environ:
407 for k in environ:
408 oldenv[k] = os.environ.get(k)
408 oldenv[k] = os.environ.get(k)
409 if cwd is not None:
409 if cwd is not None:
410 oldcwd = os.getcwd()
410 oldcwd = os.getcwd()
411 try:
411 try:
412 for k, v in environ.iteritems():
412 for k, v in environ.iteritems():
413 os.environ[k] = py2shell(v)
413 os.environ[k] = py2shell(v)
414 if cwd is not None and oldcwd != cwd:
414 if cwd is not None and oldcwd != cwd:
415 os.chdir(cwd)
415 os.chdir(cwd)
416 rc = os.system(cmd)
416 rc = os.system(cmd)
417 if rc and onerr:
417 if rc and onerr:
418 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
418 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
419 explain_exit(rc)[0])
419 explain_exit(rc)[0])
420 if errprefix:
420 if errprefix:
421 errmsg = '%s: %s' % (errprefix, errmsg)
421 errmsg = '%s: %s' % (errprefix, errmsg)
422 try:
422 try:
423 onerr.warn(errmsg + '\n')
423 onerr.warn(errmsg + '\n')
424 except AttributeError:
424 except AttributeError:
425 raise onerr(errmsg)
425 raise onerr(errmsg)
426 return rc
426 return rc
427 finally:
427 finally:
428 for k, v in oldenv.iteritems():
428 for k, v in oldenv.iteritems():
429 if v is None:
429 if v is None:
430 del os.environ[k]
430 del os.environ[k]
431 else:
431 else:
432 os.environ[k] = v
432 os.environ[k] = v
433 if cwd is not None and oldcwd != cwd:
433 if cwd is not None and oldcwd != cwd:
434 os.chdir(oldcwd)
434 os.chdir(oldcwd)
435
435
436 def rename(src, dst):
436 def rename(src, dst):
437 """forcibly rename a file"""
437 """forcibly rename a file"""
438 try:
438 try:
439 os.rename(src, dst)
439 os.rename(src, dst)
440 except OSError, err:
440 except OSError, err:
441 # on windows, rename to existing file is not allowed, so we
441 # on windows, rename to existing file is not allowed, so we
442 # must delete destination first. but if file is open, unlink
442 # must delete destination first. but if file is open, unlink
443 # schedules it for delete but does not delete it. rename
443 # schedules it for delete but does not delete it. rename
444 # happens immediately even for open files, so we create
444 # happens immediately even for open files, so we create
445 # temporary file, delete it, rename destination to that name,
445 # temporary file, delete it, rename destination to that name,
446 # then delete that. then rename is safe to do.
446 # then delete that. then rename is safe to do.
447 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
447 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
448 os.close(fd)
448 os.close(fd)
449 os.unlink(temp)
449 os.unlink(temp)
450 os.rename(dst, temp)
450 os.rename(dst, temp)
451 os.unlink(temp)
451 os.unlink(temp)
452 os.rename(src, dst)
452 os.rename(src, dst)
453
453
454 def unlink(f):
454 def unlink(f):
455 """unlink and remove the directory if it is empty"""
455 """unlink and remove the directory if it is empty"""
456 os.unlink(f)
456 os.unlink(f)
457 # try removing directories that might now be empty
457 # try removing directories that might now be empty
458 try:
458 try:
459 os.removedirs(os.path.dirname(f))
459 os.removedirs(os.path.dirname(f))
460 except OSError:
460 except OSError:
461 pass
461 pass
462
462
463 def copyfile(src, dest):
464 "copy a file, preserving mode"
465 try:
466 shutil.copyfile(src, dest)
467 shutil.copymode(src, dest)
468 except shutil.Error, inst:
469 raise util.Abort(str(inst))
470
463 def copyfiles(src, dst, hardlink=None):
471 def copyfiles(src, dst, hardlink=None):
464 """Copy a directory tree using hardlinks if possible"""
472 """Copy a directory tree using hardlinks if possible"""
465
473
466 if hardlink is None:
474 if hardlink is None:
467 hardlink = (os.stat(src).st_dev ==
475 hardlink = (os.stat(src).st_dev ==
468 os.stat(os.path.dirname(dst)).st_dev)
476 os.stat(os.path.dirname(dst)).st_dev)
469
477
470 if os.path.isdir(src):
478 if os.path.isdir(src):
471 os.mkdir(dst)
479 os.mkdir(dst)
472 for name in os.listdir(src):
480 for name in os.listdir(src):
473 srcname = os.path.join(src, name)
481 srcname = os.path.join(src, name)
474 dstname = os.path.join(dst, name)
482 dstname = os.path.join(dst, name)
475 copyfiles(srcname, dstname, hardlink)
483 copyfiles(srcname, dstname, hardlink)
476 else:
484 else:
477 if hardlink:
485 if hardlink:
478 try:
486 try:
479 os_link(src, dst)
487 os_link(src, dst)
480 except (IOError, OSError):
488 except (IOError, OSError):
481 hardlink = False
489 hardlink = False
482 shutil.copy(src, dst)
490 shutil.copy(src, dst)
483 else:
491 else:
484 shutil.copy(src, dst)
492 shutil.copy(src, dst)
485
493
486 def audit_path(path):
494 def audit_path(path):
487 """Abort if path contains dangerous components"""
495 """Abort if path contains dangerous components"""
488 parts = os.path.normcase(path).split(os.sep)
496 parts = os.path.normcase(path).split(os.sep)
489 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
497 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
490 or os.pardir in parts):
498 or os.pardir in parts):
491 raise Abort(_("path contains illegal component: %s\n") % path)
499 raise Abort(_("path contains illegal component: %s\n") % path)
492
500
493 def _makelock_file(info, pathname):
501 def _makelock_file(info, pathname):
494 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
502 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
495 os.write(ld, info)
503 os.write(ld, info)
496 os.close(ld)
504 os.close(ld)
497
505
498 def _readlock_file(pathname):
506 def _readlock_file(pathname):
499 return posixfile(pathname).read()
507 return posixfile(pathname).read()
500
508
501 def nlinks(pathname):
509 def nlinks(pathname):
502 """Return number of hardlinks for the given file."""
510 """Return number of hardlinks for the given file."""
503 return os.lstat(pathname).st_nlink
511 return os.lstat(pathname).st_nlink
504
512
505 if hasattr(os, 'link'):
513 if hasattr(os, 'link'):
506 os_link = os.link
514 os_link = os.link
507 else:
515 else:
508 def os_link(src, dst):
516 def os_link(src, dst):
509 raise OSError(0, _("Hardlinks not supported"))
517 raise OSError(0, _("Hardlinks not supported"))
510
518
511 def fstat(fp):
519 def fstat(fp):
512 '''stat file object that may not have fileno method.'''
520 '''stat file object that may not have fileno method.'''
513 try:
521 try:
514 return os.fstat(fp.fileno())
522 return os.fstat(fp.fileno())
515 except AttributeError:
523 except AttributeError:
516 return os.stat(fp.name)
524 return os.stat(fp.name)
517
525
518 posixfile = file
526 posixfile = file
519
527
520 def is_win_9x():
528 def is_win_9x():
521 '''return true if run on windows 95, 98 or me.'''
529 '''return true if run on windows 95, 98 or me.'''
522 try:
530 try:
523 return sys.getwindowsversion()[3] == 1
531 return sys.getwindowsversion()[3] == 1
524 except AttributeError:
532 except AttributeError:
525 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
533 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
526
534
527 def username(uid=None):
535 def username(uid=None):
528 """Return the name of the user with the given uid.
536 """Return the name of the user with the given uid.
529
537
530 If uid is None, return the name of the current user."""
538 If uid is None, return the name of the current user."""
531 try:
539 try:
532 import pwd
540 import pwd
533 if uid is None:
541 if uid is None:
534 uid = os.getuid()
542 uid = os.getuid()
535 try:
543 try:
536 return pwd.getpwuid(uid)[0]
544 return pwd.getpwuid(uid)[0]
537 except KeyError:
545 except KeyError:
538 return str(uid)
546 return str(uid)
539 except ImportError:
547 except ImportError:
540 return None
548 return None
541
549
542 def groupname(gid=None):
550 def groupname(gid=None):
543 """Return the name of the group with the given gid.
551 """Return the name of the group with the given gid.
544
552
545 If gid is None, return the name of the current group."""
553 If gid is None, return the name of the current group."""
546 try:
554 try:
547 import grp
555 import grp
548 if gid is None:
556 if gid is None:
549 gid = os.getgid()
557 gid = os.getgid()
550 try:
558 try:
551 return grp.getgrgid(gid)[0]
559 return grp.getgrgid(gid)[0]
552 except KeyError:
560 except KeyError:
553 return str(gid)
561 return str(gid)
554 except ImportError:
562 except ImportError:
555 return None
563 return None
556
564
557 # Platform specific variants
565 # Platform specific variants
558 if os.name == 'nt':
566 if os.name == 'nt':
559 demandload(globals(), "msvcrt")
567 demandload(globals(), "msvcrt")
560 nulldev = 'NUL:'
568 nulldev = 'NUL:'
561
569
562 class winstdout:
570 class winstdout:
563 '''stdout on windows misbehaves if sent through a pipe'''
571 '''stdout on windows misbehaves if sent through a pipe'''
564
572
565 def __init__(self, fp):
573 def __init__(self, fp):
566 self.fp = fp
574 self.fp = fp
567
575
568 def __getattr__(self, key):
576 def __getattr__(self, key):
569 return getattr(self.fp, key)
577 return getattr(self.fp, key)
570
578
571 def close(self):
579 def close(self):
572 try:
580 try:
573 self.fp.close()
581 self.fp.close()
574 except: pass
582 except: pass
575
583
576 def write(self, s):
584 def write(self, s):
577 try:
585 try:
578 return self.fp.write(s)
586 return self.fp.write(s)
579 except IOError, inst:
587 except IOError, inst:
580 if inst.errno != 0: raise
588 if inst.errno != 0: raise
581 self.close()
589 self.close()
582 raise IOError(errno.EPIPE, 'Broken pipe')
590 raise IOError(errno.EPIPE, 'Broken pipe')
583
591
584 sys.stdout = winstdout(sys.stdout)
592 sys.stdout = winstdout(sys.stdout)
585
593
586 def system_rcpath():
594 def system_rcpath():
587 try:
595 try:
588 return system_rcpath_win32()
596 return system_rcpath_win32()
589 except:
597 except:
590 return [r'c:\mercurial\mercurial.ini']
598 return [r'c:\mercurial\mercurial.ini']
591
599
592 def os_rcpath():
600 def os_rcpath():
593 '''return default os-specific hgrc search path'''
601 '''return default os-specific hgrc search path'''
594 path = system_rcpath()
602 path = system_rcpath()
595 path.append(user_rcpath())
603 path.append(user_rcpath())
596 userprofile = os.environ.get('USERPROFILE')
604 userprofile = os.environ.get('USERPROFILE')
597 if userprofile:
605 if userprofile:
598 path.append(os.path.join(userprofile, 'mercurial.ini'))
606 path.append(os.path.join(userprofile, 'mercurial.ini'))
599 return path
607 return path
600
608
601 def user_rcpath():
609 def user_rcpath():
602 '''return os-specific hgrc search path to the user dir'''
610 '''return os-specific hgrc search path to the user dir'''
603 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
611 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
604
612
605 def parse_patch_output(output_line):
613 def parse_patch_output(output_line):
606 """parses the output produced by patch and returns the file name"""
614 """parses the output produced by patch and returns the file name"""
607 pf = output_line[14:]
615 pf = output_line[14:]
608 if pf[0] == '`':
616 if pf[0] == '`':
609 pf = pf[1:-1] # Remove the quotes
617 pf = pf[1:-1] # Remove the quotes
610 return pf
618 return pf
611
619
612 def testpid(pid):
620 def testpid(pid):
613 '''return False if pid dead, True if running or not known'''
621 '''return False if pid dead, True if running or not known'''
614 return True
622 return True
615
623
616 def is_exec(f, last):
624 def is_exec(f, last):
617 return last
625 return last
618
626
619 def set_exec(f, mode):
627 def set_exec(f, mode):
620 pass
628 pass
621
629
622 def set_binary(fd):
630 def set_binary(fd):
623 msvcrt.setmode(fd.fileno(), os.O_BINARY)
631 msvcrt.setmode(fd.fileno(), os.O_BINARY)
624
632
625 def pconvert(path):
633 def pconvert(path):
626 return path.replace("\\", "/")
634 return path.replace("\\", "/")
627
635
628 def localpath(path):
636 def localpath(path):
629 return path.replace('/', '\\')
637 return path.replace('/', '\\')
630
638
631 def normpath(path):
639 def normpath(path):
632 return pconvert(os.path.normpath(path))
640 return pconvert(os.path.normpath(path))
633
641
634 makelock = _makelock_file
642 makelock = _makelock_file
635 readlock = _readlock_file
643 readlock = _readlock_file
636
644
637 def samestat(s1, s2):
645 def samestat(s1, s2):
638 return False
646 return False
639
647
640 def shellquote(s):
648 def shellquote(s):
641 return '"%s"' % s.replace('"', '\\"')
649 return '"%s"' % s.replace('"', '\\"')
642
650
643 def explain_exit(code):
651 def explain_exit(code):
644 return _("exited with status %d") % code, code
652 return _("exited with status %d") % code, code
645
653
646 try:
654 try:
647 # override functions with win32 versions if possible
655 # override functions with win32 versions if possible
648 from util_win32 import *
656 from util_win32 import *
649 if not is_win_9x():
657 if not is_win_9x():
650 posixfile = posixfile_nt
658 posixfile = posixfile_nt
651 except ImportError:
659 except ImportError:
652 pass
660 pass
653
661
654 else:
662 else:
655 nulldev = '/dev/null'
663 nulldev = '/dev/null'
656
664
657 def rcfiles(path):
665 def rcfiles(path):
658 rcs = [os.path.join(path, 'hgrc')]
666 rcs = [os.path.join(path, 'hgrc')]
659 rcdir = os.path.join(path, 'hgrc.d')
667 rcdir = os.path.join(path, 'hgrc.d')
660 try:
668 try:
661 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
669 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
662 if f.endswith(".rc")])
670 if f.endswith(".rc")])
663 except OSError:
671 except OSError:
664 pass
672 pass
665 return rcs
673 return rcs
666
674
667 def os_rcpath():
675 def os_rcpath():
668 '''return default os-specific hgrc search path'''
676 '''return default os-specific hgrc search path'''
669 path = []
677 path = []
670 # old mod_python does not set sys.argv
678 # old mod_python does not set sys.argv
671 if len(getattr(sys, 'argv', [])) > 0:
679 if len(getattr(sys, 'argv', [])) > 0:
672 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
680 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
673 '/../etc/mercurial'))
681 '/../etc/mercurial'))
674 path.extend(rcfiles('/etc/mercurial'))
682 path.extend(rcfiles('/etc/mercurial'))
675 path.append(os.path.expanduser('~/.hgrc'))
683 path.append(os.path.expanduser('~/.hgrc'))
676 path = [os.path.normpath(f) for f in path]
684 path = [os.path.normpath(f) for f in path]
677 return path
685 return path
678
686
679 def parse_patch_output(output_line):
687 def parse_patch_output(output_line):
680 """parses the output produced by patch and returns the file name"""
688 """parses the output produced by patch and returns the file name"""
681 pf = output_line[14:]
689 pf = output_line[14:]
682 if pf.startswith("'") and pf.endswith("'") and " " in pf:
690 if pf.startswith("'") and pf.endswith("'") and " " in pf:
683 pf = pf[1:-1] # Remove the quotes
691 pf = pf[1:-1] # Remove the quotes
684 return pf
692 return pf
685
693
686 def is_exec(f, last):
694 def is_exec(f, last):
687 """check whether a file is executable"""
695 """check whether a file is executable"""
688 return (os.lstat(f).st_mode & 0100 != 0)
696 return (os.lstat(f).st_mode & 0100 != 0)
689
697
690 def set_exec(f, mode):
698 def set_exec(f, mode):
691 s = os.lstat(f).st_mode
699 s = os.lstat(f).st_mode
692 if (s & 0100 != 0) == mode:
700 if (s & 0100 != 0) == mode:
693 return
701 return
694 if mode:
702 if mode:
695 # Turn on +x for every +r bit when making a file executable
703 # Turn on +x for every +r bit when making a file executable
696 # and obey umask.
704 # and obey umask.
697 umask = os.umask(0)
705 umask = os.umask(0)
698 os.umask(umask)
706 os.umask(umask)
699 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
707 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
700 else:
708 else:
701 os.chmod(f, s & 0666)
709 os.chmod(f, s & 0666)
702
710
703 def set_binary(fd):
711 def set_binary(fd):
704 pass
712 pass
705
713
706 def pconvert(path):
714 def pconvert(path):
707 return path
715 return path
708
716
709 def localpath(path):
717 def localpath(path):
710 return path
718 return path
711
719
712 normpath = os.path.normpath
720 normpath = os.path.normpath
713 samestat = os.path.samestat
721 samestat = os.path.samestat
714
722
715 def makelock(info, pathname):
723 def makelock(info, pathname):
716 try:
724 try:
717 os.symlink(info, pathname)
725 os.symlink(info, pathname)
718 except OSError, why:
726 except OSError, why:
719 if why.errno == errno.EEXIST:
727 if why.errno == errno.EEXIST:
720 raise
728 raise
721 else:
729 else:
722 _makelock_file(info, pathname)
730 _makelock_file(info, pathname)
723
731
724 def readlock(pathname):
732 def readlock(pathname):
725 try:
733 try:
726 return os.readlink(pathname)
734 return os.readlink(pathname)
727 except OSError, why:
735 except OSError, why:
728 if why.errno == errno.EINVAL:
736 if why.errno == errno.EINVAL:
729 return _readlock_file(pathname)
737 return _readlock_file(pathname)
730 else:
738 else:
731 raise
739 raise
732
740
733 def shellquote(s):
741 def shellquote(s):
734 return "'%s'" % s.replace("'", "'\\''")
742 return "'%s'" % s.replace("'", "'\\''")
735
743
736 def testpid(pid):
744 def testpid(pid):
737 '''return False if pid dead, True if running or not sure'''
745 '''return False if pid dead, True if running or not sure'''
738 try:
746 try:
739 os.kill(pid, 0)
747 os.kill(pid, 0)
740 return True
748 return True
741 except OSError, inst:
749 except OSError, inst:
742 return inst.errno != errno.ESRCH
750 return inst.errno != errno.ESRCH
743
751
744 def explain_exit(code):
752 def explain_exit(code):
745 """return a 2-tuple (desc, code) describing a process's status"""
753 """return a 2-tuple (desc, code) describing a process's status"""
746 if os.WIFEXITED(code):
754 if os.WIFEXITED(code):
747 val = os.WEXITSTATUS(code)
755 val = os.WEXITSTATUS(code)
748 return _("exited with status %d") % val, val
756 return _("exited with status %d") % val, val
749 elif os.WIFSIGNALED(code):
757 elif os.WIFSIGNALED(code):
750 val = os.WTERMSIG(code)
758 val = os.WTERMSIG(code)
751 return _("killed by signal %d") % val, val
759 return _("killed by signal %d") % val, val
752 elif os.WIFSTOPPED(code):
760 elif os.WIFSTOPPED(code):
753 val = os.WSTOPSIG(code)
761 val = os.WSTOPSIG(code)
754 return _("stopped by signal %d") % val, val
762 return _("stopped by signal %d") % val, val
755 raise ValueError(_("invalid exit code"))
763 raise ValueError(_("invalid exit code"))
756
764
757 def opener(base, audit=True):
765 def opener(base, audit=True):
758 """
766 """
759 return a function that opens files relative to base
767 return a function that opens files relative to base
760
768
761 this function is used to hide the details of COW semantics and
769 this function is used to hide the details of COW semantics and
762 remote file access from higher level code.
770 remote file access from higher level code.
763 """
771 """
764 p = base
772 p = base
765 audit_p = audit
773 audit_p = audit
766
774
767 def mktempcopy(name):
775 def mktempcopy(name):
768 d, fn = os.path.split(name)
776 d, fn = os.path.split(name)
769 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
777 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
770 os.close(fd)
778 os.close(fd)
771 ofp = posixfile(temp, "wb")
779 ofp = posixfile(temp, "wb")
772 try:
780 try:
773 try:
781 try:
774 ifp = posixfile(name, "rb")
782 ifp = posixfile(name, "rb")
775 except IOError, inst:
783 except IOError, inst:
776 if not getattr(inst, 'filename', None):
784 if not getattr(inst, 'filename', None):
777 inst.filename = name
785 inst.filename = name
778 raise
786 raise
779 for chunk in filechunkiter(ifp):
787 for chunk in filechunkiter(ifp):
780 ofp.write(chunk)
788 ofp.write(chunk)
781 ifp.close()
789 ifp.close()
782 ofp.close()
790 ofp.close()
783 except:
791 except:
784 try: os.unlink(temp)
792 try: os.unlink(temp)
785 except: pass
793 except: pass
786 raise
794 raise
787 st = os.lstat(name)
795 st = os.lstat(name)
788 os.chmod(temp, st.st_mode)
796 os.chmod(temp, st.st_mode)
789 return temp
797 return temp
790
798
791 class atomictempfile(posixfile):
799 class atomictempfile(posixfile):
792 """the file will only be copied when rename is called"""
800 """the file will only be copied when rename is called"""
793 def __init__(self, name, mode):
801 def __init__(self, name, mode):
794 self.__name = name
802 self.__name = name
795 self.temp = mktempcopy(name)
803 self.temp = mktempcopy(name)
796 posixfile.__init__(self, self.temp, mode)
804 posixfile.__init__(self, self.temp, mode)
797 def rename(self):
805 def rename(self):
798 if not self.closed:
806 if not self.closed:
799 posixfile.close(self)
807 posixfile.close(self)
800 rename(self.temp, localpath(self.__name))
808 rename(self.temp, localpath(self.__name))
801 def __del__(self):
809 def __del__(self):
802 if not self.closed:
810 if not self.closed:
803 try:
811 try:
804 os.unlink(self.temp)
812 os.unlink(self.temp)
805 except: pass
813 except: pass
806 posixfile.close(self)
814 posixfile.close(self)
807
815
808 class atomicfile(atomictempfile):
816 class atomicfile(atomictempfile):
809 """the file will only be copied on close"""
817 """the file will only be copied on close"""
810 def __init__(self, name, mode):
818 def __init__(self, name, mode):
811 atomictempfile.__init__(self, name, mode)
819 atomictempfile.__init__(self, name, mode)
812 def close(self):
820 def close(self):
813 self.rename()
821 self.rename()
814 def __del__(self):
822 def __del__(self):
815 self.rename()
823 self.rename()
816
824
817 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
825 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
818 if audit_p:
826 if audit_p:
819 audit_path(path)
827 audit_path(path)
820 f = os.path.join(p, path)
828 f = os.path.join(p, path)
821
829
822 if not text:
830 if not text:
823 mode += "b" # for that other OS
831 mode += "b" # for that other OS
824
832
825 if mode[0] != "r":
833 if mode[0] != "r":
826 try:
834 try:
827 nlink = nlinks(f)
835 nlink = nlinks(f)
828 except OSError:
836 except OSError:
829 d = os.path.dirname(f)
837 d = os.path.dirname(f)
830 if not os.path.isdir(d):
838 if not os.path.isdir(d):
831 os.makedirs(d)
839 os.makedirs(d)
832 else:
840 else:
833 if atomic:
841 if atomic:
834 return atomicfile(f, mode)
842 return atomicfile(f, mode)
835 elif atomictemp:
843 elif atomictemp:
836 return atomictempfile(f, mode)
844 return atomictempfile(f, mode)
837 if nlink > 1:
845 if nlink > 1:
838 rename(mktempcopy(f), f)
846 rename(mktempcopy(f), f)
839 return posixfile(f, mode)
847 return posixfile(f, mode)
840
848
841 return o
849 return o
842
850
843 class chunkbuffer(object):
851 class chunkbuffer(object):
844 """Allow arbitrary sized chunks of data to be efficiently read from an
852 """Allow arbitrary sized chunks of data to be efficiently read from an
845 iterator over chunks of arbitrary size."""
853 iterator over chunks of arbitrary size."""
846
854
847 def __init__(self, in_iter, targetsize = 2**16):
855 def __init__(self, in_iter, targetsize = 2**16):
848 """in_iter is the iterator that's iterating over the input chunks.
856 """in_iter is the iterator that's iterating over the input chunks.
849 targetsize is how big a buffer to try to maintain."""
857 targetsize is how big a buffer to try to maintain."""
850 self.in_iter = iter(in_iter)
858 self.in_iter = iter(in_iter)
851 self.buf = ''
859 self.buf = ''
852 self.targetsize = int(targetsize)
860 self.targetsize = int(targetsize)
853 if self.targetsize <= 0:
861 if self.targetsize <= 0:
854 raise ValueError(_("targetsize must be greater than 0, was %d") %
862 raise ValueError(_("targetsize must be greater than 0, was %d") %
855 targetsize)
863 targetsize)
856 self.iterempty = False
864 self.iterempty = False
857
865
858 def fillbuf(self):
866 def fillbuf(self):
859 """Ignore target size; read every chunk from iterator until empty."""
867 """Ignore target size; read every chunk from iterator until empty."""
860 if not self.iterempty:
868 if not self.iterempty:
861 collector = cStringIO.StringIO()
869 collector = cStringIO.StringIO()
862 collector.write(self.buf)
870 collector.write(self.buf)
863 for ch in self.in_iter:
871 for ch in self.in_iter:
864 collector.write(ch)
872 collector.write(ch)
865 self.buf = collector.getvalue()
873 self.buf = collector.getvalue()
866 self.iterempty = True
874 self.iterempty = True
867
875
868 def read(self, l):
876 def read(self, l):
869 """Read L bytes of data from the iterator of chunks of data.
877 """Read L bytes of data from the iterator of chunks of data.
870 Returns less than L bytes if the iterator runs dry."""
878 Returns less than L bytes if the iterator runs dry."""
871 if l > len(self.buf) and not self.iterempty:
879 if l > len(self.buf) and not self.iterempty:
872 # Clamp to a multiple of self.targetsize
880 # Clamp to a multiple of self.targetsize
873 targetsize = self.targetsize * ((l // self.targetsize) + 1)
881 targetsize = self.targetsize * ((l // self.targetsize) + 1)
874 collector = cStringIO.StringIO()
882 collector = cStringIO.StringIO()
875 collector.write(self.buf)
883 collector.write(self.buf)
876 collected = len(self.buf)
884 collected = len(self.buf)
877 for chunk in self.in_iter:
885 for chunk in self.in_iter:
878 collector.write(chunk)
886 collector.write(chunk)
879 collected += len(chunk)
887 collected += len(chunk)
880 if collected >= targetsize:
888 if collected >= targetsize:
881 break
889 break
882 if collected < targetsize:
890 if collected < targetsize:
883 self.iterempty = True
891 self.iterempty = True
884 self.buf = collector.getvalue()
892 self.buf = collector.getvalue()
885 s, self.buf = self.buf[:l], buffer(self.buf, l)
893 s, self.buf = self.buf[:l], buffer(self.buf, l)
886 return s
894 return s
887
895
888 def filechunkiter(f, size=65536, limit=None):
896 def filechunkiter(f, size=65536, limit=None):
889 """Create a generator that produces the data in the file size
897 """Create a generator that produces the data in the file size
890 (default 65536) bytes at a time, up to optional limit (default is
898 (default 65536) bytes at a time, up to optional limit (default is
891 to read all data). Chunks may be less than size bytes if the
899 to read all data). Chunks may be less than size bytes if the
892 chunk is the last chunk in the file, or the file is a socket or
900 chunk is the last chunk in the file, or the file is a socket or
893 some other type of file that sometimes reads less data than is
901 some other type of file that sometimes reads less data than is
894 requested."""
902 requested."""
895 assert size >= 0
903 assert size >= 0
896 assert limit is None or limit >= 0
904 assert limit is None or limit >= 0
897 while True:
905 while True:
898 if limit is None: nbytes = size
906 if limit is None: nbytes = size
899 else: nbytes = min(limit, size)
907 else: nbytes = min(limit, size)
900 s = nbytes and f.read(nbytes)
908 s = nbytes and f.read(nbytes)
901 if not s: break
909 if not s: break
902 if limit: limit -= len(s)
910 if limit: limit -= len(s)
903 yield s
911 yield s
904
912
905 def makedate():
913 def makedate():
906 lt = time.localtime()
914 lt = time.localtime()
907 if lt[8] == 1 and time.daylight:
915 if lt[8] == 1 and time.daylight:
908 tz = time.altzone
916 tz = time.altzone
909 else:
917 else:
910 tz = time.timezone
918 tz = time.timezone
911 return time.mktime(lt), tz
919 return time.mktime(lt), tz
912
920
913 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
921 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
914 """represent a (unixtime, offset) tuple as a localized time.
922 """represent a (unixtime, offset) tuple as a localized time.
915 unixtime is seconds since the epoch, and offset is the time zone's
923 unixtime is seconds since the epoch, and offset is the time zone's
916 number of seconds away from UTC. if timezone is false, do not
924 number of seconds away from UTC. if timezone is false, do not
917 append time zone to string."""
925 append time zone to string."""
918 t, tz = date or makedate()
926 t, tz = date or makedate()
919 s = time.strftime(format, time.gmtime(float(t) - tz))
927 s = time.strftime(format, time.gmtime(float(t) - tz))
920 if timezone:
928 if timezone:
921 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
929 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
922 return s
930 return s
923
931
924 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
932 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
925 """parse a localized time string and return a (unixtime, offset) tuple.
933 """parse a localized time string and return a (unixtime, offset) tuple.
926 if the string cannot be parsed, ValueError is raised."""
934 if the string cannot be parsed, ValueError is raised."""
927 def hastimezone(string):
935 def hastimezone(string):
928 return (string[-4:].isdigit() and
936 return (string[-4:].isdigit() and
929 (string[-5] == '+' or string[-5] == '-') and
937 (string[-5] == '+' or string[-5] == '-') and
930 string[-6].isspace())
938 string[-6].isspace())
931
939
932 # NOTE: unixtime = localunixtime + offset
940 # NOTE: unixtime = localunixtime + offset
933 if hastimezone(string):
941 if hastimezone(string):
934 date, tz = string[:-6], string[-5:]
942 date, tz = string[:-6], string[-5:]
935 tz = int(tz)
943 tz = int(tz)
936 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
944 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
937 else:
945 else:
938 date, offset = string, None
946 date, offset = string, None
939 timetuple = time.strptime(date, format)
947 timetuple = time.strptime(date, format)
940 localunixtime = int(calendar.timegm(timetuple))
948 localunixtime = int(calendar.timegm(timetuple))
941 if offset is None:
949 if offset is None:
942 # local timezone
950 # local timezone
943 unixtime = int(time.mktime(timetuple))
951 unixtime = int(time.mktime(timetuple))
944 offset = unixtime - localunixtime
952 offset = unixtime - localunixtime
945 else:
953 else:
946 unixtime = localunixtime + offset
954 unixtime = localunixtime + offset
947 return unixtime, offset
955 return unixtime, offset
948
956
949 def parsedate(string, formats=None):
957 def parsedate(string, formats=None):
950 """parse a localized time string and return a (unixtime, offset) tuple.
958 """parse a localized time string and return a (unixtime, offset) tuple.
951 The date may be a "unixtime offset" string or in one of the specified
959 The date may be a "unixtime offset" string or in one of the specified
952 formats."""
960 formats."""
953 if not formats:
961 if not formats:
954 formats = defaultdateformats
962 formats = defaultdateformats
955 try:
963 try:
956 when, offset = map(int, string.split(' '))
964 when, offset = map(int, string.split(' '))
957 except ValueError:
965 except ValueError:
958 for format in formats:
966 for format in formats:
959 try:
967 try:
960 when, offset = strdate(string, format)
968 when, offset = strdate(string, format)
961 except ValueError:
969 except ValueError:
962 pass
970 pass
963 else:
971 else:
964 break
972 break
965 else:
973 else:
966 raise ValueError(_('invalid date: %r '
974 raise ValueError(_('invalid date: %r '
967 'see hg(1) manual page for details')
975 'see hg(1) manual page for details')
968 % string)
976 % string)
969 # validate explicit (probably user-specified) date and
977 # validate explicit (probably user-specified) date and
970 # time zone offset. values must fit in signed 32 bits for
978 # time zone offset. values must fit in signed 32 bits for
971 # current 32-bit linux runtimes. timezones go from UTC-12
979 # current 32-bit linux runtimes. timezones go from UTC-12
972 # to UTC+14
980 # to UTC+14
973 if abs(when) > 0x7fffffff:
981 if abs(when) > 0x7fffffff:
974 raise ValueError(_('date exceeds 32 bits: %d') % when)
982 raise ValueError(_('date exceeds 32 bits: %d') % when)
975 if offset < -50400 or offset > 43200:
983 if offset < -50400 or offset > 43200:
976 raise ValueError(_('impossible time zone offset: %d') % offset)
984 raise ValueError(_('impossible time zone offset: %d') % offset)
977 return when, offset
985 return when, offset
978
986
979 def shortuser(user):
987 def shortuser(user):
980 """Return a short representation of a user name or email address."""
988 """Return a short representation of a user name or email address."""
981 f = user.find('@')
989 f = user.find('@')
982 if f >= 0:
990 if f >= 0:
983 user = user[:f]
991 user = user[:f]
984 f = user.find('<')
992 f = user.find('<')
985 if f >= 0:
993 if f >= 0:
986 user = user[f+1:]
994 user = user[f+1:]
987 f = user.find(' ')
995 f = user.find(' ')
988 if f >= 0:
996 if f >= 0:
989 user = user[:f]
997 user = user[:f]
990 f = user.find('.')
998 f = user.find('.')
991 if f >= 0:
999 if f >= 0:
992 user = user[:f]
1000 user = user[:f]
993 return user
1001 return user
994
1002
995 def walkrepos(path):
1003 def walkrepos(path):
996 '''yield every hg repository under path, recursively.'''
1004 '''yield every hg repository under path, recursively.'''
997 def errhandler(err):
1005 def errhandler(err):
998 if err.filename == path:
1006 if err.filename == path:
999 raise err
1007 raise err
1000
1008
1001 for root, dirs, files in os.walk(path, onerror=errhandler):
1009 for root, dirs, files in os.walk(path, onerror=errhandler):
1002 for d in dirs:
1010 for d in dirs:
1003 if d == '.hg':
1011 if d == '.hg':
1004 yield root
1012 yield root
1005 dirs[:] = []
1013 dirs[:] = []
1006 break
1014 break
1007
1015
1008 _rcpath = None
1016 _rcpath = None
1009
1017
1010 def rcpath():
1018 def rcpath():
1011 '''return hgrc search path. if env var HGRCPATH is set, use it.
1019 '''return hgrc search path. if env var HGRCPATH is set, use it.
1012 for each item in path, if directory, use files ending in .rc,
1020 for each item in path, if directory, use files ending in .rc,
1013 else use item.
1021 else use item.
1014 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1022 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1015 if no HGRCPATH, use default os-specific path.'''
1023 if no HGRCPATH, use default os-specific path.'''
1016 global _rcpath
1024 global _rcpath
1017 if _rcpath is None:
1025 if _rcpath is None:
1018 if 'HGRCPATH' in os.environ:
1026 if 'HGRCPATH' in os.environ:
1019 _rcpath = []
1027 _rcpath = []
1020 for p in os.environ['HGRCPATH'].split(os.pathsep):
1028 for p in os.environ['HGRCPATH'].split(os.pathsep):
1021 if not p: continue
1029 if not p: continue
1022 if os.path.isdir(p):
1030 if os.path.isdir(p):
1023 for f in os.listdir(p):
1031 for f in os.listdir(p):
1024 if f.endswith('.rc'):
1032 if f.endswith('.rc'):
1025 _rcpath.append(os.path.join(p, f))
1033 _rcpath.append(os.path.join(p, f))
1026 else:
1034 else:
1027 _rcpath.append(p)
1035 _rcpath.append(p)
1028 else:
1036 else:
1029 _rcpath = os_rcpath()
1037 _rcpath = os_rcpath()
1030 return _rcpath
1038 return _rcpath
1031
1039
1032 def bytecount(nbytes):
1040 def bytecount(nbytes):
1033 '''return byte count formatted as readable string, with units'''
1041 '''return byte count formatted as readable string, with units'''
1034
1042
1035 units = (
1043 units = (
1036 (100, 1<<30, _('%.0f GB')),
1044 (100, 1<<30, _('%.0f GB')),
1037 (10, 1<<30, _('%.1f GB')),
1045 (10, 1<<30, _('%.1f GB')),
1038 (1, 1<<30, _('%.2f GB')),
1046 (1, 1<<30, _('%.2f GB')),
1039 (100, 1<<20, _('%.0f MB')),
1047 (100, 1<<20, _('%.0f MB')),
1040 (10, 1<<20, _('%.1f MB')),
1048 (10, 1<<20, _('%.1f MB')),
1041 (1, 1<<20, _('%.2f MB')),
1049 (1, 1<<20, _('%.2f MB')),
1042 (100, 1<<10, _('%.0f KB')),
1050 (100, 1<<10, _('%.0f KB')),
1043 (10, 1<<10, _('%.1f KB')),
1051 (10, 1<<10, _('%.1f KB')),
1044 (1, 1<<10, _('%.2f KB')),
1052 (1, 1<<10, _('%.2f KB')),
1045 (1, 1, _('%.0f bytes')),
1053 (1, 1, _('%.0f bytes')),
1046 )
1054 )
1047
1055
1048 for multiplier, divisor, format in units:
1056 for multiplier, divisor, format in units:
1049 if nbytes >= divisor * multiplier:
1057 if nbytes >= divisor * multiplier:
1050 return format % (nbytes / float(divisor))
1058 return format % (nbytes / float(divisor))
1051 return units[-1][2] % nbytes
1059 return units[-1][2] % nbytes
1052
1060
1053 def drop_scheme(scheme, path):
1061 def drop_scheme(scheme, path):
1054 sc = scheme + ':'
1062 sc = scheme + ':'
1055 if path.startswith(sc):
1063 if path.startswith(sc):
1056 path = path[len(sc):]
1064 path = path[len(sc):]
1057 if path.startswith('//'):
1065 if path.startswith('//'):
1058 path = path[2:]
1066 path = path[2:]
1059 return path
1067 return path
General Comments 0
You need to be logged in to leave comments. Login now