##// END OF EJS Templates
narrow: mark the critical chunks of narrowing/widening as unsafe...
Augie Fackler -
r38547:a1d5951e default
parent child Browse files
Show More
@@ -1,401 +1,403 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 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import itertools
9 import itertools
10
10
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial import (
12 from mercurial import (
13 changegroup,
13 changegroup,
14 cmdutil,
14 cmdutil,
15 commands,
15 commands,
16 discovery,
16 discovery,
17 error,
17 error,
18 exchange,
18 exchange,
19 extensions,
19 extensions,
20 hg,
20 hg,
21 merge,
21 merge,
22 narrowspec,
22 narrowspec,
23 node,
23 node,
24 pycompat,
24 pycompat,
25 registrar,
25 registrar,
26 repair,
26 repair,
27 repoview,
27 repoview,
28 util,
28 util,
29 )
29 )
30
30
31 from . import (
31 from . import (
32 narrowbundle2,
32 narrowbundle2,
33 )
33 )
34
34
35 table = {}
35 table = {}
36 command = registrar.command(table)
36 command = registrar.command(table)
37
37
38 def setup():
38 def setup():
39 """Wraps user-facing mercurial commands with narrow-aware versions."""
39 """Wraps user-facing mercurial commands with narrow-aware versions."""
40
40
41 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
41 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
42 entry[1].append(('', 'narrow', None,
42 entry[1].append(('', 'narrow', None,
43 _("create a narrow clone of select files")))
43 _("create a narrow clone of select files")))
44 entry[1].append(('', 'depth', '',
44 entry[1].append(('', 'depth', '',
45 _("limit the history fetched by distance from heads")))
45 _("limit the history fetched by distance from heads")))
46 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
46 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
47 if 'sparse' not in extensions.enabled():
47 if 'sparse' not in extensions.enabled():
48 entry[1].append(('', 'include', [],
48 entry[1].append(('', 'include', [],
49 _("specifically fetch this file/directory")))
49 _("specifically fetch this file/directory")))
50 entry[1].append(
50 entry[1].append(
51 ('', 'exclude', [],
51 ('', 'exclude', [],
52 _("do not fetch this file/directory, even if included")))
52 _("do not fetch this file/directory, even if included")))
53
53
54 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
54 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
55 entry[1].append(('', 'depth', '',
55 entry[1].append(('', 'depth', '',
56 _("limit the history fetched by distance from heads")))
56 _("limit the history fetched by distance from heads")))
57
57
58 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
58 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
59
59
60 def expandpull(pullop, includepats, excludepats):
60 def expandpull(pullop, includepats, excludepats):
61 if not narrowspec.needsexpansion(includepats):
61 if not narrowspec.needsexpansion(includepats):
62 return includepats, excludepats
62 return includepats, excludepats
63
63
64 heads = pullop.heads or pullop.rheads
64 heads = pullop.heads or pullop.rheads
65 includepats, excludepats = pullop.remote.expandnarrow(
65 includepats, excludepats = pullop.remote.expandnarrow(
66 includepats, excludepats, heads)
66 includepats, excludepats, heads)
67 pullop.repo.ui.debug('Expanded narrowspec to inc=%s, exc=%s\n' % (
67 pullop.repo.ui.debug('Expanded narrowspec to inc=%s, exc=%s\n' % (
68 includepats, excludepats))
68 includepats, excludepats))
69 return set(includepats), set(excludepats)
69 return set(includepats), set(excludepats)
70
70
71 def clonenarrowcmd(orig, ui, repo, *args, **opts):
71 def clonenarrowcmd(orig, ui, repo, *args, **opts):
72 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
72 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
73 opts = pycompat.byteskwargs(opts)
73 opts = pycompat.byteskwargs(opts)
74 wrappedextraprepare = util.nullcontextmanager()
74 wrappedextraprepare = util.nullcontextmanager()
75 opts_narrow = opts['narrow']
75 opts_narrow = opts['narrow']
76 if opts_narrow:
76 if opts_narrow:
77 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
77 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
78 # Create narrow spec patterns from clone flags
78 # Create narrow spec patterns from clone flags
79 includepats = narrowspec.parsepatterns(opts['include'])
79 includepats = narrowspec.parsepatterns(opts['include'])
80 excludepats = narrowspec.parsepatterns(opts['exclude'])
80 excludepats = narrowspec.parsepatterns(opts['exclude'])
81
81
82 # If necessary, ask the server to expand the narrowspec.
82 # If necessary, ask the server to expand the narrowspec.
83 includepats, excludepats = expandpull(
83 includepats, excludepats = expandpull(
84 pullop, includepats, excludepats)
84 pullop, includepats, excludepats)
85
85
86 if not includepats and excludepats:
86 if not includepats and excludepats:
87 # If nothing was included, we assume the user meant to include
87 # If nothing was included, we assume the user meant to include
88 # everything, except what they asked to exclude.
88 # everything, except what they asked to exclude.
89 includepats = {'path:.'}
89 includepats = {'path:.'}
90
90
91 pullop.repo.setnarrowpats(includepats, excludepats)
91 pullop.repo.setnarrowpats(includepats, excludepats)
92
92
93 # This will populate 'includepats' etc with the values from the
93 # This will populate 'includepats' etc with the values from the
94 # narrowspec we just saved.
94 # narrowspec we just saved.
95 orig(pullop, kwargs)
95 orig(pullop, kwargs)
96
96
97 if opts.get('depth'):
97 if opts.get('depth'):
98 kwargs['depth'] = opts['depth']
98 kwargs['depth'] = opts['depth']
99 wrappedextraprepare = extensions.wrappedfunction(exchange,
99 wrappedextraprepare = extensions.wrappedfunction(exchange,
100 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
100 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
101
101
102 def pullnarrow(orig, repo, *args, **kwargs):
102 def pullnarrow(orig, repo, *args, **kwargs):
103 if opts_narrow:
103 if opts_narrow:
104 repo.requirements.add(changegroup.NARROW_REQUIREMENT)
104 repo.requirements.add(changegroup.NARROW_REQUIREMENT)
105 repo._writerequirements()
105 repo._writerequirements()
106
106
107 return orig(repo, *args, **kwargs)
107 return orig(repo, *args, **kwargs)
108
108
109 wrappedpull = extensions.wrappedfunction(exchange, 'pull', pullnarrow)
109 wrappedpull = extensions.wrappedfunction(exchange, 'pull', pullnarrow)
110
110
111 with wrappedextraprepare, wrappedpull:
111 with wrappedextraprepare, wrappedpull:
112 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
112 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
113
113
114 def pullnarrowcmd(orig, ui, repo, *args, **opts):
114 def pullnarrowcmd(orig, ui, repo, *args, **opts):
115 """Wraps pull command to allow modifying narrow spec."""
115 """Wraps pull command to allow modifying narrow spec."""
116 wrappedextraprepare = util.nullcontextmanager()
116 wrappedextraprepare = util.nullcontextmanager()
117 if changegroup.NARROW_REQUIREMENT in repo.requirements:
117 if changegroup.NARROW_REQUIREMENT in repo.requirements:
118
118
119 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
119 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
120 orig(pullop, kwargs)
120 orig(pullop, kwargs)
121 if opts.get(r'depth'):
121 if opts.get(r'depth'):
122 kwargs['depth'] = opts[r'depth']
122 kwargs['depth'] = opts[r'depth']
123 wrappedextraprepare = extensions.wrappedfunction(exchange,
123 wrappedextraprepare = extensions.wrappedfunction(exchange,
124 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
124 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
125
125
126 with wrappedextraprepare:
126 with wrappedextraprepare:
127 return orig(ui, repo, *args, **opts)
127 return orig(ui, repo, *args, **opts)
128
128
129 def archivenarrowcmd(orig, ui, repo, *args, **opts):
129 def archivenarrowcmd(orig, ui, repo, *args, **opts):
130 """Wraps archive command to narrow the default includes."""
130 """Wraps archive command to narrow the default includes."""
131 if changegroup.NARROW_REQUIREMENT in repo.requirements:
131 if changegroup.NARROW_REQUIREMENT in repo.requirements:
132 repo_includes, repo_excludes = repo.narrowpats
132 repo_includes, repo_excludes = repo.narrowpats
133 includes = set(opts.get(r'include', []))
133 includes = set(opts.get(r'include', []))
134 excludes = set(opts.get(r'exclude', []))
134 excludes = set(opts.get(r'exclude', []))
135 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
135 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
136 includes, excludes, repo_includes, repo_excludes)
136 includes, excludes, repo_includes, repo_excludes)
137 if includes:
137 if includes:
138 opts[r'include'] = includes
138 opts[r'include'] = includes
139 if excludes:
139 if excludes:
140 opts[r'exclude'] = excludes
140 opts[r'exclude'] = excludes
141 return orig(ui, repo, *args, **opts)
141 return orig(ui, repo, *args, **opts)
142
142
143 def pullbundle2extraprepare(orig, pullop, kwargs):
143 def pullbundle2extraprepare(orig, pullop, kwargs):
144 repo = pullop.repo
144 repo = pullop.repo
145 if changegroup.NARROW_REQUIREMENT not in repo.requirements:
145 if changegroup.NARROW_REQUIREMENT not in repo.requirements:
146 return orig(pullop, kwargs)
146 return orig(pullop, kwargs)
147
147
148 if narrowbundle2.NARROWCAP not in pullop.remotebundle2caps:
148 if narrowbundle2.NARROWCAP not in pullop.remotebundle2caps:
149 raise error.Abort(_("server doesn't support narrow clones"))
149 raise error.Abort(_("server doesn't support narrow clones"))
150 orig(pullop, kwargs)
150 orig(pullop, kwargs)
151 kwargs['narrow'] = True
151 kwargs['narrow'] = True
152 include, exclude = repo.narrowpats
152 include, exclude = repo.narrowpats
153 kwargs['oldincludepats'] = include
153 kwargs['oldincludepats'] = include
154 kwargs['oldexcludepats'] = exclude
154 kwargs['oldexcludepats'] = exclude
155 kwargs['includepats'] = include
155 kwargs['includepats'] = include
156 kwargs['excludepats'] = exclude
156 kwargs['excludepats'] = exclude
157 kwargs['known'] = [node.hex(ctx.node()) for ctx in
157 kwargs['known'] = [node.hex(ctx.node()) for ctx in
158 repo.set('::%ln', pullop.common)
158 repo.set('::%ln', pullop.common)
159 if ctx.node() != node.nullid]
159 if ctx.node() != node.nullid]
160 if not kwargs['known']:
160 if not kwargs['known']:
161 # Mercurial serialized an empty list as '' and deserializes it as
161 # Mercurial serialized an empty list as '' and deserializes it as
162 # [''], so delete it instead to avoid handling the empty string on the
162 # [''], so delete it instead to avoid handling the empty string on the
163 # server.
163 # server.
164 del kwargs['known']
164 del kwargs['known']
165
165
166 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
166 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
167 pullbundle2extraprepare)
167 pullbundle2extraprepare)
168
168
169 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
169 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
170 newincludes, newexcludes, force):
170 newincludes, newexcludes, force):
171 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
171 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
172 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
172 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
173
173
174 # This is essentially doing "hg outgoing" to find all local-only
174 # This is essentially doing "hg outgoing" to find all local-only
175 # commits. We will then check that the local-only commits don't
175 # commits. We will then check that the local-only commits don't
176 # have any changes to files that will be untracked.
176 # have any changes to files that will be untracked.
177 unfi = repo.unfiltered()
177 unfi = repo.unfiltered()
178 outgoing = discovery.findcommonoutgoing(unfi, remote,
178 outgoing = discovery.findcommonoutgoing(unfi, remote,
179 commoninc=commoninc)
179 commoninc=commoninc)
180 ui.status(_('looking for local changes to affected paths\n'))
180 ui.status(_('looking for local changes to affected paths\n'))
181 localnodes = []
181 localnodes = []
182 for n in itertools.chain(outgoing.missing, outgoing.excluded):
182 for n in itertools.chain(outgoing.missing, outgoing.excluded):
183 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
183 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
184 localnodes.append(n)
184 localnodes.append(n)
185 revstostrip = unfi.revs('descendants(%ln)', localnodes)
185 revstostrip = unfi.revs('descendants(%ln)', localnodes)
186 hiddenrevs = repoview.filterrevs(repo, 'visible')
186 hiddenrevs = repoview.filterrevs(repo, 'visible')
187 visibletostrip = list(repo.changelog.node(r)
187 visibletostrip = list(repo.changelog.node(r)
188 for r in (revstostrip - hiddenrevs))
188 for r in (revstostrip - hiddenrevs))
189 if visibletostrip:
189 if visibletostrip:
190 ui.status(_('The following changeset(s) or their ancestors have '
190 ui.status(_('The following changeset(s) or their ancestors have '
191 'local changes not on the remote:\n'))
191 'local changes not on the remote:\n'))
192 maxnodes = 10
192 maxnodes = 10
193 if ui.verbose or len(visibletostrip) <= maxnodes:
193 if ui.verbose or len(visibletostrip) <= maxnodes:
194 for n in visibletostrip:
194 for n in visibletostrip:
195 ui.status('%s\n' % node.short(n))
195 ui.status('%s\n' % node.short(n))
196 else:
196 else:
197 for n in visibletostrip[:maxnodes]:
197 for n in visibletostrip[:maxnodes]:
198 ui.status('%s\n' % node.short(n))
198 ui.status('%s\n' % node.short(n))
199 ui.status(_('...and %d more, use --verbose to list all\n') %
199 ui.status(_('...and %d more, use --verbose to list all\n') %
200 (len(visibletostrip) - maxnodes))
200 (len(visibletostrip) - maxnodes))
201 if not force:
201 if not force:
202 raise error.Abort(_('local changes found'),
202 raise error.Abort(_('local changes found'),
203 hint=_('use --force-delete-local-changes to '
203 hint=_('use --force-delete-local-changes to '
204 'ignore'))
204 'ignore'))
205
205
206 with ui.uninterruptable():
206 if revstostrip:
207 if revstostrip:
207 tostrip = [unfi.changelog.node(r) for r in revstostrip]
208 tostrip = [unfi.changelog.node(r) for r in revstostrip]
208 if repo['.'].node() in tostrip:
209 if repo['.'].node() in tostrip:
209 # stripping working copy, so move to a different commit first
210 # stripping working copy, so move to a different commit first
210 urev = max(repo.revs('(::%n) - %ln + null',
211 urev = max(repo.revs('(::%n) - %ln + null',
211 repo['.'].node(), visibletostrip))
212 repo['.'].node(), visibletostrip))
212 hg.clean(repo, urev)
213 hg.clean(repo, urev)
213 repair.strip(ui, unfi, tostrip, topic='narrow')
214 repair.strip(ui, unfi, tostrip, topic='narrow')
214
215
215 todelete = []
216 todelete = []
216 for f, f2, size in repo.store.datafiles():
217 for f, f2, size in repo.store.datafiles():
217 if f.startswith('data/'):
218 if f.startswith('data/'):
218 file = f[5:-2]
219 file = f[5:-2]
219 if not newmatch(file):
220 if not newmatch(file):
220 todelete.append(f)
221 todelete.append(f)
221 elif f.startswith('meta/'):
222 elif f.startswith('meta/'):
222 dir = f[5:-13]
223 dir = f[5:-13]
223 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
224 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
224 include = True
225 include = True
225 for d in dirs:
226 for d in dirs:
226 visit = newmatch.visitdir(d)
227 visit = newmatch.visitdir(d)
227 if not visit:
228 if not visit:
228 include = False
229 include = False
229 break
230 break
230 if visit == 'all':
231 if visit == 'all':
231 break
232 break
232 if not include:
233 if not include:
233 todelete.append(f)
234 todelete.append(f)
234
235
235 repo.destroying()
236 repo.destroying()
236
237
237 with repo.transaction("narrowing"):
238 with repo.transaction("narrowing"):
238 for f in todelete:
239 for f in todelete:
239 ui.status(_('deleting %s\n') % f)
240 ui.status(_('deleting %s\n') % f)
240 util.unlinkpath(repo.svfs.join(f))
241 util.unlinkpath(repo.svfs.join(f))
241 repo.store.markremoved(f)
242 repo.store.markremoved(f)
242
243
243 for f in repo.dirstate:
244 for f in repo.dirstate:
244 if not newmatch(f):
245 if not newmatch(f):
245 repo.dirstate.drop(f)
246 repo.dirstate.drop(f)
246 repo.wvfs.unlinkpath(f)
247 repo.wvfs.unlinkpath(f)
247 repo.setnarrowpats(newincludes, newexcludes)
248 repo.setnarrowpats(newincludes, newexcludes)
248
249
249 repo.destroyed()
250 repo.destroyed()
250
251
251 def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
252 def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
252 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
253 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
253
254
254 # TODO(martinvonz): Get expansion working with widening/narrowing.
255 # TODO(martinvonz): Get expansion working with widening/narrowing.
255 if narrowspec.needsexpansion(newincludes):
256 if narrowspec.needsexpansion(newincludes):
256 raise error.Abort('Expansion not yet supported on pull')
257 raise error.Abort('Expansion not yet supported on pull')
257
258
258 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
259 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
259 orig(pullop, kwargs)
260 orig(pullop, kwargs)
260 # The old{in,ex}cludepats have already been set by orig()
261 # The old{in,ex}cludepats have already been set by orig()
261 kwargs['includepats'] = newincludes
262 kwargs['includepats'] = newincludes
262 kwargs['excludepats'] = newexcludes
263 kwargs['excludepats'] = newexcludes
263 wrappedextraprepare = extensions.wrappedfunction(exchange,
264 wrappedextraprepare = extensions.wrappedfunction(exchange,
264 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
265 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
265
266
266 # define a function that narrowbundle2 can call after creating the
267 # define a function that narrowbundle2 can call after creating the
267 # backup bundle, but before applying the bundle from the server
268 # backup bundle, but before applying the bundle from the server
268 def setnewnarrowpats():
269 def setnewnarrowpats():
269 repo.setnarrowpats(newincludes, newexcludes)
270 repo.setnarrowpats(newincludes, newexcludes)
270 repo.setnewnarrowpats = setnewnarrowpats
271 repo.setnewnarrowpats = setnewnarrowpats
271
272
273 with ui.uninterruptable():
272 ds = repo.dirstate
274 ds = repo.dirstate
273 p1, p2 = ds.p1(), ds.p2()
275 p1, p2 = ds.p1(), ds.p2()
274 with ds.parentchange():
276 with ds.parentchange():
275 ds.setparents(node.nullid, node.nullid)
277 ds.setparents(node.nullid, node.nullid)
276 common = commoninc[0]
278 common = commoninc[0]
277 with wrappedextraprepare:
279 with wrappedextraprepare:
278 exchange.pull(repo, remote, heads=common)
280 exchange.pull(repo, remote, heads=common)
279 with ds.parentchange():
281 with ds.parentchange():
280 ds.setparents(p1, p2)
282 ds.setparents(p1, p2)
281
283
282 actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
284 actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
283 addgaction = actions['g'].append
285 addgaction = actions['g'].append
284
286
285 mf = repo['.'].manifest().matches(newmatch)
287 mf = repo['.'].manifest().matches(newmatch)
286 for f, fn in mf.iteritems():
288 for f, fn in mf.iteritems():
287 if f not in repo.dirstate:
289 if f not in repo.dirstate:
288 addgaction((f, (mf.flags(f), False),
290 addgaction((f, (mf.flags(f), False),
289 "add from widened narrow clone"))
291 "add from widened narrow clone"))
290
292
291 merge.applyupdates(repo, actions, wctx=repo[None],
293 merge.applyupdates(repo, actions, wctx=repo[None],
292 mctx=repo['.'], overwrite=False)
294 mctx=repo['.'], overwrite=False)
293 merge.recordupdates(repo, actions, branchmerge=False)
295 merge.recordupdates(repo, actions, branchmerge=False)
294
296
295 # TODO(rdamazio): Make new matcher format and update description
297 # TODO(rdamazio): Make new matcher format and update description
296 @command('tracked',
298 @command('tracked',
297 [('', 'addinclude', [], _('new paths to include')),
299 [('', 'addinclude', [], _('new paths to include')),
298 ('', 'removeinclude', [], _('old paths to no longer include')),
300 ('', 'removeinclude', [], _('old paths to no longer include')),
299 ('', 'addexclude', [], _('new paths to exclude')),
301 ('', 'addexclude', [], _('new paths to exclude')),
300 ('', 'removeexclude', [], _('old paths to no longer exclude')),
302 ('', 'removeexclude', [], _('old paths to no longer exclude')),
301 ('', 'clear', False, _('whether to replace the existing narrowspec')),
303 ('', 'clear', False, _('whether to replace the existing narrowspec')),
302 ('', 'force-delete-local-changes', False,
304 ('', 'force-delete-local-changes', False,
303 _('forces deletion of local changes when narrowing')),
305 _('forces deletion of local changes when narrowing')),
304 ] + commands.remoteopts,
306 ] + commands.remoteopts,
305 _('[OPTIONS]... [REMOTE]'),
307 _('[OPTIONS]... [REMOTE]'),
306 inferrepo=True)
308 inferrepo=True)
307 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
309 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
308 """show or change the current narrowspec
310 """show or change the current narrowspec
309
311
310 With no argument, shows the current narrowspec entries, one per line. Each
312 With no argument, shows the current narrowspec entries, one per line. Each
311 line will be prefixed with 'I' or 'X' for included or excluded patterns,
313 line will be prefixed with 'I' or 'X' for included or excluded patterns,
312 respectively.
314 respectively.
313
315
314 The narrowspec is comprised of expressions to match remote files and/or
316 The narrowspec is comprised of expressions to match remote files and/or
315 directories that should be pulled into your client.
317 directories that should be pulled into your client.
316 The narrowspec has *include* and *exclude* expressions, with excludes always
318 The narrowspec has *include* and *exclude* expressions, with excludes always
317 trumping includes: that is, if a file matches an exclude expression, it will
319 trumping includes: that is, if a file matches an exclude expression, it will
318 be excluded even if it also matches an include expression.
320 be excluded even if it also matches an include expression.
319 Excluding files that were never included has no effect.
321 Excluding files that were never included has no effect.
320
322
321 Each included or excluded entry is in the format described by
323 Each included or excluded entry is in the format described by
322 'hg help patterns'.
324 'hg help patterns'.
323
325
324 The options allow you to add or remove included and excluded expressions.
326 The options allow you to add or remove included and excluded expressions.
325
327
326 If --clear is specified, then all previous includes and excludes are DROPPED
328 If --clear is specified, then all previous includes and excludes are DROPPED
327 and replaced by the new ones specified to --addinclude and --addexclude.
329 and replaced by the new ones specified to --addinclude and --addexclude.
328 If --clear is specified without any further options, the narrowspec will be
330 If --clear is specified without any further options, the narrowspec will be
329 empty and will not match any files.
331 empty and will not match any files.
330 """
332 """
331 opts = pycompat.byteskwargs(opts)
333 opts = pycompat.byteskwargs(opts)
332 if changegroup.NARROW_REQUIREMENT not in repo.requirements:
334 if changegroup.NARROW_REQUIREMENT not in repo.requirements:
333 ui.warn(_('The narrow command is only supported on respositories cloned'
335 ui.warn(_('The narrow command is only supported on respositories cloned'
334 ' with --narrow.\n'))
336 ' with --narrow.\n'))
335 return 1
337 return 1
336
338
337 # Before supporting, decide whether it "hg tracked --clear" should mean
339 # Before supporting, decide whether it "hg tracked --clear" should mean
338 # tracking no paths or all paths.
340 # tracking no paths or all paths.
339 if opts['clear']:
341 if opts['clear']:
340 ui.warn(_('The --clear option is not yet supported.\n'))
342 ui.warn(_('The --clear option is not yet supported.\n'))
341 return 1
343 return 1
342
344
343 if narrowspec.needsexpansion(opts['addinclude'] + opts['addexclude']):
345 if narrowspec.needsexpansion(opts['addinclude'] + opts['addexclude']):
344 raise error.Abort('Expansion not yet supported on widen/narrow')
346 raise error.Abort('Expansion not yet supported on widen/narrow')
345
347
346 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
348 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
347 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
349 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
348 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
350 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
349 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
351 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
350 widening = addedincludes or removedexcludes
352 widening = addedincludes or removedexcludes
351 narrowing = removedincludes or addedexcludes
353 narrowing = removedincludes or addedexcludes
352 only_show = not widening and not narrowing
354 only_show = not widening and not narrowing
353
355
354 # Only print the current narrowspec.
356 # Only print the current narrowspec.
355 if only_show:
357 if only_show:
356 include, exclude = repo.narrowpats
358 include, exclude = repo.narrowpats
357
359
358 ui.pager('tracked')
360 ui.pager('tracked')
359 fm = ui.formatter('narrow', opts)
361 fm = ui.formatter('narrow', opts)
360 for i in sorted(include):
362 for i in sorted(include):
361 fm.startitem()
363 fm.startitem()
362 fm.write('status', '%s ', 'I', label='narrow.included')
364 fm.write('status', '%s ', 'I', label='narrow.included')
363 fm.write('pat', '%s\n', i, label='narrow.included')
365 fm.write('pat', '%s\n', i, label='narrow.included')
364 for i in sorted(exclude):
366 for i in sorted(exclude):
365 fm.startitem()
367 fm.startitem()
366 fm.write('status', '%s ', 'X', label='narrow.excluded')
368 fm.write('status', '%s ', 'X', label='narrow.excluded')
367 fm.write('pat', '%s\n', i, label='narrow.excluded')
369 fm.write('pat', '%s\n', i, label='narrow.excluded')
368 fm.end()
370 fm.end()
369 return 0
371 return 0
370
372
371 with repo.wlock(), repo.lock():
373 with repo.wlock(), repo.lock():
372 cmdutil.bailifchanged(repo)
374 cmdutil.bailifchanged(repo)
373
375
374 # Find the revisions we have in common with the remote. These will
376 # Find the revisions we have in common with the remote. These will
375 # be used for finding local-only changes for narrowing. They will
377 # be used for finding local-only changes for narrowing. They will
376 # also define the set of revisions to update for widening.
378 # also define the set of revisions to update for widening.
377 remotepath = ui.expandpath(remotepath or 'default')
379 remotepath = ui.expandpath(remotepath or 'default')
378 url, branches = hg.parseurl(remotepath)
380 url, branches = hg.parseurl(remotepath)
379 ui.status(_('comparing with %s\n') % util.hidepassword(url))
381 ui.status(_('comparing with %s\n') % util.hidepassword(url))
380 remote = hg.peer(repo, opts, url)
382 remote = hg.peer(repo, opts, url)
381 commoninc = discovery.findcommonincoming(repo, remote)
383 commoninc = discovery.findcommonincoming(repo, remote)
382
384
383 oldincludes, oldexcludes = repo.narrowpats
385 oldincludes, oldexcludes = repo.narrowpats
384 if narrowing:
386 if narrowing:
385 newincludes = oldincludes - removedincludes
387 newincludes = oldincludes - removedincludes
386 newexcludes = oldexcludes | addedexcludes
388 newexcludes = oldexcludes | addedexcludes
387 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
389 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
388 newincludes, newexcludes,
390 newincludes, newexcludes,
389 opts['force_delete_local_changes'])
391 opts['force_delete_local_changes'])
390 # _narrow() updated the narrowspec and _widen() below needs to
392 # _narrow() updated the narrowspec and _widen() below needs to
391 # use the updated values as its base (otherwise removed includes
393 # use the updated values as its base (otherwise removed includes
392 # and addedexcludes will be lost in the resulting narrowspec)
394 # and addedexcludes will be lost in the resulting narrowspec)
393 oldincludes = newincludes
395 oldincludes = newincludes
394 oldexcludes = newexcludes
396 oldexcludes = newexcludes
395
397
396 if widening:
398 if widening:
397 newincludes = oldincludes | addedincludes
399 newincludes = oldincludes | addedincludes
398 newexcludes = oldexcludes - removedexcludes
400 newexcludes = oldexcludes - removedexcludes
399 _widen(ui, repo, remote, commoninc, newincludes, newexcludes)
401 _widen(ui, repo, remote, commoninc, newincludes, newexcludes)
400
402
401 return 0
403 return 0
General Comments 0
You need to be logged in to leave comments. Login now