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