##// END OF EJS Templates
generalize copy/rename to handle more than one source directory
Robin Farine -
r1512:53ad6ee6 default
parent child Browse files
Show More
@@ -0,0 +1,60 b''
1 #!/bin/sh
2
3 hg init
4 mkdir d1 d1/d11 d2
5 echo d1/a > d1/a
6 echo d1/ba > d1/ba
7 echo d1/a1 > d1/d11/a1
8 echo d1/b > d1/b
9 echo d2/b > d2/b
10 hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
11 hg commit -m "1" -d "0 0"
12
13 echo "# rename a single file"
14 hg rename d1/d11/a1 d2/c
15 hg status
16 hg update -C
17
18 echo "# move a single file to an existing directory"
19 hg rename d1/d11/a1 d2
20 hg status
21 hg update -C
22
23 echo "# rename directory d1 as d3"
24 hg rename d1 d3
25 hg status
26 hg update -C
27
28 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
29 hg rename d1/d11 d2
30 hg status
31 hg update -C
32
33 echo "# move directories d1 and d2 to a new directory d3"
34 mkdir d3
35 hg rename d1 d2 d3
36 hg status
37 hg update -C
38
39 echo "# move everything under directory d1 to existing directory d2, do not"
40 echo "# overwrite existing files (d2/b)"
41 hg rename d1/* d2
42 hg status
43 diff d1/b d2/b
44 hg update -C
45
46 echo "# attempt to move potentially more than one file into a non-existent"
47 echo "# directory"
48 hg rename 'glob:d1/**' dx
49
50 echo "# move every file under d1 to d2/d21 (glob)"
51 mkdir d2/d21
52 hg rename 'glob:d1/**' d2/d21
53 hg status
54 hg update -C
55
56 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
57 mkdir d2/d21
58 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
59 hg status
60 hg update -C
@@ -0,0 +1,93 b''
1 # rename a single file
2 A d2/c
3 R d1/d11/a1
4 # move a single file to an existing directory
5 A d2/a1
6 R d1/d11/a1
7 # rename directory d1 as d3
8 copying d1/a to d3/a
9 copying d1/b to d3/b
10 copying d1/ba to d3/ba
11 copying d1/d11/a1 to d3/d11/a1
12 removing d1/a
13 removing d1/b
14 removing d1/ba
15 removing d1/d11/a1
16 A d3/a
17 A d3/b
18 A d3/ba
19 A d3/d11/a1
20 R d1/a
21 R d1/b
22 R d1/ba
23 R d1/d11/a1
24 # move directory d1/d11 to an existing directory d2 (removes empty d1)
25 copying d1/d11/a1 to d2/d11/a1
26 removing d1/d11/a1
27 A d2/d11/a1
28 R d1/d11/a1
29 # move directories d1 and d2 to a new directory d3
30 copying d1/a to d3/d1/a
31 copying d1/b to d3/d1/b
32 copying d1/ba to d3/d1/ba
33 copying d1/d11/a1 to d3/d1/d11/a1
34 copying d2/b to d3/d2/b
35 removing d1/a
36 removing d1/b
37 removing d1/ba
38 removing d1/d11/a1
39 removing d2/b
40 A d3/d1/a
41 A d3/d1/b
42 A d3/d1/ba
43 A d3/d1/d11/a1
44 A d3/d2/b
45 R d1/a
46 R d1/b
47 R d1/ba
48 R d1/d11/a1
49 R d2/b
50 # move everything under directory d1 to existing directory d2, do not
51 # overwrite existing files (d2/b)
52 d2/b: not overwriting - file already managed
53 copying d1/d11/a1 to d2/d11/a1
54 removing d1/d11/a1
55 A d2/a
56 A d2/ba
57 A d2/d11/a1
58 R d1/a
59 R d1/ba
60 R d1/d11/a1
61 1c1
62 < d1/b
63 ---
64 > d2/b
65 # attempt to move potentially more than one file into a non-existent
66 # directory
67 abort: with multiple sources, destination must be an existing directory
68 # move every file under d1 to d2/d21 (glob)
69 copying d1/a to d2/d21/a
70 copying d1/b to d2/d21/b
71 copying d1/ba to d2/d21/ba
72 copying d1/d11/a1 to d2/d21/a1
73 removing d1/a
74 removing d1/b
75 removing d1/ba
76 removing d1/d11/a1
77 A d2/d21/a
78 A d2/d21/a1
79 A d2/d21/b
80 A d2/d21/ba
81 R d1/a
82 R d1/b
83 R d1/ba
84 R d1/d11/a1
85 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
86 copying d1/a to d2/d21/a
87 copying d1/d11/a1 to d2/d21/a1
88 removing d1/a
89 removing d1/d11/a1
90 A d2/d21/a
91 A d2/d21/a1
92 R d1/a
93 R d1/d11/a1
@@ -1,2634 +1,2623 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog")
13 13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
14 14 demandload(globals(), "errno socket version struct atexit sets bz2")
15 15
16 16 class UnknownCommand(Exception):
17 17 """Exception raised if command is not in the command table."""
18 18
19 19 def filterfiles(filters, files):
20 20 l = [x for x in files if x in filters]
21 21
22 22 for t in filters:
23 23 if t and t[-1] != "/":
24 24 t += "/"
25 25 l += [x for x in files if x.startswith(t)]
26 26 return l
27 27
28 28 def relpath(repo, args):
29 29 cwd = repo.getcwd()
30 30 if cwd:
31 31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 32 return args
33 33
34 34 def matchpats(repo, cwd, pats=[], opts={}, head=''):
35 35 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
36 36 opts.get('exclude'), head)
37 37
38 38 def makewalk(repo, pats, opts, head=''):
39 39 cwd = repo.getcwd()
40 40 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
41 41 exact = dict(zip(files, files))
42 42 def walk():
43 43 for src, fn in repo.walk(files=files, match=matchfn):
44 44 yield src, fn, util.pathto(cwd, fn), fn in exact
45 45 return files, matchfn, walk()
46 46
47 47 def walk(repo, pats, opts, head=''):
48 48 files, matchfn, results = makewalk(repo, pats, opts, head)
49 49 for r in results:
50 50 yield r
51 51
52 52 def walkchangerevs(ui, repo, cwd, pats, opts):
53 53 '''Iterate over files and the revs they changed in.
54 54
55 55 Callers most commonly need to iterate backwards over the history
56 56 it is interested in. Doing so has awful (quadratic-looking)
57 57 performance, so we use iterators in a "windowed" way.
58 58
59 59 We walk a window of revisions in the desired order. Within the
60 60 window, we first walk forwards to gather data, then in the desired
61 61 order (usually backwards) to display it.
62 62
63 63 This function returns an (iterator, getchange) pair. The
64 64 getchange function returns the changelog entry for a numeric
65 65 revision. The iterator yields 3-tuples. They will be of one of
66 66 the following forms:
67 67
68 68 "window", incrementing, lastrev: stepping through a window,
69 69 positive if walking forwards through revs, last rev in the
70 70 sequence iterated over - use to reset state for the current window
71 71
72 72 "add", rev, fns: out-of-order traversal of the given file names
73 73 fns, which changed during revision rev - use to gather data for
74 74 possible display
75 75
76 76 "iter", rev, None: in-order traversal of the revs earlier iterated
77 77 over with "add" - use to display data'''
78 78
79 79 if repo.changelog.count() == 0:
80 80 return [], False
81 81
82 82 cwd = repo.getcwd()
83 83 if not pats and cwd:
84 84 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
85 85 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
86 86 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
87 87 pats, opts)
88 88 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
89 89 wanted = {}
90 90 slowpath = anypats
91 91 window = 300
92 92 fncache = {}
93 93
94 94 chcache = {}
95 95 def getchange(rev):
96 96 ch = chcache.get(rev)
97 97 if ch is None:
98 98 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
99 99 return ch
100 100
101 101 if not slowpath and not files:
102 102 # No files, no patterns. Display all revs.
103 103 wanted = dict(zip(revs, revs))
104 104 if not slowpath:
105 105 # Only files, no patterns. Check the history of each file.
106 106 def filerevgen(filelog):
107 107 for i in xrange(filelog.count() - 1, -1, -window):
108 108 revs = []
109 109 for j in xrange(max(0, i - window), i + 1):
110 110 revs.append(filelog.linkrev(filelog.node(j)))
111 111 revs.reverse()
112 112 for rev in revs:
113 113 yield rev
114 114
115 115 minrev, maxrev = min(revs), max(revs)
116 116 for file in files:
117 117 filelog = repo.file(file)
118 118 # A zero count may be a directory or deleted file, so
119 119 # try to find matching entries on the slow path.
120 120 if filelog.count() == 0:
121 121 slowpath = True
122 122 break
123 123 for rev in filerevgen(filelog):
124 124 if rev <= maxrev:
125 125 if rev < minrev:
126 126 break
127 127 fncache.setdefault(rev, [])
128 128 fncache[rev].append(file)
129 129 wanted[rev] = 1
130 130 if slowpath:
131 131 # The slow path checks files modified in every changeset.
132 132 def changerevgen():
133 133 for i in xrange(repo.changelog.count() - 1, -1, -window):
134 134 for j in xrange(max(0, i - window), i + 1):
135 135 yield j, getchange(j)[3]
136 136
137 137 for rev, changefiles in changerevgen():
138 138 matches = filter(matchfn, changefiles)
139 139 if matches:
140 140 fncache[rev] = matches
141 141 wanted[rev] = 1
142 142
143 143 def iterate():
144 144 for i in xrange(0, len(revs), window):
145 145 yield 'window', revs[0] < revs[-1], revs[-1]
146 146 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
147 147 if rev in wanted]
148 148 srevs = list(nrevs)
149 149 srevs.sort()
150 150 for rev in srevs:
151 151 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
152 152 yield 'add', rev, fns
153 153 for rev in nrevs:
154 154 yield 'iter', rev, None
155 155 return iterate(), getchange
156 156
157 157 revrangesep = ':'
158 158
159 159 def revrange(ui, repo, revs, revlog=None):
160 160 """Yield revision as strings from a list of revision specifications."""
161 161 if revlog is None:
162 162 revlog = repo.changelog
163 163 revcount = revlog.count()
164 164 def fix(val, defval):
165 165 if not val:
166 166 return defval
167 167 try:
168 168 num = int(val)
169 169 if str(num) != val:
170 170 raise ValueError
171 171 if num < 0: num += revcount
172 172 if num < 0: num = 0
173 173 elif num >= revcount:
174 174 raise ValueError
175 175 except ValueError:
176 176 try:
177 177 num = repo.changelog.rev(repo.lookup(val))
178 178 except KeyError:
179 179 try:
180 180 num = revlog.rev(revlog.lookup(val))
181 181 except KeyError:
182 182 raise util.Abort(_('invalid revision identifier %s'), val)
183 183 return num
184 184 seen = {}
185 185 for spec in revs:
186 186 if spec.find(revrangesep) >= 0:
187 187 start, end = spec.split(revrangesep, 1)
188 188 start = fix(start, 0)
189 189 end = fix(end, revcount - 1)
190 190 step = start > end and -1 or 1
191 191 for rev in xrange(start, end+step, step):
192 192 if rev in seen: continue
193 193 seen[rev] = 1
194 194 yield str(rev)
195 195 else:
196 196 rev = fix(spec, None)
197 197 if rev in seen: continue
198 198 seen[rev] = 1
199 199 yield str(rev)
200 200
201 201 def make_filename(repo, r, pat, node=None,
202 202 total=None, seqno=None, revwidth=None, pathname=None):
203 203 node_expander = {
204 204 'H': lambda: hex(node),
205 205 'R': lambda: str(r.rev(node)),
206 206 'h': lambda: short(node),
207 207 }
208 208 expander = {
209 209 '%': lambda: '%',
210 210 'b': lambda: os.path.basename(repo.root),
211 211 }
212 212
213 213 try:
214 214 if node:
215 215 expander.update(node_expander)
216 216 if node and revwidth is not None:
217 217 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
218 218 if total is not None:
219 219 expander['N'] = lambda: str(total)
220 220 if seqno is not None:
221 221 expander['n'] = lambda: str(seqno)
222 222 if total is not None and seqno is not None:
223 223 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
224 224 if pathname is not None:
225 225 expander['s'] = lambda: os.path.basename(pathname)
226 226 expander['d'] = lambda: os.path.dirname(pathname) or '.'
227 227 expander['p'] = lambda: pathname
228 228
229 229 newname = []
230 230 patlen = len(pat)
231 231 i = 0
232 232 while i < patlen:
233 233 c = pat[i]
234 234 if c == '%':
235 235 i += 1
236 236 c = pat[i]
237 237 c = expander[c]()
238 238 newname.append(c)
239 239 i += 1
240 240 return ''.join(newname)
241 241 except KeyError, inst:
242 242 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
243 243 inst.args[0])
244 244
245 245 def make_file(repo, r, pat, node=None,
246 246 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
247 247 if not pat or pat == '-':
248 248 return 'w' in mode and sys.stdout or sys.stdin
249 249 if hasattr(pat, 'write') and 'w' in mode:
250 250 return pat
251 251 if hasattr(pat, 'read') and 'r' in mode:
252 252 return pat
253 253 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
254 254 pathname),
255 255 mode)
256 256
257 257 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
258 258 changes=None, text=False):
259 259 if not changes:
260 260 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
261 261 else:
262 262 (c, a, d, u) = changes
263 263 if files:
264 264 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
265 265
266 266 if not c and not a and not d:
267 267 return
268 268
269 269 if node2:
270 270 change = repo.changelog.read(node2)
271 271 mmap2 = repo.manifest.read(change[0])
272 272 date2 = util.datestr(change[2])
273 273 def read(f):
274 274 return repo.file(f).read(mmap2[f])
275 275 else:
276 276 date2 = util.datestr()
277 277 if not node1:
278 278 node1 = repo.dirstate.parents()[0]
279 279 def read(f):
280 280 return repo.wfile(f).read()
281 281
282 282 if ui.quiet:
283 283 r = None
284 284 else:
285 285 hexfunc = ui.verbose and hex or short
286 286 r = [hexfunc(node) for node in [node1, node2] if node]
287 287
288 288 change = repo.changelog.read(node1)
289 289 mmap = repo.manifest.read(change[0])
290 290 date1 = util.datestr(change[2])
291 291
292 292 for f in c:
293 293 to = None
294 294 if f in mmap:
295 295 to = repo.file(f).read(mmap[f])
296 296 tn = read(f)
297 297 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
298 298 for f in a:
299 299 to = None
300 300 tn = read(f)
301 301 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
302 302 for f in d:
303 303 to = repo.file(f).read(mmap[f])
304 304 tn = None
305 305 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
306 306
307 307 def trimuser(ui, name, rev, revcache):
308 308 """trim the name of the user who committed a change"""
309 309 user = revcache.get(rev)
310 310 if user is None:
311 311 user = revcache[rev] = ui.shortuser(name)
312 312 return user
313 313
314 314 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
315 315 """show a single changeset or file revision"""
316 316 log = repo.changelog
317 317 if changenode is None:
318 318 changenode = log.node(rev)
319 319 elif not rev:
320 320 rev = log.rev(changenode)
321 321
322 322 if ui.quiet:
323 323 ui.write("%d:%s\n" % (rev, short(changenode)))
324 324 return
325 325
326 326 changes = log.read(changenode)
327 327 date = util.datestr(changes[2])
328 328
329 329 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
330 330 for p in log.parents(changenode)
331 331 if ui.debugflag or p != nullid]
332 332 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
333 333 parents = []
334 334
335 335 if ui.verbose:
336 336 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
337 337 else:
338 338 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
339 339
340 340 for tag in repo.nodetags(changenode):
341 341 ui.status(_("tag: %s\n") % tag)
342 342 for parent in parents:
343 343 ui.write(_("parent: %d:%s\n") % parent)
344 344
345 345 if brinfo and changenode in brinfo:
346 346 br = brinfo[changenode]
347 347 ui.write(_("branch: %s\n") % " ".join(br))
348 348
349 349 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
350 350 hex(changes[0])))
351 351 ui.status(_("user: %s\n") % changes[1])
352 352 ui.status(_("date: %s\n") % date)
353 353
354 354 if ui.debugflag:
355 355 files = repo.changes(log.parents(changenode)[0], changenode)
356 356 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
357 357 if value:
358 358 ui.note("%-12s %s\n" % (key, " ".join(value)))
359 359 else:
360 360 ui.note(_("files: %s\n") % " ".join(changes[3]))
361 361
362 362 description = changes[4].strip()
363 363 if description:
364 364 if ui.verbose:
365 365 ui.status(_("description:\n"))
366 366 ui.status(description)
367 367 ui.status("\n\n")
368 368 else:
369 369 ui.status(_("summary: %s\n") % description.splitlines()[0])
370 370 ui.status("\n")
371 371
372 372 def show_version(ui):
373 373 """output version and copyright information"""
374 374 ui.write(_("Mercurial Distributed SCM (version %s)\n")
375 375 % version.get_version())
376 376 ui.status(_(
377 377 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
378 378 "This is free software; see the source for copying conditions. "
379 379 "There is NO\nwarranty; "
380 380 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
381 381 ))
382 382
383 383 def help_(ui, cmd=None, with_version=False):
384 384 """show help for a given command or all commands"""
385 385 option_lists = []
386 386 if cmd and cmd != 'shortlist':
387 387 if with_version:
388 388 show_version(ui)
389 389 ui.write('\n')
390 390 key, i = find(cmd)
391 391 # synopsis
392 392 ui.write("%s\n\n" % i[2])
393 393
394 394 # description
395 395 doc = i[0].__doc__
396 396 if ui.quiet:
397 397 doc = doc.splitlines(0)[0]
398 398 ui.write("%s\n" % doc.rstrip())
399 399
400 400 if not ui.quiet:
401 401 # aliases
402 402 aliases = ', '.join(key.split('|')[1:])
403 403 if aliases:
404 404 ui.write(_("\naliases: %s\n") % aliases)
405 405
406 406 # options
407 407 if i[1]:
408 408 option_lists.append(("options", i[1]))
409 409
410 410 else:
411 411 # program name
412 412 if ui.verbose or with_version:
413 413 show_version(ui)
414 414 else:
415 415 ui.status(_("Mercurial Distributed SCM\n"))
416 416 ui.status('\n')
417 417
418 418 # list of commands
419 419 if cmd == "shortlist":
420 420 ui.status(_('basic commands (use "hg help" '
421 421 'for the full list or option "-v" for details):\n\n'))
422 422 elif ui.verbose:
423 423 ui.status(_('list of commands:\n\n'))
424 424 else:
425 425 ui.status(_('list of commands (use "hg help -v" '
426 426 'to show aliases and global options):\n\n'))
427 427
428 428 h = {}
429 429 cmds = {}
430 430 for c, e in table.items():
431 431 f = c.split("|")[0]
432 432 if cmd == "shortlist" and not f.startswith("^"):
433 433 continue
434 434 f = f.lstrip("^")
435 435 if not ui.debugflag and f.startswith("debug"):
436 436 continue
437 437 d = ""
438 438 if e[0].__doc__:
439 439 d = e[0].__doc__.splitlines(0)[0].rstrip()
440 440 h[f] = d
441 441 cmds[f]=c.lstrip("^")
442 442
443 443 fns = h.keys()
444 444 fns.sort()
445 445 m = max(map(len, fns))
446 446 for f in fns:
447 447 if ui.verbose:
448 448 commands = cmds[f].replace("|",", ")
449 449 ui.write(" %s:\n %s\n"%(commands,h[f]))
450 450 else:
451 451 ui.write(' %-*s %s\n' % (m, f, h[f]))
452 452
453 453 # global options
454 454 if ui.verbose:
455 455 option_lists.append(("global options", globalopts))
456 456
457 457 # list all option lists
458 458 opt_output = []
459 459 for title, options in option_lists:
460 460 opt_output.append(("\n%s:\n" % title, None))
461 461 for shortopt, longopt, default, desc in options:
462 462 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
463 463 longopt and " --%s" % longopt),
464 464 "%s%s" % (desc,
465 465 default and _(" (default: %s)") % default
466 466 or "")))
467 467
468 468 if opt_output:
469 469 opts_len = max([len(line[0]) for line in opt_output if line[1]])
470 470 for first, second in opt_output:
471 471 if second:
472 472 ui.write(" %-*s %s\n" % (opts_len, first, second))
473 473 else:
474 474 ui.write("%s\n" % first)
475 475
476 476 # Commands start here, listed alphabetically
477 477
478 478 def add(ui, repo, *pats, **opts):
479 479 """add the specified files on the next commit
480 480
481 481 Schedule files to be version controlled and added to the repository.
482 482
483 483 The files will be added to the repository at the next commit.
484 484
485 485 If no names are given, add all files in the current directory and
486 486 its subdirectories.
487 487 """
488 488
489 489 names = []
490 490 for src, abs, rel, exact in walk(repo, pats, opts):
491 491 if exact:
492 492 if ui.verbose: ui.status(_('adding %s\n') % rel)
493 493 names.append(abs)
494 494 elif repo.dirstate.state(abs) == '?':
495 495 ui.status(_('adding %s\n') % rel)
496 496 names.append(abs)
497 497 repo.add(names)
498 498
499 499 def addremove(ui, repo, *pats, **opts):
500 500 """add all new files, delete all missing files
501 501
502 502 Add all new files and remove all missing files from the repository.
503 503
504 504 New files are ignored if they match any of the patterns in .hgignore. As
505 505 with add, these changes take effect at the next commit.
506 506 """
507 507 add, remove = [], []
508 508 for src, abs, rel, exact in walk(repo, pats, opts):
509 509 if src == 'f' and repo.dirstate.state(abs) == '?':
510 510 add.append(abs)
511 511 if ui.verbose or not exact:
512 512 ui.status(_('adding %s\n') % rel)
513 513 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
514 514 remove.append(abs)
515 515 if ui.verbose or not exact:
516 516 ui.status(_('removing %s\n') % rel)
517 517 repo.add(add)
518 518 repo.remove(remove)
519 519
520 520 def annotate(ui, repo, *pats, **opts):
521 521 """show changeset information per file line
522 522
523 523 List changes in files, showing the revision id responsible for each line
524 524
525 525 This command is useful to discover who did a change or when a change took
526 526 place.
527 527
528 528 Without the -a option, annotate will avoid processing files it
529 529 detects as binary. With -a, annotate will generate an annotation
530 530 anyway, probably with undesirable results.
531 531 """
532 532 def getnode(rev):
533 533 return short(repo.changelog.node(rev))
534 534
535 535 ucache = {}
536 536 def getname(rev):
537 537 cl = repo.changelog.read(repo.changelog.node(rev))
538 538 return trimuser(ui, cl[1], rev, ucache)
539 539
540 540 if not pats:
541 541 raise util.Abort(_('at least one file name or pattern required'))
542 542
543 543 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
544 544 if not opts['user'] and not opts['changeset']:
545 545 opts['number'] = 1
546 546
547 547 if opts['rev']:
548 548 node = repo.changelog.lookup(opts['rev'])
549 549 else:
550 550 node = repo.dirstate.parents()[0]
551 551 change = repo.changelog.read(node)
552 552 mmap = repo.manifest.read(change[0])
553 553
554 554 for src, abs, rel, exact in walk(repo, pats, opts):
555 555 if abs not in mmap:
556 556 ui.warn(_("warning: %s is not in the repository!\n") % rel)
557 557 continue
558 558
559 559 f = repo.file(abs)
560 560 if not opts['text'] and util.binary(f.read(mmap[abs])):
561 561 ui.write(_("%s: binary file\n") % rel)
562 562 continue
563 563
564 564 lines = f.annotate(mmap[abs])
565 565 pieces = []
566 566
567 567 for o, f in opmap:
568 568 if opts[o]:
569 569 l = [f(n) for n, dummy in lines]
570 570 if l:
571 571 m = max(map(len, l))
572 572 pieces.append(["%*s" % (m, x) for x in l])
573 573
574 574 if pieces:
575 575 for p, l in zip(zip(*pieces), lines):
576 576 ui.write("%s: %s" % (" ".join(p), l[1]))
577 577
578 578 def bundle(ui, repo, fname, dest="default-push", **opts):
579 579 """create a changegroup file
580 580
581 581 Generate a compressed changegroup file collecting all changesets
582 582 not found in the other repository.
583 583
584 584 This file can then be transferred using conventional means and
585 585 applied to another repository with the unbundle command. This is
586 586 useful when native push and pull are not available or when
587 587 exporting an entire repository is undesirable. The standard file
588 588 extension is ".hg".
589 589
590 590 Unlike import/export, this exactly preserves all changeset
591 591 contents including permissions, rename data, and revision history.
592 592 """
593 593 f = open(fname, "wb")
594 594 dest = ui.expandpath(dest, repo.root)
595 595 other = hg.repository(ui, dest)
596 596 o = repo.findoutgoing(other)
597 597 cg = repo.changegroup(o)
598 598
599 599 try:
600 600 f.write("HG10")
601 601 z = bz2.BZ2Compressor(9)
602 602 while 1:
603 603 chunk = cg.read(4096)
604 604 if not chunk:
605 605 break
606 606 f.write(z.compress(chunk))
607 607 f.write(z.flush())
608 608 except:
609 609 os.unlink(fname)
610 610 raise
611 611
612 612 def cat(ui, repo, file1, *pats, **opts):
613 613 """output the latest or given revisions of files
614 614
615 615 Print the specified files as they were at the given revision.
616 616 If no revision is given then the tip is used.
617 617
618 618 Output may be to a file, in which case the name of the file is
619 619 given using a format string. The formatting rules are the same as
620 620 for the export command, with the following additions:
621 621
622 622 %s basename of file being printed
623 623 %d dirname of file being printed, or '.' if in repo root
624 624 %p root-relative path name of file being printed
625 625 """
626 626 mf = {}
627 627 if opts['rev']:
628 628 change = repo.changelog.read(repo.lookup(opts['rev']))
629 629 mf = repo.manifest.read(change[0])
630 630 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
631 631 r = repo.file(abs)
632 632 if opts['rev']:
633 633 try:
634 634 n = mf[abs]
635 635 except (hg.RepoError, KeyError):
636 636 try:
637 637 n = r.lookup(rev)
638 638 except KeyError, inst:
639 639 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
640 640 else:
641 641 n = r.tip()
642 642 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
643 643 fp.write(r.read(n))
644 644
645 645 def clone(ui, source, dest=None, **opts):
646 646 """make a copy of an existing repository
647 647
648 648 Create a copy of an existing repository in a new directory.
649 649
650 650 If no destination directory name is specified, it defaults to the
651 651 basename of the source.
652 652
653 653 The location of the source is added to the new repository's
654 654 .hg/hgrc file, as the default to be used for future pulls.
655 655
656 656 For efficiency, hardlinks are used for cloning whenever the source
657 657 and destination are on the same filesystem. Some filesystems,
658 658 such as AFS, implement hardlinking incorrectly, but do not report
659 659 errors. In these cases, use the --pull option to avoid
660 660 hardlinking.
661 661 """
662 662 if dest is None:
663 663 dest = os.path.basename(os.path.normpath(source))
664 664
665 665 if os.path.exists(dest):
666 666 raise util.Abort(_("destination '%s' already exists"), dest)
667 667
668 668 dest = os.path.realpath(dest)
669 669
670 670 class Dircleanup:
671 671 def __init__(self, dir_):
672 672 self.rmtree = shutil.rmtree
673 673 self.dir_ = dir_
674 674 os.mkdir(dir_)
675 675 def close(self):
676 676 self.dir_ = None
677 677 def __del__(self):
678 678 if self.dir_:
679 679 self.rmtree(self.dir_, True)
680 680
681 681 if opts['ssh']:
682 682 ui.setconfig("ui", "ssh", opts['ssh'])
683 683 if opts['remotecmd']:
684 684 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
685 685
686 686 if not os.path.exists(source):
687 687 source = ui.expandpath(source)
688 688
689 689 d = Dircleanup(dest)
690 690 abspath = source
691 691 other = hg.repository(ui, source)
692 692
693 693 copy = False
694 694 if other.dev() != -1:
695 695 abspath = os.path.abspath(source)
696 696 if not opts['pull'] and not opts['rev']:
697 697 copy = True
698 698
699 699 if copy:
700 700 try:
701 701 # we use a lock here because if we race with commit, we
702 702 # can end up with extra data in the cloned revlogs that's
703 703 # not pointed to by changesets, thus causing verify to
704 704 # fail
705 705 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
706 706 except OSError:
707 707 copy = False
708 708
709 709 if copy:
710 710 # we lock here to avoid premature writing to the target
711 711 os.mkdir(os.path.join(dest, ".hg"))
712 712 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
713 713
714 714 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
715 715 for f in files.split():
716 716 src = os.path.join(source, ".hg", f)
717 717 dst = os.path.join(dest, ".hg", f)
718 718 try:
719 719 util.copyfiles(src, dst)
720 720 except OSError, inst:
721 721 if inst.errno != errno.ENOENT: raise
722 722
723 723 repo = hg.repository(ui, dest)
724 724
725 725 else:
726 726 revs = None
727 727 if opts['rev']:
728 728 if not other.local():
729 729 raise util.Abort("clone -r not supported yet for remote repositories.")
730 730 else:
731 731 revs = [other.lookup(rev) for rev in opts['rev']]
732 732 repo = hg.repository(ui, dest, create=1)
733 733 repo.pull(other, heads = revs)
734 734
735 735 f = repo.opener("hgrc", "w", text=True)
736 736 f.write("[paths]\n")
737 737 f.write("default = %s\n" % abspath)
738 738
739 739 if not opts['noupdate']:
740 740 update(ui, repo)
741 741
742 742 d.close()
743 743
744 744 def commit(ui, repo, *pats, **opts):
745 745 """commit the specified files or all outstanding changes
746 746
747 747 Commit changes to the given files into the repository.
748 748
749 749 If a list of files is omitted, all changes reported by "hg status"
750 750 from the root of the repository will be commited.
751 751
752 752 The HGEDITOR or EDITOR environment variables are used to start an
753 753 editor to add a commit comment.
754 754 """
755 755 message = opts['message']
756 756 logfile = opts['logfile']
757 757
758 758 if message and logfile:
759 759 raise util.Abort(_('options --message and --logfile are mutually '
760 760 'exclusive'))
761 761 if not message and logfile:
762 762 try:
763 763 if logfile == '-':
764 764 message = sys.stdin.read()
765 765 else:
766 766 message = open(logfile).read()
767 767 except IOError, inst:
768 768 raise util.Abort(_("can't read commit message '%s': %s") %
769 769 (logfile, inst.strerror))
770 770
771 771 if opts['addremove']:
772 772 addremove(ui, repo, *pats, **opts)
773 773 cwd = repo.getcwd()
774 774 if not pats and cwd:
775 775 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
776 776 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
777 777 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
778 778 pats, opts)
779 779 if pats:
780 780 c, a, d, u = repo.changes(files=fns, match=match)
781 781 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
782 782 else:
783 783 files = []
784 784 try:
785 785 repo.commit(files, message, opts['user'], opts['date'], match)
786 786 except ValueError, inst:
787 787 raise util.Abort(str(inst))
788 788
789 789 def docopy(ui, repo, pats, opts):
790 if not pats:
791 raise util.Abort(_('no source or destination specified'))
792 elif len(pats) == 1:
793 raise util.Abort(_('no destination specified'))
794 pats = list(pats)
795 dest = pats.pop()
796 sources = []
797 dir2dir = len(pats) == 1 and os.path.isdir(pats[0])
790 cwd = repo.getcwd()
791 errors = 0
792 copied = []
798 793
799 794 def okaytocopy(abs, rel, exact):
800 795 reasons = {'?': _('is not managed'),
801 796 'a': _('has been marked for add')}
802 797 reason = reasons.get(repo.dirstate.state(abs))
803 798 if reason:
804 799 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
805 800 else:
806 801 return True
807 802
808 for src, abs, rel, exact in walk(repo, pats, opts):
809 if okaytocopy(abs, rel, exact):
810 sources.append((abs, rel, exact))
811 if not sources:
812 raise util.Abort(_('no files to copy'))
813
814 cwd = repo.getcwd()
815 absdest = util.canonpath(repo.root, cwd, dest)
816 reldest = util.pathto(cwd, absdest)
817 if os.path.exists(reldest):
818 destisfile = not os.path.isdir(reldest)
819 else:
820 destisfile = not dir2dir and (len(sources) == 1
821 or repo.dirstate.state(absdest) != '?')
822
823 if destisfile and len(sources) > 1:
824 raise util.Abort(_('with multiple sources, destination must be a '
825 'directory'))
826
827 srcpfxlen = 0
828 if dir2dir:
829 srcpfx = util.pathto(cwd, util.canonpath(repo.root, cwd, pats[0]))
830 if os.path.exists(reldest):
831 srcpfx = os.path.split(srcpfx)[0]
832 if srcpfx:
833 srcpfx += os.sep
834 srcpfxlen = len(srcpfx)
835
836 errs, copied = 0, []
837 for abs, rel, exact in sources:
838 if destisfile:
839 mydest = reldest
840 elif dir2dir:
841 mydest = os.path.join(dest, rel[srcpfxlen:])
842 else:
843 mydest = os.path.join(dest, os.path.basename(rel))
844 myabsdest = util.canonpath(repo.root, cwd, mydest)
845 myreldest = util.pathto(cwd, myabsdest)
846 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
847 ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
848 continue
849 mydestdir = os.path.dirname(myreldest) or '.'
803 def copy(abssrc, relsrc, target, exact):
804 abstarget = util.canonpath(repo.root, cwd, target)
805 reltarget = util.pathto(cwd, abstarget)
806 if not opts['force'] and repo.dirstate.state(abstarget) not in 'a?':
807 ui.warn(_('%s: not overwriting - file already managed\n') %
808 reltarget)
809 return
810 if ui.verbose or not exact:
811 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
850 812 if not opts['after']:
813 targetdir = os.path.dirname(reltarget) or '.'
814 if not os.path.isdir(targetdir):
815 os.makedirs(targetdir)
851 816 try:
852 if dir2dir: os.makedirs(mydestdir)
853 elif not destisfile: os.mkdir(mydestdir)
854 except OSError, inst:
855 if inst.errno != errno.EEXIST: raise
856 if ui.verbose or not exact:
857 ui.status(_('copying %s to %s\n') % (rel, myreldest))
858 if not opts['after']:
859 try:
860 shutil.copyfile(rel, myreldest)
861 shutil.copymode(rel, myreldest)
817 shutil.copyfile(relsrc, reltarget)
818 shutil.copymode(relsrc, reltarget)
862 819 except shutil.Error, inst:
863 820 raise util.Abort(str(inst))
864 821 except IOError, inst:
865 822 if inst.errno == errno.ENOENT:
866 ui.warn(_('%s: deleted in working copy\n') % rel)
823 ui.warn(_('%s: deleted in working copy\n') % relsrc)
867 824 else:
868 ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
869 errs += 1
870 continue
871 repo.copy(abs, myabsdest)
872 copied.append((abs, rel, exact))
873 if errs:
825 ui.warn(_('%s: cannot copy - %s\n') %
826 (relsrc, inst.strerror))
827 errors += 1
828 return
829 repo.copy(abssrc, abstarget)
830 copied.append((abssrc, relsrc, exact))
831
832 pats = list(pats)
833 if not pats:
834 raise util.Abort(_('no source or destination specified'))
835 if len(pats) == 1:
836 raise util.Abort(_('no destination specified'))
837 dest = pats.pop()
838 destdirexists = os.path.isdir(dest)
839 if (len(pats) > 1 or not os.path.exists(pats[0])) and not destdirexists:
840 raise util.Abort(_('with multiple sources, destination must be an '
841 'existing directory'))
842
843 for pat in pats:
844 if os.path.isdir(pat):
845 if destdirexists:
846 striplen = len(os.path.split(pat)[0])
847 else:
848 striplen = len(pat)
849 if striplen:
850 striplen += len(os.sep)
851 targetpath = lambda p: os.path.join(dest, p[striplen:])
852 elif destdirexists:
853 targetpath = lambda p: os.path.join(dest, os.path.basename(p))
854 else:
855 targetpath = lambda p: dest
856 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
857 if okaytocopy(abssrc, relsrc, exact):
858 copy(abssrc, relsrc, targetpath(abssrc), exact)
859
860 if errors:
874 861 ui.warn(_('(consider using --after)\n'))
875 return errs, copied
862 if len(copied) == 0:
863 raise util.Abort(_('no files to copy'))
864 return errors, copied
876 865
877 866 def copy(ui, repo, *pats, **opts):
878 867 """mark files as copied for the next commit
879 868
880 869 Mark dest as having copies of source files. If dest is a
881 870 directory, copies are put in that directory. If dest is a file,
882 871 there can only be one source.
883 872
884 873 By default, this command copies the contents of files as they
885 874 stand in the working directory. If invoked with --after, the
886 875 operation is recorded, but no copying is performed.
887 876
888 877 This command takes effect in the next commit.
889 878
890 879 NOTE: This command should be treated as experimental. While it
891 880 should properly record copied files, this information is not yet
892 881 fully used by merge, nor fully reported by log.
893 882 """
894 883 errs, copied = docopy(ui, repo, pats, opts)
895 884 return errs
896 885
897 886 def debugancestor(ui, index, rev1, rev2):
898 887 """find the ancestor revision of two revisions in a given index"""
899 888 r = revlog.revlog(util.opener(os.getcwd()), index, "")
900 889 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
901 890 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
902 891
903 892 def debugcheckstate(ui, repo):
904 893 """validate the correctness of the current dirstate"""
905 894 parent1, parent2 = repo.dirstate.parents()
906 895 repo.dirstate.read()
907 896 dc = repo.dirstate.map
908 897 keys = dc.keys()
909 898 keys.sort()
910 899 m1n = repo.changelog.read(parent1)[0]
911 900 m2n = repo.changelog.read(parent2)[0]
912 901 m1 = repo.manifest.read(m1n)
913 902 m2 = repo.manifest.read(m2n)
914 903 errors = 0
915 904 for f in dc:
916 905 state = repo.dirstate.state(f)
917 906 if state in "nr" and f not in m1:
918 907 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
919 908 errors += 1
920 909 if state in "a" and f in m1:
921 910 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
922 911 errors += 1
923 912 if state in "m" and f not in m1 and f not in m2:
924 913 ui.warn(_("%s in state %s, but not in either manifest\n") %
925 914 (f, state))
926 915 errors += 1
927 916 for f in m1:
928 917 state = repo.dirstate.state(f)
929 918 if state not in "nrm":
930 919 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
931 920 errors += 1
932 921 if errors:
933 922 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
934 923
935 924 def debugconfig(ui):
936 925 """show combined config settings from all hgrc files"""
937 926 try:
938 927 repo = hg.repository(ui)
939 928 except hg.RepoError:
940 929 pass
941 930 for section, name, value in ui.walkconfig():
942 931 ui.write('%s.%s=%s\n' % (section, name, value))
943 932
944 933 def debugsetparents(ui, repo, rev1, rev2=None):
945 934 """manually set the parents of the current working directory
946 935
947 936 This is useful for writing repository conversion tools, but should
948 937 be used with care.
949 938 """
950 939
951 940 if not rev2:
952 941 rev2 = hex(nullid)
953 942
954 943 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
955 944
956 945 def debugstate(ui, repo):
957 946 """show the contents of the current dirstate"""
958 947 repo.dirstate.read()
959 948 dc = repo.dirstate.map
960 949 keys = dc.keys()
961 950 keys.sort()
962 951 for file_ in keys:
963 952 ui.write("%c %3o %10d %s %s\n"
964 953 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
965 954 time.strftime("%x %X",
966 955 time.localtime(dc[file_][3])), file_))
967 956 for f in repo.dirstate.copies:
968 957 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
969 958
970 959 def debugdata(ui, file_, rev):
971 960 """dump the contents of an data file revision"""
972 961 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
973 962 try:
974 963 ui.write(r.revision(r.lookup(rev)))
975 964 except KeyError:
976 965 raise util.Abort(_('invalid revision identifier %s'), rev)
977 966
978 967 def debugindex(ui, file_):
979 968 """dump the contents of an index file"""
980 969 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
981 970 ui.write(" rev offset length base linkrev" +
982 971 " nodeid p1 p2\n")
983 972 for i in range(r.count()):
984 973 e = r.index[i]
985 974 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
986 975 i, e[0], e[1], e[2], e[3],
987 976 short(e[6]), short(e[4]), short(e[5])))
988 977
989 978 def debugindexdot(ui, file_):
990 979 """dump an index DAG as a .dot file"""
991 980 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
992 981 ui.write("digraph G {\n")
993 982 for i in range(r.count()):
994 983 e = r.index[i]
995 984 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
996 985 if e[5] != nullid:
997 986 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
998 987 ui.write("}\n")
999 988
1000 989 def debugrename(ui, repo, file, rev=None):
1001 990 """dump rename information"""
1002 991 r = repo.file(relpath(repo, [file])[0])
1003 992 if rev:
1004 993 try:
1005 994 # assume all revision numbers are for changesets
1006 995 n = repo.lookup(rev)
1007 996 change = repo.changelog.read(n)
1008 997 m = repo.manifest.read(change[0])
1009 998 n = m[relpath(repo, [file])[0]]
1010 999 except hg.RepoError, KeyError:
1011 1000 n = r.lookup(rev)
1012 1001 else:
1013 1002 n = r.tip()
1014 1003 m = r.renamed(n)
1015 1004 if m:
1016 1005 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1017 1006 else:
1018 1007 ui.write(_("not renamed\n"))
1019 1008
1020 1009 def debugwalk(ui, repo, *pats, **opts):
1021 1010 """show how files match on given patterns"""
1022 1011 items = list(walk(repo, pats, opts))
1023 1012 if not items:
1024 1013 return
1025 1014 fmt = '%%s %%-%ds %%-%ds %%s' % (
1026 1015 max([len(abs) for (src, abs, rel, exact) in items]),
1027 1016 max([len(rel) for (src, abs, rel, exact) in items]))
1028 1017 for src, abs, rel, exact in items:
1029 1018 line = fmt % (src, abs, rel, exact and 'exact' or '')
1030 1019 ui.write("%s\n" % line.rstrip())
1031 1020
1032 1021 def diff(ui, repo, *pats, **opts):
1033 1022 """diff working directory (or selected files)
1034 1023
1035 1024 Show differences between revisions for the specified files.
1036 1025
1037 1026 Differences between files are shown using the unified diff format.
1038 1027
1039 1028 When two revision arguments are given, then changes are shown
1040 1029 between those revisions. If only one revision is specified then
1041 1030 that revision is compared to the working directory, and, when no
1042 1031 revisions are specified, the working directory files are compared
1043 1032 to its parent.
1044 1033
1045 1034 Without the -a option, diff will avoid generating diffs of files
1046 1035 it detects as binary. With -a, diff will generate a diff anyway,
1047 1036 probably with undesirable results.
1048 1037 """
1049 1038 node1, node2 = None, None
1050 1039 revs = [repo.lookup(x) for x in opts['rev']]
1051 1040
1052 1041 if len(revs) > 0:
1053 1042 node1 = revs[0]
1054 1043 if len(revs) > 1:
1055 1044 node2 = revs[1]
1056 1045 if len(revs) > 2:
1057 1046 raise util.Abort(_("too many revisions to diff"))
1058 1047
1059 1048 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
1060 1049
1061 1050 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1062 1051 text=opts['text'])
1063 1052
1064 1053 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1065 1054 node = repo.lookup(changeset)
1066 1055 prev, other = repo.changelog.parents(node)
1067 1056 change = repo.changelog.read(node)
1068 1057
1069 1058 fp = make_file(repo, repo.changelog, opts['output'],
1070 1059 node=node, total=total, seqno=seqno,
1071 1060 revwidth=revwidth)
1072 1061 if fp != sys.stdout:
1073 1062 ui.note("%s\n" % fp.name)
1074 1063
1075 1064 fp.write("# HG changeset patch\n")
1076 1065 fp.write("# User %s\n" % change[1])
1077 1066 fp.write("# Node ID %s\n" % hex(node))
1078 1067 fp.write("# Parent %s\n" % hex(prev))
1079 1068 if other != nullid:
1080 1069 fp.write("# Parent %s\n" % hex(other))
1081 1070 fp.write(change[4].rstrip())
1082 1071 fp.write("\n\n")
1083 1072
1084 1073 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1085 1074 if fp != sys.stdout:
1086 1075 fp.close()
1087 1076
1088 1077 def export(ui, repo, *changesets, **opts):
1089 1078 """dump the header and diffs for one or more changesets
1090 1079
1091 1080 Print the changeset header and diffs for one or more revisions.
1092 1081
1093 1082 The information shown in the changeset header is: author,
1094 1083 changeset hash, parent and commit comment.
1095 1084
1096 1085 Output may be to a file, in which case the name of the file is
1097 1086 given using a format string. The formatting rules are as follows:
1098 1087
1099 1088 %% literal "%" character
1100 1089 %H changeset hash (40 bytes of hexadecimal)
1101 1090 %N number of patches being generated
1102 1091 %R changeset revision number
1103 1092 %b basename of the exporting repository
1104 1093 %h short-form changeset hash (12 bytes of hexadecimal)
1105 1094 %n zero-padded sequence number, starting at 1
1106 1095 %r zero-padded changeset revision number
1107 1096
1108 1097 Without the -a option, export will avoid generating diffs of files
1109 1098 it detects as binary. With -a, export will generate a diff anyway,
1110 1099 probably with undesirable results.
1111 1100 """
1112 1101 if not changesets:
1113 1102 raise util.Abort(_("export requires at least one changeset"))
1114 1103 seqno = 0
1115 1104 revs = list(revrange(ui, repo, changesets))
1116 1105 total = len(revs)
1117 1106 revwidth = max(map(len, revs))
1118 1107 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
1119 1108 for cset in revs:
1120 1109 seqno += 1
1121 1110 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1122 1111
1123 1112 def forget(ui, repo, *pats, **opts):
1124 1113 """don't add the specified files on the next commit
1125 1114
1126 1115 Undo an 'hg add' scheduled for the next commit.
1127 1116 """
1128 1117 forget = []
1129 1118 for src, abs, rel, exact in walk(repo, pats, opts):
1130 1119 if repo.dirstate.state(abs) == 'a':
1131 1120 forget.append(abs)
1132 1121 if ui.verbose or not exact:
1133 1122 ui.status(_('forgetting %s\n') % rel)
1134 1123 repo.forget(forget)
1135 1124
1136 1125 def grep(ui, repo, pattern, *pats, **opts):
1137 1126 """search for a pattern in specified files and revisions
1138 1127
1139 1128 Search revisions of files for a regular expression.
1140 1129
1141 1130 This command behaves differently than Unix grep. It only accepts
1142 1131 Python/Perl regexps. It searches repository history, not the
1143 1132 working directory. It always prints the revision number in which
1144 1133 a match appears.
1145 1134
1146 1135 By default, grep only prints output for the first revision of a
1147 1136 file in which it finds a match. To get it to print every revision
1148 1137 that contains a change in match status ("-" for a match that
1149 1138 becomes a non-match, or "+" for a non-match that becomes a match),
1150 1139 use the --all flag.
1151 1140 """
1152 1141 reflags = 0
1153 1142 if opts['ignore_case']:
1154 1143 reflags |= re.I
1155 1144 regexp = re.compile(pattern, reflags)
1156 1145 sep, eol = ':', '\n'
1157 1146 if opts['print0']:
1158 1147 sep = eol = '\0'
1159 1148
1160 1149 fcache = {}
1161 1150 def getfile(fn):
1162 1151 if fn not in fcache:
1163 1152 fcache[fn] = repo.file(fn)
1164 1153 return fcache[fn]
1165 1154
1166 1155 def matchlines(body):
1167 1156 begin = 0
1168 1157 linenum = 0
1169 1158 while True:
1170 1159 match = regexp.search(body, begin)
1171 1160 if not match:
1172 1161 break
1173 1162 mstart, mend = match.span()
1174 1163 linenum += body.count('\n', begin, mstart) + 1
1175 1164 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1176 1165 lend = body.find('\n', mend)
1177 1166 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1178 1167 begin = lend + 1
1179 1168
1180 1169 class linestate:
1181 1170 def __init__(self, line, linenum, colstart, colend):
1182 1171 self.line = line
1183 1172 self.linenum = linenum
1184 1173 self.colstart = colstart
1185 1174 self.colend = colend
1186 1175 def __eq__(self, other):
1187 1176 return self.line == other.line
1188 1177 def __hash__(self):
1189 1178 return hash(self.line)
1190 1179
1191 1180 matches = {}
1192 1181 def grepbody(fn, rev, body):
1193 1182 matches[rev].setdefault(fn, {})
1194 1183 m = matches[rev][fn]
1195 1184 for lnum, cstart, cend, line in matchlines(body):
1196 1185 s = linestate(line, lnum, cstart, cend)
1197 1186 m[s] = s
1198 1187
1199 1188 prev = {}
1200 1189 ucache = {}
1201 1190 def display(fn, rev, states, prevstates):
1202 1191 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1203 1192 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1204 1193 counts = {'-': 0, '+': 0}
1205 1194 filerevmatches = {}
1206 1195 for l in diff:
1207 1196 if incrementing or not opts['all']:
1208 1197 change = ((l in prevstates) and '-') or '+'
1209 1198 r = rev
1210 1199 else:
1211 1200 change = ((l in states) and '-') or '+'
1212 1201 r = prev[fn]
1213 1202 cols = [fn, str(rev)]
1214 1203 if opts['line_number']: cols.append(str(l.linenum))
1215 1204 if opts['all']: cols.append(change)
1216 1205 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1217 1206 ucache))
1218 1207 if opts['files_with_matches']:
1219 1208 c = (fn, rev)
1220 1209 if c in filerevmatches: continue
1221 1210 filerevmatches[c] = 1
1222 1211 else:
1223 1212 cols.append(l.line)
1224 1213 ui.write(sep.join(cols), eol)
1225 1214 counts[change] += 1
1226 1215 return counts['+'], counts['-']
1227 1216
1228 1217 fstate = {}
1229 1218 skip = {}
1230 1219 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1231 1220 count = 0
1232 1221 incrementing = False
1233 1222 for st, rev, fns in changeiter:
1234 1223 if st == 'window':
1235 1224 incrementing = rev
1236 1225 matches.clear()
1237 1226 elif st == 'add':
1238 1227 change = repo.changelog.read(repo.lookup(str(rev)))
1239 1228 mf = repo.manifest.read(change[0])
1240 1229 matches[rev] = {}
1241 1230 for fn in fns:
1242 1231 if fn in skip: continue
1243 1232 fstate.setdefault(fn, {})
1244 1233 try:
1245 1234 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1246 1235 except KeyError:
1247 1236 pass
1248 1237 elif st == 'iter':
1249 1238 states = matches[rev].items()
1250 1239 states.sort()
1251 1240 for fn, m in states:
1252 1241 if fn in skip: continue
1253 1242 if incrementing or not opts['all'] or fstate[fn]:
1254 1243 pos, neg = display(fn, rev, m, fstate[fn])
1255 1244 count += pos + neg
1256 1245 if pos and not opts['all']:
1257 1246 skip[fn] = True
1258 1247 fstate[fn] = m
1259 1248 prev[fn] = rev
1260 1249
1261 1250 if not incrementing:
1262 1251 fstate = fstate.items()
1263 1252 fstate.sort()
1264 1253 for fn, state in fstate:
1265 1254 if fn in skip: continue
1266 1255 display(fn, rev, {}, state)
1267 1256 return (count == 0 and 1) or 0
1268 1257
1269 1258 def heads(ui, repo, **opts):
1270 1259 """show current repository heads
1271 1260
1272 1261 Show all repository head changesets.
1273 1262
1274 1263 Repository "heads" are changesets that don't have children
1275 1264 changesets. They are where development generally takes place and
1276 1265 are the usual targets for update and merge operations.
1277 1266 """
1278 1267 heads = repo.changelog.heads()
1279 1268 br = None
1280 1269 if opts['branches']:
1281 1270 br = repo.branchlookup(heads)
1282 1271 for n in repo.changelog.heads():
1283 1272 show_changeset(ui, repo, changenode=n, brinfo=br)
1284 1273
1285 1274 def identify(ui, repo):
1286 1275 """print information about the working copy
1287 1276
1288 1277 Print a short summary of the current state of the repo.
1289 1278
1290 1279 This summary identifies the repository state using one or two parent
1291 1280 hash identifiers, followed by a "+" if there are uncommitted changes
1292 1281 in the working directory, followed by a list of tags for this revision.
1293 1282 """
1294 1283 parents = [p for p in repo.dirstate.parents() if p != nullid]
1295 1284 if not parents:
1296 1285 ui.write(_("unknown\n"))
1297 1286 return
1298 1287
1299 1288 hexfunc = ui.verbose and hex or short
1300 1289 (c, a, d, u) = repo.changes()
1301 1290 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1302 1291 (c or a or d) and "+" or "")]
1303 1292
1304 1293 if not ui.quiet:
1305 1294 # multiple tags for a single parent separated by '/'
1306 1295 parenttags = ['/'.join(tags)
1307 1296 for tags in map(repo.nodetags, parents) if tags]
1308 1297 # tags for multiple parents separated by ' + '
1309 1298 if parenttags:
1310 1299 output.append(' + '.join(parenttags))
1311 1300
1312 1301 ui.write("%s\n" % ' '.join(output))
1313 1302
1314 1303 def import_(ui, repo, patch1, *patches, **opts):
1315 1304 """import an ordered set of patches
1316 1305
1317 1306 Import a list of patches and commit them individually.
1318 1307
1319 1308 If there are outstanding changes in the working directory, import
1320 1309 will abort unless given the -f flag.
1321 1310
1322 1311 If a patch looks like a mail message (its first line starts with
1323 1312 "From " or looks like an RFC822 header), it will not be applied
1324 1313 unless the -f option is used. The importer neither parses nor
1325 1314 discards mail headers, so use -f only to override the "mailness"
1326 1315 safety check, not to import a real mail message.
1327 1316 """
1328 1317 patches = (patch1,) + patches
1329 1318
1330 1319 if not opts['force']:
1331 1320 (c, a, d, u) = repo.changes()
1332 1321 if c or a or d:
1333 1322 raise util.Abort(_("outstanding uncommitted changes"))
1334 1323
1335 1324 d = opts["base"]
1336 1325 strip = opts["strip"]
1337 1326
1338 1327 mailre = re.compile(r'(?:From |[\w-]+:)')
1339 1328
1340 1329 # attempt to detect the start of a patch
1341 1330 # (this heuristic is borrowed from quilt)
1342 1331 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1343 1332 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1344 1333 '(---|\*\*\*)[ \t])')
1345 1334
1346 1335 for patch in patches:
1347 1336 ui.status(_("applying %s\n") % patch)
1348 1337 pf = os.path.join(d, patch)
1349 1338
1350 1339 message = []
1351 1340 user = None
1352 1341 hgpatch = False
1353 1342 for line in file(pf):
1354 1343 line = line.rstrip()
1355 1344 if (not message and not hgpatch and
1356 1345 mailre.match(line) and not opts['force']):
1357 1346 if len(line) > 35: line = line[:32] + '...'
1358 1347 raise util.Abort(_('first line looks like a '
1359 1348 'mail header: ') + line)
1360 1349 if diffre.match(line):
1361 1350 break
1362 1351 elif hgpatch:
1363 1352 # parse values when importing the result of an hg export
1364 1353 if line.startswith("# User "):
1365 1354 user = line[7:]
1366 1355 ui.debug(_('User: %s\n') % user)
1367 1356 elif not line.startswith("# ") and line:
1368 1357 message.append(line)
1369 1358 hgpatch = False
1370 1359 elif line == '# HG changeset patch':
1371 1360 hgpatch = True
1372 1361 message = [] # We may have collected garbage
1373 1362 else:
1374 1363 message.append(line)
1375 1364
1376 1365 # make sure message isn't empty
1377 1366 if not message:
1378 1367 message = _("imported patch %s\n") % patch
1379 1368 else:
1380 1369 message = "%s\n" % '\n'.join(message)
1381 1370 ui.debug(_('message:\n%s\n') % message)
1382 1371
1383 1372 files = util.patch(strip, pf, ui)
1384 1373
1385 1374 if len(files) > 0:
1386 1375 addremove(ui, repo, *files)
1387 1376 repo.commit(files, message, user)
1388 1377
1389 1378 def incoming(ui, repo, source="default", **opts):
1390 1379 """show new changesets found in source
1391 1380
1392 1381 Show new changesets found in the specified repo or the default
1393 1382 pull repo. These are the changesets that would be pulled if a pull
1394 1383 was requested.
1395 1384
1396 1385 Currently only local repositories are supported.
1397 1386 """
1398 1387 source = ui.expandpath(source, repo.root)
1399 1388 other = hg.repository(ui, source)
1400 1389 if not other.local():
1401 1390 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1402 1391 o = repo.findincoming(other)
1403 1392 if not o:
1404 1393 return
1405 1394 o = other.changelog.nodesbetween(o)[0]
1406 1395 if opts['newest_first']:
1407 1396 o.reverse()
1408 1397 for n in o:
1409 1398 parents = [p for p in other.changelog.parents(n) if p != nullid]
1410 1399 if opts['no_merges'] and len(parents) == 2:
1411 1400 continue
1412 1401 show_changeset(ui, other, changenode=n)
1413 1402 if opts['patch']:
1414 1403 prev = (parents and parents[0]) or nullid
1415 1404 dodiff(ui, ui, other, prev, n)
1416 1405 ui.write("\n")
1417 1406
1418 1407 def init(ui, dest="."):
1419 1408 """create a new repository in the given directory
1420 1409
1421 1410 Initialize a new repository in the given directory. If the given
1422 1411 directory does not exist, it is created.
1423 1412
1424 1413 If no directory is given, the current directory is used.
1425 1414 """
1426 1415 if not os.path.exists(dest):
1427 1416 os.mkdir(dest)
1428 1417 hg.repository(ui, dest, create=1)
1429 1418
1430 1419 def locate(ui, repo, *pats, **opts):
1431 1420 """locate files matching specific patterns
1432 1421
1433 1422 Print all files under Mercurial control whose names match the
1434 1423 given patterns.
1435 1424
1436 1425 This command searches the current directory and its
1437 1426 subdirectories. To search an entire repository, move to the root
1438 1427 of the repository.
1439 1428
1440 1429 If no patterns are given to match, this command prints all file
1441 1430 names.
1442 1431
1443 1432 If you want to feed the output of this command into the "xargs"
1444 1433 command, use the "-0" option to both this command and "xargs".
1445 1434 This will avoid the problem of "xargs" treating single filenames
1446 1435 that contain white space as multiple filenames.
1447 1436 """
1448 1437 end = opts['print0'] and '\0' or '\n'
1449 1438
1450 1439 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1451 1440 if repo.dirstate.state(abs) == '?':
1452 1441 continue
1453 1442 if opts['fullpath']:
1454 1443 ui.write(os.path.join(repo.root, abs), end)
1455 1444 else:
1456 1445 ui.write(rel, end)
1457 1446
1458 1447 def log(ui, repo, *pats, **opts):
1459 1448 """show revision history of entire repository or files
1460 1449
1461 1450 Print the revision history of the specified files or the entire project.
1462 1451
1463 1452 By default this command outputs: changeset id and hash, tags,
1464 1453 parents, user, date and time, and a summary for each commit. The
1465 1454 -v switch adds some more detail, such as changed files, manifest
1466 1455 hashes or message signatures.
1467 1456 """
1468 1457 class dui:
1469 1458 # Implement and delegate some ui protocol. Save hunks of
1470 1459 # output for later display in the desired order.
1471 1460 def __init__(self, ui):
1472 1461 self.ui = ui
1473 1462 self.hunk = {}
1474 1463 def bump(self, rev):
1475 1464 self.rev = rev
1476 1465 self.hunk[rev] = []
1477 1466 def note(self, *args):
1478 1467 if self.verbose:
1479 1468 self.write(*args)
1480 1469 def status(self, *args):
1481 1470 if not self.quiet:
1482 1471 self.write(*args)
1483 1472 def write(self, *args):
1484 1473 self.hunk[self.rev].append(args)
1485 1474 def debug(self, *args):
1486 1475 if self.debugflag:
1487 1476 self.write(*args)
1488 1477 def __getattr__(self, key):
1489 1478 return getattr(self.ui, key)
1490 1479 cwd = repo.getcwd()
1491 1480 if not pats and cwd:
1492 1481 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1493 1482 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1494 1483 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1495 1484 pats, opts)
1496 1485 for st, rev, fns in changeiter:
1497 1486 if st == 'window':
1498 1487 du = dui(ui)
1499 1488 elif st == 'add':
1500 1489 du.bump(rev)
1501 1490 changenode = repo.changelog.node(rev)
1502 1491 parents = [p for p in repo.changelog.parents(changenode)
1503 1492 if p != nullid]
1504 1493 if opts['no_merges'] and len(parents) == 2:
1505 1494 continue
1506 1495 if opts['only_merges'] and len(parents) != 2:
1507 1496 continue
1508 1497
1509 1498 br = None
1510 1499 if opts['keyword']:
1511 1500 changes = repo.changelog.read(repo.changelog.node(rev))
1512 1501 miss = 0
1513 1502 for k in [kw.lower() for kw in opts['keyword']]:
1514 1503 if not (k in changes[1].lower() or
1515 1504 k in changes[4].lower() or
1516 1505 k in " ".join(changes[3][:20]).lower()):
1517 1506 miss = 1
1518 1507 break
1519 1508 if miss:
1520 1509 continue
1521 1510
1522 1511 if opts['branch']:
1523 1512 br = repo.branchlookup([repo.changelog.node(rev)])
1524 1513
1525 1514 show_changeset(du, repo, rev, brinfo=br)
1526 1515 if opts['patch']:
1527 1516 prev = (parents and parents[0]) or nullid
1528 1517 dodiff(du, du, repo, prev, changenode, fns)
1529 1518 du.write("\n\n")
1530 1519 elif st == 'iter':
1531 1520 for args in du.hunk[rev]:
1532 1521 ui.write(*args)
1533 1522
1534 1523 def manifest(ui, repo, rev=None):
1535 1524 """output the latest or given revision of the project manifest
1536 1525
1537 1526 Print a list of version controlled files for the given revision.
1538 1527
1539 1528 The manifest is the list of files being version controlled. If no revision
1540 1529 is given then the tip is used.
1541 1530 """
1542 1531 if rev:
1543 1532 try:
1544 1533 # assume all revision numbers are for changesets
1545 1534 n = repo.lookup(rev)
1546 1535 change = repo.changelog.read(n)
1547 1536 n = change[0]
1548 1537 except hg.RepoError:
1549 1538 n = repo.manifest.lookup(rev)
1550 1539 else:
1551 1540 n = repo.manifest.tip()
1552 1541 m = repo.manifest.read(n)
1553 1542 mf = repo.manifest.readflags(n)
1554 1543 files = m.keys()
1555 1544 files.sort()
1556 1545
1557 1546 for f in files:
1558 1547 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1559 1548
1560 1549 def outgoing(ui, repo, dest="default-push", **opts):
1561 1550 """show changesets not found in destination
1562 1551
1563 1552 Show changesets not found in the specified destination repo or the
1564 1553 default push repo. These are the changesets that would be pushed
1565 1554 if a push was requested.
1566 1555 """
1567 1556 dest = ui.expandpath(dest, repo.root)
1568 1557 other = hg.repository(ui, dest)
1569 1558 o = repo.findoutgoing(other)
1570 1559 o = repo.changelog.nodesbetween(o)[0]
1571 1560 if opts['newest_first']:
1572 1561 o.reverse()
1573 1562 for n in o:
1574 1563 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1575 1564 if opts['no_merges'] and len(parents) == 2:
1576 1565 continue
1577 1566 show_changeset(ui, repo, changenode=n)
1578 1567 if opts['patch']:
1579 1568 prev = (parents and parents[0]) or nullid
1580 1569 dodiff(ui, ui, repo, prev, n)
1581 1570 ui.write("\n")
1582 1571
1583 1572 def parents(ui, repo, rev=None):
1584 1573 """show the parents of the working dir or revision
1585 1574
1586 1575 Print the working directory's parent revisions.
1587 1576 """
1588 1577 if rev:
1589 1578 p = repo.changelog.parents(repo.lookup(rev))
1590 1579 else:
1591 1580 p = repo.dirstate.parents()
1592 1581
1593 1582 for n in p:
1594 1583 if n != nullid:
1595 1584 show_changeset(ui, repo, changenode=n)
1596 1585
1597 1586 def paths(ui, search=None):
1598 1587 """show definition of symbolic path names
1599 1588
1600 1589 Show definition of symbolic path name NAME. If no name is given, show
1601 1590 definition of available names.
1602 1591
1603 1592 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1604 1593 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1605 1594 """
1606 1595 try:
1607 1596 repo = hg.repository(ui=ui)
1608 1597 except hg.RepoError:
1609 1598 pass
1610 1599
1611 1600 if search:
1612 1601 for name, path in ui.configitems("paths"):
1613 1602 if name == search:
1614 1603 ui.write("%s\n" % path)
1615 1604 return
1616 1605 ui.warn(_("not found!\n"))
1617 1606 return 1
1618 1607 else:
1619 1608 for name, path in ui.configitems("paths"):
1620 1609 ui.write("%s = %s\n" % (name, path))
1621 1610
1622 1611 def pull(ui, repo, source="default", **opts):
1623 1612 """pull changes from the specified source
1624 1613
1625 1614 Pull changes from a remote repository to a local one.
1626 1615
1627 1616 This finds all changes from the repository at the specified path
1628 1617 or URL and adds them to the local repository. By default, this
1629 1618 does not update the copy of the project in the working directory.
1630 1619
1631 1620 Valid URLs are of the form:
1632 1621
1633 1622 local/filesystem/path
1634 1623 http://[user@]host[:port][/path]
1635 1624 https://[user@]host[:port][/path]
1636 1625 ssh://[user@]host[:port][/path]
1637 1626
1638 1627 SSH requires an accessible shell account on the destination machine
1639 1628 and a copy of hg in the remote path. With SSH, paths are relative
1640 1629 to the remote user's home directory by default; use two slashes at
1641 1630 the start of a path to specify it as relative to the filesystem root.
1642 1631 """
1643 1632 source = ui.expandpath(source, repo.root)
1644 1633 ui.status(_('pulling from %s\n') % (source))
1645 1634
1646 1635 if opts['ssh']:
1647 1636 ui.setconfig("ui", "ssh", opts['ssh'])
1648 1637 if opts['remotecmd']:
1649 1638 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1650 1639
1651 1640 other = hg.repository(ui, source)
1652 1641 revs = None
1653 1642 if opts['rev'] and not other.local():
1654 1643 raise util.Abort("pull -r doesn't work for remote repositories yet")
1655 1644 elif opts['rev']:
1656 1645 revs = [other.lookup(rev) for rev in opts['rev']]
1657 1646 r = repo.pull(other, heads=revs)
1658 1647 if not r:
1659 1648 if opts['update']:
1660 1649 return update(ui, repo)
1661 1650 else:
1662 1651 ui.status(_("(run 'hg update' to get a working copy)\n"))
1663 1652
1664 1653 return r
1665 1654
1666 1655 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1667 1656 """push changes to the specified destination
1668 1657
1669 1658 Push changes from the local repository to the given destination.
1670 1659
1671 1660 This is the symmetrical operation for pull. It helps to move
1672 1661 changes from the current repository to a different one. If the
1673 1662 destination is local this is identical to a pull in that directory
1674 1663 from the current one.
1675 1664
1676 1665 By default, push will refuse to run if it detects the result would
1677 1666 increase the number of remote heads. This generally indicates the
1678 1667 the client has forgotten to sync and merge before pushing.
1679 1668
1680 1669 Valid URLs are of the form:
1681 1670
1682 1671 local/filesystem/path
1683 1672 ssh://[user@]host[:port][/path]
1684 1673
1685 1674 SSH requires an accessible shell account on the destination
1686 1675 machine and a copy of hg in the remote path.
1687 1676 """
1688 1677 dest = ui.expandpath(dest, repo.root)
1689 1678 ui.status('pushing to %s\n' % (dest))
1690 1679
1691 1680 if ssh:
1692 1681 ui.setconfig("ui", "ssh", ssh)
1693 1682 if remotecmd:
1694 1683 ui.setconfig("ui", "remotecmd", remotecmd)
1695 1684
1696 1685 other = hg.repository(ui, dest)
1697 1686 r = repo.push(other, force)
1698 1687 return r
1699 1688
1700 1689 def rawcommit(ui, repo, *flist, **rc):
1701 1690 """raw commit interface
1702 1691
1703 1692 Lowlevel commit, for use in helper scripts.
1704 1693
1705 1694 This command is not intended to be used by normal users, as it is
1706 1695 primarily useful for importing from other SCMs.
1707 1696 """
1708 1697 message = rc['message']
1709 1698 if not message and rc['logfile']:
1710 1699 try:
1711 1700 message = open(rc['logfile']).read()
1712 1701 except IOError:
1713 1702 pass
1714 1703 if not message and not rc['logfile']:
1715 1704 raise util.Abort(_("missing commit message"))
1716 1705
1717 1706 files = relpath(repo, list(flist))
1718 1707 if rc['files']:
1719 1708 files += open(rc['files']).read().splitlines()
1720 1709
1721 1710 rc['parent'] = map(repo.lookup, rc['parent'])
1722 1711
1723 1712 try:
1724 1713 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1725 1714 except ValueError, inst:
1726 1715 raise util.Abort(str(inst))
1727 1716
1728 1717 def recover(ui, repo):
1729 1718 """roll back an interrupted transaction
1730 1719
1731 1720 Recover from an interrupted commit or pull.
1732 1721
1733 1722 This command tries to fix the repository status after an interrupted
1734 1723 operation. It should only be necessary when Mercurial suggests it.
1735 1724 """
1736 1725 repo.recover()
1737 1726
1738 1727 def remove(ui, repo, pat, *pats, **opts):
1739 1728 """remove the specified files on the next commit
1740 1729
1741 1730 Schedule the indicated files for removal from the repository.
1742 1731
1743 1732 This command schedules the files to be removed at the next commit.
1744 1733 This only removes files from the current branch, not from the
1745 1734 entire project history. If the files still exist in the working
1746 1735 directory, they will be deleted from it.
1747 1736 """
1748 1737 names = []
1749 1738 def okaytoremove(abs, rel, exact):
1750 1739 c, a, d, u = repo.changes(files = [abs])
1751 1740 reason = None
1752 1741 if c: reason = _('is modified')
1753 1742 elif a: reason = _('has been marked for add')
1754 1743 elif u: reason = _('is not managed')
1755 1744 if reason:
1756 1745 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1757 1746 else:
1758 1747 return True
1759 1748 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1760 1749 if okaytoremove(abs, rel, exact):
1761 1750 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1762 1751 names.append(abs)
1763 1752 repo.remove(names, unlink=True)
1764 1753
1765 1754 def rename(ui, repo, *pats, **opts):
1766 1755 """rename files; equivalent of copy + remove
1767 1756
1768 1757 Mark dest as copies of sources; mark sources for deletion. If
1769 1758 dest is a directory, copies are put in that directory. If dest is
1770 1759 a file, there can only be one source.
1771 1760
1772 1761 By default, this command copies the contents of files as they
1773 1762 stand in the working directory. If invoked with --after, the
1774 1763 operation is recorded, but no copying is performed.
1775 1764
1776 1765 This command takes effect in the next commit.
1777 1766
1778 1767 NOTE: This command should be treated as experimental. While it
1779 1768 should properly record rename files, this information is not yet
1780 1769 fully used by merge, nor fully reported by log.
1781 1770 """
1782 1771 errs, copied = docopy(ui, repo, pats, opts)
1783 1772 names = []
1784 1773 for abs, rel, exact in copied:
1785 1774 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1786 1775 names.append(abs)
1787 1776 repo.remove(names, unlink=True)
1788 1777 return errs
1789 1778
1790 1779 def revert(ui, repo, *pats, **opts):
1791 1780 """revert modified files or dirs back to their unmodified states
1792 1781
1793 1782 Revert any uncommitted modifications made to the named files or
1794 1783 directories. This restores the contents of the affected files to
1795 1784 an unmodified state.
1796 1785
1797 1786 If a file has been deleted, it is recreated. If the executable
1798 1787 mode of a file was changed, it is reset.
1799 1788
1800 1789 If names are given, all files matching the names are reverted.
1801 1790
1802 1791 If no names are given, all files in the current directory and
1803 1792 its subdirectories are reverted.
1804 1793 """
1805 1794 node = opts['rev'] and repo.lookup(opts['rev']) or \
1806 1795 repo.dirstate.parents()[0]
1807 1796
1808 1797 files, choose, anypats = matchpats(repo, repo.getcwd(), pats, opts)
1809 1798 (c, a, d, u) = repo.changes(match=choose)
1810 1799 repo.forget(a)
1811 1800 repo.undelete(d)
1812 1801
1813 1802 return repo.update(node, False, True, choose, False)
1814 1803
1815 1804 def root(ui, repo):
1816 1805 """print the root (top) of the current working dir
1817 1806
1818 1807 Print the root directory of the current repository.
1819 1808 """
1820 1809 ui.write(repo.root + "\n")
1821 1810
1822 1811 def serve(ui, repo, **opts):
1823 1812 """export the repository via HTTP
1824 1813
1825 1814 Start a local HTTP repository browser and pull server.
1826 1815
1827 1816 By default, the server logs accesses to stdout and errors to
1828 1817 stderr. Use the "-A" and "-E" options to log to files.
1829 1818 """
1830 1819
1831 1820 if opts["stdio"]:
1832 1821 fin, fout = sys.stdin, sys.stdout
1833 1822 sys.stdout = sys.stderr
1834 1823
1835 1824 # Prevent insertion/deletion of CRs
1836 1825 util.set_binary(fin)
1837 1826 util.set_binary(fout)
1838 1827
1839 1828 def getarg():
1840 1829 argline = fin.readline()[:-1]
1841 1830 arg, l = argline.split()
1842 1831 val = fin.read(int(l))
1843 1832 return arg, val
1844 1833 def respond(v):
1845 1834 fout.write("%d\n" % len(v))
1846 1835 fout.write(v)
1847 1836 fout.flush()
1848 1837
1849 1838 lock = None
1850 1839
1851 1840 while 1:
1852 1841 cmd = fin.readline()[:-1]
1853 1842 if cmd == '':
1854 1843 return
1855 1844 if cmd == "heads":
1856 1845 h = repo.heads()
1857 1846 respond(" ".join(map(hex, h)) + "\n")
1858 1847 if cmd == "lock":
1859 1848 lock = repo.lock()
1860 1849 respond("")
1861 1850 if cmd == "unlock":
1862 1851 if lock:
1863 1852 lock.release()
1864 1853 lock = None
1865 1854 respond("")
1866 1855 elif cmd == "branches":
1867 1856 arg, nodes = getarg()
1868 1857 nodes = map(bin, nodes.split(" "))
1869 1858 r = []
1870 1859 for b in repo.branches(nodes):
1871 1860 r.append(" ".join(map(hex, b)) + "\n")
1872 1861 respond("".join(r))
1873 1862 elif cmd == "between":
1874 1863 arg, pairs = getarg()
1875 1864 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1876 1865 r = []
1877 1866 for b in repo.between(pairs):
1878 1867 r.append(" ".join(map(hex, b)) + "\n")
1879 1868 respond("".join(r))
1880 1869 elif cmd == "changegroup":
1881 1870 nodes = []
1882 1871 arg, roots = getarg()
1883 1872 nodes = map(bin, roots.split(" "))
1884 1873
1885 1874 cg = repo.changegroup(nodes)
1886 1875 while 1:
1887 1876 d = cg.read(4096)
1888 1877 if not d:
1889 1878 break
1890 1879 fout.write(d)
1891 1880
1892 1881 fout.flush()
1893 1882
1894 1883 elif cmd == "addchangegroup":
1895 1884 if not lock:
1896 1885 respond("not locked")
1897 1886 continue
1898 1887 respond("")
1899 1888
1900 1889 r = repo.addchangegroup(fin)
1901 1890 respond("")
1902 1891
1903 1892 optlist = "name templates style address port ipv6 accesslog errorlog"
1904 1893 for o in optlist.split():
1905 1894 if opts[o]:
1906 1895 ui.setconfig("web", o, opts[o])
1907 1896
1908 1897 try:
1909 1898 httpd = hgweb.create_server(repo)
1910 1899 except socket.error, inst:
1911 1900 raise util.Abort('cannot start server: ' + inst.args[1])
1912 1901
1913 1902 if ui.verbose:
1914 1903 addr, port = httpd.socket.getsockname()
1915 1904 if addr == '0.0.0.0':
1916 1905 addr = socket.gethostname()
1917 1906 else:
1918 1907 try:
1919 1908 addr = socket.gethostbyaddr(addr)[0]
1920 1909 except socket.error:
1921 1910 pass
1922 1911 if port != 80:
1923 1912 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1924 1913 else:
1925 1914 ui.status(_('listening at http://%s/\n') % addr)
1926 1915 httpd.serve_forever()
1927 1916
1928 1917 def status(ui, repo, *pats, **opts):
1929 1918 """show changed files in the working directory
1930 1919
1931 1920 Show changed files in the working directory. If no names are
1932 1921 given, all files are shown. Otherwise, only files matching the
1933 1922 given names are shown.
1934 1923
1935 1924 The codes used to show the status of files are:
1936 1925 M = modified
1937 1926 A = added
1938 1927 R = removed
1939 1928 ? = not tracked
1940 1929 """
1941 1930
1942 1931 cwd = repo.getcwd()
1943 1932 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1944 1933 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1945 1934 for n in repo.changes(files=files, match=matchfn)]
1946 1935
1947 1936 changetypes = [(_('modified'), 'M', c),
1948 1937 (_('added'), 'A', a),
1949 1938 (_('removed'), 'R', d),
1950 1939 (_('unknown'), '?', u)]
1951 1940
1952 1941 end = opts['print0'] and '\0' or '\n'
1953 1942
1954 1943 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1955 1944 or changetypes):
1956 1945 if opts['no_status']:
1957 1946 format = "%%s%s" % end
1958 1947 else:
1959 1948 format = "%s %%s%s" % (char, end);
1960 1949
1961 1950 for f in changes:
1962 1951 ui.write(format % f)
1963 1952
1964 1953 def tag(ui, repo, name, rev=None, **opts):
1965 1954 """add a tag for the current tip or a given revision
1966 1955
1967 1956 Name a particular revision using <name>.
1968 1957
1969 1958 Tags are used to name particular revisions of the repository and are
1970 1959 very useful to compare different revision, to go back to significant
1971 1960 earlier versions or to mark branch points as releases, etc.
1972 1961
1973 1962 If no revision is given, the tip is used.
1974 1963
1975 1964 To facilitate version control, distribution, and merging of tags,
1976 1965 they are stored as a file named ".hgtags" which is managed
1977 1966 similarly to other project files and can be hand-edited if
1978 1967 necessary.
1979 1968 """
1980 1969 if name == "tip":
1981 1970 raise util.Abort(_("the name 'tip' is reserved"))
1982 1971 if 'rev' in opts:
1983 1972 rev = opts['rev']
1984 1973 if rev:
1985 1974 r = hex(repo.lookup(rev))
1986 1975 else:
1987 1976 r = hex(repo.changelog.tip())
1988 1977
1989 1978 if name.find(revrangesep) >= 0:
1990 1979 raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
1991 1980
1992 1981 if opts['local']:
1993 1982 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1994 1983 return
1995 1984
1996 1985 (c, a, d, u) = repo.changes()
1997 1986 for x in (c, a, d, u):
1998 1987 if ".hgtags" in x:
1999 1988 raise util.Abort(_("working copy of .hgtags is changed "
2000 1989 "(please commit .hgtags manually)"))
2001 1990
2002 1991 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2003 1992 if repo.dirstate.state(".hgtags") == '?':
2004 1993 repo.add([".hgtags"])
2005 1994
2006 1995 message = (opts['message'] or
2007 1996 _("Added tag %s for changeset %s") % (name, r))
2008 1997 try:
2009 1998 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2010 1999 except ValueError, inst:
2011 2000 raise util.Abort(str(inst))
2012 2001
2013 2002 def tags(ui, repo):
2014 2003 """list repository tags
2015 2004
2016 2005 List the repository tags.
2017 2006
2018 2007 This lists both regular and local tags.
2019 2008 """
2020 2009
2021 2010 l = repo.tagslist()
2022 2011 l.reverse()
2023 2012 for t, n in l:
2024 2013 try:
2025 2014 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2026 2015 except KeyError:
2027 2016 r = " ?:?"
2028 2017 ui.write("%-30s %s\n" % (t, r))
2029 2018
2030 2019 def tip(ui, repo):
2031 2020 """show the tip revision
2032 2021
2033 2022 Show the tip revision.
2034 2023 """
2035 2024 n = repo.changelog.tip()
2036 2025 show_changeset(ui, repo, changenode=n)
2037 2026
2038 2027 def unbundle(ui, repo, fname):
2039 2028 """apply a changegroup file
2040 2029
2041 2030 Apply a compressed changegroup file generated by the bundle
2042 2031 command.
2043 2032 """
2044 2033 f = urllib.urlopen(fname)
2045 2034
2046 2035 if f.read(4) != "HG10":
2047 2036 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2048 2037
2049 2038 def bzgenerator(f):
2050 2039 zd = bz2.BZ2Decompressor()
2051 2040 for chunk in f:
2052 2041 yield zd.decompress(chunk)
2053 2042
2054 2043 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2055 2044 repo.addchangegroup(util.chunkbuffer(bzgen))
2056 2045
2057 2046 def undo(ui, repo):
2058 2047 """undo the last commit or pull
2059 2048
2060 2049 Roll back the last pull or commit transaction on the
2061 2050 repository, restoring the project to its earlier state.
2062 2051
2063 2052 This command should be used with care. There is only one level of
2064 2053 undo and there is no redo.
2065 2054
2066 2055 This command is not intended for use on public repositories. Once
2067 2056 a change is visible for pull by other users, undoing it locally is
2068 2057 ineffective.
2069 2058 """
2070 2059 repo.undo()
2071 2060
2072 2061 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
2073 2062 """update or merge working directory
2074 2063
2075 2064 Update the working directory to the specified revision.
2076 2065
2077 2066 If there are no outstanding changes in the working directory and
2078 2067 there is a linear relationship between the current version and the
2079 2068 requested version, the result is the requested version.
2080 2069
2081 2070 Otherwise the result is a merge between the contents of the
2082 2071 current working directory and the requested version. Files that
2083 2072 changed between either parent are marked as changed for the next
2084 2073 commit and a commit must be performed before any further updates
2085 2074 are allowed.
2086 2075
2087 2076 By default, update will refuse to run if doing so would require
2088 2077 merging or discarding local changes.
2089 2078 """
2090 2079 if branch:
2091 2080 br = repo.branchlookup(branch=branch)
2092 2081 found = []
2093 2082 for x in br:
2094 2083 if branch in br[x]:
2095 2084 found.append(x)
2096 2085 if len(found) > 1:
2097 2086 ui.warn(_("Found multiple heads for %s\n") % branch)
2098 2087 for x in found:
2099 2088 show_changeset(ui, repo, changenode=x, brinfo=br)
2100 2089 return 1
2101 2090 if len(found) == 1:
2102 2091 node = found[0]
2103 2092 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2104 2093 else:
2105 2094 ui.warn(_("branch %s not found\n") % (branch))
2106 2095 return 1
2107 2096 else:
2108 2097 node = node and repo.lookup(node) or repo.changelog.tip()
2109 2098 return repo.update(node, allow=merge, force=clean)
2110 2099
2111 2100 def verify(ui, repo):
2112 2101 """verify the integrity of the repository
2113 2102
2114 2103 Verify the integrity of the current repository.
2115 2104
2116 2105 This will perform an extensive check of the repository's
2117 2106 integrity, validating the hashes and checksums of each entry in
2118 2107 the changelog, manifest, and tracked files, as well as the
2119 2108 integrity of their crosslinks and indices.
2120 2109 """
2121 2110 return repo.verify()
2122 2111
2123 2112 # Command options and aliases are listed here, alphabetically
2124 2113
2125 2114 table = {
2126 2115 "^add":
2127 2116 (add,
2128 2117 [('I', 'include', [], _('include names matching the given patterns')),
2129 2118 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2130 2119 "hg add [OPTION]... [FILE]..."),
2131 2120 "addremove":
2132 2121 (addremove,
2133 2122 [('I', 'include', [], _('include names matching the given patterns')),
2134 2123 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2135 2124 "hg addremove [OPTION]... [FILE]..."),
2136 2125 "^annotate":
2137 2126 (annotate,
2138 2127 [('r', 'rev', '', _('annotate the specified revision')),
2139 2128 ('a', 'text', None, _('treat all files as text')),
2140 2129 ('u', 'user', None, _('list the author')),
2141 2130 ('n', 'number', None, _('list the revision number (default)')),
2142 2131 ('c', 'changeset', None, _('list the changeset')),
2143 2132 ('I', 'include', [], _('include names matching the given patterns')),
2144 2133 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2145 2134 _('hg annotate [OPTION]... FILE...')),
2146 2135 "bundle":
2147 2136 (bundle,
2148 2137 [],
2149 2138 _('hg bundle FILE DEST')),
2150 2139 "cat":
2151 2140 (cat,
2152 2141 [('I', 'include', [], _('include names matching the given patterns')),
2153 2142 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2154 2143 ('o', 'output', "", _('print output to file with formatted name')),
2155 2144 ('r', 'rev', '', _('print the given revision'))],
2156 2145 _('hg cat [OPTION]... FILE...')),
2157 2146 "^clone":
2158 2147 (clone,
2159 2148 [('U', 'noupdate', None, _('do not update the new working directory')),
2160 2149 ('e', 'ssh', "", _('specify ssh command to use')),
2161 2150 ('', 'pull', None, _('use pull protocol to copy metadata')),
2162 2151 ('r', 'rev', [], _('a changeset you would like to have after cloning')),
2163 2152 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2164 2153 _('hg clone [OPTION]... SOURCE [DEST]')),
2165 2154 "^commit|ci":
2166 2155 (commit,
2167 2156 [('A', 'addremove', None, _('run addremove during commit')),
2168 2157 ('I', 'include', [], _('include names matching the given patterns')),
2169 2158 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2170 2159 ('m', 'message', "", _('use <text> as commit message')),
2171 2160 ('l', 'logfile', "", _('read the commit message from <file>')),
2172 2161 ('d', 'date', "", _('record datecode as commit date')),
2173 2162 ('u', 'user', "", _('record user as commiter'))],
2174 2163 _('hg commit [OPTION]... [FILE]...')),
2175 2164 "copy|cp": (copy,
2176 2165 [('I', 'include', [], _('include names matching the given patterns')),
2177 2166 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2178 2167 ('A', 'after', None, _('record a copy that has already occurred')),
2179 2168 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2180 2169 _('hg copy [OPTION]... [SOURCE]... DEST')),
2181 2170 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2182 2171 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2183 2172 "debugconfig": (debugconfig, [], _('debugconfig')),
2184 2173 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2185 2174 "debugstate": (debugstate, [], _('debugstate')),
2186 2175 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2187 2176 "debugindex": (debugindex, [], _('debugindex FILE')),
2188 2177 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2189 2178 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2190 2179 "debugwalk":
2191 2180 (debugwalk,
2192 2181 [('I', 'include', [], _('include names matching the given patterns')),
2193 2182 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2194 2183 _('debugwalk [OPTION]... [FILE]...')),
2195 2184 "^diff":
2196 2185 (diff,
2197 2186 [('r', 'rev', [], _('revision')),
2198 2187 ('a', 'text', None, _('treat all files as text')),
2199 2188 ('I', 'include', [], _('include names matching the given patterns')),
2200 2189 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2201 2190 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2202 2191 "^export":
2203 2192 (export,
2204 2193 [('o', 'output', "", _('print output to file with formatted name')),
2205 2194 ('a', 'text', None, _('treat all files as text'))],
2206 2195 "hg export [-a] [-o OUTFILE] REV..."),
2207 2196 "forget":
2208 2197 (forget,
2209 2198 [('I', 'include', [], _('include names matching the given patterns')),
2210 2199 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2211 2200 "hg forget [OPTION]... FILE..."),
2212 2201 "grep":
2213 2202 (grep,
2214 2203 [('0', 'print0', None, _('end fields with NUL')),
2215 2204 ('I', 'include', [], _('include names matching the given patterns')),
2216 2205 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2217 2206 ('', 'all', None, _('print all revisions that match')),
2218 2207 ('i', 'ignore-case', None, _('ignore case when matching')),
2219 2208 ('l', 'files-with-matches', None, _('print only filenames and revs that match')),
2220 2209 ('n', 'line-number', None, _('print matching line numbers')),
2221 2210 ('r', 'rev', [], _('search in given revision range')),
2222 2211 ('u', 'user', None, _('print user who committed change'))],
2223 2212 "hg grep [OPTION]... PATTERN [FILE]..."),
2224 2213 "heads":
2225 2214 (heads,
2226 2215 [('b', 'branches', None, _('find branch info'))],
2227 2216 _('hg heads [-b]')),
2228 2217 "help": (help_, [], _('hg help [COMMAND]')),
2229 2218 "identify|id": (identify, [], _('hg identify')),
2230 2219 "import|patch":
2231 2220 (import_,
2232 2221 [('p', 'strip', 1, _('directory strip option for patch. This has the same\n') +
2233 2222 _('meaning as the corresponding patch option')),
2234 2223 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
2235 2224 ('b', 'base', "", _('base path'))],
2236 2225 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
2237 2226 "incoming|in": (incoming,
2238 2227 [('M', 'no-merges', None, _("do not show merges")),
2239 2228 ('p', 'patch', None, _('show patch')),
2240 2229 ('n', 'newest-first', None, _('show newest record first'))],
2241 2230 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2242 2231 "^init": (init, [], _('hg init [DEST]')),
2243 2232 "locate":
2244 2233 (locate,
2245 2234 [('r', 'rev', '', _('search the repository as it stood at rev')),
2246 2235 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2247 2236 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
2248 2237 ('I', 'include', [], _('include names matching the given patterns')),
2249 2238 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2250 2239 _('hg locate [OPTION]... [PATTERN]...')),
2251 2240 "^log|history":
2252 2241 (log,
2253 2242 [('I', 'include', [], _('include names matching the given patterns')),
2254 2243 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2255 2244 ('b', 'branch', None, _('show branches')),
2256 2245 ('k', 'keyword', [], _('search for a keyword')),
2257 2246 ('r', 'rev', [], _('show the specified revision or range')),
2258 2247 ('M', 'no-merges', None, _("do not show merges")),
2259 2248 ('m', 'only-merges', None, _("show only merges")),
2260 2249 ('p', 'patch', None, _('show patch'))],
2261 2250 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2262 2251 "manifest": (manifest, [], _('hg manifest [REV]')),
2263 2252 "outgoing|out": (outgoing,
2264 2253 [('M', 'no-merges', None, _("do not show merges")),
2265 2254 ('p', 'patch', None, _('show patch')),
2266 2255 ('n', 'newest-first', None, _('show newest record first'))],
2267 2256 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2268 2257 "^parents": (parents, [], _('hg parents [REV]')),
2269 2258 "paths": (paths, [], _('hg paths [NAME]')),
2270 2259 "^pull":
2271 2260 (pull,
2272 2261 [('u', 'update', None, _('update the working directory to tip after pull')),
2273 2262 ('e', 'ssh', "", _('specify ssh command to use')),
2274 2263 ('r', 'rev', [], _('a specific revision you would like to pull')),
2275 2264 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2276 2265 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2277 2266 "^push":
2278 2267 (push,
2279 2268 [('f', 'force', None, _('force push')),
2280 2269 ('e', 'ssh', "", _('specify ssh command to use')),
2281 2270 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2282 2271 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2283 2272 "rawcommit":
2284 2273 (rawcommit,
2285 2274 [('p', 'parent', [], _('parent')),
2286 2275 ('d', 'date', "", _('date code')),
2287 2276 ('u', 'user', "", _('user')),
2288 2277 ('F', 'files', "", _('file list')),
2289 2278 ('m', 'message', "", _('commit message')),
2290 2279 ('l', 'logfile', "", _('commit message file'))],
2291 2280 _('hg rawcommit [OPTION]... [FILE]...')),
2292 2281 "recover": (recover, [], _("hg recover")),
2293 2282 "^remove|rm": (remove,
2294 2283 [('I', 'include', [], _('include names matching the given patterns')),
2295 2284 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2296 2285 _("hg remove [OPTION]... FILE...")),
2297 2286 "rename|mv": (rename,
2298 2287 [('I', 'include', [], _('include names matching the given patterns')),
2299 2288 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2300 2289 ('A', 'after', None, _('record a rename that has already occurred')),
2301 2290 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2302 2291 _('hg rename [OPTION]... [SOURCE]... DEST')),
2303 2292 "^revert":
2304 2293 (revert,
2305 2294 [('I', 'include', [], _('include names matching the given patterns')),
2306 2295 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2307 2296 ("r", "rev", "", _("revision to revert to"))],
2308 2297 _("hg revert [-n] [-r REV] [NAME]...")),
2309 2298 "root": (root, [], _("hg root")),
2310 2299 "^serve":
2311 2300 (serve,
2312 2301 [('A', 'accesslog', '', _('name of access log file to write to')),
2313 2302 ('E', 'errorlog', '', _('name of error log file to write to')),
2314 2303 ('p', 'port', 0, _('port to use (default: 8000)')),
2315 2304 ('a', 'address', '', _('address to use')),
2316 2305 ('n', 'name', "", _('name to show in web pages (default: working dir)')),
2317 2306 ('', 'stdio', None, _('for remote clients')),
2318 2307 ('t', 'templates', "", _('web templates to use')),
2319 2308 ('', 'style', "", _('template style to use')),
2320 2309 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2321 2310 _("hg serve [OPTION]...")),
2322 2311 "^status|st":
2323 2312 (status,
2324 2313 [('m', 'modified', None, _('show only modified files')),
2325 2314 ('a', 'added', None, _('show only added files')),
2326 2315 ('r', 'removed', None, _('show only removed files')),
2327 2316 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2328 2317 ('n', 'no-status', None, _('hide status prefix')),
2329 2318 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2330 2319 ('I', 'include', [], _('include names matching the given patterns')),
2331 2320 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2332 2321 _("hg status [OPTION]... [FILE]...")),
2333 2322 "tag":
2334 2323 (tag,
2335 2324 [('l', 'local', None, _('make the tag local')),
2336 2325 ('m', 'message', "", _('message for tag commit log entry')),
2337 2326 ('d', 'date', "", _('record datecode as commit date')),
2338 2327 ('u', 'user', "", _('record user as commiter')),
2339 2328 ('r', 'rev', "", _('revision to tag'))],
2340 2329 _('hg tag [OPTION]... NAME [REV]')),
2341 2330 "tags": (tags, [], _('hg tags')),
2342 2331 "tip": (tip, [], _('hg tip')),
2343 2332 "unbundle":
2344 2333 (unbundle,
2345 2334 [],
2346 2335 _('hg unbundle FILE')),
2347 2336 "undo": (undo, [], _('hg undo')),
2348 2337 "^update|up|checkout|co":
2349 2338 (update,
2350 2339 [('b', 'branch', "", _('checkout the head of a specific branch')),
2351 2340 ('m', 'merge', None, _('allow merging of branches')),
2352 2341 ('C', 'clean', None, _('overwrite locally modified files'))],
2353 2342 _('hg update [-b TAG] [-m] [-C] [REV]')),
2354 2343 "verify": (verify, [], _('hg verify')),
2355 2344 "version": (show_version, [], _('hg version')),
2356 2345 }
2357 2346
2358 2347 globalopts = [
2359 2348 ('R', 'repository', "", _("repository root directory")),
2360 2349 ('', 'cwd', '', _("change working directory")),
2361 2350 ('y', 'noninteractive', None, _("do not prompt, assume 'yes' for any required answers")),
2362 2351 ('q', 'quiet', None, _("suppress output")),
2363 2352 ('v', 'verbose', None, _("enable additional output")),
2364 2353 ('', 'debug', None, _("enable debugging output")),
2365 2354 ('', 'debugger', None, _("start debugger")),
2366 2355 ('', 'traceback', None, _("print traceback on exception")),
2367 2356 ('', 'time', None, _("time how long the command takes")),
2368 2357 ('', 'profile', None, _("print command execution profile")),
2369 2358 ('', 'version', None, _("output version information and exit")),
2370 2359 ('h', 'help', None, _("display help and exit")),
2371 2360 ]
2372 2361
2373 2362 norepo = ("clone init version help debugancestor debugconfig debugdata"
2374 2363 " debugindex debugindexdot paths")
2375 2364
2376 2365 def find(cmd):
2377 2366 choice = []
2378 2367 for e in table.keys():
2379 2368 aliases = e.lstrip("^").split("|")
2380 2369 if cmd in aliases:
2381 2370 return e, table[e]
2382 2371 for a in aliases:
2383 2372 if a.startswith(cmd):
2384 2373 choice.append(e)
2385 2374 if len(choice) == 1:
2386 2375 e = choice[0]
2387 2376 return e, table[e]
2388 2377
2389 2378 raise UnknownCommand(cmd)
2390 2379
2391 2380 class SignalInterrupt(Exception):
2392 2381 """Exception raised on SIGTERM and SIGHUP."""
2393 2382
2394 2383 def catchterm(*args):
2395 2384 raise SignalInterrupt
2396 2385
2397 2386 def run():
2398 2387 sys.exit(dispatch(sys.argv[1:]))
2399 2388
2400 2389 class ParseError(Exception):
2401 2390 """Exception raised on errors in parsing the command line."""
2402 2391
2403 2392 def parse(ui, args):
2404 2393 options = {}
2405 2394 cmdoptions = {}
2406 2395
2407 2396 try:
2408 2397 args = fancyopts.fancyopts(args, globalopts, options)
2409 2398 except fancyopts.getopt.GetoptError, inst:
2410 2399 raise ParseError(None, inst)
2411 2400
2412 2401 if args:
2413 2402 cmd, args = args[0], args[1:]
2414 2403 defaults = ui.config("defaults", cmd)
2415 2404 if defaults:
2416 2405 # reparse with command defaults added
2417 2406 args = [cmd] + defaults.split() + args
2418 2407 try:
2419 2408 args = fancyopts.fancyopts(args, globalopts, options)
2420 2409 except fancyopts.getopt.GetoptError, inst:
2421 2410 raise ParseError(None, inst)
2422 2411
2423 2412 cmd, args = args[0], args[1:]
2424 2413
2425 2414 i = find(cmd)[1]
2426 2415 c = list(i[1])
2427 2416 else:
2428 2417 cmd = None
2429 2418 c = []
2430 2419
2431 2420 # combine global options into local
2432 2421 for o in globalopts:
2433 2422 c.append((o[0], o[1], options[o[1]], o[3]))
2434 2423
2435 2424 try:
2436 2425 args = fancyopts.fancyopts(args, c, cmdoptions)
2437 2426 except fancyopts.getopt.GetoptError, inst:
2438 2427 raise ParseError(cmd, inst)
2439 2428
2440 2429 # separate global options back out
2441 2430 for o in globalopts:
2442 2431 n = o[1]
2443 2432 options[n] = cmdoptions[n]
2444 2433 del cmdoptions[n]
2445 2434
2446 2435 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2447 2436
2448 2437 def dispatch(args):
2449 2438 signal.signal(signal.SIGTERM, catchterm)
2450 2439 try:
2451 2440 signal.signal(signal.SIGHUP, catchterm)
2452 2441 except AttributeError:
2453 2442 pass
2454 2443
2455 2444 try:
2456 2445 u = ui.ui()
2457 2446 except util.Abort, inst:
2458 2447 sys.stderr.write(_("abort: %s\n") % inst)
2459 2448 sys.exit(1)
2460 2449
2461 2450 external = []
2462 2451 for x in u.extensions():
2463 2452 def on_exception(Exception, inst):
2464 2453 u.warn(_("*** failed to import extension %s\n") % x[1])
2465 2454 u.warn("%s\n" % inst)
2466 2455 if "--traceback" in sys.argv[1:]:
2467 2456 traceback.print_exc()
2468 2457 if x[1]:
2469 2458 try:
2470 2459 mod = imp.load_source(x[0], x[1])
2471 2460 except Exception, inst:
2472 2461 on_exception(Exception, inst)
2473 2462 continue
2474 2463 else:
2475 2464 def importh(name):
2476 2465 mod = __import__(name)
2477 2466 components = name.split('.')
2478 2467 for comp in components[1:]:
2479 2468 mod = getattr(mod, comp)
2480 2469 return mod
2481 2470 try:
2482 2471 mod = importh(x[0])
2483 2472 except Exception, inst:
2484 2473 on_exception(Exception, inst)
2485 2474 continue
2486 2475
2487 2476 external.append(mod)
2488 2477 for x in external:
2489 2478 cmdtable = getattr(x, 'cmdtable', {})
2490 2479 for t in cmdtable:
2491 2480 if t in table:
2492 2481 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2493 2482 table.update(cmdtable)
2494 2483
2495 2484 try:
2496 2485 cmd, func, args, options, cmdoptions = parse(u, args)
2497 2486 except ParseError, inst:
2498 2487 if inst.args[0]:
2499 2488 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2500 2489 help_(u, inst.args[0])
2501 2490 else:
2502 2491 u.warn(_("hg: %s\n") % inst.args[1])
2503 2492 help_(u, 'shortlist')
2504 2493 sys.exit(-1)
2505 2494 except UnknownCommand, inst:
2506 2495 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2507 2496 help_(u, 'shortlist')
2508 2497 sys.exit(1)
2509 2498
2510 2499 if options["time"]:
2511 2500 def get_times():
2512 2501 t = os.times()
2513 2502 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2514 2503 t = (t[0], t[1], t[2], t[3], time.clock())
2515 2504 return t
2516 2505 s = get_times()
2517 2506 def print_time():
2518 2507 t = get_times()
2519 2508 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2520 2509 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2521 2510 atexit.register(print_time)
2522 2511
2523 2512 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2524 2513 not options["noninteractive"])
2525 2514
2526 2515 # enter the debugger before command execution
2527 2516 if options['debugger']:
2528 2517 pdb.set_trace()
2529 2518
2530 2519 try:
2531 2520 try:
2532 2521 if options['help']:
2533 2522 help_(u, cmd, options['version'])
2534 2523 sys.exit(0)
2535 2524 elif options['version']:
2536 2525 show_version(u)
2537 2526 sys.exit(0)
2538 2527 elif not cmd:
2539 2528 help_(u, 'shortlist')
2540 2529 sys.exit(0)
2541 2530
2542 2531 if options['cwd']:
2543 2532 try:
2544 2533 os.chdir(options['cwd'])
2545 2534 except OSError, inst:
2546 2535 raise util.Abort('%s: %s' %
2547 2536 (options['cwd'], inst.strerror))
2548 2537
2549 2538 if cmd not in norepo.split():
2550 2539 path = options["repository"] or ""
2551 2540 repo = hg.repository(ui=u, path=path)
2552 2541 for x in external:
2553 2542 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2554 2543 d = lambda: func(u, repo, *args, **cmdoptions)
2555 2544 else:
2556 2545 d = lambda: func(u, *args, **cmdoptions)
2557 2546
2558 2547 if options['profile']:
2559 2548 import hotshot, hotshot.stats
2560 2549 prof = hotshot.Profile("hg.prof")
2561 2550 r = prof.runcall(d)
2562 2551 prof.close()
2563 2552 stats = hotshot.stats.load("hg.prof")
2564 2553 stats.strip_dirs()
2565 2554 stats.sort_stats('time', 'calls')
2566 2555 stats.print_stats(40)
2567 2556 return r
2568 2557 else:
2569 2558 return d()
2570 2559 except:
2571 2560 # enter the debugger when we hit an exception
2572 2561 if options['debugger']:
2573 2562 pdb.post_mortem(sys.exc_info()[2])
2574 2563 if options['traceback']:
2575 2564 traceback.print_exc()
2576 2565 raise
2577 2566 except hg.RepoError, inst:
2578 2567 u.warn(_("abort: "), inst, "!\n")
2579 2568 except revlog.RevlogError, inst:
2580 2569 u.warn(_("abort: "), inst, "!\n")
2581 2570 except SignalInterrupt:
2582 2571 u.warn(_("killed!\n"))
2583 2572 except KeyboardInterrupt:
2584 2573 try:
2585 2574 u.warn(_("interrupted!\n"))
2586 2575 except IOError, inst:
2587 2576 if inst.errno == errno.EPIPE:
2588 2577 if u.debugflag:
2589 2578 u.warn(_("\nbroken pipe\n"))
2590 2579 else:
2591 2580 raise
2592 2581 except IOError, inst:
2593 2582 if hasattr(inst, "code"):
2594 2583 u.warn(_("abort: %s\n") % inst)
2595 2584 elif hasattr(inst, "reason"):
2596 2585 u.warn(_("abort: error: %s\n") % inst.reason[1])
2597 2586 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2598 2587 if u.debugflag:
2599 2588 u.warn(_("broken pipe\n"))
2600 2589 elif getattr(inst, "strerror", None):
2601 2590 if getattr(inst, "filename", None):
2602 2591 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2603 2592 else:
2604 2593 u.warn(_("abort: %s\n") % inst.strerror)
2605 2594 else:
2606 2595 raise
2607 2596 except OSError, inst:
2608 2597 if hasattr(inst, "filename"):
2609 2598 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2610 2599 else:
2611 2600 u.warn(_("abort: %s\n") % inst.strerror)
2612 2601 except util.Abort, inst:
2613 2602 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2614 2603 sys.exit(1)
2615 2604 except TypeError, inst:
2616 2605 # was this an argument error?
2617 2606 tb = traceback.extract_tb(sys.exc_info()[2])
2618 2607 if len(tb) > 2: # no
2619 2608 raise
2620 2609 u.debug(inst, "\n")
2621 2610 u.warn(_("%s: invalid arguments\n") % cmd)
2622 2611 help_(u, cmd)
2623 2612 except UnknownCommand, inst:
2624 2613 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2625 2614 help_(u, 'shortlist')
2626 2615 except SystemExit:
2627 2616 # don't catch this in the catch-all below
2628 2617 raise
2629 2618 except:
2630 2619 u.warn(_("** unknown exception encountered, details follow\n"))
2631 2620 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2632 2621 raise
2633 2622
2634 2623 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now