1022 |
1022 |
1023 def finddate(ui, repo, date): |
1023 def finddate(ui, repo, date): |
1024 """Find the tipmost changeset that matches the given date spec""" |
1024 """Find the tipmost changeset that matches the given date spec""" |
1025 df = util.matchdate(date) |
1025 df = util.matchdate(date) |
1026 get = util.cachefunc(lambda r: repo[r]) |
1026 get = util.cachefunc(lambda r: repo[r]) |
1027 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None}) |
1027 m = matchall(repo) |
1028 results = {} |
1028 results = {} |
1029 for st, rev, fns in changeiter: |
1029 for st, rev, fns in walkchangerevs(ui, repo, m, get, {'rev':None}): |
1030 if st == 'add': |
1030 if st == 'add': |
1031 d = get(rev).date() |
1031 d = get(rev).date() |
1032 if df(d[0]): |
1032 if df(d[0]): |
1033 results[rev] = d |
1033 results[rev] = d |
1034 elif st == 'iter': |
1034 elif st == 'iter': |
1037 (rev, util.datestr(results[rev]))) |
1037 (rev, util.datestr(results[rev]))) |
1038 return str(rev) |
1038 return str(rev) |
1039 |
1039 |
1040 raise util.Abort(_("revision matching date not found")) |
1040 raise util.Abort(_("revision matching date not found")) |
1041 |
1041 |
1042 def walkchangerevs(ui, repo, pats, change, opts): |
1042 def walkchangerevs(ui, repo, match, change, opts): |
1043 '''Iterate over files and the revs in which they changed. |
1043 '''Iterate over files and the revs in which they changed. |
1044 |
1044 |
1045 Callers most commonly need to iterate backwards over the history |
1045 Callers most commonly need to iterate backwards over the history |
1046 in which they are interested. Doing so has awful (quadratic-looking) |
1046 in which they are interested. Doing so has awful (quadratic-looking) |
1047 performance, so we use iterators in a "windowed" way. |
1047 performance, so we use iterators in a "windowed" way. |
1048 |
1048 |
1049 We walk a window of revisions in the desired order. Within the |
1049 We walk a window of revisions in the desired order. Within the |
1050 window, we first walk forwards to gather data, then in the desired |
1050 window, we first walk forwards to gather data, then in the desired |
1051 order (usually backwards) to display it. |
1051 order (usually backwards) to display it. |
1052 |
1052 |
1053 This function returns an (iterator, matchfn) tuple. The iterator |
1053 This function returns an iterator. The iterator yields 3-tuples. |
1054 yields 3-tuples. They will be of one of the following forms: |
1054 They will be of one of the following forms: |
1055 |
1055 |
1056 "window", incrementing, lastrev: stepping through a window, |
1056 "window", incrementing, lastrev: stepping through a window, |
1057 positive if walking forwards through revs, last rev in the |
1057 positive if walking forwards through revs, last rev in the |
1058 sequence iterated over - use to reset state for the current window |
1058 sequence iterated over - use to reset state for the current window |
1059 |
1059 |
1076 yield start, min(windowsize, start-end-1) |
1076 yield start, min(windowsize, start-end-1) |
1077 start -= windowsize |
1077 start -= windowsize |
1078 if windowsize < sizelimit: |
1078 if windowsize < sizelimit: |
1079 windowsize *= 2 |
1079 windowsize *= 2 |
1080 |
1080 |
1081 m = match(repo, pats, opts) |
|
1082 follow = opts.get('follow') or opts.get('follow_first') |
1081 follow = opts.get('follow') or opts.get('follow_first') |
1083 |
1082 |
1084 if not len(repo): |
1083 if not len(repo): |
1085 return [], m |
1084 return [] |
1086 |
1085 |
1087 if follow: |
1086 if follow: |
1088 defrange = '%s:0' % repo['.'].rev() |
1087 defrange = '%s:0' % repo['.'].rev() |
1089 else: |
1088 else: |
1090 defrange = '-1:0' |
1089 defrange = '-1:0' |
1091 revs = revrange(repo, opts['rev'] or [defrange]) |
1090 revs = revrange(repo, opts['rev'] or [defrange]) |
1092 wanted = set() |
1091 wanted = set() |
1093 slowpath = m.anypats() or (m.files() and opts.get('removed')) |
1092 slowpath = match.anypats() or (match.files() and opts.get('removed')) |
1094 fncache = {} |
1093 fncache = {} |
1095 |
1094 |
1096 if not slowpath and not m.files(): |
1095 if not slowpath and not match.files(): |
1097 # No files, no patterns. Display all revs. |
1096 # No files, no patterns. Display all revs. |
1098 wanted = set(revs) |
1097 wanted = set(revs) |
1099 copies = [] |
1098 copies = [] |
1100 if not slowpath: |
1099 if not slowpath: |
1101 # Only files, no patterns. Check the history of each file. |
1100 # Only files, no patterns. Check the history of each file. |
1115 # only yield rev for which we have the changelog, it can |
1114 # only yield rev for which we have the changelog, it can |
1116 # happen while doing "hg log" during a pull or commit |
1115 # happen while doing "hg log" during a pull or commit |
1117 if rev[0] < cl_count: |
1116 if rev[0] < cl_count: |
1118 yield rev |
1117 yield rev |
1119 def iterfiles(): |
1118 def iterfiles(): |
1120 for filename in m.files(): |
1119 for filename in match.files(): |
1121 yield filename, None |
1120 yield filename, None |
1122 for filename_node in copies: |
1121 for filename_node in copies: |
1123 yield filename_node |
1122 yield filename_node |
1124 minrev, maxrev = min(revs), max(revs) |
1123 minrev, maxrev = min(revs), max(revs) |
1125 for file_, node in iterfiles(): |
1124 for file_, node in iterfiles(): |
1155 for i, window in increasing_windows(len(repo) - 1, nullrev): |
1154 for i, window in increasing_windows(len(repo) - 1, nullrev): |
1156 for j in xrange(i - window, i + 1): |
1155 for j in xrange(i - window, i + 1): |
1157 yield change(j) |
1156 yield change(j) |
1158 |
1157 |
1159 for ctx in changerevgen(): |
1158 for ctx in changerevgen(): |
1160 matches = filter(m, ctx.files()) |
1159 matches = filter(match, ctx.files()) |
1161 if matches: |
1160 if matches: |
1162 fncache[ctx.rev()] = matches |
1161 fncache[ctx.rev()] = matches |
1163 wanted.add(ctx.rev()) |
1162 wanted.add(ctx.rev()) |
1164 |
1163 |
1165 class followfilter(object): |
1164 class followfilter(object): |
1208 for x in xrange(rev, stop-1, -1): |
1207 for x in xrange(rev, stop-1, -1): |
1209 if ff.match(x): |
1208 if ff.match(x): |
1210 wanted.discard(x) |
1209 wanted.discard(x) |
1211 |
1210 |
1212 def iterate(): |
1211 def iterate(): |
1213 if follow and not m.files(): |
1212 if follow and not match.files(): |
1214 ff = followfilter(onlyfirst=opts.get('follow_first')) |
1213 ff = followfilter(onlyfirst=opts.get('follow_first')) |
1215 def want(rev): |
1214 def want(rev): |
1216 return ff.match(rev) and rev in wanted |
1215 return ff.match(rev) and rev in wanted |
1217 else: |
1216 else: |
1218 def want(rev): |
1217 def want(rev): |
1224 for rev in sorted(nrevs): |
1223 for rev in sorted(nrevs): |
1225 fns = fncache.get(rev) |
1224 fns = fncache.get(rev) |
1226 if not fns: |
1225 if not fns: |
1227 def fns_generator(): |
1226 def fns_generator(): |
1228 for f in change(rev).files(): |
1227 for f in change(rev).files(): |
1229 if m(f): |
1228 if match(f): |
1230 yield f |
1229 yield f |
1231 fns = fns_generator() |
1230 fns = fns_generator() |
1232 yield 'add', rev, fns |
1231 yield 'add', rev, fns |
1233 for rev in nrevs: |
1232 for rev in nrevs: |
1234 yield 'iter', rev, None |
1233 yield 'iter', rev, None |
1235 return iterate(), m |
1234 return iterate() |
1236 |
1235 |
1237 def commit(ui, repo, commitfunc, pats, opts): |
1236 def commit(ui, repo, commitfunc, pats, opts): |
1238 '''commit the specified files or all outstanding changes''' |
1237 '''commit the specified files or all outstanding changes''' |
1239 date = opts.get('date') |
1238 date = opts.get('date') |
1240 if date: |
1239 if date: |