Show More
@@ -1,285 +1,285 b'' | |||||
1 | # Copyright 2016-present Facebook. All Rights Reserved. |
|
1 | # Copyright 2016-present Facebook. All Rights Reserved. | |
2 | # |
|
2 | # | |
3 | # commands: fastannotate commands |
|
3 | # commands: fastannotate commands | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | from __future__ import absolute_import |
|
8 | from __future__ import absolute_import | |
9 |
|
9 | |||
10 | import os |
|
10 | import os | |
11 |
|
11 | |||
12 | from mercurial.i18n import _ |
|
12 | from mercurial.i18n import _ | |
13 | from mercurial import ( |
|
13 | from mercurial import ( | |
14 | commands, |
|
14 | commands, | |
15 | encoding, |
|
15 | encoding, | |
16 | error, |
|
16 | error, | |
17 | extensions, |
|
17 | extensions, | |
18 | patch, |
|
18 | patch, | |
19 | pycompat, |
|
19 | pycompat, | |
20 | registrar, |
|
20 | registrar, | |
21 | scmutil, |
|
21 | scmutil, | |
22 | util, |
|
22 | util, | |
23 | ) |
|
23 | ) | |
24 |
|
24 | |||
25 | from . import ( |
|
25 | from . import ( | |
26 | context as facontext, |
|
26 | context as facontext, | |
27 | error as faerror, |
|
27 | error as faerror, | |
28 | formatter as faformatter, |
|
28 | formatter as faformatter, | |
29 | ) |
|
29 | ) | |
30 |
|
30 | |||
31 | cmdtable = {} |
|
31 | cmdtable = {} | |
32 | command = registrar.command(cmdtable) |
|
32 | command = registrar.command(cmdtable) | |
33 |
|
33 | |||
34 | def _matchpaths(repo, rev, pats, opts, aopts=facontext.defaultopts): |
|
34 | def _matchpaths(repo, rev, pats, opts, aopts=facontext.defaultopts): | |
35 | """generate paths matching given patterns""" |
|
35 | """generate paths matching given patterns""" | |
36 | perfhack = repo.ui.configbool('fastannotate', 'perfhack') |
|
36 | perfhack = repo.ui.configbool('fastannotate', 'perfhack') | |
37 |
|
37 | |||
38 | # disable perfhack if: |
|
38 | # disable perfhack if: | |
39 | # a) any walkopt is used |
|
39 | # a) any walkopt is used | |
40 | # b) if we treat pats as plain file names, some of them do not have |
|
40 | # b) if we treat pats as plain file names, some of them do not have | |
41 | # corresponding linelog files |
|
41 | # corresponding linelog files | |
42 | if perfhack: |
|
42 | if perfhack: | |
43 | # cwd related to reporoot |
|
43 | # cwd related to reporoot | |
44 | reporoot = os.path.dirname(repo.path) |
|
44 | reporoot = os.path.dirname(repo.path) | |
45 | reldir = os.path.relpath(encoding.getcwd(), reporoot) |
|
45 | reldir = os.path.relpath(encoding.getcwd(), reporoot) | |
46 | if reldir == '.': |
|
46 | if reldir == '.': | |
47 | reldir = '' |
|
47 | reldir = '' | |
48 | if any(opts.get(o[1]) for o in commands.walkopts): # a) |
|
48 | if any(opts.get(o[1]) for o in commands.walkopts): # a) | |
49 | perfhack = False |
|
49 | perfhack = False | |
50 | else: # b) |
|
50 | else: # b) | |
51 | relpats = [os.path.relpath(p, reporoot) if os.path.isabs(p) else p |
|
51 | relpats = [os.path.relpath(p, reporoot) if os.path.isabs(p) else p | |
52 | for p in pats] |
|
52 | for p in pats] | |
53 | # disable perfhack on '..' since it allows escaping from the repo |
|
53 | # disable perfhack on '..' since it allows escaping from the repo | |
54 | if any(('..' in f or |
|
54 | if any(('..' in f or | |
55 | not os.path.isfile( |
|
55 | not os.path.isfile( | |
56 | facontext.pathhelper(repo, f, aopts).linelogpath)) |
|
56 | facontext.pathhelper(repo, f, aopts).linelogpath)) | |
57 | for f in relpats): |
|
57 | for f in relpats): | |
58 | perfhack = False |
|
58 | perfhack = False | |
59 |
|
59 | |||
60 | # perfhack: emit paths directory without checking with manifest |
|
60 | # perfhack: emit paths directory without checking with manifest | |
61 | # this can be incorrect if the rev dos not have file. |
|
61 | # this can be incorrect if the rev dos not have file. | |
62 | if perfhack: |
|
62 | if perfhack: | |
63 | for p in relpats: |
|
63 | for p in relpats: | |
64 | yield os.path.join(reldir, p) |
|
64 | yield os.path.join(reldir, p) | |
65 | else: |
|
65 | else: | |
66 | def bad(x, y): |
|
66 | def bad(x, y): | |
67 | raise error.Abort("%s: %s" % (x, y)) |
|
67 | raise error.Abort("%s: %s" % (x, y)) | |
68 | ctx = scmutil.revsingle(repo, rev) |
|
68 | ctx = scmutil.revsingle(repo, rev) | |
69 | m = scmutil.match(ctx, pats, opts, badfn=bad) |
|
69 | m = scmutil.match(ctx, pats, opts, badfn=bad) | |
70 | for p in ctx.walk(m): |
|
70 | for p in ctx.walk(m): | |
71 | yield p |
|
71 | yield p | |
72 |
|
72 | |||
73 | fastannotatecommandargs = { |
|
73 | fastannotatecommandargs = { | |
74 | r'options': [ |
|
74 | r'options': [ | |
75 | ('r', 'rev', '.', _('annotate the specified revision'), _('REV')), |
|
75 | ('r', 'rev', '.', _('annotate the specified revision'), _('REV')), | |
76 | ('u', 'user', None, _('list the author (long with -v)')), |
|
76 | ('u', 'user', None, _('list the author (long with -v)')), | |
77 | ('f', 'file', None, _('list the filename')), |
|
77 | ('f', 'file', None, _('list the filename')), | |
78 | ('d', 'date', None, _('list the date (short with -q)')), |
|
78 | ('d', 'date', None, _('list the date (short with -q)')), | |
79 | ('n', 'number', None, _('list the revision number (default)')), |
|
79 | ('n', 'number', None, _('list the revision number (default)')), | |
80 | ('c', 'changeset', None, _('list the changeset')), |
|
80 | ('c', 'changeset', None, _('list the changeset')), | |
81 | ('l', 'line-number', None, _('show line number at the first ' |
|
81 | ('l', 'line-number', None, _('show line number at the first ' | |
82 | 'appearance')), |
|
82 | 'appearance')), | |
83 | ('e', 'deleted', None, _('show deleted lines (slow) (EXPERIMENTAL)')), |
|
83 | ('e', 'deleted', None, _('show deleted lines (slow) (EXPERIMENTAL)')), | |
84 | ('', 'no-content', None, _('do not show file content (EXPERIMENTAL)')), |
|
84 | ('', 'no-content', None, _('do not show file content (EXPERIMENTAL)')), | |
85 | ('', 'no-follow', None, _("don't follow copies and renames")), |
|
85 | ('', 'no-follow', None, _("don't follow copies and renames")), | |
86 | ('', 'linear', None, _('enforce linear history, ignore second parent ' |
|
86 | ('', 'linear', None, _('enforce linear history, ignore second parent ' | |
87 | 'of merges (EXPERIMENTAL)')), |
|
87 | 'of merges (EXPERIMENTAL)')), | |
88 | ('', 'long-hash', None, _('show long changeset hash (EXPERIMENTAL)')), |
|
88 | ('', 'long-hash', None, _('show long changeset hash (EXPERIMENTAL)')), | |
89 | ('', 'rebuild', None, _('rebuild cache even if it exists ' |
|
89 | ('', 'rebuild', None, _('rebuild cache even if it exists ' | |
90 | '(EXPERIMENTAL)')), |
|
90 | '(EXPERIMENTAL)')), | |
91 | ] + commands.diffwsopts + commands.walkopts + commands.formatteropts, |
|
91 | ] + commands.diffwsopts + commands.walkopts + commands.formatteropts, | |
92 | r'synopsis': _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'), |
|
92 | r'synopsis': _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'), | |
93 | r'inferrepo': True, |
|
93 | r'inferrepo': True, | |
94 | } |
|
94 | } | |
95 |
|
95 | |||
96 | def fastannotate(ui, repo, *pats, **opts): |
|
96 | def fastannotate(ui, repo, *pats, **opts): | |
97 | """show changeset information by line for each file |
|
97 | """show changeset information by line for each file | |
98 |
|
98 | |||
99 | List changes in files, showing the revision id responsible for each line. |
|
99 | List changes in files, showing the revision id responsible for each line. | |
100 |
|
100 | |||
101 | This command is useful for discovering when a change was made and by whom. |
|
101 | This command is useful for discovering when a change was made and by whom. | |
102 |
|
102 | |||
103 | By default this command prints revision numbers. If you include --file, |
|
103 | By default this command prints revision numbers. If you include --file, | |
104 | --user, or --date, the revision number is suppressed unless you also |
|
104 | --user, or --date, the revision number is suppressed unless you also | |
105 | include --number. The default format can also be customized by setting |
|
105 | include --number. The default format can also be customized by setting | |
106 | fastannotate.defaultformat. |
|
106 | fastannotate.defaultformat. | |
107 |
|
107 | |||
108 | Returns 0 on success. |
|
108 | Returns 0 on success. | |
109 |
|
109 | |||
110 | .. container:: verbose |
|
110 | .. container:: verbose | |
111 |
|
111 | |||
112 | This command uses an implementation different from the vanilla annotate |
|
112 | This command uses an implementation different from the vanilla annotate | |
113 | command, which may produce slightly different (while still reasonable) |
|
113 | command, which may produce slightly different (while still reasonable) | |
114 | outputs for some cases. |
|
114 | outputs for some cases. | |
115 |
|
115 | |||
116 | Unlike the vanilla anootate, fastannotate follows rename regardless of |
|
116 | Unlike the vanilla anootate, fastannotate follows rename regardless of | |
117 | the existence of --file. |
|
117 | the existence of --file. | |
118 |
|
118 | |||
119 | For the best performance when running on a full repo, use -c, -l, |
|
119 | For the best performance when running on a full repo, use -c, -l, | |
120 | avoid -u, -d, -n. Use --linear and --no-content to make it even faster. |
|
120 | avoid -u, -d, -n. Use --linear and --no-content to make it even faster. | |
121 |
|
121 | |||
122 | For the best performance when running on a shallow (remotefilelog) |
|
122 | For the best performance when running on a shallow (remotefilelog) | |
123 | repo, avoid --linear, --no-follow, or any diff options. As the server |
|
123 | repo, avoid --linear, --no-follow, or any diff options. As the server | |
124 | won't be able to populate annotate cache when non-default options |
|
124 | won't be able to populate annotate cache when non-default options | |
125 | affecting results are used. |
|
125 | affecting results are used. | |
126 | """ |
|
126 | """ | |
127 | if not pats: |
|
127 | if not pats: | |
128 | raise error.Abort(_('at least one filename or pattern is required')) |
|
128 | raise error.Abort(_('at least one filename or pattern is required')) | |
129 |
|
129 | |||
130 | # performance hack: filtered repo can be slow. unfilter by default. |
|
130 | # performance hack: filtered repo can be slow. unfilter by default. | |
131 | if ui.configbool('fastannotate', 'unfilteredrepo'): |
|
131 | if ui.configbool('fastannotate', 'unfilteredrepo'): | |
132 | repo = repo.unfiltered() |
|
132 | repo = repo.unfiltered() | |
133 |
|
133 | |||
134 | opts = pycompat.byteskwargs(opts) |
|
134 | opts = pycompat.byteskwargs(opts) | |
135 |
|
135 | |||
136 | rev = opts.get('rev', '.') |
|
136 | rev = opts.get('rev', '.') | |
137 | rebuild = opts.get('rebuild', False) |
|
137 | rebuild = opts.get('rebuild', False) | |
138 |
|
138 | |||
139 | diffopts = patch.difffeatureopts(ui, opts, section='annotate', |
|
139 | diffopts = patch.difffeatureopts(ui, opts, section='annotate', | |
140 | whitespace=True) |
|
140 | whitespace=True) | |
141 | aopts = facontext.annotateopts( |
|
141 | aopts = facontext.annotateopts( | |
142 | diffopts=diffopts, |
|
142 | diffopts=diffopts, | |
143 | followmerge=not opts.get('linear', False), |
|
143 | followmerge=not opts.get('linear', False), | |
144 | followrename=not opts.get('no_follow', False)) |
|
144 | followrename=not opts.get('no_follow', False)) | |
145 |
|
145 | |||
146 | if not any(opts.get(s) |
|
146 | if not any(opts.get(s) | |
147 | for s in ['user', 'date', 'file', 'number', 'changeset']): |
|
147 | for s in ['user', 'date', 'file', 'number', 'changeset']): | |
148 | # default 'number' for compatibility. but fastannotate is more |
|
148 | # default 'number' for compatibility. but fastannotate is more | |
149 | # efficient with "changeset", "line-number" and "no-content". |
|
149 | # efficient with "changeset", "line-number" and "no-content". | |
150 | for name in ui.configlist('fastannotate', 'defaultformat', ['number']): |
|
150 | for name in ui.configlist('fastannotate', 'defaultformat', ['number']): | |
151 | opts[name] = True |
|
151 | opts[name] = True | |
152 |
|
152 | |||
153 | ui.pager('fastannotate') |
|
153 | ui.pager('fastannotate') | |
154 | template = opts.get('template') |
|
154 | template = opts.get('template') | |
155 | if template == 'json': |
|
155 | if template == 'json': | |
156 | formatter = faformatter.jsonformatter(ui, repo, opts) |
|
156 | formatter = faformatter.jsonformatter(ui, repo, opts) | |
157 | else: |
|
157 | else: | |
158 | formatter = faformatter.defaultformatter(ui, repo, opts) |
|
158 | formatter = faformatter.defaultformatter(ui, repo, opts) | |
159 | showdeleted = opts.get('deleted', False) |
|
159 | showdeleted = opts.get('deleted', False) | |
160 | showlines = not bool(opts.get('no_content')) |
|
160 | showlines = not bool(opts.get('no_content')) | |
161 | showpath = opts.get('file', False) |
|
161 | showpath = opts.get('file', False) | |
162 |
|
162 | |||
163 | # find the head of the main (master) branch |
|
163 | # find the head of the main (master) branch | |
164 | master = ui.config('fastannotate', 'mainbranch') or rev |
|
164 | master = ui.config('fastannotate', 'mainbranch') or rev | |
165 |
|
165 | |||
166 | # paths will be used for prefetching and the real annotating |
|
166 | # paths will be used for prefetching and the real annotating | |
167 | paths = list(_matchpaths(repo, rev, pats, opts, aopts)) |
|
167 | paths = list(_matchpaths(repo, rev, pats, opts, aopts)) | |
168 |
|
168 | |||
169 | # for client, prefetch from the server |
|
169 | # for client, prefetch from the server | |
170 | if util.safehasattr(repo, 'prefetchfastannotate'): |
|
170 | if util.safehasattr(repo, 'prefetchfastannotate'): | |
171 | repo.prefetchfastannotate(paths) |
|
171 | repo.prefetchfastannotate(paths) | |
172 |
|
172 | |||
173 | for path in paths: |
|
173 | for path in paths: | |
174 | result = lines = existinglines = None |
|
174 | result = lines = existinglines = None | |
175 | while True: |
|
175 | while True: | |
176 | try: |
|
176 | try: | |
177 | with facontext.annotatecontext(repo, path, aopts, rebuild) as a: |
|
177 | with facontext.annotatecontext(repo, path, aopts, rebuild) as a: | |
178 | result = a.annotate(rev, master=master, showpath=showpath, |
|
178 | result = a.annotate(rev, master=master, showpath=showpath, | |
179 | showlines=(showlines and |
|
179 | showlines=(showlines and | |
180 | not showdeleted)) |
|
180 | not showdeleted)) | |
181 | if showdeleted: |
|
181 | if showdeleted: | |
182 | existinglines = set((l[0], l[1]) for l in result) |
|
182 | existinglines = set((l[0], l[1]) for l in result) | |
183 | result = a.annotatealllines( |
|
183 | result = a.annotatealllines( | |
184 | rev, showpath=showpath, showlines=showlines) |
|
184 | rev, showpath=showpath, showlines=showlines) | |
185 | break |
|
185 | break | |
186 | except (faerror.CannotReuseError, faerror.CorruptedFileError): |
|
186 | except (faerror.CannotReuseError, faerror.CorruptedFileError): | |
187 | # happens if master moves backwards, or the file was deleted |
|
187 | # happens if master moves backwards, or the file was deleted | |
188 | # and readded, or renamed to an existing name, or corrupted. |
|
188 | # and readded, or renamed to an existing name, or corrupted. | |
189 | if rebuild: # give up since we have tried rebuild already |
|
189 | if rebuild: # give up since we have tried rebuild already | |
190 | raise |
|
190 | raise | |
191 | else: # try a second time rebuilding the cache (slow) |
|
191 | else: # try a second time rebuilding the cache (slow) | |
192 | rebuild = True |
|
192 | rebuild = True | |
193 | continue |
|
193 | continue | |
194 |
|
194 | |||
195 | if showlines: |
|
195 | if showlines: | |
196 | result, lines = result |
|
196 | result, lines = result | |
197 |
|
197 | |||
198 | formatter.write(result, lines, existinglines=existinglines) |
|
198 | formatter.write(result, lines, existinglines=existinglines) | |
199 | formatter.end() |
|
199 | formatter.end() | |
200 |
|
200 | |||
201 | _newopts = set([]) |
|
201 | _newopts = set([]) | |
202 | _knownopts = set([opt[1].replace('-', '_') for opt in |
|
202 | _knownopts = set([opt[1].replace('-', '_') for opt in | |
203 | (fastannotatecommandargs[r'options'] + commands.globalopts)]) |
|
203 | (fastannotatecommandargs[r'options'] + commands.globalopts)]) | |
204 |
|
204 | |||
205 | def _annotatewrapper(orig, ui, repo, *pats, **opts): |
|
205 | def _annotatewrapper(orig, ui, repo, *pats, **opts): | |
206 | """used by wrapdefault""" |
|
206 | """used by wrapdefault""" | |
207 | # we need this hack until the obsstore has 0.0 seconds perf impact |
|
207 | # we need this hack until the obsstore has 0.0 seconds perf impact | |
208 | if ui.configbool('fastannotate', 'unfilteredrepo'): |
|
208 | if ui.configbool('fastannotate', 'unfilteredrepo'): | |
209 | repo = repo.unfiltered() |
|
209 | repo = repo.unfiltered() | |
210 |
|
210 | |||
211 | # treat the file as text (skip the isbinary check) |
|
211 | # treat the file as text (skip the isbinary check) | |
212 | if ui.configbool('fastannotate', 'forcetext'): |
|
212 | if ui.configbool('fastannotate', 'forcetext'): | |
213 | opts[r'text'] = True |
|
213 | opts[r'text'] = True | |
214 |
|
214 | |||
215 | # check if we need to do prefetch (client-side) |
|
215 | # check if we need to do prefetch (client-side) | |
216 | rev = opts.get(r'rev') |
|
216 | rev = opts.get(r'rev') | |
217 | if util.safehasattr(repo, 'prefetchfastannotate') and rev is not None: |
|
217 | if util.safehasattr(repo, 'prefetchfastannotate') and rev is not None: | |
218 | paths = list(_matchpaths(repo, rev, pats, pycompat.byteskwargs(opts))) |
|
218 | paths = list(_matchpaths(repo, rev, pats, pycompat.byteskwargs(opts))) | |
219 | repo.prefetchfastannotate(paths) |
|
219 | repo.prefetchfastannotate(paths) | |
220 |
|
220 | |||
221 | return orig(ui, repo, *pats, **opts) |
|
221 | return orig(ui, repo, *pats, **opts) | |
222 |
|
222 | |||
223 | def registercommand(): |
|
223 | def registercommand(): | |
224 | """register the fastannotate command""" |
|
224 | """register the fastannotate command""" | |
225 | name = 'fastannotate|fastblame|fa' |
|
225 | name = 'fastannotate|fastblame|fa' | |
226 | command(name, helpbasic=True, **fastannotatecommandargs)(fastannotate) |
|
226 | command(name, helpbasic=True, **fastannotatecommandargs)(fastannotate) | |
227 |
|
227 | |||
228 | def wrapdefault(): |
|
228 | def wrapdefault(): | |
229 | """wrap the default annotate command, to be aware of the protocol""" |
|
229 | """wrap the default annotate command, to be aware of the protocol""" | |
230 | extensions.wrapcommand(commands.table, 'annotate', _annotatewrapper) |
|
230 | extensions.wrapcommand(commands.table, 'annotate', _annotatewrapper) | |
231 |
|
231 | |||
232 | @command('debugbuildannotatecache', |
|
232 | @command('debugbuildannotatecache', | |
233 | [('r', 'rev', '', _('build up to the specific revision'), _('REV')) |
|
233 | [('r', 'rev', '', _('build up to the specific revision'), _('REV')) | |
234 | ] + commands.walkopts, |
|
234 | ] + commands.walkopts, | |
235 | _('[-r REV] FILE...')) |
|
235 | _('[-r REV] FILE...')) | |
236 | def debugbuildannotatecache(ui, repo, *pats, **opts): |
|
236 | def debugbuildannotatecache(ui, repo, *pats, **opts): | |
237 | """incrementally build fastannotate cache up to REV for specified files |
|
237 | """incrementally build fastannotate cache up to REV for specified files | |
238 |
|
238 | |||
239 | If REV is not specified, use the config 'fastannotate.mainbranch'. |
|
239 | If REV is not specified, use the config 'fastannotate.mainbranch'. | |
240 |
|
240 | |||
241 | If fastannotate.client is True, download the annotate cache from the |
|
241 | If fastannotate.client is True, download the annotate cache from the | |
242 | server. Otherwise, build the annotate cache locally. |
|
242 | server. Otherwise, build the annotate cache locally. | |
243 |
|
243 | |||
244 | The annotate cache will be built using the default diff and follow |
|
244 | The annotate cache will be built using the default diff and follow | |
245 | options and lives in '.hg/fastannotate/default'. |
|
245 | options and lives in '.hg/fastannotate/default'. | |
246 | """ |
|
246 | """ | |
247 | opts = pycompat.byteskwargs(opts) |
|
247 | opts = pycompat.byteskwargs(opts) | |
248 | rev = opts.get('REV') or ui.config('fastannotate', 'mainbranch') |
|
248 | rev = opts.get('REV') or ui.config('fastannotate', 'mainbranch') | |
249 | if not rev: |
|
249 | if not rev: | |
250 | raise error.Abort(_('you need to provide a revision'), |
|
250 | raise error.Abort(_('you need to provide a revision'), | |
251 | hint=_('set fastannotate.mainbranch or use --rev')) |
|
251 | hint=_('set fastannotate.mainbranch or use --rev')) | |
252 | if ui.configbool('fastannotate', 'unfilteredrepo'): |
|
252 | if ui.configbool('fastannotate', 'unfilteredrepo'): | |
253 | repo = repo.unfiltered() |
|
253 | repo = repo.unfiltered() | |
254 | ctx = scmutil.revsingle(repo, rev) |
|
254 | ctx = scmutil.revsingle(repo, rev) | |
255 | m = scmutil.match(ctx, pats, opts) |
|
255 | m = scmutil.match(ctx, pats, opts) | |
256 | paths = list(ctx.walk(m)) |
|
256 | paths = list(ctx.walk(m)) | |
257 | if util.safehasattr(repo, 'prefetchfastannotate'): |
|
257 | if util.safehasattr(repo, 'prefetchfastannotate'): | |
258 | # client |
|
258 | # client | |
259 | if opts.get('REV'): |
|
259 | if opts.get('REV'): | |
260 | raise error.Abort(_('--rev cannot be used for client')) |
|
260 | raise error.Abort(_('--rev cannot be used for client')) | |
261 | repo.prefetchfastannotate(paths) |
|
261 | repo.prefetchfastannotate(paths) | |
262 | else: |
|
262 | else: | |
263 | # server, or full repo |
|
263 | # server, or full repo | |
|
264 | progress = ui.makeprogress(_('building'), total=len(paths)) | |||
264 | for i, path in enumerate(paths): |
|
265 | for i, path in enumerate(paths): | |
265 | ui.progress(_('building'), i, total=len(paths)) |
|
266 | progress.update(i) | |
266 | with facontext.annotatecontext(repo, path) as actx: |
|
267 | with facontext.annotatecontext(repo, path) as actx: | |
267 | try: |
|
268 | try: | |
268 | if actx.isuptodate(rev): |
|
269 | if actx.isuptodate(rev): | |
269 | continue |
|
270 | continue | |
270 | actx.annotate(rev, rev) |
|
271 | actx.annotate(rev, rev) | |
271 | except (faerror.CannotReuseError, faerror.CorruptedFileError): |
|
272 | except (faerror.CannotReuseError, faerror.CorruptedFileError): | |
272 | # the cache is broken (could happen with renaming so the |
|
273 | # the cache is broken (could happen with renaming so the | |
273 | # file history gets invalidated). rebuild and try again. |
|
274 | # file history gets invalidated). rebuild and try again. | |
274 | ui.debug('fastannotate: %s: rebuilding broken cache\n' |
|
275 | ui.debug('fastannotate: %s: rebuilding broken cache\n' | |
275 | % path) |
|
276 | % path) | |
276 | actx.rebuild() |
|
277 | actx.rebuild() | |
277 | try: |
|
278 | try: | |
278 | actx.annotate(rev, rev) |
|
279 | actx.annotate(rev, rev) | |
279 | except Exception as ex: |
|
280 | except Exception as ex: | |
280 | # possibly a bug, but should not stop us from building |
|
281 | # possibly a bug, but should not stop us from building | |
281 | # cache for other files. |
|
282 | # cache for other files. | |
282 | ui.warn(_('fastannotate: %s: failed to ' |
|
283 | ui.warn(_('fastannotate: %s: failed to ' | |
283 | 'build cache: %r\n') % (path, ex)) |
|
284 | 'build cache: %r\n') % (path, ex)) | |
284 | # clear the progress bar |
|
285 | progress.complete() | |
285 | ui.write() |
|
General Comments 0
You need to be logged in to leave comments.
Login now