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