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