##// END OF EJS Templates
subrepo: fix a crash when archiving an svn or git subrepo...
Matt Harbison -
r44467:7ca8aa88 stable
parent child Browse files
Show More
@@ -1,1808 +1,1825 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import contextlib
12 import contextlib
13 import copy
13 import copy
14 import os
14 import os
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17
17
18 from mercurial.pycompat import open
18 from mercurial.pycompat import open
19
19
20 from mercurial.hgweb import webcommands
20 from mercurial.hgweb import webcommands
21
21
22 from mercurial import (
22 from mercurial import (
23 archival,
23 archival,
24 cmdutil,
24 cmdutil,
25 copies as copiesmod,
25 copies as copiesmod,
26 error,
26 error,
27 exchange,
27 exchange,
28 extensions,
28 extensions,
29 exthelper,
29 exthelper,
30 filemerge,
30 filemerge,
31 hg,
31 hg,
32 logcmdutil,
32 logcmdutil,
33 match as matchmod,
33 match as matchmod,
34 merge,
34 merge,
35 pathutil,
35 pathutil,
36 pycompat,
36 pycompat,
37 scmutil,
37 scmutil,
38 smartset,
38 smartset,
39 subrepo,
39 subrepo,
40 upgrade,
40 upgrade,
41 url as urlmod,
41 url as urlmod,
42 util,
42 util,
43 )
43 )
44
44
45 from . import (
45 from . import (
46 lfcommands,
46 lfcommands,
47 lfutil,
47 lfutil,
48 storefactory,
48 storefactory,
49 )
49 )
50
50
51 eh = exthelper.exthelper()
51 eh = exthelper.exthelper()
52
52
53 # -- Utility functions: commonly/repeatedly needed functionality ---------------
53 # -- Utility functions: commonly/repeatedly needed functionality ---------------
54
54
55
55
56 def composelargefilematcher(match, manifest):
56 def composelargefilematcher(match, manifest):
57 '''create a matcher that matches only the largefiles in the original
57 '''create a matcher that matches only the largefiles in the original
58 matcher'''
58 matcher'''
59 m = copy.copy(match)
59 m = copy.copy(match)
60 lfile = lambda f: lfutil.standin(f) in manifest
60 lfile = lambda f: lfutil.standin(f) in manifest
61 m._files = [lf for lf in m._files if lfile(lf)]
61 m._files = [lf for lf in m._files if lfile(lf)]
62 m._fileset = set(m._files)
62 m._fileset = set(m._files)
63 m.always = lambda: False
63 m.always = lambda: False
64 origmatchfn = m.matchfn
64 origmatchfn = m.matchfn
65 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
65 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
66 return m
66 return m
67
67
68
68
69 def composenormalfilematcher(match, manifest, exclude=None):
69 def composenormalfilematcher(match, manifest, exclude=None):
70 excluded = set()
70 excluded = set()
71 if exclude is not None:
71 if exclude is not None:
72 excluded.update(exclude)
72 excluded.update(exclude)
73
73
74 m = copy.copy(match)
74 m = copy.copy(match)
75 notlfile = lambda f: not (
75 notlfile = lambda f: not (
76 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
76 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
77 )
77 )
78 m._files = [lf for lf in m._files if notlfile(lf)]
78 m._files = [lf for lf in m._files if notlfile(lf)]
79 m._fileset = set(m._files)
79 m._fileset = set(m._files)
80 m.always = lambda: False
80 m.always = lambda: False
81 origmatchfn = m.matchfn
81 origmatchfn = m.matchfn
82 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
82 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
83 return m
83 return m
84
84
85
85
86 def addlargefiles(ui, repo, isaddremove, matcher, uipathfn, **opts):
86 def addlargefiles(ui, repo, isaddremove, matcher, uipathfn, **opts):
87 large = opts.get(r'large')
87 large = opts.get(r'large')
88 lfsize = lfutil.getminsize(
88 lfsize = lfutil.getminsize(
89 ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize')
89 ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize')
90 )
90 )
91
91
92 lfmatcher = None
92 lfmatcher = None
93 if lfutil.islfilesrepo(repo):
93 if lfutil.islfilesrepo(repo):
94 lfpats = ui.configlist(lfutil.longname, b'patterns')
94 lfpats = ui.configlist(lfutil.longname, b'patterns')
95 if lfpats:
95 if lfpats:
96 lfmatcher = matchmod.match(repo.root, b'', list(lfpats))
96 lfmatcher = matchmod.match(repo.root, b'', list(lfpats))
97
97
98 lfnames = []
98 lfnames = []
99 m = matcher
99 m = matcher
100
100
101 wctx = repo[None]
101 wctx = repo[None]
102 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
102 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
103 exact = m.exact(f)
103 exact = m.exact(f)
104 lfile = lfutil.standin(f) in wctx
104 lfile = lfutil.standin(f) in wctx
105 nfile = f in wctx
105 nfile = f in wctx
106 exists = lfile or nfile
106 exists = lfile or nfile
107
107
108 # Don't warn the user when they attempt to add a normal tracked file.
108 # Don't warn the user when they attempt to add a normal tracked file.
109 # The normal add code will do that for us.
109 # The normal add code will do that for us.
110 if exact and exists:
110 if exact and exists:
111 if lfile:
111 if lfile:
112 ui.warn(_(b'%s already a largefile\n') % uipathfn(f))
112 ui.warn(_(b'%s already a largefile\n') % uipathfn(f))
113 continue
113 continue
114
114
115 if (exact or not exists) and not lfutil.isstandin(f):
115 if (exact or not exists) and not lfutil.isstandin(f):
116 # In case the file was removed previously, but not committed
116 # In case the file was removed previously, but not committed
117 # (issue3507)
117 # (issue3507)
118 if not repo.wvfs.exists(f):
118 if not repo.wvfs.exists(f):
119 continue
119 continue
120
120
121 abovemin = (
121 abovemin = (
122 lfsize and repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024
122 lfsize and repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024
123 )
123 )
124 if large or abovemin or (lfmatcher and lfmatcher(f)):
124 if large or abovemin or (lfmatcher and lfmatcher(f)):
125 lfnames.append(f)
125 lfnames.append(f)
126 if ui.verbose or not exact:
126 if ui.verbose or not exact:
127 ui.status(_(b'adding %s as a largefile\n') % uipathfn(f))
127 ui.status(_(b'adding %s as a largefile\n') % uipathfn(f))
128
128
129 bad = []
129 bad = []
130
130
131 # Need to lock, otherwise there could be a race condition between
131 # Need to lock, otherwise there could be a race condition between
132 # when standins are created and added to the repo.
132 # when standins are created and added to the repo.
133 with repo.wlock():
133 with repo.wlock():
134 if not opts.get(r'dry_run'):
134 if not opts.get(r'dry_run'):
135 standins = []
135 standins = []
136 lfdirstate = lfutil.openlfdirstate(ui, repo)
136 lfdirstate = lfutil.openlfdirstate(ui, repo)
137 for f in lfnames:
137 for f in lfnames:
138 standinname = lfutil.standin(f)
138 standinname = lfutil.standin(f)
139 lfutil.writestandin(
139 lfutil.writestandin(
140 repo,
140 repo,
141 standinname,
141 standinname,
142 hash=b'',
142 hash=b'',
143 executable=lfutil.getexecutable(repo.wjoin(f)),
143 executable=lfutil.getexecutable(repo.wjoin(f)),
144 )
144 )
145 standins.append(standinname)
145 standins.append(standinname)
146 if lfdirstate[f] == b'r':
146 if lfdirstate[f] == b'r':
147 lfdirstate.normallookup(f)
147 lfdirstate.normallookup(f)
148 else:
148 else:
149 lfdirstate.add(f)
149 lfdirstate.add(f)
150 lfdirstate.write()
150 lfdirstate.write()
151 bad += [
151 bad += [
152 lfutil.splitstandin(f)
152 lfutil.splitstandin(f)
153 for f in repo[None].add(standins)
153 for f in repo[None].add(standins)
154 if f in m.files()
154 if f in m.files()
155 ]
155 ]
156
156
157 added = [f for f in lfnames if f not in bad]
157 added = [f for f in lfnames if f not in bad]
158 return added, bad
158 return added, bad
159
159
160
160
161 @contextlib.contextmanager
161 @contextlib.contextmanager
162 def lfstatus(repo):
162 def lfstatus(repo):
163 oldvalue = getattr(repo, 'lfstatus', False)
163 oldvalue = getattr(repo, 'lfstatus', False)
164 repo.lfstatus = True
164 repo.lfstatus = True
165 try:
165 try:
166 yield
166 yield
167 finally:
167 finally:
168 repo.lfstatus = oldvalue
168 repo.lfstatus = oldvalue
169
169
170
170
171 def removelargefiles(ui, repo, isaddremove, matcher, uipathfn, dryrun, **opts):
171 def removelargefiles(ui, repo, isaddremove, matcher, uipathfn, dryrun, **opts):
172 after = opts.get(r'after')
172 after = opts.get(r'after')
173 m = composelargefilematcher(matcher, repo[None].manifest())
173 m = composelargefilematcher(matcher, repo[None].manifest())
174 with lfstatus(repo):
174 with lfstatus(repo):
175 s = repo.status(match=m, clean=not isaddremove)
175 s = repo.status(match=m, clean=not isaddremove)
176 manifest = repo[None].manifest()
176 manifest = repo[None].manifest()
177 modified, added, deleted, clean = [
177 modified, added, deleted, clean = [
178 [f for f in list if lfutil.standin(f) in manifest]
178 [f for f in list if lfutil.standin(f) in manifest]
179 for list in (s.modified, s.added, s.deleted, s.clean)
179 for list in (s.modified, s.added, s.deleted, s.clean)
180 ]
180 ]
181
181
182 def warn(files, msg):
182 def warn(files, msg):
183 for f in files:
183 for f in files:
184 ui.warn(msg % uipathfn(f))
184 ui.warn(msg % uipathfn(f))
185 return int(len(files) > 0)
185 return int(len(files) > 0)
186
186
187 if after:
187 if after:
188 remove = deleted
188 remove = deleted
189 result = warn(
189 result = warn(
190 modified + added + clean, _(b'not removing %s: file still exists\n')
190 modified + added + clean, _(b'not removing %s: file still exists\n')
191 )
191 )
192 else:
192 else:
193 remove = deleted + clean
193 remove = deleted + clean
194 result = warn(
194 result = warn(
195 modified,
195 modified,
196 _(
196 _(
197 b'not removing %s: file is modified (use -f'
197 b'not removing %s: file is modified (use -f'
198 b' to force removal)\n'
198 b' to force removal)\n'
199 ),
199 ),
200 )
200 )
201 result = (
201 result = (
202 warn(
202 warn(
203 added,
203 added,
204 _(
204 _(
205 b'not removing %s: file has been marked for add'
205 b'not removing %s: file has been marked for add'
206 b' (use forget to undo)\n'
206 b' (use forget to undo)\n'
207 ),
207 ),
208 )
208 )
209 or result
209 or result
210 )
210 )
211
211
212 # Need to lock because standin files are deleted then removed from the
212 # Need to lock because standin files are deleted then removed from the
213 # repository and we could race in-between.
213 # repository and we could race in-between.
214 with repo.wlock():
214 with repo.wlock():
215 lfdirstate = lfutil.openlfdirstate(ui, repo)
215 lfdirstate = lfutil.openlfdirstate(ui, repo)
216 for f in sorted(remove):
216 for f in sorted(remove):
217 if ui.verbose or not m.exact(f):
217 if ui.verbose or not m.exact(f):
218 ui.status(_(b'removing %s\n') % uipathfn(f))
218 ui.status(_(b'removing %s\n') % uipathfn(f))
219
219
220 if not dryrun:
220 if not dryrun:
221 if not after:
221 if not after:
222 repo.wvfs.unlinkpath(f, ignoremissing=True)
222 repo.wvfs.unlinkpath(f, ignoremissing=True)
223
223
224 if dryrun:
224 if dryrun:
225 return result
225 return result
226
226
227 remove = [lfutil.standin(f) for f in remove]
227 remove = [lfutil.standin(f) for f in remove]
228 # If this is being called by addremove, let the original addremove
228 # If this is being called by addremove, let the original addremove
229 # function handle this.
229 # function handle this.
230 if not isaddremove:
230 if not isaddremove:
231 for f in remove:
231 for f in remove:
232 repo.wvfs.unlinkpath(f, ignoremissing=True)
232 repo.wvfs.unlinkpath(f, ignoremissing=True)
233 repo[None].forget(remove)
233 repo[None].forget(remove)
234
234
235 for f in remove:
235 for f in remove:
236 lfutil.synclfdirstate(
236 lfutil.synclfdirstate(
237 repo, lfdirstate, lfutil.splitstandin(f), False
237 repo, lfdirstate, lfutil.splitstandin(f), False
238 )
238 )
239
239
240 lfdirstate.write()
240 lfdirstate.write()
241
241
242 return result
242 return result
243
243
244
244
245 # For overriding mercurial.hgweb.webcommands so that largefiles will
245 # For overriding mercurial.hgweb.webcommands so that largefiles will
246 # appear at their right place in the manifests.
246 # appear at their right place in the manifests.
247 @eh.wrapfunction(webcommands, b'decodepath')
247 @eh.wrapfunction(webcommands, b'decodepath')
248 def decodepath(orig, path):
248 def decodepath(orig, path):
249 return lfutil.splitstandin(path) or path
249 return lfutil.splitstandin(path) or path
250
250
251
251
252 # -- Wrappers: modify existing commands --------------------------------
252 # -- Wrappers: modify existing commands --------------------------------
253
253
254
254
255 @eh.wrapcommand(
255 @eh.wrapcommand(
256 b'add',
256 b'add',
257 opts=[
257 opts=[
258 (b'', b'large', None, _(b'add as largefile')),
258 (b'', b'large', None, _(b'add as largefile')),
259 (b'', b'normal', None, _(b'add as normal file')),
259 (b'', b'normal', None, _(b'add as normal file')),
260 (
260 (
261 b'',
261 b'',
262 b'lfsize',
262 b'lfsize',
263 b'',
263 b'',
264 _(
264 _(
265 b'add all files above this size (in megabytes) '
265 b'add all files above this size (in megabytes) '
266 b'as largefiles (default: 10)'
266 b'as largefiles (default: 10)'
267 ),
267 ),
268 ),
268 ),
269 ],
269 ],
270 )
270 )
271 def overrideadd(orig, ui, repo, *pats, **opts):
271 def overrideadd(orig, ui, repo, *pats, **opts):
272 if opts.get(r'normal') and opts.get(r'large'):
272 if opts.get(r'normal') and opts.get(r'large'):
273 raise error.Abort(_(b'--normal cannot be used with --large'))
273 raise error.Abort(_(b'--normal cannot be used with --large'))
274 return orig(ui, repo, *pats, **opts)
274 return orig(ui, repo, *pats, **opts)
275
275
276
276
277 @eh.wrapfunction(cmdutil, b'add')
277 @eh.wrapfunction(cmdutil, b'add')
278 def cmdutiladd(orig, ui, repo, matcher, prefix, uipathfn, explicitonly, **opts):
278 def cmdutiladd(orig, ui, repo, matcher, prefix, uipathfn, explicitonly, **opts):
279 # The --normal flag short circuits this override
279 # The --normal flag short circuits this override
280 if opts.get(r'normal'):
280 if opts.get(r'normal'):
281 return orig(ui, repo, matcher, prefix, uipathfn, explicitonly, **opts)
281 return orig(ui, repo, matcher, prefix, uipathfn, explicitonly, **opts)
282
282
283 ladded, lbad = addlargefiles(ui, repo, False, matcher, uipathfn, **opts)
283 ladded, lbad = addlargefiles(ui, repo, False, matcher, uipathfn, **opts)
284 normalmatcher = composenormalfilematcher(
284 normalmatcher = composenormalfilematcher(
285 matcher, repo[None].manifest(), ladded
285 matcher, repo[None].manifest(), ladded
286 )
286 )
287 bad = orig(ui, repo, normalmatcher, prefix, uipathfn, explicitonly, **opts)
287 bad = orig(ui, repo, normalmatcher, prefix, uipathfn, explicitonly, **opts)
288
288
289 bad.extend(f for f in lbad)
289 bad.extend(f for f in lbad)
290 return bad
290 return bad
291
291
292
292
293 @eh.wrapfunction(cmdutil, b'remove')
293 @eh.wrapfunction(cmdutil, b'remove')
294 def cmdutilremove(
294 def cmdutilremove(
295 orig, ui, repo, matcher, prefix, uipathfn, after, force, subrepos, dryrun
295 orig, ui, repo, matcher, prefix, uipathfn, after, force, subrepos, dryrun
296 ):
296 ):
297 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
297 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
298 result = orig(
298 result = orig(
299 ui,
299 ui,
300 repo,
300 repo,
301 normalmatcher,
301 normalmatcher,
302 prefix,
302 prefix,
303 uipathfn,
303 uipathfn,
304 after,
304 after,
305 force,
305 force,
306 subrepos,
306 subrepos,
307 dryrun,
307 dryrun,
308 )
308 )
309 return (
309 return (
310 removelargefiles(
310 removelargefiles(
311 ui, repo, False, matcher, uipathfn, dryrun, after=after, force=force
311 ui, repo, False, matcher, uipathfn, dryrun, after=after, force=force
312 )
312 )
313 or result
313 or result
314 )
314 )
315
315
316
316
317 @eh.wrapfunction(subrepo.hgsubrepo, b'status')
317 @eh.wrapfunction(subrepo.hgsubrepo, b'status')
318 def overridestatusfn(orig, repo, rev2, **opts):
318 def overridestatusfn(orig, repo, rev2, **opts):
319 with lfstatus(repo._repo):
319 with lfstatus(repo._repo):
320 return orig(repo, rev2, **opts)
320 return orig(repo, rev2, **opts)
321
321
322
322
323 @eh.wrapcommand(b'status')
323 @eh.wrapcommand(b'status')
324 def overridestatus(orig, ui, repo, *pats, **opts):
324 def overridestatus(orig, ui, repo, *pats, **opts):
325 with lfstatus(repo):
325 with lfstatus(repo):
326 return orig(ui, repo, *pats, **opts)
326 return orig(ui, repo, *pats, **opts)
327
327
328
328
329 @eh.wrapfunction(subrepo.hgsubrepo, b'dirty')
329 @eh.wrapfunction(subrepo.hgsubrepo, b'dirty')
330 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
330 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
331 with lfstatus(repo._repo):
331 with lfstatus(repo._repo):
332 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
332 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
333
333
334
334
335 @eh.wrapcommand(b'log')
335 @eh.wrapcommand(b'log')
336 def overridelog(orig, ui, repo, *pats, **opts):
336 def overridelog(orig, ui, repo, *pats, **opts):
337 def overridematchandpats(
337 def overridematchandpats(
338 orig,
338 orig,
339 ctx,
339 ctx,
340 pats=(),
340 pats=(),
341 opts=None,
341 opts=None,
342 globbed=False,
342 globbed=False,
343 default=b'relpath',
343 default=b'relpath',
344 badfn=None,
344 badfn=None,
345 ):
345 ):
346 """Matcher that merges root directory with .hglf, suitable for log.
346 """Matcher that merges root directory with .hglf, suitable for log.
347 It is still possible to match .hglf directly.
347 It is still possible to match .hglf directly.
348 For any listed files run log on the standin too.
348 For any listed files run log on the standin too.
349 matchfn tries both the given filename and with .hglf stripped.
349 matchfn tries both the given filename and with .hglf stripped.
350 """
350 """
351 if opts is None:
351 if opts is None:
352 opts = {}
352 opts = {}
353 matchandpats = orig(ctx, pats, opts, globbed, default, badfn=badfn)
353 matchandpats = orig(ctx, pats, opts, globbed, default, badfn=badfn)
354 m, p = copy.copy(matchandpats)
354 m, p = copy.copy(matchandpats)
355
355
356 if m.always():
356 if m.always():
357 # We want to match everything anyway, so there's no benefit trying
357 # We want to match everything anyway, so there's no benefit trying
358 # to add standins.
358 # to add standins.
359 return matchandpats
359 return matchandpats
360
360
361 pats = set(p)
361 pats = set(p)
362
362
363 def fixpats(pat, tostandin=lfutil.standin):
363 def fixpats(pat, tostandin=lfutil.standin):
364 if pat.startswith(b'set:'):
364 if pat.startswith(b'set:'):
365 return pat
365 return pat
366
366
367 kindpat = matchmod._patsplit(pat, None)
367 kindpat = matchmod._patsplit(pat, None)
368
368
369 if kindpat[0] is not None:
369 if kindpat[0] is not None:
370 return kindpat[0] + b':' + tostandin(kindpat[1])
370 return kindpat[0] + b':' + tostandin(kindpat[1])
371 return tostandin(kindpat[1])
371 return tostandin(kindpat[1])
372
372
373 cwd = repo.getcwd()
373 cwd = repo.getcwd()
374 if cwd:
374 if cwd:
375 hglf = lfutil.shortname
375 hglf = lfutil.shortname
376 back = util.pconvert(repo.pathto(hglf)[: -len(hglf)])
376 back = util.pconvert(repo.pathto(hglf)[: -len(hglf)])
377
377
378 def tostandin(f):
378 def tostandin(f):
379 # The file may already be a standin, so truncate the back
379 # The file may already be a standin, so truncate the back
380 # prefix and test before mangling it. This avoids turning
380 # prefix and test before mangling it. This avoids turning
381 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
381 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
382 if f.startswith(back) and lfutil.splitstandin(f[len(back) :]):
382 if f.startswith(back) and lfutil.splitstandin(f[len(back) :]):
383 return f
383 return f
384
384
385 # An absolute path is from outside the repo, so truncate the
385 # An absolute path is from outside the repo, so truncate the
386 # path to the root before building the standin. Otherwise cwd
386 # path to the root before building the standin. Otherwise cwd
387 # is somewhere in the repo, relative to root, and needs to be
387 # is somewhere in the repo, relative to root, and needs to be
388 # prepended before building the standin.
388 # prepended before building the standin.
389 if os.path.isabs(cwd):
389 if os.path.isabs(cwd):
390 f = f[len(back) :]
390 f = f[len(back) :]
391 else:
391 else:
392 f = cwd + b'/' + f
392 f = cwd + b'/' + f
393 return back + lfutil.standin(f)
393 return back + lfutil.standin(f)
394
394
395 else:
395 else:
396
396
397 def tostandin(f):
397 def tostandin(f):
398 if lfutil.isstandin(f):
398 if lfutil.isstandin(f):
399 return f
399 return f
400 return lfutil.standin(f)
400 return lfutil.standin(f)
401
401
402 pats.update(fixpats(f, tostandin) for f in p)
402 pats.update(fixpats(f, tostandin) for f in p)
403
403
404 for i in range(0, len(m._files)):
404 for i in range(0, len(m._files)):
405 # Don't add '.hglf' to m.files, since that is already covered by '.'
405 # Don't add '.hglf' to m.files, since that is already covered by '.'
406 if m._files[i] == b'.':
406 if m._files[i] == b'.':
407 continue
407 continue
408 standin = lfutil.standin(m._files[i])
408 standin = lfutil.standin(m._files[i])
409 # If the "standin" is a directory, append instead of replace to
409 # If the "standin" is a directory, append instead of replace to
410 # support naming a directory on the command line with only
410 # support naming a directory on the command line with only
411 # largefiles. The original directory is kept to support normal
411 # largefiles. The original directory is kept to support normal
412 # files.
412 # files.
413 if standin in ctx:
413 if standin in ctx:
414 m._files[i] = standin
414 m._files[i] = standin
415 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
415 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
416 m._files.append(standin)
416 m._files.append(standin)
417
417
418 m._fileset = set(m._files)
418 m._fileset = set(m._files)
419 m.always = lambda: False
419 m.always = lambda: False
420 origmatchfn = m.matchfn
420 origmatchfn = m.matchfn
421
421
422 def lfmatchfn(f):
422 def lfmatchfn(f):
423 lf = lfutil.splitstandin(f)
423 lf = lfutil.splitstandin(f)
424 if lf is not None and origmatchfn(lf):
424 if lf is not None and origmatchfn(lf):
425 return True
425 return True
426 r = origmatchfn(f)
426 r = origmatchfn(f)
427 return r
427 return r
428
428
429 m.matchfn = lfmatchfn
429 m.matchfn = lfmatchfn
430
430
431 ui.debug(b'updated patterns: %s\n' % b', '.join(sorted(pats)))
431 ui.debug(b'updated patterns: %s\n' % b', '.join(sorted(pats)))
432 return m, pats
432 return m, pats
433
433
434 # For hg log --patch, the match object is used in two different senses:
434 # For hg log --patch, the match object is used in two different senses:
435 # (1) to determine what revisions should be printed out, and
435 # (1) to determine what revisions should be printed out, and
436 # (2) to determine what files to print out diffs for.
436 # (2) to determine what files to print out diffs for.
437 # The magic matchandpats override should be used for case (1) but not for
437 # The magic matchandpats override should be used for case (1) but not for
438 # case (2).
438 # case (2).
439 oldmatchandpats = scmutil.matchandpats
439 oldmatchandpats = scmutil.matchandpats
440
440
441 def overridemakefilematcher(orig, repo, pats, opts, badfn=None):
441 def overridemakefilematcher(orig, repo, pats, opts, badfn=None):
442 wctx = repo[None]
442 wctx = repo[None]
443 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
443 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
444 return lambda ctx: match
444 return lambda ctx: match
445
445
446 wrappedmatchandpats = extensions.wrappedfunction(
446 wrappedmatchandpats = extensions.wrappedfunction(
447 scmutil, b'matchandpats', overridematchandpats
447 scmutil, b'matchandpats', overridematchandpats
448 )
448 )
449 wrappedmakefilematcher = extensions.wrappedfunction(
449 wrappedmakefilematcher = extensions.wrappedfunction(
450 logcmdutil, b'_makenofollowfilematcher', overridemakefilematcher
450 logcmdutil, b'_makenofollowfilematcher', overridemakefilematcher
451 )
451 )
452 with wrappedmatchandpats, wrappedmakefilematcher:
452 with wrappedmatchandpats, wrappedmakefilematcher:
453 return orig(ui, repo, *pats, **opts)
453 return orig(ui, repo, *pats, **opts)
454
454
455
455
456 @eh.wrapcommand(
456 @eh.wrapcommand(
457 b'verify',
457 b'verify',
458 opts=[
458 opts=[
459 (
459 (
460 b'',
460 b'',
461 b'large',
461 b'large',
462 None,
462 None,
463 _(b'verify that all largefiles in current revision exists'),
463 _(b'verify that all largefiles in current revision exists'),
464 ),
464 ),
465 (
465 (
466 b'',
466 b'',
467 b'lfa',
467 b'lfa',
468 None,
468 None,
469 _(b'verify largefiles in all revisions, not just current'),
469 _(b'verify largefiles in all revisions, not just current'),
470 ),
470 ),
471 (
471 (
472 b'',
472 b'',
473 b'lfc',
473 b'lfc',
474 None,
474 None,
475 _(b'verify local largefile contents, not just existence'),
475 _(b'verify local largefile contents, not just existence'),
476 ),
476 ),
477 ],
477 ],
478 )
478 )
479 def overrideverify(orig, ui, repo, *pats, **opts):
479 def overrideverify(orig, ui, repo, *pats, **opts):
480 large = opts.pop(r'large', False)
480 large = opts.pop(r'large', False)
481 all = opts.pop(r'lfa', False)
481 all = opts.pop(r'lfa', False)
482 contents = opts.pop(r'lfc', False)
482 contents = opts.pop(r'lfc', False)
483
483
484 result = orig(ui, repo, *pats, **opts)
484 result = orig(ui, repo, *pats, **opts)
485 if large or all or contents:
485 if large or all or contents:
486 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
486 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
487 return result
487 return result
488
488
489
489
490 @eh.wrapcommand(
490 @eh.wrapcommand(
491 b'debugstate',
491 b'debugstate',
492 opts=[(b'', b'large', None, _(b'display largefiles dirstate'))],
492 opts=[(b'', b'large', None, _(b'display largefiles dirstate'))],
493 )
493 )
494 def overridedebugstate(orig, ui, repo, *pats, **opts):
494 def overridedebugstate(orig, ui, repo, *pats, **opts):
495 large = opts.pop(r'large', False)
495 large = opts.pop(r'large', False)
496 if large:
496 if large:
497
497
498 class fakerepo(object):
498 class fakerepo(object):
499 dirstate = lfutil.openlfdirstate(ui, repo)
499 dirstate = lfutil.openlfdirstate(ui, repo)
500
500
501 orig(ui, fakerepo, *pats, **opts)
501 orig(ui, fakerepo, *pats, **opts)
502 else:
502 else:
503 orig(ui, repo, *pats, **opts)
503 orig(ui, repo, *pats, **opts)
504
504
505
505
506 # Before starting the manifest merge, merge.updates will call
506 # Before starting the manifest merge, merge.updates will call
507 # _checkunknownfile to check if there are any files in the merged-in
507 # _checkunknownfile to check if there are any files in the merged-in
508 # changeset that collide with unknown files in the working copy.
508 # changeset that collide with unknown files in the working copy.
509 #
509 #
510 # The largefiles are seen as unknown, so this prevents us from merging
510 # The largefiles are seen as unknown, so this prevents us from merging
511 # in a file 'foo' if we already have a largefile with the same name.
511 # in a file 'foo' if we already have a largefile with the same name.
512 #
512 #
513 # The overridden function filters the unknown files by removing any
513 # The overridden function filters the unknown files by removing any
514 # largefiles. This makes the merge proceed and we can then handle this
514 # largefiles. This makes the merge proceed and we can then handle this
515 # case further in the overridden calculateupdates function below.
515 # case further in the overridden calculateupdates function below.
516 @eh.wrapfunction(merge, b'_checkunknownfile')
516 @eh.wrapfunction(merge, b'_checkunknownfile')
517 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
517 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
518 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
518 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
519 return False
519 return False
520 return origfn(repo, wctx, mctx, f, f2)
520 return origfn(repo, wctx, mctx, f, f2)
521
521
522
522
523 # The manifest merge handles conflicts on the manifest level. We want
523 # The manifest merge handles conflicts on the manifest level. We want
524 # to handle changes in largefile-ness of files at this level too.
524 # to handle changes in largefile-ness of files at this level too.
525 #
525 #
526 # The strategy is to run the original calculateupdates and then process
526 # The strategy is to run the original calculateupdates and then process
527 # the action list it outputs. There are two cases we need to deal with:
527 # the action list it outputs. There are two cases we need to deal with:
528 #
528 #
529 # 1. Normal file in p1, largefile in p2. Here the largefile is
529 # 1. Normal file in p1, largefile in p2. Here the largefile is
530 # detected via its standin file, which will enter the working copy
530 # detected via its standin file, which will enter the working copy
531 # with a "get" action. It is not "merge" since the standin is all
531 # with a "get" action. It is not "merge" since the standin is all
532 # Mercurial is concerned with at this level -- the link to the
532 # Mercurial is concerned with at this level -- the link to the
533 # existing normal file is not relevant here.
533 # existing normal file is not relevant here.
534 #
534 #
535 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
535 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
536 # since the largefile will be present in the working copy and
536 # since the largefile will be present in the working copy and
537 # different from the normal file in p2. Mercurial therefore
537 # different from the normal file in p2. Mercurial therefore
538 # triggers a merge action.
538 # triggers a merge action.
539 #
539 #
540 # In both cases, we prompt the user and emit new actions to either
540 # In both cases, we prompt the user and emit new actions to either
541 # remove the standin (if the normal file was kept) or to remove the
541 # remove the standin (if the normal file was kept) or to remove the
542 # normal file and get the standin (if the largefile was kept). The
542 # normal file and get the standin (if the largefile was kept). The
543 # default prompt answer is to use the largefile version since it was
543 # default prompt answer is to use the largefile version since it was
544 # presumably changed on purpose.
544 # presumably changed on purpose.
545 #
545 #
546 # Finally, the merge.applyupdates function will then take care of
546 # Finally, the merge.applyupdates function will then take care of
547 # writing the files into the working copy and lfcommands.updatelfiles
547 # writing the files into the working copy and lfcommands.updatelfiles
548 # will update the largefiles.
548 # will update the largefiles.
549 @eh.wrapfunction(merge, b'calculateupdates')
549 @eh.wrapfunction(merge, b'calculateupdates')
550 def overridecalculateupdates(
550 def overridecalculateupdates(
551 origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
551 origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
552 ):
552 ):
553 overwrite = force and not branchmerge
553 overwrite = force and not branchmerge
554 actions, diverge, renamedelete = origfn(
554 actions, diverge, renamedelete = origfn(
555 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
555 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
556 )
556 )
557
557
558 if overwrite:
558 if overwrite:
559 return actions, diverge, renamedelete
559 return actions, diverge, renamedelete
560
560
561 # Convert to dictionary with filename as key and action as value.
561 # Convert to dictionary with filename as key and action as value.
562 lfiles = set()
562 lfiles = set()
563 for f in actions:
563 for f in actions:
564 splitstandin = lfutil.splitstandin(f)
564 splitstandin = lfutil.splitstandin(f)
565 if splitstandin is not None and splitstandin in p1:
565 if splitstandin is not None and splitstandin in p1:
566 lfiles.add(splitstandin)
566 lfiles.add(splitstandin)
567 elif lfutil.standin(f) in p1:
567 elif lfutil.standin(f) in p1:
568 lfiles.add(f)
568 lfiles.add(f)
569
569
570 for lfile in sorted(lfiles):
570 for lfile in sorted(lfiles):
571 standin = lfutil.standin(lfile)
571 standin = lfutil.standin(lfile)
572 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
572 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
573 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
573 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
574 if sm in (b'g', b'dc') and lm != b'r':
574 if sm in (b'g', b'dc') and lm != b'r':
575 if sm == b'dc':
575 if sm == b'dc':
576 f1, f2, fa, move, anc = sargs
576 f1, f2, fa, move, anc = sargs
577 sargs = (p2[f2].flags(), False)
577 sargs = (p2[f2].flags(), False)
578 # Case 1: normal file in the working copy, largefile in
578 # Case 1: normal file in the working copy, largefile in
579 # the second parent
579 # the second parent
580 usermsg = (
580 usermsg = (
581 _(
581 _(
582 b'remote turned local normal file %s into a largefile\n'
582 b'remote turned local normal file %s into a largefile\n'
583 b'use (l)argefile or keep (n)ormal file?'
583 b'use (l)argefile or keep (n)ormal file?'
584 b'$$ &Largefile $$ &Normal file'
584 b'$$ &Largefile $$ &Normal file'
585 )
585 )
586 % lfile
586 % lfile
587 )
587 )
588 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
588 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
589 actions[lfile] = (b'r', None, b'replaced by standin')
589 actions[lfile] = (b'r', None, b'replaced by standin')
590 actions[standin] = (b'g', sargs, b'replaces standin')
590 actions[standin] = (b'g', sargs, b'replaces standin')
591 else: # keep local normal file
591 else: # keep local normal file
592 actions[lfile] = (b'k', None, b'replaces standin')
592 actions[lfile] = (b'k', None, b'replaces standin')
593 if branchmerge:
593 if branchmerge:
594 actions[standin] = (b'k', None, b'replaced by non-standin')
594 actions[standin] = (b'k', None, b'replaced by non-standin')
595 else:
595 else:
596 actions[standin] = (b'r', None, b'replaced by non-standin')
596 actions[standin] = (b'r', None, b'replaced by non-standin')
597 elif lm in (b'g', b'dc') and sm != b'r':
597 elif lm in (b'g', b'dc') and sm != b'r':
598 if lm == b'dc':
598 if lm == b'dc':
599 f1, f2, fa, move, anc = largs
599 f1, f2, fa, move, anc = largs
600 largs = (p2[f2].flags(), False)
600 largs = (p2[f2].flags(), False)
601 # Case 2: largefile in the working copy, normal file in
601 # Case 2: largefile in the working copy, normal file in
602 # the second parent
602 # the second parent
603 usermsg = (
603 usermsg = (
604 _(
604 _(
605 b'remote turned local largefile %s into a normal file\n'
605 b'remote turned local largefile %s into a normal file\n'
606 b'keep (l)argefile or use (n)ormal file?'
606 b'keep (l)argefile or use (n)ormal file?'
607 b'$$ &Largefile $$ &Normal file'
607 b'$$ &Largefile $$ &Normal file'
608 )
608 )
609 % lfile
609 % lfile
610 )
610 )
611 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
611 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
612 if branchmerge:
612 if branchmerge:
613 # largefile can be restored from standin safely
613 # largefile can be restored from standin safely
614 actions[lfile] = (b'k', None, b'replaced by standin')
614 actions[lfile] = (b'k', None, b'replaced by standin')
615 actions[standin] = (b'k', None, b'replaces standin')
615 actions[standin] = (b'k', None, b'replaces standin')
616 else:
616 else:
617 # "lfile" should be marked as "removed" without
617 # "lfile" should be marked as "removed" without
618 # removal of itself
618 # removal of itself
619 actions[lfile] = (
619 actions[lfile] = (
620 b'lfmr',
620 b'lfmr',
621 None,
621 None,
622 b'forget non-standin largefile',
622 b'forget non-standin largefile',
623 )
623 )
624
624
625 # linear-merge should treat this largefile as 're-added'
625 # linear-merge should treat this largefile as 're-added'
626 actions[standin] = (b'a', None, b'keep standin')
626 actions[standin] = (b'a', None, b'keep standin')
627 else: # pick remote normal file
627 else: # pick remote normal file
628 actions[lfile] = (b'g', largs, b'replaces standin')
628 actions[lfile] = (b'g', largs, b'replaces standin')
629 actions[standin] = (b'r', None, b'replaced by non-standin')
629 actions[standin] = (b'r', None, b'replaced by non-standin')
630
630
631 return actions, diverge, renamedelete
631 return actions, diverge, renamedelete
632
632
633
633
634 @eh.wrapfunction(merge, b'recordupdates')
634 @eh.wrapfunction(merge, b'recordupdates')
635 def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata):
635 def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata):
636 if b'lfmr' in actions:
636 if b'lfmr' in actions:
637 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
637 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
638 for lfile, args, msg in actions[b'lfmr']:
638 for lfile, args, msg in actions[b'lfmr']:
639 # this should be executed before 'orig', to execute 'remove'
639 # this should be executed before 'orig', to execute 'remove'
640 # before all other actions
640 # before all other actions
641 repo.dirstate.remove(lfile)
641 repo.dirstate.remove(lfile)
642 # make sure lfile doesn't get synclfdirstate'd as normal
642 # make sure lfile doesn't get synclfdirstate'd as normal
643 lfdirstate.add(lfile)
643 lfdirstate.add(lfile)
644 lfdirstate.write()
644 lfdirstate.write()
645
645
646 return orig(repo, actions, branchmerge, getfiledata)
646 return orig(repo, actions, branchmerge, getfiledata)
647
647
648
648
649 # Override filemerge to prompt the user about how they wish to merge
649 # Override filemerge to prompt the user about how they wish to merge
650 # largefiles. This will handle identical edits without prompting the user.
650 # largefiles. This will handle identical edits without prompting the user.
651 @eh.wrapfunction(filemerge, b'_filemerge')
651 @eh.wrapfunction(filemerge, b'_filemerge')
652 def overridefilemerge(
652 def overridefilemerge(
653 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
653 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
654 ):
654 ):
655 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
655 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
656 return origfn(
656 return origfn(
657 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
657 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
658 )
658 )
659
659
660 ahash = lfutil.readasstandin(fca).lower()
660 ahash = lfutil.readasstandin(fca).lower()
661 dhash = lfutil.readasstandin(fcd).lower()
661 dhash = lfutil.readasstandin(fcd).lower()
662 ohash = lfutil.readasstandin(fco).lower()
662 ohash = lfutil.readasstandin(fco).lower()
663 if (
663 if (
664 ohash != ahash
664 ohash != ahash
665 and ohash != dhash
665 and ohash != dhash
666 and (
666 and (
667 dhash == ahash
667 dhash == ahash
668 or repo.ui.promptchoice(
668 or repo.ui.promptchoice(
669 _(
669 _(
670 b'largefile %s has a merge conflict\nancestor was %s\n'
670 b'largefile %s has a merge conflict\nancestor was %s\n'
671 b'you can keep (l)ocal %s or take (o)ther %s.\n'
671 b'you can keep (l)ocal %s or take (o)ther %s.\n'
672 b'what do you want to do?'
672 b'what do you want to do?'
673 b'$$ &Local $$ &Other'
673 b'$$ &Local $$ &Other'
674 )
674 )
675 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
675 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
676 0,
676 0,
677 )
677 )
678 == 1
678 == 1
679 )
679 )
680 ):
680 ):
681 repo.wwrite(fcd.path(), fco.data(), fco.flags())
681 repo.wwrite(fcd.path(), fco.data(), fco.flags())
682 return True, 0, False
682 return True, 0, False
683
683
684
684
685 @eh.wrapfunction(copiesmod, b'pathcopies')
685 @eh.wrapfunction(copiesmod, b'pathcopies')
686 def copiespathcopies(orig, ctx1, ctx2, match=None):
686 def copiespathcopies(orig, ctx1, ctx2, match=None):
687 copies = orig(ctx1, ctx2, match=match)
687 copies = orig(ctx1, ctx2, match=match)
688 updated = {}
688 updated = {}
689
689
690 for k, v in pycompat.iteritems(copies):
690 for k, v in pycompat.iteritems(copies):
691 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
691 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
692
692
693 return updated
693 return updated
694
694
695
695
696 # Copy first changes the matchers to match standins instead of
696 # Copy first changes the matchers to match standins instead of
697 # largefiles. Then it overrides util.copyfile in that function it
697 # largefiles. Then it overrides util.copyfile in that function it
698 # checks if the destination largefile already exists. It also keeps a
698 # checks if the destination largefile already exists. It also keeps a
699 # list of copied files so that the largefiles can be copied and the
699 # list of copied files so that the largefiles can be copied and the
700 # dirstate updated.
700 # dirstate updated.
701 @eh.wrapfunction(cmdutil, b'copy')
701 @eh.wrapfunction(cmdutil, b'copy')
702 def overridecopy(orig, ui, repo, pats, opts, rename=False):
702 def overridecopy(orig, ui, repo, pats, opts, rename=False):
703 # doesn't remove largefile on rename
703 # doesn't remove largefile on rename
704 if len(pats) < 2:
704 if len(pats) < 2:
705 # this isn't legal, let the original function deal with it
705 # this isn't legal, let the original function deal with it
706 return orig(ui, repo, pats, opts, rename)
706 return orig(ui, repo, pats, opts, rename)
707
707
708 # This could copy both lfiles and normal files in one command,
708 # This could copy both lfiles and normal files in one command,
709 # but we don't want to do that. First replace their matcher to
709 # but we don't want to do that. First replace their matcher to
710 # only match normal files and run it, then replace it to just
710 # only match normal files and run it, then replace it to just
711 # match largefiles and run it again.
711 # match largefiles and run it again.
712 nonormalfiles = False
712 nonormalfiles = False
713 nolfiles = False
713 nolfiles = False
714 manifest = repo[None].manifest()
714 manifest = repo[None].manifest()
715
715
716 def normalfilesmatchfn(
716 def normalfilesmatchfn(
717 orig,
717 orig,
718 ctx,
718 ctx,
719 pats=(),
719 pats=(),
720 opts=None,
720 opts=None,
721 globbed=False,
721 globbed=False,
722 default=b'relpath',
722 default=b'relpath',
723 badfn=None,
723 badfn=None,
724 ):
724 ):
725 if opts is None:
725 if opts is None:
726 opts = {}
726 opts = {}
727 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
727 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
728 return composenormalfilematcher(match, manifest)
728 return composenormalfilematcher(match, manifest)
729
729
730 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
730 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
731 try:
731 try:
732 result = orig(ui, repo, pats, opts, rename)
732 result = orig(ui, repo, pats, opts, rename)
733 except error.Abort as e:
733 except error.Abort as e:
734 if pycompat.bytestr(e) != _(b'no files to copy'):
734 if pycompat.bytestr(e) != _(b'no files to copy'):
735 raise e
735 raise e
736 else:
736 else:
737 nonormalfiles = True
737 nonormalfiles = True
738 result = 0
738 result = 0
739
739
740 # The first rename can cause our current working directory to be removed.
740 # The first rename can cause our current working directory to be removed.
741 # In that case there is nothing left to copy/rename so just quit.
741 # In that case there is nothing left to copy/rename so just quit.
742 try:
742 try:
743 repo.getcwd()
743 repo.getcwd()
744 except OSError:
744 except OSError:
745 return result
745 return result
746
746
747 def makestandin(relpath):
747 def makestandin(relpath):
748 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
748 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
749 return repo.wvfs.join(lfutil.standin(path))
749 return repo.wvfs.join(lfutil.standin(path))
750
750
751 fullpats = scmutil.expandpats(pats)
751 fullpats = scmutil.expandpats(pats)
752 dest = fullpats[-1]
752 dest = fullpats[-1]
753
753
754 if os.path.isdir(dest):
754 if os.path.isdir(dest):
755 if not os.path.isdir(makestandin(dest)):
755 if not os.path.isdir(makestandin(dest)):
756 os.makedirs(makestandin(dest))
756 os.makedirs(makestandin(dest))
757
757
758 try:
758 try:
759 # When we call orig below it creates the standins but we don't add
759 # When we call orig below it creates the standins but we don't add
760 # them to the dir state until later so lock during that time.
760 # them to the dir state until later so lock during that time.
761 wlock = repo.wlock()
761 wlock = repo.wlock()
762
762
763 manifest = repo[None].manifest()
763 manifest = repo[None].manifest()
764
764
765 def overridematch(
765 def overridematch(
766 orig,
766 orig,
767 ctx,
767 ctx,
768 pats=(),
768 pats=(),
769 opts=None,
769 opts=None,
770 globbed=False,
770 globbed=False,
771 default=b'relpath',
771 default=b'relpath',
772 badfn=None,
772 badfn=None,
773 ):
773 ):
774 if opts is None:
774 if opts is None:
775 opts = {}
775 opts = {}
776 newpats = []
776 newpats = []
777 # The patterns were previously mangled to add the standin
777 # The patterns were previously mangled to add the standin
778 # directory; we need to remove that now
778 # directory; we need to remove that now
779 for pat in pats:
779 for pat in pats:
780 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
780 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
781 newpats.append(pat.replace(lfutil.shortname, b''))
781 newpats.append(pat.replace(lfutil.shortname, b''))
782 else:
782 else:
783 newpats.append(pat)
783 newpats.append(pat)
784 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
784 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
785 m = copy.copy(match)
785 m = copy.copy(match)
786 lfile = lambda f: lfutil.standin(f) in manifest
786 lfile = lambda f: lfutil.standin(f) in manifest
787 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
787 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
788 m._fileset = set(m._files)
788 m._fileset = set(m._files)
789 origmatchfn = m.matchfn
789 origmatchfn = m.matchfn
790
790
791 def matchfn(f):
791 def matchfn(f):
792 lfile = lfutil.splitstandin(f)
792 lfile = lfutil.splitstandin(f)
793 return (
793 return (
794 lfile is not None
794 lfile is not None
795 and (f in manifest)
795 and (f in manifest)
796 and origmatchfn(lfile)
796 and origmatchfn(lfile)
797 or None
797 or None
798 )
798 )
799
799
800 m.matchfn = matchfn
800 m.matchfn = matchfn
801 return m
801 return m
802
802
803 listpats = []
803 listpats = []
804 for pat in pats:
804 for pat in pats:
805 if matchmod.patkind(pat) is not None:
805 if matchmod.patkind(pat) is not None:
806 listpats.append(pat)
806 listpats.append(pat)
807 else:
807 else:
808 listpats.append(makestandin(pat))
808 listpats.append(makestandin(pat))
809
809
810 copiedfiles = []
810 copiedfiles = []
811
811
812 def overridecopyfile(orig, src, dest, *args, **kwargs):
812 def overridecopyfile(orig, src, dest, *args, **kwargs):
813 if lfutil.shortname in src and dest.startswith(
813 if lfutil.shortname in src and dest.startswith(
814 repo.wjoin(lfutil.shortname)
814 repo.wjoin(lfutil.shortname)
815 ):
815 ):
816 destlfile = dest.replace(lfutil.shortname, b'')
816 destlfile = dest.replace(lfutil.shortname, b'')
817 if not opts[b'force'] and os.path.exists(destlfile):
817 if not opts[b'force'] and os.path.exists(destlfile):
818 raise IOError(
818 raise IOError(
819 b'', _(b'destination largefile already exists')
819 b'', _(b'destination largefile already exists')
820 )
820 )
821 copiedfiles.append((src, dest))
821 copiedfiles.append((src, dest))
822 orig(src, dest, *args, **kwargs)
822 orig(src, dest, *args, **kwargs)
823
823
824 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
824 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
825 with extensions.wrappedfunction(scmutil, b'match', overridematch):
825 with extensions.wrappedfunction(scmutil, b'match', overridematch):
826 result += orig(ui, repo, listpats, opts, rename)
826 result += orig(ui, repo, listpats, opts, rename)
827
827
828 lfdirstate = lfutil.openlfdirstate(ui, repo)
828 lfdirstate = lfutil.openlfdirstate(ui, repo)
829 for (src, dest) in copiedfiles:
829 for (src, dest) in copiedfiles:
830 if lfutil.shortname in src and dest.startswith(
830 if lfutil.shortname in src and dest.startswith(
831 repo.wjoin(lfutil.shortname)
831 repo.wjoin(lfutil.shortname)
832 ):
832 ):
833 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
833 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
834 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
834 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
835 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
835 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
836 if not os.path.isdir(destlfiledir):
836 if not os.path.isdir(destlfiledir):
837 os.makedirs(destlfiledir)
837 os.makedirs(destlfiledir)
838 if rename:
838 if rename:
839 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
839 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
840
840
841 # The file is gone, but this deletes any empty parent
841 # The file is gone, but this deletes any empty parent
842 # directories as a side-effect.
842 # directories as a side-effect.
843 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
843 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
844 lfdirstate.remove(srclfile)
844 lfdirstate.remove(srclfile)
845 else:
845 else:
846 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
846 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
847
847
848 lfdirstate.add(destlfile)
848 lfdirstate.add(destlfile)
849 lfdirstate.write()
849 lfdirstate.write()
850 except error.Abort as e:
850 except error.Abort as e:
851 if pycompat.bytestr(e) != _(b'no files to copy'):
851 if pycompat.bytestr(e) != _(b'no files to copy'):
852 raise e
852 raise e
853 else:
853 else:
854 nolfiles = True
854 nolfiles = True
855 finally:
855 finally:
856 wlock.release()
856 wlock.release()
857
857
858 if nolfiles and nonormalfiles:
858 if nolfiles and nonormalfiles:
859 raise error.Abort(_(b'no files to copy'))
859 raise error.Abort(_(b'no files to copy'))
860
860
861 return result
861 return result
862
862
863
863
864 # When the user calls revert, we have to be careful to not revert any
864 # When the user calls revert, we have to be careful to not revert any
865 # changes to other largefiles accidentally. This means we have to keep
865 # changes to other largefiles accidentally. This means we have to keep
866 # track of the largefiles that are being reverted so we only pull down
866 # track of the largefiles that are being reverted so we only pull down
867 # the necessary largefiles.
867 # the necessary largefiles.
868 #
868 #
869 # Standins are only updated (to match the hash of largefiles) before
869 # Standins are only updated (to match the hash of largefiles) before
870 # commits. Update the standins then run the original revert, changing
870 # commits. Update the standins then run the original revert, changing
871 # the matcher to hit standins instead of largefiles. Based on the
871 # the matcher to hit standins instead of largefiles. Based on the
872 # resulting standins update the largefiles.
872 # resulting standins update the largefiles.
873 @eh.wrapfunction(cmdutil, b'revert')
873 @eh.wrapfunction(cmdutil, b'revert')
874 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
874 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
875 # Because we put the standins in a bad state (by updating them)
875 # Because we put the standins in a bad state (by updating them)
876 # and then return them to a correct state we need to lock to
876 # and then return them to a correct state we need to lock to
877 # prevent others from changing them in their incorrect state.
877 # prevent others from changing them in their incorrect state.
878 with repo.wlock():
878 with repo.wlock():
879 lfdirstate = lfutil.openlfdirstate(ui, repo)
879 lfdirstate = lfutil.openlfdirstate(ui, repo)
880 s = lfutil.lfdirstatestatus(lfdirstate, repo)
880 s = lfutil.lfdirstatestatus(lfdirstate, repo)
881 lfdirstate.write()
881 lfdirstate.write()
882 for lfile in s.modified:
882 for lfile in s.modified:
883 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
883 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
884 for lfile in s.deleted:
884 for lfile in s.deleted:
885 fstandin = lfutil.standin(lfile)
885 fstandin = lfutil.standin(lfile)
886 if repo.wvfs.exists(fstandin):
886 if repo.wvfs.exists(fstandin):
887 repo.wvfs.unlink(fstandin)
887 repo.wvfs.unlink(fstandin)
888
888
889 oldstandins = lfutil.getstandinsstate(repo)
889 oldstandins = lfutil.getstandinsstate(repo)
890
890
891 def overridematch(
891 def overridematch(
892 orig,
892 orig,
893 mctx,
893 mctx,
894 pats=(),
894 pats=(),
895 opts=None,
895 opts=None,
896 globbed=False,
896 globbed=False,
897 default=b'relpath',
897 default=b'relpath',
898 badfn=None,
898 badfn=None,
899 ):
899 ):
900 if opts is None:
900 if opts is None:
901 opts = {}
901 opts = {}
902 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
902 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
903 m = copy.copy(match)
903 m = copy.copy(match)
904
904
905 # revert supports recursing into subrepos, and though largefiles
905 # revert supports recursing into subrepos, and though largefiles
906 # currently doesn't work correctly in that case, this match is
906 # currently doesn't work correctly in that case, this match is
907 # called, so the lfdirstate above may not be the correct one for
907 # called, so the lfdirstate above may not be the correct one for
908 # this invocation of match.
908 # this invocation of match.
909 lfdirstate = lfutil.openlfdirstate(
909 lfdirstate = lfutil.openlfdirstate(
910 mctx.repo().ui, mctx.repo(), False
910 mctx.repo().ui, mctx.repo(), False
911 )
911 )
912
912
913 wctx = repo[None]
913 wctx = repo[None]
914 matchfiles = []
914 matchfiles = []
915 for f in m._files:
915 for f in m._files:
916 standin = lfutil.standin(f)
916 standin = lfutil.standin(f)
917 if standin in ctx or standin in mctx:
917 if standin in ctx or standin in mctx:
918 matchfiles.append(standin)
918 matchfiles.append(standin)
919 elif standin in wctx or lfdirstate[f] == b'r':
919 elif standin in wctx or lfdirstate[f] == b'r':
920 continue
920 continue
921 else:
921 else:
922 matchfiles.append(f)
922 matchfiles.append(f)
923 m._files = matchfiles
923 m._files = matchfiles
924 m._fileset = set(m._files)
924 m._fileset = set(m._files)
925 origmatchfn = m.matchfn
925 origmatchfn = m.matchfn
926
926
927 def matchfn(f):
927 def matchfn(f):
928 lfile = lfutil.splitstandin(f)
928 lfile = lfutil.splitstandin(f)
929 if lfile is not None:
929 if lfile is not None:
930 return origmatchfn(lfile) and (f in ctx or f in mctx)
930 return origmatchfn(lfile) and (f in ctx or f in mctx)
931 return origmatchfn(f)
931 return origmatchfn(f)
932
932
933 m.matchfn = matchfn
933 m.matchfn = matchfn
934 return m
934 return m
935
935
936 with extensions.wrappedfunction(scmutil, b'match', overridematch):
936 with extensions.wrappedfunction(scmutil, b'match', overridematch):
937 orig(ui, repo, ctx, parents, *pats, **opts)
937 orig(ui, repo, ctx, parents, *pats, **opts)
938
938
939 newstandins = lfutil.getstandinsstate(repo)
939 newstandins = lfutil.getstandinsstate(repo)
940 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
940 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
941 # lfdirstate should be 'normallookup'-ed for updated files,
941 # lfdirstate should be 'normallookup'-ed for updated files,
942 # because reverting doesn't touch dirstate for 'normal' files
942 # because reverting doesn't touch dirstate for 'normal' files
943 # when target revision is explicitly specified: in such case,
943 # when target revision is explicitly specified: in such case,
944 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
944 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
945 # of target (standin) file.
945 # of target (standin) file.
946 lfcommands.updatelfiles(
946 lfcommands.updatelfiles(
947 ui, repo, filelist, printmessage=False, normallookup=True
947 ui, repo, filelist, printmessage=False, normallookup=True
948 )
948 )
949
949
950
950
951 # after pulling changesets, we need to take some extra care to get
951 # after pulling changesets, we need to take some extra care to get
952 # largefiles updated remotely
952 # largefiles updated remotely
953 @eh.wrapcommand(
953 @eh.wrapcommand(
954 b'pull',
954 b'pull',
955 opts=[
955 opts=[
956 (
956 (
957 b'',
957 b'',
958 b'all-largefiles',
958 b'all-largefiles',
959 None,
959 None,
960 _(b'download all pulled versions of largefiles (DEPRECATED)'),
960 _(b'download all pulled versions of largefiles (DEPRECATED)'),
961 ),
961 ),
962 (
962 (
963 b'',
963 b'',
964 b'lfrev',
964 b'lfrev',
965 [],
965 [],
966 _(b'download largefiles for these revisions'),
966 _(b'download largefiles for these revisions'),
967 _(b'REV'),
967 _(b'REV'),
968 ),
968 ),
969 ],
969 ],
970 )
970 )
971 def overridepull(orig, ui, repo, source=None, **opts):
971 def overridepull(orig, ui, repo, source=None, **opts):
972 revsprepull = len(repo)
972 revsprepull = len(repo)
973 if not source:
973 if not source:
974 source = b'default'
974 source = b'default'
975 repo.lfpullsource = source
975 repo.lfpullsource = source
976 result = orig(ui, repo, source, **opts)
976 result = orig(ui, repo, source, **opts)
977 revspostpull = len(repo)
977 revspostpull = len(repo)
978 lfrevs = opts.get(r'lfrev', [])
978 lfrevs = opts.get(r'lfrev', [])
979 if opts.get(r'all_largefiles'):
979 if opts.get(r'all_largefiles'):
980 lfrevs.append(b'pulled()')
980 lfrevs.append(b'pulled()')
981 if lfrevs and revspostpull > revsprepull:
981 if lfrevs and revspostpull > revsprepull:
982 numcached = 0
982 numcached = 0
983 repo.firstpulled = revsprepull # for pulled() revset expression
983 repo.firstpulled = revsprepull # for pulled() revset expression
984 try:
984 try:
985 for rev in scmutil.revrange(repo, lfrevs):
985 for rev in scmutil.revrange(repo, lfrevs):
986 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
986 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
987 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
987 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
988 numcached += len(cached)
988 numcached += len(cached)
989 finally:
989 finally:
990 del repo.firstpulled
990 del repo.firstpulled
991 ui.status(_(b"%d largefiles cached\n") % numcached)
991 ui.status(_(b"%d largefiles cached\n") % numcached)
992 return result
992 return result
993
993
994
994
995 @eh.wrapcommand(
995 @eh.wrapcommand(
996 b'push',
996 b'push',
997 opts=[
997 opts=[
998 (
998 (
999 b'',
999 b'',
1000 b'lfrev',
1000 b'lfrev',
1001 [],
1001 [],
1002 _(b'upload largefiles for these revisions'),
1002 _(b'upload largefiles for these revisions'),
1003 _(b'REV'),
1003 _(b'REV'),
1004 )
1004 )
1005 ],
1005 ],
1006 )
1006 )
1007 def overridepush(orig, ui, repo, *args, **kwargs):
1007 def overridepush(orig, ui, repo, *args, **kwargs):
1008 """Override push command and store --lfrev parameters in opargs"""
1008 """Override push command and store --lfrev parameters in opargs"""
1009 lfrevs = kwargs.pop(r'lfrev', None)
1009 lfrevs = kwargs.pop(r'lfrev', None)
1010 if lfrevs:
1010 if lfrevs:
1011 opargs = kwargs.setdefault(r'opargs', {})
1011 opargs = kwargs.setdefault(r'opargs', {})
1012 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1012 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1013 return orig(ui, repo, *args, **kwargs)
1013 return orig(ui, repo, *args, **kwargs)
1014
1014
1015
1015
1016 @eh.wrapfunction(exchange, b'pushoperation')
1016 @eh.wrapfunction(exchange, b'pushoperation')
1017 def exchangepushoperation(orig, *args, **kwargs):
1017 def exchangepushoperation(orig, *args, **kwargs):
1018 """Override pushoperation constructor and store lfrevs parameter"""
1018 """Override pushoperation constructor and store lfrevs parameter"""
1019 lfrevs = kwargs.pop(r'lfrevs', None)
1019 lfrevs = kwargs.pop(r'lfrevs', None)
1020 pushop = orig(*args, **kwargs)
1020 pushop = orig(*args, **kwargs)
1021 pushop.lfrevs = lfrevs
1021 pushop.lfrevs = lfrevs
1022 return pushop
1022 return pushop
1023
1023
1024
1024
1025 @eh.revsetpredicate(b'pulled()')
1025 @eh.revsetpredicate(b'pulled()')
1026 def pulledrevsetsymbol(repo, subset, x):
1026 def pulledrevsetsymbol(repo, subset, x):
1027 """Changesets that just has been pulled.
1027 """Changesets that just has been pulled.
1028
1028
1029 Only available with largefiles from pull --lfrev expressions.
1029 Only available with largefiles from pull --lfrev expressions.
1030
1030
1031 .. container:: verbose
1031 .. container:: verbose
1032
1032
1033 Some examples:
1033 Some examples:
1034
1034
1035 - pull largefiles for all new changesets::
1035 - pull largefiles for all new changesets::
1036
1036
1037 hg pull -lfrev "pulled()"
1037 hg pull -lfrev "pulled()"
1038
1038
1039 - pull largefiles for all new branch heads::
1039 - pull largefiles for all new branch heads::
1040
1040
1041 hg pull -lfrev "head(pulled()) and not closed()"
1041 hg pull -lfrev "head(pulled()) and not closed()"
1042
1042
1043 """
1043 """
1044
1044
1045 try:
1045 try:
1046 firstpulled = repo.firstpulled
1046 firstpulled = repo.firstpulled
1047 except AttributeError:
1047 except AttributeError:
1048 raise error.Abort(_(b"pulled() only available in --lfrev"))
1048 raise error.Abort(_(b"pulled() only available in --lfrev"))
1049 return smartset.baseset([r for r in subset if r >= firstpulled])
1049 return smartset.baseset([r for r in subset if r >= firstpulled])
1050
1050
1051
1051
1052 @eh.wrapcommand(
1052 @eh.wrapcommand(
1053 b'clone',
1053 b'clone',
1054 opts=[
1054 opts=[
1055 (
1055 (
1056 b'',
1056 b'',
1057 b'all-largefiles',
1057 b'all-largefiles',
1058 None,
1058 None,
1059 _(b'download all versions of all largefiles'),
1059 _(b'download all versions of all largefiles'),
1060 )
1060 )
1061 ],
1061 ],
1062 )
1062 )
1063 def overrideclone(orig, ui, source, dest=None, **opts):
1063 def overrideclone(orig, ui, source, dest=None, **opts):
1064 d = dest
1064 d = dest
1065 if d is None:
1065 if d is None:
1066 d = hg.defaultdest(source)
1066 d = hg.defaultdest(source)
1067 if opts.get(r'all_largefiles') and not hg.islocal(d):
1067 if opts.get(r'all_largefiles') and not hg.islocal(d):
1068 raise error.Abort(
1068 raise error.Abort(
1069 _(b'--all-largefiles is incompatible with non-local destination %s')
1069 _(b'--all-largefiles is incompatible with non-local destination %s')
1070 % d
1070 % d
1071 )
1071 )
1072
1072
1073 return orig(ui, source, dest, **opts)
1073 return orig(ui, source, dest, **opts)
1074
1074
1075
1075
1076 @eh.wrapfunction(hg, b'clone')
1076 @eh.wrapfunction(hg, b'clone')
1077 def hgclone(orig, ui, opts, *args, **kwargs):
1077 def hgclone(orig, ui, opts, *args, **kwargs):
1078 result = orig(ui, opts, *args, **kwargs)
1078 result = orig(ui, opts, *args, **kwargs)
1079
1079
1080 if result is not None:
1080 if result is not None:
1081 sourcerepo, destrepo = result
1081 sourcerepo, destrepo = result
1082 repo = destrepo.local()
1082 repo = destrepo.local()
1083
1083
1084 # When cloning to a remote repo (like through SSH), no repo is available
1084 # When cloning to a remote repo (like through SSH), no repo is available
1085 # from the peer. Therefore the largefiles can't be downloaded and the
1085 # from the peer. Therefore the largefiles can't be downloaded and the
1086 # hgrc can't be updated.
1086 # hgrc can't be updated.
1087 if not repo:
1087 if not repo:
1088 return result
1088 return result
1089
1089
1090 # Caching is implicitly limited to 'rev' option, since the dest repo was
1090 # Caching is implicitly limited to 'rev' option, since the dest repo was
1091 # truncated at that point. The user may expect a download count with
1091 # truncated at that point. The user may expect a download count with
1092 # this option, so attempt whether or not this is a largefile repo.
1092 # this option, so attempt whether or not this is a largefile repo.
1093 if opts.get(b'all_largefiles'):
1093 if opts.get(b'all_largefiles'):
1094 success, missing = lfcommands.downloadlfiles(ui, repo, None)
1094 success, missing = lfcommands.downloadlfiles(ui, repo, None)
1095
1095
1096 if missing != 0:
1096 if missing != 0:
1097 return None
1097 return None
1098
1098
1099 return result
1099 return result
1100
1100
1101
1101
1102 @eh.wrapcommand(b'rebase', extension=b'rebase')
1102 @eh.wrapcommand(b'rebase', extension=b'rebase')
1103 def overriderebase(orig, ui, repo, **opts):
1103 def overriderebase(orig, ui, repo, **opts):
1104 if not util.safehasattr(repo, b'_largefilesenabled'):
1104 if not util.safehasattr(repo, b'_largefilesenabled'):
1105 return orig(ui, repo, **opts)
1105 return orig(ui, repo, **opts)
1106
1106
1107 resuming = opts.get(r'continue')
1107 resuming = opts.get(r'continue')
1108 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1108 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1109 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1109 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1110 try:
1110 try:
1111 return orig(ui, repo, **opts)
1111 return orig(ui, repo, **opts)
1112 finally:
1112 finally:
1113 repo._lfstatuswriters.pop()
1113 repo._lfstatuswriters.pop()
1114 repo._lfcommithooks.pop()
1114 repo._lfcommithooks.pop()
1115
1115
1116
1116
1117 @eh.wrapcommand(b'archive')
1117 @eh.wrapcommand(b'archive')
1118 def overridearchivecmd(orig, ui, repo, dest, **opts):
1118 def overridearchivecmd(orig, ui, repo, dest, **opts):
1119 with lfstatus(repo.unfiltered()):
1119 with lfstatus(repo.unfiltered()):
1120 return orig(ui, repo.unfiltered(), dest, **opts)
1120 return orig(ui, repo.unfiltered(), dest, **opts)
1121
1121
1122
1122
1123 @eh.wrapfunction(webcommands, b'archive')
1123 @eh.wrapfunction(webcommands, b'archive')
1124 def hgwebarchive(orig, web):
1124 def hgwebarchive(orig, web):
1125 with lfstatus(web.repo):
1125 with lfstatus(web.repo):
1126 return orig(web)
1126 return orig(web)
1127
1127
1128
1128
1129 @eh.wrapfunction(archival, b'archive')
1129 @eh.wrapfunction(archival, b'archive')
1130 def overridearchive(
1130 def overridearchive(
1131 orig,
1131 orig,
1132 repo,
1132 repo,
1133 dest,
1133 dest,
1134 node,
1134 node,
1135 kind,
1135 kind,
1136 decode=True,
1136 decode=True,
1137 match=None,
1137 match=None,
1138 prefix=b'',
1138 prefix=b'',
1139 mtime=None,
1139 mtime=None,
1140 subrepos=None,
1140 subrepos=None,
1141 ):
1141 ):
1142 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1142 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1143 # unfiltered repo's attr, so check that as well.
1143 # unfiltered repo's attr, so check that as well.
1144 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1144 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1145 return orig(
1145 return orig(
1146 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1146 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1147 )
1147 )
1148
1148
1149 # No need to lock because we are only reading history and
1149 # No need to lock because we are only reading history and
1150 # largefile caches, neither of which are modified.
1150 # largefile caches, neither of which are modified.
1151 if node is not None:
1151 if node is not None:
1152 lfcommands.cachelfiles(repo.ui, repo, node)
1152 lfcommands.cachelfiles(repo.ui, repo, node)
1153
1153
1154 if kind not in archival.archivers:
1154 if kind not in archival.archivers:
1155 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1155 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1156
1156
1157 ctx = repo[node]
1157 ctx = repo[node]
1158
1158
1159 if kind == b'files':
1159 if kind == b'files':
1160 if prefix:
1160 if prefix:
1161 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1161 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1162 else:
1162 else:
1163 prefix = archival.tidyprefix(dest, kind, prefix)
1163 prefix = archival.tidyprefix(dest, kind, prefix)
1164
1164
1165 def write(name, mode, islink, getdata):
1165 def write(name, mode, islink, getdata):
1166 if match and not match(name):
1166 if match and not match(name):
1167 return
1167 return
1168 data = getdata()
1168 data = getdata()
1169 if decode:
1169 if decode:
1170 data = repo.wwritedata(name, data)
1170 data = repo.wwritedata(name, data)
1171 archiver.addfile(prefix + name, mode, islink, data)
1171 archiver.addfile(prefix + name, mode, islink, data)
1172
1172
1173 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1173 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1174
1174
1175 if repo.ui.configbool(b"ui", b"archivemeta"):
1175 if repo.ui.configbool(b"ui", b"archivemeta"):
1176 write(
1176 write(
1177 b'.hg_archival.txt',
1177 b'.hg_archival.txt',
1178 0o644,
1178 0o644,
1179 False,
1179 False,
1180 lambda: archival.buildmetadata(ctx),
1180 lambda: archival.buildmetadata(ctx),
1181 )
1181 )
1182
1182
1183 for f in ctx:
1183 for f in ctx:
1184 ff = ctx.flags(f)
1184 ff = ctx.flags(f)
1185 getdata = ctx[f].data
1185 getdata = ctx[f].data
1186 lfile = lfutil.splitstandin(f)
1186 lfile = lfutil.splitstandin(f)
1187 if lfile is not None:
1187 if lfile is not None:
1188 if node is not None:
1188 if node is not None:
1189 path = lfutil.findfile(repo, getdata().strip())
1189 path = lfutil.findfile(repo, getdata().strip())
1190
1190
1191 if path is None:
1191 if path is None:
1192 raise error.Abort(
1192 raise error.Abort(
1193 _(
1193 _(
1194 b'largefile %s not found in repo store or system cache'
1194 b'largefile %s not found in repo store or system cache'
1195 )
1195 )
1196 % lfile
1196 % lfile
1197 )
1197 )
1198 else:
1198 else:
1199 path = lfile
1199 path = lfile
1200
1200
1201 f = lfile
1201 f = lfile
1202
1202
1203 getdata = lambda: util.readfile(path)
1203 getdata = lambda: util.readfile(path)
1204 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1204 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1205
1205
1206 if subrepos:
1206 if subrepos:
1207 for subpath in sorted(ctx.substate):
1207 for subpath in sorted(ctx.substate):
1208 sub = ctx.workingsub(subpath)
1208 sub = ctx.workingsub(subpath)
1209 submatch = matchmod.subdirmatcher(subpath, match)
1209 submatch = matchmod.subdirmatcher(subpath, match)
1210 subprefix = prefix + subpath + b'/'
1210 subprefix = prefix + subpath + b'/'
1211 with lfstatus(sub._repo):
1211
1212 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1213 # infer and possibly set lfstatus in hgsubrepoarchive. That would
1214 # allow only hgsubrepos to set this, instead of the current scheme
1215 # where the parent sets this for the child.
1216 with (
1217 util.safehasattr(sub, '_repo')
1218 and lfstatus(sub._repo)
1219 or util.nullcontextmanager()
1220 ):
1212 sub.archive(archiver, subprefix, submatch)
1221 sub.archive(archiver, subprefix, submatch)
1213
1222
1214 archiver.done()
1223 archiver.done()
1215
1224
1216
1225
1217 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1226 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1218 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1227 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1219 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1228 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1220 if not lfenabled or not repo._repo.lfstatus:
1229 if not lfenabled or not repo._repo.lfstatus:
1221 return orig(repo, archiver, prefix, match, decode)
1230 return orig(repo, archiver, prefix, match, decode)
1222
1231
1223 repo._get(repo._state + (b'hg',))
1232 repo._get(repo._state + (b'hg',))
1224 rev = repo._state[1]
1233 rev = repo._state[1]
1225 ctx = repo._repo[rev]
1234 ctx = repo._repo[rev]
1226
1235
1227 if ctx.node() is not None:
1236 if ctx.node() is not None:
1228 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1237 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1229
1238
1230 def write(name, mode, islink, getdata):
1239 def write(name, mode, islink, getdata):
1231 # At this point, the standin has been replaced with the largefile name,
1240 # At this point, the standin has been replaced with the largefile name,
1232 # so the normal matcher works here without the lfutil variants.
1241 # so the normal matcher works here without the lfutil variants.
1233 if match and not match(f):
1242 if match and not match(f):
1234 return
1243 return
1235 data = getdata()
1244 data = getdata()
1236 if decode:
1245 if decode:
1237 data = repo._repo.wwritedata(name, data)
1246 data = repo._repo.wwritedata(name, data)
1238
1247
1239 archiver.addfile(prefix + name, mode, islink, data)
1248 archiver.addfile(prefix + name, mode, islink, data)
1240
1249
1241 for f in ctx:
1250 for f in ctx:
1242 ff = ctx.flags(f)
1251 ff = ctx.flags(f)
1243 getdata = ctx[f].data
1252 getdata = ctx[f].data
1244 lfile = lfutil.splitstandin(f)
1253 lfile = lfutil.splitstandin(f)
1245 if lfile is not None:
1254 if lfile is not None:
1246 if ctx.node() is not None:
1255 if ctx.node() is not None:
1247 path = lfutil.findfile(repo._repo, getdata().strip())
1256 path = lfutil.findfile(repo._repo, getdata().strip())
1248
1257
1249 if path is None:
1258 if path is None:
1250 raise error.Abort(
1259 raise error.Abort(
1251 _(
1260 _(
1252 b'largefile %s not found in repo store or system cache'
1261 b'largefile %s not found in repo store or system cache'
1253 )
1262 )
1254 % lfile
1263 % lfile
1255 )
1264 )
1256 else:
1265 else:
1257 path = lfile
1266 path = lfile
1258
1267
1259 f = lfile
1268 f = lfile
1260
1269
1261 getdata = lambda: util.readfile(os.path.join(prefix, path))
1270 getdata = lambda: util.readfile(os.path.join(prefix, path))
1262
1271
1263 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1272 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1264
1273
1265 for subpath in sorted(ctx.substate):
1274 for subpath in sorted(ctx.substate):
1266 sub = ctx.workingsub(subpath)
1275 sub = ctx.workingsub(subpath)
1267 submatch = matchmod.subdirmatcher(subpath, match)
1276 submatch = matchmod.subdirmatcher(subpath, match)
1268 subprefix = prefix + subpath + b'/'
1277 subprefix = prefix + subpath + b'/'
1269 with lfstatus(sub._repo):
1278 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1279 # infer and possibly set lfstatus at the top of this function. That
1280 # would allow only hgsubrepos to set this, instead of the current scheme
1281 # where the parent sets this for the child.
1282 with (
1283 util.safehasattr(sub, '_repo')
1284 and lfstatus(sub._repo)
1285 or util.nullcontextmanager()
1286 ):
1270 sub.archive(archiver, subprefix, submatch, decode)
1287 sub.archive(archiver, subprefix, submatch, decode)
1271
1288
1272
1289
1273 # If a largefile is modified, the change is not reflected in its
1290 # If a largefile is modified, the change is not reflected in its
1274 # standin until a commit. cmdutil.bailifchanged() raises an exception
1291 # standin until a commit. cmdutil.bailifchanged() raises an exception
1275 # if the repo has uncommitted changes. Wrap it to also check if
1292 # if the repo has uncommitted changes. Wrap it to also check if
1276 # largefiles were changed. This is used by bisect, backout and fetch.
1293 # largefiles were changed. This is used by bisect, backout and fetch.
1277 @eh.wrapfunction(cmdutil, b'bailifchanged')
1294 @eh.wrapfunction(cmdutil, b'bailifchanged')
1278 def overridebailifchanged(orig, repo, *args, **kwargs):
1295 def overridebailifchanged(orig, repo, *args, **kwargs):
1279 orig(repo, *args, **kwargs)
1296 orig(repo, *args, **kwargs)
1280 with lfstatus(repo):
1297 with lfstatus(repo):
1281 s = repo.status()
1298 s = repo.status()
1282 if s.modified or s.added or s.removed or s.deleted:
1299 if s.modified or s.added or s.removed or s.deleted:
1283 raise error.Abort(_(b'uncommitted changes'))
1300 raise error.Abort(_(b'uncommitted changes'))
1284
1301
1285
1302
1286 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1303 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1287 def postcommitstatus(orig, repo, *args, **kwargs):
1304 def postcommitstatus(orig, repo, *args, **kwargs):
1288 with lfstatus(repo):
1305 with lfstatus(repo):
1289 return orig(repo, *args, **kwargs)
1306 return orig(repo, *args, **kwargs)
1290
1307
1291
1308
1292 @eh.wrapfunction(cmdutil, b'forget')
1309 @eh.wrapfunction(cmdutil, b'forget')
1293 def cmdutilforget(
1310 def cmdutilforget(
1294 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1311 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1295 ):
1312 ):
1296 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1313 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1297 bad, forgot = orig(
1314 bad, forgot = orig(
1298 ui,
1315 ui,
1299 repo,
1316 repo,
1300 normalmatcher,
1317 normalmatcher,
1301 prefix,
1318 prefix,
1302 uipathfn,
1319 uipathfn,
1303 explicitonly,
1320 explicitonly,
1304 dryrun,
1321 dryrun,
1305 interactive,
1322 interactive,
1306 )
1323 )
1307 m = composelargefilematcher(match, repo[None].manifest())
1324 m = composelargefilematcher(match, repo[None].manifest())
1308
1325
1309 with lfstatus(repo):
1326 with lfstatus(repo):
1310 s = repo.status(match=m, clean=True)
1327 s = repo.status(match=m, clean=True)
1311 manifest = repo[None].manifest()
1328 manifest = repo[None].manifest()
1312 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1329 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1313 forget = [f for f in forget if lfutil.standin(f) in manifest]
1330 forget = [f for f in forget if lfutil.standin(f) in manifest]
1314
1331
1315 for f in forget:
1332 for f in forget:
1316 fstandin = lfutil.standin(f)
1333 fstandin = lfutil.standin(f)
1317 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1334 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1318 ui.warn(
1335 ui.warn(
1319 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1336 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1320 )
1337 )
1321 bad.append(f)
1338 bad.append(f)
1322
1339
1323 for f in forget:
1340 for f in forget:
1324 if ui.verbose or not m.exact(f):
1341 if ui.verbose or not m.exact(f):
1325 ui.status(_(b'removing %s\n') % uipathfn(f))
1342 ui.status(_(b'removing %s\n') % uipathfn(f))
1326
1343
1327 # Need to lock because standin files are deleted then removed from the
1344 # Need to lock because standin files are deleted then removed from the
1328 # repository and we could race in-between.
1345 # repository and we could race in-between.
1329 with repo.wlock():
1346 with repo.wlock():
1330 lfdirstate = lfutil.openlfdirstate(ui, repo)
1347 lfdirstate = lfutil.openlfdirstate(ui, repo)
1331 for f in forget:
1348 for f in forget:
1332 if lfdirstate[f] == b'a':
1349 if lfdirstate[f] == b'a':
1333 lfdirstate.drop(f)
1350 lfdirstate.drop(f)
1334 else:
1351 else:
1335 lfdirstate.remove(f)
1352 lfdirstate.remove(f)
1336 lfdirstate.write()
1353 lfdirstate.write()
1337 standins = [lfutil.standin(f) for f in forget]
1354 standins = [lfutil.standin(f) for f in forget]
1338 for f in standins:
1355 for f in standins:
1339 repo.wvfs.unlinkpath(f, ignoremissing=True)
1356 repo.wvfs.unlinkpath(f, ignoremissing=True)
1340 rejected = repo[None].forget(standins)
1357 rejected = repo[None].forget(standins)
1341
1358
1342 bad.extend(f for f in rejected if f in m.files())
1359 bad.extend(f for f in rejected if f in m.files())
1343 forgot.extend(f for f in forget if f not in rejected)
1360 forgot.extend(f for f in forget if f not in rejected)
1344 return bad, forgot
1361 return bad, forgot
1345
1362
1346
1363
1347 def _getoutgoings(repo, other, missing, addfunc):
1364 def _getoutgoings(repo, other, missing, addfunc):
1348 """get pairs of filename and largefile hash in outgoing revisions
1365 """get pairs of filename and largefile hash in outgoing revisions
1349 in 'missing'.
1366 in 'missing'.
1350
1367
1351 largefiles already existing on 'other' repository are ignored.
1368 largefiles already existing on 'other' repository are ignored.
1352
1369
1353 'addfunc' is invoked with each unique pairs of filename and
1370 'addfunc' is invoked with each unique pairs of filename and
1354 largefile hash value.
1371 largefile hash value.
1355 """
1372 """
1356 knowns = set()
1373 knowns = set()
1357 lfhashes = set()
1374 lfhashes = set()
1358
1375
1359 def dedup(fn, lfhash):
1376 def dedup(fn, lfhash):
1360 k = (fn, lfhash)
1377 k = (fn, lfhash)
1361 if k not in knowns:
1378 if k not in knowns:
1362 knowns.add(k)
1379 knowns.add(k)
1363 lfhashes.add(lfhash)
1380 lfhashes.add(lfhash)
1364
1381
1365 lfutil.getlfilestoupload(repo, missing, dedup)
1382 lfutil.getlfilestoupload(repo, missing, dedup)
1366 if lfhashes:
1383 if lfhashes:
1367 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1384 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1368 for fn, lfhash in knowns:
1385 for fn, lfhash in knowns:
1369 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1386 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1370 addfunc(fn, lfhash)
1387 addfunc(fn, lfhash)
1371
1388
1372
1389
1373 def outgoinghook(ui, repo, other, opts, missing):
1390 def outgoinghook(ui, repo, other, opts, missing):
1374 if opts.pop(b'large', None):
1391 if opts.pop(b'large', None):
1375 lfhashes = set()
1392 lfhashes = set()
1376 if ui.debugflag:
1393 if ui.debugflag:
1377 toupload = {}
1394 toupload = {}
1378
1395
1379 def addfunc(fn, lfhash):
1396 def addfunc(fn, lfhash):
1380 if fn not in toupload:
1397 if fn not in toupload:
1381 toupload[fn] = []
1398 toupload[fn] = []
1382 toupload[fn].append(lfhash)
1399 toupload[fn].append(lfhash)
1383 lfhashes.add(lfhash)
1400 lfhashes.add(lfhash)
1384
1401
1385 def showhashes(fn):
1402 def showhashes(fn):
1386 for lfhash in sorted(toupload[fn]):
1403 for lfhash in sorted(toupload[fn]):
1387 ui.debug(b' %s\n' % lfhash)
1404 ui.debug(b' %s\n' % lfhash)
1388
1405
1389 else:
1406 else:
1390 toupload = set()
1407 toupload = set()
1391
1408
1392 def addfunc(fn, lfhash):
1409 def addfunc(fn, lfhash):
1393 toupload.add(fn)
1410 toupload.add(fn)
1394 lfhashes.add(lfhash)
1411 lfhashes.add(lfhash)
1395
1412
1396 def showhashes(fn):
1413 def showhashes(fn):
1397 pass
1414 pass
1398
1415
1399 _getoutgoings(repo, other, missing, addfunc)
1416 _getoutgoings(repo, other, missing, addfunc)
1400
1417
1401 if not toupload:
1418 if not toupload:
1402 ui.status(_(b'largefiles: no files to upload\n'))
1419 ui.status(_(b'largefiles: no files to upload\n'))
1403 else:
1420 else:
1404 ui.status(
1421 ui.status(
1405 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1422 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1406 )
1423 )
1407 for file in sorted(toupload):
1424 for file in sorted(toupload):
1408 ui.status(lfutil.splitstandin(file) + b'\n')
1425 ui.status(lfutil.splitstandin(file) + b'\n')
1409 showhashes(file)
1426 showhashes(file)
1410 ui.status(b'\n')
1427 ui.status(b'\n')
1411
1428
1412
1429
1413 @eh.wrapcommand(
1430 @eh.wrapcommand(
1414 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1431 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1415 )
1432 )
1416 def _outgoingcmd(orig, *args, **kwargs):
1433 def _outgoingcmd(orig, *args, **kwargs):
1417 # Nothing to do here other than add the extra help option- the hook above
1434 # Nothing to do here other than add the extra help option- the hook above
1418 # processes it.
1435 # processes it.
1419 return orig(*args, **kwargs)
1436 return orig(*args, **kwargs)
1420
1437
1421
1438
1422 def summaryremotehook(ui, repo, opts, changes):
1439 def summaryremotehook(ui, repo, opts, changes):
1423 largeopt = opts.get(b'large', False)
1440 largeopt = opts.get(b'large', False)
1424 if changes is None:
1441 if changes is None:
1425 if largeopt:
1442 if largeopt:
1426 return (False, True) # only outgoing check is needed
1443 return (False, True) # only outgoing check is needed
1427 else:
1444 else:
1428 return (False, False)
1445 return (False, False)
1429 elif largeopt:
1446 elif largeopt:
1430 url, branch, peer, outgoing = changes[1]
1447 url, branch, peer, outgoing = changes[1]
1431 if peer is None:
1448 if peer is None:
1432 # i18n: column positioning for "hg summary"
1449 # i18n: column positioning for "hg summary"
1433 ui.status(_(b'largefiles: (no remote repo)\n'))
1450 ui.status(_(b'largefiles: (no remote repo)\n'))
1434 return
1451 return
1435
1452
1436 toupload = set()
1453 toupload = set()
1437 lfhashes = set()
1454 lfhashes = set()
1438
1455
1439 def addfunc(fn, lfhash):
1456 def addfunc(fn, lfhash):
1440 toupload.add(fn)
1457 toupload.add(fn)
1441 lfhashes.add(lfhash)
1458 lfhashes.add(lfhash)
1442
1459
1443 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1460 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1444
1461
1445 if not toupload:
1462 if not toupload:
1446 # i18n: column positioning for "hg summary"
1463 # i18n: column positioning for "hg summary"
1447 ui.status(_(b'largefiles: (no files to upload)\n'))
1464 ui.status(_(b'largefiles: (no files to upload)\n'))
1448 else:
1465 else:
1449 # i18n: column positioning for "hg summary"
1466 # i18n: column positioning for "hg summary"
1450 ui.status(
1467 ui.status(
1451 _(b'largefiles: %d entities for %d files to upload\n')
1468 _(b'largefiles: %d entities for %d files to upload\n')
1452 % (len(lfhashes), len(toupload))
1469 % (len(lfhashes), len(toupload))
1453 )
1470 )
1454
1471
1455
1472
1456 @eh.wrapcommand(
1473 @eh.wrapcommand(
1457 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1474 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1458 )
1475 )
1459 def overridesummary(orig, ui, repo, *pats, **opts):
1476 def overridesummary(orig, ui, repo, *pats, **opts):
1460 with lfstatus(repo):
1477 with lfstatus(repo):
1461 orig(ui, repo, *pats, **opts)
1478 orig(ui, repo, *pats, **opts)
1462
1479
1463
1480
1464 @eh.wrapfunction(scmutil, b'addremove')
1481 @eh.wrapfunction(scmutil, b'addremove')
1465 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1482 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1466 if opts is None:
1483 if opts is None:
1467 opts = {}
1484 opts = {}
1468 if not lfutil.islfilesrepo(repo):
1485 if not lfutil.islfilesrepo(repo):
1469 return orig(repo, matcher, prefix, uipathfn, opts)
1486 return orig(repo, matcher, prefix, uipathfn, opts)
1470 # Get the list of missing largefiles so we can remove them
1487 # Get the list of missing largefiles so we can remove them
1471 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1488 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1472 unsure, s = lfdirstate.status(
1489 unsure, s = lfdirstate.status(
1473 matchmod.always(),
1490 matchmod.always(),
1474 subrepos=[],
1491 subrepos=[],
1475 ignored=False,
1492 ignored=False,
1476 clean=False,
1493 clean=False,
1477 unknown=False,
1494 unknown=False,
1478 )
1495 )
1479
1496
1480 # Call into the normal remove code, but the removing of the standin, we want
1497 # Call into the normal remove code, but the removing of the standin, we want
1481 # to have handled by original addremove. Monkey patching here makes sure
1498 # to have handled by original addremove. Monkey patching here makes sure
1482 # we don't remove the standin in the largefiles code, preventing a very
1499 # we don't remove the standin in the largefiles code, preventing a very
1483 # confused state later.
1500 # confused state later.
1484 if s.deleted:
1501 if s.deleted:
1485 m = copy.copy(matcher)
1502 m = copy.copy(matcher)
1486
1503
1487 # The m._files and m._map attributes are not changed to the deleted list
1504 # The m._files and m._map attributes are not changed to the deleted list
1488 # because that affects the m.exact() test, which in turn governs whether
1505 # because that affects the m.exact() test, which in turn governs whether
1489 # or not the file name is printed, and how. Simply limit the original
1506 # or not the file name is printed, and how. Simply limit the original
1490 # matches to those in the deleted status list.
1507 # matches to those in the deleted status list.
1491 matchfn = m.matchfn
1508 matchfn = m.matchfn
1492 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1509 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1493
1510
1494 removelargefiles(
1511 removelargefiles(
1495 repo.ui,
1512 repo.ui,
1496 repo,
1513 repo,
1497 True,
1514 True,
1498 m,
1515 m,
1499 uipathfn,
1516 uipathfn,
1500 opts.get(b'dry_run'),
1517 opts.get(b'dry_run'),
1501 **pycompat.strkwargs(opts)
1518 **pycompat.strkwargs(opts)
1502 )
1519 )
1503 # Call into the normal add code, and any files that *should* be added as
1520 # Call into the normal add code, and any files that *should* be added as
1504 # largefiles will be
1521 # largefiles will be
1505 added, bad = addlargefiles(
1522 added, bad = addlargefiles(
1506 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1523 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1507 )
1524 )
1508 # Now that we've handled largefiles, hand off to the original addremove
1525 # Now that we've handled largefiles, hand off to the original addremove
1509 # function to take care of the rest. Make sure it doesn't do anything with
1526 # function to take care of the rest. Make sure it doesn't do anything with
1510 # largefiles by passing a matcher that will ignore them.
1527 # largefiles by passing a matcher that will ignore them.
1511 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1528 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1512 return orig(repo, matcher, prefix, uipathfn, opts)
1529 return orig(repo, matcher, prefix, uipathfn, opts)
1513
1530
1514
1531
1515 # Calling purge with --all will cause the largefiles to be deleted.
1532 # Calling purge with --all will cause the largefiles to be deleted.
1516 # Override repo.status to prevent this from happening.
1533 # Override repo.status to prevent this from happening.
1517 @eh.wrapcommand(b'purge', extension=b'purge')
1534 @eh.wrapcommand(b'purge', extension=b'purge')
1518 def overridepurge(orig, ui, repo, *dirs, **opts):
1535 def overridepurge(orig, ui, repo, *dirs, **opts):
1519 # XXX Monkey patching a repoview will not work. The assigned attribute will
1536 # XXX Monkey patching a repoview will not work. The assigned attribute will
1520 # be set on the unfiltered repo, but we will only lookup attributes in the
1537 # be set on the unfiltered repo, but we will only lookup attributes in the
1521 # unfiltered repo if the lookup in the repoview object itself fails. As the
1538 # unfiltered repo if the lookup in the repoview object itself fails. As the
1522 # monkey patched method exists on the repoview class the lookup will not
1539 # monkey patched method exists on the repoview class the lookup will not
1523 # fail. As a result, the original version will shadow the monkey patched
1540 # fail. As a result, the original version will shadow the monkey patched
1524 # one, defeating the monkey patch.
1541 # one, defeating the monkey patch.
1525 #
1542 #
1526 # As a work around we use an unfiltered repo here. We should do something
1543 # As a work around we use an unfiltered repo here. We should do something
1527 # cleaner instead.
1544 # cleaner instead.
1528 repo = repo.unfiltered()
1545 repo = repo.unfiltered()
1529 oldstatus = repo.status
1546 oldstatus = repo.status
1530
1547
1531 def overridestatus(
1548 def overridestatus(
1532 node1=b'.',
1549 node1=b'.',
1533 node2=None,
1550 node2=None,
1534 match=None,
1551 match=None,
1535 ignored=False,
1552 ignored=False,
1536 clean=False,
1553 clean=False,
1537 unknown=False,
1554 unknown=False,
1538 listsubrepos=False,
1555 listsubrepos=False,
1539 ):
1556 ):
1540 r = oldstatus(
1557 r = oldstatus(
1541 node1, node2, match, ignored, clean, unknown, listsubrepos
1558 node1, node2, match, ignored, clean, unknown, listsubrepos
1542 )
1559 )
1543 lfdirstate = lfutil.openlfdirstate(ui, repo)
1560 lfdirstate = lfutil.openlfdirstate(ui, repo)
1544 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1561 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1545 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1562 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1546 return scmutil.status(
1563 return scmutil.status(
1547 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1564 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1548 )
1565 )
1549
1566
1550 repo.status = overridestatus
1567 repo.status = overridestatus
1551 orig(ui, repo, *dirs, **opts)
1568 orig(ui, repo, *dirs, **opts)
1552 repo.status = oldstatus
1569 repo.status = oldstatus
1553
1570
1554
1571
1555 @eh.wrapcommand(b'rollback')
1572 @eh.wrapcommand(b'rollback')
1556 def overriderollback(orig, ui, repo, **opts):
1573 def overriderollback(orig, ui, repo, **opts):
1557 with repo.wlock():
1574 with repo.wlock():
1558 before = repo.dirstate.parents()
1575 before = repo.dirstate.parents()
1559 orphans = set(
1576 orphans = set(
1560 f
1577 f
1561 for f in repo.dirstate
1578 for f in repo.dirstate
1562 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1579 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1563 )
1580 )
1564 result = orig(ui, repo, **opts)
1581 result = orig(ui, repo, **opts)
1565 after = repo.dirstate.parents()
1582 after = repo.dirstate.parents()
1566 if before == after:
1583 if before == after:
1567 return result # no need to restore standins
1584 return result # no need to restore standins
1568
1585
1569 pctx = repo[b'.']
1586 pctx = repo[b'.']
1570 for f in repo.dirstate:
1587 for f in repo.dirstate:
1571 if lfutil.isstandin(f):
1588 if lfutil.isstandin(f):
1572 orphans.discard(f)
1589 orphans.discard(f)
1573 if repo.dirstate[f] == b'r':
1590 if repo.dirstate[f] == b'r':
1574 repo.wvfs.unlinkpath(f, ignoremissing=True)
1591 repo.wvfs.unlinkpath(f, ignoremissing=True)
1575 elif f in pctx:
1592 elif f in pctx:
1576 fctx = pctx[f]
1593 fctx = pctx[f]
1577 repo.wwrite(f, fctx.data(), fctx.flags())
1594 repo.wwrite(f, fctx.data(), fctx.flags())
1578 else:
1595 else:
1579 # content of standin is not so important in 'a',
1596 # content of standin is not so important in 'a',
1580 # 'm' or 'n' (coming from the 2nd parent) cases
1597 # 'm' or 'n' (coming from the 2nd parent) cases
1581 lfutil.writestandin(repo, f, b'', False)
1598 lfutil.writestandin(repo, f, b'', False)
1582 for standin in orphans:
1599 for standin in orphans:
1583 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1600 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1584
1601
1585 lfdirstate = lfutil.openlfdirstate(ui, repo)
1602 lfdirstate = lfutil.openlfdirstate(ui, repo)
1586 orphans = set(lfdirstate)
1603 orphans = set(lfdirstate)
1587 lfiles = lfutil.listlfiles(repo)
1604 lfiles = lfutil.listlfiles(repo)
1588 for file in lfiles:
1605 for file in lfiles:
1589 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1606 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1590 orphans.discard(file)
1607 orphans.discard(file)
1591 for lfile in orphans:
1608 for lfile in orphans:
1592 lfdirstate.drop(lfile)
1609 lfdirstate.drop(lfile)
1593 lfdirstate.write()
1610 lfdirstate.write()
1594 return result
1611 return result
1595
1612
1596
1613
1597 @eh.wrapcommand(b'transplant', extension=b'transplant')
1614 @eh.wrapcommand(b'transplant', extension=b'transplant')
1598 def overridetransplant(orig, ui, repo, *revs, **opts):
1615 def overridetransplant(orig, ui, repo, *revs, **opts):
1599 resuming = opts.get(r'continue')
1616 resuming = opts.get(r'continue')
1600 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1617 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1601 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1618 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1602 try:
1619 try:
1603 result = orig(ui, repo, *revs, **opts)
1620 result = orig(ui, repo, *revs, **opts)
1604 finally:
1621 finally:
1605 repo._lfstatuswriters.pop()
1622 repo._lfstatuswriters.pop()
1606 repo._lfcommithooks.pop()
1623 repo._lfcommithooks.pop()
1607 return result
1624 return result
1608
1625
1609
1626
1610 @eh.wrapcommand(b'cat')
1627 @eh.wrapcommand(b'cat')
1611 def overridecat(orig, ui, repo, file1, *pats, **opts):
1628 def overridecat(orig, ui, repo, file1, *pats, **opts):
1612 opts = pycompat.byteskwargs(opts)
1629 opts = pycompat.byteskwargs(opts)
1613 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1630 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1614 err = 1
1631 err = 1
1615 notbad = set()
1632 notbad = set()
1616 m = scmutil.match(ctx, (file1,) + pats, opts)
1633 m = scmutil.match(ctx, (file1,) + pats, opts)
1617 origmatchfn = m.matchfn
1634 origmatchfn = m.matchfn
1618
1635
1619 def lfmatchfn(f):
1636 def lfmatchfn(f):
1620 if origmatchfn(f):
1637 if origmatchfn(f):
1621 return True
1638 return True
1622 lf = lfutil.splitstandin(f)
1639 lf = lfutil.splitstandin(f)
1623 if lf is None:
1640 if lf is None:
1624 return False
1641 return False
1625 notbad.add(lf)
1642 notbad.add(lf)
1626 return origmatchfn(lf)
1643 return origmatchfn(lf)
1627
1644
1628 m.matchfn = lfmatchfn
1645 m.matchfn = lfmatchfn
1629 origbadfn = m.bad
1646 origbadfn = m.bad
1630
1647
1631 def lfbadfn(f, msg):
1648 def lfbadfn(f, msg):
1632 if not f in notbad:
1649 if not f in notbad:
1633 origbadfn(f, msg)
1650 origbadfn(f, msg)
1634
1651
1635 m.bad = lfbadfn
1652 m.bad = lfbadfn
1636
1653
1637 origvisitdirfn = m.visitdir
1654 origvisitdirfn = m.visitdir
1638
1655
1639 def lfvisitdirfn(dir):
1656 def lfvisitdirfn(dir):
1640 if dir == lfutil.shortname:
1657 if dir == lfutil.shortname:
1641 return True
1658 return True
1642 ret = origvisitdirfn(dir)
1659 ret = origvisitdirfn(dir)
1643 if ret:
1660 if ret:
1644 return ret
1661 return ret
1645 lf = lfutil.splitstandin(dir)
1662 lf = lfutil.splitstandin(dir)
1646 if lf is None:
1663 if lf is None:
1647 return False
1664 return False
1648 return origvisitdirfn(lf)
1665 return origvisitdirfn(lf)
1649
1666
1650 m.visitdir = lfvisitdirfn
1667 m.visitdir = lfvisitdirfn
1651
1668
1652 for f in ctx.walk(m):
1669 for f in ctx.walk(m):
1653 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1670 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1654 lf = lfutil.splitstandin(f)
1671 lf = lfutil.splitstandin(f)
1655 if lf is None or origmatchfn(f):
1672 if lf is None or origmatchfn(f):
1656 # duplicating unreachable code from commands.cat
1673 # duplicating unreachable code from commands.cat
1657 data = ctx[f].data()
1674 data = ctx[f].data()
1658 if opts.get(b'decode'):
1675 if opts.get(b'decode'):
1659 data = repo.wwritedata(f, data)
1676 data = repo.wwritedata(f, data)
1660 fp.write(data)
1677 fp.write(data)
1661 else:
1678 else:
1662 hash = lfutil.readasstandin(ctx[f])
1679 hash = lfutil.readasstandin(ctx[f])
1663 if not lfutil.inusercache(repo.ui, hash):
1680 if not lfutil.inusercache(repo.ui, hash):
1664 store = storefactory.openstore(repo)
1681 store = storefactory.openstore(repo)
1665 success, missing = store.get([(lf, hash)])
1682 success, missing = store.get([(lf, hash)])
1666 if len(success) != 1:
1683 if len(success) != 1:
1667 raise error.Abort(
1684 raise error.Abort(
1668 _(
1685 _(
1669 b'largefile %s is not in cache and could not be '
1686 b'largefile %s is not in cache and could not be '
1670 b'downloaded'
1687 b'downloaded'
1671 )
1688 )
1672 % lf
1689 % lf
1673 )
1690 )
1674 path = lfutil.usercachepath(repo.ui, hash)
1691 path = lfutil.usercachepath(repo.ui, hash)
1675 with open(path, b"rb") as fpin:
1692 with open(path, b"rb") as fpin:
1676 for chunk in util.filechunkiter(fpin):
1693 for chunk in util.filechunkiter(fpin):
1677 fp.write(chunk)
1694 fp.write(chunk)
1678 err = 0
1695 err = 0
1679 return err
1696 return err
1680
1697
1681
1698
1682 @eh.wrapfunction(merge, b'update')
1699 @eh.wrapfunction(merge, b'update')
1683 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1700 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1684 matcher = kwargs.get(r'matcher', None)
1701 matcher = kwargs.get(r'matcher', None)
1685 # note if this is a partial update
1702 # note if this is a partial update
1686 partial = matcher and not matcher.always()
1703 partial = matcher and not matcher.always()
1687 with repo.wlock():
1704 with repo.wlock():
1688 # branch | | |
1705 # branch | | |
1689 # merge | force | partial | action
1706 # merge | force | partial | action
1690 # -------+-------+---------+--------------
1707 # -------+-------+---------+--------------
1691 # x | x | x | linear-merge
1708 # x | x | x | linear-merge
1692 # o | x | x | branch-merge
1709 # o | x | x | branch-merge
1693 # x | o | x | overwrite (as clean update)
1710 # x | o | x | overwrite (as clean update)
1694 # o | o | x | force-branch-merge (*1)
1711 # o | o | x | force-branch-merge (*1)
1695 # x | x | o | (*)
1712 # x | x | o | (*)
1696 # o | x | o | (*)
1713 # o | x | o | (*)
1697 # x | o | o | overwrite (as revert)
1714 # x | o | o | overwrite (as revert)
1698 # o | o | o | (*)
1715 # o | o | o | (*)
1699 #
1716 #
1700 # (*) don't care
1717 # (*) don't care
1701 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1718 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1702
1719
1703 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1720 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1704 unsure, s = lfdirstate.status(
1721 unsure, s = lfdirstate.status(
1705 matchmod.always(),
1722 matchmod.always(),
1706 subrepos=[],
1723 subrepos=[],
1707 ignored=False,
1724 ignored=False,
1708 clean=True,
1725 clean=True,
1709 unknown=False,
1726 unknown=False,
1710 )
1727 )
1711 oldclean = set(s.clean)
1728 oldclean = set(s.clean)
1712 pctx = repo[b'.']
1729 pctx = repo[b'.']
1713 dctx = repo[node]
1730 dctx = repo[node]
1714 for lfile in unsure + s.modified:
1731 for lfile in unsure + s.modified:
1715 lfileabs = repo.wvfs.join(lfile)
1732 lfileabs = repo.wvfs.join(lfile)
1716 if not repo.wvfs.exists(lfileabs):
1733 if not repo.wvfs.exists(lfileabs):
1717 continue
1734 continue
1718 lfhash = lfutil.hashfile(lfileabs)
1735 lfhash = lfutil.hashfile(lfileabs)
1719 standin = lfutil.standin(lfile)
1736 standin = lfutil.standin(lfile)
1720 lfutil.writestandin(
1737 lfutil.writestandin(
1721 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1738 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1722 )
1739 )
1723 if standin in pctx and lfhash == lfutil.readasstandin(
1740 if standin in pctx and lfhash == lfutil.readasstandin(
1724 pctx[standin]
1741 pctx[standin]
1725 ):
1742 ):
1726 oldclean.add(lfile)
1743 oldclean.add(lfile)
1727 for lfile in s.added:
1744 for lfile in s.added:
1728 fstandin = lfutil.standin(lfile)
1745 fstandin = lfutil.standin(lfile)
1729 if fstandin not in dctx:
1746 if fstandin not in dctx:
1730 # in this case, content of standin file is meaningless
1747 # in this case, content of standin file is meaningless
1731 # (in dctx, lfile is unknown, or normal file)
1748 # (in dctx, lfile is unknown, or normal file)
1732 continue
1749 continue
1733 lfutil.updatestandin(repo, lfile, fstandin)
1750 lfutil.updatestandin(repo, lfile, fstandin)
1734 # mark all clean largefiles as dirty, just in case the update gets
1751 # mark all clean largefiles as dirty, just in case the update gets
1735 # interrupted before largefiles and lfdirstate are synchronized
1752 # interrupted before largefiles and lfdirstate are synchronized
1736 for lfile in oldclean:
1753 for lfile in oldclean:
1737 lfdirstate.normallookup(lfile)
1754 lfdirstate.normallookup(lfile)
1738 lfdirstate.write()
1755 lfdirstate.write()
1739
1756
1740 oldstandins = lfutil.getstandinsstate(repo)
1757 oldstandins = lfutil.getstandinsstate(repo)
1741 # Make sure the merge runs on disk, not in-memory. largefiles is not a
1758 # Make sure the merge runs on disk, not in-memory. largefiles is not a
1742 # good candidate for in-memory merge (large files, custom dirstate,
1759 # good candidate for in-memory merge (large files, custom dirstate,
1743 # matcher usage).
1760 # matcher usage).
1744 kwargs[r'wc'] = repo[None]
1761 kwargs[r'wc'] = repo[None]
1745 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1762 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1746
1763
1747 newstandins = lfutil.getstandinsstate(repo)
1764 newstandins = lfutil.getstandinsstate(repo)
1748 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1765 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1749
1766
1750 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1767 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1751 # all the ones that didn't change as clean
1768 # all the ones that didn't change as clean
1752 for lfile in oldclean.difference(filelist):
1769 for lfile in oldclean.difference(filelist):
1753 lfdirstate.normal(lfile)
1770 lfdirstate.normal(lfile)
1754 lfdirstate.write()
1771 lfdirstate.write()
1755
1772
1756 if branchmerge or force or partial:
1773 if branchmerge or force or partial:
1757 filelist.extend(s.deleted + s.removed)
1774 filelist.extend(s.deleted + s.removed)
1758
1775
1759 lfcommands.updatelfiles(
1776 lfcommands.updatelfiles(
1760 repo.ui, repo, filelist=filelist, normallookup=partial
1777 repo.ui, repo, filelist=filelist, normallookup=partial
1761 )
1778 )
1762
1779
1763 return result
1780 return result
1764
1781
1765
1782
1766 @eh.wrapfunction(scmutil, b'marktouched')
1783 @eh.wrapfunction(scmutil, b'marktouched')
1767 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1784 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1768 result = orig(repo, files, *args, **kwargs)
1785 result = orig(repo, files, *args, **kwargs)
1769
1786
1770 filelist = []
1787 filelist = []
1771 for f in files:
1788 for f in files:
1772 lf = lfutil.splitstandin(f)
1789 lf = lfutil.splitstandin(f)
1773 if lf is not None:
1790 if lf is not None:
1774 filelist.append(lf)
1791 filelist.append(lf)
1775 if filelist:
1792 if filelist:
1776 lfcommands.updatelfiles(
1793 lfcommands.updatelfiles(
1777 repo.ui,
1794 repo.ui,
1778 repo,
1795 repo,
1779 filelist=filelist,
1796 filelist=filelist,
1780 printmessage=False,
1797 printmessage=False,
1781 normallookup=True,
1798 normallookup=True,
1782 )
1799 )
1783
1800
1784 return result
1801 return result
1785
1802
1786
1803
1787 @eh.wrapfunction(upgrade, b'preservedrequirements')
1804 @eh.wrapfunction(upgrade, b'preservedrequirements')
1788 @eh.wrapfunction(upgrade, b'supporteddestrequirements')
1805 @eh.wrapfunction(upgrade, b'supporteddestrequirements')
1789 def upgraderequirements(orig, repo):
1806 def upgraderequirements(orig, repo):
1790 reqs = orig(repo)
1807 reqs = orig(repo)
1791 if b'largefiles' in repo.requirements:
1808 if b'largefiles' in repo.requirements:
1792 reqs.add(b'largefiles')
1809 reqs.add(b'largefiles')
1793 return reqs
1810 return reqs
1794
1811
1795
1812
1796 _lfscheme = b'largefile://'
1813 _lfscheme = b'largefile://'
1797
1814
1798
1815
1799 @eh.wrapfunction(urlmod, b'open')
1816 @eh.wrapfunction(urlmod, b'open')
1800 def openlargefile(orig, ui, url_, data=None):
1817 def openlargefile(orig, ui, url_, data=None):
1801 if url_.startswith(_lfscheme):
1818 if url_.startswith(_lfscheme):
1802 if data:
1819 if data:
1803 msg = b"cannot use data on a 'largefile://' url"
1820 msg = b"cannot use data on a 'largefile://' url"
1804 raise error.ProgrammingError(msg)
1821 raise error.ProgrammingError(msg)
1805 lfid = url_[len(_lfscheme) :]
1822 lfid = url_[len(_lfscheme) :]
1806 return storefactory.getlfile(ui, lfid)
1823 return storefactory.getlfile(ui, lfid)
1807 else:
1824 else:
1808 return orig(ui, url_, data=data)
1825 return orig(ui, url_, data=data)
@@ -1,1262 +1,1271 b''
1 #require git
1 #require git
2
2
3 make git commits repeatable
3 make git commits repeatable
4
4
5 $ cat >> $HGRCPATH <<EOF
5 $ cat >> $HGRCPATH <<EOF
6 > [defaults]
6 > [defaults]
7 > commit = -d "0 0"
7 > commit = -d "0 0"
8 > EOF
8 > EOF
9
9
10 $ echo "[core]" >> $HOME/.gitconfig
10 $ echo "[core]" >> $HOME/.gitconfig
11 $ echo "autocrlf = false" >> $HOME/.gitconfig
11 $ echo "autocrlf = false" >> $HOME/.gitconfig
12 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
12 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
13 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
13 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
14 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
14 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
15 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
15 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
16 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
16 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
17 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
17 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
18 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
18 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
19
19
20 root hg repo
20 root hg repo
21
21
22 $ hg init t
22 $ hg init t
23 $ cd t
23 $ cd t
24 $ echo a > a
24 $ echo a > a
25 $ hg add a
25 $ hg add a
26 $ hg commit -m a
26 $ hg commit -m a
27 $ cd ..
27 $ cd ..
28
28
29 new external git repo
29 new external git repo
30
30
31 $ mkdir gitroot
31 $ mkdir gitroot
32 $ cd gitroot
32 $ cd gitroot
33 $ git init -q
33 $ git init -q
34 $ echo g > g
34 $ echo g > g
35 $ git add g
35 $ git add g
36 $ git commit -q -m g
36 $ git commit -q -m g
37
37
38 add subrepo clone
38 add subrepo clone
39
39
40 $ cd ../t
40 $ cd ../t
41 $ echo 's = [git]../gitroot' > .hgsub
41 $ echo 's = [git]../gitroot' > .hgsub
42 $ git clone -q ../gitroot s
42 $ git clone -q ../gitroot s
43 $ hg add .hgsub
43 $ hg add .hgsub
44
44
45 git subrepo is disabled by default
45 git subrepo is disabled by default
46
46
47 $ hg commit -m 'new git subrepo'
47 $ hg commit -m 'new git subrepo'
48 abort: git subrepos not allowed
48 abort: git subrepos not allowed
49 (see 'hg help config.subrepos' for details)
49 (see 'hg help config.subrepos' for details)
50 [255]
50 [255]
51
51
52 so enable it
52 so enable it
53
53
54 $ cat >> $HGRCPATH <<EOF
54 $ cat >> $HGRCPATH <<EOF
55 > [subrepos]
55 > [subrepos]
56 > git:allowed = true
56 > git:allowed = true
57 > EOF
57 > EOF
58
58
59 $ hg commit -m 'new git subrepo'
59 $ hg commit -m 'new git subrepo'
60
60
61 $ hg debugsub
61 $ hg debugsub
62 path s
62 path s
63 source ../gitroot
63 source ../gitroot
64 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
64 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
65
65
66 record a new commit from upstream from a different branch
66 record a new commit from upstream from a different branch
67
67
68 $ cd ../gitroot
68 $ cd ../gitroot
69 $ git checkout -q -b testing
69 $ git checkout -q -b testing
70 $ echo gg >> g
70 $ echo gg >> g
71 $ git commit -q -a -m gg
71 $ git commit -q -a -m gg
72
72
73 $ cd ../t/s
73 $ cd ../t/s
74 $ git pull -q >/dev/null 2>/dev/null
74 $ git pull -q >/dev/null 2>/dev/null
75 $ git checkout -q -b testing origin/testing >/dev/null
75 $ git checkout -q -b testing origin/testing >/dev/null
76
76
77 $ cd ..
77 $ cd ..
78 $ hg status --subrepos
78 $ hg status --subrepos
79 M s/g
79 M s/g
80 $ hg commit -m 'update git subrepo'
80 $ hg commit -m 'update git subrepo'
81 $ hg debugsub
81 $ hg debugsub
82 path s
82 path s
83 source ../gitroot
83 source ../gitroot
84 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
84 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
85
85
86 make $GITROOT pushable, by replacing it with a clone with nothing checked out
86 make $GITROOT pushable, by replacing it with a clone with nothing checked out
87
87
88 $ cd ..
88 $ cd ..
89 $ git clone gitroot gitrootbare --bare -q
89 $ git clone gitroot gitrootbare --bare -q
90 $ rm -rf gitroot
90 $ rm -rf gitroot
91 $ mv gitrootbare gitroot
91 $ mv gitrootbare gitroot
92
92
93 clone root
93 clone root
94
94
95 $ cd t
95 $ cd t
96 $ hg clone . ../tc 2> /dev/null
96 $ hg clone . ../tc 2> /dev/null
97 updating to branch default
97 updating to branch default
98 cloning subrepo s from $TESTTMP/gitroot
98 cloning subrepo s from $TESTTMP/gitroot
99 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 $ cd ../tc
100 $ cd ../tc
101 $ hg debugsub
101 $ hg debugsub
102 path s
102 path s
103 source ../gitroot
103 source ../gitroot
104 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
104 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
105 $ cd ..
105 $ cd ..
106
106
107 clone with subrepo disabled (update should fail)
107 clone with subrepo disabled (update should fail)
108
108
109 $ hg clone t -U tc2 --config subrepos.allowed=false
109 $ hg clone t -U tc2 --config subrepos.allowed=false
110 $ hg update -R tc2 --config subrepos.allowed=false
110 $ hg update -R tc2 --config subrepos.allowed=false
111 abort: subrepos not enabled
111 abort: subrepos not enabled
112 (see 'hg help config.subrepos' for details)
112 (see 'hg help config.subrepos' for details)
113 [255]
113 [255]
114 $ ls tc2
114 $ ls tc2
115 a
115 a
116
116
117 $ hg clone t tc3 --config subrepos.allowed=false
117 $ hg clone t tc3 --config subrepos.allowed=false
118 updating to branch default
118 updating to branch default
119 abort: subrepos not enabled
119 abort: subrepos not enabled
120 (see 'hg help config.subrepos' for details)
120 (see 'hg help config.subrepos' for details)
121 [255]
121 [255]
122 $ ls tc3
122 $ ls tc3
123 a
123 a
124
124
125 update to previous substate
125 update to previous substate
126
126
127 $ cd tc
127 $ cd tc
128 $ hg update 1 -q
128 $ hg update 1 -q
129 $ cat s/g
129 $ cat s/g
130 g
130 g
131 $ hg debugsub
131 $ hg debugsub
132 path s
132 path s
133 source ../gitroot
133 source ../gitroot
134 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
134 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
135
135
136 clone root, make local change
136 clone root, make local change
137
137
138 $ cd ../t
138 $ cd ../t
139 $ hg clone . ../ta 2> /dev/null
139 $ hg clone . ../ta 2> /dev/null
140 updating to branch default
140 updating to branch default
141 cloning subrepo s from $TESTTMP/gitroot
141 cloning subrepo s from $TESTTMP/gitroot
142 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
143
143
144 $ cd ../ta
144 $ cd ../ta
145 $ echo ggg >> s/g
145 $ echo ggg >> s/g
146 $ hg status --subrepos
146 $ hg status --subrepos
147 M s/g
147 M s/g
148 $ hg diff --subrepos
148 $ hg diff --subrepos
149 diff --git a/s/g b/s/g
149 diff --git a/s/g b/s/g
150 index 089258f..85341ee 100644
150 index 089258f..85341ee 100644
151 --- a/s/g
151 --- a/s/g
152 +++ b/s/g
152 +++ b/s/g
153 @@ -1,2 +1,3 @@
153 @@ -1,2 +1,3 @@
154 g
154 g
155 gg
155 gg
156 +ggg
156 +ggg
157 $ hg commit --subrepos -m ggg
157 $ hg commit --subrepos -m ggg
158 committing subrepository s
158 committing subrepository s
159 $ hg debugsub
159 $ hg debugsub
160 path s
160 path s
161 source ../gitroot
161 source ../gitroot
162 revision 79695940086840c99328513acbe35f90fcd55e57
162 revision 79695940086840c99328513acbe35f90fcd55e57
163
163
164 clone root separately, make different local change
164 clone root separately, make different local change
165
165
166 $ cd ../t
166 $ cd ../t
167 $ hg clone . ../tb 2> /dev/null
167 $ hg clone . ../tb 2> /dev/null
168 updating to branch default
168 updating to branch default
169 cloning subrepo s from $TESTTMP/gitroot
169 cloning subrepo s from $TESTTMP/gitroot
170 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
171
171
172 $ cd ../tb/s
172 $ cd ../tb/s
173 $ hg status --subrepos
173 $ hg status --subrepos
174 $ echo f > f
174 $ echo f > f
175 $ hg status --subrepos
175 $ hg status --subrepos
176 ? s/f
176 ? s/f
177 $ hg add .
177 $ hg add .
178 adding f
178 adding f
179 $ git add f
179 $ git add f
180 $ cd ..
180 $ cd ..
181
181
182 $ hg status --subrepos
182 $ hg status --subrepos
183 A s/f
183 A s/f
184 $ hg commit --subrepos -m f
184 $ hg commit --subrepos -m f
185 committing subrepository s
185 committing subrepository s
186 $ hg debugsub
186 $ hg debugsub
187 path s
187 path s
188 source ../gitroot
188 source ../gitroot
189 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
189 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
190
190
191 user b push changes
191 user b push changes
192
192
193 $ hg push 2>/dev/null
193 $ hg push 2>/dev/null
194 pushing to $TESTTMP/t
194 pushing to $TESTTMP/t
195 pushing branch testing of subrepository "s"
195 pushing branch testing of subrepository "s"
196 searching for changes
196 searching for changes
197 adding changesets
197 adding changesets
198 adding manifests
198 adding manifests
199 adding file changes
199 adding file changes
200 added 1 changesets with 1 changes to 1 files
200 added 1 changesets with 1 changes to 1 files
201
201
202 user a pulls, merges, commits
202 user a pulls, merges, commits
203
203
204 $ cd ../ta
204 $ cd ../ta
205 $ hg pull
205 $ hg pull
206 pulling from $TESTTMP/t
206 pulling from $TESTTMP/t
207 searching for changes
207 searching for changes
208 adding changesets
208 adding changesets
209 adding manifests
209 adding manifests
210 adding file changes
210 adding file changes
211 added 1 changesets with 1 changes to 1 files (+1 heads)
211 added 1 changesets with 1 changes to 1 files (+1 heads)
212 new changesets 089416c11d73
212 new changesets 089416c11d73
213 (run 'hg heads' to see heads, 'hg merge' to merge)
213 (run 'hg heads' to see heads, 'hg merge' to merge)
214 $ hg merge 2>/dev/null
214 $ hg merge 2>/dev/null
215 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
215 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
216 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev].
216 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev].
217 what do you want to do? m
217 what do you want to do? m
218 pulling subrepo s from $TESTTMP/gitroot
218 pulling subrepo s from $TESTTMP/gitroot
219 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 (branch merge, don't forget to commit)
220 (branch merge, don't forget to commit)
221 $ hg st --subrepos s
221 $ hg st --subrepos s
222 A s/f
222 A s/f
223 $ cat s/f
223 $ cat s/f
224 f
224 f
225 $ cat s/g
225 $ cat s/g
226 g
226 g
227 gg
227 gg
228 ggg
228 ggg
229 $ hg commit --subrepos -m 'merge'
229 $ hg commit --subrepos -m 'merge'
230 committing subrepository s
230 committing subrepository s
231 $ hg status --subrepos --rev 1:5
231 $ hg status --subrepos --rev 1:5
232 M .hgsubstate
232 M .hgsubstate
233 M s/g
233 M s/g
234 A s/f
234 A s/f
235 $ hg debugsub
235 $ hg debugsub
236 path s
236 path s
237 source ../gitroot
237 source ../gitroot
238 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
238 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
239 $ hg push 2>/dev/null
239 $ hg push 2>/dev/null
240 pushing to $TESTTMP/t
240 pushing to $TESTTMP/t
241 pushing branch testing of subrepository "s"
241 pushing branch testing of subrepository "s"
242 searching for changes
242 searching for changes
243 adding changesets
243 adding changesets
244 adding manifests
244 adding manifests
245 adding file changes
245 adding file changes
246 added 2 changesets with 2 changes to 1 files
246 added 2 changesets with 2 changes to 1 files
247
247
248 make upstream git changes
248 make upstream git changes
249
249
250 $ cd ..
250 $ cd ..
251 $ git clone -q gitroot gitclone
251 $ git clone -q gitroot gitclone
252 $ cd gitclone
252 $ cd gitclone
253 $ echo ff >> f
253 $ echo ff >> f
254 $ git commit -q -a -m ff
254 $ git commit -q -a -m ff
255 $ echo fff >> f
255 $ echo fff >> f
256 $ git commit -q -a -m fff
256 $ git commit -q -a -m fff
257 $ git push origin testing 2>/dev/null
257 $ git push origin testing 2>/dev/null
258
258
259 make and push changes to hg without updating the subrepo
259 make and push changes to hg without updating the subrepo
260
260
261 $ cd ../t
261 $ cd ../t
262 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
262 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
263 updating to branch default
263 updating to branch default
264 cloning subrepo s from $TESTTMP/gitroot
264 cloning subrepo s from $TESTTMP/gitroot
265 checking out detached HEAD in subrepository "s"
265 checking out detached HEAD in subrepository "s"
266 check out a git branch if you intend to make changes
266 check out a git branch if you intend to make changes
267 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
268 $ cd ../td
268 $ cd ../td
269 $ echo aa >> a
269 $ echo aa >> a
270 $ hg commit -m aa
270 $ hg commit -m aa
271 $ hg push
271 $ hg push
272 pushing to $TESTTMP/t
272 pushing to $TESTTMP/t
273 searching for changes
273 searching for changes
274 adding changesets
274 adding changesets
275 adding manifests
275 adding manifests
276 adding file changes
276 adding file changes
277 added 1 changesets with 1 changes to 1 files
277 added 1 changesets with 1 changes to 1 files
278
278
279 sync to upstream git, distribute changes
279 sync to upstream git, distribute changes
280
280
281 $ cd ../ta
281 $ cd ../ta
282 $ hg pull -u -q
282 $ hg pull -u -q
283 $ cd s
283 $ cd s
284 $ git pull -q >/dev/null 2>/dev/null
284 $ git pull -q >/dev/null 2>/dev/null
285 $ cd ..
285 $ cd ..
286 $ hg commit -m 'git upstream sync'
286 $ hg commit -m 'git upstream sync'
287 $ hg debugsub
287 $ hg debugsub
288 path s
288 path s
289 source ../gitroot
289 source ../gitroot
290 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
290 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
291 $ hg push -q
291 $ hg push -q
292
292
293 $ cd ../tb
293 $ cd ../tb
294 $ hg pull -q
294 $ hg pull -q
295 $ hg update 2>/dev/null
295 $ hg update 2>/dev/null
296 pulling subrepo s from $TESTTMP/gitroot
296 pulling subrepo s from $TESTTMP/gitroot
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 $ hg debugsub
298 $ hg debugsub
299 path s
299 path s
300 source ../gitroot
300 source ../gitroot
301 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
301 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
302
302
303 create a new git branch
303 create a new git branch
304
304
305 $ cd s
305 $ cd s
306 $ git checkout -b b2
306 $ git checkout -b b2
307 Switched to a new branch 'b2'
307 Switched to a new branch 'b2'
308 $ echo a>a
308 $ echo a>a
309 $ git add a
309 $ git add a
310 $ git commit -qm 'add a'
310 $ git commit -qm 'add a'
311 $ cd ..
311 $ cd ..
312 $ hg commit -m 'add branch in s'
312 $ hg commit -m 'add branch in s'
313
313
314 pulling new git branch should not create tracking branch named 'origin/b2'
314 pulling new git branch should not create tracking branch named 'origin/b2'
315 (issue3870)
315 (issue3870)
316 $ cd ../td/s
316 $ cd ../td/s
317 $ git remote set-url origin $TESTTMP/tb/s
317 $ git remote set-url origin $TESTTMP/tb/s
318 $ git branch --no-track oldtesting
318 $ git branch --no-track oldtesting
319 $ cd ..
319 $ cd ..
320 $ hg pull -q ../tb
320 $ hg pull -q ../tb
321 $ hg up
321 $ hg up
322 From $TESTTMP/tb/s
322 From $TESTTMP/tb/s
323 * [new branch] b2 -> origin/b2
323 * [new branch] b2 -> origin/b2
324 Previous HEAD position was f47b465* merge (glob)
324 Previous HEAD position was f47b465* merge (glob)
325 Switched to a new branch 'b2'
325 Switched to a new branch 'b2'
326 pulling subrepo s from $TESTTMP/tb/s
326 pulling subrepo s from $TESTTMP/tb/s
327 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
328
328
329 update to a revision without the subrepo, keeping the local git repository
329 update to a revision without the subrepo, keeping the local git repository
330
330
331 $ cd ../t
331 $ cd ../t
332 $ hg up 0
332 $ hg up 0
333 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
333 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
334 $ ls -a s
334 $ ls -a s
335 .
335 .
336 ..
336 ..
337 .git
337 .git
338
338
339 $ hg up 2
339 $ hg up 2
340 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 $ ls -a s
341 $ ls -a s
342 .
342 .
343 ..
343 ..
344 .git
344 .git
345 g
345 g
346
346
347 archive subrepos
347 archive subrepos
348
348
349 $ cd ../tc
349 $ cd ../tc
350 $ hg pull -q
350 $ hg pull -q
351 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
351 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
352 pulling subrepo s from $TESTTMP/gitroot
352 pulling subrepo s from $TESTTMP/gitroot
353 $ cd ../archive
353 $ cd ../archive
354 $ cat s/f
354 $ cat s/f
355 f
355 f
356 $ cat s/g
356 $ cat s/g
357 g
357 g
358 gg
358 gg
359 ggg
359 ggg
360
360
361 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
361 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
362 $ find ../archive_x | sort | grep -v pax_global_header
362 $ find ../archive_x | sort | grep -v pax_global_header
363 ../archive_x
363 ../archive_x
364 ../archive_x/.hg_archival.txt
364 ../archive_x/.hg_archival.txt
365 ../archive_x/.hgsub
365 ../archive_x/.hgsub
366 ../archive_x/.hgsubstate
366 ../archive_x/.hgsubstate
367 ../archive_x/a
367 ../archive_x/a
368 ../archive_x/s
368 ../archive_x/s
369 ../archive_x/s/g
369 ../archive_x/s/g
370
370
371 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
371 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
372 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
372 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
373 .hg_archival.txt
373 .hg_archival.txt
374 .hgsub
374 .hgsub
375 .hgsubstate
375 .hgsubstate
376 a
376 a
377 s/g
377 s/g
378
378
379 $ hg -R ../tc archive -S ../lf_archive.tgz --prefix '.' \
380 > --config extensions.largefiles= 2>/dev/null
381 $ tar -tzf ../lf_archive.tgz | sort | grep -v pax_global_header
382 .hg_archival.txt
383 .hgsub
384 .hgsubstate
385 a
386 s/g
387
379 create nested repo
388 create nested repo
380
389
381 $ cd ..
390 $ cd ..
382 $ hg init outer
391 $ hg init outer
383 $ cd outer
392 $ cd outer
384 $ echo b>b
393 $ echo b>b
385 $ hg add b
394 $ hg add b
386 $ hg commit -m b
395 $ hg commit -m b
387
396
388 $ hg clone ../t inner 2> /dev/null
397 $ hg clone ../t inner 2> /dev/null
389 updating to branch default
398 updating to branch default
390 cloning subrepo s from $TESTTMP/gitroot
399 cloning subrepo s from $TESTTMP/gitroot
391 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 $ echo inner = inner > .hgsub
401 $ echo inner = inner > .hgsub
393 $ hg add .hgsub
402 $ hg add .hgsub
394 $ hg commit -m 'nested sub'
403 $ hg commit -m 'nested sub'
395
404
396 nested commit
405 nested commit
397
406
398 $ echo ffff >> inner/s/f
407 $ echo ffff >> inner/s/f
399 $ hg status --subrepos
408 $ hg status --subrepos
400 M inner/s/f
409 M inner/s/f
401 $ hg commit --subrepos -m nested
410 $ hg commit --subrepos -m nested
402 committing subrepository inner
411 committing subrepository inner
403 committing subrepository inner/s
412 committing subrepository inner/s
404
413
405 nested archive
414 nested archive
406
415
407 $ hg archive --subrepos ../narchive
416 $ hg archive --subrepos ../narchive
408 $ ls ../narchive/inner/s | grep -v pax_global_header
417 $ ls ../narchive/inner/s | grep -v pax_global_header
409 f
418 f
410 g
419 g
411
420
412 relative source expansion
421 relative source expansion
413
422
414 $ cd ..
423 $ cd ..
415 $ mkdir d
424 $ mkdir d
416 $ hg clone t d/t 2> /dev/null
425 $ hg clone t d/t 2> /dev/null
417 updating to branch default
426 updating to branch default
418 cloning subrepo s from $TESTTMP/gitroot
427 cloning subrepo s from $TESTTMP/gitroot
419 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
420
429
421 Don't crash if the subrepo is missing
430 Don't crash if the subrepo is missing
422
431
423 $ hg clone t missing -q
432 $ hg clone t missing -q
424 $ cd missing
433 $ cd missing
425 $ rm -rf s
434 $ rm -rf s
426 $ hg status -S
435 $ hg status -S
427 $ hg sum | grep commit
436 $ hg sum | grep commit
428 commit: 1 subrepos
437 commit: 1 subrepos
429 $ hg push -q
438 $ hg push -q
430 abort: subrepo s is missing (in subrepository "s")
439 abort: subrepo s is missing (in subrepository "s")
431 [255]
440 [255]
432 $ hg commit --subrepos -qm missing
441 $ hg commit --subrepos -qm missing
433 abort: subrepo s is missing (in subrepository "s")
442 abort: subrepo s is missing (in subrepository "s")
434 [255]
443 [255]
435
444
436 #if symlink
445 #if symlink
437 Don't crash if subrepo is a broken symlink
446 Don't crash if subrepo is a broken symlink
438 $ ln -s broken s
447 $ ln -s broken s
439 $ hg status -S
448 $ hg status -S
440 abort: subrepo 's' traverses symbolic link
449 abort: subrepo 's' traverses symbolic link
441 [255]
450 [255]
442 $ hg push -q
451 $ hg push -q
443 abort: subrepo 's' traverses symbolic link
452 abort: subrepo 's' traverses symbolic link
444 [255]
453 [255]
445 $ hg commit --subrepos -qm missing
454 $ hg commit --subrepos -qm missing
446 abort: subrepo 's' traverses symbolic link
455 abort: subrepo 's' traverses symbolic link
447 [255]
456 [255]
448 $ rm s
457 $ rm s
449 #endif
458 #endif
450
459
451 $ hg update -C 2> /dev/null
460 $ hg update -C 2> /dev/null
452 cloning subrepo s from $TESTTMP/gitroot
461 cloning subrepo s from $TESTTMP/gitroot
453 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
454 $ hg sum | grep commit
463 $ hg sum | grep commit
455 commit: (clean)
464 commit: (clean)
456
465
457 Don't crash if the .hgsubstate entry is missing
466 Don't crash if the .hgsubstate entry is missing
458
467
459 $ hg update 1 -q
468 $ hg update 1 -q
460 $ hg rm .hgsubstate
469 $ hg rm .hgsubstate
461 $ hg commit .hgsubstate -m 'no substate'
470 $ hg commit .hgsubstate -m 'no substate'
462 nothing changed
471 nothing changed
463 [1]
472 [1]
464 $ hg tag -l nosubstate
473 $ hg tag -l nosubstate
465 $ hg manifest
474 $ hg manifest
466 .hgsub
475 .hgsub
467 .hgsubstate
476 .hgsubstate
468 a
477 a
469
478
470 $ hg status -S
479 $ hg status -S
471 R .hgsubstate
480 R .hgsubstate
472 $ hg sum | grep commit
481 $ hg sum | grep commit
473 commit: 1 removed, 1 subrepos (new branch head)
482 commit: 1 removed, 1 subrepos (new branch head)
474
483
475 $ hg commit -m 'restore substate'
484 $ hg commit -m 'restore substate'
476 nothing changed
485 nothing changed
477 [1]
486 [1]
478 $ hg manifest
487 $ hg manifest
479 .hgsub
488 .hgsub
480 .hgsubstate
489 .hgsubstate
481 a
490 a
482 $ hg sum | grep commit
491 $ hg sum | grep commit
483 commit: 1 removed, 1 subrepos (new branch head)
492 commit: 1 removed, 1 subrepos (new branch head)
484
493
485 $ hg update -qC nosubstate
494 $ hg update -qC nosubstate
486 $ ls s
495 $ ls s
487 g
496 g
488
497
489 issue3109: false positives in git diff-index
498 issue3109: false positives in git diff-index
490
499
491 $ hg update -q
500 $ hg update -q
492 $ touch -t 200001010000 s/g
501 $ touch -t 200001010000 s/g
493 $ hg status --subrepos
502 $ hg status --subrepos
494 $ touch -t 200001010000 s/g
503 $ touch -t 200001010000 s/g
495 $ hg sum | grep commit
504 $ hg sum | grep commit
496 commit: (clean)
505 commit: (clean)
497
506
498 Check hg update --clean
507 Check hg update --clean
499 $ cd $TESTTMP/ta
508 $ cd $TESTTMP/ta
500 $ echo > s/g
509 $ echo > s/g
501 $ cd s
510 $ cd s
502 $ echo c1 > f1
511 $ echo c1 > f1
503 $ echo c1 > f2
512 $ echo c1 > f2
504 $ git add f1
513 $ git add f1
505 $ cd ..
514 $ cd ..
506 $ hg status -S
515 $ hg status -S
507 M s/g
516 M s/g
508 A s/f1
517 A s/f1
509 ? s/f2
518 ? s/f2
510 $ ls s
519 $ ls s
511 f
520 f
512 f1
521 f1
513 f2
522 f2
514 g
523 g
515 $ hg update --clean
524 $ hg update --clean
516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
525 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 $ hg status -S
526 $ hg status -S
518 ? s/f1
527 ? s/f1
519 ? s/f2
528 ? s/f2
520 $ ls s
529 $ ls s
521 f
530 f
522 f1
531 f1
523 f2
532 f2
524 g
533 g
525
534
526 Sticky subrepositories, no changes
535 Sticky subrepositories, no changes
527 $ cd $TESTTMP/ta
536 $ cd $TESTTMP/ta
528 $ hg id -n
537 $ hg id -n
529 7
538 7
530 $ cd s
539 $ cd s
531 $ git rev-parse HEAD
540 $ git rev-parse HEAD
532 32a343883b74769118bb1d3b4b1fbf9156f4dddc
541 32a343883b74769118bb1d3b4b1fbf9156f4dddc
533 $ cd ..
542 $ cd ..
534 $ hg update 1 > /dev/null 2>&1
543 $ hg update 1 > /dev/null 2>&1
535 $ hg id -n
544 $ hg id -n
536 1
545 1
537 $ cd s
546 $ cd s
538 $ git rev-parse HEAD
547 $ git rev-parse HEAD
539 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
548 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
540 $ cd ..
549 $ cd ..
541
550
542 Sticky subrepositories, file changes
551 Sticky subrepositories, file changes
543 $ touch s/f1
552 $ touch s/f1
544 $ cd s
553 $ cd s
545 $ git add f1
554 $ git add f1
546 $ cd ..
555 $ cd ..
547 $ hg id -n
556 $ hg id -n
548 1+
557 1+
549 $ cd s
558 $ cd s
550 $ git rev-parse HEAD
559 $ git rev-parse HEAD
551 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
560 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
552 $ cd ..
561 $ cd ..
553 $ hg update 4
562 $ hg update 4
554 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
563 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
555 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
564 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
556 what do you want to do? m
565 what do you want to do? m
557 subrepository sources for s differ
566 subrepository sources for s differ
558 you can use (l)ocal source (da5f5b1) or (r)emote source (aa84837).
567 you can use (l)ocal source (da5f5b1) or (r)emote source (aa84837).
559 what do you want to do? l
568 what do you want to do? l
560 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
569 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 $ hg id -n
570 $ hg id -n
562 4+
571 4+
563 $ cd s
572 $ cd s
564 $ git rev-parse HEAD
573 $ git rev-parse HEAD
565 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
574 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
566 $ cd ..
575 $ cd ..
567 $ hg update --clean tip > /dev/null 2>&1
576 $ hg update --clean tip > /dev/null 2>&1
568
577
569 Sticky subrepository, revision updates
578 Sticky subrepository, revision updates
570 $ hg id -n
579 $ hg id -n
571 7
580 7
572 $ cd s
581 $ cd s
573 $ git rev-parse HEAD
582 $ git rev-parse HEAD
574 32a343883b74769118bb1d3b4b1fbf9156f4dddc
583 32a343883b74769118bb1d3b4b1fbf9156f4dddc
575 $ cd ..
584 $ cd ..
576 $ cd s
585 $ cd s
577 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
586 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
578 Previous HEAD position was 32a3438* fff (glob)
587 Previous HEAD position was 32a3438* fff (glob)
579 HEAD is now at aa84837* f (glob)
588 HEAD is now at aa84837* f (glob)
580 $ cd ..
589 $ cd ..
581 $ hg update 1
590 $ hg update 1
582 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
591 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
583 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
592 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
584 what do you want to do? m
593 what do you want to do? m
585 subrepository sources for s differ (in checked out version)
594 subrepository sources for s differ (in checked out version)
586 you can use (l)ocal source (32a3438) or (r)emote source (da5f5b1).
595 you can use (l)ocal source (32a3438) or (r)emote source (da5f5b1).
587 what do you want to do? l
596 what do you want to do? l
588 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
589 $ hg id -n
598 $ hg id -n
590 1+
599 1+
591 $ cd s
600 $ cd s
592 $ git rev-parse HEAD
601 $ git rev-parse HEAD
593 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
602 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
594 $ cd ..
603 $ cd ..
595
604
596 Sticky subrepository, file changes and revision updates
605 Sticky subrepository, file changes and revision updates
597 $ touch s/f1
606 $ touch s/f1
598 $ cd s
607 $ cd s
599 $ git add f1
608 $ git add f1
600 $ git rev-parse HEAD
609 $ git rev-parse HEAD
601 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
610 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
602 $ cd ..
611 $ cd ..
603 $ hg id -n
612 $ hg id -n
604 1+
613 1+
605 $ hg update 7
614 $ hg update 7
606 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
615 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
607 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
616 you can (m)erge, keep (l)ocal [working copy] or keep (r)emote [destination].
608 what do you want to do? m
617 what do you want to do? m
609 subrepository sources for s differ
618 subrepository sources for s differ
610 you can use (l)ocal source (32a3438) or (r)emote source (32a3438).
619 you can use (l)ocal source (32a3438) or (r)emote source (32a3438).
611 what do you want to do? l
620 what do you want to do? l
612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
621 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
613 $ hg id -n
622 $ hg id -n
614 7+
623 7+
615 $ cd s
624 $ cd s
616 $ git rev-parse HEAD
625 $ git rev-parse HEAD
617 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
626 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
618 $ cd ..
627 $ cd ..
619
628
620 Sticky repository, update --clean
629 Sticky repository, update --clean
621 $ hg update --clean tip 2>/dev/null
630 $ hg update --clean tip 2>/dev/null
622 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
631 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
623 $ hg id -n
632 $ hg id -n
624 7
633 7
625 $ cd s
634 $ cd s
626 $ git rev-parse HEAD
635 $ git rev-parse HEAD
627 32a343883b74769118bb1d3b4b1fbf9156f4dddc
636 32a343883b74769118bb1d3b4b1fbf9156f4dddc
628 $ cd ..
637 $ cd ..
629
638
630 Test subrepo already at intended revision:
639 Test subrepo already at intended revision:
631 $ cd s
640 $ cd s
632 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
641 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
633 HEAD is now at 32a3438* fff (glob)
642 HEAD is now at 32a3438* fff (glob)
634 $ cd ..
643 $ cd ..
635 $ hg update 1
644 $ hg update 1
636 Previous HEAD position was 32a3438* fff (glob)
645 Previous HEAD position was 32a3438* fff (glob)
637 HEAD is now at da5f5b1* g (glob)
646 HEAD is now at da5f5b1* g (glob)
638 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
647 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 $ hg id -n
648 $ hg id -n
640 1
649 1
641 $ cd s
650 $ cd s
642 $ git rev-parse HEAD
651 $ git rev-parse HEAD
643 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
652 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
644 $ cd ..
653 $ cd ..
645
654
646 Test forgetting files, not implemented in git subrepo, used to
655 Test forgetting files, not implemented in git subrepo, used to
647 traceback
656 traceback
648 #if no-windows
657 #if no-windows
649 $ hg forget 'notafile*'
658 $ hg forget 'notafile*'
650 notafile*: $ENOENT$
659 notafile*: $ENOENT$
651 [1]
660 [1]
652 #else
661 #else
653 error: The filename, directory name, or volume label syntax is incorrect
662 error: The filename, directory name, or volume label syntax is incorrect
654 $ hg forget 'notafile'
663 $ hg forget 'notafile'
655 notafile: * (glob)
664 notafile: * (glob)
656 [1]
665 [1]
657 #endif
666 #endif
658
667
659 $ cd ..
668 $ cd ..
660
669
661 Test sanitizing ".hg/hgrc" in subrepo
670 Test sanitizing ".hg/hgrc" in subrepo
662
671
663 $ cd t
672 $ cd t
664 $ hg tip -q
673 $ hg tip -q
665 7:af6d2edbb0d3
674 7:af6d2edbb0d3
666 $ hg update -q -C af6d2edbb0d3
675 $ hg update -q -C af6d2edbb0d3
667 $ cd s
676 $ cd s
668 $ git checkout -q -b sanitize-test
677 $ git checkout -q -b sanitize-test
669 $ mkdir .hg
678 $ mkdir .hg
670 $ echo '.hg/hgrc in git repo' > .hg/hgrc
679 $ echo '.hg/hgrc in git repo' > .hg/hgrc
671 $ mkdir -p sub/.hg
680 $ mkdir -p sub/.hg
672 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
681 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
673 $ git add .hg sub
682 $ git add .hg sub
674 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
683 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
675 $ git push -q origin sanitize-test
684 $ git push -q origin sanitize-test
676 $ cd ..
685 $ cd ..
677 $ grep ' s$' .hgsubstate
686 $ grep ' s$' .hgsubstate
678 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
687 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
679 $ hg commit -qm 'commit with git revision including .hg/hgrc'
688 $ hg commit -qm 'commit with git revision including .hg/hgrc'
680 $ hg parents -q
689 $ hg parents -q
681 8:3473d20bddcf
690 8:3473d20bddcf
682 $ grep ' s$' .hgsubstate
691 $ grep ' s$' .hgsubstate
683 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
692 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
684 $ cd ..
693 $ cd ..
685
694
686 $ hg -R tc pull -q
695 $ hg -R tc pull -q
687 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
696 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
688 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg'
697 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg'
689 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg'
698 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg'
690 $ cd tc
699 $ cd tc
691 $ hg parents -q
700 $ hg parents -q
692 8:3473d20bddcf
701 8:3473d20bddcf
693 $ grep ' s$' .hgsubstate
702 $ grep ' s$' .hgsubstate
694 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
703 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
695 $ test -f s/.hg/hgrc
704 $ test -f s/.hg/hgrc
696 [1]
705 [1]
697 $ test -f s/sub/.hg/hgrc
706 $ test -f s/sub/.hg/hgrc
698 [1]
707 [1]
699 $ cd ..
708 $ cd ..
700
709
701 additional test for "git merge --ff" route:
710 additional test for "git merge --ff" route:
702
711
703 $ cd t
712 $ cd t
704 $ hg tip -q
713 $ hg tip -q
705 8:3473d20bddcf
714 8:3473d20bddcf
706 $ hg update -q -C af6d2edbb0d3
715 $ hg update -q -C af6d2edbb0d3
707 $ cd s
716 $ cd s
708 $ git checkout -q testing
717 $ git checkout -q testing
709 $ mkdir .hg
718 $ mkdir .hg
710 $ echo '.hg/hgrc in git repo' > .hg/hgrc
719 $ echo '.hg/hgrc in git repo' > .hg/hgrc
711 $ mkdir -p sub/.hg
720 $ mkdir -p sub/.hg
712 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
721 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
713 $ git add .hg sub
722 $ git add .hg sub
714 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
723 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
715 $ git push -q origin testing
724 $ git push -q origin testing
716 $ cd ..
725 $ cd ..
717 $ grep ' s$' .hgsubstate
726 $ grep ' s$' .hgsubstate
718 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
727 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
719 $ hg commit -qm 'commit with git revision including .hg/hgrc'
728 $ hg commit -qm 'commit with git revision including .hg/hgrc'
720 $ hg parents -q
729 $ hg parents -q
721 9:ed23f7fe024e
730 9:ed23f7fe024e
722 $ grep ' s$' .hgsubstate
731 $ grep ' s$' .hgsubstate
723 f262643c1077219fbd3858d54e78ef050ef84fbf s
732 f262643c1077219fbd3858d54e78ef050ef84fbf s
724 $ cd ..
733 $ cd ..
725
734
726 $ cd tc
735 $ cd tc
727 $ hg update -q -C af6d2edbb0d3
736 $ hg update -q -C af6d2edbb0d3
728 $ test -f s/.hg/hgrc
737 $ test -f s/.hg/hgrc
729 [1]
738 [1]
730 $ test -f s/sub/.hg/hgrc
739 $ test -f s/sub/.hg/hgrc
731 [1]
740 [1]
732 $ cd ..
741 $ cd ..
733 $ hg -R tc pull -q
742 $ hg -R tc pull -q
734 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
743 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
735 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg'
744 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg'
736 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg'
745 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg'
737 $ cd tc
746 $ cd tc
738 $ hg parents -q
747 $ hg parents -q
739 9:ed23f7fe024e
748 9:ed23f7fe024e
740 $ grep ' s$' .hgsubstate
749 $ grep ' s$' .hgsubstate
741 f262643c1077219fbd3858d54e78ef050ef84fbf s
750 f262643c1077219fbd3858d54e78ef050ef84fbf s
742 $ test -f s/.hg/hgrc
751 $ test -f s/.hg/hgrc
743 [1]
752 [1]
744 $ test -f s/sub/.hg/hgrc
753 $ test -f s/sub/.hg/hgrc
745 [1]
754 [1]
746
755
747 Test that sanitizing is omitted in meta data area:
756 Test that sanitizing is omitted in meta data area:
748
757
749 $ mkdir s/.git/.hg
758 $ mkdir s/.git/.hg
750 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
759 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
751 $ hg update -q -C af6d2edbb0d3
760 $ hg update -q -C af6d2edbb0d3
752 checking out detached HEAD in subrepository "s"
761 checking out detached HEAD in subrepository "s"
753 check out a git branch if you intend to make changes
762 check out a git branch if you intend to make changes
754
763
755 check differences made by most recent change
764 check differences made by most recent change
756 $ cd s
765 $ cd s
757 $ cat > foobar << EOF
766 $ cat > foobar << EOF
758 > woopwoop
767 > woopwoop
759 >
768 >
760 > foo
769 > foo
761 > bar
770 > bar
762 > EOF
771 > EOF
763 $ git add foobar
772 $ git add foobar
764 $ cd ..
773 $ cd ..
765
774
766 $ hg diff --subrepos
775 $ hg diff --subrepos
767 diff --git a/s/foobar b/s/foobar
776 diff --git a/s/foobar b/s/foobar
768 new file mode 100644
777 new file mode 100644
769 index 0000000..8a5a5e2
778 index 0000000..8a5a5e2
770 --- /dev/null
779 --- /dev/null
771 +++ b/s/foobar
780 +++ b/s/foobar
772 @@ -0,0 +1,4 @@
781 @@ -0,0 +1,4 @@
773 +woopwoop
782 +woopwoop
774 +
783 +
775 +foo
784 +foo
776 +bar
785 +bar
777
786
778 $ hg commit --subrepos -m "Added foobar"
787 $ hg commit --subrepos -m "Added foobar"
779 committing subrepository s
788 committing subrepository s
780 created new head
789 created new head
781
790
782 $ hg diff -c . --subrepos --nodates
791 $ hg diff -c . --subrepos --nodates
783 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
792 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
784 --- a/.hgsubstate
793 --- a/.hgsubstate
785 +++ b/.hgsubstate
794 +++ b/.hgsubstate
786 @@ -1,1 +1,1 @@
795 @@ -1,1 +1,1 @@
787 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
796 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
788 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
797 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
789 diff --git a/s/foobar b/s/foobar
798 diff --git a/s/foobar b/s/foobar
790 new file mode 100644
799 new file mode 100644
791 index 0000000..8a5a5e2
800 index 0000000..8a5a5e2
792 --- /dev/null
801 --- /dev/null
793 +++ b/s/foobar
802 +++ b/s/foobar
794 @@ -0,0 +1,4 @@
803 @@ -0,0 +1,4 @@
795 +woopwoop
804 +woopwoop
796 +
805 +
797 +foo
806 +foo
798 +bar
807 +bar
799
808
800 check output when only diffing the subrepository
809 check output when only diffing the subrepository
801 $ hg diff -c . --subrepos s
810 $ hg diff -c . --subrepos s
802 diff --git a/s/foobar b/s/foobar
811 diff --git a/s/foobar b/s/foobar
803 new file mode 100644
812 new file mode 100644
804 index 0000000..8a5a5e2
813 index 0000000..8a5a5e2
805 --- /dev/null
814 --- /dev/null
806 +++ b/s/foobar
815 +++ b/s/foobar
807 @@ -0,0 +1,4 @@
816 @@ -0,0 +1,4 @@
808 +woopwoop
817 +woopwoop
809 +
818 +
810 +foo
819 +foo
811 +bar
820 +bar
812
821
813 check output when diffing something else
822 check output when diffing something else
814 $ hg diff -c . --subrepos .hgsubstate --nodates
823 $ hg diff -c . --subrepos .hgsubstate --nodates
815 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
824 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
816 --- a/.hgsubstate
825 --- a/.hgsubstate
817 +++ b/.hgsubstate
826 +++ b/.hgsubstate
818 @@ -1,1 +1,1 @@
827 @@ -1,1 +1,1 @@
819 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
828 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
820 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
829 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
821
830
822 add new changes, including whitespace
831 add new changes, including whitespace
823 $ cd s
832 $ cd s
824 $ cat > foobar << EOF
833 $ cat > foobar << EOF
825 > woop woop
834 > woop woop
826 >
835 >
827 > foo
836 > foo
828 > bar
837 > bar
829 > EOF
838 > EOF
830 $ echo foo > barfoo
839 $ echo foo > barfoo
831 $ git add barfoo
840 $ git add barfoo
832 $ cd ..
841 $ cd ..
833
842
834 $ hg diff --subrepos --ignore-all-space
843 $ hg diff --subrepos --ignore-all-space
835 diff --git a/s/barfoo b/s/barfoo
844 diff --git a/s/barfoo b/s/barfoo
836 new file mode 100644
845 new file mode 100644
837 index 0000000..257cc56
846 index 0000000..257cc56
838 --- /dev/null
847 --- /dev/null
839 +++ b/s/barfoo
848 +++ b/s/barfoo
840 @@ -0,0 +1* @@ (glob)
849 @@ -0,0 +1* @@ (glob)
841 +foo
850 +foo
842 $ hg diff --subrepos s/foobar
851 $ hg diff --subrepos s/foobar
843 diff --git a/s/foobar b/s/foobar
852 diff --git a/s/foobar b/s/foobar
844 index 8a5a5e2..bd5812a 100644
853 index 8a5a5e2..bd5812a 100644
845 --- a/s/foobar
854 --- a/s/foobar
846 +++ b/s/foobar
855 +++ b/s/foobar
847 @@ -1,4 +1,4 @@
856 @@ -1,4 +1,4 @@
848 -woopwoop
857 -woopwoop
849 +woop woop
858 +woop woop
850
859
851 foo
860 foo
852 bar
861 bar
853
862
854 execute a diffstat
863 execute a diffstat
855 the output contains a regex, because git 1.7.10 and 1.7.11
864 the output contains a regex, because git 1.7.10 and 1.7.11
856 change the amount of whitespace
865 change the amount of whitespace
857 $ hg diff --subrepos --stat
866 $ hg diff --subrepos --stat
858 \s*barfoo \|\s+1 \+ (re)
867 \s*barfoo \|\s+1 \+ (re)
859 \s*foobar \|\s+2 \+- (re)
868 \s*foobar \|\s+2 \+- (re)
860 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
869 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
861
870
862 adding an include should ignore the other elements
871 adding an include should ignore the other elements
863 $ hg diff --subrepos -I s/foobar
872 $ hg diff --subrepos -I s/foobar
864 diff --git a/s/foobar b/s/foobar
873 diff --git a/s/foobar b/s/foobar
865 index 8a5a5e2..bd5812a 100644
874 index 8a5a5e2..bd5812a 100644
866 --- a/s/foobar
875 --- a/s/foobar
867 +++ b/s/foobar
876 +++ b/s/foobar
868 @@ -1,4 +1,4 @@
877 @@ -1,4 +1,4 @@
869 -woopwoop
878 -woopwoop
870 +woop woop
879 +woop woop
871
880
872 foo
881 foo
873 bar
882 bar
874
883
875 adding an exclude should ignore this element
884 adding an exclude should ignore this element
876 $ hg diff --subrepos -X s/foobar
885 $ hg diff --subrepos -X s/foobar
877 diff --git a/s/barfoo b/s/barfoo
886 diff --git a/s/barfoo b/s/barfoo
878 new file mode 100644
887 new file mode 100644
879 index 0000000..257cc56
888 index 0000000..257cc56
880 --- /dev/null
889 --- /dev/null
881 +++ b/s/barfoo
890 +++ b/s/barfoo
882 @@ -0,0 +1* @@ (glob)
891 @@ -0,0 +1* @@ (glob)
883 +foo
892 +foo
884
893
885 moving a file should show a removal and an add
894 moving a file should show a removal and an add
886 $ hg revert --all
895 $ hg revert --all
887 reverting subrepo ../gitroot
896 reverting subrepo ../gitroot
888 $ cd s
897 $ cd s
889 $ git mv foobar woop
898 $ git mv foobar woop
890 $ cd ..
899 $ cd ..
891 $ hg diff --subrepos
900 $ hg diff --subrepos
892 diff --git a/s/foobar b/s/foobar
901 diff --git a/s/foobar b/s/foobar
893 deleted file mode 100644
902 deleted file mode 100644
894 index 8a5a5e2..0000000
903 index 8a5a5e2..0000000
895 --- a/s/foobar
904 --- a/s/foobar
896 +++ /dev/null
905 +++ /dev/null
897 @@ -1,4 +0,0 @@
906 @@ -1,4 +0,0 @@
898 -woopwoop
907 -woopwoop
899 -
908 -
900 -foo
909 -foo
901 -bar
910 -bar
902 diff --git a/s/woop b/s/woop
911 diff --git a/s/woop b/s/woop
903 new file mode 100644
912 new file mode 100644
904 index 0000000..8a5a5e2
913 index 0000000..8a5a5e2
905 --- /dev/null
914 --- /dev/null
906 +++ b/s/woop
915 +++ b/s/woop
907 @@ -0,0 +1,4 @@
916 @@ -0,0 +1,4 @@
908 +woopwoop
917 +woopwoop
909 +
918 +
910 +foo
919 +foo
911 +bar
920 +bar
912 $ rm s/woop
921 $ rm s/woop
913
922
914 revert the subrepository
923 revert the subrepository
915 $ hg revert --all
924 $ hg revert --all
916 reverting subrepo ../gitroot
925 reverting subrepo ../gitroot
917
926
918 $ hg status --subrepos
927 $ hg status --subrepos
919 ? s/barfoo
928 ? s/barfoo
920 ? s/foobar.orig
929 ? s/foobar.orig
921
930
922 $ mv s/foobar.orig s/foobar
931 $ mv s/foobar.orig s/foobar
923
932
924 $ hg revert --no-backup s
933 $ hg revert --no-backup s
925 reverting subrepo ../gitroot
934 reverting subrepo ../gitroot
926
935
927 $ hg status --subrepos
936 $ hg status --subrepos
928 ? s/barfoo
937 ? s/barfoo
929
938
930 revert moves orig files to the right place
939 revert moves orig files to the right place
931 $ echo 'bloop' > s/foobar
940 $ echo 'bloop' > s/foobar
932 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
941 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
933 reverting subrepo ../gitroot
942 reverting subrepo ../gitroot
934 creating directory: $TESTTMP/tc/.hg/origbackups/s
943 creating directory: $TESTTMP/tc/.hg/origbackups/s
935 saving current version of foobar as .hg/origbackups/s/foobar
944 saving current version of foobar as .hg/origbackups/s/foobar
936 $ ls .hg/origbackups/s
945 $ ls .hg/origbackups/s
937 foobar
946 foobar
938 $ rm -rf .hg/origbackups
947 $ rm -rf .hg/origbackups
939
948
940 show file at specific revision
949 show file at specific revision
941 $ cat > s/foobar << EOF
950 $ cat > s/foobar << EOF
942 > woop woop
951 > woop woop
943 > fooo bar
952 > fooo bar
944 > EOF
953 > EOF
945 $ hg commit --subrepos -m "updated foobar"
954 $ hg commit --subrepos -m "updated foobar"
946 committing subrepository s
955 committing subrepository s
947 $ cat > s/foobar << EOF
956 $ cat > s/foobar << EOF
948 > current foobar
957 > current foobar
949 > (should not be visible using hg cat)
958 > (should not be visible using hg cat)
950 > EOF
959 > EOF
951
960
952 $ hg cat -r . s/foobar
961 $ hg cat -r . s/foobar
953 woop woop
962 woop woop
954 fooo bar (no-eol)
963 fooo bar (no-eol)
955 $ hg cat -r "parents(.)" s/foobar > catparents
964 $ hg cat -r "parents(.)" s/foobar > catparents
956
965
957 $ mkdir -p tmp/s
966 $ mkdir -p tmp/s
958
967
959 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
968 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
960 $ diff tmp/% catparents
969 $ diff tmp/% catparents
961
970
962 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
971 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
963 $ diff tmp/foobar catparents
972 $ diff tmp/foobar catparents
964
973
965 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
974 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
966 $ diff tmp/s/otherfoobar catparents
975 $ diff tmp/s/otherfoobar catparents
967
976
968 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
977 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
969 $ diff tmp/s/foobar catparents
978 $ diff tmp/s/foobar catparents
970
979
971 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
980 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
972 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
981 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
973
982
974 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
983 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
975 $ diff tmp/10 catparents
984 $ diff tmp/10 catparents
976
985
977 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
986 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
978 $ diff tmp/255ee8cf690e catparents
987 $ diff tmp/255ee8cf690e catparents
979
988
980 $ rm tmp/10
989 $ rm tmp/10
981 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
990 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
982 $ diff tmp/10 catparents
991 $ diff tmp/10 catparents
983
992
984 $ mkdir tmp/tc
993 $ mkdir tmp/tc
985 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
994 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
986 $ diff tmp/tc/foobar catparents
995 $ diff tmp/tc/foobar catparents
987
996
988 cleanup
997 cleanup
989 $ rm -r tmp
998 $ rm -r tmp
990 $ rm catparents
999 $ rm catparents
991
1000
992 add git files, using either files or patterns
1001 add git files, using either files or patterns
993 $ echo "hsss! hsssssssh!" > s/snake.python
1002 $ echo "hsss! hsssssssh!" > s/snake.python
994 $ echo "ccc" > s/c.c
1003 $ echo "ccc" > s/c.c
995 $ echo "cpp" > s/cpp.cpp
1004 $ echo "cpp" > s/cpp.cpp
996
1005
997 $ hg add s/snake.python s/c.c s/cpp.cpp
1006 $ hg add s/snake.python s/c.c s/cpp.cpp
998 $ hg st --subrepos s
1007 $ hg st --subrepos s
999 M s/foobar
1008 M s/foobar
1000 A s/c.c
1009 A s/c.c
1001 A s/cpp.cpp
1010 A s/cpp.cpp
1002 A s/snake.python
1011 A s/snake.python
1003 ? s/barfoo
1012 ? s/barfoo
1004 $ hg revert s
1013 $ hg revert s
1005 reverting subrepo ../gitroot
1014 reverting subrepo ../gitroot
1006
1015
1007 $ hg add --subrepos "glob:**.python"
1016 $ hg add --subrepos "glob:**.python"
1008 adding s/snake.python
1017 adding s/snake.python
1009 $ hg st --subrepos s
1018 $ hg st --subrepos s
1010 A s/snake.python
1019 A s/snake.python
1011 ? s/barfoo
1020 ? s/barfoo
1012 ? s/c.c
1021 ? s/c.c
1013 ? s/cpp.cpp
1022 ? s/cpp.cpp
1014 ? s/foobar.orig
1023 ? s/foobar.orig
1015 $ hg revert s
1024 $ hg revert s
1016 reverting subrepo ../gitroot
1025 reverting subrepo ../gitroot
1017
1026
1018 $ hg add --subrepos s
1027 $ hg add --subrepos s
1019 adding s/barfoo
1028 adding s/barfoo
1020 adding s/c.c
1029 adding s/c.c
1021 adding s/cpp.cpp
1030 adding s/cpp.cpp
1022 adding s/foobar.orig
1031 adding s/foobar.orig
1023 adding s/snake.python
1032 adding s/snake.python
1024 $ hg st --subrepos s
1033 $ hg st --subrepos s
1025 A s/barfoo
1034 A s/barfoo
1026 A s/c.c
1035 A s/c.c
1027 A s/cpp.cpp
1036 A s/cpp.cpp
1028 A s/foobar.orig
1037 A s/foobar.orig
1029 A s/snake.python
1038 A s/snake.python
1030 $ hg revert s
1039 $ hg revert s
1031 reverting subrepo ../gitroot
1040 reverting subrepo ../gitroot
1032 make sure everything is reverted correctly
1041 make sure everything is reverted correctly
1033 $ hg st --subrepos s
1042 $ hg st --subrepos s
1034 ? s/barfoo
1043 ? s/barfoo
1035 ? s/c.c
1044 ? s/c.c
1036 ? s/cpp.cpp
1045 ? s/cpp.cpp
1037 ? s/foobar.orig
1046 ? s/foobar.orig
1038 ? s/snake.python
1047 ? s/snake.python
1039
1048
1040 $ hg add --subrepos --exclude "path:s/c.c"
1049 $ hg add --subrepos --exclude "path:s/c.c"
1041 adding s/barfoo
1050 adding s/barfoo
1042 adding s/cpp.cpp
1051 adding s/cpp.cpp
1043 adding s/foobar.orig
1052 adding s/foobar.orig
1044 adding s/snake.python
1053 adding s/snake.python
1045 $ hg st --subrepos s
1054 $ hg st --subrepos s
1046 A s/barfoo
1055 A s/barfoo
1047 A s/cpp.cpp
1056 A s/cpp.cpp
1048 A s/foobar.orig
1057 A s/foobar.orig
1049 A s/snake.python
1058 A s/snake.python
1050 ? s/c.c
1059 ? s/c.c
1051 $ hg revert --all -q
1060 $ hg revert --all -q
1052
1061
1053 .hgignore should not have influence in subrepos
1062 .hgignore should not have influence in subrepos
1054 $ cat > .hgignore << EOF
1063 $ cat > .hgignore << EOF
1055 > syntax: glob
1064 > syntax: glob
1056 > *.python
1065 > *.python
1057 > EOF
1066 > EOF
1058 $ hg add .hgignore
1067 $ hg add .hgignore
1059 $ hg add --subrepos "glob:**.python" s/barfoo
1068 $ hg add --subrepos "glob:**.python" s/barfoo
1060 adding s/snake.python
1069 adding s/snake.python
1061 $ hg st --subrepos s
1070 $ hg st --subrepos s
1062 A s/barfoo
1071 A s/barfoo
1063 A s/snake.python
1072 A s/snake.python
1064 ? s/c.c
1073 ? s/c.c
1065 ? s/cpp.cpp
1074 ? s/cpp.cpp
1066 ? s/foobar.orig
1075 ? s/foobar.orig
1067 $ hg revert --all -q
1076 $ hg revert --all -q
1068
1077
1069 .gitignore should have influence,
1078 .gitignore should have influence,
1070 except for explicitly added files (no patterns)
1079 except for explicitly added files (no patterns)
1071 $ cat > s/.gitignore << EOF
1080 $ cat > s/.gitignore << EOF
1072 > *.python
1081 > *.python
1073 > EOF
1082 > EOF
1074 $ hg add s/.gitignore
1083 $ hg add s/.gitignore
1075 $ hg st --subrepos s
1084 $ hg st --subrepos s
1076 A s/.gitignore
1085 A s/.gitignore
1077 ? s/barfoo
1086 ? s/barfoo
1078 ? s/c.c
1087 ? s/c.c
1079 ? s/cpp.cpp
1088 ? s/cpp.cpp
1080 ? s/foobar.orig
1089 ? s/foobar.orig
1081 $ hg st --subrepos s --all
1090 $ hg st --subrepos s --all
1082 A s/.gitignore
1091 A s/.gitignore
1083 ? s/barfoo
1092 ? s/barfoo
1084 ? s/c.c
1093 ? s/c.c
1085 ? s/cpp.cpp
1094 ? s/cpp.cpp
1086 ? s/foobar.orig
1095 ? s/foobar.orig
1087 I s/snake.python
1096 I s/snake.python
1088 C s/f
1097 C s/f
1089 C s/foobar
1098 C s/foobar
1090 C s/g
1099 C s/g
1091 $ hg add --subrepos "glob:**.python"
1100 $ hg add --subrepos "glob:**.python"
1092 $ hg st --subrepos s
1101 $ hg st --subrepos s
1093 A s/.gitignore
1102 A s/.gitignore
1094 ? s/barfoo
1103 ? s/barfoo
1095 ? s/c.c
1104 ? s/c.c
1096 ? s/cpp.cpp
1105 ? s/cpp.cpp
1097 ? s/foobar.orig
1106 ? s/foobar.orig
1098 $ hg add --subrepos s/snake.python
1107 $ hg add --subrepos s/snake.python
1099 $ hg st --subrepos s
1108 $ hg st --subrepos s
1100 A s/.gitignore
1109 A s/.gitignore
1101 A s/snake.python
1110 A s/snake.python
1102 ? s/barfoo
1111 ? s/barfoo
1103 ? s/c.c
1112 ? s/c.c
1104 ? s/cpp.cpp
1113 ? s/cpp.cpp
1105 ? s/foobar.orig
1114 ? s/foobar.orig
1106
1115
1107 correctly do a dry run
1116 correctly do a dry run
1108 $ hg add --subrepos s --dry-run
1117 $ hg add --subrepos s --dry-run
1109 adding s/barfoo
1118 adding s/barfoo
1110 adding s/c.c
1119 adding s/c.c
1111 adding s/cpp.cpp
1120 adding s/cpp.cpp
1112 adding s/foobar.orig
1121 adding s/foobar.orig
1113 $ hg st --subrepos s
1122 $ hg st --subrepos s
1114 A s/.gitignore
1123 A s/.gitignore
1115 A s/snake.python
1124 A s/snake.python
1116 ? s/barfoo
1125 ? s/barfoo
1117 ? s/c.c
1126 ? s/c.c
1118 ? s/cpp.cpp
1127 ? s/cpp.cpp
1119 ? s/foobar.orig
1128 ? s/foobar.orig
1120
1129
1121 error given when adding an already tracked file
1130 error given when adding an already tracked file
1122 $ hg add s/.gitignore
1131 $ hg add s/.gitignore
1123 s/.gitignore already tracked!
1132 s/.gitignore already tracked!
1124 [1]
1133 [1]
1125 $ hg add s/g
1134 $ hg add s/g
1126 s/g already tracked!
1135 s/g already tracked!
1127 [1]
1136 [1]
1128
1137
1129 removed files can be re-added
1138 removed files can be re-added
1130 removing files using 'rm' or 'git rm' has the same effect,
1139 removing files using 'rm' or 'git rm' has the same effect,
1131 since we ignore the staging area
1140 since we ignore the staging area
1132 $ hg ci --subrepos -m 'snake'
1141 $ hg ci --subrepos -m 'snake'
1133 committing subrepository s
1142 committing subrepository s
1134 $ cd s
1143 $ cd s
1135 $ rm snake.python
1144 $ rm snake.python
1136 (remove leftover .hg so Mercurial doesn't look for a root here)
1145 (remove leftover .hg so Mercurial doesn't look for a root here)
1137 $ rm -rf .hg
1146 $ rm -rf .hg
1138 $ hg status --subrepos --all .
1147 $ hg status --subrepos --all .
1139 R snake.python
1148 R snake.python
1140 ? barfoo
1149 ? barfoo
1141 ? c.c
1150 ? c.c
1142 ? cpp.cpp
1151 ? cpp.cpp
1143 ? foobar.orig
1152 ? foobar.orig
1144 C .gitignore
1153 C .gitignore
1145 C f
1154 C f
1146 C foobar
1155 C foobar
1147 C g
1156 C g
1148 $ git rm snake.python
1157 $ git rm snake.python
1149 rm 'snake.python'
1158 rm 'snake.python'
1150 $ hg status --subrepos --all .
1159 $ hg status --subrepos --all .
1151 R snake.python
1160 R snake.python
1152 ? barfoo
1161 ? barfoo
1153 ? c.c
1162 ? c.c
1154 ? cpp.cpp
1163 ? cpp.cpp
1155 ? foobar.orig
1164 ? foobar.orig
1156 C .gitignore
1165 C .gitignore
1157 C f
1166 C f
1158 C foobar
1167 C foobar
1159 C g
1168 C g
1160 $ touch snake.python
1169 $ touch snake.python
1161 $ cd ..
1170 $ cd ..
1162 $ hg add s/snake.python
1171 $ hg add s/snake.python
1163 $ hg status -S
1172 $ hg status -S
1164 M s/snake.python
1173 M s/snake.python
1165 ? .hgignore
1174 ? .hgignore
1166 ? s/barfoo
1175 ? s/barfoo
1167 ? s/c.c
1176 ? s/c.c
1168 ? s/cpp.cpp
1177 ? s/cpp.cpp
1169 ? s/foobar.orig
1178 ? s/foobar.orig
1170 $ hg revert --all -q
1179 $ hg revert --all -q
1171
1180
1172 make sure we show changed files, rather than changed subtrees
1181 make sure we show changed files, rather than changed subtrees
1173 $ mkdir s/foo
1182 $ mkdir s/foo
1174 $ touch s/foo/bwuh
1183 $ touch s/foo/bwuh
1175 $ hg add s/foo/bwuh
1184 $ hg add s/foo/bwuh
1176 $ hg commit -S -m "add bwuh"
1185 $ hg commit -S -m "add bwuh"
1177 committing subrepository s
1186 committing subrepository s
1178 $ hg status -S --change .
1187 $ hg status -S --change .
1179 M .hgsubstate
1188 M .hgsubstate
1180 A s/foo/bwuh
1189 A s/foo/bwuh
1181 ? s/barfoo
1190 ? s/barfoo
1182 ? s/c.c
1191 ? s/c.c
1183 ? s/cpp.cpp
1192 ? s/cpp.cpp
1184 ? s/foobar.orig
1193 ? s/foobar.orig
1185 ? s/snake.python.orig
1194 ? s/snake.python.orig
1186
1195
1187 #if git19
1196 #if git19
1188
1197
1189 test for Git CVE-2016-3068
1198 test for Git CVE-2016-3068
1190 $ hg init malicious-subrepository
1199 $ hg init malicious-subrepository
1191 $ cd malicious-subrepository
1200 $ cd malicious-subrepository
1192 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1201 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1193 $ git init s
1202 $ git init s
1194 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1203 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1195 $ cd s
1204 $ cd s
1196 $ git commit --allow-empty -m 'empty'
1205 $ git commit --allow-empty -m 'empty'
1197 [master (root-commit) 153f934] empty
1206 [master (root-commit) 153f934] empty
1198 $ cd ..
1207 $ cd ..
1199 $ hg add .hgsub
1208 $ hg add .hgsub
1200 $ hg commit -m "add subrepo"
1209 $ hg commit -m "add subrepo"
1201 $ cd ..
1210 $ cd ..
1202 $ rm -f pwned.txt
1211 $ rm -f pwned.txt
1203 $ unset GIT_ALLOW_PROTOCOL
1212 $ unset GIT_ALLOW_PROTOCOL
1204 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1213 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1205 > malicious-subrepository malicious-subrepository-protected
1214 > malicious-subrepository malicious-subrepository-protected
1206 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'...
1215 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'...
1207 fatal: transport 'ext' not allowed
1216 fatal: transport 'ext' not allowed
1208 updating to branch default
1217 updating to branch default
1209 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1218 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1210 abort: git clone error 128 in s (in subrepository "s")
1219 abort: git clone error 128 in s (in subrepository "s")
1211 [255]
1220 [255]
1212 $ f -Dq pwned.txt
1221 $ f -Dq pwned.txt
1213 pwned.txt: file not found
1222 pwned.txt: file not found
1214
1223
1215 whitelisting of ext should be respected (that's the git submodule behaviour)
1224 whitelisting of ext should be respected (that's the git submodule behaviour)
1216 $ rm -f pwned.txt
1225 $ rm -f pwned.txt
1217 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1226 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1218 > malicious-subrepository malicious-subrepository-clone-allowed
1227 > malicious-subrepository malicious-subrepository-clone-allowed
1219 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'...
1228 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'...
1220 fatal: Could not read from remote repository.
1229 fatal: Could not read from remote repository.
1221
1230
1222 Please make sure you have the correct access rights
1231 Please make sure you have the correct access rights
1223 and the repository exists.
1232 and the repository exists.
1224 updating to branch default
1233 updating to branch default
1225 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1234 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1226 abort: git clone error 128 in s (in subrepository "s")
1235 abort: git clone error 128 in s (in subrepository "s")
1227 [255]
1236 [255]
1228 $ f -Dq pwned.txt
1237 $ f -Dq pwned.txt
1229 pwned: you asked for it
1238 pwned: you asked for it
1230
1239
1231 #endif
1240 #endif
1232
1241
1233 test for ssh exploit with git subrepos 2017-07-25
1242 test for ssh exploit with git subrepos 2017-07-25
1234
1243
1235 $ hg init malicious-proxycommand
1244 $ hg init malicious-proxycommand
1236 $ cd malicious-proxycommand
1245 $ cd malicious-proxycommand
1237 $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
1246 $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
1238 $ git init s
1247 $ git init s
1239 Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
1248 Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
1240 $ cd s
1249 $ cd s
1241 $ git commit --allow-empty -m 'empty'
1250 $ git commit --allow-empty -m 'empty'
1242 [master (root-commit) 153f934] empty
1251 [master (root-commit) 153f934] empty
1243 $ cd ..
1252 $ cd ..
1244 $ hg add .hgsub
1253 $ hg add .hgsub
1245 $ hg ci -m 'add subrepo'
1254 $ hg ci -m 'add subrepo'
1246 $ cd ..
1255 $ cd ..
1247 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1256 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1248 updating to branch default
1257 updating to branch default
1249 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
1258 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
1250 [255]
1259 [255]
1251
1260
1252 also check that a percent encoded '-' (%2D) doesn't work
1261 also check that a percent encoded '-' (%2D) doesn't work
1253
1262
1254 $ cd malicious-proxycommand
1263 $ cd malicious-proxycommand
1255 $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
1264 $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
1256 $ hg ci -m 'change url to percent encoded'
1265 $ hg ci -m 'change url to percent encoded'
1257 $ cd ..
1266 $ cd ..
1258 $ rm -r malicious-proxycommand-clone
1267 $ rm -r malicious-proxycommand-clone
1259 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1268 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1260 updating to branch default
1269 updating to branch default
1261 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
1270 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepository "s")
1262 [255]
1271 [255]
General Comments 0
You need to be logged in to leave comments. Login now