##// END OF EJS Templates
largefiles: make linear update set unsure largefiles normal if unchanged...
Mads Kiilerich -
r23841:9d25bb84 default
parent child Browse files
Show More
@@ -1,1328 +1,1341 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
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
15 archival, pathutil, revset
15 archival, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18
18
19 import lfutil
19 import lfutil
20 import lfcommands
20 import lfcommands
21 import basestore
21 import basestore
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def composelargefilematcher(match, manifest):
25 def composelargefilematcher(match, manifest):
26 '''create a matcher that matches only the largefiles in the original
26 '''create a matcher that matches only the largefiles in the original
27 matcher'''
27 matcher'''
28 m = copy.copy(match)
28 m = copy.copy(match)
29 lfile = lambda f: lfutil.standin(f) in manifest
29 lfile = lambda f: lfutil.standin(f) in manifest
30 m._files = filter(lfile, m._files)
30 m._files = filter(lfile, m._files)
31 m._fmap = set(m._files)
31 m._fmap = set(m._files)
32 m._always = False
32 m._always = False
33 origmatchfn = m.matchfn
33 origmatchfn = m.matchfn
34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
35 return m
35 return m
36
36
37 def composenormalfilematcher(match, manifest, exclude=None):
37 def composenormalfilematcher(match, manifest, exclude=None):
38 excluded = set()
38 excluded = set()
39 if exclude is not None:
39 if exclude is not None:
40 excluded.update(exclude)
40 excluded.update(exclude)
41
41
42 m = copy.copy(match)
42 m = copy.copy(match)
43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
44 manifest or f in excluded)
44 manifest or f in excluded)
45 m._files = filter(notlfile, m._files)
45 m._files = filter(notlfile, m._files)
46 m._fmap = set(m._files)
46 m._fmap = set(m._files)
47 m._always = False
47 m._always = False
48 origmatchfn = m.matchfn
48 origmatchfn = m.matchfn
49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
50 return m
50 return m
51
51
52 def installnormalfilesmatchfn(manifest):
52 def installnormalfilesmatchfn(manifest):
53 '''installmatchfn with a matchfn that ignores all largefiles'''
53 '''installmatchfn with a matchfn that ignores all largefiles'''
54 def overridematch(ctx, pats=[], opts={}, globbed=False,
54 def overridematch(ctx, pats=[], opts={}, globbed=False,
55 default='relpath'):
55 default='relpath'):
56 match = oldmatch(ctx, pats, opts, globbed, default)
56 match = oldmatch(ctx, pats, opts, globbed, default)
57 return composenormalfilematcher(match, manifest)
57 return composenormalfilematcher(match, manifest)
58 oldmatch = installmatchfn(overridematch)
58 oldmatch = installmatchfn(overridematch)
59
59
60 def installmatchfn(f):
60 def installmatchfn(f):
61 '''monkey patch the scmutil module with a custom match function.
61 '''monkey patch the scmutil module with a custom match function.
62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
63 oldmatch = scmutil.match
63 oldmatch = scmutil.match
64 setattr(f, 'oldmatch', oldmatch)
64 setattr(f, 'oldmatch', oldmatch)
65 scmutil.match = f
65 scmutil.match = f
66 return oldmatch
66 return oldmatch
67
67
68 def restorematchfn():
68 def restorematchfn():
69 '''restores scmutil.match to what it was before installmatchfn
69 '''restores scmutil.match to what it was before installmatchfn
70 was called. no-op if scmutil.match is its original function.
70 was called. no-op if scmutil.match is its original function.
71
71
72 Note that n calls to installmatchfn will require n calls to
72 Note that n calls to installmatchfn will require n calls to
73 restore the original matchfn.'''
73 restore the original matchfn.'''
74 scmutil.match = getattr(scmutil.match, 'oldmatch')
74 scmutil.match = getattr(scmutil.match, 'oldmatch')
75
75
76 def installmatchandpatsfn(f):
76 def installmatchandpatsfn(f):
77 oldmatchandpats = scmutil.matchandpats
77 oldmatchandpats = scmutil.matchandpats
78 setattr(f, 'oldmatchandpats', oldmatchandpats)
78 setattr(f, 'oldmatchandpats', oldmatchandpats)
79 scmutil.matchandpats = f
79 scmutil.matchandpats = f
80 return oldmatchandpats
80 return oldmatchandpats
81
81
82 def restorematchandpatsfn():
82 def restorematchandpatsfn():
83 '''restores scmutil.matchandpats to what it was before
83 '''restores scmutil.matchandpats to what it was before
84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
85 is its original function.
85 is its original function.
86
86
87 Note that n calls to installmatchandpatsfn will require n calls
87 Note that n calls to installmatchandpatsfn will require n calls
88 to restore the original matchfn.'''
88 to restore the original matchfn.'''
89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
90 scmutil.matchandpats)
90 scmutil.matchandpats)
91
91
92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
93 large = opts.pop('large', None)
93 large = opts.pop('large', None)
94 lfsize = lfutil.getminsize(
94 lfsize = lfutil.getminsize(
95 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
95 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
96
96
97 lfmatcher = None
97 lfmatcher = None
98 if lfutil.islfilesrepo(repo):
98 if lfutil.islfilesrepo(repo):
99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
100 if lfpats:
100 if lfpats:
101 lfmatcher = match_.match(repo.root, '', list(lfpats))
101 lfmatcher = match_.match(repo.root, '', list(lfpats))
102
102
103 lfnames = []
103 lfnames = []
104 m = copy.copy(matcher)
104 m = copy.copy(matcher)
105 m.bad = lambda x, y: None
105 m.bad = lambda x, y: None
106 wctx = repo[None]
106 wctx = repo[None]
107 for f in repo.walk(m):
107 for f in repo.walk(m):
108 exact = m.exact(f)
108 exact = m.exact(f)
109 lfile = lfutil.standin(f) in wctx
109 lfile = lfutil.standin(f) in wctx
110 nfile = f in wctx
110 nfile = f in wctx
111 exists = lfile or nfile
111 exists = lfile or nfile
112
112
113 # addremove in core gets fancy with the name, add doesn't
113 # addremove in core gets fancy with the name, add doesn't
114 if isaddremove:
114 if isaddremove:
115 name = m.uipath(f)
115 name = m.uipath(f)
116 else:
116 else:
117 name = m.rel(f)
117 name = m.rel(f)
118
118
119 # Don't warn the user when they attempt to add a normal tracked file.
119 # Don't warn the user when they attempt to add a normal tracked file.
120 # The normal add code will do that for us.
120 # The normal add code will do that for us.
121 if exact and exists:
121 if exact and exists:
122 if lfile:
122 if lfile:
123 ui.warn(_('%s already a largefile\n') % name)
123 ui.warn(_('%s already a largefile\n') % name)
124 continue
124 continue
125
125
126 if (exact or not exists) and not lfutil.isstandin(f):
126 if (exact or not exists) and not lfutil.isstandin(f):
127 # In case the file was removed previously, but not committed
127 # In case the file was removed previously, but not committed
128 # (issue3507)
128 # (issue3507)
129 if not repo.wvfs.exists(f):
129 if not repo.wvfs.exists(f):
130 continue
130 continue
131
131
132 abovemin = (lfsize and
132 abovemin = (lfsize and
133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
134 if large or abovemin or (lfmatcher and lfmatcher(f)):
134 if large or abovemin or (lfmatcher and lfmatcher(f)):
135 lfnames.append(f)
135 lfnames.append(f)
136 if ui.verbose or not exact:
136 if ui.verbose or not exact:
137 ui.status(_('adding %s as a largefile\n') % name)
137 ui.status(_('adding %s as a largefile\n') % name)
138
138
139 bad = []
139 bad = []
140
140
141 # Need to lock, otherwise there could be a race condition between
141 # Need to lock, otherwise there could be a race condition between
142 # when standins are created and added to the repo.
142 # when standins are created and added to the repo.
143 wlock = repo.wlock()
143 wlock = repo.wlock()
144 try:
144 try:
145 if not opts.get('dry_run'):
145 if not opts.get('dry_run'):
146 standins = []
146 standins = []
147 lfdirstate = lfutil.openlfdirstate(ui, repo)
147 lfdirstate = lfutil.openlfdirstate(ui, repo)
148 for f in lfnames:
148 for f in lfnames:
149 standinname = lfutil.standin(f)
149 standinname = lfutil.standin(f)
150 lfutil.writestandin(repo, standinname, hash='',
150 lfutil.writestandin(repo, standinname, hash='',
151 executable=lfutil.getexecutable(repo.wjoin(f)))
151 executable=lfutil.getexecutable(repo.wjoin(f)))
152 standins.append(standinname)
152 standins.append(standinname)
153 if lfdirstate[f] == 'r':
153 if lfdirstate[f] == '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 += [lfutil.splitstandin(f)
158 bad += [lfutil.splitstandin(f)
159 for f in repo[None].add(standins)
159 for f in repo[None].add(standins)
160 if f in m.files()]
160 if f in m.files()]
161
161
162 added = [f for f in lfnames if f not in bad]
162 added = [f for f in lfnames if f not in bad]
163 finally:
163 finally:
164 wlock.release()
164 wlock.release()
165 return added, bad
165 return added, bad
166
166
167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
168 after = opts.get('after')
168 after = opts.get('after')
169 m = composelargefilematcher(matcher, repo[None].manifest())
169 m = composelargefilematcher(matcher, repo[None].manifest())
170 try:
170 try:
171 repo.lfstatus = True
171 repo.lfstatus = True
172 s = repo.status(match=m, clean=not isaddremove)
172 s = repo.status(match=m, clean=not isaddremove)
173 finally:
173 finally:
174 repo.lfstatus = False
174 repo.lfstatus = False
175 manifest = repo[None].manifest()
175 manifest = repo[None].manifest()
176 modified, added, deleted, clean = [[f for f in list
176 modified, added, deleted, clean = [[f for f in list
177 if lfutil.standin(f) in manifest]
177 if lfutil.standin(f) in manifest]
178 for list in (s.modified, s.added,
178 for list in (s.modified, s.added,
179 s.deleted, s.clean)]
179 s.deleted, s.clean)]
180
180
181 def warn(files, msg):
181 def warn(files, msg):
182 for f in files:
182 for f in files:
183 ui.warn(msg % m.rel(f))
183 ui.warn(msg % m.rel(f))
184 return int(len(files) > 0)
184 return int(len(files) > 0)
185
185
186 result = 0
186 result = 0
187
187
188 if after:
188 if after:
189 remove = deleted
189 remove = deleted
190 result = warn(modified + added + clean,
190 result = warn(modified + added + clean,
191 _('not removing %s: file still exists\n'))
191 _('not removing %s: file still exists\n'))
192 else:
192 else:
193 remove = deleted + clean
193 remove = deleted + clean
194 result = warn(modified, _('not removing %s: file is modified (use -f'
194 result = warn(modified, _('not removing %s: file is modified (use -f'
195 ' to force removal)\n'))
195 ' to force removal)\n'))
196 result = warn(added, _('not removing %s: file has been marked for add'
196 result = warn(added, _('not removing %s: file has been marked for add'
197 ' (use forget to undo)\n')) or result
197 ' (use forget to undo)\n')) or result
198
198
199 # Need to lock because standin files are deleted then removed from the
199 # Need to lock because standin files are deleted then removed from the
200 # repository and we could race in-between.
200 # repository and we could race in-between.
201 wlock = repo.wlock()
201 wlock = repo.wlock()
202 try:
202 try:
203 lfdirstate = lfutil.openlfdirstate(ui, repo)
203 lfdirstate = lfutil.openlfdirstate(ui, repo)
204 for f in sorted(remove):
204 for f in sorted(remove):
205 if ui.verbose or not m.exact(f):
205 if ui.verbose or not m.exact(f):
206 # addremove in core gets fancy with the name, remove doesn't
206 # addremove in core gets fancy with the name, remove doesn't
207 if isaddremove:
207 if isaddremove:
208 name = m.uipath(f)
208 name = m.uipath(f)
209 else:
209 else:
210 name = m.rel(f)
210 name = m.rel(f)
211 ui.status(_('removing %s\n') % name)
211 ui.status(_('removing %s\n') % name)
212
212
213 if not opts.get('dry_run'):
213 if not opts.get('dry_run'):
214 if not after:
214 if not after:
215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
216
216
217 if opts.get('dry_run'):
217 if opts.get('dry_run'):
218 return result
218 return result
219
219
220 remove = [lfutil.standin(f) for f in remove]
220 remove = [lfutil.standin(f) for f in remove]
221 # If this is being called by addremove, let the original addremove
221 # If this is being called by addremove, let the original addremove
222 # function handle this.
222 # function handle this.
223 if not isaddremove:
223 if not isaddremove:
224 for f in remove:
224 for f in remove:
225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
226 repo[None].forget(remove)
226 repo[None].forget(remove)
227
227
228 for f in remove:
228 for f in remove:
229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
230 False)
230 False)
231
231
232 lfdirstate.write()
232 lfdirstate.write()
233 finally:
233 finally:
234 wlock.release()
234 wlock.release()
235
235
236 return result
236 return result
237
237
238 # For overriding mercurial.hgweb.webcommands so that largefiles will
238 # For overriding mercurial.hgweb.webcommands so that largefiles will
239 # appear at their right place in the manifests.
239 # appear at their right place in the manifests.
240 def decodepath(orig, path):
240 def decodepath(orig, path):
241 return lfutil.splitstandin(path) or path
241 return lfutil.splitstandin(path) or path
242
242
243 # -- Wrappers: modify existing commands --------------------------------
243 # -- Wrappers: modify existing commands --------------------------------
244
244
245 # Add works by going through the files that the user wanted to add and
245 # Add works by going through the files that the user wanted to add and
246 # checking if they should be added as largefiles. Then it makes a new
246 # checking if they should be added as largefiles. Then it makes a new
247 # matcher which matches only the normal files and runs the original
247 # matcher which matches only the normal files and runs the original
248 # version of add.
248 # version of add.
249 def overrideadd(orig, ui, repo, *pats, **opts):
249 def overrideadd(orig, ui, repo, *pats, **opts):
250 normal = opts.pop('normal')
250 normal = opts.pop('normal')
251 if normal:
251 if normal:
252 if opts.get('large'):
252 if opts.get('large'):
253 raise util.Abort(_('--normal cannot be used with --large'))
253 raise util.Abort(_('--normal cannot be used with --large'))
254 return orig(ui, repo, *pats, **opts)
254 return orig(ui, repo, *pats, **opts)
255 matcher = scmutil.match(repo[None], pats, opts)
255 matcher = scmutil.match(repo[None], pats, opts)
256 added, bad = addlargefiles(ui, repo, False, matcher, **opts)
256 added, bad = addlargefiles(ui, repo, False, matcher, **opts)
257 installnormalfilesmatchfn(repo[None].manifest())
257 installnormalfilesmatchfn(repo[None].manifest())
258 result = orig(ui, repo, *pats, **opts)
258 result = orig(ui, repo, *pats, **opts)
259 restorematchfn()
259 restorematchfn()
260
260
261 return (result == 1 or bad) and 1 or 0
261 return (result == 1 or bad) and 1 or 0
262
262
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
266 return removelargefiles(ui, repo, False, matcher, after=after,
266 return removelargefiles(ui, repo, False, matcher, after=after,
267 force=force) or result
267 force=force) or result
268
268
269 def overridestatusfn(orig, repo, rev2, **opts):
269 def overridestatusfn(orig, repo, rev2, **opts):
270 try:
270 try:
271 repo._repo.lfstatus = True
271 repo._repo.lfstatus = True
272 return orig(repo, rev2, **opts)
272 return orig(repo, rev2, **opts)
273 finally:
273 finally:
274 repo._repo.lfstatus = False
274 repo._repo.lfstatus = False
275
275
276 def overridestatus(orig, ui, repo, *pats, **opts):
276 def overridestatus(orig, ui, repo, *pats, **opts):
277 try:
277 try:
278 repo.lfstatus = True
278 repo.lfstatus = True
279 return orig(ui, repo, *pats, **opts)
279 return orig(ui, repo, *pats, **opts)
280 finally:
280 finally:
281 repo.lfstatus = False
281 repo.lfstatus = False
282
282
283 def overridedirty(orig, repo, ignoreupdate=False):
283 def overridedirty(orig, repo, ignoreupdate=False):
284 try:
284 try:
285 repo._repo.lfstatus = True
285 repo._repo.lfstatus = True
286 return orig(repo, ignoreupdate)
286 return orig(repo, ignoreupdate)
287 finally:
287 finally:
288 repo._repo.lfstatus = False
288 repo._repo.lfstatus = False
289
289
290 def overridelog(orig, ui, repo, *pats, **opts):
290 def overridelog(orig, ui, repo, *pats, **opts):
291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
292 default='relpath'):
292 default='relpath'):
293 """Matcher that merges root directory with .hglf, suitable for log.
293 """Matcher that merges root directory with .hglf, suitable for log.
294 It is still possible to match .hglf directly.
294 It is still possible to match .hglf directly.
295 For any listed files run log on the standin too.
295 For any listed files run log on the standin too.
296 matchfn tries both the given filename and with .hglf stripped.
296 matchfn tries both the given filename and with .hglf stripped.
297 """
297 """
298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
299 m, p = copy.copy(matchandpats)
299 m, p = copy.copy(matchandpats)
300
300
301 if m.always():
301 if m.always():
302 # We want to match everything anyway, so there's no benefit trying
302 # We want to match everything anyway, so there's no benefit trying
303 # to add standins.
303 # to add standins.
304 return matchandpats
304 return matchandpats
305
305
306 pats = set(p)
306 pats = set(p)
307 # TODO: handling of patterns in both cases below
307 # TODO: handling of patterns in both cases below
308 if m._cwd:
308 if m._cwd:
309 if os.path.isabs(m._cwd):
309 if os.path.isabs(m._cwd):
310 # TODO: handle largefile magic when invoked from other cwd
310 # TODO: handle largefile magic when invoked from other cwd
311 return matchandpats
311 return matchandpats
312 back = (m._cwd.count('/') + 1) * '../'
312 back = (m._cwd.count('/') + 1) * '../'
313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
314 else:
314 else:
315 pats.update(lfutil.standin(f) for f in p)
315 pats.update(lfutil.standin(f) for f in p)
316
316
317 for i in range(0, len(m._files)):
317 for i in range(0, len(m._files)):
318 standin = lfutil.standin(m._files[i])
318 standin = lfutil.standin(m._files[i])
319 if standin in repo[ctx.node()]:
319 if standin in repo[ctx.node()]:
320 m._files[i] = standin
320 m._files[i] = standin
321 elif m._files[i] not in repo[ctx.node()]:
321 elif m._files[i] not in repo[ctx.node()]:
322 m._files.append(standin)
322 m._files.append(standin)
323 pats.add(standin)
323 pats.add(standin)
324
324
325 m._fmap = set(m._files)
325 m._fmap = set(m._files)
326 m._always = False
326 m._always = False
327 origmatchfn = m.matchfn
327 origmatchfn = m.matchfn
328 def lfmatchfn(f):
328 def lfmatchfn(f):
329 lf = lfutil.splitstandin(f)
329 lf = lfutil.splitstandin(f)
330 if lf is not None and origmatchfn(lf):
330 if lf is not None and origmatchfn(lf):
331 return True
331 return True
332 r = origmatchfn(f)
332 r = origmatchfn(f)
333 return r
333 return r
334 m.matchfn = lfmatchfn
334 m.matchfn = lfmatchfn
335
335
336 return m, pats
336 return m, pats
337
337
338 # For hg log --patch, the match object is used in two different senses:
338 # For hg log --patch, the match object is used in two different senses:
339 # (1) to determine what revisions should be printed out, and
339 # (1) to determine what revisions should be printed out, and
340 # (2) to determine what files to print out diffs for.
340 # (2) to determine what files to print out diffs for.
341 # The magic matchandpats override should be used for case (1) but not for
341 # The magic matchandpats override should be used for case (1) but not for
342 # case (2).
342 # case (2).
343 def overridemakelogfilematcher(repo, pats, opts):
343 def overridemakelogfilematcher(repo, pats, opts):
344 pctx = repo[None]
344 pctx = repo[None]
345 match, pats = oldmatchandpats(pctx, pats, opts)
345 match, pats = oldmatchandpats(pctx, pats, opts)
346 return lambda rev: match
346 return lambda rev: match
347
347
348 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
348 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
349 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
349 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
350 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
350 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
351
351
352 try:
352 try:
353 return orig(ui, repo, *pats, **opts)
353 return orig(ui, repo, *pats, **opts)
354 finally:
354 finally:
355 restorematchandpatsfn()
355 restorematchandpatsfn()
356 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
356 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
357
357
358 def overrideverify(orig, ui, repo, *pats, **opts):
358 def overrideverify(orig, ui, repo, *pats, **opts):
359 large = opts.pop('large', False)
359 large = opts.pop('large', False)
360 all = opts.pop('lfa', False)
360 all = opts.pop('lfa', False)
361 contents = opts.pop('lfc', False)
361 contents = opts.pop('lfc', False)
362
362
363 result = orig(ui, repo, *pats, **opts)
363 result = orig(ui, repo, *pats, **opts)
364 if large or all or contents:
364 if large or all or contents:
365 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
365 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
366 return result
366 return result
367
367
368 def overridedebugstate(orig, ui, repo, *pats, **opts):
368 def overridedebugstate(orig, ui, repo, *pats, **opts):
369 large = opts.pop('large', False)
369 large = opts.pop('large', False)
370 if large:
370 if large:
371 class fakerepo(object):
371 class fakerepo(object):
372 dirstate = lfutil.openlfdirstate(ui, repo)
372 dirstate = lfutil.openlfdirstate(ui, repo)
373 orig(ui, fakerepo, *pats, **opts)
373 orig(ui, fakerepo, *pats, **opts)
374 else:
374 else:
375 orig(ui, repo, *pats, **opts)
375 orig(ui, repo, *pats, **opts)
376
376
377 # Override needs to refresh standins so that update's normal merge
377 # Override needs to refresh standins so that update's normal merge
378 # will go through properly. Then the other update hook (overriding repo.update)
378 # will go through properly. Then the other update hook (overriding repo.update)
379 # will get the new files. Filemerge is also overridden so that the merge
379 # will get the new files. Filemerge is also overridden so that the merge
380 # will merge standins correctly.
380 # will merge standins correctly.
381 def overrideupdate(orig, ui, repo, *pats, **opts):
381 def overrideupdate(orig, ui, repo, *pats, **opts):
382 # Need to lock between the standins getting updated and their
382 # Need to lock between the standins getting updated and their
383 # largefiles getting updated
383 # largefiles getting updated
384 wlock = repo.wlock()
384 wlock = repo.wlock()
385 try:
385 try:
386 if opts['check']:
386 if opts['check']:
387 lfdirstate = lfutil.openlfdirstate(ui, repo)
387 lfdirstate = lfutil.openlfdirstate(ui, repo)
388 unsure, s = lfdirstate.status(
388 unsure, s = lfdirstate.status(
389 match_.always(repo.root, repo.getcwd()),
389 match_.always(repo.root, repo.getcwd()),
390 [], False, False, False)
390 [], False, False, False)
391
391
392 mod = len(s.modified) > 0
392 mod = len(s.modified) > 0
393 for lfile in unsure:
393 for lfile in unsure:
394 standin = lfutil.standin(lfile)
394 standin = lfutil.standin(lfile)
395 if repo['.'][standin].data().strip() != \
395 if repo['.'][standin].data().strip() != \
396 lfutil.hashfile(repo.wjoin(lfile)):
396 lfutil.hashfile(repo.wjoin(lfile)):
397 mod = True
397 mod = True
398 else:
398 else:
399 lfdirstate.normal(lfile)
399 lfdirstate.normal(lfile)
400 lfdirstate.write()
400 lfdirstate.write()
401 if mod:
401 if mod:
402 raise util.Abort(_('uncommitted changes'))
402 raise util.Abort(_('uncommitted changes'))
403 return orig(ui, repo, *pats, **opts)
403 return orig(ui, repo, *pats, **opts)
404 finally:
404 finally:
405 wlock.release()
405 wlock.release()
406
406
407 # Before starting the manifest merge, merge.updates will call
407 # Before starting the manifest merge, merge.updates will call
408 # _checkunknownfile to check if there are any files in the merged-in
408 # _checkunknownfile to check if there are any files in the merged-in
409 # changeset that collide with unknown files in the working copy.
409 # changeset that collide with unknown files in the working copy.
410 #
410 #
411 # The largefiles are seen as unknown, so this prevents us from merging
411 # The largefiles are seen as unknown, so this prevents us from merging
412 # in a file 'foo' if we already have a largefile with the same name.
412 # in a file 'foo' if we already have a largefile with the same name.
413 #
413 #
414 # The overridden function filters the unknown files by removing any
414 # The overridden function filters the unknown files by removing any
415 # largefiles. This makes the merge proceed and we can then handle this
415 # largefiles. This makes the merge proceed and we can then handle this
416 # case further in the overridden calculateupdates function below.
416 # case further in the overridden calculateupdates function below.
417 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
417 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
418 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
418 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
419 return False
419 return False
420 return origfn(repo, wctx, mctx, f, f2)
420 return origfn(repo, wctx, mctx, f, f2)
421
421
422 # The manifest merge handles conflicts on the manifest level. We want
422 # The manifest merge handles conflicts on the manifest level. We want
423 # to handle changes in largefile-ness of files at this level too.
423 # to handle changes in largefile-ness of files at this level too.
424 #
424 #
425 # The strategy is to run the original calculateupdates and then process
425 # The strategy is to run the original calculateupdates and then process
426 # the action list it outputs. There are two cases we need to deal with:
426 # the action list it outputs. There are two cases we need to deal with:
427 #
427 #
428 # 1. Normal file in p1, largefile in p2. Here the largefile is
428 # 1. Normal file in p1, largefile in p2. Here the largefile is
429 # detected via its standin file, which will enter the working copy
429 # detected via its standin file, which will enter the working copy
430 # with a "get" action. It is not "merge" since the standin is all
430 # with a "get" action. It is not "merge" since the standin is all
431 # Mercurial is concerned with at this level -- the link to the
431 # Mercurial is concerned with at this level -- the link to the
432 # existing normal file is not relevant here.
432 # existing normal file is not relevant here.
433 #
433 #
434 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
434 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
435 # since the largefile will be present in the working copy and
435 # since the largefile will be present in the working copy and
436 # different from the normal file in p2. Mercurial therefore
436 # different from the normal file in p2. Mercurial therefore
437 # triggers a merge action.
437 # triggers a merge action.
438 #
438 #
439 # In both cases, we prompt the user and emit new actions to either
439 # In both cases, we prompt the user and emit new actions to either
440 # remove the standin (if the normal file was kept) or to remove the
440 # remove the standin (if the normal file was kept) or to remove the
441 # normal file and get the standin (if the largefile was kept). The
441 # normal file and get the standin (if the largefile was kept). The
442 # default prompt answer is to use the largefile version since it was
442 # default prompt answer is to use the largefile version since it was
443 # presumably changed on purpose.
443 # presumably changed on purpose.
444 #
444 #
445 # Finally, the merge.applyupdates function will then take care of
445 # Finally, the merge.applyupdates function will then take care of
446 # writing the files into the working copy and lfcommands.updatelfiles
446 # writing the files into the working copy and lfcommands.updatelfiles
447 # will update the largefiles.
447 # will update the largefiles.
448 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
448 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
449 partial, acceptremote, followcopies):
449 partial, acceptremote, followcopies):
450 overwrite = force and not branchmerge
450 overwrite = force and not branchmerge
451 actions, diverge, renamedelete = origfn(
451 actions, diverge, renamedelete = origfn(
452 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
452 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
453 followcopies)
453 followcopies)
454
454
455 if overwrite:
455 if overwrite:
456 return actions, diverge, renamedelete
456 return actions, diverge, renamedelete
457
457
458 # Convert to dictionary with filename as key and action as value.
458 # Convert to dictionary with filename as key and action as value.
459 lfiles = set()
459 lfiles = set()
460 for f in actions:
460 for f in actions:
461 splitstandin = f and lfutil.splitstandin(f)
461 splitstandin = f and lfutil.splitstandin(f)
462 if splitstandin in p1:
462 if splitstandin in p1:
463 lfiles.add(splitstandin)
463 lfiles.add(splitstandin)
464 elif lfutil.standin(f) in p1:
464 elif lfutil.standin(f) in p1:
465 lfiles.add(f)
465 lfiles.add(f)
466
466
467 for lfile in lfiles:
467 for lfile in lfiles:
468 standin = lfutil.standin(lfile)
468 standin = lfutil.standin(lfile)
469 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
469 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
470 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
470 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
471 if sm in ('g', 'dc') and lm != 'r':
471 if sm in ('g', 'dc') and lm != 'r':
472 # Case 1: normal file in the working copy, largefile in
472 # Case 1: normal file in the working copy, largefile in
473 # the second parent
473 # the second parent
474 usermsg = _('remote turned local normal file %s into a largefile\n'
474 usermsg = _('remote turned local normal file %s into a largefile\n'
475 'use (l)argefile or keep (n)ormal file?'
475 'use (l)argefile or keep (n)ormal file?'
476 '$$ &Largefile $$ &Normal file') % lfile
476 '$$ &Largefile $$ &Normal file') % lfile
477 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
477 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
478 actions[lfile] = ('r', None, 'replaced by standin')
478 actions[lfile] = ('r', None, 'replaced by standin')
479 actions[standin] = ('g', sargs, 'replaces standin')
479 actions[standin] = ('g', sargs, 'replaces standin')
480 else: # keep local normal file
480 else: # keep local normal file
481 actions[lfile] = ('k', None, 'replaces standin')
481 actions[lfile] = ('k', None, 'replaces standin')
482 if branchmerge:
482 if branchmerge:
483 actions[standin] = ('k', None, 'replaced by non-standin')
483 actions[standin] = ('k', None, 'replaced by non-standin')
484 else:
484 else:
485 actions[standin] = ('r', None, 'replaced by non-standin')
485 actions[standin] = ('r', None, 'replaced by non-standin')
486 elif lm in ('g', 'dc') and sm != 'r':
486 elif lm in ('g', 'dc') and sm != 'r':
487 # Case 2: largefile in the working copy, normal file in
487 # Case 2: largefile in the working copy, normal file in
488 # the second parent
488 # the second parent
489 usermsg = _('remote turned local largefile %s into a normal file\n'
489 usermsg = _('remote turned local largefile %s into a normal file\n'
490 'keep (l)argefile or use (n)ormal file?'
490 'keep (l)argefile or use (n)ormal file?'
491 '$$ &Largefile $$ &Normal file') % lfile
491 '$$ &Largefile $$ &Normal file') % lfile
492 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
492 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
493 if branchmerge:
493 if branchmerge:
494 # largefile can be restored from standin safely
494 # largefile can be restored from standin safely
495 actions[lfile] = ('k', None, 'replaced by standin')
495 actions[lfile] = ('k', None, 'replaced by standin')
496 actions[standin] = ('k', None, 'replaces standin')
496 actions[standin] = ('k', None, 'replaces standin')
497 else:
497 else:
498 # "lfile" should be marked as "removed" without
498 # "lfile" should be marked as "removed" without
499 # removal of itself
499 # removal of itself
500 actions[lfile] = ('lfmr', None,
500 actions[lfile] = ('lfmr', None,
501 'forget non-standin largefile')
501 'forget non-standin largefile')
502
502
503 # linear-merge should treat this largefile as 're-added'
503 # linear-merge should treat this largefile as 're-added'
504 actions[standin] = ('a', None, 'keep standin')
504 actions[standin] = ('a', None, 'keep standin')
505 else: # pick remote normal file
505 else: # pick remote normal file
506 actions[lfile] = ('g', largs, 'replaces standin')
506 actions[lfile] = ('g', largs, 'replaces standin')
507 actions[standin] = ('r', None, 'replaced by non-standin')
507 actions[standin] = ('r', None, 'replaced by non-standin')
508
508
509 return actions, diverge, renamedelete
509 return actions, diverge, renamedelete
510
510
511 def mergerecordupdates(orig, repo, actions, branchmerge):
511 def mergerecordupdates(orig, repo, actions, branchmerge):
512 if 'lfmr' in actions:
512 if 'lfmr' in actions:
513 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
513 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
514 for lfile, args, msg in actions['lfmr']:
514 for lfile, args, msg in actions['lfmr']:
515 # this should be executed before 'orig', to execute 'remove'
515 # this should be executed before 'orig', to execute 'remove'
516 # before all other actions
516 # before all other actions
517 repo.dirstate.remove(lfile)
517 repo.dirstate.remove(lfile)
518 # make sure lfile doesn't get synclfdirstate'd as normal
518 # make sure lfile doesn't get synclfdirstate'd as normal
519 lfdirstate.add(lfile)
519 lfdirstate.add(lfile)
520 lfdirstate.write()
520 lfdirstate.write()
521
521
522 return orig(repo, actions, branchmerge)
522 return orig(repo, actions, branchmerge)
523
523
524
524
525 # Override filemerge to prompt the user about how they wish to merge
525 # Override filemerge to prompt the user about how they wish to merge
526 # largefiles. This will handle identical edits without prompting the user.
526 # largefiles. This will handle identical edits without prompting the user.
527 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
527 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
528 if not lfutil.isstandin(orig):
528 if not lfutil.isstandin(orig):
529 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
529 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
530
530
531 ahash = fca.data().strip().lower()
531 ahash = fca.data().strip().lower()
532 dhash = fcd.data().strip().lower()
532 dhash = fcd.data().strip().lower()
533 ohash = fco.data().strip().lower()
533 ohash = fco.data().strip().lower()
534 if (ohash != ahash and
534 if (ohash != ahash and
535 ohash != dhash and
535 ohash != dhash and
536 (dhash == ahash or
536 (dhash == ahash or
537 repo.ui.promptchoice(
537 repo.ui.promptchoice(
538 _('largefile %s has a merge conflict\nancestor was %s\n'
538 _('largefile %s has a merge conflict\nancestor was %s\n'
539 'keep (l)ocal %s or\ntake (o)ther %s?'
539 'keep (l)ocal %s or\ntake (o)ther %s?'
540 '$$ &Local $$ &Other') %
540 '$$ &Local $$ &Other') %
541 (lfutil.splitstandin(orig), ahash, dhash, ohash),
541 (lfutil.splitstandin(orig), ahash, dhash, ohash),
542 0) == 1)):
542 0) == 1)):
543 repo.wwrite(fcd.path(), fco.data(), fco.flags())
543 repo.wwrite(fcd.path(), fco.data(), fco.flags())
544 return 0
544 return 0
545
545
546 # Copy first changes the matchers to match standins instead of
546 # Copy first changes the matchers to match standins instead of
547 # largefiles. Then it overrides util.copyfile in that function it
547 # largefiles. Then it overrides util.copyfile in that function it
548 # checks if the destination largefile already exists. It also keeps a
548 # checks if the destination largefile already exists. It also keeps a
549 # list of copied files so that the largefiles can be copied and the
549 # list of copied files so that the largefiles can be copied and the
550 # dirstate updated.
550 # dirstate updated.
551 def overridecopy(orig, ui, repo, pats, opts, rename=False):
551 def overridecopy(orig, ui, repo, pats, opts, rename=False):
552 # doesn't remove largefile on rename
552 # doesn't remove largefile on rename
553 if len(pats) < 2:
553 if len(pats) < 2:
554 # this isn't legal, let the original function deal with it
554 # this isn't legal, let the original function deal with it
555 return orig(ui, repo, pats, opts, rename)
555 return orig(ui, repo, pats, opts, rename)
556
556
557 def makestandin(relpath):
557 def makestandin(relpath):
558 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
558 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
559 return os.path.join(repo.wjoin(lfutil.standin(path)))
559 return os.path.join(repo.wjoin(lfutil.standin(path)))
560
560
561 fullpats = scmutil.expandpats(pats)
561 fullpats = scmutil.expandpats(pats)
562 dest = fullpats[-1]
562 dest = fullpats[-1]
563
563
564 if os.path.isdir(dest):
564 if os.path.isdir(dest):
565 if not os.path.isdir(makestandin(dest)):
565 if not os.path.isdir(makestandin(dest)):
566 os.makedirs(makestandin(dest))
566 os.makedirs(makestandin(dest))
567 # This could copy both lfiles and normal files in one command,
567 # This could copy both lfiles and normal files in one command,
568 # but we don't want to do that. First replace their matcher to
568 # but we don't want to do that. First replace their matcher to
569 # only match normal files and run it, then replace it to just
569 # only match normal files and run it, then replace it to just
570 # match largefiles and run it again.
570 # match largefiles and run it again.
571 nonormalfiles = False
571 nonormalfiles = False
572 nolfiles = False
572 nolfiles = False
573 installnormalfilesmatchfn(repo[None].manifest())
573 installnormalfilesmatchfn(repo[None].manifest())
574 try:
574 try:
575 try:
575 try:
576 result = orig(ui, repo, pats, opts, rename)
576 result = orig(ui, repo, pats, opts, rename)
577 except util.Abort, e:
577 except util.Abort, e:
578 if str(e) != _('no files to copy'):
578 if str(e) != _('no files to copy'):
579 raise e
579 raise e
580 else:
580 else:
581 nonormalfiles = True
581 nonormalfiles = True
582 result = 0
582 result = 0
583 finally:
583 finally:
584 restorematchfn()
584 restorematchfn()
585
585
586 # The first rename can cause our current working directory to be removed.
586 # The first rename can cause our current working directory to be removed.
587 # In that case there is nothing left to copy/rename so just quit.
587 # In that case there is nothing left to copy/rename so just quit.
588 try:
588 try:
589 repo.getcwd()
589 repo.getcwd()
590 except OSError:
590 except OSError:
591 return result
591 return result
592
592
593 try:
593 try:
594 try:
594 try:
595 # When we call orig below it creates the standins but we don't add
595 # When we call orig below it creates the standins but we don't add
596 # them to the dir state until later so lock during that time.
596 # them to the dir state until later so lock during that time.
597 wlock = repo.wlock()
597 wlock = repo.wlock()
598
598
599 manifest = repo[None].manifest()
599 manifest = repo[None].manifest()
600 def overridematch(ctx, pats=[], opts={}, globbed=False,
600 def overridematch(ctx, pats=[], opts={}, globbed=False,
601 default='relpath'):
601 default='relpath'):
602 newpats = []
602 newpats = []
603 # The patterns were previously mangled to add the standin
603 # The patterns were previously mangled to add the standin
604 # directory; we need to remove that now
604 # directory; we need to remove that now
605 for pat in pats:
605 for pat in pats:
606 if match_.patkind(pat) is None and lfutil.shortname in pat:
606 if match_.patkind(pat) is None and lfutil.shortname in pat:
607 newpats.append(pat.replace(lfutil.shortname, ''))
607 newpats.append(pat.replace(lfutil.shortname, ''))
608 else:
608 else:
609 newpats.append(pat)
609 newpats.append(pat)
610 match = oldmatch(ctx, newpats, opts, globbed, default)
610 match = oldmatch(ctx, newpats, opts, globbed, default)
611 m = copy.copy(match)
611 m = copy.copy(match)
612 lfile = lambda f: lfutil.standin(f) in manifest
612 lfile = lambda f: lfutil.standin(f) in manifest
613 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
613 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
614 m._fmap = set(m._files)
614 m._fmap = set(m._files)
615 origmatchfn = m.matchfn
615 origmatchfn = m.matchfn
616 m.matchfn = lambda f: (lfutil.isstandin(f) and
616 m.matchfn = lambda f: (lfutil.isstandin(f) and
617 (f in manifest) and
617 (f in manifest) and
618 origmatchfn(lfutil.splitstandin(f)) or
618 origmatchfn(lfutil.splitstandin(f)) or
619 None)
619 None)
620 return m
620 return m
621 oldmatch = installmatchfn(overridematch)
621 oldmatch = installmatchfn(overridematch)
622 listpats = []
622 listpats = []
623 for pat in pats:
623 for pat in pats:
624 if match_.patkind(pat) is not None:
624 if match_.patkind(pat) is not None:
625 listpats.append(pat)
625 listpats.append(pat)
626 else:
626 else:
627 listpats.append(makestandin(pat))
627 listpats.append(makestandin(pat))
628
628
629 try:
629 try:
630 origcopyfile = util.copyfile
630 origcopyfile = util.copyfile
631 copiedfiles = []
631 copiedfiles = []
632 def overridecopyfile(src, dest):
632 def overridecopyfile(src, dest):
633 if (lfutil.shortname in src and
633 if (lfutil.shortname in src and
634 dest.startswith(repo.wjoin(lfutil.shortname))):
634 dest.startswith(repo.wjoin(lfutil.shortname))):
635 destlfile = dest.replace(lfutil.shortname, '')
635 destlfile = dest.replace(lfutil.shortname, '')
636 if not opts['force'] and os.path.exists(destlfile):
636 if not opts['force'] and os.path.exists(destlfile):
637 raise IOError('',
637 raise IOError('',
638 _('destination largefile already exists'))
638 _('destination largefile already exists'))
639 copiedfiles.append((src, dest))
639 copiedfiles.append((src, dest))
640 origcopyfile(src, dest)
640 origcopyfile(src, dest)
641
641
642 util.copyfile = overridecopyfile
642 util.copyfile = overridecopyfile
643 result += orig(ui, repo, listpats, opts, rename)
643 result += orig(ui, repo, listpats, opts, rename)
644 finally:
644 finally:
645 util.copyfile = origcopyfile
645 util.copyfile = origcopyfile
646
646
647 lfdirstate = lfutil.openlfdirstate(ui, repo)
647 lfdirstate = lfutil.openlfdirstate(ui, repo)
648 for (src, dest) in copiedfiles:
648 for (src, dest) in copiedfiles:
649 if (lfutil.shortname in src and
649 if (lfutil.shortname in src and
650 dest.startswith(repo.wjoin(lfutil.shortname))):
650 dest.startswith(repo.wjoin(lfutil.shortname))):
651 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
651 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
652 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
652 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
653 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
653 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
654 if not os.path.isdir(destlfiledir):
654 if not os.path.isdir(destlfiledir):
655 os.makedirs(destlfiledir)
655 os.makedirs(destlfiledir)
656 if rename:
656 if rename:
657 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
657 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
658
658
659 # The file is gone, but this deletes any empty parent
659 # The file is gone, but this deletes any empty parent
660 # directories as a side-effect.
660 # directories as a side-effect.
661 util.unlinkpath(repo.wjoin(srclfile), True)
661 util.unlinkpath(repo.wjoin(srclfile), True)
662 lfdirstate.remove(srclfile)
662 lfdirstate.remove(srclfile)
663 else:
663 else:
664 util.copyfile(repo.wjoin(srclfile),
664 util.copyfile(repo.wjoin(srclfile),
665 repo.wjoin(destlfile))
665 repo.wjoin(destlfile))
666
666
667 lfdirstate.add(destlfile)
667 lfdirstate.add(destlfile)
668 lfdirstate.write()
668 lfdirstate.write()
669 except util.Abort, e:
669 except util.Abort, e:
670 if str(e) != _('no files to copy'):
670 if str(e) != _('no files to copy'):
671 raise e
671 raise e
672 else:
672 else:
673 nolfiles = True
673 nolfiles = True
674 finally:
674 finally:
675 restorematchfn()
675 restorematchfn()
676 wlock.release()
676 wlock.release()
677
677
678 if nolfiles and nonormalfiles:
678 if nolfiles and nonormalfiles:
679 raise util.Abort(_('no files to copy'))
679 raise util.Abort(_('no files to copy'))
680
680
681 return result
681 return result
682
682
683 # When the user calls revert, we have to be careful to not revert any
683 # When the user calls revert, we have to be careful to not revert any
684 # changes to other largefiles accidentally. This means we have to keep
684 # changes to other largefiles accidentally. This means we have to keep
685 # track of the largefiles that are being reverted so we only pull down
685 # track of the largefiles that are being reverted so we only pull down
686 # the necessary largefiles.
686 # the necessary largefiles.
687 #
687 #
688 # Standins are only updated (to match the hash of largefiles) before
688 # Standins are only updated (to match the hash of largefiles) before
689 # commits. Update the standins then run the original revert, changing
689 # commits. Update the standins then run the original revert, changing
690 # the matcher to hit standins instead of largefiles. Based on the
690 # the matcher to hit standins instead of largefiles. Based on the
691 # resulting standins update the largefiles.
691 # resulting standins update the largefiles.
692 def overriderevert(orig, ui, repo, *pats, **opts):
692 def overriderevert(orig, ui, repo, *pats, **opts):
693 # Because we put the standins in a bad state (by updating them)
693 # Because we put the standins in a bad state (by updating them)
694 # and then return them to a correct state we need to lock to
694 # and then return them to a correct state we need to lock to
695 # prevent others from changing them in their incorrect state.
695 # prevent others from changing them in their incorrect state.
696 wlock = repo.wlock()
696 wlock = repo.wlock()
697 try:
697 try:
698 lfdirstate = lfutil.openlfdirstate(ui, repo)
698 lfdirstate = lfutil.openlfdirstate(ui, repo)
699 s = lfutil.lfdirstatestatus(lfdirstate, repo)
699 s = lfutil.lfdirstatestatus(lfdirstate, repo)
700 lfdirstate.write()
700 lfdirstate.write()
701 for lfile in s.modified:
701 for lfile in s.modified:
702 lfutil.updatestandin(repo, lfutil.standin(lfile))
702 lfutil.updatestandin(repo, lfutil.standin(lfile))
703 for lfile in s.deleted:
703 for lfile in s.deleted:
704 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
704 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
705 os.unlink(repo.wjoin(lfutil.standin(lfile)))
705 os.unlink(repo.wjoin(lfutil.standin(lfile)))
706
706
707 oldstandins = lfutil.getstandinsstate(repo)
707 oldstandins = lfutil.getstandinsstate(repo)
708
708
709 def overridematch(ctx, pats=[], opts={}, globbed=False,
709 def overridematch(ctx, pats=[], opts={}, globbed=False,
710 default='relpath'):
710 default='relpath'):
711 match = oldmatch(ctx, pats, opts, globbed, default)
711 match = oldmatch(ctx, pats, opts, globbed, default)
712 m = copy.copy(match)
712 m = copy.copy(match)
713 def tostandin(f):
713 def tostandin(f):
714 if lfutil.standin(f) in ctx:
714 if lfutil.standin(f) in ctx:
715 return lfutil.standin(f)
715 return lfutil.standin(f)
716 elif lfutil.standin(f) in repo[None]:
716 elif lfutil.standin(f) in repo[None]:
717 return None
717 return None
718 return f
718 return f
719 m._files = [tostandin(f) for f in m._files]
719 m._files = [tostandin(f) for f in m._files]
720 m._files = [f for f in m._files if f is not None]
720 m._files = [f for f in m._files if f is not None]
721 m._fmap = set(m._files)
721 m._fmap = set(m._files)
722 origmatchfn = m.matchfn
722 origmatchfn = m.matchfn
723 def matchfn(f):
723 def matchfn(f):
724 if lfutil.isstandin(f):
724 if lfutil.isstandin(f):
725 return (origmatchfn(lfutil.splitstandin(f)) and
725 return (origmatchfn(lfutil.splitstandin(f)) and
726 (f in repo[None] or f in ctx))
726 (f in repo[None] or f in ctx))
727 return origmatchfn(f)
727 return origmatchfn(f)
728 m.matchfn = matchfn
728 m.matchfn = matchfn
729 return m
729 return m
730 oldmatch = installmatchfn(overridematch)
730 oldmatch = installmatchfn(overridematch)
731 try:
731 try:
732 orig(ui, repo, *pats, **opts)
732 orig(ui, repo, *pats, **opts)
733 finally:
733 finally:
734 restorematchfn()
734 restorematchfn()
735
735
736 newstandins = lfutil.getstandinsstate(repo)
736 newstandins = lfutil.getstandinsstate(repo)
737 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
737 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
738 # lfdirstate should be 'normallookup'-ed for updated files,
738 # lfdirstate should be 'normallookup'-ed for updated files,
739 # because reverting doesn't touch dirstate for 'normal' files
739 # because reverting doesn't touch dirstate for 'normal' files
740 # when target revision is explicitly specified: in such case,
740 # when target revision is explicitly specified: in such case,
741 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
741 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
742 # of target (standin) file.
742 # of target (standin) file.
743 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
743 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
744 normallookup=True)
744 normallookup=True)
745
745
746 finally:
746 finally:
747 wlock.release()
747 wlock.release()
748
748
749 # after pulling changesets, we need to take some extra care to get
749 # after pulling changesets, we need to take some extra care to get
750 # largefiles updated remotely
750 # largefiles updated remotely
751 def overridepull(orig, ui, repo, source=None, **opts):
751 def overridepull(orig, ui, repo, source=None, **opts):
752 revsprepull = len(repo)
752 revsprepull = len(repo)
753 if not source:
753 if not source:
754 source = 'default'
754 source = 'default'
755 repo.lfpullsource = source
755 repo.lfpullsource = source
756 result = orig(ui, repo, source, **opts)
756 result = orig(ui, repo, source, **opts)
757 revspostpull = len(repo)
757 revspostpull = len(repo)
758 lfrevs = opts.get('lfrev', [])
758 lfrevs = opts.get('lfrev', [])
759 if opts.get('all_largefiles'):
759 if opts.get('all_largefiles'):
760 lfrevs.append('pulled()')
760 lfrevs.append('pulled()')
761 if lfrevs and revspostpull > revsprepull:
761 if lfrevs and revspostpull > revsprepull:
762 numcached = 0
762 numcached = 0
763 repo.firstpulled = revsprepull # for pulled() revset expression
763 repo.firstpulled = revsprepull # for pulled() revset expression
764 try:
764 try:
765 for rev in scmutil.revrange(repo, lfrevs):
765 for rev in scmutil.revrange(repo, lfrevs):
766 ui.note(_('pulling largefiles for revision %s\n') % rev)
766 ui.note(_('pulling largefiles for revision %s\n') % rev)
767 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
767 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
768 numcached += len(cached)
768 numcached += len(cached)
769 finally:
769 finally:
770 del repo.firstpulled
770 del repo.firstpulled
771 ui.status(_("%d largefiles cached\n") % numcached)
771 ui.status(_("%d largefiles cached\n") % numcached)
772 return result
772 return result
773
773
774 def pulledrevsetsymbol(repo, subset, x):
774 def pulledrevsetsymbol(repo, subset, x):
775 """``pulled()``
775 """``pulled()``
776 Changesets that just has been pulled.
776 Changesets that just has been pulled.
777
777
778 Only available with largefiles from pull --lfrev expressions.
778 Only available with largefiles from pull --lfrev expressions.
779
779
780 .. container:: verbose
780 .. container:: verbose
781
781
782 Some examples:
782 Some examples:
783
783
784 - pull largefiles for all new changesets::
784 - pull largefiles for all new changesets::
785
785
786 hg pull -lfrev "pulled()"
786 hg pull -lfrev "pulled()"
787
787
788 - pull largefiles for all new branch heads::
788 - pull largefiles for all new branch heads::
789
789
790 hg pull -lfrev "head(pulled()) and not closed()"
790 hg pull -lfrev "head(pulled()) and not closed()"
791
791
792 """
792 """
793
793
794 try:
794 try:
795 firstpulled = repo.firstpulled
795 firstpulled = repo.firstpulled
796 except AttributeError:
796 except AttributeError:
797 raise util.Abort(_("pulled() only available in --lfrev"))
797 raise util.Abort(_("pulled() only available in --lfrev"))
798 return revset.baseset([r for r in subset if r >= firstpulled])
798 return revset.baseset([r for r in subset if r >= firstpulled])
799
799
800 def overrideclone(orig, ui, source, dest=None, **opts):
800 def overrideclone(orig, ui, source, dest=None, **opts):
801 d = dest
801 d = dest
802 if d is None:
802 if d is None:
803 d = hg.defaultdest(source)
803 d = hg.defaultdest(source)
804 if opts.get('all_largefiles') and not hg.islocal(d):
804 if opts.get('all_largefiles') and not hg.islocal(d):
805 raise util.Abort(_(
805 raise util.Abort(_(
806 '--all-largefiles is incompatible with non-local destination %s') %
806 '--all-largefiles is incompatible with non-local destination %s') %
807 d)
807 d)
808
808
809 return orig(ui, source, dest, **opts)
809 return orig(ui, source, dest, **opts)
810
810
811 def hgclone(orig, ui, opts, *args, **kwargs):
811 def hgclone(orig, ui, opts, *args, **kwargs):
812 result = orig(ui, opts, *args, **kwargs)
812 result = orig(ui, opts, *args, **kwargs)
813
813
814 if result is not None:
814 if result is not None:
815 sourcerepo, destrepo = result
815 sourcerepo, destrepo = result
816 repo = destrepo.local()
816 repo = destrepo.local()
817
817
818 # Caching is implicitly limited to 'rev' option, since the dest repo was
818 # Caching is implicitly limited to 'rev' option, since the dest repo was
819 # truncated at that point. The user may expect a download count with
819 # truncated at that point. The user may expect a download count with
820 # this option, so attempt whether or not this is a largefile repo.
820 # this option, so attempt whether or not this is a largefile repo.
821 if opts.get('all_largefiles'):
821 if opts.get('all_largefiles'):
822 success, missing = lfcommands.downloadlfiles(ui, repo, None)
822 success, missing = lfcommands.downloadlfiles(ui, repo, None)
823
823
824 if missing != 0:
824 if missing != 0:
825 return None
825 return None
826
826
827 return result
827 return result
828
828
829 def overriderebase(orig, ui, repo, **opts):
829 def overriderebase(orig, ui, repo, **opts):
830 resuming = opts.get('continue')
830 resuming = opts.get('continue')
831 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
831 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
832 repo._lfstatuswriters.append(lambda *msg, **opts: None)
832 repo._lfstatuswriters.append(lambda *msg, **opts: None)
833 try:
833 try:
834 return orig(ui, repo, **opts)
834 return orig(ui, repo, **opts)
835 finally:
835 finally:
836 repo._lfstatuswriters.pop()
836 repo._lfstatuswriters.pop()
837 repo._lfcommithooks.pop()
837 repo._lfcommithooks.pop()
838
838
839 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
839 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
840 prefix=None, mtime=None, subrepos=None):
840 prefix=None, mtime=None, subrepos=None):
841 # No need to lock because we are only reading history and
841 # No need to lock because we are only reading history and
842 # largefile caches, neither of which are modified.
842 # largefile caches, neither of which are modified.
843 lfcommands.cachelfiles(repo.ui, repo, node)
843 lfcommands.cachelfiles(repo.ui, repo, node)
844
844
845 if kind not in archival.archivers:
845 if kind not in archival.archivers:
846 raise util.Abort(_("unknown archive type '%s'") % kind)
846 raise util.Abort(_("unknown archive type '%s'") % kind)
847
847
848 ctx = repo[node]
848 ctx = repo[node]
849
849
850 if kind == 'files':
850 if kind == 'files':
851 if prefix:
851 if prefix:
852 raise util.Abort(
852 raise util.Abort(
853 _('cannot give prefix when archiving to files'))
853 _('cannot give prefix when archiving to files'))
854 else:
854 else:
855 prefix = archival.tidyprefix(dest, kind, prefix)
855 prefix = archival.tidyprefix(dest, kind, prefix)
856
856
857 def write(name, mode, islink, getdata):
857 def write(name, mode, islink, getdata):
858 if matchfn and not matchfn(name):
858 if matchfn and not matchfn(name):
859 return
859 return
860 data = getdata()
860 data = getdata()
861 if decode:
861 if decode:
862 data = repo.wwritedata(name, data)
862 data = repo.wwritedata(name, data)
863 archiver.addfile(prefix + name, mode, islink, data)
863 archiver.addfile(prefix + name, mode, islink, data)
864
864
865 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
865 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
866
866
867 if repo.ui.configbool("ui", "archivemeta", True):
867 if repo.ui.configbool("ui", "archivemeta", True):
868 def metadata():
868 def metadata():
869 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
869 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
870 hex(repo.changelog.node(0)), hex(node), ctx.branch())
870 hex(repo.changelog.node(0)), hex(node), ctx.branch())
871
871
872 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
872 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
873 if repo.tagtype(t) == 'global')
873 if repo.tagtype(t) == 'global')
874 if not tags:
874 if not tags:
875 repo.ui.pushbuffer()
875 repo.ui.pushbuffer()
876 opts = {'template': '{latesttag}\n{latesttagdistance}',
876 opts = {'template': '{latesttag}\n{latesttagdistance}',
877 'style': '', 'patch': None, 'git': None}
877 'style': '', 'patch': None, 'git': None}
878 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
878 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
879 ltags, dist = repo.ui.popbuffer().split('\n')
879 ltags, dist = repo.ui.popbuffer().split('\n')
880 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
880 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
881 tags += 'latesttagdistance: %s\n' % dist
881 tags += 'latesttagdistance: %s\n' % dist
882
882
883 return base + tags
883 return base + tags
884
884
885 write('.hg_archival.txt', 0644, False, metadata)
885 write('.hg_archival.txt', 0644, False, metadata)
886
886
887 for f in ctx:
887 for f in ctx:
888 ff = ctx.flags(f)
888 ff = ctx.flags(f)
889 getdata = ctx[f].data
889 getdata = ctx[f].data
890 if lfutil.isstandin(f):
890 if lfutil.isstandin(f):
891 path = lfutil.findfile(repo, getdata().strip())
891 path = lfutil.findfile(repo, getdata().strip())
892 if path is None:
892 if path is None:
893 raise util.Abort(
893 raise util.Abort(
894 _('largefile %s not found in repo store or system cache')
894 _('largefile %s not found in repo store or system cache')
895 % lfutil.splitstandin(f))
895 % lfutil.splitstandin(f))
896 f = lfutil.splitstandin(f)
896 f = lfutil.splitstandin(f)
897
897
898 def getdatafn():
898 def getdatafn():
899 fd = None
899 fd = None
900 try:
900 try:
901 fd = open(path, 'rb')
901 fd = open(path, 'rb')
902 return fd.read()
902 return fd.read()
903 finally:
903 finally:
904 if fd:
904 if fd:
905 fd.close()
905 fd.close()
906
906
907 getdata = getdatafn
907 getdata = getdatafn
908 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
908 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
909
909
910 if subrepos:
910 if subrepos:
911 for subpath in sorted(ctx.substate):
911 for subpath in sorted(ctx.substate):
912 sub = ctx.sub(subpath)
912 sub = ctx.sub(subpath)
913 submatch = match_.narrowmatcher(subpath, matchfn)
913 submatch = match_.narrowmatcher(subpath, matchfn)
914 sub.archive(archiver, prefix, submatch)
914 sub.archive(archiver, prefix, submatch)
915
915
916 archiver.done()
916 archiver.done()
917
917
918 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
918 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
919 repo._get(repo._state + ('hg',))
919 repo._get(repo._state + ('hg',))
920 rev = repo._state[1]
920 rev = repo._state[1]
921 ctx = repo._repo[rev]
921 ctx = repo._repo[rev]
922
922
923 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
923 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
924
924
925 def write(name, mode, islink, getdata):
925 def write(name, mode, islink, getdata):
926 # At this point, the standin has been replaced with the largefile name,
926 # At this point, the standin has been replaced with the largefile name,
927 # so the normal matcher works here without the lfutil variants.
927 # so the normal matcher works here without the lfutil variants.
928 if match and not match(f):
928 if match and not match(f):
929 return
929 return
930 data = getdata()
930 data = getdata()
931
931
932 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
932 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
933
933
934 for f in ctx:
934 for f in ctx:
935 ff = ctx.flags(f)
935 ff = ctx.flags(f)
936 getdata = ctx[f].data
936 getdata = ctx[f].data
937 if lfutil.isstandin(f):
937 if lfutil.isstandin(f):
938 path = lfutil.findfile(repo._repo, getdata().strip())
938 path = lfutil.findfile(repo._repo, getdata().strip())
939 if path is None:
939 if path is None:
940 raise util.Abort(
940 raise util.Abort(
941 _('largefile %s not found in repo store or system cache')
941 _('largefile %s not found in repo store or system cache')
942 % lfutil.splitstandin(f))
942 % lfutil.splitstandin(f))
943 f = lfutil.splitstandin(f)
943 f = lfutil.splitstandin(f)
944
944
945 def getdatafn():
945 def getdatafn():
946 fd = None
946 fd = None
947 try:
947 try:
948 fd = open(os.path.join(prefix, path), 'rb')
948 fd = open(os.path.join(prefix, path), 'rb')
949 return fd.read()
949 return fd.read()
950 finally:
950 finally:
951 if fd:
951 if fd:
952 fd.close()
952 fd.close()
953
953
954 getdata = getdatafn
954 getdata = getdatafn
955
955
956 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
956 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
957
957
958 for subpath in sorted(ctx.substate):
958 for subpath in sorted(ctx.substate):
959 sub = ctx.sub(subpath)
959 sub = ctx.sub(subpath)
960 submatch = match_.narrowmatcher(subpath, match)
960 submatch = match_.narrowmatcher(subpath, match)
961 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
961 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
962
962
963 # If a largefile is modified, the change is not reflected in its
963 # If a largefile is modified, the change is not reflected in its
964 # standin until a commit. cmdutil.bailifchanged() raises an exception
964 # standin until a commit. cmdutil.bailifchanged() raises an exception
965 # if the repo has uncommitted changes. Wrap it to also check if
965 # if the repo has uncommitted changes. Wrap it to also check if
966 # largefiles were changed. This is used by bisect, backout and fetch.
966 # largefiles were changed. This is used by bisect, backout and fetch.
967 def overridebailifchanged(orig, repo):
967 def overridebailifchanged(orig, repo):
968 orig(repo)
968 orig(repo)
969 repo.lfstatus = True
969 repo.lfstatus = True
970 s = repo.status()
970 s = repo.status()
971 repo.lfstatus = False
971 repo.lfstatus = False
972 if s.modified or s.added or s.removed or s.deleted:
972 if s.modified or s.added or s.removed or s.deleted:
973 raise util.Abort(_('uncommitted changes'))
973 raise util.Abort(_('uncommitted changes'))
974
974
975 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
975 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
976 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
976 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
977 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
977 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
978 m = composelargefilematcher(match, repo[None].manifest())
978 m = composelargefilematcher(match, repo[None].manifest())
979
979
980 try:
980 try:
981 repo.lfstatus = True
981 repo.lfstatus = True
982 s = repo.status(match=m, clean=True)
982 s = repo.status(match=m, clean=True)
983 finally:
983 finally:
984 repo.lfstatus = False
984 repo.lfstatus = False
985 forget = sorted(s.modified + s.added + s.deleted + s.clean)
985 forget = sorted(s.modified + s.added + s.deleted + s.clean)
986 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
986 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
987
987
988 for f in forget:
988 for f in forget:
989 if lfutil.standin(f) not in repo.dirstate and not \
989 if lfutil.standin(f) not in repo.dirstate and not \
990 repo.wvfs.isdir(lfutil.standin(f)):
990 repo.wvfs.isdir(lfutil.standin(f)):
991 ui.warn(_('not removing %s: file is already untracked\n')
991 ui.warn(_('not removing %s: file is already untracked\n')
992 % m.rel(f))
992 % m.rel(f))
993 bad.append(f)
993 bad.append(f)
994
994
995 for f in forget:
995 for f in forget:
996 if ui.verbose or not m.exact(f):
996 if ui.verbose or not m.exact(f):
997 ui.status(_('removing %s\n') % m.rel(f))
997 ui.status(_('removing %s\n') % m.rel(f))
998
998
999 # Need to lock because standin files are deleted then removed from the
999 # Need to lock because standin files are deleted then removed from the
1000 # repository and we could race in-between.
1000 # repository and we could race in-between.
1001 wlock = repo.wlock()
1001 wlock = repo.wlock()
1002 try:
1002 try:
1003 lfdirstate = lfutil.openlfdirstate(ui, repo)
1003 lfdirstate = lfutil.openlfdirstate(ui, repo)
1004 for f in forget:
1004 for f in forget:
1005 if lfdirstate[f] == 'a':
1005 if lfdirstate[f] == 'a':
1006 lfdirstate.drop(f)
1006 lfdirstate.drop(f)
1007 else:
1007 else:
1008 lfdirstate.remove(f)
1008 lfdirstate.remove(f)
1009 lfdirstate.write()
1009 lfdirstate.write()
1010 standins = [lfutil.standin(f) for f in forget]
1010 standins = [lfutil.standin(f) for f in forget]
1011 for f in standins:
1011 for f in standins:
1012 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1012 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1013 rejected = repo[None].forget(standins)
1013 rejected = repo[None].forget(standins)
1014 finally:
1014 finally:
1015 wlock.release()
1015 wlock.release()
1016
1016
1017 bad.extend(f for f in rejected if f in m.files())
1017 bad.extend(f for f in rejected if f in m.files())
1018 forgot.extend(f for f in forget if f not in rejected)
1018 forgot.extend(f for f in forget if f not in rejected)
1019 return bad, forgot
1019 return bad, forgot
1020
1020
1021 def _getoutgoings(repo, other, missing, addfunc):
1021 def _getoutgoings(repo, other, missing, addfunc):
1022 """get pairs of filename and largefile hash in outgoing revisions
1022 """get pairs of filename and largefile hash in outgoing revisions
1023 in 'missing'.
1023 in 'missing'.
1024
1024
1025 largefiles already existing on 'other' repository are ignored.
1025 largefiles already existing on 'other' repository are ignored.
1026
1026
1027 'addfunc' is invoked with each unique pairs of filename and
1027 'addfunc' is invoked with each unique pairs of filename and
1028 largefile hash value.
1028 largefile hash value.
1029 """
1029 """
1030 knowns = set()
1030 knowns = set()
1031 lfhashes = set()
1031 lfhashes = set()
1032 def dedup(fn, lfhash):
1032 def dedup(fn, lfhash):
1033 k = (fn, lfhash)
1033 k = (fn, lfhash)
1034 if k not in knowns:
1034 if k not in knowns:
1035 knowns.add(k)
1035 knowns.add(k)
1036 lfhashes.add(lfhash)
1036 lfhashes.add(lfhash)
1037 lfutil.getlfilestoupload(repo, missing, dedup)
1037 lfutil.getlfilestoupload(repo, missing, dedup)
1038 if lfhashes:
1038 if lfhashes:
1039 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1039 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1040 for fn, lfhash in knowns:
1040 for fn, lfhash in knowns:
1041 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1041 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1042 addfunc(fn, lfhash)
1042 addfunc(fn, lfhash)
1043
1043
1044 def outgoinghook(ui, repo, other, opts, missing):
1044 def outgoinghook(ui, repo, other, opts, missing):
1045 if opts.pop('large', None):
1045 if opts.pop('large', None):
1046 lfhashes = set()
1046 lfhashes = set()
1047 if ui.debugflag:
1047 if ui.debugflag:
1048 toupload = {}
1048 toupload = {}
1049 def addfunc(fn, lfhash):
1049 def addfunc(fn, lfhash):
1050 if fn not in toupload:
1050 if fn not in toupload:
1051 toupload[fn] = []
1051 toupload[fn] = []
1052 toupload[fn].append(lfhash)
1052 toupload[fn].append(lfhash)
1053 lfhashes.add(lfhash)
1053 lfhashes.add(lfhash)
1054 def showhashes(fn):
1054 def showhashes(fn):
1055 for lfhash in sorted(toupload[fn]):
1055 for lfhash in sorted(toupload[fn]):
1056 ui.debug(' %s\n' % (lfhash))
1056 ui.debug(' %s\n' % (lfhash))
1057 else:
1057 else:
1058 toupload = set()
1058 toupload = set()
1059 def addfunc(fn, lfhash):
1059 def addfunc(fn, lfhash):
1060 toupload.add(fn)
1060 toupload.add(fn)
1061 lfhashes.add(lfhash)
1061 lfhashes.add(lfhash)
1062 def showhashes(fn):
1062 def showhashes(fn):
1063 pass
1063 pass
1064 _getoutgoings(repo, other, missing, addfunc)
1064 _getoutgoings(repo, other, missing, addfunc)
1065
1065
1066 if not toupload:
1066 if not toupload:
1067 ui.status(_('largefiles: no files to upload\n'))
1067 ui.status(_('largefiles: no files to upload\n'))
1068 else:
1068 else:
1069 ui.status(_('largefiles to upload (%d entities):\n')
1069 ui.status(_('largefiles to upload (%d entities):\n')
1070 % (len(lfhashes)))
1070 % (len(lfhashes)))
1071 for file in sorted(toupload):
1071 for file in sorted(toupload):
1072 ui.status(lfutil.splitstandin(file) + '\n')
1072 ui.status(lfutil.splitstandin(file) + '\n')
1073 showhashes(file)
1073 showhashes(file)
1074 ui.status('\n')
1074 ui.status('\n')
1075
1075
1076 def summaryremotehook(ui, repo, opts, changes):
1076 def summaryremotehook(ui, repo, opts, changes):
1077 largeopt = opts.get('large', False)
1077 largeopt = opts.get('large', False)
1078 if changes is None:
1078 if changes is None:
1079 if largeopt:
1079 if largeopt:
1080 return (False, True) # only outgoing check is needed
1080 return (False, True) # only outgoing check is needed
1081 else:
1081 else:
1082 return (False, False)
1082 return (False, False)
1083 elif largeopt:
1083 elif largeopt:
1084 url, branch, peer, outgoing = changes[1]
1084 url, branch, peer, outgoing = changes[1]
1085 if peer is None:
1085 if peer is None:
1086 # i18n: column positioning for "hg summary"
1086 # i18n: column positioning for "hg summary"
1087 ui.status(_('largefiles: (no remote repo)\n'))
1087 ui.status(_('largefiles: (no remote repo)\n'))
1088 return
1088 return
1089
1089
1090 toupload = set()
1090 toupload = set()
1091 lfhashes = set()
1091 lfhashes = set()
1092 def addfunc(fn, lfhash):
1092 def addfunc(fn, lfhash):
1093 toupload.add(fn)
1093 toupload.add(fn)
1094 lfhashes.add(lfhash)
1094 lfhashes.add(lfhash)
1095 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1095 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1096
1096
1097 if not toupload:
1097 if not toupload:
1098 # i18n: column positioning for "hg summary"
1098 # i18n: column positioning for "hg summary"
1099 ui.status(_('largefiles: (no files to upload)\n'))
1099 ui.status(_('largefiles: (no files to upload)\n'))
1100 else:
1100 else:
1101 # i18n: column positioning for "hg summary"
1101 # i18n: column positioning for "hg summary"
1102 ui.status(_('largefiles: %d entities for %d files to upload\n')
1102 ui.status(_('largefiles: %d entities for %d files to upload\n')
1103 % (len(lfhashes), len(toupload)))
1103 % (len(lfhashes), len(toupload)))
1104
1104
1105 def overridesummary(orig, ui, repo, *pats, **opts):
1105 def overridesummary(orig, ui, repo, *pats, **opts):
1106 try:
1106 try:
1107 repo.lfstatus = True
1107 repo.lfstatus = True
1108 orig(ui, repo, *pats, **opts)
1108 orig(ui, repo, *pats, **opts)
1109 finally:
1109 finally:
1110 repo.lfstatus = False
1110 repo.lfstatus = False
1111
1111
1112 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1112 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1113 similarity=None):
1113 similarity=None):
1114 if not lfutil.islfilesrepo(repo):
1114 if not lfutil.islfilesrepo(repo):
1115 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1115 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1116 # Get the list of missing largefiles so we can remove them
1116 # Get the list of missing largefiles so we can remove them
1117 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1117 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1118 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1118 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1119 False, False, False)
1119 False, False, False)
1120
1120
1121 # Call into the normal remove code, but the removing of the standin, we want
1121 # Call into the normal remove code, but the removing of the standin, we want
1122 # to have handled by original addremove. Monkey patching here makes sure
1122 # to have handled by original addremove. Monkey patching here makes sure
1123 # we don't remove the standin in the largefiles code, preventing a very
1123 # we don't remove the standin in the largefiles code, preventing a very
1124 # confused state later.
1124 # confused state later.
1125 if s.deleted:
1125 if s.deleted:
1126 m = copy.copy(matcher)
1126 m = copy.copy(matcher)
1127
1127
1128 # The m._files and m._map attributes are not changed to the deleted list
1128 # The m._files and m._map attributes are not changed to the deleted list
1129 # because that affects the m.exact() test, which in turn governs whether
1129 # because that affects the m.exact() test, which in turn governs whether
1130 # or not the file name is printed, and how. Simply limit the original
1130 # or not the file name is printed, and how. Simply limit the original
1131 # matches to those in the deleted status list.
1131 # matches to those in the deleted status list.
1132 matchfn = m.matchfn
1132 matchfn = m.matchfn
1133 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1133 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1134
1134
1135 removelargefiles(repo.ui, repo, True, m, **opts)
1135 removelargefiles(repo.ui, repo, True, m, **opts)
1136 # Call into the normal add code, and any files that *should* be added as
1136 # Call into the normal add code, and any files that *should* be added as
1137 # largefiles will be
1137 # largefiles will be
1138 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1138 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1139 # Now that we've handled largefiles, hand off to the original addremove
1139 # Now that we've handled largefiles, hand off to the original addremove
1140 # function to take care of the rest. Make sure it doesn't do anything with
1140 # function to take care of the rest. Make sure it doesn't do anything with
1141 # largefiles by passing a matcher that will ignore them.
1141 # largefiles by passing a matcher that will ignore them.
1142 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1142 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1143 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1143 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1144
1144
1145 # Calling purge with --all will cause the largefiles to be deleted.
1145 # Calling purge with --all will cause the largefiles to be deleted.
1146 # Override repo.status to prevent this from happening.
1146 # Override repo.status to prevent this from happening.
1147 def overridepurge(orig, ui, repo, *dirs, **opts):
1147 def overridepurge(orig, ui, repo, *dirs, **opts):
1148 # XXX Monkey patching a repoview will not work. The assigned attribute will
1148 # XXX Monkey patching a repoview will not work. The assigned attribute will
1149 # be set on the unfiltered repo, but we will only lookup attributes in the
1149 # be set on the unfiltered repo, but we will only lookup attributes in the
1150 # unfiltered repo if the lookup in the repoview object itself fails. As the
1150 # unfiltered repo if the lookup in the repoview object itself fails. As the
1151 # monkey patched method exists on the repoview class the lookup will not
1151 # monkey patched method exists on the repoview class the lookup will not
1152 # fail. As a result, the original version will shadow the monkey patched
1152 # fail. As a result, the original version will shadow the monkey patched
1153 # one, defeating the monkey patch.
1153 # one, defeating the monkey patch.
1154 #
1154 #
1155 # As a work around we use an unfiltered repo here. We should do something
1155 # As a work around we use an unfiltered repo here. We should do something
1156 # cleaner instead.
1156 # cleaner instead.
1157 repo = repo.unfiltered()
1157 repo = repo.unfiltered()
1158 oldstatus = repo.status
1158 oldstatus = repo.status
1159 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1159 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1160 clean=False, unknown=False, listsubrepos=False):
1160 clean=False, unknown=False, listsubrepos=False):
1161 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1161 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1162 listsubrepos)
1162 listsubrepos)
1163 lfdirstate = lfutil.openlfdirstate(ui, repo)
1163 lfdirstate = lfutil.openlfdirstate(ui, repo)
1164 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1164 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1165 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1165 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1166 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1166 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1167 unknown, ignored, r.clean)
1167 unknown, ignored, r.clean)
1168 repo.status = overridestatus
1168 repo.status = overridestatus
1169 orig(ui, repo, *dirs, **opts)
1169 orig(ui, repo, *dirs, **opts)
1170 repo.status = oldstatus
1170 repo.status = oldstatus
1171 def overriderollback(orig, ui, repo, **opts):
1171 def overriderollback(orig, ui, repo, **opts):
1172 wlock = repo.wlock()
1172 wlock = repo.wlock()
1173 try:
1173 try:
1174 before = repo.dirstate.parents()
1174 before = repo.dirstate.parents()
1175 orphans = set(f for f in repo.dirstate
1175 orphans = set(f for f in repo.dirstate
1176 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1176 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1177 result = orig(ui, repo, **opts)
1177 result = orig(ui, repo, **opts)
1178 after = repo.dirstate.parents()
1178 after = repo.dirstate.parents()
1179 if before == after:
1179 if before == after:
1180 return result # no need to restore standins
1180 return result # no need to restore standins
1181
1181
1182 pctx = repo['.']
1182 pctx = repo['.']
1183 for f in repo.dirstate:
1183 for f in repo.dirstate:
1184 if lfutil.isstandin(f):
1184 if lfutil.isstandin(f):
1185 orphans.discard(f)
1185 orphans.discard(f)
1186 if repo.dirstate[f] == 'r':
1186 if repo.dirstate[f] == 'r':
1187 repo.wvfs.unlinkpath(f, ignoremissing=True)
1187 repo.wvfs.unlinkpath(f, ignoremissing=True)
1188 elif f in pctx:
1188 elif f in pctx:
1189 fctx = pctx[f]
1189 fctx = pctx[f]
1190 repo.wwrite(f, fctx.data(), fctx.flags())
1190 repo.wwrite(f, fctx.data(), fctx.flags())
1191 else:
1191 else:
1192 # content of standin is not so important in 'a',
1192 # content of standin is not so important in 'a',
1193 # 'm' or 'n' (coming from the 2nd parent) cases
1193 # 'm' or 'n' (coming from the 2nd parent) cases
1194 lfutil.writestandin(repo, f, '', False)
1194 lfutil.writestandin(repo, f, '', False)
1195 for standin in orphans:
1195 for standin in orphans:
1196 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1196 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1197
1197
1198 lfdirstate = lfutil.openlfdirstate(ui, repo)
1198 lfdirstate = lfutil.openlfdirstate(ui, repo)
1199 orphans = set(lfdirstate)
1199 orphans = set(lfdirstate)
1200 lfiles = lfutil.listlfiles(repo)
1200 lfiles = lfutil.listlfiles(repo)
1201 for file in lfiles:
1201 for file in lfiles:
1202 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1202 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1203 orphans.discard(file)
1203 orphans.discard(file)
1204 for lfile in orphans:
1204 for lfile in orphans:
1205 lfdirstate.drop(lfile)
1205 lfdirstate.drop(lfile)
1206 lfdirstate.write()
1206 lfdirstate.write()
1207 finally:
1207 finally:
1208 wlock.release()
1208 wlock.release()
1209 return result
1209 return result
1210
1210
1211 def overridetransplant(orig, ui, repo, *revs, **opts):
1211 def overridetransplant(orig, ui, repo, *revs, **opts):
1212 resuming = opts.get('continue')
1212 resuming = opts.get('continue')
1213 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1213 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1214 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1214 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1215 try:
1215 try:
1216 result = orig(ui, repo, *revs, **opts)
1216 result = orig(ui, repo, *revs, **opts)
1217 finally:
1217 finally:
1218 repo._lfstatuswriters.pop()
1218 repo._lfstatuswriters.pop()
1219 repo._lfcommithooks.pop()
1219 repo._lfcommithooks.pop()
1220 return result
1220 return result
1221
1221
1222 def overridecat(orig, ui, repo, file1, *pats, **opts):
1222 def overridecat(orig, ui, repo, file1, *pats, **opts):
1223 ctx = scmutil.revsingle(repo, opts.get('rev'))
1223 ctx = scmutil.revsingle(repo, opts.get('rev'))
1224 err = 1
1224 err = 1
1225 notbad = set()
1225 notbad = set()
1226 m = scmutil.match(ctx, (file1,) + pats, opts)
1226 m = scmutil.match(ctx, (file1,) + pats, opts)
1227 origmatchfn = m.matchfn
1227 origmatchfn = m.matchfn
1228 def lfmatchfn(f):
1228 def lfmatchfn(f):
1229 if origmatchfn(f):
1229 if origmatchfn(f):
1230 return True
1230 return True
1231 lf = lfutil.splitstandin(f)
1231 lf = lfutil.splitstandin(f)
1232 if lf is None:
1232 if lf is None:
1233 return False
1233 return False
1234 notbad.add(lf)
1234 notbad.add(lf)
1235 return origmatchfn(lf)
1235 return origmatchfn(lf)
1236 m.matchfn = lfmatchfn
1236 m.matchfn = lfmatchfn
1237 origbadfn = m.bad
1237 origbadfn = m.bad
1238 def lfbadfn(f, msg):
1238 def lfbadfn(f, msg):
1239 if not f in notbad:
1239 if not f in notbad:
1240 origbadfn(f, msg)
1240 origbadfn(f, msg)
1241 m.bad = lfbadfn
1241 m.bad = lfbadfn
1242 for f in ctx.walk(m):
1242 for f in ctx.walk(m):
1243 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1243 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1244 pathname=f)
1244 pathname=f)
1245 lf = lfutil.splitstandin(f)
1245 lf = lfutil.splitstandin(f)
1246 if lf is None or origmatchfn(f):
1246 if lf is None or origmatchfn(f):
1247 # duplicating unreachable code from commands.cat
1247 # duplicating unreachable code from commands.cat
1248 data = ctx[f].data()
1248 data = ctx[f].data()
1249 if opts.get('decode'):
1249 if opts.get('decode'):
1250 data = repo.wwritedata(f, data)
1250 data = repo.wwritedata(f, data)
1251 fp.write(data)
1251 fp.write(data)
1252 else:
1252 else:
1253 hash = lfutil.readstandin(repo, lf, ctx.rev())
1253 hash = lfutil.readstandin(repo, lf, ctx.rev())
1254 if not lfutil.inusercache(repo.ui, hash):
1254 if not lfutil.inusercache(repo.ui, hash):
1255 store = basestore._openstore(repo)
1255 store = basestore._openstore(repo)
1256 success, missing = store.get([(lf, hash)])
1256 success, missing = store.get([(lf, hash)])
1257 if len(success) != 1:
1257 if len(success) != 1:
1258 raise util.Abort(
1258 raise util.Abort(
1259 _('largefile %s is not in cache and could not be '
1259 _('largefile %s is not in cache and could not be '
1260 'downloaded') % lf)
1260 'downloaded') % lf)
1261 path = lfutil.usercachepath(repo.ui, hash)
1261 path = lfutil.usercachepath(repo.ui, hash)
1262 fpin = open(path, "rb")
1262 fpin = open(path, "rb")
1263 for chunk in util.filechunkiter(fpin, 128 * 1024):
1263 for chunk in util.filechunkiter(fpin, 128 * 1024):
1264 fp.write(chunk)
1264 fp.write(chunk)
1265 fpin.close()
1265 fpin.close()
1266 fp.close()
1266 fp.close()
1267 err = 0
1267 err = 0
1268 return err
1268 return err
1269
1269
1270 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1270 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1271 *args, **kwargs):
1271 *args, **kwargs):
1272 wlock = repo.wlock()
1272 wlock = repo.wlock()
1273 try:
1273 try:
1274 # branch | | |
1274 # branch | | |
1275 # merge | force | partial | action
1275 # merge | force | partial | action
1276 # -------+-------+---------+--------------
1276 # -------+-------+---------+--------------
1277 # x | x | x | linear-merge
1277 # x | x | x | linear-merge
1278 # o | x | x | branch-merge
1278 # o | x | x | branch-merge
1279 # x | o | x | overwrite (as clean update)
1279 # x | o | x | overwrite (as clean update)
1280 # o | o | x | force-branch-merge (*1)
1280 # o | o | x | force-branch-merge (*1)
1281 # x | x | o | (*)
1281 # x | x | o | (*)
1282 # o | x | o | (*)
1282 # o | x | o | (*)
1283 # x | o | o | overwrite (as revert)
1283 # x | o | o | overwrite (as revert)
1284 # o | o | o | (*)
1284 # o | o | o | (*)
1285 #
1285 #
1286 # (*) don't care
1286 # (*) don't care
1287 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1287 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1288
1288
1289 linearmerge = not branchmerge and not force and not partial
1289 linearmerge = not branchmerge and not force and not partial
1290
1290
1291 if linearmerge or (branchmerge and force and not partial):
1291 if linearmerge or (branchmerge and force and not partial):
1292 # update standins for linear-merge or force-branch-merge,
1292 # update standins for linear-merge or force-branch-merge,
1293 # because largefiles in the working directory may be modified
1293 # because largefiles in the working directory may be modified
1294 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1294 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1295 unsure, s = lfdirstate.status(match_.always(repo.root,
1295 unsure, s = lfdirstate.status(match_.always(repo.root,
1296 repo.getcwd()),
1296 repo.getcwd()),
1297 [], False, False, False)
1297 [], False, False, False)
1298 for lfile in unsure + s.modified + s.added:
1298 pctx = repo['.']
1299 for lfile in unsure + s.modified:
1300 lfileabs = repo.wvfs.join(lfile)
1301 if not os.path.exists(lfileabs):
1302 continue
1303 lfhash = lfutil.hashrepofile(repo, lfile)
1304 standin = lfutil.standin(lfile)
1305 lfutil.writestandin(repo, standin, lfhash,
1306 lfutil.getexecutable(lfileabs))
1307 if (standin in pctx and
1308 lfhash == lfutil.readstandin(repo, lfile, '.')):
1309 lfdirstate.normal(lfile)
1310 for lfile in s.added:
1299 lfutil.updatestandin(repo, lfutil.standin(lfile))
1311 lfutil.updatestandin(repo, lfutil.standin(lfile))
1312 lfdirstate.write()
1300
1313
1301 if linearmerge:
1314 if linearmerge:
1302 # Only call updatelfiles on the standins that have changed
1315 # Only call updatelfiles on the standins that have changed
1303 # to save time
1316 # to save time
1304 oldstandins = lfutil.getstandinsstate(repo)
1317 oldstandins = lfutil.getstandinsstate(repo)
1305
1318
1306 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1319 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1307
1320
1308 filelist = None
1321 filelist = None
1309 if linearmerge:
1322 if linearmerge:
1310 newstandins = lfutil.getstandinsstate(repo)
1323 newstandins = lfutil.getstandinsstate(repo)
1311 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1324 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1312
1325
1313 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1326 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1314 normallookup=partial)
1327 normallookup=partial)
1315
1328
1316 return result
1329 return result
1317 finally:
1330 finally:
1318 wlock.release()
1331 wlock.release()
1319
1332
1320 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1333 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1321 result = orig(repo, files, *args, **kwargs)
1334 result = orig(repo, files, *args, **kwargs)
1322
1335
1323 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1336 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1324 if filelist:
1337 if filelist:
1325 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1338 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1326 printmessage=False, normallookup=True)
1339 printmessage=False, normallookup=True)
1327
1340
1328 return result
1341 return result
@@ -1,661 +1,694 b''
1 This file focuses mainly on updating largefiles in the working
1 This file focuses mainly on updating largefiles in the working
2 directory (and ".hg/largefiles/dirstate")
2 directory (and ".hg/largefiles/dirstate")
3
3
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [ui]
5 > [ui]
6 > merge = internal:fail
6 > merge = internal:fail
7 > [extensions]
7 > [extensions]
8 > largefiles =
8 > largefiles =
9 > EOF
9 > EOF
10
10
11 $ hg init repo
11 $ hg init repo
12 $ cd repo
12 $ cd repo
13
13
14 $ echo large1 > large1
14 $ echo large1 > large1
15 $ echo large2 > large2
15 $ echo large2 > large2
16 $ hg add --large large1 large2
16 $ hg add --large large1 large2
17 $ echo normal1 > normal1
17 $ echo normal1 > normal1
18 $ hg add normal1
18 $ hg add normal1
19 $ hg commit -m '#0'
19 $ hg commit -m '#0'
20 $ echo 'large1 in #1' > large1
20 $ echo 'large1 in #1' > large1
21 $ echo 'normal1 in #1' > normal1
21 $ echo 'normal1 in #1' > normal1
22 $ hg commit -m '#1'
22 $ hg commit -m '#1'
23 $ hg update -q -C 0
23 $ hg update -q -C 0
24 $ echo 'large2 in #2' > large2
24 $ echo 'large2 in #2' > large2
25 $ hg commit -m '#2'
25 $ hg commit -m '#2'
26 created new head
26 created new head
27
27
28 Test that update also updates the lfdirstate of 'unsure' largefiles after
29 hashing them:
30
31 The previous operations will usually have left us with largefiles with a mtime
32 within the same second as the dirstate was written.
33 The lfdirstate entries will thus have been written with an invalidated/unset
34 mtime to make sure further changes within the same second is detected.
35 We will however occasionally be "lucky" and get a tick between writing
36 largefiles and writing dirstate so we get valid lfdirstate timestamps. The
37 following verification is thus disabled but can be verified manually.
38
39 #if false
40 $ hg debugdirstate --large --nodate
41 n 644 7 unset large1
42 n 644 13 unset large2
43 #endif
44
45 Wait to make sure we get a tick so the mtime of the largefiles become valid.
46
47 $ sleep 1
48
49 A linear merge will update standins before performing the actual merge. It will
50 do a lfdirstate status walk and find 'unset'/'unsure' files, hash them, and
51 update the corresponding standins.
52 Verify that it actually marks the clean files as clean in lfdirstate so
53 we don't have to hash them again next time we update.
54
55 $ hg up
56 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 $ hg debugdirstate --large --nodate
58 n 644 7 set large1
59 n 644 13 set large2
60
28 Test that lfdirstate keeps track of last modification of largefiles and
61 Test that lfdirstate keeps track of last modification of largefiles and
29 prevents unnecessary hashing of content - also after linear/noop update
62 prevents unnecessary hashing of content - also after linear/noop update
30
63
31 $ sleep 1
64 $ sleep 1
32 $ hg st
65 $ hg st
33 $ hg debugdirstate --large --nodate
66 $ hg debugdirstate --large --nodate
34 n 644 7 set large1
67 n 644 7 set large1
35 n 644 13 set large2
68 n 644 13 set large2
36 $ hg up
69 $ hg up
37 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 $ hg debugdirstate --large --nodate
71 $ hg debugdirstate --large --nodate
39 n 644 7 set large1
72 n 644 7 set large1
40 n 644 13 set large2
73 n 644 13 set large2
41
74
42 Test that "hg merge" updates largefiles from "other" correctly
75 Test that "hg merge" updates largefiles from "other" correctly
43
76
44 (getting largefiles from "other" normally)
77 (getting largefiles from "other" normally)
45
78
46 $ hg status -A large1
79 $ hg status -A large1
47 C large1
80 C large1
48 $ cat large1
81 $ cat large1
49 large1
82 large1
50 $ cat .hglf/large1
83 $ cat .hglf/large1
51 4669e532d5b2c093a78eca010077e708a071bb64
84 4669e532d5b2c093a78eca010077e708a071bb64
52 $ hg merge --config debug.dirstate.delaywrite=2
85 $ hg merge --config debug.dirstate.delaywrite=2
53 getting changed largefiles
86 getting changed largefiles
54 1 largefiles updated, 0 removed
87 1 largefiles updated, 0 removed
55 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 (branch merge, don't forget to commit)
89 (branch merge, don't forget to commit)
57 $ hg status -A large1
90 $ hg status -A large1
58 M large1
91 M large1
59 $ cat large1
92 $ cat large1
60 large1 in #1
93 large1 in #1
61 $ cat .hglf/large1
94 $ cat .hglf/large1
62 58e24f733a964da346e2407a2bee99d9001184f5
95 58e24f733a964da346e2407a2bee99d9001184f5
63 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
96 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
64 -4669e532d5b2c093a78eca010077e708a071bb64
97 -4669e532d5b2c093a78eca010077e708a071bb64
65 +58e24f733a964da346e2407a2bee99d9001184f5
98 +58e24f733a964da346e2407a2bee99d9001184f5
66
99
67 (getting largefiles from "other" via conflict prompt)
100 (getting largefiles from "other" via conflict prompt)
68
101
69 $ hg update -q -C 2
102 $ hg update -q -C 2
70 $ echo 'large1 in #3' > large1
103 $ echo 'large1 in #3' > large1
71 $ echo 'normal1 in #3' > normal1
104 $ echo 'normal1 in #3' > normal1
72 $ hg commit -m '#3'
105 $ hg commit -m '#3'
73 $ cat .hglf/large1
106 $ cat .hglf/large1
74 e5bb990443d6a92aaf7223813720f7566c9dd05b
107 e5bb990443d6a92aaf7223813720f7566c9dd05b
75 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
108 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
76 > o
109 > o
77 > EOF
110 > EOF
78 largefile large1 has a merge conflict
111 largefile large1 has a merge conflict
79 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
112 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
80 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
113 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
81 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
114 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
82 merging normal1
115 merging normal1
83 warning: conflicts during merge.
116 warning: conflicts during merge.
84 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
117 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
85 getting changed largefiles
118 getting changed largefiles
86 1 largefiles updated, 0 removed
119 1 largefiles updated, 0 removed
87 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
120 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
88 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
121 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
89 [1]
122 [1]
90 $ hg status -A large1
123 $ hg status -A large1
91 M large1
124 M large1
92 $ cat large1
125 $ cat large1
93 large1 in #1
126 large1 in #1
94 $ cat .hglf/large1
127 $ cat .hglf/large1
95 58e24f733a964da346e2407a2bee99d9001184f5
128 58e24f733a964da346e2407a2bee99d9001184f5
96
129
97 Test that "hg revert -r REV" updates largefiles from "REV" correctly
130 Test that "hg revert -r REV" updates largefiles from "REV" correctly
98
131
99 $ hg update -q -C 3
132 $ hg update -q -C 3
100 $ hg status -A large1
133 $ hg status -A large1
101 C large1
134 C large1
102 $ cat large1
135 $ cat large1
103 large1 in #3
136 large1 in #3
104 $ cat .hglf/large1
137 $ cat .hglf/large1
105 e5bb990443d6a92aaf7223813720f7566c9dd05b
138 e5bb990443d6a92aaf7223813720f7566c9dd05b
106 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
139 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
107 -4669e532d5b2c093a78eca010077e708a071bb64
140 -4669e532d5b2c093a78eca010077e708a071bb64
108 +58e24f733a964da346e2407a2bee99d9001184f5
141 +58e24f733a964da346e2407a2bee99d9001184f5
109 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
142 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
110 $ hg status -A large1
143 $ hg status -A large1
111 M large1
144 M large1
112 $ cat large1
145 $ cat large1
113 large1 in #1
146 large1 in #1
114 $ cat .hglf/large1
147 $ cat .hglf/large1
115 58e24f733a964da346e2407a2bee99d9001184f5
148 58e24f733a964da346e2407a2bee99d9001184f5
116
149
117 Test that "hg rollback" restores status of largefiles correctly
150 Test that "hg rollback" restores status of largefiles correctly
118
151
119 $ hg update -C -q
152 $ hg update -C -q
120 $ hg remove large1
153 $ hg remove large1
121 $ test -f .hglf/large1
154 $ test -f .hglf/large1
122 [1]
155 [1]
123 $ hg forget large2
156 $ hg forget large2
124 $ test -f .hglf/large2
157 $ test -f .hglf/large2
125 [1]
158 [1]
126 $ echo largeX > largeX
159 $ echo largeX > largeX
127 $ hg add --large largeX
160 $ hg add --large largeX
128 $ cat .hglf/largeX
161 $ cat .hglf/largeX
129
162
130 $ hg commit -m 'will be rollback-ed soon'
163 $ hg commit -m 'will be rollback-ed soon'
131 $ echo largeY > largeY
164 $ echo largeY > largeY
132 $ hg add --large largeY
165 $ hg add --large largeY
133 #if windows
166 #if windows
134 $ hg status -A large1
167 $ hg status -A large1
135 large1: * (glob)
168 large1: * (glob)
136 #else
169 #else
137 $ hg status -A large1
170 $ hg status -A large1
138 large1: No such file or directory
171 large1: No such file or directory
139 #endif
172 #endif
140 $ hg status -A large2
173 $ hg status -A large2
141 ? large2
174 ? large2
142 $ hg status -A largeX
175 $ hg status -A largeX
143 C largeX
176 C largeX
144 $ hg status -A largeY
177 $ hg status -A largeY
145 A largeY
178 A largeY
146 $ hg rollback
179 $ hg rollback
147 repository tip rolled back to revision 3 (undo commit)
180 repository tip rolled back to revision 3 (undo commit)
148 working directory now based on revision 3
181 working directory now based on revision 3
149 $ hg status -A large1
182 $ hg status -A large1
150 R large1
183 R large1
151 $ test -f .hglf/large1
184 $ test -f .hglf/large1
152 [1]
185 [1]
153 $ hg status -A large2
186 $ hg status -A large2
154 R large2
187 R large2
155 $ test -f .hglf/large2
188 $ test -f .hglf/large2
156 [1]
189 [1]
157 $ hg status -A largeX
190 $ hg status -A largeX
158 A largeX
191 A largeX
159 $ cat .hglf/largeX
192 $ cat .hglf/largeX
160
193
161 $ hg status -A largeY
194 $ hg status -A largeY
162 ? largeY
195 ? largeY
163 $ test -f .hglf/largeY
196 $ test -f .hglf/largeY
164 [1]
197 [1]
165
198
166 Test that "hg rollback" restores standins correctly
199 Test that "hg rollback" restores standins correctly
167
200
168 $ hg commit -m 'will be rollback-ed soon'
201 $ hg commit -m 'will be rollback-ed soon'
169 $ hg update -q -C 2
202 $ hg update -q -C 2
170 $ cat large1
203 $ cat large1
171 large1
204 large1
172 $ cat .hglf/large1
205 $ cat .hglf/large1
173 4669e532d5b2c093a78eca010077e708a071bb64
206 4669e532d5b2c093a78eca010077e708a071bb64
174 $ cat large2
207 $ cat large2
175 large2 in #2
208 large2 in #2
176 $ cat .hglf/large2
209 $ cat .hglf/large2
177 3cfce6277e7668985707b6887ce56f9f62f6ccd9
210 3cfce6277e7668985707b6887ce56f9f62f6ccd9
178
211
179 $ hg rollback -q -f
212 $ hg rollback -q -f
180 $ cat large1
213 $ cat large1
181 large1
214 large1
182 $ cat .hglf/large1
215 $ cat .hglf/large1
183 4669e532d5b2c093a78eca010077e708a071bb64
216 4669e532d5b2c093a78eca010077e708a071bb64
184 $ cat large2
217 $ cat large2
185 large2 in #2
218 large2 in #2
186 $ cat .hglf/large2
219 $ cat .hglf/large2
187 3cfce6277e7668985707b6887ce56f9f62f6ccd9
220 3cfce6277e7668985707b6887ce56f9f62f6ccd9
188
221
189 (rollback the parent of the working directory, when the parent of it
222 (rollback the parent of the working directory, when the parent of it
190 is not branch-tip)
223 is not branch-tip)
191
224
192 $ hg update -q -C 1
225 $ hg update -q -C 1
193 $ cat .hglf/large1
226 $ cat .hglf/large1
194 58e24f733a964da346e2407a2bee99d9001184f5
227 58e24f733a964da346e2407a2bee99d9001184f5
195 $ cat .hglf/large2
228 $ cat .hglf/large2
196 1deebade43c8c498a3c8daddac0244dc55d1331d
229 1deebade43c8c498a3c8daddac0244dc55d1331d
197
230
198 $ echo normalX > normalX
231 $ echo normalX > normalX
199 $ hg add normalX
232 $ hg add normalX
200 $ hg commit -m 'will be rollback-ed soon'
233 $ hg commit -m 'will be rollback-ed soon'
201 $ hg rollback -q
234 $ hg rollback -q
202
235
203 $ cat .hglf/large1
236 $ cat .hglf/large1
204 58e24f733a964da346e2407a2bee99d9001184f5
237 58e24f733a964da346e2407a2bee99d9001184f5
205 $ cat .hglf/large2
238 $ cat .hglf/large2
206 1deebade43c8c498a3c8daddac0244dc55d1331d
239 1deebade43c8c498a3c8daddac0244dc55d1331d
207
240
208 Test that "hg status" shows status of largefiles correctly just after
241 Test that "hg status" shows status of largefiles correctly just after
209 automated commit like rebase/transplant
242 automated commit like rebase/transplant
210
243
211 $ cat >> .hg/hgrc <<EOF
244 $ cat >> .hg/hgrc <<EOF
212 > [extensions]
245 > [extensions]
213 > rebase =
246 > rebase =
214 > strip =
247 > strip =
215 > transplant =
248 > transplant =
216 > EOF
249 > EOF
217 $ hg update -q -C 1
250 $ hg update -q -C 1
218 $ hg remove large1
251 $ hg remove large1
219 $ echo largeX > largeX
252 $ echo largeX > largeX
220 $ hg add --large largeX
253 $ hg add --large largeX
221 $ hg commit -m '#4'
254 $ hg commit -m '#4'
222
255
223 $ hg rebase -s 1 -d 2 --keep
256 $ hg rebase -s 1 -d 2 --keep
224 rebasing 1:72518492caa6 "#1"
257 rebasing 1:72518492caa6 "#1"
225 rebasing 4:07d6153b5c04 "#4" (tip)
258 rebasing 4:07d6153b5c04 "#4" (tip)
226 #if windows
259 #if windows
227 $ hg status -A large1
260 $ hg status -A large1
228 large1: * (glob)
261 large1: * (glob)
229 #else
262 #else
230 $ hg status -A large1
263 $ hg status -A large1
231 large1: No such file or directory
264 large1: No such file or directory
232 #endif
265 #endif
233 $ hg status -A largeX
266 $ hg status -A largeX
234 C largeX
267 C largeX
235 $ hg strip -q 5
268 $ hg strip -q 5
236
269
237 $ hg update -q -C 2
270 $ hg update -q -C 2
238 $ hg transplant -q 1 4
271 $ hg transplant -q 1 4
239 #if windows
272 #if windows
240 $ hg status -A large1
273 $ hg status -A large1
241 large1: * (glob)
274 large1: * (glob)
242 #else
275 #else
243 $ hg status -A large1
276 $ hg status -A large1
244 large1: No such file or directory
277 large1: No such file or directory
245 #endif
278 #endif
246 $ hg status -A largeX
279 $ hg status -A largeX
247 C largeX
280 C largeX
248 $ hg strip -q 5
281 $ hg strip -q 5
249
282
250 $ hg update -q -C 2
283 $ hg update -q -C 2
251 $ hg transplant -q --merge 1 --merge 4
284 $ hg transplant -q --merge 1 --merge 4
252 #if windows
285 #if windows
253 $ hg status -A large1
286 $ hg status -A large1
254 large1: * (glob)
287 large1: * (glob)
255 #else
288 #else
256 $ hg status -A large1
289 $ hg status -A large1
257 large1: No such file or directory
290 large1: No such file or directory
258 #endif
291 #endif
259 $ hg status -A largeX
292 $ hg status -A largeX
260 C largeX
293 C largeX
261 $ hg strip -q 5
294 $ hg strip -q 5
262
295
263 Test that linear merge can detect modification (and conflict) correctly
296 Test that linear merge can detect modification (and conflict) correctly
264
297
265 (linear merge without conflict)
298 (linear merge without conflict)
266
299
267 $ echo 'large2 for linear merge (no conflict)' > large2
300 $ echo 'large2 for linear merge (no conflict)' > large2
268 $ hg update 3 --config debug.dirstate.delaywrite=2
301 $ hg update 3 --config debug.dirstate.delaywrite=2
269 getting changed largefiles
302 getting changed largefiles
270 1 largefiles updated, 0 removed
303 1 largefiles updated, 0 removed
271 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 $ hg status -A large2
305 $ hg status -A large2
273 M large2
306 M large2
274 $ cat large2
307 $ cat large2
275 large2 for linear merge (no conflict)
308 large2 for linear merge (no conflict)
276 $ cat .hglf/large2
309 $ cat .hglf/large2
277 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
310 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
278
311
279 (linear merge with conflict, choosing "other")
312 (linear merge with conflict, choosing "other")
280
313
281 $ hg update -q -C 2
314 $ hg update -q -C 2
282 $ echo 'large1 for linear merge (conflict)' > large1
315 $ echo 'large1 for linear merge (conflict)' > large1
283 $ hg update 3 --config ui.interactive=True <<EOF
316 $ hg update 3 --config ui.interactive=True <<EOF
284 > o
317 > o
285 > EOF
318 > EOF
286 largefile large1 has a merge conflict
319 largefile large1 has a merge conflict
287 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
320 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
288 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
321 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
289 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
322 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
290 getting changed largefiles
323 getting changed largefiles
291 1 largefiles updated, 0 removed
324 1 largefiles updated, 0 removed
292 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
325 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
293 $ hg status -A large1
326 $ hg status -A large1
294 C large1
327 C large1
295 $ cat large1
328 $ cat large1
296 large1 in #3
329 large1 in #3
297 $ cat .hglf/large1
330 $ cat .hglf/large1
298 e5bb990443d6a92aaf7223813720f7566c9dd05b
331 e5bb990443d6a92aaf7223813720f7566c9dd05b
299
332
300 (linear merge with conflict, choosing "local")
333 (linear merge with conflict, choosing "local")
301
334
302 $ hg update -q -C 2
335 $ hg update -q -C 2
303 $ echo 'large1 for linear merge (conflict)' > large1
336 $ echo 'large1 for linear merge (conflict)' > large1
304 $ hg update 3 --config debug.dirstate.delaywrite=2
337 $ hg update 3 --config debug.dirstate.delaywrite=2
305 largefile large1 has a merge conflict
338 largefile large1 has a merge conflict
306 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
339 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
307 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
340 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
308 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
341 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
309 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
342 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
310 $ hg status -A large1
343 $ hg status -A large1
311 M large1
344 M large1
312 $ cat large1
345 $ cat large1
313 large1 for linear merge (conflict)
346 large1 for linear merge (conflict)
314 $ cat .hglf/large1
347 $ cat .hglf/large1
315 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
348 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
316
349
317 Test a linear merge to a revision containing same-name normal file
350 Test a linear merge to a revision containing same-name normal file
318
351
319 $ hg update -q -C 3
352 $ hg update -q -C 3
320 $ hg remove large2
353 $ hg remove large2
321 $ echo 'large2 as normal file' > large2
354 $ echo 'large2 as normal file' > large2
322 $ hg add large2
355 $ hg add large2
323 $ echo 'large3 as normal file' > large3
356 $ echo 'large3 as normal file' > large3
324 $ hg add large3
357 $ hg add large3
325 $ hg commit -m '#5'
358 $ hg commit -m '#5'
326 $ hg manifest
359 $ hg manifest
327 .hglf/large1
360 .hglf/large1
328 large2
361 large2
329 large3
362 large3
330 normal1
363 normal1
331
364
332 (modified largefile is already switched to normal)
365 (modified largefile is already switched to normal)
333
366
334 $ hg update -q -C 2
367 $ hg update -q -C 2
335 $ echo 'modified large2 for linear merge' > large2
368 $ echo 'modified large2 for linear merge' > large2
336 $ hg update -q 5
369 $ hg update -q 5
337 remote turned local largefile large2 into a normal file
370 remote turned local largefile large2 into a normal file
338 keep (l)argefile or use (n)ormal file? l
371 keep (l)argefile or use (n)ormal file? l
339 $ hg debugdirstate --nodates | grep large2
372 $ hg debugdirstate --nodates | grep large2
340 a 0 -1 unset .hglf/large2
373 a 0 -1 unset .hglf/large2
341 r 0 0 set large2
374 r 0 0 set large2
342 $ hg status -A large2
375 $ hg status -A large2
343 A large2
376 A large2
344 $ cat large2
377 $ cat large2
345 modified large2 for linear merge
378 modified large2 for linear merge
346
379
347 (added largefile is already committed as normal)
380 (added largefile is already committed as normal)
348
381
349 $ hg update -q -C 2
382 $ hg update -q -C 2
350 $ echo 'large3 as large file for linear merge' > large3
383 $ echo 'large3 as large file for linear merge' > large3
351 $ hg add --large large3
384 $ hg add --large large3
352 $ hg update -q 5
385 $ hg update -q 5
353 remote turned local largefile large3 into a normal file
386 remote turned local largefile large3 into a normal file
354 keep (l)argefile or use (n)ormal file? l
387 keep (l)argefile or use (n)ormal file? l
355 $ hg debugdirstate --nodates | grep large3
388 $ hg debugdirstate --nodates | grep large3
356 a 0 -1 unset .hglf/large3
389 a 0 -1 unset .hglf/large3
357 r 0 0 set large3
390 r 0 0 set large3
358 $ hg status -A large3
391 $ hg status -A large3
359 A large3
392 A large3
360 $ cat large3
393 $ cat large3
361 large3 as large file for linear merge
394 large3 as large file for linear merge
362 $ rm -f large3 .hglf/large3
395 $ rm -f large3 .hglf/large3
363
396
364 Test that the internal linear merging works correctly
397 Test that the internal linear merging works correctly
365 (both heads are stripped to keep pairing of revision number and commit log)
398 (both heads are stripped to keep pairing of revision number and commit log)
366
399
367 $ hg update -q -C 2
400 $ hg update -q -C 2
368 $ hg strip 3 4
401 $ hg strip 3 4
369 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-2e7b195d-backup.hg (glob)
402 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-2e7b195d-backup.hg (glob)
370 $ mv .hg/strip-backup/9530e27857f7-2e7b195d-backup.hg $TESTTMP
403 $ mv .hg/strip-backup/9530e27857f7-2e7b195d-backup.hg $TESTTMP
371
404
372 (internal linear merging at "hg pull --update")
405 (internal linear merging at "hg pull --update")
373
406
374 $ echo 'large1 for linear merge (conflict)' > large1
407 $ echo 'large1 for linear merge (conflict)' > large1
375 $ echo 'large2 for linear merge (conflict with normal file)' > large2
408 $ echo 'large2 for linear merge (conflict with normal file)' > large2
376 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
409 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
377 pulling from $TESTTMP/9530e27857f7-2e7b195d-backup.hg (glob)
410 pulling from $TESTTMP/9530e27857f7-2e7b195d-backup.hg (glob)
378 searching for changes
411 searching for changes
379 adding changesets
412 adding changesets
380 adding manifests
413 adding manifests
381 adding file changes
414 adding file changes
382 added 3 changesets with 5 changes to 5 files
415 added 3 changesets with 5 changes to 5 files
383 remote turned local largefile large2 into a normal file
416 remote turned local largefile large2 into a normal file
384 keep (l)argefile or use (n)ormal file? l
417 keep (l)argefile or use (n)ormal file? l
385 largefile large1 has a merge conflict
418 largefile large1 has a merge conflict
386 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
419 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
387 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
420 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
388 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
421 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
389 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
422 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
390
423
391 $ hg status -A large1
424 $ hg status -A large1
392 M large1
425 M large1
393 $ cat large1
426 $ cat large1
394 large1 for linear merge (conflict)
427 large1 for linear merge (conflict)
395 $ cat .hglf/large1
428 $ cat .hglf/large1
396 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
429 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
397 $ hg status -A large2
430 $ hg status -A large2
398 A large2
431 A large2
399 $ cat large2
432 $ cat large2
400 large2 for linear merge (conflict with normal file)
433 large2 for linear merge (conflict with normal file)
401 $ cat .hglf/large2
434 $ cat .hglf/large2
402 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
435 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
403
436
404 (internal linear merging at "hg unbundle --update")
437 (internal linear merging at "hg unbundle --update")
405
438
406 $ hg update -q -C 2
439 $ hg update -q -C 2
407 $ hg rollback -q
440 $ hg rollback -q
408
441
409 $ echo 'large1 for linear merge (conflict)' > large1
442 $ echo 'large1 for linear merge (conflict)' > large1
410 $ echo 'large2 for linear merge (conflict with normal file)' > large2
443 $ echo 'large2 for linear merge (conflict with normal file)' > large2
411 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
444 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
412 adding changesets
445 adding changesets
413 adding manifests
446 adding manifests
414 adding file changes
447 adding file changes
415 added 3 changesets with 5 changes to 5 files
448 added 3 changesets with 5 changes to 5 files
416 remote turned local largefile large2 into a normal file
449 remote turned local largefile large2 into a normal file
417 keep (l)argefile or use (n)ormal file? l
450 keep (l)argefile or use (n)ormal file? l
418 largefile large1 has a merge conflict
451 largefile large1 has a merge conflict
419 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
452 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
420 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
453 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
421 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
454 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
422 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
455 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
423
456
424 $ hg status -A large1
457 $ hg status -A large1
425 M large1
458 M large1
426 $ cat large1
459 $ cat large1
427 large1 for linear merge (conflict)
460 large1 for linear merge (conflict)
428 $ cat .hglf/large1
461 $ cat .hglf/large1
429 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
462 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
430 $ hg status -A large2
463 $ hg status -A large2
431 A large2
464 A large2
432 $ cat large2
465 $ cat large2
433 large2 for linear merge (conflict with normal file)
466 large2 for linear merge (conflict with normal file)
434 $ cat .hglf/large2
467 $ cat .hglf/large2
435 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
468 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
436
469
437 (internal linear merging in subrepo at "hg update")
470 (internal linear merging in subrepo at "hg update")
438
471
439 $ cd ..
472 $ cd ..
440 $ hg init subparent
473 $ hg init subparent
441 $ cd subparent
474 $ cd subparent
442
475
443 $ hg clone -q -u 2 ../repo sub
476 $ hg clone -q -u 2 ../repo sub
444 $ cat > .hgsub <<EOF
477 $ cat > .hgsub <<EOF
445 > sub = sub
478 > sub = sub
446 > EOF
479 > EOF
447 $ hg add .hgsub
480 $ hg add .hgsub
448 $ hg commit -m '#0@parent'
481 $ hg commit -m '#0@parent'
449 $ cat .hgsubstate
482 $ cat .hgsubstate
450 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
483 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
451 $ hg -R sub update -q
484 $ hg -R sub update -q
452 $ hg commit -m '#1@parent'
485 $ hg commit -m '#1@parent'
453 $ cat .hgsubstate
486 $ cat .hgsubstate
454 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
487 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
455 $ hg update -q 0
488 $ hg update -q 0
456
489
457 $ echo 'large1 for linear merge (conflict)' > sub/large1
490 $ echo 'large1 for linear merge (conflict)' > sub/large1
458 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
491 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
459 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
492 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
460 > m
493 > m
461 > r
494 > r
462 > l
495 > l
463 > l
496 > l
464 > EOF
497 > EOF
465 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
498 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
466 (M)erge, keep (l)ocal or keep (r)emote? m
499 (M)erge, keep (l)ocal or keep (r)emote? m
467 subrepository sources for sub differ (in checked out version)
500 subrepository sources for sub differ (in checked out version)
468 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
501 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
469 remote turned local largefile large2 into a normal file
502 remote turned local largefile large2 into a normal file
470 keep (l)argefile or use (n)ormal file? l
503 keep (l)argefile or use (n)ormal file? l
471 largefile large1 has a merge conflict
504 largefile large1 has a merge conflict
472 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
505 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
473 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
506 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
474 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
507 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
475 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
508 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
476 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
509 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
477
510
478 $ hg -R sub status -A sub/large1
511 $ hg -R sub status -A sub/large1
479 M sub/large1
512 M sub/large1
480 $ cat sub/large1
513 $ cat sub/large1
481 large1 for linear merge (conflict)
514 large1 for linear merge (conflict)
482 $ cat sub/.hglf/large1
515 $ cat sub/.hglf/large1
483 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
516 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
484 $ hg -R sub status -A sub/large2
517 $ hg -R sub status -A sub/large2
485 A sub/large2
518 A sub/large2
486 $ cat sub/large2
519 $ cat sub/large2
487 large2 for linear merge (conflict with normal file)
520 large2 for linear merge (conflict with normal file)
488 $ cat sub/.hglf/large2
521 $ cat sub/.hglf/large2
489 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
522 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
490
523
491 $ cd ..
524 $ cd ..
492 $ cd repo
525 $ cd repo
493
526
494 Test that rebase updates largefiles in the working directory even if
527 Test that rebase updates largefiles in the working directory even if
495 it is aborted by conflict.
528 it is aborted by conflict.
496
529
497 $ hg update -q -C 3
530 $ hg update -q -C 3
498 $ cat .hglf/large1
531 $ cat .hglf/large1
499 e5bb990443d6a92aaf7223813720f7566c9dd05b
532 e5bb990443d6a92aaf7223813720f7566c9dd05b
500 $ cat large1
533 $ cat large1
501 large1 in #3
534 large1 in #3
502 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
535 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
503 > o
536 > o
504 > EOF
537 > EOF
505 rebasing 1:72518492caa6 "#1"
538 rebasing 1:72518492caa6 "#1"
506 largefile large1 has a merge conflict
539 largefile large1 has a merge conflict
507 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
540 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
508 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
541 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
509 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
542 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
510 merging normal1
543 merging normal1
511 warning: conflicts during merge.
544 warning: conflicts during merge.
512 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
545 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
513 unresolved conflicts (see hg resolve, then hg rebase --continue)
546 unresolved conflicts (see hg resolve, then hg rebase --continue)
514 [1]
547 [1]
515 $ cat .hglf/large1
548 $ cat .hglf/large1
516 58e24f733a964da346e2407a2bee99d9001184f5
549 58e24f733a964da346e2407a2bee99d9001184f5
517 $ cat large1
550 $ cat large1
518 large1 in #1
551 large1 in #1
519
552
520 Test that rebase updates standins for manually modified largefiles at
553 Test that rebase updates standins for manually modified largefiles at
521 the 1st commit of resuming.
554 the 1st commit of resuming.
522
555
523 $ echo "manually modified before 'hg rebase --continue'" > large1
556 $ echo "manually modified before 'hg rebase --continue'" > large1
524 $ hg resolve -m normal1
557 $ hg resolve -m normal1
525 (no more unresolved files)
558 (no more unresolved files)
526 $ hg rebase --continue --config ui.interactive=True <<EOF
559 $ hg rebase --continue --config ui.interactive=True <<EOF
527 > c
560 > c
528 > EOF
561 > EOF
529 rebasing 1:72518492caa6 "#1"
562 rebasing 1:72518492caa6 "#1"
530 rebasing 4:07d6153b5c04 "#4"
563 rebasing 4:07d6153b5c04 "#4"
531 local changed .hglf/large1 which remote deleted
564 local changed .hglf/large1 which remote deleted
532 use (c)hanged version or (d)elete? c
565 use (c)hanged version or (d)elete? c
533
566
534 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
567 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
535 -e5bb990443d6a92aaf7223813720f7566c9dd05b
568 -e5bb990443d6a92aaf7223813720f7566c9dd05b
536 +8a4f783556e7dea21139ca0466eafce954c75c13
569 +8a4f783556e7dea21139ca0466eafce954c75c13
537 $ rm -f large1
570 $ rm -f large1
538 $ hg update -q -C tip
571 $ hg update -q -C tip
539 $ cat large1
572 $ cat large1
540 manually modified before 'hg rebase --continue'
573 manually modified before 'hg rebase --continue'
541
574
542 Test that transplant updates largefiles, of which standins are safely
575 Test that transplant updates largefiles, of which standins are safely
543 changed, even if it is aborted by conflict of other.
576 changed, even if it is aborted by conflict of other.
544
577
545 $ hg update -q -C 5
578 $ hg update -q -C 5
546 $ cat .hglf/large1
579 $ cat .hglf/large1
547 e5bb990443d6a92aaf7223813720f7566c9dd05b
580 e5bb990443d6a92aaf7223813720f7566c9dd05b
548 $ cat large1
581 $ cat large1
549 large1 in #3
582 large1 in #3
550 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
583 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
551 +fa44618ea25181aff4f48b70428294790cec9f61
584 +fa44618ea25181aff4f48b70428294790cec9f61
552 $ hg transplant 4
585 $ hg transplant 4
553 applying 07d6153b5c04
586 applying 07d6153b5c04
554 patching file .hglf/large1
587 patching file .hglf/large1
555 Hunk #1 FAILED at 0
588 Hunk #1 FAILED at 0
556 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
589 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
557 patch failed to apply
590 patch failed to apply
558 abort: fix up the merge and run hg transplant --continue
591 abort: fix up the merge and run hg transplant --continue
559 [255]
592 [255]
560 $ hg status -A large1
593 $ hg status -A large1
561 C large1
594 C large1
562 $ cat .hglf/large1
595 $ cat .hglf/large1
563 e5bb990443d6a92aaf7223813720f7566c9dd05b
596 e5bb990443d6a92aaf7223813720f7566c9dd05b
564 $ cat large1
597 $ cat large1
565 large1 in #3
598 large1 in #3
566 $ hg status -A largeX
599 $ hg status -A largeX
567 A largeX
600 A largeX
568 $ cat .hglf/largeX
601 $ cat .hglf/largeX
569 fa44618ea25181aff4f48b70428294790cec9f61
602 fa44618ea25181aff4f48b70428294790cec9f61
570 $ cat largeX
603 $ cat largeX
571 largeX
604 largeX
572
605
573 Test that transplant updates standins for manually modified largefiles
606 Test that transplant updates standins for manually modified largefiles
574 at the 1st commit of resuming.
607 at the 1st commit of resuming.
575
608
576 $ echo "manually modified before 'hg transplant --continue'" > large1
609 $ echo "manually modified before 'hg transplant --continue'" > large1
577 $ hg transplant --continue
610 $ hg transplant --continue
578 07d6153b5c04 transplanted as f1bf30eb88cc
611 07d6153b5c04 transplanted as f1bf30eb88cc
579 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
612 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
580 -e5bb990443d6a92aaf7223813720f7566c9dd05b
613 -e5bb990443d6a92aaf7223813720f7566c9dd05b
581 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
614 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
582 $ rm -f large1
615 $ rm -f large1
583 $ hg update -q -C tip
616 $ hg update -q -C tip
584 $ cat large1
617 $ cat large1
585 manually modified before 'hg transplant --continue'
618 manually modified before 'hg transplant --continue'
586
619
587 Test that "hg status" doesn't show removal of largefiles not managed
620 Test that "hg status" doesn't show removal of largefiles not managed
588 in the target context.
621 in the target context.
589
622
590 $ hg update -q -C 4
623 $ hg update -q -C 4
591 $ hg remove largeX
624 $ hg remove largeX
592 $ hg status -A largeX
625 $ hg status -A largeX
593 R largeX
626 R largeX
594 $ hg status -A --rev '.^1' largeX
627 $ hg status -A --rev '.^1' largeX
595
628
596 #if execbit
629 #if execbit
597
630
598 Test that "hg status" against revisions other than parent notices exec
631 Test that "hg status" against revisions other than parent notices exec
599 bit changes of largefiles.
632 bit changes of largefiles.
600
633
601 $ hg update -q -C 4
634 $ hg update -q -C 4
602
635
603 (the case that large2 doesn't have exec bit in the target context but
636 (the case that large2 doesn't have exec bit in the target context but
604 in the working context)
637 in the working context)
605
638
606 $ chmod +x large2
639 $ chmod +x large2
607 $ hg status -A --rev 0 large2
640 $ hg status -A --rev 0 large2
608 M large2
641 M large2
609 $ hg commit -m 'chmod +x large2'
642 $ hg commit -m 'chmod +x large2'
610
643
611 (the case that large2 has exec bit in the target context but not in
644 (the case that large2 has exec bit in the target context but not in
612 the working context)
645 the working context)
613
646
614 $ echo dummy > dummy
647 $ echo dummy > dummy
615 $ hg add dummy
648 $ hg add dummy
616 $ hg commit -m 'revision for separation'
649 $ hg commit -m 'revision for separation'
617 $ chmod -x large2
650 $ chmod -x large2
618 $ hg status -A --rev '.^1' large2
651 $ hg status -A --rev '.^1' large2
619 M large2
652 M large2
620
653
621 #else
654 #else
622
655
623 Test that "hg status" against revisions other than parent ignores exec
656 Test that "hg status" against revisions other than parent ignores exec
624 bit correctly on the platform being unaware of it.
657 bit correctly on the platform being unaware of it.
625
658
626 $ hg update -q -C 4
659 $ hg update -q -C 4
627
660
628 $ cat > exec-bit.patch <<EOF
661 $ cat > exec-bit.patch <<EOF
629 > # HG changeset patch
662 > # HG changeset patch
630 > # User test
663 > # User test
631 > # Date 0 0
664 > # Date 0 0
632 > # Thu Jan 01 00:00:00 1970 +0000
665 > # Thu Jan 01 00:00:00 1970 +0000
633 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
666 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
634 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
667 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
635 > chmod +x large2
668 > chmod +x large2
636 >
669 >
637 > diff --git a/.hglf/large2 b/.hglf/large2
670 > diff --git a/.hglf/large2 b/.hglf/large2
638 > old mode 100644
671 > old mode 100644
639 > new mode 100755
672 > new mode 100755
640 > EOF
673 > EOF
641 $ hg import --exact --bypass exec-bit.patch
674 $ hg import --exact --bypass exec-bit.patch
642 applying exec-bit.patch
675 applying exec-bit.patch
643 $ hg status -A --rev tip large2
676 $ hg status -A --rev tip large2
644 C large2
677 C large2
645
678
646 #endif
679 #endif
647
680
648 $ cd ..
681 $ cd ..
649
682
650 Test that "hg convert" avoids copying largefiles from the working
683 Test that "hg convert" avoids copying largefiles from the working
651 directory into store, because "hg convert" doesn't update largefiles
684 directory into store, because "hg convert" doesn't update largefiles
652 in the working directory (removing files under ".cache/largefiles"
685 in the working directory (removing files under ".cache/largefiles"
653 forces "hg convert" to copy corresponding largefiles)
686 forces "hg convert" to copy corresponding largefiles)
654
687
655 $ cat >> $HGRCPATH <<EOF
688 $ cat >> $HGRCPATH <<EOF
656 > [extensions]
689 > [extensions]
657 > convert =
690 > convert =
658 > EOF
691 > EOF
659
692
660 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
693 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
661 $ hg convert -q repo repo.converted
694 $ hg convert -q repo repo.converted
General Comments 0
You need to be logged in to leave comments. Login now