##// END OF EJS Templates
Make show_changeset show added and deleted files in verbose mode....
Thomas Arendsen Hein -
r490:df9b77f6 default
parent child Browse files
Show More
@@ -1,45 +1,44 b''
1 1 General:
2 2 - Better documentation
3 3 - More regression tests
4 4 - More specific try/except.
5 5 - less code duplication, more code in the right places
6 6 - python 2.2 support
7 7 - better import support
8 8 - export to git
9 9 - Add standard files: AUTHORS, CREDITS, ChangeLog? What else?
10 10 - Code cleanup: apply http://python.org/peps/pep-0008.html
11 11
12 12 Core:
13 13 - difflib creating/removing files (fixed except dates: should be epoch)
14 14 - directory foo.d or foo.i with existing file foo (use some quoting?)
15 15 - get various options from hgrc (e.g. history always -v, tip always -q)
16 16 - better push support (hack exists)
17 17 - hg over ssh:// and https://
18 18 - commit mailinglist/trigger/hooks
19 19
20 20 Commands:
21 21 - hg status <filename>: file rev, changeset rev, changed, added,
22 22 deleted, sha-1
23 23 - select to pull a subset of the heads
24 24 - commands.py: number of args too much magic (e.g. in patch())
25 25 - automatic pull fallback to old-http://
26 26 - hg init|pull http://example.com doesn't say that no repo was found
27 27 - hg annotate -u and hgweb annotate with long $EMAIL
28 - hg -v history doesn't show tkmerge as modified (removed).
29 28 - hg pull default in a subdir doesn't work, if it is a relative path
30 29 - optionally only show merges (two parents or parent != changeset-1, etc.)
31 30
32 31 Web:
33 32 - show tags in hgweb
34 33 - show parent changeset number in hgweb
35 34 - optionally only show merges (two parents or parent != changeset-1, etc.)
36 35 - one hgweb with many repos (another script)
37 36 - hgweb tip link too verbose
38 37 - hgweb: deliver static files (e.g. favicon, stylesheets)
39 38 - hgweb personalization: timezone (display/change), display of
40 39 features
41 40 - hg export 240 shows -tkmerge (good), hgweb does not (bad).
42 41 - some web servers think hgweb.cgi.[di] is a CGI script with old-http://
43 42 (use quoting (see foo.d in Core) or document server configurations?)
44 43 - link children in hgweb
45 44 - search field searching in descriptions, file names, what else?
@@ -1,892 +1,896 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 import os, re, sys, signal
9 9 import fancyopts, ui, hg, util
10 10 from demandload import *
11 11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
12 12
13 13 class UnknownCommand(Exception): pass
14 14
15 15 def filterfiles(filters, files):
16 16 l = [ x for x in files if x in filters ]
17 17
18 18 for t in filters:
19 19 if t and t[-1] != "/": t += "/"
20 20 l += [ x for x in files if x.startswith(t) ]
21 21 return l
22 22
23 23 def relfilter(repo, files):
24 24 if os.getcwd() != repo.root:
25 25 p = os.getcwd()[len(repo.root) + 1: ]
26 26 return filterfiles([util.pconvert(p)], files)
27 27 return files
28 28
29 29 def relpath(repo, args):
30 30 if os.getcwd() != repo.root:
31 31 p = os.getcwd()[len(repo.root) + 1: ]
32 32 return [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
33 33 return args
34 34
35 35 def dodiff(ui, repo, path, files = None, node1 = None, node2 = None):
36 36 def date(c):
37 37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
38 38
39 39 if node2:
40 40 change = repo.changelog.read(node2)
41 41 mmap2 = repo.manifest.read(change[0])
42 42 (c, a, d) = repo.diffrevs(node1, node2)
43 43 def read(f): return repo.file(f).read(mmap2[f])
44 44 date2 = date(change)
45 45 else:
46 46 date2 = time.asctime()
47 47 (c, a, d, u) = repo.diffdir(path, node1)
48 48 if not node1:
49 49 node1 = repo.dirstate.parents()[0]
50 50 def read(f): return repo.wfile(f).read()
51 51
52 52 if ui.quiet:
53 53 r = None
54 54 else:
55 55 hexfunc = ui.verbose and hg.hex or hg.short
56 56 r = [hexfunc(node) for node in [node1, node2] if node]
57 57
58 58 change = repo.changelog.read(node1)
59 59 mmap = repo.manifest.read(change[0])
60 60 date1 = date(change)
61 61
62 62 if files:
63 63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
64 64
65 65 for f in c:
66 66 to = None
67 67 if f in mmap:
68 68 to = repo.file(f).read(mmap[f])
69 69 tn = read(f)
70 70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
71 71 for f in a:
72 72 to = None
73 73 tn = read(f)
74 74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
75 75 for f in d:
76 76 to = repo.file(f).read(mmap[f])
77 77 tn = None
78 78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
79 79
80 80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
81 81 """show a single changeset or file revision"""
82 82 changelog = repo.changelog
83 83 if filelog:
84 84 log = filelog
85 85 filerev = rev
86 86 node = filenode = filelog.node(filerev)
87 87 changerev = filelog.linkrev(filenode)
88 88 changenode = changenode or changelog.node(changerev)
89 89 else:
90 90 log = changelog
91 91 changerev = rev
92 92 if changenode is None:
93 93 changenode = changelog.node(changerev)
94 94 elif not changerev:
95 95 rev = changerev = changelog.rev(changenode)
96 96 node = changenode
97 97
98 98 if ui.quiet:
99 99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
100 100 return
101 101
102 102 changes = changelog.read(changenode)
103 103
104 104 parents = [(log.rev(parent), hg.hex(parent))
105 105 for parent in log.parents(node)
106 106 if ui.debugflag or parent != hg.nullid]
107 107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
108 108 parents = []
109 109
110 110 if filelog:
111 111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
112 112 for parent in parents:
113 113 ui.write("parent: %d:%s\n" % parent)
114 114 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
115 115 else:
116 116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
117 117 for tag in repo.nodetags(changenode):
118 118 ui.status("tag: %s\n" % tag)
119 119 for parent in parents:
120 120 ui.write("parent: %d:%s\n" % parent)
121 121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
122 122 hg.hex(changes[0])))
123 123 ui.status("user: %s\n" % changes[1])
124 124 ui.status("date: %s\n" % time.asctime(
125 125 time.localtime(float(changes[2].split(' ')[0]))))
126 ui.note("files: %s\n" % " ".join(changes[3]))
126 if ui.verbose:
127 files = repo.diffrevs(changelog.parents(changenode)[0], changenode)
128 for key, value in zip(["files:", "files+:", "files-:"], files):
129 if value:
130 ui.note("%-12s %s\n" % (key, " ".join(value)))
127 131 description = changes[4].strip()
128 132 if description:
129 133 if ui.verbose:
130 134 ui.status("description:\n")
131 135 ui.status(description)
132 136 ui.status("\n")
133 137 else:
134 138 ui.status("summary: %s\n" % description.splitlines()[0])
135 139 ui.status("\n")
136 140
137 141 def show_version(ui):
138 142 """output version and copyright information"""
139 143 ui.write("Mercurial version %s\n" % version.get_version())
140 144 ui.status(
141 145 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
142 146 "This is free software; see the source for copying conditions. "
143 147 "There is NO\nwarranty; "
144 148 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
145 149 )
146 150
147 151 def help(ui, cmd=None):
148 152 '''show help for a given command or all commands'''
149 153 if cmd:
150 154 try:
151 155 i = find(cmd)
152 156 ui.write("%s\n\n" % i[2])
153 157
154 158 if i[1]:
155 159 for s, l, d, c in i[1]:
156 160 opt=' '
157 161 if s: opt = opt + '-' + s + ' '
158 162 if l: opt = opt + '--' + l + ' '
159 163 if d: opt = opt + '(' + str(d) + ')'
160 164 ui.write(opt, "\n")
161 165 if c: ui.write(' %s\n' % c)
162 166 ui.write("\n")
163 167
164 168 ui.write(i[0].__doc__, "\n")
165 169 except UnknownCommand:
166 170 ui.warn("hg: unknown command %s\n" % cmd)
167 171 sys.exit(0)
168 172 else:
169 173 if not ui.quiet:
170 174 show_version(ui)
171 175 ui.write('\n')
172 176 ui.write('hg commands:\n\n')
173 177
174 178 h = {}
175 179 for c, e in table.items():
176 180 f = c.split("|")[0]
177 181 if f.startswith("debug"):
178 182 continue
179 183 d = ""
180 184 if e[0].__doc__:
181 185 d = e[0].__doc__.splitlines(0)[0].rstrip()
182 186 h[f] = d
183 187
184 188 fns = h.keys()
185 189 fns.sort()
186 190 m = max(map(len, fns))
187 191 for f in fns:
188 192 ui.write(' %-*s %s\n' % (m, f, h[f]))
189 193
190 194 # Commands start here, listed alphabetically
191 195
192 196 def add(ui, repo, file, *files):
193 197 '''add the specified files on the next commit'''
194 198 repo.add(relpath(repo, (file,) + files))
195 199
196 200 def addremove(ui, repo, *files):
197 201 """add all new files, delete all missing files"""
198 202 if files:
199 203 files = relpath(repo, files)
200 204 d = []
201 205 u = []
202 206 for f in files:
203 207 p = repo.wjoin(f)
204 208 s = repo.dirstate.state(f)
205 209 isfile = os.path.isfile(p)
206 210 if s != 'r' and not isfile:
207 211 d.append(f)
208 212 elif s not in 'nmai' and isfile:
209 213 u.append(f)
210 214 else:
211 215 (c, a, d, u) = repo.diffdir(repo.root)
212 216 repo.add(u)
213 217 repo.remove(d)
214 218
215 219 def annotate(u, repo, file, *files, **ops):
216 220 """show changeset information per file line"""
217 221 def getnode(rev):
218 222 return hg.short(repo.changelog.node(rev))
219 223
220 224 def getname(rev):
221 225 try:
222 226 return bcache[rev]
223 227 except KeyError:
224 228 cl = repo.changelog.read(repo.changelog.node(rev))
225 229 name = cl[1]
226 230 f = name.find('@')
227 231 if f >= 0:
228 232 name = name[:f]
229 233 bcache[rev] = name
230 234 return name
231 235
232 236 bcache = {}
233 237 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
234 238 if not ops['user'] and not ops['changeset']:
235 239 ops['number'] = 1
236 240
237 241 node = repo.dirstate.parents()[0]
238 242 if ops['revision']:
239 243 node = repo.changelog.lookup(ops['revision'])
240 244 change = repo.changelog.read(node)
241 245 mmap = repo.manifest.read(change[0])
242 246 for f in relpath(repo, (file,) + files):
243 247 lines = repo.file(f).annotate(mmap[f])
244 248 pieces = []
245 249
246 250 for o, f in opmap:
247 251 if ops[o]:
248 252 l = [ f(n) for n,t in lines ]
249 253 m = max(map(len, l))
250 254 pieces.append([ "%*s" % (m, x) for x in l])
251 255
252 256 for p,l in zip(zip(*pieces), lines):
253 257 u.write(" ".join(p) + ": " + l[1])
254 258
255 259 def cat(ui, repo, file, rev = []):
256 260 """output the latest or given revision of a file"""
257 261 r = repo.file(relpath(repo, [file])[0])
258 262 n = r.tip()
259 263 if rev: n = r.lookup(rev)
260 264 sys.stdout.write(r.read(n))
261 265
262 266 def clone(ui, source, dest = None, **opts):
263 267 """make a copy of an existing repository"""
264 268 paths = {}
265 269 for name, path in ui.configitems("paths"):
266 270 paths[name] = path
267 271
268 272 if source in paths: source = paths[source]
269 273
270 274 if dest is None:
271 275 dest = os.getcwd()
272 276 elif not os.path.exists(dest):
273 277 os.makedirs(dest)
274 278
275 279 link = 0
276 280 if not source.startswith("http://"):
277 281 source = os.path.realpath(source)
278 282 d1 = os.stat(dest).st_dev
279 283 d2 = os.stat(source).st_dev
280 284 if d1 == d2: link = 1
281 285
282 286 os.chdir(dest)
283 287
284 288 if link:
285 289 ui.debug("copying by hardlink\n")
286 290 os.system("cp -al %s/.hg .hg" % source)
287 291 try:
288 292 os.remove(".hg/dirstate")
289 293 except: pass
290 294
291 295 repo = hg.repository(ui, ".")
292 296
293 297 else:
294 298 repo = hg.repository(ui, ".", create=1)
295 299 other = hg.repository(ui, source)
296 300 cg = repo.getchangegroup(other)
297 301 repo.addchangegroup(cg)
298 302
299 303 f = repo.opener("hgrc", "w")
300 304 f.write("[paths]\n")
301 305 f.write("default = %s\n" % source)
302 306
303 307 if not opts['no-update']:
304 308 update(ui, repo)
305 309
306 310 def commit(ui, repo, *files, **opts):
307 311 """commit the specified files or all outstanding changes"""
308 312 text = opts['text']
309 313 if not text and opts['logfile']:
310 314 try: text = open(opts['logfile']).read()
311 315 except IOError: pass
312 316
313 317 if opts['addremove']:
314 318 addremove(ui, repo, *files)
315 319 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
316 320
317 321 def copy(ui, repo, source, dest):
318 322 """mark a file as copied or renamed for the next commit"""
319 323 return repo.copy(*relpath(repo, (source, dest)))
320 324
321 325 def debugcheckdirstate(ui, repo):
322 326 parent1, parent2 = repo.dirstate.parents()
323 327 dc = repo.dirstate.dup()
324 328 keys = dc.keys()
325 329 keys.sort()
326 330 m1n = repo.changelog.read(parent1)[0]
327 331 m2n = repo.changelog.read(parent2)[0]
328 332 m1 = repo.manifest.read(m1n)
329 333 m2 = repo.manifest.read(m2n)
330 334 errors = 0
331 335 for f in dc:
332 336 state = repo.dirstate.state(f)
333 337 if state in "nr" and f not in m1:
334 338 print "%s in state %s, but not listed in manifest1" % (f, state)
335 339 errors += 1
336 340 if state in "a" and f in m1:
337 341 print "%s in state %s, but also listed in manifest1" % (f, state)
338 342 errors += 1
339 343 if state in "m" and f not in m1 and f not in m2:
340 344 print "%s in state %s, but not listed in either manifest" % (f, state)
341 345 errors += 1
342 346 for f in m1:
343 347 state = repo.dirstate.state(f)
344 348 if state not in "nrm":
345 349 print "%s in manifest1, but listed as state %s" % (f, state)
346 350 errors += 1
347 351 if errors:
348 352 print ".hg/dirstate inconsistent with current parent's manifest, aborting"
349 353 sys.exit(1)
350 354
351 355 def debugdumpdirstate(ui, repo):
352 356 dc = repo.dirstate.dup()
353 357 keys = dc.keys()
354 358 keys.sort()
355 359 for file in keys:
356 360 print "%s => %c" % (file, dc[file][0])
357 361
358 362 def debugindex(ui, file):
359 363 r = hg.revlog(hg.opener(""), file, "")
360 364 print " rev offset length base linkrev"+\
361 365 " p1 p2 nodeid"
362 366 for i in range(r.count()):
363 367 e = r.index[i]
364 368 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
365 369 i, e[0], e[1], e[2], e[3],
366 370 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
367 371
368 372 def debugindexdot(ui, file):
369 373 r = hg.revlog(hg.opener(""), file, "")
370 374 print "digraph G {"
371 375 for i in range(r.count()):
372 376 e = r.index[i]
373 377 print "\t%d -> %d" % (r.rev(e[4]), i)
374 378 if e[5] != hg.nullid:
375 379 print "\t%d -> %d" % (r.rev(e[5]), i)
376 380 print "}"
377 381
378 382 def diff(ui, repo, *files, **opts):
379 383 """diff working directory (or selected files)"""
380 384 revs = []
381 385 if opts['rev']:
382 386 revs = map(lambda x: repo.lookup(x), opts['rev'])
383 387
384 388 if len(revs) > 2:
385 389 ui.warn("too many revisions to diff\n")
386 390 sys.exit(1)
387 391
388 392 if files:
389 393 files = relpath(repo, files)
390 394 else:
391 395 files = relpath(repo, [""])
392 396
393 397 dodiff(ui, repo, os.getcwd(), files, *revs)
394 398
395 399 def export(ui, repo, changeset):
396 400 """dump the changeset header and diffs for a revision"""
397 401 node = repo.lookup(changeset)
398 402 prev, other = repo.changelog.parents(node)
399 403 change = repo.changelog.read(node)
400 404 print "# HG changeset patch"
401 405 print "# User %s" % change[1]
402 406 print "# Node ID %s" % hg.hex(node)
403 407 print "# Parent %s" % hg.hex(prev)
404 408 print
405 409 if other != hg.nullid:
406 410 print "# Parent %s" % hg.hex(other)
407 411 print change[4].rstrip()
408 412 print
409 413
410 414 dodiff(ui, repo, "", None, prev, node)
411 415
412 416 def forget(ui, repo, file, *files):
413 417 """don't add the specified files on the next commit"""
414 418 repo.forget(relpath(repo, (file,) + files))
415 419
416 420 def heads(ui, repo):
417 421 """show current repository heads"""
418 422 for n in repo.changelog.heads():
419 423 show_changeset(ui, repo, changenode=n)
420 424
421 425 def history(ui, repo):
422 426 """show the changelog history"""
423 427 for i in range(repo.changelog.count() - 1, -1, -1):
424 428 show_changeset(ui, repo, rev=i)
425 429
426 430 def identify(ui, repo):
427 431 """print information about the working copy"""
428 432 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
429 433 if not parents:
430 434 ui.write("unknown\n")
431 435 return
432 436
433 437 hexfunc = ui.verbose and hg.hex or hg.short
434 438 (c, a, d, u) = repo.diffdir(repo.root)
435 439 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
436 440 (c or a or d) and "+" or "")]
437 441
438 442 if not ui.quiet:
439 443 # multiple tags for a single parent separated by '/'
440 444 parenttags = ['/'.join(tags)
441 445 for tags in map(repo.nodetags, parents) if tags]
442 446 # tags for multiple parents separated by ' + '
443 447 output.append(' + '.join(parenttags))
444 448
445 449 ui.write("%s\n" % ' '.join(output))
446 450
447 451 def import_(ui, repo, patch1, *patches, **opts):
448 452 """import an ordered set of patches"""
449 453 try:
450 454 import psyco
451 455 psyco.full()
452 456 except:
453 457 pass
454 458
455 459 patches = (patch1,) + patches
456 460
457 461 d = opts["base"]
458 462 strip = opts["strip"]
459 463
460 464 for patch in patches:
461 465 ui.status("applying %s\n" % patch)
462 466 pf = os.path.join(d, patch)
463 467
464 468 text = ""
465 469 for l in file(pf):
466 470 if l[:4] == "--- ": break
467 471 text += l
468 472
469 473 # make sure text isn't empty
470 474 if not text: text = "imported patch %s\n" % patch
471 475
472 476 f = os.popen("patch -p%d < %s" % (strip, pf))
473 477 files = []
474 478 for l in f.read().splitlines():
475 479 l.rstrip('\r\n');
476 480 ui.status("%s\n" % l)
477 481 if l[:14] == 'patching file ':
478 482 pf = l[14:]
479 483 if pf not in files:
480 484 files.append(pf)
481 485 patcherr = f.close()
482 486 if patcherr:
483 487 sys.stderr.write("patch failed")
484 488 sys.exit(1)
485 489
486 490 if len(files) > 0:
487 491 addremove(ui, repo, *files)
488 492 repo.commit(files, text)
489 493
490 494 def init(ui, source=None, **opts):
491 495 """create a new repository or (deprecated, use clone) copy an existing one"""
492 496
493 497 if source:
494 498 ui.warn("this use of init is deprecated: use \"hg clone\" instead\n")
495 499 opts['no-update'] = not opts['update']
496 500 clone(ui, source, None, **opts)
497 501 else:
498 502 repo = hg.repository(ui, ".", create=1)
499 503
500 504 def log(ui, repo, f):
501 505 """show the revision history of a single file"""
502 506 f = relpath(repo, [f])[0]
503 507
504 508 r = repo.file(f)
505 509 for i in range(r.count() - 1, -1, -1):
506 510 show_changeset(ui, repo, filelog=r, rev=i)
507 511
508 512 def manifest(ui, repo, rev = []):
509 513 """output the latest or given revision of the project manifest"""
510 514 n = repo.manifest.tip()
511 515 if rev:
512 516 n = repo.manifest.lookup(rev)
513 517 m = repo.manifest.read(n)
514 518 mf = repo.manifest.readflags(n)
515 519 files = m.keys()
516 520 files.sort()
517 521
518 522 for f in files:
519 523 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
520 524
521 525 def parents(ui, repo, node = None):
522 526 '''show the parents of the current working dir'''
523 527 if node:
524 528 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
525 529 else:
526 530 p = repo.dirstate.parents()
527 531
528 532 for n in p:
529 533 if n != hg.nullid:
530 534 show_changeset(ui, repo, changenode=n)
531 535
532 536 def pull(ui, repo, source="default", **opts):
533 537 """pull changes from the specified source"""
534 538 paths = {}
535 539 for name, path in ui.configitems("paths"):
536 540 paths[name] = path
537 541
538 542 if source in paths:
539 543 source = paths[source]
540 544
541 545 ui.status('pulling from %s\n' % (source))
542 546
543 547 other = hg.repository(ui, source)
544 548 cg = repo.getchangegroup(other)
545 549 r = repo.addchangegroup(cg)
546 550 if cg and not r:
547 551 if opts['update']:
548 552 return update(ui, repo)
549 553 else:
550 554 ui.status("(run 'hg update' to get a working copy)\n")
551 555
552 556 return r
553 557
554 558 def push(ui, repo, dest="default-push"):
555 559 """push changes to the specified destination"""
556 560 paths = {}
557 561 for name, path in ui.configitems("paths"):
558 562 paths[name] = path
559 563
560 564 if dest in paths: dest = paths[dest]
561 565
562 566 if not dest.startswith("ssh://"):
563 567 ui.warn("abort: can only push to ssh:// destinations currently\n")
564 568 return 1
565 569
566 570 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
567 571 if not m:
568 572 ui.warn("abort: couldn't parse destination %s\n" % dest)
569 573 return 1
570 574
571 575 user, host, port, path = map(m.group, (2, 3, 5, 7))
572 576 host = user and ("%s@%s" % (user, host)) or host
573 577 port = port and (" -p %s") % port or ""
574 578 path = path or ""
575 579
576 580 sport = random.randrange(30000, 60000)
577 581 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
578 582 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
579 583
580 584 child = os.fork()
581 585 if not child:
582 586 sys.stdout = file("/dev/null", "w")
583 587 sys.stderr = sys.stdout
584 588 hgweb.server(repo.root, "pull", "", "localhost", sport)
585 589 else:
586 590 r = os.system(cmd)
587 591 os.kill(child, signal.SIGTERM)
588 592 return r
589 593
590 594 def rawcommit(ui, repo, *flist, **rc):
591 595 "raw commit interface"
592 596
593 597 text = rc['text']
594 598 if not text and rc['logfile']:
595 599 try: text = open(rc['logfile']).read()
596 600 except IOError: pass
597 601 if not text and not rc['logfile']:
598 602 print "missing commit text"
599 603 return 1
600 604
601 605 files = relpath(repo, list(flist))
602 606 if rc['files']:
603 607 files += open(rc['files']).read().splitlines()
604 608
605 609 rc['parent'] = map(repo.lookup, rc['parent'])
606 610
607 611 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
608 612
609 613 def recover(ui, repo):
610 614 """roll back an interrupted transaction"""
611 615 repo.recover()
612 616
613 617 def remove(ui, repo, file, *files):
614 618 """remove the specified files on the next commit"""
615 619 repo.remove(relpath(repo, (file,) + files))
616 620
617 621 def root(ui, repo):
618 622 """print the root (top) of the current working dir"""
619 623 ui.write(repo.root + "\n")
620 624
621 625 def serve(ui, repo, **opts):
622 626 """export the repository via HTTP"""
623 627 hgweb.server(repo.root, opts["name"], opts["templates"],
624 628 opts["address"], opts["port"])
625 629
626 630 def status(ui, repo):
627 631 '''show changed files in the working directory
628 632
629 633 C = changed
630 634 A = added
631 635 R = removed
632 636 ? = not tracked'''
633 637
634 638 (c, a, d, u) = repo.diffdir(os.getcwd())
635 639 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
636 640
637 641 for f in c: print "C", f
638 642 for f in a: print "A", f
639 643 for f in d: print "R", f
640 644 for f in u: print "?", f
641 645
642 646 def tag(ui, repo, name, rev = None, **opts):
643 647 """add a tag for the current tip or a given revision"""
644 648
645 649 if name == "tip":
646 650 ui.warn("abort: 'tip' is a reserved name!\n")
647 651 return -1
648 652
649 653 (c, a, d, u) = repo.diffdir(repo.root)
650 654 for x in (c, a, d, u):
651 655 if ".hgtags" in x:
652 656 ui.warn("abort: working copy of .hgtags is changed!\n")
653 657 ui.status("(please commit .hgtags manually)\n")
654 658 return -1
655 659
656 660 if rev:
657 661 r = hg.hex(repo.lookup(rev))
658 662 else:
659 663 r = hg.hex(repo.changelog.tip())
660 664
661 665 add = 0
662 666 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
663 667 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
664 668 if add: repo.add([".hgtags"])
665 669
666 670 if not opts['text']:
667 671 opts['text'] = "Added tag %s for changeset %s" % (name, r)
668 672
669 673 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
670 674
671 675 def tags(ui, repo):
672 676 """list repository tags"""
673 677
674 678 l = repo.tagslist()
675 679 l.reverse()
676 680 for t, n in l:
677 681 try:
678 682 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
679 683 except KeyError:
680 684 r = " ?:?"
681 685 ui.write("%-30s %s\n" % (t, r))
682 686
683 687 def tip(ui, repo):
684 688 """show the tip revision"""
685 689 n = repo.changelog.tip()
686 690 show_changeset(ui, repo, changenode=n)
687 691
688 692 def undo(ui, repo):
689 693 """undo the last transaction"""
690 694 repo.undo()
691 695
692 696 def update(ui, repo, node=None, merge=False, clean=False):
693 697 '''update or merge working directory
694 698
695 699 If there are no outstanding changes in the working directory and
696 700 there is a linear relationship between the current version and the
697 701 requested version, the result is the requested version.
698 702
699 703 Otherwise the result is a merge between the contents of the
700 704 current working directory and the requested version. Files that
701 705 changed between either parent are marked as changed for the next
702 706 commit and a commit must be performed before any further updates
703 707 are allowed.
704 708 '''
705 709 node = node and repo.lookup(node) or repo.changelog.tip()
706 710 return repo.update(node, allow=merge, force=clean)
707 711
708 712 def verify(ui, repo):
709 713 """verify the integrity of the repository"""
710 714 return repo.verify()
711 715
712 716 # Command options and aliases are listed here, alphabetically
713 717
714 718 table = {
715 719 "add": (add, [], "hg add [files]"),
716 720 "addremove": (addremove, [], "hg addremove [files]"),
717 721 "annotate": (annotate,
718 722 [('r', 'revision', '', 'revision'),
719 723 ('u', 'user', None, 'show user'),
720 724 ('n', 'number', None, 'show revision number'),
721 725 ('c', 'changeset', None, 'show changeset')],
722 726 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
723 727 "cat": (cat, [], 'hg cat <file> [rev]'),
724 728 "clone": (clone, [('U', 'no-update', None, 'skip update after cloning')],
725 729 'hg clone [options] <source> [dest]'),
726 730 "commit|ci": (commit,
727 731 [('t', 'text', "", 'commit text'),
728 732 ('A', 'addremove', None, 'run add/remove during commit'),
729 733 ('l', 'logfile', "", 'commit text file'),
730 734 ('d', 'date', "", 'data'),
731 735 ('u', 'user', "", 'user')],
732 736 'hg commit [files]'),
733 737 "copy": (copy, [], 'hg copy <source> <dest>'),
734 738 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
735 739 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
736 740 "debugindex": (debugindex, [], 'debugindex <file>'),
737 741 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
738 742 "diff": (diff, [('r', 'rev', [], 'revision')],
739 743 'hg diff [-r A] [-r B] [files]'),
740 744 "export": (export, [], "hg export <changeset>"),
741 745 "forget": (forget, [], "hg forget [files]"),
742 746 "heads": (heads, [], 'hg heads'),
743 747 "history": (history, [], 'hg history'),
744 748 "help": (help, [], 'hg help [command]'),
745 749 "identify|id": (identify, [], 'hg identify'),
746 750 "import|patch": (import_,
747 751 [('p', 'strip', 1, 'path strip'),
748 752 ('b', 'base', "", 'base path')],
749 753 "hg import [options] <patches>"),
750 754 "init": (init, [('u', 'update', None, 'update after init')],
751 755 'hg init [options] [url]'),
752 756 "log": (log, [], 'hg log <file>'),
753 757 "manifest": (manifest, [], 'hg manifest [rev]'),
754 758 "parents": (parents, [], 'hg parents [node]'),
755 759 "pull": (pull,
756 760 [('u', 'update', None, 'update working directory')],
757 761 'hg pull [options] [source]'),
758 762 "push": (push, [], 'hg push <destination>'),
759 763 "rawcommit": (rawcommit,
760 764 [('p', 'parent', [], 'parent'),
761 765 ('d', 'date', "", 'data'),
762 766 ('u', 'user', "", 'user'),
763 767 ('F', 'files', "", 'file list'),
764 768 ('t', 'text', "", 'commit text'),
765 769 ('l', 'logfile', "", 'commit text file')],
766 770 'hg rawcommit [options] [files]'),
767 771 "recover": (recover, [], "hg recover"),
768 772 "remove|rm": (remove, [], "hg remove [files]"),
769 773 "root": (root, [], "hg root"),
770 774 "serve": (serve, [('p', 'port', 8000, 'listen port'),
771 775 ('a', 'address', '', 'interface address'),
772 776 ('n', 'name', os.getcwd(), 'repository name'),
773 777 ('t', 'templates', "", 'template map')],
774 778 "hg serve [options]"),
775 779 "status": (status, [], 'hg status'),
776 780 "tag": (tag, [('t', 'text', "", 'commit text'),
777 781 ('d', 'date', "", 'date'),
778 782 ('u', 'user', "", 'user')],
779 783 'hg tag [options] <name> [rev]'),
780 784 "tags": (tags, [], 'hg tags'),
781 785 "tip": (tip, [], 'hg tip'),
782 786 "undo": (undo, [], 'hg undo'),
783 787 "update|up|checkout|co":
784 788 (update,
785 789 [('m', 'merge', None, 'allow merging of conflicts'),
786 790 ('C', 'clean', None, 'overwrite locally modified files')],
787 791 'hg update [options] [node]'),
788 792 "verify": (verify, [], 'hg verify'),
789 793 "version": (show_version, [], 'hg version'),
790 794 }
791 795
792 796 norepo = "clone init version help debugindex debugindexdot"
793 797
794 798 def find(cmd):
795 799 for e in table.keys():
796 800 if re.match("(%s)$" % e, cmd):
797 801 return table[e]
798 802
799 803 raise UnknownCommand(cmd)
800 804
801 805 class SignalInterrupt(Exception): pass
802 806
803 807 def catchterm(*args):
804 808 raise SignalInterrupt
805 809
806 810 def run():
807 811 sys.exit(dispatch(sys.argv[1:]))
808 812
809 813 def dispatch(args):
810 814 options = {}
811 815 opts = [('v', 'verbose', None, 'verbose'),
812 816 ('d', 'debug', None, 'debug'),
813 817 ('q', 'quiet', None, 'quiet'),
814 818 ('p', 'profile', None, 'profile'),
815 819 ('y', 'noninteractive', None, 'run non-interactively'),
816 820 ('', 'version', None, 'output version information and exit'),
817 821 ]
818 822
819 823 args = fancyopts.fancyopts(args, opts, options,
820 824 'hg [options] <command> [options] [files]')
821 825
822 826 if not args:
823 827 cmd = "help"
824 828 else:
825 829 cmd, args = args[0], args[1:]
826 830
827 831 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
828 832 not options["noninteractive"])
829 833
830 834 if options["version"]:
831 835 show_version(u)
832 836 sys.exit(0)
833 837
834 838 try:
835 839 i = find(cmd)
836 840 except UnknownCommand:
837 841 u.warn("hg: unknown command '%s'\n" % cmd)
838 842 help(u)
839 843 sys.exit(1)
840 844
841 845 signal.signal(signal.SIGTERM, catchterm)
842 846
843 847 cmdoptions = {}
844 848 try:
845 849 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
846 850 except fancyopts.getopt.GetoptError, inst:
847 851 u.warn("hg %s: %s\n" % (cmd, inst))
848 852 help(u, cmd)
849 853 sys.exit(-1)
850 854
851 855 if cmd not in norepo.split():
852 856 repo = hg.repository(ui = u)
853 857 d = lambda: i[0](u, repo, *args, **cmdoptions)
854 858 else:
855 859 d = lambda: i[0](u, *args, **cmdoptions)
856 860
857 861 try:
858 862 if options['profile']:
859 863 import hotshot, hotshot.stats
860 864 prof = hotshot.Profile("hg.prof")
861 865 r = prof.runcall(d)
862 866 prof.close()
863 867 stats = hotshot.stats.load("hg.prof")
864 868 stats.strip_dirs()
865 869 stats.sort_stats('time', 'calls')
866 870 stats.print_stats(40)
867 871 return r
868 872 else:
869 873 return d()
870 874 except SignalInterrupt:
871 875 u.warn("killed!\n")
872 876 except KeyboardInterrupt:
873 877 u.warn("interrupted!\n")
874 878 except IOError, inst:
875 879 if hasattr(inst, "code"):
876 880 u.warn("abort: %s\n" % inst)
877 881 elif hasattr(inst, "reason"):
878 882 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
879 883 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
880 884 u.warn("broken pipe\n")
881 885 else:
882 886 raise
883 887 except TypeError, inst:
884 888 # was this an argument error?
885 889 tb = traceback.extract_tb(sys.exc_info()[2])
886 890 if len(tb) > 2: # no
887 891 raise
888 892 u.debug(inst, "\n")
889 893 u.warn("%s: invalid arguments\n" % i[0].__name__)
890 894 help(u, cmd)
891 895 sys.exit(-1)
892 896
@@ -1,113 +1,111 b''
1 1 + hg -d init
2 2 + echo this is a1
3 3 + hg -d add a
4 4 + hg -d commit -t0 -d '0 0' -u user
5 5 a
6 6 + echo this is b1
7 7 + hg -d add b
8 8 + hg -d commit -t1 -d '0 0' -u user
9 9 b
10 10 + hg -d manifest 1
11 11 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
12 12 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
13 13 + echo this is c1
14 14 + hg -d rawcommit -p 1 -d '0 0' -u user -t2 c
15 15 + hg -d manifest 2
16 16 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
17 17 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
18 18 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
19 19 + hg -d parents
20 20 changeset: 2:c4ef0ef0554dff3ceade68d75539e4f208a2be0a
21 21 tag: tip
22 22 parent: 1:3cefbe5cc27b3a068b7a6899ddff22a9874a7e69
23 23 parent: -1:0000000000000000000000000000000000000000
24 24 manifest: 2:f5d7a10be55c91e08fbd4f527ab313aff2761fc6
25 25 user: user
26 26 date: Thu Jan 1 00:00:00 1970
27 files: c
27 files+: c
28 28 description:
29 29 2
30 30
31 31 + rm b
32 32 + hg -d rawcommit -p 2 -d '0 0' -u user -t3 b
33 33 + hg -d manifest 3
34 34 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
35 35 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
36 36 + hg -d parents
37 37 changeset: 3:923669243607c26c4c8f0c11f48c1182ce1a7aff
38 38 tag: tip
39 39 parent: 2:c4ef0ef0554dff3ceade68d75539e4f208a2be0a
40 40 parent: -1:0000000000000000000000000000000000000000
41 41 manifest: 3:1102cb6dde652ec2ba8cc2777e464853afa67cef
42 42 user: user
43 43 date: Thu Jan 1 00:00:00 1970
44 files: b
44 files-: b
45 45 description:
46 46 3
47 47
48 48 + echo this is a22
49 49 + hg -d rawcommit -p 3 -d '0 0' -u user -t4 a
50 50 + hg -d manifest 4
51 51 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
52 52 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
53 53 + hg -d parents
54 54 changeset: 4:2361ec7b1da5142bce1285c50f3bb2960706263d
55 55 tag: tip
56 56 parent: 3:923669243607c26c4c8f0c11f48c1182ce1a7aff
57 57 parent: -1:0000000000000000000000000000000000000000
58 58 manifest: 4:cf4021621d6357e928385ffeee996f87c0bf991d
59 59 user: user
60 60 date: Thu Jan 1 00:00:00 1970
61 61 files: a
62 62 description:
63 63 4
64 64
65 65 + echo this is c22
66 66 + hg -d rawcommit -p 1 -d '0 0' -u user -t5 c
67 67 + hg -d manifest 5
68 68 05f9e54f4c9b86b09099803d8b49a50edcb4eaab 644 a
69 69 54837d97f2932a8194e69745a280a2c11e61ff9c 644 b
70 70 3570202ceac2b52517df64ebd0a062cb0d8fe33a 644 c
71 71 + hg -d parents
72 72 changeset: 4:2361ec7b1da5142bce1285c50f3bb2960706263d
73 73 parent: 3:923669243607c26c4c8f0c11f48c1182ce1a7aff
74 74 parent: -1:0000000000000000000000000000000000000000
75 75 manifest: 4:cf4021621d6357e928385ffeee996f87c0bf991d
76 76 user: user
77 77 date: Thu Jan 1 00:00:00 1970
78 78 files: a
79 79 description:
80 80 4
81 81
82 82 + hg -d rawcommit -p 4 -p 5 -d '0 0' -u user -t6
83 83 + hg -d manifest 6
84 84 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
85 85 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
86 86 + hg -d parents
87 87 changeset: 6:aaf55aee7b6249fd7d4ba295d98c4492ec2740d7
88 88 tag: tip
89 89 parent: 4:2361ec7b1da5142bce1285c50f3bb2960706263d
90 90 parent: 5:f8292b00383d88b470efcb2ea9c71409832ec9d6
91 91 manifest: 6:71c4262e09a89666ee12a92fefa12085aad53243
92 92 user: user
93 93 date: Thu Jan 1 00:00:00 1970
94 files:
95 94 description:
96 95 6
97 96
98 97 + hg -d rawcommit -p 6 -d '0 0' -u user -t7
99 98 + hg -d manifest 7
100 99 d6e3c4976c13feb1728cd3ac851abaf7256a5c23 644 a
101 100 76d5e637cbec1bcc04a5a3fa4bcc7d13f6847c00 644 c
102 101 + hg -d parents
103 102 changeset: 7:836ff890ac9ecb8c4b7c209b3e8b93f8805ca5f0
104 103 tag: tip
105 104 parent: 6:aaf55aee7b6249fd7d4ba295d98c4492ec2740d7
106 105 parent: -1:0000000000000000000000000000000000000000
107 106 manifest: 7:c15305fbac9dd3f49bffcc17d659b2d06d10b9a2
108 107 user: user
109 108 date: Thu Jan 1 00:00:00 1970
110 files:
111 109 description:
112 110 7
113 111
@@ -1,72 +1,73 b''
1 1 + mkdir r1
2 2 + cd r1
3 3 + hg init
4 4 + echo a
5 5 + hg addremove
6 6 + hg commit -t 1 -u test -d '0 0'
7 7 + hg clone . ../r2
8 8 + cd ../r2
9 9 + hg up
10 10 + echo abc
11 11 + hg diff
12 12 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
13 13 diff -r c19d34741b0a a
14 14 --- a/a
15 15 +++ b/a
16 16 @@ -1,1 +1,1 @@
17 17 -a
18 18 +abc
19 19 + cd ../r1
20 20 + echo b
21 21 + echo a2
22 22 + hg addremove
23 23 + hg commit -t 2 -u test -d '0 0'
24 24 + cd ../r2
25 25 + hg -q pull ../r1
26 26 + hg status
27 27 C a
28 28 + hg -d up
29 29 resolving manifests
30 30 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
31 31 a versions differ, resolve
32 32 remote created b
33 33 getting b
34 34 merging a
35 35 resolving a
36 36 file a: other d730145abbf9 ancestor b789fdd96dc2
37 37 + hg -d up -m
38 38 resolving manifests
39 39 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
40 40 + hg parents
41 41 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
42 42 tag: tip
43 43 user: test
44 44 date: Thu Jan 1 00:00:00 1970
45 45 summary: 2
46 46
47 47 + hg -v history
48 48 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
49 49 tag: tip
50 50 manifest: 1:1165e8bd193e17ad7d321d846fcf27ff3f412758
51 51 user: test
52 52 date: Thu Jan 1 00:00:00 1970
53 files: a b
53 files: a
54 files+: b
54 55 description:
55 56 2
56 57
57 58 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
58 59 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
59 60 user: test
60 61 date: Thu Jan 1 00:00:00 1970
61 files: a
62 files+: a
62 63 description:
63 64 1
64 65
65 66 + hg diff
66 67 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
67 68 diff -r 1e71731e6fbb a
68 69 --- a/a
69 70 +++ b/a
70 71 @@ -1,1 +1,1 @@
71 72 -a2
72 73 +abc
General Comments 0
You need to be logged in to leave comments. Login now