Mercurial > public > mercurial-scm > hg
comparison mercurial/cmdutil.py @ 19290:f21f4a1b6c24
log: move log file walk to its own function
This moves the logic that determines which changesets to process during a
'hg log foo.txt' command. Putting it in its own function allows extensions
to modify how the file log is traversed. For instance, the current
implementation uses filelog revs heavily. Other implementations may not have
filelog revs available.
The function throws an exception if the traversal is not possible via the
filelog, so the parent function can do things the slow way if necessary
(by walking the entire commit history).
Aside from the exception throwing, no logic is changed.
author | Durham Goode <durham@fb.com> |
---|---|
date | Thu, 30 May 2013 19:25:55 -0700 |
parents | 3bfd7f1e7485 |
children | 446ab88d3f1c |
comparison
equal
deleted
inserted
replaced
19289:6ea1f858efd9 | 19290:f21f4a1b6c24 |
---|---|
1003 yield start, min(windowsize, start - end - 1) | 1003 yield start, min(windowsize, start - end - 1) |
1004 start -= windowsize | 1004 start -= windowsize |
1005 if windowsize < sizelimit: | 1005 if windowsize < sizelimit: |
1006 windowsize *= 2 | 1006 windowsize *= 2 |
1007 | 1007 |
1008 class FileWalkError(Exception): | |
1009 pass | |
1010 | |
1011 def walkfilerevs(repo, match, follow, revs, fncache): | |
1012 '''Walks the file history for the matched files. | |
1013 | |
1014 Returns the changeset revs that are involved in the file history. | |
1015 | |
1016 Throws FileWalkError if the file history can't be walked using | |
1017 filelogs alone. | |
1018 ''' | |
1019 wanted = set() | |
1020 copies = [] | |
1021 minrev, maxrev = min(revs), max(revs) | |
1022 def filerevgen(filelog, last): | |
1023 """ | |
1024 Only files, no patterns. Check the history of each file. | |
1025 | |
1026 Examines filelog entries within minrev, maxrev linkrev range | |
1027 Returns an iterator yielding (linkrev, parentlinkrevs, copied) | |
1028 tuples in backwards order | |
1029 """ | |
1030 cl_count = len(repo) | |
1031 revs = [] | |
1032 for j in xrange(0, last + 1): | |
1033 linkrev = filelog.linkrev(j) | |
1034 if linkrev < minrev: | |
1035 continue | |
1036 # only yield rev for which we have the changelog, it can | |
1037 # happen while doing "hg log" during a pull or commit | |
1038 if linkrev >= cl_count: | |
1039 break | |
1040 | |
1041 parentlinkrevs = [] | |
1042 for p in filelog.parentrevs(j): | |
1043 if p != nullrev: | |
1044 parentlinkrevs.append(filelog.linkrev(p)) | |
1045 n = filelog.node(j) | |
1046 revs.append((linkrev, parentlinkrevs, | |
1047 follow and filelog.renamed(n))) | |
1048 | |
1049 return reversed(revs) | |
1050 def iterfiles(): | |
1051 pctx = repo['.'] | |
1052 for filename in match.files(): | |
1053 if follow: | |
1054 if filename not in pctx: | |
1055 raise util.Abort(_('cannot follow file not in parent ' | |
1056 'revision: "%s"') % filename) | |
1057 yield filename, pctx[filename].filenode() | |
1058 else: | |
1059 yield filename, None | |
1060 for filename_node in copies: | |
1061 yield filename_node | |
1062 | |
1063 for file_, node in iterfiles(): | |
1064 filelog = repo.file(file_) | |
1065 if not len(filelog): | |
1066 if node is None: | |
1067 # A zero count may be a directory or deleted file, so | |
1068 # try to find matching entries on the slow path. | |
1069 if follow: | |
1070 raise util.Abort( | |
1071 _('cannot follow nonexistent file: "%s"') % file_) | |
1072 raise FileWalkError("Cannot walk via filelog") | |
1073 else: | |
1074 continue | |
1075 | |
1076 if node is None: | |
1077 last = len(filelog) - 1 | |
1078 else: | |
1079 last = filelog.rev(node) | |
1080 | |
1081 | |
1082 # keep track of all ancestors of the file | |
1083 ancestors = set([filelog.linkrev(last)]) | |
1084 | |
1085 # iterate from latest to oldest revision | |
1086 for rev, flparentlinkrevs, copied in filerevgen(filelog, last): | |
1087 if not follow: | |
1088 if rev > maxrev: | |
1089 continue | |
1090 else: | |
1091 # Note that last might not be the first interesting | |
1092 # rev to us: | |
1093 # if the file has been changed after maxrev, we'll | |
1094 # have linkrev(last) > maxrev, and we still need | |
1095 # to explore the file graph | |
1096 if rev not in ancestors: | |
1097 continue | |
1098 # XXX insert 1327 fix here | |
1099 if flparentlinkrevs: | |
1100 ancestors.update(flparentlinkrevs) | |
1101 | |
1102 fncache.setdefault(rev, []).append(file_) | |
1103 wanted.add(rev) | |
1104 if copied: | |
1105 copies.append(copied) | |
1106 | |
1107 return wanted | |
1108 | |
1008 def walkchangerevs(repo, match, opts, prepare): | 1109 def walkchangerevs(repo, match, opts, prepare): |
1009 '''Iterate over files and the revs in which they changed. | 1110 '''Iterate over files and the revs in which they changed. |
1010 | 1111 |
1011 Callers most commonly need to iterate backwards over the history | 1112 Callers most commonly need to iterate backwards over the history |
1012 in which they are interested. Doing so has awful (quadratic-looking) | 1113 in which they are interested. Doing so has awful (quadratic-looking) |
1042 # match the file filtering conditions. | 1143 # match the file filtering conditions. |
1043 | 1144 |
1044 if not slowpath and not match.files(): | 1145 if not slowpath and not match.files(): |
1045 # No files, no patterns. Display all revs. | 1146 # No files, no patterns. Display all revs. |
1046 wanted = set(revs) | 1147 wanted = set(revs) |
1047 copies = [] | |
1048 | 1148 |
1049 if not slowpath and match.files(): | 1149 if not slowpath and match.files(): |
1050 # We only have to read through the filelog to find wanted revisions | 1150 # We only have to read through the filelog to find wanted revisions |
1051 | 1151 |
1052 minrev, maxrev = min(revs), max(revs) | 1152 try: |
1053 def filerevgen(filelog, last): | 1153 wanted = walkfilerevs(repo, match, follow, revs, fncache) |
1054 """ | 1154 except FileWalkError: |
1055 Only files, no patterns. Check the history of each file. | 1155 slowpath = True |
1056 | 1156 |
1057 Examines filelog entries within minrev, maxrev linkrev range | 1157 # We decided to fall back to the slowpath because at least one |
1058 Returns an iterator yielding (linkrev, parentlinkrevs, copied) | 1158 # of the paths was not a file. Check to see if at least one of them |
1059 tuples in backwards order | 1159 # existed in history, otherwise simply return |
1060 """ | |
1061 cl_count = len(repo) | |
1062 revs = [] | |
1063 for j in xrange(0, last + 1): | |
1064 linkrev = filelog.linkrev(j) | |
1065 if linkrev < minrev: | |
1066 continue | |
1067 # only yield rev for which we have the changelog, it can | |
1068 # happen while doing "hg log" during a pull or commit | |
1069 if linkrev >= cl_count: | |
1070 break | |
1071 | |
1072 parentlinkrevs = [] | |
1073 for p in filelog.parentrevs(j): | |
1074 if p != nullrev: | |
1075 parentlinkrevs.append(filelog.linkrev(p)) | |
1076 n = filelog.node(j) | |
1077 revs.append((linkrev, parentlinkrevs, | |
1078 follow and filelog.renamed(n))) | |
1079 | |
1080 return reversed(revs) | |
1081 def iterfiles(): | |
1082 pctx = repo['.'] | |
1083 for filename in match.files(): | |
1084 if follow: | |
1085 if filename not in pctx: | |
1086 raise util.Abort(_('cannot follow file not in parent ' | |
1087 'revision: "%s"') % filename) | |
1088 yield filename, pctx[filename].filenode() | |
1089 else: | |
1090 yield filename, None | |
1091 for filename_node in copies: | |
1092 yield filename_node | |
1093 for file_, node in iterfiles(): | |
1094 filelog = repo.file(file_) | |
1095 if not len(filelog): | |
1096 if node is None: | |
1097 # A zero count may be a directory or deleted file, so | |
1098 # try to find matching entries on the slow path. | |
1099 if follow: | |
1100 raise util.Abort( | |
1101 _('cannot follow nonexistent file: "%s"') % file_) | |
1102 slowpath = True | |
1103 break | |
1104 else: | |
1105 continue | |
1106 | |
1107 if node is None: | |
1108 last = len(filelog) - 1 | |
1109 else: | |
1110 last = filelog.rev(node) | |
1111 | |
1112 | |
1113 # keep track of all ancestors of the file | |
1114 ancestors = set([filelog.linkrev(last)]) | |
1115 | |
1116 # iterate from latest to oldest revision | |
1117 for rev, flparentlinkrevs, copied in filerevgen(filelog, last): | |
1118 if not follow: | |
1119 if rev > maxrev: | |
1120 continue | |
1121 else: | |
1122 # Note that last might not be the first interesting | |
1123 # rev to us: | |
1124 # if the file has been changed after maxrev, we'll | |
1125 # have linkrev(last) > maxrev, and we still need | |
1126 # to explore the file graph | |
1127 if rev not in ancestors: | |
1128 continue | |
1129 # XXX insert 1327 fix here | |
1130 if flparentlinkrevs: | |
1131 ancestors.update(flparentlinkrevs) | |
1132 | |
1133 fncache.setdefault(rev, []).append(file_) | |
1134 wanted.add(rev) | |
1135 if copied: | |
1136 copies.append(copied) | |
1137 | |
1138 # We decided to fall back to the slowpath because at least one | |
1139 # of the paths was not a file. Check to see if at least one of them | |
1140 # existed in history, otherwise simply return | |
1141 if slowpath: | |
1142 for path in match.files(): | 1160 for path in match.files(): |
1143 if path == '.' or path in repo.store: | 1161 if path == '.' or path in repo.store: |
1144 break | 1162 break |
1145 else: | 1163 else: |
1146 return [] | 1164 return [] |