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