comparison mercurial/commands.py @ 3568:23f7d9621783

Merge with upstream
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 27 Oct 2006 23:09:46 +0200
parents eda9e7c9300d 730ca93ed788
children 736a78469a85
comparison
equal deleted inserted replaced
3553:3bab1fc0ab75 3568:23f7d9621783
48 except IOError, inst: 48 except IOError, inst:
49 raise util.Abort(_("can't read commit message '%s': %s") % 49 raise util.Abort(_("can't read commit message '%s': %s") %
50 (logfile, inst.strerror)) 50 (logfile, inst.strerror))
51 return message 51 return message
52 52
53 def walkchangerevs(ui, repo, pats, opts): 53 def walkchangerevs(ui, repo, pats, change, opts):
54 '''Iterate over files and the revs they changed in. 54 '''Iterate over files and the revs they changed in.
55 55
56 Callers most commonly need to iterate backwards over the history 56 Callers most commonly need to iterate backwards over the history
57 it is interested in. Doing so has awful (quadratic-looking) 57 it is interested in. Doing so has awful (quadratic-looking)
58 performance, so we use iterators in a "windowed" way. 58 performance, so we use iterators in a "windowed" way.
59 59
60 We walk a window of revisions in the desired order. Within the 60 We walk a window of revisions in the desired order. Within the
61 window, we first walk forwards to gather data, then in the desired 61 window, we first walk forwards to gather data, then in the desired
62 order (usually backwards) to display it. 62 order (usually backwards) to display it.
63 63
64 This function returns an (iterator, getchange, matchfn) tuple. The 64 This function returns an (iterator, matchfn) tuple. The iterator
65 getchange function returns the changelog entry for a numeric 65 yields 3-tuples. They will be of one of the following forms:
66 revision. The iterator yields 3-tuples. They will be of one of
67 the following forms:
68 66
69 "window", incrementing, lastrev: stepping through a window, 67 "window", incrementing, lastrev: stepping through a window,
70 positive if walking forwards through revs, last rev in the 68 positive if walking forwards through revs, last rev in the
71 sequence iterated over - use to reset state for the current window 69 sequence iterated over - use to reset state for the current window
72 70
89 yield start, min(windowsize, start-end-1) 87 yield start, min(windowsize, start-end-1)
90 start -= windowsize 88 start -= windowsize
91 if windowsize < sizelimit: 89 if windowsize < sizelimit:
92 windowsize *= 2 90 windowsize *= 2
93 91
94
95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) 92 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 follow = opts.get('follow') or opts.get('follow_first') 93 follow = opts.get('follow') or opts.get('follow_first')
97 94
98 if repo.changelog.count() == 0: 95 if repo.changelog.count() == 0:
99 return [], False, matchfn 96 return [], matchfn
100 97
101 if follow: 98 if follow:
102 defrange = '%s:0' % repo.changectx().rev() 99 defrange = '%s:0' % repo.changectx().rev()
103 else: 100 else:
104 defrange = 'tip:0' 101 defrange = 'tip:0'
105 revs = map(int, cmdutil.revrange(ui, repo, opts['rev'] or [defrange])) 102 revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange])
106 wanted = {} 103 wanted = {}
107 slowpath = anypats 104 slowpath = anypats
108 fncache = {} 105 fncache = {}
109 106
110 chcache = {}
111 def getchange(rev):
112 ch = chcache.get(rev)
113 if ch is None:
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 return ch
116
117 if not slowpath and not files: 107 if not slowpath and not files:
118 # No files, no patterns. Display all revs. 108 # No files, no patterns. Display all revs.
119 wanted = dict(zip(revs, revs)) 109 wanted = dict.fromkeys(revs)
120 copies = [] 110 copies = []
121 if not slowpath: 111 if not slowpath:
122 # Only files, no patterns. Check the history of each file. 112 # Only files, no patterns. Check the history of each file.
123 def filerevgen(filelog, node): 113 def filerevgen(filelog, node):
124 cl_count = repo.changelog.count() 114 cl_count = repo.changelog.count()
167 157
168 # The slow path checks files modified in every changeset. 158 # The slow path checks files modified in every changeset.
169 def changerevgen(): 159 def changerevgen():
170 for i, window in increasing_windows(repo.changelog.count()-1, -1): 160 for i, window in increasing_windows(repo.changelog.count()-1, -1):
171 for j in xrange(i - window, i + 1): 161 for j in xrange(i - window, i + 1):
172 yield j, getchange(j)[3] 162 yield j, change(j)[3]
173 163
174 for rev, changefiles in changerevgen(): 164 for rev, changefiles in changerevgen():
175 matches = filter(matchfn, changefiles) 165 matches = filter(matchfn, changefiles)
176 if matches: 166 if matches:
177 fncache[rev] = matches 167 fncache[rev] = matches
218 for rev in opts.get('prune', ()): 208 for rev in opts.get('prune', ()):
219 rev = repo.changelog.rev(repo.lookup(rev)) 209 rev = repo.changelog.rev(repo.lookup(rev))
220 ff = followfilter() 210 ff = followfilter()
221 stop = min(revs[0], revs[-1]) 211 stop = min(revs[0], revs[-1])
222 for x in xrange(rev, stop-1, -1): 212 for x in xrange(rev, stop-1, -1):
223 if ff.match(x) and wanted.has_key(x): 213 if ff.match(x) and x in wanted:
224 del wanted[x] 214 del wanted[x]
225 215
226 def iterate(): 216 def iterate():
227 if follow and not files: 217 if follow and not files:
228 ff = followfilter(onlyfirst=opts.get('follow_first')) 218 ff = followfilter(onlyfirst=opts.get('follow_first'))
238 yield 'window', revs[0] < revs[-1], revs[-1] 228 yield 'window', revs[0] < revs[-1], revs[-1]
239 nrevs = [rev for rev in revs[i:i+window] if want(rev)] 229 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
240 srevs = list(nrevs) 230 srevs = list(nrevs)
241 srevs.sort() 231 srevs.sort()
242 for rev in srevs: 232 for rev in srevs:
243 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3]) 233 fns = fncache.get(rev) or filter(matchfn, change(rev)[3])
244 yield 'add', rev, fns 234 yield 'add', rev, fns
245 for rev in nrevs: 235 for rev in nrevs:
246 yield 'iter', rev, None 236 yield 'iter', rev, None
247 return iterate(), getchange, matchfn 237 return iterate(), matchfn
248 238
249 def write_bundle(cg, filename=None, compress=True): 239 def write_bundle(cg, filename=None, compress=True):
250 """Write a bundle file and return its filename. 240 """Write a bundle file and return its filename.
251 241
252 Existing files will not be overwritten. 242 Existing files will not be overwritten.
295 finally: 285 finally:
296 if fh is not None: 286 if fh is not None:
297 fh.close() 287 fh.close()
298 if cleanup is not None: 288 if cleanup is not None:
299 os.unlink(cleanup) 289 os.unlink(cleanup)
300
301 def trimuser(ui, name, rev, revcache):
302 """trim the name of the user who committed a change"""
303 user = revcache.get(rev)
304 if user is None:
305 user = revcache[rev] = ui.shortuser(name)
306 return user
307 290
308 class changeset_printer(object): 291 class changeset_printer(object):
309 '''show changeset information when templating not requested.''' 292 '''show changeset information when templating not requested.'''
310 293
311 def __init__(self, ui, repo): 294 def __init__(self, ui, repo):
1381 With the --switch-parent option, the diff will be against the second 1364 With the --switch-parent option, the diff will be against the second
1382 parent. It can be useful to review a merge. 1365 parent. It can be useful to review a merge.
1383 """ 1366 """
1384 if not changesets: 1367 if not changesets:
1385 raise util.Abort(_("export requires at least one changeset")) 1368 raise util.Abort(_("export requires at least one changeset"))
1386 revs = list(cmdutil.revrange(ui, repo, changesets)) 1369 revs = cmdutil.revrange(ui, repo, changesets)
1387 if len(revs) > 1: 1370 if len(revs) > 1:
1388 ui.note(_('exporting patches:\n')) 1371 ui.note(_('exporting patches:\n'))
1389 else: 1372 else:
1390 ui.note(_('exporting patch:\n')) 1373 ui.note(_('exporting patch:\n'))
1391 patch.export(repo, map(repo.lookup, revs), template=opts['output'], 1374 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1469 yield ('-', a[i]) 1452 yield ('-', a[i])
1470 for i in xrange(blo, bhi): 1453 for i in xrange(blo, bhi):
1471 yield ('+', b[i]) 1454 yield ('+', b[i])
1472 1455
1473 prev = {} 1456 prev = {}
1474 ucache = {}
1475 def display(fn, rev, states, prevstates): 1457 def display(fn, rev, states, prevstates):
1476 counts = {'-': 0, '+': 0} 1458 counts = {'-': 0, '+': 0}
1477 filerevmatches = {} 1459 filerevmatches = {}
1478 if incrementing or not opts['all']: 1460 if incrementing or not opts['all']:
1479 a, b = prevstates, states 1461 a, b, r = prevstates, states, rev
1480 else: 1462 else:
1481 a, b = states, prevstates 1463 a, b, r = states, prevstates, prev.get(fn, -1)
1482 for change, l in difflinestates(a, b): 1464 for change, l in difflinestates(a, b):
1483 if incrementing or not opts['all']:
1484 r = rev
1485 else:
1486 r = prev[fn]
1487 cols = [fn, str(r)] 1465 cols = [fn, str(r)]
1488 if opts['line_number']: 1466 if opts['line_number']:
1489 cols.append(str(l.linenum)) 1467 cols.append(str(l.linenum))
1490 if opts['all']: 1468 if opts['all']:
1491 cols.append(change) 1469 cols.append(change)
1492 if opts['user']: 1470 if opts['user']:
1493 cols.append(trimuser(ui, getchange(r)[1], rev, 1471 cols.append(ui.shortuser(getchange(r)[1]))
1494 ucache))
1495 if opts['files_with_matches']: 1472 if opts['files_with_matches']:
1496 c = (fn, rev) 1473 c = (fn, r)
1497 if c in filerevmatches: 1474 if c in filerevmatches:
1498 continue 1475 continue
1499 filerevmatches[c] = 1 1476 filerevmatches[c] = 1
1500 else: 1477 else:
1501 cols.append(l.line) 1478 cols.append(l.line)
1503 counts[change] += 1 1480 counts[change] += 1
1504 return counts['+'], counts['-'] 1481 return counts['+'], counts['-']
1505 1482
1506 fstate = {} 1483 fstate = {}
1507 skip = {} 1484 skip = {}
1508 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts) 1485 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1486 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1509 count = 0 1487 count = 0
1510 incrementing = False 1488 incrementing = False
1511 follow = opts.get('follow') 1489 follow = opts.get('follow')
1512 for st, rev, fns in changeiter: 1490 for st, rev, fns in changeiter:
1513 if st == 'window': 1491 if st == 'window':
1514 incrementing = rev 1492 incrementing = rev
1515 matches.clear() 1493 matches.clear()
1516 elif st == 'add': 1494 elif st == 'add':
1517 change = repo.changelog.read(repo.lookup(str(rev))) 1495 mf = repo.changectx(rev).manifest()
1518 mf = repo.manifest.read(change[0])
1519 matches[rev] = {} 1496 matches[rev] = {}
1520 for fn in fns: 1497 for fn in fns:
1521 if fn in skip: 1498 if fn in skip:
1522 continue 1499 continue
1523 fstate.setdefault(fn, {}) 1500 fstate.setdefault(fn, {})
1836 if self.debugflag: 1813 if self.debugflag:
1837 self.write(*args) 1814 self.write(*args)
1838 def __getattr__(self, key): 1815 def __getattr__(self, key):
1839 return getattr(self.ui, key) 1816 return getattr(self.ui, key)
1840 1817
1841 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts) 1818 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1819 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1842 1820
1843 if opts['branches']: 1821 if opts['branches']:
1844 ui.warn(_("the --branches option is deprecated, " 1822 ui.warn(_("the --branches option is deprecated, "
1845 "please use 'hg branches' instead\n")) 1823 "please use 'hg branches' instead\n"))
1846 1824
1853 else: 1831 else:
1854 limit = sys.maxint 1832 limit = sys.maxint
1855 count = 0 1833 count = 0
1856 1834
1857 if opts['copies'] and opts['rev']: 1835 if opts['copies'] and opts['rev']:
1858 endrev = max([int(i) 1836 endrev = max(cmdutil.revrange(ui, repo, opts['rev'])) + 1
1859 for i in cmdutil.revrange(ui, repo, opts['rev'])]) + 1
1860 else: 1837 else:
1861 endrev = repo.changelog.count() 1838 endrev = repo.changelog.count()
1862 rcache = {} 1839 rcache = {}
1863 ncache = {} 1840 ncache = {}
1864 dcache = [] 1841 dcache = []