mercurial/commands.py
changeset 3568 23f7d9621783
parent 3550 eda9e7c9300d
parent 3562 730ca93ed788
child 3571 736a78469a85
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 = []