##// END OF EJS Templates
fastannotate: process files as they arrive...
Martin von Zweigbergk -
r39751:d8a7690c default
parent child Browse files
Show More
@@ -1,228 +1,228 b''
1 # Copyright 2016-present Facebook. All Rights Reserved.
1 # Copyright 2016-present Facebook. All Rights Reserved.
2 #
2 #
3 # protocol: logic for a server providing fastannotate support
3 # protocol: logic for a server providing fastannotate support
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 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import contextlib
9 import contextlib
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 error,
14 error,
15 extensions,
15 extensions,
16 hg,
16 hg,
17 util,
17 util,
18 wireprotov1peer,
18 wireprotov1peer,
19 wireprotov1server,
19 wireprotov1server,
20 )
20 )
21 from . import context
21 from . import context
22
22
23 # common
23 # common
24
24
25 def _getmaster(ui):
25 def _getmaster(ui):
26 """get the mainbranch, and enforce it is set"""
26 """get the mainbranch, and enforce it is set"""
27 master = ui.config('fastannotate', 'mainbranch')
27 master = ui.config('fastannotate', 'mainbranch')
28 if not master:
28 if not master:
29 raise error.Abort(_('fastannotate.mainbranch is required '
29 raise error.Abort(_('fastannotate.mainbranch is required '
30 'for both the client and the server'))
30 'for both the client and the server'))
31 return master
31 return master
32
32
33 # server-side
33 # server-side
34
34
35 def _capabilities(orig, repo, proto):
35 def _capabilities(orig, repo, proto):
36 result = orig(repo, proto)
36 result = orig(repo, proto)
37 result.append('getannotate')
37 result.append('getannotate')
38 return result
38 return result
39
39
40 def _getannotate(repo, proto, path, lastnode):
40 def _getannotate(repo, proto, path, lastnode):
41 # output:
41 # output:
42 # FILE := vfspath + '\0' + str(size) + '\0' + content
42 # FILE := vfspath + '\0' + str(size) + '\0' + content
43 # OUTPUT := '' | FILE + OUTPUT
43 # OUTPUT := '' | FILE + OUTPUT
44 result = ''
44 result = ''
45 buildondemand = repo.ui.configbool('fastannotate', 'serverbuildondemand',
45 buildondemand = repo.ui.configbool('fastannotate', 'serverbuildondemand',
46 True)
46 True)
47 with context.annotatecontext(repo, path) as actx:
47 with context.annotatecontext(repo, path) as actx:
48 if buildondemand:
48 if buildondemand:
49 # update before responding to the client
49 # update before responding to the client
50 master = _getmaster(repo.ui)
50 master = _getmaster(repo.ui)
51 try:
51 try:
52 if not actx.isuptodate(master):
52 if not actx.isuptodate(master):
53 actx.annotate(master, master)
53 actx.annotate(master, master)
54 except Exception:
54 except Exception:
55 # non-fast-forward move or corrupted. rebuild automically.
55 # non-fast-forward move or corrupted. rebuild automically.
56 actx.rebuild()
56 actx.rebuild()
57 try:
57 try:
58 actx.annotate(master, master)
58 actx.annotate(master, master)
59 except Exception:
59 except Exception:
60 actx.rebuild() # delete files
60 actx.rebuild() # delete files
61 finally:
61 finally:
62 # although the "with" context will also do a close/flush, we
62 # although the "with" context will also do a close/flush, we
63 # need to do it early so we can send the correct respond to
63 # need to do it early so we can send the correct respond to
64 # client.
64 # client.
65 actx.close()
65 actx.close()
66 # send back the full content of revmap and linelog, in the future we
66 # send back the full content of revmap and linelog, in the future we
67 # may want to do some rsync-like fancy updating.
67 # may want to do some rsync-like fancy updating.
68 # the lastnode check is not necessary if the client and the server
68 # the lastnode check is not necessary if the client and the server
69 # agree where the main branch is.
69 # agree where the main branch is.
70 if actx.lastnode != lastnode:
70 if actx.lastnode != lastnode:
71 for p in [actx.revmappath, actx.linelogpath]:
71 for p in [actx.revmappath, actx.linelogpath]:
72 if not os.path.exists(p):
72 if not os.path.exists(p):
73 continue
73 continue
74 content = ''
74 content = ''
75 with open(p, 'rb') as f:
75 with open(p, 'rb') as f:
76 content = f.read()
76 content = f.read()
77 vfsbaselen = len(repo.vfs.base + '/')
77 vfsbaselen = len(repo.vfs.base + '/')
78 relpath = p[vfsbaselen:]
78 relpath = p[vfsbaselen:]
79 result += '%s\0%d\0%s' % (relpath, len(content), content)
79 result += '%s\0%d\0%s' % (relpath, len(content), content)
80 return result
80 return result
81
81
82 def _registerwireprotocommand():
82 def _registerwireprotocommand():
83 if 'getannotate' in wireprotov1server.commands:
83 if 'getannotate' in wireprotov1server.commands:
84 return
84 return
85 wireprotov1server.wireprotocommand(
85 wireprotov1server.wireprotocommand(
86 'getannotate', 'path lastnode')(_getannotate)
86 'getannotate', 'path lastnode')(_getannotate)
87
87
88 def serveruisetup(ui):
88 def serveruisetup(ui):
89 _registerwireprotocommand()
89 _registerwireprotocommand()
90 extensions.wrapfunction(wireprotov1server, '_capabilities', _capabilities)
90 extensions.wrapfunction(wireprotov1server, '_capabilities', _capabilities)
91
91
92 # client-side
92 # client-side
93
93
94 def _parseresponse(payload):
94 def _parseresponse(payload):
95 result = {}
95 result = {}
96 i = 0
96 i = 0
97 l = len(payload) - 1
97 l = len(payload) - 1
98 state = 0 # 0: vfspath, 1: size
98 state = 0 # 0: vfspath, 1: size
99 vfspath = size = ''
99 vfspath = size = ''
100 while i < l:
100 while i < l:
101 ch = payload[i]
101 ch = payload[i]
102 if ch == '\0':
102 if ch == '\0':
103 if state == 1:
103 if state == 1:
104 result[vfspath] = buffer(payload, i + 1, int(size))
104 result[vfspath] = buffer(payload, i + 1, int(size))
105 i += int(size)
105 i += int(size)
106 state = 0
106 state = 0
107 vfspath = size = ''
107 vfspath = size = ''
108 elif state == 0:
108 elif state == 0:
109 state = 1
109 state = 1
110 else:
110 else:
111 if state == 1:
111 if state == 1:
112 size += ch
112 size += ch
113 elif state == 0:
113 elif state == 0:
114 vfspath += ch
114 vfspath += ch
115 i += 1
115 i += 1
116 return result
116 return result
117
117
118 def peersetup(ui, peer):
118 def peersetup(ui, peer):
119 class fastannotatepeer(peer.__class__):
119 class fastannotatepeer(peer.__class__):
120 @wireprotov1peer.batchable
120 @wireprotov1peer.batchable
121 def getannotate(self, path, lastnode=None):
121 def getannotate(self, path, lastnode=None):
122 if not self.capable('getannotate'):
122 if not self.capable('getannotate'):
123 ui.warn(_('remote peer cannot provide annotate cache\n'))
123 ui.warn(_('remote peer cannot provide annotate cache\n'))
124 yield None, None
124 yield None, None
125 else:
125 else:
126 args = {'path': path, 'lastnode': lastnode or ''}
126 args = {'path': path, 'lastnode': lastnode or ''}
127 f = wireprotov1peer.future()
127 f = wireprotov1peer.future()
128 yield args, f
128 yield args, f
129 yield _parseresponse(f.value)
129 yield _parseresponse(f.value)
130 peer.__class__ = fastannotatepeer
130 peer.__class__ = fastannotatepeer
131
131
132 @contextlib.contextmanager
132 @contextlib.contextmanager
133 def annotatepeer(repo):
133 def annotatepeer(repo):
134 ui = repo.ui
134 ui = repo.ui
135
135
136 remotepath = ui.expandpath(
136 remotepath = ui.expandpath(
137 ui.config('fastannotate', 'remotepath', 'default'))
137 ui.config('fastannotate', 'remotepath', 'default'))
138 peer = hg.peer(ui, {}, remotepath)
138 peer = hg.peer(ui, {}, remotepath)
139
139
140 try:
140 try:
141 yield peer
141 yield peer
142 finally:
142 finally:
143 peer.close()
143 peer.close()
144
144
145 def clientfetch(repo, paths, lastnodemap=None, peer=None):
145 def clientfetch(repo, paths, lastnodemap=None, peer=None):
146 """download annotate cache from the server for paths"""
146 """download annotate cache from the server for paths"""
147 if not paths:
147 if not paths:
148 return
148 return
149
149
150 if peer is None:
150 if peer is None:
151 with annotatepeer(repo) as peer:
151 with annotatepeer(repo) as peer:
152 return clientfetch(repo, paths, lastnodemap, peer)
152 return clientfetch(repo, paths, lastnodemap, peer)
153
153
154 if lastnodemap is None:
154 if lastnodemap is None:
155 lastnodemap = {}
155 lastnodemap = {}
156
156
157 ui = repo.ui
157 ui = repo.ui
158 results = []
158 results = []
159 with peer.commandexecutor() as batcher:
159 with peer.commandexecutor() as batcher:
160 ui.debug('fastannotate: requesting %d files\n' % len(paths))
160 ui.debug('fastannotate: requesting %d files\n' % len(paths))
161 for p in paths:
161 for p in paths:
162 results.append(batcher.callcommand(
162 results.append(batcher.callcommand(
163 'getannotate',
163 'getannotate',
164 {'path': p, 'lastnode':lastnodemap.get(p)}))
164 {'path': p, 'lastnode':lastnodemap.get(p)}))
165
165
166 ui.debug('fastannotate: server returned\n')
166 for result in results:
167 for result in results:
167 r = result.result()
168 r = result.result()
168 # TODO: pconvert these paths on the server?
169 # TODO: pconvert these paths on the server?
169 r = {util.pconvert(p): v for p, v in r.iteritems()}
170 r = {util.pconvert(p): v for p, v in r.iteritems()}
170 for path in sorted(r):
171 for path in sorted(r):
171 # ignore malicious paths
172 # ignore malicious paths
172 if (not path.startswith('fastannotate/')
173 if not path.startswith('fastannotate/') or '/../' in (path + '/'):
173 or '/../' in (path + '/')):
174 ui.debug('fastannotate: ignored malicious path %s\n' % path)
174 ui.debug('fastannotate: ignored malicious path %s\n' % path)
175 continue
175 continue
176 content = r[path]
176 content = r[path]
177 if ui.debugflag:
177 if ui.debugflag:
178 ui.debug('fastannotate: writing %d bytes to %s\n'
178 ui.debug('fastannotate: writing %d bytes to %s\n'
179 % (len(content), path))
179 % (len(content), path))
180 repo.vfs.makedirs(os.path.dirname(path))
180 repo.vfs.makedirs(os.path.dirname(path))
181 with repo.vfs(path, 'wb') as f:
181 with repo.vfs(path, 'wb') as f:
182 f.write(content)
182 f.write(content)
183
183
184 def _filterfetchpaths(repo, paths):
184 def _filterfetchpaths(repo, paths):
185 """return a subset of paths whose history is long and need to fetch linelog
185 """return a subset of paths whose history is long and need to fetch linelog
186 from the server. works with remotefilelog and non-remotefilelog repos.
186 from the server. works with remotefilelog and non-remotefilelog repos.
187 """
187 """
188 threshold = repo.ui.configint('fastannotate', 'clientfetchthreshold', 10)
188 threshold = repo.ui.configint('fastannotate', 'clientfetchthreshold', 10)
189 if threshold <= 0:
189 if threshold <= 0:
190 return paths
190 return paths
191
191
192 result = []
192 result = []
193 for path in paths:
193 for path in paths:
194 try:
194 try:
195 if len(repo.file(path)) >= threshold:
195 if len(repo.file(path)) >= threshold:
196 result.append(path)
196 result.append(path)
197 except Exception: # file not found etc.
197 except Exception: # file not found etc.
198 result.append(path)
198 result.append(path)
199
199
200 return result
200 return result
201
201
202 def localreposetup(ui, repo):
202 def localreposetup(ui, repo):
203 class fastannotaterepo(repo.__class__):
203 class fastannotaterepo(repo.__class__):
204 def prefetchfastannotate(self, paths, peer=None):
204 def prefetchfastannotate(self, paths, peer=None):
205 master = _getmaster(self.ui)
205 master = _getmaster(self.ui)
206 needupdatepaths = []
206 needupdatepaths = []
207 lastnodemap = {}
207 lastnodemap = {}
208 try:
208 try:
209 for path in _filterfetchpaths(self, paths):
209 for path in _filterfetchpaths(self, paths):
210 with context.annotatecontext(self, path) as actx:
210 with context.annotatecontext(self, path) as actx:
211 if not actx.isuptodate(master, strict=False):
211 if not actx.isuptodate(master, strict=False):
212 needupdatepaths.append(path)
212 needupdatepaths.append(path)
213 lastnodemap[path] = actx.lastnode
213 lastnodemap[path] = actx.lastnode
214 if needupdatepaths:
214 if needupdatepaths:
215 clientfetch(self, needupdatepaths, lastnodemap, peer)
215 clientfetch(self, needupdatepaths, lastnodemap, peer)
216 except Exception as ex:
216 except Exception as ex:
217 # could be directory not writable or so, not fatal
217 # could be directory not writable or so, not fatal
218 self.ui.debug('fastannotate: prefetch failed: %r\n' % ex)
218 self.ui.debug('fastannotate: prefetch failed: %r\n' % ex)
219 repo.__class__ = fastannotaterepo
219 repo.__class__ = fastannotaterepo
220
220
221 def clientreposetup(ui, repo):
221 def clientreposetup(ui, repo):
222 _registerwireprotocommand()
222 _registerwireprotocommand()
223 if repo.local():
223 if repo.local():
224 localreposetup(ui, repo)
224 localreposetup(ui, repo)
225 # TODO: this mutates global state, but only if at least one repo
225 # TODO: this mutates global state, but only if at least one repo
226 # has the extension enabled. This is probably bad for hgweb.
226 # has the extension enabled. This is probably bad for hgweb.
227 if peersetup not in hg.wirepeersetupfuncs:
227 if peersetup not in hg.wirepeersetupfuncs:
228 hg.wirepeersetupfuncs.append(peersetup)
228 hg.wirepeersetupfuncs.append(peersetup)
@@ -1,215 +1,211 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [ui]
2 > [ui]
3 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
3 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
4 > [extensions]
4 > [extensions]
5 > fastannotate=
5 > fastannotate=
6 > [fastannotate]
6 > [fastannotate]
7 > mainbranch=@
7 > mainbranch=@
8 > EOF
8 > EOF
9
9
10 $ HGMERGE=true; export HGMERGE
10 $ HGMERGE=true; export HGMERGE
11
11
12 setup the server repo
12 setup the server repo
13
13
14 $ hg init repo-server
14 $ hg init repo-server
15 $ cd repo-server
15 $ cd repo-server
16 $ cat >> .hg/hgrc << EOF
16 $ cat >> .hg/hgrc << EOF
17 > [fastannotate]
17 > [fastannotate]
18 > server=1
18 > server=1
19 > EOF
19 > EOF
20 $ for i in 1 2 3 4; do
20 $ for i in 1 2 3 4; do
21 > echo $i >> a
21 > echo $i >> a
22 > hg commit -A -m $i a
22 > hg commit -A -m $i a
23 > done
23 > done
24 $ [ -d .hg/fastannotate ]
24 $ [ -d .hg/fastannotate ]
25 [1]
25 [1]
26 $ hg bookmark @
26 $ hg bookmark @
27 $ cd ..
27 $ cd ..
28
28
29 setup the local repo
29 setup the local repo
30
30
31 $ hg clone 'ssh://user@dummy/repo-server' repo-local -q
31 $ hg clone 'ssh://user@dummy/repo-server' repo-local -q
32 $ cd repo-local
32 $ cd repo-local
33 $ cat >> .hg/hgrc << EOF
33 $ cat >> .hg/hgrc << EOF
34 > [fastannotate]
34 > [fastannotate]
35 > client=1
35 > client=1
36 > clientfetchthreshold=0
36 > clientfetchthreshold=0
37 > EOF
37 > EOF
38 $ [ -d .hg/fastannotate ]
38 $ [ -d .hg/fastannotate ]
39 [1]
39 [1]
40 $ hg fastannotate a --debug
40 $ hg fastannotate a --debug
41 running * (glob)
41 running * (glob)
42 sending hello command
42 sending hello command
43 sending between command
43 sending between command
44 remote: * (glob) (?)
44 remote: * (glob) (?)
45 remote: capabilities: * (glob)
45 remote: capabilities: * (glob)
46 remote: * (glob) (?)
46 remote: * (glob) (?)
47 sending protocaps command
47 sending protocaps command
48 fastannotate: requesting 1 files
48 fastannotate: requesting 1 files
49 sending getannotate command
49 sending getannotate command
50 fastannotate: server returned
51 fastannotate: writing 112 bytes to fastannotate/default/a.l
50 fastannotate: writing 112 bytes to fastannotate/default/a.l
52 fastannotate: writing 94 bytes to fastannotate/default/a.m
51 fastannotate: writing 94 bytes to fastannotate/default/a.m
53 fastannotate: a: using fast path (resolved fctx: True)
52 fastannotate: a: using fast path (resolved fctx: True)
54 0: 1
53 0: 1
55 1: 2
54 1: 2
56 2: 3
55 2: 3
57 3: 4
56 3: 4
58
57
59 the cache could be reused and no download is necessary
58 the cache could be reused and no download is necessary
60
59
61 $ hg fastannotate a --debug
60 $ hg fastannotate a --debug
62 fastannotate: a: using fast path (resolved fctx: True)
61 fastannotate: a: using fast path (resolved fctx: True)
63 0: 1
62 0: 1
64 1: 2
63 1: 2
65 2: 3
64 2: 3
66 3: 4
65 3: 4
67
66
68 if the client agrees where the head of the master branch is, no re-download
67 if the client agrees where the head of the master branch is, no re-download
69 happens even if the client has more commits
68 happens even if the client has more commits
70
69
71 $ echo 5 >> a
70 $ echo 5 >> a
72 $ hg commit -m 5
71 $ hg commit -m 5
73 $ hg bookmark -r 3 @ -f
72 $ hg bookmark -r 3 @ -f
74 $ hg fastannotate a --debug
73 $ hg fastannotate a --debug
75 0: 1
74 0: 1
76 1: 2
75 1: 2
77 2: 3
76 2: 3
78 3: 4
77 3: 4
79 4: 5
78 4: 5
80
79
81 if the client has a different "@" (head of the master branch) and "@" is ahead
80 if the client has a different "@" (head of the master branch) and "@" is ahead
82 of the server, the server can detect things are unchanged and does not return
81 of the server, the server can detect things are unchanged and does not return
83 full contents (not that there is no "writing ... to fastannotate"), but the
82 full contents (not that there is no "writing ... to fastannotate"), but the
84 client can also build things up on its own (causing diverge)
83 client can also build things up on its own (causing diverge)
85
84
86 $ hg bookmark -r 4 @ -f
85 $ hg bookmark -r 4 @ -f
87 $ hg fastannotate a --debug
86 $ hg fastannotate a --debug
88 running * (glob)
87 running * (glob)
89 sending hello command
88 sending hello command
90 sending between command
89 sending between command
91 remote: * (glob) (?)
90 remote: * (glob) (?)
92 remote: capabilities: * (glob)
91 remote: capabilities: * (glob)
93 remote: * (glob) (?)
92 remote: * (glob) (?)
94 sending protocaps command
93 sending protocaps command
95 fastannotate: requesting 1 files
94 fastannotate: requesting 1 files
96 sending getannotate command
95 sending getannotate command
97 fastannotate: server returned
98 fastannotate: a: 1 new changesets in the main branch
96 fastannotate: a: 1 new changesets in the main branch
99 0: 1
97 0: 1
100 1: 2
98 1: 2
101 2: 3
99 2: 3
102 3: 4
100 3: 4
103 4: 5
101 4: 5
104
102
105 if the client has a different "@" which is behind the server. no download is
103 if the client has a different "@" which is behind the server. no download is
106 necessary
104 necessary
107
105
108 $ hg fastannotate a --debug --config fastannotate.mainbranch=2
106 $ hg fastannotate a --debug --config fastannotate.mainbranch=2
109 fastannotate: a: using fast path (resolved fctx: True)
107 fastannotate: a: using fast path (resolved fctx: True)
110 0: 1
108 0: 1
111 1: 2
109 1: 2
112 2: 3
110 2: 3
113 3: 4
111 3: 4
114 4: 5
112 4: 5
115
113
116 define fastannotate on-disk paths
114 define fastannotate on-disk paths
117
115
118 $ p1=.hg/fastannotate/default
116 $ p1=.hg/fastannotate/default
119 $ p2=../repo-server/.hg/fastannotate/default
117 $ p2=../repo-server/.hg/fastannotate/default
120
118
121 revert bookmark change so the client is behind the server
119 revert bookmark change so the client is behind the server
122
120
123 $ hg bookmark -r 2 @ -f
121 $ hg bookmark -r 2 @ -f
124
122
125 in the "fctx" mode with the "annotate" command, the client also downloads the
123 in the "fctx" mode with the "annotate" command, the client also downloads the
126 cache. but not in the (default) "fastannotate" mode.
124 cache. but not in the (default) "fastannotate" mode.
127
125
128 $ rm $p1/a.l $p1/a.m
126 $ rm $p1/a.l $p1/a.m
129 $ hg annotate a --debug | grep 'fastannotate: writing'
127 $ hg annotate a --debug | grep 'fastannotate: writing'
130 [1]
128 [1]
131 $ hg annotate a --config fastannotate.modes=fctx --debug | grep 'fastannotate: writing' | sort
129 $ hg annotate a --config fastannotate.modes=fctx --debug | grep 'fastannotate: writing' | sort
132 fastannotate: writing 112 bytes to fastannotate/default/a.l
130 fastannotate: writing 112 bytes to fastannotate/default/a.l
133 fastannotate: writing 94 bytes to fastannotate/default/a.m
131 fastannotate: writing 94 bytes to fastannotate/default/a.m
134
132
135 the fastannotate cache (built server-side, downloaded client-side) in two repos
133 the fastannotate cache (built server-side, downloaded client-side) in two repos
136 have the same content (because the client downloads from the server)
134 have the same content (because the client downloads from the server)
137
135
138 $ diff $p1/a.l $p2/a.l
136 $ diff $p1/a.l $p2/a.l
139 $ diff $p1/a.m $p2/a.m
137 $ diff $p1/a.m $p2/a.m
140
138
141 in the "fctx" mode, the client could also build the cache locally
139 in the "fctx" mode, the client could also build the cache locally
142
140
143 $ hg annotate a --config fastannotate.modes=fctx --debug --config fastannotate.mainbranch=4 | grep fastannotate
141 $ hg annotate a --config fastannotate.modes=fctx --debug --config fastannotate.mainbranch=4 | grep fastannotate
144 fastannotate: requesting 1 files
142 fastannotate: requesting 1 files
145 fastannotate: server returned
146 fastannotate: a: 1 new changesets in the main branch
143 fastannotate: a: 1 new changesets in the main branch
147
144
148 the server would rebuild broken cache automatically
145 the server would rebuild broken cache automatically
149
146
150 $ cp $p2/a.m $p2/a.m.bak
147 $ cp $p2/a.m $p2/a.m.bak
151 $ echo BROKEN1 > $p1/a.m
148 $ echo BROKEN1 > $p1/a.m
152 $ echo BROKEN2 > $p2/a.m
149 $ echo BROKEN2 > $p2/a.m
153 $ hg fastannotate a --debug | grep 'fastannotate: writing' | sort
150 $ hg fastannotate a --debug | grep 'fastannotate: writing' | sort
154 fastannotate: writing 112 bytes to fastannotate/default/a.l
151 fastannotate: writing 112 bytes to fastannotate/default/a.l
155 fastannotate: writing 94 bytes to fastannotate/default/a.m
152 fastannotate: writing 94 bytes to fastannotate/default/a.m
156 $ diff $p1/a.m $p2/a.m
153 $ diff $p1/a.m $p2/a.m
157 $ diff $p2/a.m $p2/a.m.bak
154 $ diff $p2/a.m $p2/a.m.bak
158
155
159 use the "debugbuildannotatecache" command to build annotate cache
156 use the "debugbuildannotatecache" command to build annotate cache
160
157
161 $ rm -rf $p1 $p2
158 $ rm -rf $p1 $p2
162 $ hg --cwd ../repo-server debugbuildannotatecache a --debug
159 $ hg --cwd ../repo-server debugbuildannotatecache a --debug
163 fastannotate: a: 4 new changesets in the main branch
160 fastannotate: a: 4 new changesets in the main branch
164 $ hg --cwd ../repo-local debugbuildannotatecache a --debug
161 $ hg --cwd ../repo-local debugbuildannotatecache a --debug
165 running * (glob)
162 running * (glob)
166 sending hello command
163 sending hello command
167 sending between command
164 sending between command
168 remote: * (glob) (?)
165 remote: * (glob) (?)
169 remote: capabilities: * (glob)
166 remote: capabilities: * (glob)
170 remote: * (glob) (?)
167 remote: * (glob) (?)
171 sending protocaps command
168 sending protocaps command
172 fastannotate: requesting 1 files
169 fastannotate: requesting 1 files
173 sending getannotate command
170 sending getannotate command
174 fastannotate: server returned
175 fastannotate: writing * (glob)
171 fastannotate: writing * (glob)
176 fastannotate: writing * (glob)
172 fastannotate: writing * (glob)
177 $ diff $p1/a.l $p2/a.l
173 $ diff $p1/a.l $p2/a.l
178 $ diff $p1/a.m $p2/a.m
174 $ diff $p1/a.m $p2/a.m
179
175
180 with the clientfetchthreshold config option, the client can build up the cache
176 with the clientfetchthreshold config option, the client can build up the cache
181 without downloading from the server
177 without downloading from the server
182
178
183 $ rm -rf $p1
179 $ rm -rf $p1
184 $ hg fastannotate a --debug --config fastannotate.clientfetchthreshold=10
180 $ hg fastannotate a --debug --config fastannotate.clientfetchthreshold=10
185 fastannotate: a: 3 new changesets in the main branch
181 fastannotate: a: 3 new changesets in the main branch
186 0: 1
182 0: 1
187 1: 2
183 1: 2
188 2: 3
184 2: 3
189 3: 4
185 3: 4
190 4: 5
186 4: 5
191
187
192 if the fastannotate directory is not writable, the fctx mode still works
188 if the fastannotate directory is not writable, the fctx mode still works
193
189
194 $ rm -rf $p1
190 $ rm -rf $p1
195 $ touch $p1
191 $ touch $p1
196 $ hg annotate a --debug --traceback --config fastannotate.modes=fctx
192 $ hg annotate a --debug --traceback --config fastannotate.modes=fctx
197 fastannotate: a: cache broken and deleted
193 fastannotate: a: cache broken and deleted
198 fastannotate: prefetch failed: * (glob)
194 fastannotate: prefetch failed: * (glob)
199 fastannotate: a: cache broken and deleted
195 fastannotate: a: cache broken and deleted
200 fastannotate: falling back to the vanilla annotate: * (glob)
196 fastannotate: falling back to the vanilla annotate: * (glob)
201 0: 1
197 0: 1
202 1: 2
198 1: 2
203 2: 3
199 2: 3
204 3: 4
200 3: 4
205 4: 5
201 4: 5
206
202
207 with serverbuildondemand=False, the server will not build anything
203 with serverbuildondemand=False, the server will not build anything
208
204
209 $ cat >> ../repo-server/.hg/hgrc <<EOF
205 $ cat >> ../repo-server/.hg/hgrc <<EOF
210 > [fastannotate]
206 > [fastannotate]
211 > serverbuildondemand=False
207 > serverbuildondemand=False
212 > EOF
208 > EOF
213 $ rm -rf $p1 $p2
209 $ rm -rf $p1 $p2
214 $ hg fastannotate a --debug | grep 'fastannotate: writing'
210 $ hg fastannotate a --debug | grep 'fastannotate: writing'
215 [1]
211 [1]
General Comments 0
You need to be logged in to leave comments. Login now