##// END OF EJS Templates
path: pass `path` to `peer` in narrow...
marmoute -
r50627:0058c74d default
parent child Browse files
Show More
@@ -1,693 +1,692 b''
1 # narrowcommands.py - command modifications for narrowhg extension
1 # narrowcommands.py - command modifications for narrowhg extension
2 #
2 #
3 # Copyright 2017 Google, Inc.
3 # Copyright 2017 Google, Inc.
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 import itertools
8 import itertools
9 import os
9 import os
10
10
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial.node import (
12 from mercurial.node import (
13 hex,
13 hex,
14 short,
14 short,
15 )
15 )
16 from mercurial import (
16 from mercurial import (
17 bundle2,
17 bundle2,
18 cmdutil,
18 cmdutil,
19 commands,
19 commands,
20 discovery,
20 discovery,
21 encoding,
21 encoding,
22 error,
22 error,
23 exchange,
23 exchange,
24 extensions,
24 extensions,
25 hg,
25 hg,
26 narrowspec,
26 narrowspec,
27 pathutil,
27 pathutil,
28 pycompat,
28 pycompat,
29 registrar,
29 registrar,
30 repair,
30 repair,
31 repoview,
31 repoview,
32 requirements,
32 requirements,
33 sparse,
33 sparse,
34 util,
34 util,
35 wireprototypes,
35 wireprototypes,
36 )
36 )
37 from mercurial.utils import (
37 from mercurial.utils import (
38 urlutil,
38 urlutil,
39 )
39 )
40
40
41 table = {}
41 table = {}
42 command = registrar.command(table)
42 command = registrar.command(table)
43
43
44
44
45 def setup():
45 def setup():
46 """Wraps user-facing mercurial commands with narrow-aware versions."""
46 """Wraps user-facing mercurial commands with narrow-aware versions."""
47
47
48 entry = extensions.wrapcommand(commands.table, b'clone', clonenarrowcmd)
48 entry = extensions.wrapcommand(commands.table, b'clone', clonenarrowcmd)
49 entry[1].append(
49 entry[1].append(
50 (b'', b'narrow', None, _(b"create a narrow clone of select files"))
50 (b'', b'narrow', None, _(b"create a narrow clone of select files"))
51 )
51 )
52 entry[1].append(
52 entry[1].append(
53 (
53 (
54 b'',
54 b'',
55 b'depth',
55 b'depth',
56 b'',
56 b'',
57 _(b"limit the history fetched by distance from heads"),
57 _(b"limit the history fetched by distance from heads"),
58 )
58 )
59 )
59 )
60 entry[1].append((b'', b'narrowspec', b'', _(b"read narrowspecs from file")))
60 entry[1].append((b'', b'narrowspec', b'', _(b"read narrowspecs from file")))
61 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
61 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
62 if b'sparse' not in extensions.enabled():
62 if b'sparse' not in extensions.enabled():
63 entry[1].append(
63 entry[1].append(
64 (b'', b'include', [], _(b"specifically fetch this file/directory"))
64 (b'', b'include', [], _(b"specifically fetch this file/directory"))
65 )
65 )
66 entry[1].append(
66 entry[1].append(
67 (
67 (
68 b'',
68 b'',
69 b'exclude',
69 b'exclude',
70 [],
70 [],
71 _(b"do not fetch this file/directory, even if included"),
71 _(b"do not fetch this file/directory, even if included"),
72 )
72 )
73 )
73 )
74
74
75 entry = extensions.wrapcommand(commands.table, b'pull', pullnarrowcmd)
75 entry = extensions.wrapcommand(commands.table, b'pull', pullnarrowcmd)
76 entry[1].append(
76 entry[1].append(
77 (
77 (
78 b'',
78 b'',
79 b'depth',
79 b'depth',
80 b'',
80 b'',
81 _(b"limit the history fetched by distance from heads"),
81 _(b"limit the history fetched by distance from heads"),
82 )
82 )
83 )
83 )
84
84
85 extensions.wrapcommand(commands.table, b'archive', archivenarrowcmd)
85 extensions.wrapcommand(commands.table, b'archive', archivenarrowcmd)
86
86
87
87
88 def clonenarrowcmd(orig, ui, repo, *args, **opts):
88 def clonenarrowcmd(orig, ui, repo, *args, **opts):
89 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
89 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
90 opts = pycompat.byteskwargs(opts)
90 opts = pycompat.byteskwargs(opts)
91 wrappedextraprepare = util.nullcontextmanager()
91 wrappedextraprepare = util.nullcontextmanager()
92 narrowspecfile = opts[b'narrowspec']
92 narrowspecfile = opts[b'narrowspec']
93
93
94 if narrowspecfile:
94 if narrowspecfile:
95 filepath = os.path.join(encoding.getcwd(), narrowspecfile)
95 filepath = os.path.join(encoding.getcwd(), narrowspecfile)
96 ui.status(_(b"reading narrowspec from '%s'\n") % filepath)
96 ui.status(_(b"reading narrowspec from '%s'\n") % filepath)
97 try:
97 try:
98 fdata = util.readfile(filepath)
98 fdata = util.readfile(filepath)
99 except IOError as inst:
99 except IOError as inst:
100 raise error.Abort(
100 raise error.Abort(
101 _(b"cannot read narrowspecs from '%s': %s")
101 _(b"cannot read narrowspecs from '%s': %s")
102 % (filepath, encoding.strtolocal(inst.strerror))
102 % (filepath, encoding.strtolocal(inst.strerror))
103 )
103 )
104
104
105 includes, excludes, profiles = sparse.parseconfig(ui, fdata, b'narrow')
105 includes, excludes, profiles = sparse.parseconfig(ui, fdata, b'narrow')
106 if profiles:
106 if profiles:
107 raise error.ConfigError(
107 raise error.ConfigError(
108 _(
108 _(
109 b"cannot specify other files using '%include' in"
109 b"cannot specify other files using '%include' in"
110 b" narrowspec"
110 b" narrowspec"
111 )
111 )
112 )
112 )
113
113
114 narrowspec.validatepatterns(includes)
114 narrowspec.validatepatterns(includes)
115 narrowspec.validatepatterns(excludes)
115 narrowspec.validatepatterns(excludes)
116
116
117 # narrowspec is passed so we should assume that user wants narrow clone
117 # narrowspec is passed so we should assume that user wants narrow clone
118 opts[b'narrow'] = True
118 opts[b'narrow'] = True
119 opts[b'include'].extend(includes)
119 opts[b'include'].extend(includes)
120 opts[b'exclude'].extend(excludes)
120 opts[b'exclude'].extend(excludes)
121
121
122 if opts[b'narrow']:
122 if opts[b'narrow']:
123
123
124 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
124 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
125 orig(pullop, kwargs)
125 orig(pullop, kwargs)
126
126
127 if opts.get(b'depth'):
127 if opts.get(b'depth'):
128 kwargs[b'depth'] = opts[b'depth']
128 kwargs[b'depth'] = opts[b'depth']
129
129
130 wrappedextraprepare = extensions.wrappedfunction(
130 wrappedextraprepare = extensions.wrappedfunction(
131 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
131 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
132 )
132 )
133
133
134 with wrappedextraprepare:
134 with wrappedextraprepare:
135 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
135 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
136
136
137
137
138 def pullnarrowcmd(orig, ui, repo, *args, **opts):
138 def pullnarrowcmd(orig, ui, repo, *args, **opts):
139 """Wraps pull command to allow modifying narrow spec."""
139 """Wraps pull command to allow modifying narrow spec."""
140 wrappedextraprepare = util.nullcontextmanager()
140 wrappedextraprepare = util.nullcontextmanager()
141 if requirements.NARROW_REQUIREMENT in repo.requirements:
141 if requirements.NARROW_REQUIREMENT in repo.requirements:
142
142
143 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
143 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
144 orig(pullop, kwargs)
144 orig(pullop, kwargs)
145 if opts.get('depth'):
145 if opts.get('depth'):
146 kwargs[b'depth'] = opts['depth']
146 kwargs[b'depth'] = opts['depth']
147
147
148 wrappedextraprepare = extensions.wrappedfunction(
148 wrappedextraprepare = extensions.wrappedfunction(
149 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
149 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
150 )
150 )
151
151
152 with wrappedextraprepare:
152 with wrappedextraprepare:
153 return orig(ui, repo, *args, **opts)
153 return orig(ui, repo, *args, **opts)
154
154
155
155
156 def archivenarrowcmd(orig, ui, repo, *args, **opts):
156 def archivenarrowcmd(orig, ui, repo, *args, **opts):
157 """Wraps archive command to narrow the default includes."""
157 """Wraps archive command to narrow the default includes."""
158 if requirements.NARROW_REQUIREMENT in repo.requirements:
158 if requirements.NARROW_REQUIREMENT in repo.requirements:
159 repo_includes, repo_excludes = repo.narrowpats
159 repo_includes, repo_excludes = repo.narrowpats
160 includes = set(opts.get('include', []))
160 includes = set(opts.get('include', []))
161 excludes = set(opts.get('exclude', []))
161 excludes = set(opts.get('exclude', []))
162 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
162 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
163 includes, excludes, repo_includes, repo_excludes
163 includes, excludes, repo_includes, repo_excludes
164 )
164 )
165 if includes:
165 if includes:
166 opts['include'] = includes
166 opts['include'] = includes
167 if excludes:
167 if excludes:
168 opts['exclude'] = excludes
168 opts['exclude'] = excludes
169 return orig(ui, repo, *args, **opts)
169 return orig(ui, repo, *args, **opts)
170
170
171
171
172 def pullbundle2extraprepare(orig, pullop, kwargs):
172 def pullbundle2extraprepare(orig, pullop, kwargs):
173 repo = pullop.repo
173 repo = pullop.repo
174 if requirements.NARROW_REQUIREMENT not in repo.requirements:
174 if requirements.NARROW_REQUIREMENT not in repo.requirements:
175 return orig(pullop, kwargs)
175 return orig(pullop, kwargs)
176
176
177 if wireprototypes.NARROWCAP not in pullop.remote.capabilities():
177 if wireprototypes.NARROWCAP not in pullop.remote.capabilities():
178 raise error.Abort(_(b"server does not support narrow clones"))
178 raise error.Abort(_(b"server does not support narrow clones"))
179 orig(pullop, kwargs)
179 orig(pullop, kwargs)
180 kwargs[b'narrow'] = True
180 kwargs[b'narrow'] = True
181 include, exclude = repo.narrowpats
181 include, exclude = repo.narrowpats
182 kwargs[b'oldincludepats'] = include
182 kwargs[b'oldincludepats'] = include
183 kwargs[b'oldexcludepats'] = exclude
183 kwargs[b'oldexcludepats'] = exclude
184 if include:
184 if include:
185 kwargs[b'includepats'] = include
185 kwargs[b'includepats'] = include
186 if exclude:
186 if exclude:
187 kwargs[b'excludepats'] = exclude
187 kwargs[b'excludepats'] = exclude
188 # calculate known nodes only in ellipses cases because in non-ellipses cases
188 # calculate known nodes only in ellipses cases because in non-ellipses cases
189 # we have all the nodes
189 # we have all the nodes
190 if wireprototypes.ELLIPSESCAP1 in pullop.remote.capabilities():
190 if wireprototypes.ELLIPSESCAP1 in pullop.remote.capabilities():
191 kwargs[b'known'] = [
191 kwargs[b'known'] = [
192 hex(ctx.node())
192 hex(ctx.node())
193 for ctx in repo.set(b'::%ln', pullop.common)
193 for ctx in repo.set(b'::%ln', pullop.common)
194 if ctx.node() != repo.nullid
194 if ctx.node() != repo.nullid
195 ]
195 ]
196 if not kwargs[b'known']:
196 if not kwargs[b'known']:
197 # Mercurial serializes an empty list as '' and deserializes it as
197 # Mercurial serializes an empty list as '' and deserializes it as
198 # [''], so delete it instead to avoid handling the empty string on
198 # [''], so delete it instead to avoid handling the empty string on
199 # the server.
199 # the server.
200 del kwargs[b'known']
200 del kwargs[b'known']
201
201
202
202
203 extensions.wrapfunction(
203 extensions.wrapfunction(
204 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare
204 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare
205 )
205 )
206
206
207
207
208 def _narrow(
208 def _narrow(
209 ui,
209 ui,
210 repo,
210 repo,
211 remote,
211 remote,
212 commoninc,
212 commoninc,
213 oldincludes,
213 oldincludes,
214 oldexcludes,
214 oldexcludes,
215 newincludes,
215 newincludes,
216 newexcludes,
216 newexcludes,
217 force,
217 force,
218 backup,
218 backup,
219 ):
219 ):
220 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
220 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
221 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
221 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
222
222
223 # This is essentially doing "hg outgoing" to find all local-only
223 # This is essentially doing "hg outgoing" to find all local-only
224 # commits. We will then check that the local-only commits don't
224 # commits. We will then check that the local-only commits don't
225 # have any changes to files that will be untracked.
225 # have any changes to files that will be untracked.
226 unfi = repo.unfiltered()
226 unfi = repo.unfiltered()
227 outgoing = discovery.findcommonoutgoing(unfi, remote, commoninc=commoninc)
227 outgoing = discovery.findcommonoutgoing(unfi, remote, commoninc=commoninc)
228 ui.status(_(b'looking for local changes to affected paths\n'))
228 ui.status(_(b'looking for local changes to affected paths\n'))
229 progress = ui.makeprogress(
229 progress = ui.makeprogress(
230 topic=_(b'changesets'),
230 topic=_(b'changesets'),
231 unit=_(b'changesets'),
231 unit=_(b'changesets'),
232 total=len(outgoing.missing) + len(outgoing.excluded),
232 total=len(outgoing.missing) + len(outgoing.excluded),
233 )
233 )
234 localnodes = []
234 localnodes = []
235 with progress:
235 with progress:
236 for n in itertools.chain(outgoing.missing, outgoing.excluded):
236 for n in itertools.chain(outgoing.missing, outgoing.excluded):
237 progress.increment()
237 progress.increment()
238 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
238 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
239 localnodes.append(n)
239 localnodes.append(n)
240 revstostrip = unfi.revs(b'descendants(%ln)', localnodes)
240 revstostrip = unfi.revs(b'descendants(%ln)', localnodes)
241 hiddenrevs = repoview.filterrevs(repo, b'visible')
241 hiddenrevs = repoview.filterrevs(repo, b'visible')
242 visibletostrip = list(
242 visibletostrip = list(
243 repo.changelog.node(r) for r in (revstostrip - hiddenrevs)
243 repo.changelog.node(r) for r in (revstostrip - hiddenrevs)
244 )
244 )
245 if visibletostrip:
245 if visibletostrip:
246 ui.status(
246 ui.status(
247 _(
247 _(
248 b'The following changeset(s) or their ancestors have '
248 b'The following changeset(s) or their ancestors have '
249 b'local changes not on the remote:\n'
249 b'local changes not on the remote:\n'
250 )
250 )
251 )
251 )
252 maxnodes = 10
252 maxnodes = 10
253 if ui.verbose or len(visibletostrip) <= maxnodes:
253 if ui.verbose or len(visibletostrip) <= maxnodes:
254 for n in visibletostrip:
254 for n in visibletostrip:
255 ui.status(b'%s\n' % short(n))
255 ui.status(b'%s\n' % short(n))
256 else:
256 else:
257 for n in visibletostrip[:maxnodes]:
257 for n in visibletostrip[:maxnodes]:
258 ui.status(b'%s\n' % short(n))
258 ui.status(b'%s\n' % short(n))
259 ui.status(
259 ui.status(
260 _(b'...and %d more, use --verbose to list all\n')
260 _(b'...and %d more, use --verbose to list all\n')
261 % (len(visibletostrip) - maxnodes)
261 % (len(visibletostrip) - maxnodes)
262 )
262 )
263 if not force:
263 if not force:
264 raise error.StateError(
264 raise error.StateError(
265 _(b'local changes found'),
265 _(b'local changes found'),
266 hint=_(b'use --force-delete-local-changes to ignore'),
266 hint=_(b'use --force-delete-local-changes to ignore'),
267 )
267 )
268
268
269 with ui.uninterruptible():
269 with ui.uninterruptible():
270 if revstostrip:
270 if revstostrip:
271 tostrip = [unfi.changelog.node(r) for r in revstostrip]
271 tostrip = [unfi.changelog.node(r) for r in revstostrip]
272 if repo[b'.'].node() in tostrip:
272 if repo[b'.'].node() in tostrip:
273 # stripping working copy, so move to a different commit first
273 # stripping working copy, so move to a different commit first
274 urev = max(
274 urev = max(
275 repo.revs(
275 repo.revs(
276 b'(::%n) - %ln + null',
276 b'(::%n) - %ln + null',
277 repo[b'.'].node(),
277 repo[b'.'].node(),
278 visibletostrip,
278 visibletostrip,
279 )
279 )
280 )
280 )
281 hg.clean(repo, urev)
281 hg.clean(repo, urev)
282 overrides = {(b'devel', b'strip-obsmarkers'): False}
282 overrides = {(b'devel', b'strip-obsmarkers'): False}
283 if backup:
283 if backup:
284 ui.status(_(b'moving unwanted changesets to backup\n'))
284 ui.status(_(b'moving unwanted changesets to backup\n'))
285 else:
285 else:
286 ui.status(_(b'deleting unwanted changesets\n'))
286 ui.status(_(b'deleting unwanted changesets\n'))
287 with ui.configoverride(overrides, b'narrow'):
287 with ui.configoverride(overrides, b'narrow'):
288 repair.strip(ui, unfi, tostrip, topic=b'narrow', backup=backup)
288 repair.strip(ui, unfi, tostrip, topic=b'narrow', backup=backup)
289
289
290 todelete = []
290 todelete = []
291 for t, f, size in repo.store.datafiles():
291 for t, f, size in repo.store.datafiles():
292 if f.startswith(b'data/'):
292 if f.startswith(b'data/'):
293 file = f[5:-2]
293 file = f[5:-2]
294 if not newmatch(file):
294 if not newmatch(file):
295 todelete.append(f)
295 todelete.append(f)
296 elif f.startswith(b'meta/'):
296 elif f.startswith(b'meta/'):
297 dir = f[5:-13]
297 dir = f[5:-13]
298 dirs = sorted(pathutil.dirs({dir})) + [dir]
298 dirs = sorted(pathutil.dirs({dir})) + [dir]
299 include = True
299 include = True
300 for d in dirs:
300 for d in dirs:
301 visit = newmatch.visitdir(d)
301 visit = newmatch.visitdir(d)
302 if not visit:
302 if not visit:
303 include = False
303 include = False
304 break
304 break
305 if visit == b'all':
305 if visit == b'all':
306 break
306 break
307 if not include:
307 if not include:
308 todelete.append(f)
308 todelete.append(f)
309
309
310 repo.destroying()
310 repo.destroying()
311
311
312 with repo.transaction(b'narrowing'):
312 with repo.transaction(b'narrowing'):
313 # Update narrowspec before removing revlogs, so repo won't be
313 # Update narrowspec before removing revlogs, so repo won't be
314 # corrupt in case of crash
314 # corrupt in case of crash
315 repo.setnarrowpats(newincludes, newexcludes)
315 repo.setnarrowpats(newincludes, newexcludes)
316
316
317 for f in todelete:
317 for f in todelete:
318 ui.status(_(b'deleting %s\n') % f)
318 ui.status(_(b'deleting %s\n') % f)
319 util.unlinkpath(repo.svfs.join(f))
319 util.unlinkpath(repo.svfs.join(f))
320 repo.store.markremoved(f)
320 repo.store.markremoved(f)
321
321
322 ui.status(_(b'deleting unwanted files from working copy\n'))
322 ui.status(_(b'deleting unwanted files from working copy\n'))
323 with repo.dirstate.parentchange():
323 with repo.dirstate.parentchange():
324 narrowspec.updateworkingcopy(repo, assumeclean=True)
324 narrowspec.updateworkingcopy(repo, assumeclean=True)
325 narrowspec.copytoworkingcopy(repo)
325 narrowspec.copytoworkingcopy(repo)
326
326
327 repo.destroyed()
327 repo.destroyed()
328
328
329
329
330 def _widen(
330 def _widen(
331 ui,
331 ui,
332 repo,
332 repo,
333 remote,
333 remote,
334 commoninc,
334 commoninc,
335 oldincludes,
335 oldincludes,
336 oldexcludes,
336 oldexcludes,
337 newincludes,
337 newincludes,
338 newexcludes,
338 newexcludes,
339 ):
339 ):
340 # for now we assume that if a server has ellipses enabled, we will be
340 # for now we assume that if a server has ellipses enabled, we will be
341 # exchanging ellipses nodes. In future we should add ellipses as a client
341 # exchanging ellipses nodes. In future we should add ellipses as a client
342 # side requirement (maybe) to distinguish a client is shallow or not and
342 # side requirement (maybe) to distinguish a client is shallow or not and
343 # then send that information to server whether we want ellipses or not.
343 # then send that information to server whether we want ellipses or not.
344 # Theoretically a non-ellipses repo should be able to use narrow
344 # Theoretically a non-ellipses repo should be able to use narrow
345 # functionality from an ellipses enabled server
345 # functionality from an ellipses enabled server
346 remotecap = remote.capabilities()
346 remotecap = remote.capabilities()
347 ellipsesremote = any(
347 ellipsesremote = any(
348 cap in remotecap for cap in wireprototypes.SUPPORTED_ELLIPSESCAP
348 cap in remotecap for cap in wireprototypes.SUPPORTED_ELLIPSESCAP
349 )
349 )
350
350
351 # check whether we are talking to a server which supports old version of
351 # check whether we are talking to a server which supports old version of
352 # ellipses capabilities
352 # ellipses capabilities
353 isoldellipses = (
353 isoldellipses = (
354 ellipsesremote
354 ellipsesremote
355 and wireprototypes.ELLIPSESCAP1 in remotecap
355 and wireprototypes.ELLIPSESCAP1 in remotecap
356 and wireprototypes.ELLIPSESCAP not in remotecap
356 and wireprototypes.ELLIPSESCAP not in remotecap
357 )
357 )
358
358
359 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
359 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
360 orig(pullop, kwargs)
360 orig(pullop, kwargs)
361 # The old{in,ex}cludepats have already been set by orig()
361 # The old{in,ex}cludepats have already been set by orig()
362 kwargs[b'includepats'] = newincludes
362 kwargs[b'includepats'] = newincludes
363 kwargs[b'excludepats'] = newexcludes
363 kwargs[b'excludepats'] = newexcludes
364
364
365 wrappedextraprepare = extensions.wrappedfunction(
365 wrappedextraprepare = extensions.wrappedfunction(
366 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
366 exchange, b'_pullbundle2extraprepare', pullbundle2extraprepare_widen
367 )
367 )
368
368
369 # define a function that narrowbundle2 can call after creating the
369 # define a function that narrowbundle2 can call after creating the
370 # backup bundle, but before applying the bundle from the server
370 # backup bundle, but before applying the bundle from the server
371 def setnewnarrowpats():
371 def setnewnarrowpats():
372 repo.setnarrowpats(newincludes, newexcludes)
372 repo.setnarrowpats(newincludes, newexcludes)
373
373
374 repo.setnewnarrowpats = setnewnarrowpats
374 repo.setnewnarrowpats = setnewnarrowpats
375 # silence the devel-warning of applying an empty changegroup
375 # silence the devel-warning of applying an empty changegroup
376 overrides = {(b'devel', b'all-warnings'): False}
376 overrides = {(b'devel', b'all-warnings'): False}
377
377
378 common = commoninc[0]
378 common = commoninc[0]
379 with ui.uninterruptible():
379 with ui.uninterruptible():
380 if ellipsesremote:
380 if ellipsesremote:
381 ds = repo.dirstate
381 ds = repo.dirstate
382 p1, p2 = ds.p1(), ds.p2()
382 p1, p2 = ds.p1(), ds.p2()
383 with ds.parentchange():
383 with ds.parentchange():
384 ds.setparents(repo.nullid, repo.nullid)
384 ds.setparents(repo.nullid, repo.nullid)
385 if isoldellipses:
385 if isoldellipses:
386 with wrappedextraprepare:
386 with wrappedextraprepare:
387 exchange.pull(repo, remote, heads=common)
387 exchange.pull(repo, remote, heads=common)
388 else:
388 else:
389 known = []
389 known = []
390 if ellipsesremote:
390 if ellipsesremote:
391 known = [
391 known = [
392 ctx.node()
392 ctx.node()
393 for ctx in repo.set(b'::%ln', common)
393 for ctx in repo.set(b'::%ln', common)
394 if ctx.node() != repo.nullid
394 if ctx.node() != repo.nullid
395 ]
395 ]
396 with remote.commandexecutor() as e:
396 with remote.commandexecutor() as e:
397 bundle = e.callcommand(
397 bundle = e.callcommand(
398 b'narrow_widen',
398 b'narrow_widen',
399 {
399 {
400 b'oldincludes': oldincludes,
400 b'oldincludes': oldincludes,
401 b'oldexcludes': oldexcludes,
401 b'oldexcludes': oldexcludes,
402 b'newincludes': newincludes,
402 b'newincludes': newincludes,
403 b'newexcludes': newexcludes,
403 b'newexcludes': newexcludes,
404 b'cgversion': b'03',
404 b'cgversion': b'03',
405 b'commonheads': common,
405 b'commonheads': common,
406 b'known': known,
406 b'known': known,
407 b'ellipses': ellipsesremote,
407 b'ellipses': ellipsesremote,
408 },
408 },
409 ).result()
409 ).result()
410
410
411 trmanager = exchange.transactionmanager(
411 trmanager = exchange.transactionmanager(
412 repo, b'widen', remote.url()
412 repo, b'widen', remote.url()
413 )
413 )
414 with trmanager, repo.ui.configoverride(overrides, b'widen'):
414 with trmanager, repo.ui.configoverride(overrides, b'widen'):
415 op = bundle2.bundleoperation(
415 op = bundle2.bundleoperation(
416 repo, trmanager.transaction, source=b'widen'
416 repo, trmanager.transaction, source=b'widen'
417 )
417 )
418 # TODO: we should catch error.Abort here
418 # TODO: we should catch error.Abort here
419 bundle2.processbundle(repo, bundle, op=op)
419 bundle2.processbundle(repo, bundle, op=op)
420
420
421 if ellipsesremote:
421 if ellipsesremote:
422 with ds.parentchange():
422 with ds.parentchange():
423 ds.setparents(p1, p2)
423 ds.setparents(p1, p2)
424
424
425 with repo.transaction(b'widening'), repo.dirstate.parentchange():
425 with repo.transaction(b'widening'), repo.dirstate.parentchange():
426 repo.setnewnarrowpats()
426 repo.setnewnarrowpats()
427 narrowspec.updateworkingcopy(repo)
427 narrowspec.updateworkingcopy(repo)
428 narrowspec.copytoworkingcopy(repo)
428 narrowspec.copytoworkingcopy(repo)
429
429
430
430
431 # TODO(rdamazio): Make new matcher format and update description
431 # TODO(rdamazio): Make new matcher format and update description
432 @command(
432 @command(
433 b'tracked',
433 b'tracked',
434 [
434 [
435 (b'', b'addinclude', [], _(b'new paths to include')),
435 (b'', b'addinclude', [], _(b'new paths to include')),
436 (b'', b'removeinclude', [], _(b'old paths to no longer include')),
436 (b'', b'removeinclude', [], _(b'old paths to no longer include')),
437 (
437 (
438 b'',
438 b'',
439 b'auto-remove-includes',
439 b'auto-remove-includes',
440 False,
440 False,
441 _(b'automatically choose unused includes to remove'),
441 _(b'automatically choose unused includes to remove'),
442 ),
442 ),
443 (b'', b'addexclude', [], _(b'new paths to exclude')),
443 (b'', b'addexclude', [], _(b'new paths to exclude')),
444 (b'', b'import-rules', b'', _(b'import narrowspecs from a file')),
444 (b'', b'import-rules', b'', _(b'import narrowspecs from a file')),
445 (b'', b'removeexclude', [], _(b'old paths to no longer exclude')),
445 (b'', b'removeexclude', [], _(b'old paths to no longer exclude')),
446 (
446 (
447 b'',
447 b'',
448 b'clear',
448 b'clear',
449 False,
449 False,
450 _(b'whether to replace the existing narrowspec'),
450 _(b'whether to replace the existing narrowspec'),
451 ),
451 ),
452 (
452 (
453 b'',
453 b'',
454 b'force-delete-local-changes',
454 b'force-delete-local-changes',
455 False,
455 False,
456 _(b'forces deletion of local changes when narrowing'),
456 _(b'forces deletion of local changes when narrowing'),
457 ),
457 ),
458 (
458 (
459 b'',
459 b'',
460 b'backup',
460 b'backup',
461 True,
461 True,
462 _(b'back up local changes when narrowing'),
462 _(b'back up local changes when narrowing'),
463 ),
463 ),
464 (
464 (
465 b'',
465 b'',
466 b'update-working-copy',
466 b'update-working-copy',
467 False,
467 False,
468 _(b'update working copy when the store has changed'),
468 _(b'update working copy when the store has changed'),
469 ),
469 ),
470 ]
470 ]
471 + commands.remoteopts,
471 + commands.remoteopts,
472 _(b'[OPTIONS]... [REMOTE]'),
472 _(b'[OPTIONS]... [REMOTE]'),
473 inferrepo=True,
473 inferrepo=True,
474 helpcategory=command.CATEGORY_MAINTENANCE,
474 helpcategory=command.CATEGORY_MAINTENANCE,
475 )
475 )
476 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
476 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
477 """show or change the current narrowspec
477 """show or change the current narrowspec
478
478
479 With no argument, shows the current narrowspec entries, one per line. Each
479 With no argument, shows the current narrowspec entries, one per line. Each
480 line will be prefixed with 'I' or 'X' for included or excluded patterns,
480 line will be prefixed with 'I' or 'X' for included or excluded patterns,
481 respectively.
481 respectively.
482
482
483 The narrowspec is comprised of expressions to match remote files and/or
483 The narrowspec is comprised of expressions to match remote files and/or
484 directories that should be pulled into your client.
484 directories that should be pulled into your client.
485 The narrowspec has *include* and *exclude* expressions, with excludes always
485 The narrowspec has *include* and *exclude* expressions, with excludes always
486 trumping includes: that is, if a file matches an exclude expression, it will
486 trumping includes: that is, if a file matches an exclude expression, it will
487 be excluded even if it also matches an include expression.
487 be excluded even if it also matches an include expression.
488 Excluding files that were never included has no effect.
488 Excluding files that were never included has no effect.
489
489
490 Each included or excluded entry is in the format described by
490 Each included or excluded entry is in the format described by
491 'hg help patterns'.
491 'hg help patterns'.
492
492
493 The options allow you to add or remove included and excluded expressions.
493 The options allow you to add or remove included and excluded expressions.
494
494
495 If --clear is specified, then all previous includes and excludes are DROPPED
495 If --clear is specified, then all previous includes and excludes are DROPPED
496 and replaced by the new ones specified to --addinclude and --addexclude.
496 and replaced by the new ones specified to --addinclude and --addexclude.
497 If --clear is specified without any further options, the narrowspec will be
497 If --clear is specified without any further options, the narrowspec will be
498 empty and will not match any files.
498 empty and will not match any files.
499
499
500 If --auto-remove-includes is specified, then those includes that don't match
500 If --auto-remove-includes is specified, then those includes that don't match
501 any files modified by currently visible local commits (those not shared by
501 any files modified by currently visible local commits (those not shared by
502 the remote) will be added to the set of explicitly specified includes to
502 the remote) will be added to the set of explicitly specified includes to
503 remove.
503 remove.
504
504
505 --import-rules accepts a path to a file containing rules, allowing you to
505 --import-rules accepts a path to a file containing rules, allowing you to
506 add --addinclude, --addexclude rules in bulk. Like the other include and
506 add --addinclude, --addexclude rules in bulk. Like the other include and
507 exclude switches, the changes are applied immediately.
507 exclude switches, the changes are applied immediately.
508 """
508 """
509 opts = pycompat.byteskwargs(opts)
509 opts = pycompat.byteskwargs(opts)
510 if requirements.NARROW_REQUIREMENT not in repo.requirements:
510 if requirements.NARROW_REQUIREMENT not in repo.requirements:
511 raise error.InputError(
511 raise error.InputError(
512 _(
512 _(
513 b'the tracked command is only supported on '
513 b'the tracked command is only supported on '
514 b'repositories cloned with --narrow'
514 b'repositories cloned with --narrow'
515 )
515 )
516 )
516 )
517
517
518 # Before supporting, decide whether it "hg tracked --clear" should mean
518 # Before supporting, decide whether it "hg tracked --clear" should mean
519 # tracking no paths or all paths.
519 # tracking no paths or all paths.
520 if opts[b'clear']:
520 if opts[b'clear']:
521 raise error.InputError(_(b'the --clear option is not yet supported'))
521 raise error.InputError(_(b'the --clear option is not yet supported'))
522
522
523 # import rules from a file
523 # import rules from a file
524 newrules = opts.get(b'import_rules')
524 newrules = opts.get(b'import_rules')
525 if newrules:
525 if newrules:
526 try:
526 try:
527 filepath = os.path.join(encoding.getcwd(), newrules)
527 filepath = os.path.join(encoding.getcwd(), newrules)
528 fdata = util.readfile(filepath)
528 fdata = util.readfile(filepath)
529 except IOError as inst:
529 except IOError as inst:
530 raise error.StorageError(
530 raise error.StorageError(
531 _(b"cannot read narrowspecs from '%s': %s")
531 _(b"cannot read narrowspecs from '%s': %s")
532 % (filepath, encoding.strtolocal(inst.strerror))
532 % (filepath, encoding.strtolocal(inst.strerror))
533 )
533 )
534 includepats, excludepats, profiles = sparse.parseconfig(
534 includepats, excludepats, profiles = sparse.parseconfig(
535 ui, fdata, b'narrow'
535 ui, fdata, b'narrow'
536 )
536 )
537 if profiles:
537 if profiles:
538 raise error.InputError(
538 raise error.InputError(
539 _(
539 _(
540 b"including other spec files using '%include' "
540 b"including other spec files using '%include' "
541 b"is not supported in narrowspec"
541 b"is not supported in narrowspec"
542 )
542 )
543 )
543 )
544 opts[b'addinclude'].extend(includepats)
544 opts[b'addinclude'].extend(includepats)
545 opts[b'addexclude'].extend(excludepats)
545 opts[b'addexclude'].extend(excludepats)
546
546
547 addedincludes = narrowspec.parsepatterns(opts[b'addinclude'])
547 addedincludes = narrowspec.parsepatterns(opts[b'addinclude'])
548 removedincludes = narrowspec.parsepatterns(opts[b'removeinclude'])
548 removedincludes = narrowspec.parsepatterns(opts[b'removeinclude'])
549 addedexcludes = narrowspec.parsepatterns(opts[b'addexclude'])
549 addedexcludes = narrowspec.parsepatterns(opts[b'addexclude'])
550 removedexcludes = narrowspec.parsepatterns(opts[b'removeexclude'])
550 removedexcludes = narrowspec.parsepatterns(opts[b'removeexclude'])
551 autoremoveincludes = opts[b'auto_remove_includes']
551 autoremoveincludes = opts[b'auto_remove_includes']
552
552
553 update_working_copy = opts[b'update_working_copy']
553 update_working_copy = opts[b'update_working_copy']
554 only_show = not (
554 only_show = not (
555 addedincludes
555 addedincludes
556 or removedincludes
556 or removedincludes
557 or addedexcludes
557 or addedexcludes
558 or removedexcludes
558 or removedexcludes
559 or newrules
559 or newrules
560 or autoremoveincludes
560 or autoremoveincludes
561 or update_working_copy
561 or update_working_copy
562 )
562 )
563
563
564 oldincludes, oldexcludes = repo.narrowpats
564 oldincludes, oldexcludes = repo.narrowpats
565
565
566 # filter the user passed additions and deletions into actual additions and
566 # filter the user passed additions and deletions into actual additions and
567 # deletions of excludes and includes
567 # deletions of excludes and includes
568 addedincludes -= oldincludes
568 addedincludes -= oldincludes
569 removedincludes &= oldincludes
569 removedincludes &= oldincludes
570 addedexcludes -= oldexcludes
570 addedexcludes -= oldexcludes
571 removedexcludes &= oldexcludes
571 removedexcludes &= oldexcludes
572
572
573 widening = addedincludes or removedexcludes
573 widening = addedincludes or removedexcludes
574 narrowing = removedincludes or addedexcludes
574 narrowing = removedincludes or addedexcludes
575
575
576 # Only print the current narrowspec.
576 # Only print the current narrowspec.
577 if only_show:
577 if only_show:
578 ui.pager(b'tracked')
578 ui.pager(b'tracked')
579 fm = ui.formatter(b'narrow', opts)
579 fm = ui.formatter(b'narrow', opts)
580 for i in sorted(oldincludes):
580 for i in sorted(oldincludes):
581 fm.startitem()
581 fm.startitem()
582 fm.write(b'status', b'%s ', b'I', label=b'narrow.included')
582 fm.write(b'status', b'%s ', b'I', label=b'narrow.included')
583 fm.write(b'pat', b'%s\n', i, label=b'narrow.included')
583 fm.write(b'pat', b'%s\n', i, label=b'narrow.included')
584 for i in sorted(oldexcludes):
584 for i in sorted(oldexcludes):
585 fm.startitem()
585 fm.startitem()
586 fm.write(b'status', b'%s ', b'X', label=b'narrow.excluded')
586 fm.write(b'status', b'%s ', b'X', label=b'narrow.excluded')
587 fm.write(b'pat', b'%s\n', i, label=b'narrow.excluded')
587 fm.write(b'pat', b'%s\n', i, label=b'narrow.excluded')
588 fm.end()
588 fm.end()
589 return 0
589 return 0
590
590
591 if update_working_copy:
591 if update_working_copy:
592 with repo.wlock(), repo.lock(), repo.transaction(
592 with repo.wlock(), repo.lock(), repo.transaction(
593 b'narrow-wc'
593 b'narrow-wc'
594 ), repo.dirstate.parentchange():
594 ), repo.dirstate.parentchange():
595 narrowspec.updateworkingcopy(repo)
595 narrowspec.updateworkingcopy(repo)
596 narrowspec.copytoworkingcopy(repo)
596 narrowspec.copytoworkingcopy(repo)
597 return 0
597 return 0
598
598
599 if not (widening or narrowing or autoremoveincludes):
599 if not (widening or narrowing or autoremoveincludes):
600 ui.status(_(b"nothing to widen or narrow\n"))
600 ui.status(_(b"nothing to widen or narrow\n"))
601 return 0
601 return 0
602
602
603 with repo.wlock(), repo.lock():
603 with repo.wlock(), repo.lock():
604 cmdutil.bailifchanged(repo)
604 cmdutil.bailifchanged(repo)
605
605
606 # Find the revisions we have in common with the remote. These will
606 # Find the revisions we have in common with the remote. These will
607 # be used for finding local-only changes for narrowing. They will
607 # be used for finding local-only changes for narrowing. They will
608 # also define the set of revisions to update for widening.
608 # also define the set of revisions to update for widening.
609 r = urlutil.get_unique_pull_path(b'tracked', repo, ui, remotepath)
609 path = urlutil.get_unique_pull_path_obj(b'tracked', ui, remotepath)
610 url, branches = r
610 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(path.loc))
611 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(url))
611 remote = hg.peer(repo, opts, path)
612 remote = hg.peer(repo, opts, url)
613
612
614 try:
613 try:
615 # check narrow support before doing anything if widening needs to be
614 # check narrow support before doing anything if widening needs to be
616 # performed. In future we should also abort if client is ellipses and
615 # performed. In future we should also abort if client is ellipses and
617 # server does not support ellipses
616 # server does not support ellipses
618 if (
617 if (
619 widening
618 widening
620 and wireprototypes.NARROWCAP not in remote.capabilities()
619 and wireprototypes.NARROWCAP not in remote.capabilities()
621 ):
620 ):
622 raise error.Abort(_(b"server does not support narrow clones"))
621 raise error.Abort(_(b"server does not support narrow clones"))
623
622
624 commoninc = discovery.findcommonincoming(repo, remote)
623 commoninc = discovery.findcommonincoming(repo, remote)
625
624
626 if autoremoveincludes:
625 if autoremoveincludes:
627 outgoing = discovery.findcommonoutgoing(
626 outgoing = discovery.findcommonoutgoing(
628 repo, remote, commoninc=commoninc
627 repo, remote, commoninc=commoninc
629 )
628 )
630 ui.status(_(b'looking for unused includes to remove\n'))
629 ui.status(_(b'looking for unused includes to remove\n'))
631 localfiles = set()
630 localfiles = set()
632 for n in itertools.chain(outgoing.missing, outgoing.excluded):
631 for n in itertools.chain(outgoing.missing, outgoing.excluded):
633 localfiles.update(repo[n].files())
632 localfiles.update(repo[n].files())
634 suggestedremovals = []
633 suggestedremovals = []
635 for include in sorted(oldincludes):
634 for include in sorted(oldincludes):
636 match = narrowspec.match(repo.root, [include], oldexcludes)
635 match = narrowspec.match(repo.root, [include], oldexcludes)
637 if not any(match(f) for f in localfiles):
636 if not any(match(f) for f in localfiles):
638 suggestedremovals.append(include)
637 suggestedremovals.append(include)
639 if suggestedremovals:
638 if suggestedremovals:
640 for s in suggestedremovals:
639 for s in suggestedremovals:
641 ui.status(b'%s\n' % s)
640 ui.status(b'%s\n' % s)
642 if (
641 if (
643 ui.promptchoice(
642 ui.promptchoice(
644 _(
643 _(
645 b'remove these unused includes (yn)?'
644 b'remove these unused includes (yn)?'
646 b'$$ &Yes $$ &No'
645 b'$$ &Yes $$ &No'
647 )
646 )
648 )
647 )
649 == 0
648 == 0
650 ):
649 ):
651 removedincludes.update(suggestedremovals)
650 removedincludes.update(suggestedremovals)
652 narrowing = True
651 narrowing = True
653 else:
652 else:
654 ui.status(_(b'found no unused includes\n'))
653 ui.status(_(b'found no unused includes\n'))
655
654
656 if narrowing:
655 if narrowing:
657 newincludes = oldincludes - removedincludes
656 newincludes = oldincludes - removedincludes
658 newexcludes = oldexcludes | addedexcludes
657 newexcludes = oldexcludes | addedexcludes
659 _narrow(
658 _narrow(
660 ui,
659 ui,
661 repo,
660 repo,
662 remote,
661 remote,
663 commoninc,
662 commoninc,
664 oldincludes,
663 oldincludes,
665 oldexcludes,
664 oldexcludes,
666 newincludes,
665 newincludes,
667 newexcludes,
666 newexcludes,
668 opts[b'force_delete_local_changes'],
667 opts[b'force_delete_local_changes'],
669 opts[b'backup'],
668 opts[b'backup'],
670 )
669 )
671 # _narrow() updated the narrowspec and _widen() below needs to
670 # _narrow() updated the narrowspec and _widen() below needs to
672 # use the updated values as its base (otherwise removed includes
671 # use the updated values as its base (otherwise removed includes
673 # and addedexcludes will be lost in the resulting narrowspec)
672 # and addedexcludes will be lost in the resulting narrowspec)
674 oldincludes = newincludes
673 oldincludes = newincludes
675 oldexcludes = newexcludes
674 oldexcludes = newexcludes
676
675
677 if widening:
676 if widening:
678 newincludes = oldincludes | addedincludes
677 newincludes = oldincludes | addedincludes
679 newexcludes = oldexcludes - removedexcludes
678 newexcludes = oldexcludes - removedexcludes
680 _widen(
679 _widen(
681 ui,
680 ui,
682 repo,
681 repo,
683 remote,
682 remote,
684 commoninc,
683 commoninc,
685 oldincludes,
684 oldincludes,
686 oldexcludes,
685 oldexcludes,
687 newincludes,
686 newincludes,
688 newexcludes,
687 newexcludes,
689 )
688 )
690 finally:
689 finally:
691 remote.close()
690 remote.close()
692
691
693 return 0
692 return 0
General Comments 0
You need to be logged in to leave comments. Login now