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