##// END OF EJS Templates
largefiles: restore standins from non branch-tip parent at rollback correctly...
FUJIWARA Katsunori -
r22284:72b37865 default
parent child Browse files
Show More
@@ -1,1288 +1,1288
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, merge, pathutil, revset
15 archival, merge, 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 result = orig(ui, repo, **opts)
1200 result = orig(ui, repo, **opts)
1201 after = repo.dirstate.parents()
1201 after = repo.dirstate.parents()
1202 if before == after:
1202 if before == after:
1203 return result # no need to restore standins
1203 return result # no need to restore standins
1204
1204
1205 merge.update(repo, node=None, branchmerge=False, force=True,
1205 merge.update(repo, node='.', branchmerge=False, force=True,
1206 partial=lfutil.isstandin)
1206 partial=lfutil.isstandin)
1207
1207
1208 lfdirstate = lfutil.openlfdirstate(ui, repo)
1208 lfdirstate = lfutil.openlfdirstate(ui, repo)
1209 orphans = set(lfdirstate)
1209 orphans = set(lfdirstate)
1210 lfiles = lfutil.listlfiles(repo)
1210 lfiles = lfutil.listlfiles(repo)
1211 for file in lfiles:
1211 for file in lfiles:
1212 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1212 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1213 orphans.discard(file)
1213 orphans.discard(file)
1214 for lfile in orphans:
1214 for lfile in orphans:
1215 lfdirstate.drop(lfile)
1215 lfdirstate.drop(lfile)
1216 lfdirstate.write()
1216 lfdirstate.write()
1217 finally:
1217 finally:
1218 wlock.release()
1218 wlock.release()
1219 return result
1219 return result
1220
1220
1221 def overridetransplant(orig, ui, repo, *revs, **opts):
1221 def overridetransplant(orig, ui, repo, *revs, **opts):
1222 try:
1222 try:
1223 oldstandins = lfutil.getstandinsstate(repo)
1223 oldstandins = lfutil.getstandinsstate(repo)
1224 repo._istransplanting = True
1224 repo._istransplanting = True
1225 result = orig(ui, repo, *revs, **opts)
1225 result = orig(ui, repo, *revs, **opts)
1226 newstandins = lfutil.getstandinsstate(repo)
1226 newstandins = lfutil.getstandinsstate(repo)
1227 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1227 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1228 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1228 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1229 printmessage=True)
1229 printmessage=True)
1230 finally:
1230 finally:
1231 repo._istransplanting = False
1231 repo._istransplanting = False
1232 return result
1232 return result
1233
1233
1234 def overridecat(orig, ui, repo, file1, *pats, **opts):
1234 def overridecat(orig, ui, repo, file1, *pats, **opts):
1235 ctx = scmutil.revsingle(repo, opts.get('rev'))
1235 ctx = scmutil.revsingle(repo, opts.get('rev'))
1236 err = 1
1236 err = 1
1237 notbad = set()
1237 notbad = set()
1238 m = scmutil.match(ctx, (file1,) + pats, opts)
1238 m = scmutil.match(ctx, (file1,) + pats, opts)
1239 origmatchfn = m.matchfn
1239 origmatchfn = m.matchfn
1240 def lfmatchfn(f):
1240 def lfmatchfn(f):
1241 if origmatchfn(f):
1241 if origmatchfn(f):
1242 return True
1242 return True
1243 lf = lfutil.splitstandin(f)
1243 lf = lfutil.splitstandin(f)
1244 if lf is None:
1244 if lf is None:
1245 return False
1245 return False
1246 notbad.add(lf)
1246 notbad.add(lf)
1247 return origmatchfn(lf)
1247 return origmatchfn(lf)
1248 m.matchfn = lfmatchfn
1248 m.matchfn = lfmatchfn
1249 origbadfn = m.bad
1249 origbadfn = m.bad
1250 def lfbadfn(f, msg):
1250 def lfbadfn(f, msg):
1251 if not f in notbad:
1251 if not f in notbad:
1252 origbadfn(f, msg)
1252 origbadfn(f, msg)
1253 m.bad = lfbadfn
1253 m.bad = lfbadfn
1254 for f in ctx.walk(m):
1254 for f in ctx.walk(m):
1255 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1255 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1256 pathname=f)
1256 pathname=f)
1257 lf = lfutil.splitstandin(f)
1257 lf = lfutil.splitstandin(f)
1258 if lf is None or origmatchfn(f):
1258 if lf is None or origmatchfn(f):
1259 # duplicating unreachable code from commands.cat
1259 # duplicating unreachable code from commands.cat
1260 data = ctx[f].data()
1260 data = ctx[f].data()
1261 if opts.get('decode'):
1261 if opts.get('decode'):
1262 data = repo.wwritedata(f, data)
1262 data = repo.wwritedata(f, data)
1263 fp.write(data)
1263 fp.write(data)
1264 else:
1264 else:
1265 hash = lfutil.readstandin(repo, lf, ctx.rev())
1265 hash = lfutil.readstandin(repo, lf, ctx.rev())
1266 if not lfutil.inusercache(repo.ui, hash):
1266 if not lfutil.inusercache(repo.ui, hash):
1267 store = basestore._openstore(repo)
1267 store = basestore._openstore(repo)
1268 success, missing = store.get([(lf, hash)])
1268 success, missing = store.get([(lf, hash)])
1269 if len(success) != 1:
1269 if len(success) != 1:
1270 raise util.Abort(
1270 raise util.Abort(
1271 _('largefile %s is not in cache and could not be '
1271 _('largefile %s is not in cache and could not be '
1272 'downloaded') % lf)
1272 'downloaded') % lf)
1273 path = lfutil.usercachepath(repo.ui, hash)
1273 path = lfutil.usercachepath(repo.ui, hash)
1274 fpin = open(path, "rb")
1274 fpin = open(path, "rb")
1275 for chunk in util.filechunkiter(fpin, 128 * 1024):
1275 for chunk in util.filechunkiter(fpin, 128 * 1024):
1276 fp.write(chunk)
1276 fp.write(chunk)
1277 fpin.close()
1277 fpin.close()
1278 fp.close()
1278 fp.close()
1279 err = 0
1279 err = 0
1280 return err
1280 return err
1281
1281
1282 def mercurialsinkbefore(orig, sink):
1282 def mercurialsinkbefore(orig, sink):
1283 sink.repo._isconverting = True
1283 sink.repo._isconverting = True
1284 orig(sink)
1284 orig(sink)
1285
1285
1286 def mercurialsinkafter(orig, sink):
1286 def mercurialsinkafter(orig, sink):
1287 sink.repo._isconverting = False
1287 sink.repo._isconverting = False
1288 orig(sink)
1288 orig(sink)
@@ -1,294 +1,313
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 $ hg forget large2
106 $ hg forget large2
107 $ echo largeX > largeX
107 $ echo largeX > largeX
108 $ hg add --large largeX
108 $ hg add --large largeX
109 $ hg commit -m 'will be rollback-ed soon'
109 $ hg commit -m 'will be rollback-ed soon'
110 $ echo largeY > largeY
110 $ echo largeY > largeY
111 $ hg add --large largeY
111 $ hg add --large largeY
112 $ hg status -A large1
112 $ hg status -A large1
113 large1: No such file or directory
113 large1: No such file or directory
114 $ hg status -A large2
114 $ hg status -A large2
115 ? large2
115 ? large2
116 $ hg status -A largeX
116 $ hg status -A largeX
117 C largeX
117 C largeX
118 $ hg status -A largeY
118 $ hg status -A largeY
119 A largeY
119 A largeY
120 $ hg rollback
120 $ hg rollback
121 repository tip rolled back to revision 3 (undo commit)
121 repository tip rolled back to revision 3 (undo commit)
122 working directory now based on revision 3
122 working directory now based on revision 3
123 $ hg status -A large1
123 $ hg status -A large1
124 R large1
124 R large1
125 $ hg status -A large2
125 $ hg status -A large2
126 R large2
126 R large2
127 $ hg status -A largeX
127 $ hg status -A largeX
128 A largeX
128 A largeX
129 $ hg status -A largeY
129 $ hg status -A largeY
130 ? largeY
130 ? largeY
131
131
132 Test that "hg rollback" restores standins correctly
132 Test that "hg rollback" restores standins correctly
133
133
134 $ hg commit -m 'will be rollback-ed soon'
134 $ hg commit -m 'will be rollback-ed soon'
135 $ hg update -q -C 2
135 $ hg update -q -C 2
136 $ cat large1
136 $ cat large1
137 large1
137 large1
138 $ cat .hglf/large1
138 $ cat .hglf/large1
139 4669e532d5b2c093a78eca010077e708a071bb64
139 4669e532d5b2c093a78eca010077e708a071bb64
140 $ cat large2
140 $ cat large2
141 large2 in #2
141 large2 in #2
142 $ cat .hglf/large2
142 $ cat .hglf/large2
143 3cfce6277e7668985707b6887ce56f9f62f6ccd9
143 3cfce6277e7668985707b6887ce56f9f62f6ccd9
144
144
145 $ hg rollback -q -f
145 $ hg rollback -q -f
146 $ cat large1
146 $ cat large1
147 large1
147 large1
148 $ cat .hglf/large1
148 $ cat .hglf/large1
149 4669e532d5b2c093a78eca010077e708a071bb64
149 4669e532d5b2c093a78eca010077e708a071bb64
150 $ cat large2
150 $ cat large2
151 large2 in #2
151 large2 in #2
152 $ cat .hglf/large2
152 $ cat .hglf/large2
153 3cfce6277e7668985707b6887ce56f9f62f6ccd9
153 3cfce6277e7668985707b6887ce56f9f62f6ccd9
154
154
155 (rollback the parent of the working directory, when the parent of it
156 is not branch-tip)
157
158 $ hg update -q -C 1
159 $ cat .hglf/large1
160 58e24f733a964da346e2407a2bee99d9001184f5
161 $ cat .hglf/large2
162 1deebade43c8c498a3c8daddac0244dc55d1331d
163
164 $ echo normalX > normalX
165 $ hg add normalX
166 $ hg commit -m 'will be rollback-ed soon'
167 $ hg rollback -q
168
169 $ cat .hglf/large1
170 58e24f733a964da346e2407a2bee99d9001184f5
171 $ cat .hglf/large2
172 1deebade43c8c498a3c8daddac0244dc55d1331d
173
155 Test that "hg status" shows status of largefiles correctly just after
174 Test that "hg status" shows status of largefiles correctly just after
156 automated commit like rebase/transplant
175 automated commit like rebase/transplant
157
176
158 $ cat >> .hg/hgrc <<EOF
177 $ cat >> .hg/hgrc <<EOF
159 > [extensions]
178 > [extensions]
160 > rebase =
179 > rebase =
161 > strip =
180 > strip =
162 > transplant =
181 > transplant =
163 > EOF
182 > EOF
164 $ hg update -q -C 1
183 $ hg update -q -C 1
165 $ hg remove large1
184 $ hg remove large1
166 $ echo largeX > largeX
185 $ echo largeX > largeX
167 $ hg add --large largeX
186 $ hg add --large largeX
168 $ hg commit -m '#4'
187 $ hg commit -m '#4'
169
188
170 $ hg rebase -s 1 -d 2 --keep
189 $ hg rebase -s 1 -d 2 --keep
171 $ hg status -A large1
190 $ hg status -A large1
172 large1: No such file or directory
191 large1: No such file or directory
173 $ hg status -A largeX
192 $ hg status -A largeX
174 C largeX
193 C largeX
175 $ hg strip -q 5
194 $ hg strip -q 5
176
195
177 $ hg update -q -C 2
196 $ hg update -q -C 2
178 $ hg transplant -q 1 4
197 $ hg transplant -q 1 4
179 $ hg status -A large1
198 $ hg status -A large1
180 large1: No such file or directory
199 large1: No such file or directory
181 $ hg status -A largeX
200 $ hg status -A largeX
182 C largeX
201 C largeX
183 $ hg strip -q 5
202 $ hg strip -q 5
184
203
185 $ hg update -q -C 2
204 $ hg update -q -C 2
186 $ hg transplant -q --merge 1 --merge 4
205 $ hg transplant -q --merge 1 --merge 4
187 $ hg status -A large1
206 $ hg status -A large1
188 large1: No such file or directory
207 large1: No such file or directory
189 $ hg status -A largeX
208 $ hg status -A largeX
190 C largeX
209 C largeX
191 $ hg strip -q 5
210 $ hg strip -q 5
192
211
193 Test that linear merge can detect modification (and conflict) correctly
212 Test that linear merge can detect modification (and conflict) correctly
194
213
195 (linear merge without conflict)
214 (linear merge without conflict)
196
215
197 $ echo 'large2 for linear merge (no conflict)' > large2
216 $ echo 'large2 for linear merge (no conflict)' > large2
198 $ hg update 3 --config debug.dirstate.delaywrite=2
217 $ hg update 3 --config debug.dirstate.delaywrite=2
199 getting changed largefiles
218 getting changed largefiles
200 1 largefiles updated, 0 removed
219 1 largefiles updated, 0 removed
201 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 $ hg status -A large2
221 $ hg status -A large2
203 M large2
222 M large2
204 $ cat large2
223 $ cat large2
205 large2 for linear merge (no conflict)
224 large2 for linear merge (no conflict)
206 $ cat .hglf/large2
225 $ cat .hglf/large2
207 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
226 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
208
227
209 (linear merge with conflict, choosing "other")
228 (linear merge with conflict, choosing "other")
210
229
211 $ hg update -q -C 2
230 $ hg update -q -C 2
212 $ echo 'large1 for linear merge (conflict)' > large1
231 $ echo 'large1 for linear merge (conflict)' > large1
213 $ hg update 3 --config ui.interactive=True <<EOF
232 $ hg update 3 --config ui.interactive=True <<EOF
214 > o
233 > o
215 > EOF
234 > EOF
216 largefile large1 has a merge conflict
235 largefile large1 has a merge conflict
217 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
236 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
218 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
237 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
219 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? getting changed largefiles
238 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? getting changed largefiles
220 1 largefiles updated, 0 removed
239 1 largefiles updated, 0 removed
221 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
240 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
222 $ hg status -A large1
241 $ hg status -A large1
223 C large1
242 C large1
224 $ cat large1
243 $ cat large1
225 large1 in #3
244 large1 in #3
226 $ cat .hglf/large1
245 $ cat .hglf/large1
227 e5bb990443d6a92aaf7223813720f7566c9dd05b
246 e5bb990443d6a92aaf7223813720f7566c9dd05b
228
247
229 (linear merge with conflict, choosing "local")
248 (linear merge with conflict, choosing "local")
230
249
231 $ hg update -q -C 2
250 $ hg update -q -C 2
232 $ echo 'large1 for linear merge (conflict)' > large1
251 $ echo 'large1 for linear merge (conflict)' > large1
233 $ hg update 3 --config debug.dirstate.delaywrite=2
252 $ hg update 3 --config debug.dirstate.delaywrite=2
234 largefile large1 has a merge conflict
253 largefile large1 has a merge conflict
235 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
254 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
236 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
255 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
237 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
256 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
238 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
257 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
239 $ hg status -A large1
258 $ hg status -A large1
240 M large1
259 M large1
241 $ cat large1
260 $ cat large1
242 large1 for linear merge (conflict)
261 large1 for linear merge (conflict)
243 $ cat .hglf/large1
262 $ cat .hglf/large1
244 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
263 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
245
264
246 Test a linear merge to a revision containing same-name normal file
265 Test a linear merge to a revision containing same-name normal file
247
266
248 $ hg update -q -C 3
267 $ hg update -q -C 3
249 $ hg remove large2
268 $ hg remove large2
250 $ echo 'large2 as normal file' > large2
269 $ echo 'large2 as normal file' > large2
251 $ hg add large2
270 $ hg add large2
252 $ echo 'large3 as normal file' > large3
271 $ echo 'large3 as normal file' > large3
253 $ hg add large3
272 $ hg add large3
254 $ hg commit -m '#5'
273 $ hg commit -m '#5'
255 $ hg manifest
274 $ hg manifest
256 .hglf/large1
275 .hglf/large1
257 large2
276 large2
258 large3
277 large3
259 normal1
278 normal1
260
279
261 (modified largefile is already switched to normal)
280 (modified largefile is already switched to normal)
262
281
263 $ hg update -q -C 2
282 $ hg update -q -C 2
264 $ echo 'modified large2 for linear merge' > large2
283 $ echo 'modified large2 for linear merge' > large2
265 $ hg update -q 5
284 $ hg update -q 5
266 local changed .hglf/large2 which remote deleted
285 local changed .hglf/large2 which remote deleted
267 use (c)hanged version or (d)elete? c
286 use (c)hanged version or (d)elete? c
268 remote turned local largefile large2 into a normal file
287 remote turned local largefile large2 into a normal file
269 keep (l)argefile or use (n)ormal file? l
288 keep (l)argefile or use (n)ormal file? l
270 $ hg debugdirstate --nodates | grep large2
289 $ hg debugdirstate --nodates | grep large2
271 a 0 -1 .hglf/large2
290 a 0 -1 .hglf/large2
272 r 0 0 large2
291 r 0 0 large2
273 $ hg status -A large2
292 $ hg status -A large2
274 A large2
293 A large2
275 $ cat large2
294 $ cat large2
276 modified large2 for linear merge
295 modified large2 for linear merge
277
296
278 (added largefile is already committed as normal)
297 (added largefile is already committed as normal)
279
298
280 $ hg update -q -C 2
299 $ hg update -q -C 2
281 $ echo 'large3 as large file for linear merge' > large3
300 $ echo 'large3 as large file for linear merge' > large3
282 $ hg add --large large3
301 $ hg add --large large3
283 $ hg update -q 5
302 $ hg update -q 5
284 remote turned local largefile large3 into a normal file
303 remote turned local largefile large3 into a normal file
285 keep (l)argefile or use (n)ormal file? l
304 keep (l)argefile or use (n)ormal file? l
286 $ hg debugdirstate --nodates | grep large3
305 $ hg debugdirstate --nodates | grep large3
287 a 0 -1 .hglf/large3
306 a 0 -1 .hglf/large3
288 r 0 0 large3
307 r 0 0 large3
289 $ hg status -A large3
308 $ hg status -A large3
290 A large3
309 A large3
291 $ cat large3
310 $ cat large3
292 large3 as large file for linear merge
311 large3 as large file for linear merge
293
312
294 $ cd ..
313 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now