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