##// END OF EJS Templates
largefiles: fix confusion upon removal of added largefile (issue3176)...
Na'Tosha Bard -
r15786:aca0f2b3 stable
parent child Browse files
Show More
@@ -1,912 +1,910 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 node, archival, error, merge
15 node, archival, error, merge
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
22
23 def installnormalfilesmatchfn(manifest):
23 def installnormalfilesmatchfn(manifest):
24 '''overrides scmutil.match so that the matcher it returns will ignore all
24 '''overrides scmutil.match so that the matcher it returns will ignore all
25 largefiles'''
25 largefiles'''
26 oldmatch = None # for the closure
26 oldmatch = None # for the closure
27 def override_match(ctx, pats=[], opts={}, globbed=False,
27 def override_match(ctx, pats=[], opts={}, globbed=False,
28 default='relpath'):
28 default='relpath'):
29 match = oldmatch(ctx, pats, opts, globbed, default)
29 match = oldmatch(ctx, pats, opts, globbed, default)
30 m = copy.copy(match)
30 m = copy.copy(match)
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 manifest)
32 manifest)
33 m._files = filter(notlfile, m._files)
33 m._files = filter(notlfile, m._files)
34 m._fmap = set(m._files)
34 m._fmap = set(m._files)
35 orig_matchfn = m.matchfn
35 orig_matchfn = m.matchfn
36 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
36 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
37 return m
37 return m
38 oldmatch = installmatchfn(override_match)
38 oldmatch = installmatchfn(override_match)
39
39
40 def installmatchfn(f):
40 def installmatchfn(f):
41 oldmatch = scmutil.match
41 oldmatch = scmutil.match
42 setattr(f, 'oldmatch', oldmatch)
42 setattr(f, 'oldmatch', oldmatch)
43 scmutil.match = f
43 scmutil.match = f
44 return oldmatch
44 return oldmatch
45
45
46 def restorematchfn():
46 def restorematchfn():
47 '''restores scmutil.match to what it was before installnormalfilesmatchfn
47 '''restores scmutil.match to what it was before installnormalfilesmatchfn
48 was called. no-op if scmutil.match is its original function.
48 was called. no-op if scmutil.match is its original function.
49
49
50 Note that n calls to installnormalfilesmatchfn will require n calls to
50 Note that n calls to installnormalfilesmatchfn will require n calls to
51 restore matchfn to reverse'''
51 restore matchfn to reverse'''
52 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
52 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
53
53
54 # -- Wrappers: modify existing commands --------------------------------
54 # -- Wrappers: modify existing commands --------------------------------
55
55
56 # Add works by going through the files that the user wanted to add and
56 # Add works by going through the files that the user wanted to add and
57 # checking if they should be added as largefiles. Then it makes a new
57 # checking if they should be added as largefiles. Then it makes a new
58 # matcher which matches only the normal files and runs the original
58 # matcher which matches only the normal files and runs the original
59 # version of add.
59 # version of add.
60 def override_add(orig, ui, repo, *pats, **opts):
60 def override_add(orig, ui, repo, *pats, **opts):
61 large = opts.pop('large', None)
61 large = opts.pop('large', None)
62 lfsize = lfutil.getminsize(
62 lfsize = lfutil.getminsize(
63 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
63 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
64
64
65 lfmatcher = None
65 lfmatcher = None
66 if os.path.exists(repo.wjoin(lfutil.shortname)):
66 if os.path.exists(repo.wjoin(lfutil.shortname)):
67 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
67 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
68 if lfpats:
68 if lfpats:
69 lfmatcher = match_.match(repo.root, '', list(lfpats))
69 lfmatcher = match_.match(repo.root, '', list(lfpats))
70
70
71 lfnames = []
71 lfnames = []
72 m = scmutil.match(repo[None], pats, opts)
72 m = scmutil.match(repo[None], pats, opts)
73 m.bad = lambda x, y: None
73 m.bad = lambda x, y: None
74 wctx = repo[None]
74 wctx = repo[None]
75 for f in repo.walk(m):
75 for f in repo.walk(m):
76 exact = m.exact(f)
76 exact = m.exact(f)
77 lfile = lfutil.standin(f) in wctx
77 lfile = lfutil.standin(f) in wctx
78 nfile = f in wctx
78 nfile = f in wctx
79 exists = lfile or nfile
79 exists = lfile or nfile
80
80
81 # Don't warn the user when they attempt to add a normal tracked file.
81 # Don't warn the user when they attempt to add a normal tracked file.
82 # The normal add code will do that for us.
82 # The normal add code will do that for us.
83 if exact and exists:
83 if exact and exists:
84 if lfile:
84 if lfile:
85 ui.warn(_('%s already a largefile\n') % f)
85 ui.warn(_('%s already a largefile\n') % f)
86 continue
86 continue
87
87
88 if exact or not exists:
88 if exact or not exists:
89 abovemin = (lfsize and
89 abovemin = (lfsize and
90 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
90 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
91 if large or abovemin or (lfmatcher and lfmatcher(f)):
91 if large or abovemin or (lfmatcher and lfmatcher(f)):
92 lfnames.append(f)
92 lfnames.append(f)
93 if ui.verbose or not exact:
93 if ui.verbose or not exact:
94 ui.status(_('adding %s as a largefile\n') % m.rel(f))
94 ui.status(_('adding %s as a largefile\n') % m.rel(f))
95
95
96 bad = []
96 bad = []
97 standins = []
97 standins = []
98
98
99 # Need to lock, otherwise there could be a race condition between
99 # Need to lock, otherwise there could be a race condition between
100 # when standins are created and added to the repo.
100 # when standins are created and added to the repo.
101 wlock = repo.wlock()
101 wlock = repo.wlock()
102 try:
102 try:
103 if not opts.get('dry_run'):
103 if not opts.get('dry_run'):
104 lfdirstate = lfutil.openlfdirstate(ui, repo)
104 lfdirstate = lfutil.openlfdirstate(ui, repo)
105 for f in lfnames:
105 for f in lfnames:
106 standinname = lfutil.standin(f)
106 standinname = lfutil.standin(f)
107 lfutil.writestandin(repo, standinname, hash='',
107 lfutil.writestandin(repo, standinname, hash='',
108 executable=lfutil.getexecutable(repo.wjoin(f)))
108 executable=lfutil.getexecutable(repo.wjoin(f)))
109 standins.append(standinname)
109 standins.append(standinname)
110 if lfdirstate[f] == 'r':
110 if lfdirstate[f] == 'r':
111 lfdirstate.normallookup(f)
111 lfdirstate.normallookup(f)
112 else:
112 else:
113 lfdirstate.add(f)
113 lfdirstate.add(f)
114 lfdirstate.write()
114 lfdirstate.write()
115 bad += [lfutil.splitstandin(f)
115 bad += [lfutil.splitstandin(f)
116 for f in lfutil.repo_add(repo, standins)
116 for f in lfutil.repo_add(repo, standins)
117 if f in m.files()]
117 if f in m.files()]
118 finally:
118 finally:
119 wlock.release()
119 wlock.release()
120
120
121 installnormalfilesmatchfn(repo[None].manifest())
121 installnormalfilesmatchfn(repo[None].manifest())
122 result = orig(ui, repo, *pats, **opts)
122 result = orig(ui, repo, *pats, **opts)
123 restorematchfn()
123 restorematchfn()
124
124
125 return (result == 1 or bad) and 1 or 0
125 return (result == 1 or bad) and 1 or 0
126
126
127 def override_remove(orig, ui, repo, *pats, **opts):
127 def override_remove(orig, ui, repo, *pats, **opts):
128 manifest = repo[None].manifest()
128 manifest = repo[None].manifest()
129 installnormalfilesmatchfn(manifest)
129 installnormalfilesmatchfn(manifest)
130 orig(ui, repo, *pats, **opts)
130 orig(ui, repo, *pats, **opts)
131 restorematchfn()
131 restorematchfn()
132
132
133 after, force = opts.get('after'), opts.get('force')
133 after = opts.get('after')
134 if not pats and not after:
134 if not pats and not after:
135 raise util.Abort(_('no files specified'))
135 raise util.Abort(_('no files specified'))
136 m = scmutil.match(repo[None], pats, opts)
136 m = scmutil.match(repo[None], pats, opts)
137 try:
137 try:
138 repo.lfstatus = True
138 repo.lfstatus = True
139 s = repo.status(match=m, clean=True)
139 s = repo.status(match=m, clean=True)
140 finally:
140 finally:
141 repo.lfstatus = False
141 repo.lfstatus = False
142 modified, added, deleted, clean = [[f for f in list
142 modified, added, deleted, clean = [[f for f in list
143 if lfutil.standin(f) in manifest]
143 if lfutil.standin(f) in manifest]
144 for list in [s[0], s[1], s[3], s[6]]]
144 for list in [s[0], s[1], s[3], s[6]]]
145
145
146 def warn(files, reason):
146 def warn(files, reason):
147 for f in files:
147 for f in files:
148 ui.warn(_('not removing %s: %s (use -f to force removal)\n')
148 ui.warn(_('not removing %s: %s (use forget to undo)\n')
149 % (m.rel(f), reason))
149 % (m.rel(f), reason))
150
150
151 if force:
151 if after:
152 remove, forget = modified + deleted + clean, added
153 elif after:
154 remove, forget = deleted, []
152 remove, forget = deleted, []
155 warn(modified + added + clean, _('file still exists'))
153 warn(modified + added + clean, _('file still exists'))
156 else:
154 else:
157 remove, forget = deleted + clean, []
155 remove, forget = deleted + clean, []
158 warn(modified, _('file is modified'))
156 warn(modified, _('file is modified'))
159 warn(added, _('file has been marked for add'))
157 warn(added, _('file has been marked for add'))
160
158
161 for f in sorted(remove + forget):
159 for f in sorted(remove + forget):
162 if ui.verbose or not m.exact(f):
160 if ui.verbose or not m.exact(f):
163 ui.status(_('removing %s\n') % m.rel(f))
161 ui.status(_('removing %s\n') % m.rel(f))
164
162
165 # Need to lock because standin files are deleted then removed from the
163 # Need to lock because standin files are deleted then removed from the
166 # repository and we could race inbetween.
164 # repository and we could race inbetween.
167 wlock = repo.wlock()
165 wlock = repo.wlock()
168 try:
166 try:
169 lfdirstate = lfutil.openlfdirstate(ui, repo)
167 lfdirstate = lfutil.openlfdirstate(ui, repo)
170 for f in remove:
168 for f in remove:
171 if not after:
169 if not after:
172 os.unlink(repo.wjoin(f))
170 os.unlink(repo.wjoin(f))
173 currentdir = os.path.split(f)[0]
171 currentdir = os.path.split(f)[0]
174 while currentdir and not os.listdir(repo.wjoin(currentdir)):
172 while currentdir and not os.listdir(repo.wjoin(currentdir)):
175 os.rmdir(repo.wjoin(currentdir))
173 os.rmdir(repo.wjoin(currentdir))
176 currentdir = os.path.split(currentdir)[0]
174 currentdir = os.path.split(currentdir)[0]
177 lfdirstate.remove(f)
175 lfdirstate.remove(f)
178 lfdirstate.write()
176 lfdirstate.write()
179
177
180 forget = [lfutil.standin(f) for f in forget]
178 forget = [lfutil.standin(f) for f in forget]
181 remove = [lfutil.standin(f) for f in remove]
179 remove = [lfutil.standin(f) for f in remove]
182 lfutil.repo_forget(repo, forget)
180 lfutil.repo_forget(repo, forget)
183 lfutil.repo_remove(repo, remove, unlink=True)
181 lfutil.repo_remove(repo, remove, unlink=True)
184 finally:
182 finally:
185 wlock.release()
183 wlock.release()
186
184
187 def override_status(orig, ui, repo, *pats, **opts):
185 def override_status(orig, ui, repo, *pats, **opts):
188 try:
186 try:
189 repo.lfstatus = True
187 repo.lfstatus = True
190 return orig(ui, repo, *pats, **opts)
188 return orig(ui, repo, *pats, **opts)
191 finally:
189 finally:
192 repo.lfstatus = False
190 repo.lfstatus = False
193
191
194 def override_log(orig, ui, repo, *pats, **opts):
192 def override_log(orig, ui, repo, *pats, **opts):
195 try:
193 try:
196 repo.lfstatus = True
194 repo.lfstatus = True
197 orig(ui, repo, *pats, **opts)
195 orig(ui, repo, *pats, **opts)
198 finally:
196 finally:
199 repo.lfstatus = False
197 repo.lfstatus = False
200
198
201 def override_verify(orig, ui, repo, *pats, **opts):
199 def override_verify(orig, ui, repo, *pats, **opts):
202 large = opts.pop('large', False)
200 large = opts.pop('large', False)
203 all = opts.pop('lfa', False)
201 all = opts.pop('lfa', False)
204 contents = opts.pop('lfc', False)
202 contents = opts.pop('lfc', False)
205
203
206 result = orig(ui, repo, *pats, **opts)
204 result = orig(ui, repo, *pats, **opts)
207 if large:
205 if large:
208 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
206 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
209 return result
207 return result
210
208
211 # Override needs to refresh standins so that update's normal merge
209 # Override needs to refresh standins so that update's normal merge
212 # will go through properly. Then the other update hook (overriding repo.update)
210 # will go through properly. Then the other update hook (overriding repo.update)
213 # will get the new files. Filemerge is also overriden so that the merge
211 # will get the new files. Filemerge is also overriden so that the merge
214 # will merge standins correctly.
212 # will merge standins correctly.
215 def override_update(orig, ui, repo, *pats, **opts):
213 def override_update(orig, ui, repo, *pats, **opts):
216 lfdirstate = lfutil.openlfdirstate(ui, repo)
214 lfdirstate = lfutil.openlfdirstate(ui, repo)
217 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
215 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
218 False, False)
216 False, False)
219 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
217 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
220
218
221 # Need to lock between the standins getting updated and their
219 # Need to lock between the standins getting updated and their
222 # largefiles getting updated
220 # largefiles getting updated
223 wlock = repo.wlock()
221 wlock = repo.wlock()
224 try:
222 try:
225 if opts['check']:
223 if opts['check']:
226 mod = len(modified) > 0
224 mod = len(modified) > 0
227 for lfile in unsure:
225 for lfile in unsure:
228 standin = lfutil.standin(lfile)
226 standin = lfutil.standin(lfile)
229 if repo['.'][standin].data().strip() != \
227 if repo['.'][standin].data().strip() != \
230 lfutil.hashfile(repo.wjoin(lfile)):
228 lfutil.hashfile(repo.wjoin(lfile)):
231 mod = True
229 mod = True
232 else:
230 else:
233 lfdirstate.normal(lfile)
231 lfdirstate.normal(lfile)
234 lfdirstate.write()
232 lfdirstate.write()
235 if mod:
233 if mod:
236 raise util.Abort(_('uncommitted local changes'))
234 raise util.Abort(_('uncommitted local changes'))
237 # XXX handle removed differently
235 # XXX handle removed differently
238 if not opts['clean']:
236 if not opts['clean']:
239 for lfile in unsure + modified + added:
237 for lfile in unsure + modified + added:
240 lfutil.updatestandin(repo, lfutil.standin(lfile))
238 lfutil.updatestandin(repo, lfutil.standin(lfile))
241 finally:
239 finally:
242 wlock.release()
240 wlock.release()
243 return orig(ui, repo, *pats, **opts)
241 return orig(ui, repo, *pats, **opts)
244
242
245 # Before starting the manifest merge, merge.updates will call
243 # Before starting the manifest merge, merge.updates will call
246 # _checkunknown to check if there are any files in the merged-in
244 # _checkunknown to check if there are any files in the merged-in
247 # changeset that collide with unknown files in the working copy.
245 # changeset that collide with unknown files in the working copy.
248 #
246 #
249 # The largefiles are seen as unknown, so this prevents us from merging
247 # The largefiles are seen as unknown, so this prevents us from merging
250 # in a file 'foo' if we already have a largefile with the same name.
248 # in a file 'foo' if we already have a largefile with the same name.
251 #
249 #
252 # The overridden function filters the unknown files by removing any
250 # The overridden function filters the unknown files by removing any
253 # largefiles. This makes the merge proceed and we can then handle this
251 # largefiles. This makes the merge proceed and we can then handle this
254 # case further in the overridden manifestmerge function below.
252 # case further in the overridden manifestmerge function below.
255 def override_checkunknown(origfn, wctx, mctx, folding):
253 def override_checkunknown(origfn, wctx, mctx, folding):
256 origunknown = wctx.unknown()
254 origunknown = wctx.unknown()
257 wctx._unknown = filter(lambda f: lfutil.standin(f) not in wctx, origunknown)
255 wctx._unknown = filter(lambda f: lfutil.standin(f) not in wctx, origunknown)
258 try:
256 try:
259 return origfn(wctx, mctx, folding)
257 return origfn(wctx, mctx, folding)
260 finally:
258 finally:
261 wctx._unknown = origunknown
259 wctx._unknown = origunknown
262
260
263 # The manifest merge handles conflicts on the manifest level. We want
261 # The manifest merge handles conflicts on the manifest level. We want
264 # to handle changes in largefile-ness of files at this level too.
262 # to handle changes in largefile-ness of files at this level too.
265 #
263 #
266 # The strategy is to run the original manifestmerge and then process
264 # The strategy is to run the original manifestmerge and then process
267 # the action list it outputs. There are two cases we need to deal with:
265 # the action list it outputs. There are two cases we need to deal with:
268 #
266 #
269 # 1. Normal file in p1, largefile in p2. Here the largefile is
267 # 1. Normal file in p1, largefile in p2. Here the largefile is
270 # detected via its standin file, which will enter the working copy
268 # detected via its standin file, which will enter the working copy
271 # with a "get" action. It is not "merge" since the standin is all
269 # with a "get" action. It is not "merge" since the standin is all
272 # Mercurial is concerned with at this level -- the link to the
270 # Mercurial is concerned with at this level -- the link to the
273 # existing normal file is not relevant here.
271 # existing normal file is not relevant here.
274 #
272 #
275 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
273 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
276 # since the largefile will be present in the working copy and
274 # since the largefile will be present in the working copy and
277 # different from the normal file in p2. Mercurial therefore
275 # different from the normal file in p2. Mercurial therefore
278 # triggers a merge action.
276 # triggers a merge action.
279 #
277 #
280 # In both cases, we prompt the user and emit new actions to either
278 # In both cases, we prompt the user and emit new actions to either
281 # remove the standin (if the normal file was kept) or to remove the
279 # remove the standin (if the normal file was kept) or to remove the
282 # normal file and get the standin (if the largefile was kept). The
280 # normal file and get the standin (if the largefile was kept). The
283 # default prompt answer is to use the largefile version since it was
281 # default prompt answer is to use the largefile version since it was
284 # presumably changed on purpose.
282 # presumably changed on purpose.
285 #
283 #
286 # Finally, the merge.applyupdates function will then take care of
284 # Finally, the merge.applyupdates function will then take care of
287 # writing the files into the working copy and lfcommands.updatelfiles
285 # writing the files into the working copy and lfcommands.updatelfiles
288 # will update the largefiles.
286 # will update the largefiles.
289 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
287 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
290 actions = origfn(repo, p1, p2, pa, overwrite, partial)
288 actions = origfn(repo, p1, p2, pa, overwrite, partial)
291 processed = []
289 processed = []
292
290
293 for action in actions:
291 for action in actions:
294 if overwrite:
292 if overwrite:
295 processed.append(action)
293 processed.append(action)
296 continue
294 continue
297 f, m = action[:2]
295 f, m = action[:2]
298
296
299 choices = (_('&Largefile'), _('&Normal file'))
297 choices = (_('&Largefile'), _('&Normal file'))
300 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
298 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
301 # Case 1: normal file in the working copy, largefile in
299 # Case 1: normal file in the working copy, largefile in
302 # the second parent
300 # the second parent
303 lfile = lfutil.splitstandin(f)
301 lfile = lfutil.splitstandin(f)
304 standin = f
302 standin = f
305 msg = _('%s has been turned into a largefile\n'
303 msg = _('%s has been turned into a largefile\n'
306 'use (l)argefile or keep as (n)ormal file?') % lfile
304 'use (l)argefile or keep as (n)ormal file?') % lfile
307 if repo.ui.promptchoice(msg, choices, 0) == 0:
305 if repo.ui.promptchoice(msg, choices, 0) == 0:
308 processed.append((lfile, "r"))
306 processed.append((lfile, "r"))
309 processed.append((standin, "g", p2.flags(standin)))
307 processed.append((standin, "g", p2.flags(standin)))
310 else:
308 else:
311 processed.append((standin, "r"))
309 processed.append((standin, "r"))
312 elif m == "m" and lfutil.standin(f) in p1 and f in p2:
310 elif m == "m" and lfutil.standin(f) in p1 and f in p2:
313 # Case 2: largefile in the working copy, normal file in
311 # Case 2: largefile in the working copy, normal file in
314 # the second parent
312 # the second parent
315 standin = lfutil.standin(f)
313 standin = lfutil.standin(f)
316 lfile = f
314 lfile = f
317 msg = _('%s has been turned into a normal file\n'
315 msg = _('%s has been turned into a normal file\n'
318 'keep as (l)argefile or use (n)ormal file?') % lfile
316 'keep as (l)argefile or use (n)ormal file?') % lfile
319 if repo.ui.promptchoice(msg, choices, 0) == 0:
317 if repo.ui.promptchoice(msg, choices, 0) == 0:
320 processed.append((lfile, "r"))
318 processed.append((lfile, "r"))
321 else:
319 else:
322 processed.append((standin, "r"))
320 processed.append((standin, "r"))
323 processed.append((lfile, "g", p2.flags(lfile)))
321 processed.append((lfile, "g", p2.flags(lfile)))
324 else:
322 else:
325 processed.append(action)
323 processed.append(action)
326
324
327 return processed
325 return processed
328
326
329 # Override filemerge to prompt the user about how they wish to merge
327 # Override filemerge to prompt the user about how they wish to merge
330 # largefiles. This will handle identical edits, and copy/rename +
328 # largefiles. This will handle identical edits, and copy/rename +
331 # edit without prompting the user.
329 # edit without prompting the user.
332 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
330 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
333 # Use better variable names here. Because this is a wrapper we cannot
331 # Use better variable names here. Because this is a wrapper we cannot
334 # change the variable names in the function declaration.
332 # change the variable names in the function declaration.
335 fcdest, fcother, fcancestor = fcd, fco, fca
333 fcdest, fcother, fcancestor = fcd, fco, fca
336 if not lfutil.isstandin(orig):
334 if not lfutil.isstandin(orig):
337 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
335 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
338 else:
336 else:
339 if not fcother.cmp(fcdest): # files identical?
337 if not fcother.cmp(fcdest): # files identical?
340 return None
338 return None
341
339
342 # backwards, use working dir parent as ancestor
340 # backwards, use working dir parent as ancestor
343 if fcancestor == fcother:
341 if fcancestor == fcother:
344 fcancestor = fcdest.parents()[0]
342 fcancestor = fcdest.parents()[0]
345
343
346 if orig != fcother.path():
344 if orig != fcother.path():
347 repo.ui.status(_('merging %s and %s to %s\n')
345 repo.ui.status(_('merging %s and %s to %s\n')
348 % (lfutil.splitstandin(orig),
346 % (lfutil.splitstandin(orig),
349 lfutil.splitstandin(fcother.path()),
347 lfutil.splitstandin(fcother.path()),
350 lfutil.splitstandin(fcdest.path())))
348 lfutil.splitstandin(fcdest.path())))
351 else:
349 else:
352 repo.ui.status(_('merging %s\n')
350 repo.ui.status(_('merging %s\n')
353 % lfutil.splitstandin(fcdest.path()))
351 % lfutil.splitstandin(fcdest.path()))
354
352
355 if fcancestor.path() != fcother.path() and fcother.data() == \
353 if fcancestor.path() != fcother.path() and fcother.data() == \
356 fcancestor.data():
354 fcancestor.data():
357 return 0
355 return 0
358 if fcancestor.path() != fcdest.path() and fcdest.data() == \
356 if fcancestor.path() != fcdest.path() and fcdest.data() == \
359 fcancestor.data():
357 fcancestor.data():
360 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
358 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
361 return 0
359 return 0
362
360
363 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
361 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
364 'keep (l)ocal or take (o)ther?') %
362 'keep (l)ocal or take (o)ther?') %
365 lfutil.splitstandin(orig),
363 lfutil.splitstandin(orig),
366 (_('&Local'), _('&Other')), 0) == 0:
364 (_('&Local'), _('&Other')), 0) == 0:
367 return 0
365 return 0
368 else:
366 else:
369 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
367 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
370 return 0
368 return 0
371
369
372 # Copy first changes the matchers to match standins instead of
370 # Copy first changes the matchers to match standins instead of
373 # largefiles. Then it overrides util.copyfile in that function it
371 # largefiles. Then it overrides util.copyfile in that function it
374 # checks if the destination largefile already exists. It also keeps a
372 # checks if the destination largefile already exists. It also keeps a
375 # list of copied files so that the largefiles can be copied and the
373 # list of copied files so that the largefiles can be copied and the
376 # dirstate updated.
374 # dirstate updated.
377 def override_copy(orig, ui, repo, pats, opts, rename=False):
375 def override_copy(orig, ui, repo, pats, opts, rename=False):
378 # doesn't remove largefile on rename
376 # doesn't remove largefile on rename
379 if len(pats) < 2:
377 if len(pats) < 2:
380 # this isn't legal, let the original function deal with it
378 # this isn't legal, let the original function deal with it
381 return orig(ui, repo, pats, opts, rename)
379 return orig(ui, repo, pats, opts, rename)
382
380
383 def makestandin(relpath):
381 def makestandin(relpath):
384 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
382 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
385 return os.path.join(repo.wjoin(lfutil.standin(path)))
383 return os.path.join(repo.wjoin(lfutil.standin(path)))
386
384
387 fullpats = scmutil.expandpats(pats)
385 fullpats = scmutil.expandpats(pats)
388 dest = fullpats[-1]
386 dest = fullpats[-1]
389
387
390 if os.path.isdir(dest):
388 if os.path.isdir(dest):
391 if not os.path.isdir(makestandin(dest)):
389 if not os.path.isdir(makestandin(dest)):
392 os.makedirs(makestandin(dest))
390 os.makedirs(makestandin(dest))
393 # This could copy both lfiles and normal files in one command,
391 # This could copy both lfiles and normal files in one command,
394 # but we don't want to do that. First replace their matcher to
392 # but we don't want to do that. First replace their matcher to
395 # only match normal files and run it, then replace it to just
393 # only match normal files and run it, then replace it to just
396 # match largefiles and run it again.
394 # match largefiles and run it again.
397 nonormalfiles = False
395 nonormalfiles = False
398 nolfiles = False
396 nolfiles = False
399 try:
397 try:
400 try:
398 try:
401 installnormalfilesmatchfn(repo[None].manifest())
399 installnormalfilesmatchfn(repo[None].manifest())
402 result = orig(ui, repo, pats, opts, rename)
400 result = orig(ui, repo, pats, opts, rename)
403 except util.Abort, e:
401 except util.Abort, e:
404 if str(e) != 'no files to copy':
402 if str(e) != 'no files to copy':
405 raise e
403 raise e
406 else:
404 else:
407 nonormalfiles = True
405 nonormalfiles = True
408 result = 0
406 result = 0
409 finally:
407 finally:
410 restorematchfn()
408 restorematchfn()
411
409
412 # The first rename can cause our current working directory to be removed.
410 # The first rename can cause our current working directory to be removed.
413 # In that case there is nothing left to copy/rename so just quit.
411 # In that case there is nothing left to copy/rename so just quit.
414 try:
412 try:
415 repo.getcwd()
413 repo.getcwd()
416 except OSError:
414 except OSError:
417 return result
415 return result
418
416
419 try:
417 try:
420 try:
418 try:
421 # When we call orig below it creates the standins but we don't add them
419 # When we call orig below it creates the standins but we don't add them
422 # to the dir state until later so lock during that time.
420 # to the dir state until later so lock during that time.
423 wlock = repo.wlock()
421 wlock = repo.wlock()
424
422
425 manifest = repo[None].manifest()
423 manifest = repo[None].manifest()
426 oldmatch = None # for the closure
424 oldmatch = None # for the closure
427 def override_match(ctx, pats=[], opts={}, globbed=False,
425 def override_match(ctx, pats=[], opts={}, globbed=False,
428 default='relpath'):
426 default='relpath'):
429 newpats = []
427 newpats = []
430 # The patterns were previously mangled to add the standin
428 # The patterns were previously mangled to add the standin
431 # directory; we need to remove that now
429 # directory; we need to remove that now
432 for pat in pats:
430 for pat in pats:
433 if match_.patkind(pat) is None and lfutil.shortname in pat:
431 if match_.patkind(pat) is None and lfutil.shortname in pat:
434 newpats.append(pat.replace(lfutil.shortname, ''))
432 newpats.append(pat.replace(lfutil.shortname, ''))
435 else:
433 else:
436 newpats.append(pat)
434 newpats.append(pat)
437 match = oldmatch(ctx, newpats, opts, globbed, default)
435 match = oldmatch(ctx, newpats, opts, globbed, default)
438 m = copy.copy(match)
436 m = copy.copy(match)
439 lfile = lambda f: lfutil.standin(f) in manifest
437 lfile = lambda f: lfutil.standin(f) in manifest
440 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
438 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
441 m._fmap = set(m._files)
439 m._fmap = set(m._files)
442 orig_matchfn = m.matchfn
440 orig_matchfn = m.matchfn
443 m.matchfn = lambda f: (lfutil.isstandin(f) and
441 m.matchfn = lambda f: (lfutil.isstandin(f) and
444 lfile(lfutil.splitstandin(f)) and
442 lfile(lfutil.splitstandin(f)) and
445 orig_matchfn(lfutil.splitstandin(f)) or
443 orig_matchfn(lfutil.splitstandin(f)) or
446 None)
444 None)
447 return m
445 return m
448 oldmatch = installmatchfn(override_match)
446 oldmatch = installmatchfn(override_match)
449 listpats = []
447 listpats = []
450 for pat in pats:
448 for pat in pats:
451 if match_.patkind(pat) is not None:
449 if match_.patkind(pat) is not None:
452 listpats.append(pat)
450 listpats.append(pat)
453 else:
451 else:
454 listpats.append(makestandin(pat))
452 listpats.append(makestandin(pat))
455
453
456 try:
454 try:
457 origcopyfile = util.copyfile
455 origcopyfile = util.copyfile
458 copiedfiles = []
456 copiedfiles = []
459 def override_copyfile(src, dest):
457 def override_copyfile(src, dest):
460 if (lfutil.shortname in src and
458 if (lfutil.shortname in src and
461 dest.startswith(repo.wjoin(lfutil.shortname))):
459 dest.startswith(repo.wjoin(lfutil.shortname))):
462 destlfile = dest.replace(lfutil.shortname, '')
460 destlfile = dest.replace(lfutil.shortname, '')
463 if not opts['force'] and os.path.exists(destlfile):
461 if not opts['force'] and os.path.exists(destlfile):
464 raise IOError('',
462 raise IOError('',
465 _('destination largefile already exists'))
463 _('destination largefile already exists'))
466 copiedfiles.append((src, dest))
464 copiedfiles.append((src, dest))
467 origcopyfile(src, dest)
465 origcopyfile(src, dest)
468
466
469 util.copyfile = override_copyfile
467 util.copyfile = override_copyfile
470 result += orig(ui, repo, listpats, opts, rename)
468 result += orig(ui, repo, listpats, opts, rename)
471 finally:
469 finally:
472 util.copyfile = origcopyfile
470 util.copyfile = origcopyfile
473
471
474 lfdirstate = lfutil.openlfdirstate(ui, repo)
472 lfdirstate = lfutil.openlfdirstate(ui, repo)
475 for (src, dest) in copiedfiles:
473 for (src, dest) in copiedfiles:
476 if (lfutil.shortname in src and
474 if (lfutil.shortname in src and
477 dest.startswith(repo.wjoin(lfutil.shortname))):
475 dest.startswith(repo.wjoin(lfutil.shortname))):
478 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
476 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
479 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
477 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
480 destlfiledir = os.path.dirname(destlfile) or '.'
478 destlfiledir = os.path.dirname(destlfile) or '.'
481 if not os.path.isdir(destlfiledir):
479 if not os.path.isdir(destlfiledir):
482 os.makedirs(destlfiledir)
480 os.makedirs(destlfiledir)
483 if rename:
481 if rename:
484 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
482 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
485 lfdirstate.remove(srclfile)
483 lfdirstate.remove(srclfile)
486 else:
484 else:
487 util.copyfile(srclfile, destlfile)
485 util.copyfile(srclfile, destlfile)
488 lfdirstate.add(destlfile)
486 lfdirstate.add(destlfile)
489 lfdirstate.write()
487 lfdirstate.write()
490 except util.Abort, e:
488 except util.Abort, e:
491 if str(e) != 'no files to copy':
489 if str(e) != 'no files to copy':
492 raise e
490 raise e
493 else:
491 else:
494 nolfiles = True
492 nolfiles = True
495 finally:
493 finally:
496 restorematchfn()
494 restorematchfn()
497 wlock.release()
495 wlock.release()
498
496
499 if nolfiles and nonormalfiles:
497 if nolfiles and nonormalfiles:
500 raise util.Abort(_('no files to copy'))
498 raise util.Abort(_('no files to copy'))
501
499
502 return result
500 return result
503
501
504 # When the user calls revert, we have to be careful to not revert any
502 # When the user calls revert, we have to be careful to not revert any
505 # changes to other largefiles accidentally. This means we have to keep
503 # changes to other largefiles accidentally. This means we have to keep
506 # track of the largefiles that are being reverted so we only pull down
504 # track of the largefiles that are being reverted so we only pull down
507 # the necessary largefiles.
505 # the necessary largefiles.
508 #
506 #
509 # Standins are only updated (to match the hash of largefiles) before
507 # Standins are only updated (to match the hash of largefiles) before
510 # commits. Update the standins then run the original revert, changing
508 # commits. Update the standins then run the original revert, changing
511 # the matcher to hit standins instead of largefiles. Based on the
509 # the matcher to hit standins instead of largefiles. Based on the
512 # resulting standins update the largefiles. Then return the standins
510 # resulting standins update the largefiles. Then return the standins
513 # to their proper state
511 # to their proper state
514 def override_revert(orig, ui, repo, *pats, **opts):
512 def override_revert(orig, ui, repo, *pats, **opts):
515 # Because we put the standins in a bad state (by updating them)
513 # Because we put the standins in a bad state (by updating them)
516 # and then return them to a correct state we need to lock to
514 # and then return them to a correct state we need to lock to
517 # prevent others from changing them in their incorrect state.
515 # prevent others from changing them in their incorrect state.
518 wlock = repo.wlock()
516 wlock = repo.wlock()
519 try:
517 try:
520 lfdirstate = lfutil.openlfdirstate(ui, repo)
518 lfdirstate = lfutil.openlfdirstate(ui, repo)
521 (modified, added, removed, missing, unknown, ignored, clean) = \
519 (modified, added, removed, missing, unknown, ignored, clean) = \
522 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
520 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
523 for lfile in modified:
521 for lfile in modified:
524 lfutil.updatestandin(repo, lfutil.standin(lfile))
522 lfutil.updatestandin(repo, lfutil.standin(lfile))
525
523
526 try:
524 try:
527 ctx = repo[opts.get('rev')]
525 ctx = repo[opts.get('rev')]
528 oldmatch = None # for the closure
526 oldmatch = None # for the closure
529 def override_match(ctx, pats=[], opts={}, globbed=False,
527 def override_match(ctx, pats=[], opts={}, globbed=False,
530 default='relpath'):
528 default='relpath'):
531 match = oldmatch(ctx, pats, opts, globbed, default)
529 match = oldmatch(ctx, pats, opts, globbed, default)
532 m = copy.copy(match)
530 m = copy.copy(match)
533 def tostandin(f):
531 def tostandin(f):
534 if lfutil.standin(f) in ctx or lfutil.standin(f) in ctx:
532 if lfutil.standin(f) in ctx or lfutil.standin(f) in ctx:
535 return lfutil.standin(f)
533 return lfutil.standin(f)
536 elif lfutil.standin(f) in repo[None]:
534 elif lfutil.standin(f) in repo[None]:
537 return None
535 return None
538 return f
536 return f
539 m._files = [tostandin(f) for f in m._files]
537 m._files = [tostandin(f) for f in m._files]
540 m._files = [f for f in m._files if f is not None]
538 m._files = [f for f in m._files if f is not None]
541 m._fmap = set(m._files)
539 m._fmap = set(m._files)
542 orig_matchfn = m.matchfn
540 orig_matchfn = m.matchfn
543 def matchfn(f):
541 def matchfn(f):
544 if lfutil.isstandin(f):
542 if lfutil.isstandin(f):
545 # We need to keep track of what largefiles are being
543 # We need to keep track of what largefiles are being
546 # matched so we know which ones to update later --
544 # matched so we know which ones to update later --
547 # otherwise we accidentally revert changes to other
545 # otherwise we accidentally revert changes to other
548 # largefiles. This is repo-specific, so duckpunch the
546 # largefiles. This is repo-specific, so duckpunch the
549 # repo object to keep the list of largefiles for us
547 # repo object to keep the list of largefiles for us
550 # later.
548 # later.
551 if orig_matchfn(lfutil.splitstandin(f)) and \
549 if orig_matchfn(lfutil.splitstandin(f)) and \
552 (f in repo[None] or f in ctx):
550 (f in repo[None] or f in ctx):
553 lfileslist = getattr(repo, '_lfilestoupdate', [])
551 lfileslist = getattr(repo, '_lfilestoupdate', [])
554 lfileslist.append(lfutil.splitstandin(f))
552 lfileslist.append(lfutil.splitstandin(f))
555 repo._lfilestoupdate = lfileslist
553 repo._lfilestoupdate = lfileslist
556 return True
554 return True
557 else:
555 else:
558 return False
556 return False
559 return orig_matchfn(f)
557 return orig_matchfn(f)
560 m.matchfn = matchfn
558 m.matchfn = matchfn
561 return m
559 return m
562 oldmatch = installmatchfn(override_match)
560 oldmatch = installmatchfn(override_match)
563 scmutil.match
561 scmutil.match
564 matches = override_match(repo[None], pats, opts)
562 matches = override_match(repo[None], pats, opts)
565 orig(ui, repo, *pats, **opts)
563 orig(ui, repo, *pats, **opts)
566 finally:
564 finally:
567 restorematchfn()
565 restorematchfn()
568 lfileslist = getattr(repo, '_lfilestoupdate', [])
566 lfileslist = getattr(repo, '_lfilestoupdate', [])
569 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
567 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
570 printmessage=False)
568 printmessage=False)
571
569
572 # empty out the largefiles list so we start fresh next time
570 # empty out the largefiles list so we start fresh next time
573 repo._lfilestoupdate = []
571 repo._lfilestoupdate = []
574 for lfile in modified:
572 for lfile in modified:
575 if lfile in lfileslist:
573 if lfile in lfileslist:
576 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
574 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
577 in repo['.']:
575 in repo['.']:
578 lfutil.writestandin(repo, lfutil.standin(lfile),
576 lfutil.writestandin(repo, lfutil.standin(lfile),
579 repo['.'][lfile].data().strip(),
577 repo['.'][lfile].data().strip(),
580 'x' in repo['.'][lfile].flags())
578 'x' in repo['.'][lfile].flags())
581 lfdirstate = lfutil.openlfdirstate(ui, repo)
579 lfdirstate = lfutil.openlfdirstate(ui, repo)
582 for lfile in added:
580 for lfile in added:
583 standin = lfutil.standin(lfile)
581 standin = lfutil.standin(lfile)
584 if standin not in ctx and (standin in matches or opts.get('all')):
582 if standin not in ctx and (standin in matches or opts.get('all')):
585 if lfile in lfdirstate:
583 if lfile in lfdirstate:
586 lfdirstate.drop(lfile)
584 lfdirstate.drop(lfile)
587 util.unlinkpath(repo.wjoin(standin))
585 util.unlinkpath(repo.wjoin(standin))
588 lfdirstate.write()
586 lfdirstate.write()
589 finally:
587 finally:
590 wlock.release()
588 wlock.release()
591
589
592 def hg_update(orig, repo, node):
590 def hg_update(orig, repo, node):
593 result = orig(repo, node)
591 result = orig(repo, node)
594 # XXX check if it worked first
592 # XXX check if it worked first
595 lfcommands.updatelfiles(repo.ui, repo)
593 lfcommands.updatelfiles(repo.ui, repo)
596 return result
594 return result
597
595
598 def hg_clean(orig, repo, node, show_stats=True):
596 def hg_clean(orig, repo, node, show_stats=True):
599 result = orig(repo, node, show_stats)
597 result = orig(repo, node, show_stats)
600 lfcommands.updatelfiles(repo.ui, repo)
598 lfcommands.updatelfiles(repo.ui, repo)
601 return result
599 return result
602
600
603 def hg_merge(orig, repo, node, force=None, remind=True):
601 def hg_merge(orig, repo, node, force=None, remind=True):
604 result = orig(repo, node, force, remind)
602 result = orig(repo, node, force, remind)
605 lfcommands.updatelfiles(repo.ui, repo)
603 lfcommands.updatelfiles(repo.ui, repo)
606 return result
604 return result
607
605
608 # When we rebase a repository with remotely changed largefiles, we need to
606 # When we rebase a repository with remotely changed largefiles, we need to
609 # take some extra care so that the largefiles are correctly updated in the
607 # take some extra care so that the largefiles are correctly updated in the
610 # working copy
608 # working copy
611 def override_pull(orig, ui, repo, source=None, **opts):
609 def override_pull(orig, ui, repo, source=None, **opts):
612 if opts.get('rebase', False):
610 if opts.get('rebase', False):
613 repo._isrebasing = True
611 repo._isrebasing = True
614 try:
612 try:
615 if opts.get('update'):
613 if opts.get('update'):
616 del opts['update']
614 del opts['update']
617 ui.debug('--update and --rebase are not compatible, ignoring '
615 ui.debug('--update and --rebase are not compatible, ignoring '
618 'the update flag\n')
616 'the update flag\n')
619 del opts['rebase']
617 del opts['rebase']
620 cmdutil.bailifchanged(repo)
618 cmdutil.bailifchanged(repo)
621 revsprepull = len(repo)
619 revsprepull = len(repo)
622 origpostincoming = commands.postincoming
620 origpostincoming = commands.postincoming
623 def _dummy(*args, **kwargs):
621 def _dummy(*args, **kwargs):
624 pass
622 pass
625 commands.postincoming = _dummy
623 commands.postincoming = _dummy
626 repo.lfpullsource = source
624 repo.lfpullsource = source
627 if not source:
625 if not source:
628 source = 'default'
626 source = 'default'
629 try:
627 try:
630 result = commands.pull(ui, repo, source, **opts)
628 result = commands.pull(ui, repo, source, **opts)
631 finally:
629 finally:
632 commands.postincoming = origpostincoming
630 commands.postincoming = origpostincoming
633 revspostpull = len(repo)
631 revspostpull = len(repo)
634 if revspostpull > revsprepull:
632 if revspostpull > revsprepull:
635 result = result or rebase.rebase(ui, repo)
633 result = result or rebase.rebase(ui, repo)
636 finally:
634 finally:
637 repo._isrebasing = False
635 repo._isrebasing = False
638 else:
636 else:
639 repo.lfpullsource = source
637 repo.lfpullsource = source
640 if not source:
638 if not source:
641 source = 'default'
639 source = 'default'
642 result = orig(ui, repo, source, **opts)
640 result = orig(ui, repo, source, **opts)
643 return result
641 return result
644
642
645 def override_rebase(orig, ui, repo, **opts):
643 def override_rebase(orig, ui, repo, **opts):
646 repo._isrebasing = True
644 repo._isrebasing = True
647 try:
645 try:
648 orig(ui, repo, **opts)
646 orig(ui, repo, **opts)
649 finally:
647 finally:
650 repo._isrebasing = False
648 repo._isrebasing = False
651
649
652 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
650 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
653 prefix=None, mtime=None, subrepos=None):
651 prefix=None, mtime=None, subrepos=None):
654 # No need to lock because we are only reading history and
652 # No need to lock because we are only reading history and
655 # largefile caches, neither of which are modified.
653 # largefile caches, neither of which are modified.
656 lfcommands.cachelfiles(repo.ui, repo, node)
654 lfcommands.cachelfiles(repo.ui, repo, node)
657
655
658 if kind not in archival.archivers:
656 if kind not in archival.archivers:
659 raise util.Abort(_("unknown archive type '%s'") % kind)
657 raise util.Abort(_("unknown archive type '%s'") % kind)
660
658
661 ctx = repo[node]
659 ctx = repo[node]
662
660
663 if kind == 'files':
661 if kind == 'files':
664 if prefix:
662 if prefix:
665 raise util.Abort(
663 raise util.Abort(
666 _('cannot give prefix when archiving to files'))
664 _('cannot give prefix when archiving to files'))
667 else:
665 else:
668 prefix = archival.tidyprefix(dest, kind, prefix)
666 prefix = archival.tidyprefix(dest, kind, prefix)
669
667
670 def write(name, mode, islink, getdata):
668 def write(name, mode, islink, getdata):
671 if matchfn and not matchfn(name):
669 if matchfn and not matchfn(name):
672 return
670 return
673 data = getdata()
671 data = getdata()
674 if decode:
672 if decode:
675 data = repo.wwritedata(name, data)
673 data = repo.wwritedata(name, data)
676 archiver.addfile(prefix + name, mode, islink, data)
674 archiver.addfile(prefix + name, mode, islink, data)
677
675
678 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
676 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
679
677
680 if repo.ui.configbool("ui", "archivemeta", True):
678 if repo.ui.configbool("ui", "archivemeta", True):
681 def metadata():
679 def metadata():
682 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
680 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
683 hex(repo.changelog.node(0)), hex(node), ctx.branch())
681 hex(repo.changelog.node(0)), hex(node), ctx.branch())
684
682
685 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
683 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
686 if repo.tagtype(t) == 'global')
684 if repo.tagtype(t) == 'global')
687 if not tags:
685 if not tags:
688 repo.ui.pushbuffer()
686 repo.ui.pushbuffer()
689 opts = {'template': '{latesttag}\n{latesttagdistance}',
687 opts = {'template': '{latesttag}\n{latesttagdistance}',
690 'style': '', 'patch': None, 'git': None}
688 'style': '', 'patch': None, 'git': None}
691 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
689 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
692 ltags, dist = repo.ui.popbuffer().split('\n')
690 ltags, dist = repo.ui.popbuffer().split('\n')
693 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
691 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
694 tags += 'latesttagdistance: %s\n' % dist
692 tags += 'latesttagdistance: %s\n' % dist
695
693
696 return base + tags
694 return base + tags
697
695
698 write('.hg_archival.txt', 0644, False, metadata)
696 write('.hg_archival.txt', 0644, False, metadata)
699
697
700 for f in ctx:
698 for f in ctx:
701 ff = ctx.flags(f)
699 ff = ctx.flags(f)
702 getdata = ctx[f].data
700 getdata = ctx[f].data
703 if lfutil.isstandin(f):
701 if lfutil.isstandin(f):
704 path = lfutil.findfile(repo, getdata().strip())
702 path = lfutil.findfile(repo, getdata().strip())
705 f = lfutil.splitstandin(f)
703 f = lfutil.splitstandin(f)
706
704
707 def getdatafn():
705 def getdatafn():
708 fd = None
706 fd = None
709 try:
707 try:
710 fd = open(path, 'rb')
708 fd = open(path, 'rb')
711 return fd.read()
709 return fd.read()
712 finally:
710 finally:
713 if fd:
711 if fd:
714 fd.close()
712 fd.close()
715
713
716 getdata = getdatafn
714 getdata = getdatafn
717 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
715 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
718
716
719 if subrepos:
717 if subrepos:
720 for subpath in ctx.substate:
718 for subpath in ctx.substate:
721 sub = ctx.sub(subpath)
719 sub = ctx.sub(subpath)
722 try:
720 try:
723 sub.archive(repo.ui, archiver, prefix)
721 sub.archive(repo.ui, archiver, prefix)
724 except TypeError:
722 except TypeError:
725 sub.archive(archiver, prefix)
723 sub.archive(archiver, prefix)
726
724
727 archiver.done()
725 archiver.done()
728
726
729 # If a largefile is modified, the change is not reflected in its
727 # If a largefile is modified, the change is not reflected in its
730 # standin until a commit. cmdutil.bailifchanged() raises an exception
728 # standin until a commit. cmdutil.bailifchanged() raises an exception
731 # if the repo has uncommitted changes. Wrap it to also check if
729 # if the repo has uncommitted changes. Wrap it to also check if
732 # largefiles were changed. This is used by bisect and backout.
730 # largefiles were changed. This is used by bisect and backout.
733 def override_bailifchanged(orig, repo):
731 def override_bailifchanged(orig, repo):
734 orig(repo)
732 orig(repo)
735 repo.lfstatus = True
733 repo.lfstatus = True
736 modified, added, removed, deleted = repo.status()[:4]
734 modified, added, removed, deleted = repo.status()[:4]
737 repo.lfstatus = False
735 repo.lfstatus = False
738 if modified or added or removed or deleted:
736 if modified or added or removed or deleted:
739 raise util.Abort(_('outstanding uncommitted changes'))
737 raise util.Abort(_('outstanding uncommitted changes'))
740
738
741 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
739 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
742 def override_fetch(orig, ui, repo, *pats, **opts):
740 def override_fetch(orig, ui, repo, *pats, **opts):
743 repo.lfstatus = True
741 repo.lfstatus = True
744 modified, added, removed, deleted = repo.status()[:4]
742 modified, added, removed, deleted = repo.status()[:4]
745 repo.lfstatus = False
743 repo.lfstatus = False
746 if modified or added or removed or deleted:
744 if modified or added or removed or deleted:
747 raise util.Abort(_('outstanding uncommitted changes'))
745 raise util.Abort(_('outstanding uncommitted changes'))
748 return orig(ui, repo, *pats, **opts)
746 return orig(ui, repo, *pats, **opts)
749
747
750 def override_forget(orig, ui, repo, *pats, **opts):
748 def override_forget(orig, ui, repo, *pats, **opts):
751 installnormalfilesmatchfn(repo[None].manifest())
749 installnormalfilesmatchfn(repo[None].manifest())
752 orig(ui, repo, *pats, **opts)
750 orig(ui, repo, *pats, **opts)
753 restorematchfn()
751 restorematchfn()
754 m = scmutil.match(repo[None], pats, opts)
752 m = scmutil.match(repo[None], pats, opts)
755
753
756 try:
754 try:
757 repo.lfstatus = True
755 repo.lfstatus = True
758 s = repo.status(match=m, clean=True)
756 s = repo.status(match=m, clean=True)
759 finally:
757 finally:
760 repo.lfstatus = False
758 repo.lfstatus = False
761 forget = sorted(s[0] + s[1] + s[3] + s[6])
759 forget = sorted(s[0] + s[1] + s[3] + s[6])
762 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
760 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
763
761
764 for f in forget:
762 for f in forget:
765 if lfutil.standin(f) not in repo.dirstate and not \
763 if lfutil.standin(f) not in repo.dirstate and not \
766 os.path.isdir(m.rel(lfutil.standin(f))):
764 os.path.isdir(m.rel(lfutil.standin(f))):
767 ui.warn(_('not removing %s: file is already untracked\n')
765 ui.warn(_('not removing %s: file is already untracked\n')
768 % m.rel(f))
766 % m.rel(f))
769
767
770 for f in forget:
768 for f in forget:
771 if ui.verbose or not m.exact(f):
769 if ui.verbose or not m.exact(f):
772 ui.status(_('removing %s\n') % m.rel(f))
770 ui.status(_('removing %s\n') % m.rel(f))
773
771
774 # Need to lock because standin files are deleted then removed from the
772 # Need to lock because standin files are deleted then removed from the
775 # repository and we could race inbetween.
773 # repository and we could race inbetween.
776 wlock = repo.wlock()
774 wlock = repo.wlock()
777 try:
775 try:
778 lfdirstate = lfutil.openlfdirstate(ui, repo)
776 lfdirstate = lfutil.openlfdirstate(ui, repo)
779 for f in forget:
777 for f in forget:
780 if lfdirstate[f] == 'a':
778 if lfdirstate[f] == 'a':
781 lfdirstate.drop(f)
779 lfdirstate.drop(f)
782 else:
780 else:
783 lfdirstate.remove(f)
781 lfdirstate.remove(f)
784 lfdirstate.write()
782 lfdirstate.write()
785 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
783 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
786 unlink=True)
784 unlink=True)
787 finally:
785 finally:
788 wlock.release()
786 wlock.release()
789
787
790 def getoutgoinglfiles(ui, repo, dest=None, **opts):
788 def getoutgoinglfiles(ui, repo, dest=None, **opts):
791 dest = ui.expandpath(dest or 'default-push', dest or 'default')
789 dest = ui.expandpath(dest or 'default-push', dest or 'default')
792 dest, branches = hg.parseurl(dest, opts.get('branch'))
790 dest, branches = hg.parseurl(dest, opts.get('branch'))
793 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
791 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
794 if revs:
792 if revs:
795 revs = [repo.lookup(rev) for rev in revs]
793 revs = [repo.lookup(rev) for rev in revs]
796
794
797 remoteui = hg.remoteui
795 remoteui = hg.remoteui
798
796
799 try:
797 try:
800 remote = hg.repository(remoteui(repo, opts), dest)
798 remote = hg.repository(remoteui(repo, opts), dest)
801 except error.RepoError:
799 except error.RepoError:
802 return None
800 return None
803 o = lfutil.findoutgoing(repo, remote, False)
801 o = lfutil.findoutgoing(repo, remote, False)
804 if not o:
802 if not o:
805 return None
803 return None
806 o = repo.changelog.nodesbetween(o, revs)[0]
804 o = repo.changelog.nodesbetween(o, revs)[0]
807 if opts.get('newest_first'):
805 if opts.get('newest_first'):
808 o.reverse()
806 o.reverse()
809
807
810 toupload = set()
808 toupload = set()
811 for n in o:
809 for n in o:
812 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
810 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
813 ctx = repo[n]
811 ctx = repo[n]
814 files = set(ctx.files())
812 files = set(ctx.files())
815 if len(parents) == 2:
813 if len(parents) == 2:
816 mc = ctx.manifest()
814 mc = ctx.manifest()
817 mp1 = ctx.parents()[0].manifest()
815 mp1 = ctx.parents()[0].manifest()
818 mp2 = ctx.parents()[1].manifest()
816 mp2 = ctx.parents()[1].manifest()
819 for f in mp1:
817 for f in mp1:
820 if f not in mc:
818 if f not in mc:
821 files.add(f)
819 files.add(f)
822 for f in mp2:
820 for f in mp2:
823 if f not in mc:
821 if f not in mc:
824 files.add(f)
822 files.add(f)
825 for f in mc:
823 for f in mc:
826 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
824 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
827 files.add(f)
825 files.add(f)
828 toupload = toupload.union(
826 toupload = toupload.union(
829 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
827 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
830 return toupload
828 return toupload
831
829
832 def override_outgoing(orig, ui, repo, dest=None, **opts):
830 def override_outgoing(orig, ui, repo, dest=None, **opts):
833 orig(ui, repo, dest, **opts)
831 orig(ui, repo, dest, **opts)
834
832
835 if opts.pop('large', None):
833 if opts.pop('large', None):
836 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
834 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
837 if toupload is None:
835 if toupload is None:
838 ui.status(_('largefiles: No remote repo\n'))
836 ui.status(_('largefiles: No remote repo\n'))
839 else:
837 else:
840 ui.status(_('largefiles to upload:\n'))
838 ui.status(_('largefiles to upload:\n'))
841 for file in toupload:
839 for file in toupload:
842 ui.status(lfutil.splitstandin(file) + '\n')
840 ui.status(lfutil.splitstandin(file) + '\n')
843 ui.status('\n')
841 ui.status('\n')
844
842
845 def override_summary(orig, ui, repo, *pats, **opts):
843 def override_summary(orig, ui, repo, *pats, **opts):
846 orig(ui, repo, *pats, **opts)
844 orig(ui, repo, *pats, **opts)
847
845
848 if opts.pop('large', None):
846 if opts.pop('large', None):
849 toupload = getoutgoinglfiles(ui, repo, None, **opts)
847 toupload = getoutgoinglfiles(ui, repo, None, **opts)
850 if toupload is None:
848 if toupload is None:
851 ui.status(_('largefiles: No remote repo\n'))
849 ui.status(_('largefiles: No remote repo\n'))
852 else:
850 else:
853 ui.status(_('largefiles: %d to upload\n') % len(toupload))
851 ui.status(_('largefiles: %d to upload\n') % len(toupload))
854
852
855 def override_addremove(orig, ui, repo, *pats, **opts):
853 def override_addremove(orig, ui, repo, *pats, **opts):
856 # Check if the parent or child has largefiles; if so, disallow
854 # Check if the parent or child has largefiles; if so, disallow
857 # addremove. If there is a symlink in the manifest then getting
855 # addremove. If there is a symlink in the manifest then getting
858 # the manifest throws an exception: catch it and let addremove
856 # the manifest throws an exception: catch it and let addremove
859 # deal with it.
857 # deal with it.
860 try:
858 try:
861 manifesttip = set(repo['tip'].manifest())
859 manifesttip = set(repo['tip'].manifest())
862 except util.Abort:
860 except util.Abort:
863 manifesttip = set()
861 manifesttip = set()
864 try:
862 try:
865 manifestworking = set(repo[None].manifest())
863 manifestworking = set(repo[None].manifest())
866 except util.Abort:
864 except util.Abort:
867 manifestworking = set()
865 manifestworking = set()
868
866
869 # Manifests are only iterable so turn them into sets then union
867 # Manifests are only iterable so turn them into sets then union
870 for file in manifesttip.union(manifestworking):
868 for file in manifesttip.union(manifestworking):
871 if file.startswith(lfutil.shortname):
869 if file.startswith(lfutil.shortname):
872 raise util.Abort(
870 raise util.Abort(
873 _('addremove cannot be run on a repo with largefiles'))
871 _('addremove cannot be run on a repo with largefiles'))
874
872
875 return orig(ui, repo, *pats, **opts)
873 return orig(ui, repo, *pats, **opts)
876
874
877 # Calling purge with --all will cause the largefiles to be deleted.
875 # Calling purge with --all will cause the largefiles to be deleted.
878 # Override repo.status to prevent this from happening.
876 # Override repo.status to prevent this from happening.
879 def override_purge(orig, ui, repo, *dirs, **opts):
877 def override_purge(orig, ui, repo, *dirs, **opts):
880 oldstatus = repo.status
878 oldstatus = repo.status
881 def override_status(node1='.', node2=None, match=None, ignored=False,
879 def override_status(node1='.', node2=None, match=None, ignored=False,
882 clean=False, unknown=False, listsubrepos=False):
880 clean=False, unknown=False, listsubrepos=False):
883 r = oldstatus(node1, node2, match, ignored, clean, unknown,
881 r = oldstatus(node1, node2, match, ignored, clean, unknown,
884 listsubrepos)
882 listsubrepos)
885 lfdirstate = lfutil.openlfdirstate(ui, repo)
883 lfdirstate = lfutil.openlfdirstate(ui, repo)
886 modified, added, removed, deleted, unknown, ignored, clean = r
884 modified, added, removed, deleted, unknown, ignored, clean = r
887 unknown = [f for f in unknown if lfdirstate[f] == '?']
885 unknown = [f for f in unknown if lfdirstate[f] == '?']
888 ignored = [f for f in ignored if lfdirstate[f] == '?']
886 ignored = [f for f in ignored if lfdirstate[f] == '?']
889 return modified, added, removed, deleted, unknown, ignored, clean
887 return modified, added, removed, deleted, unknown, ignored, clean
890 repo.status = override_status
888 repo.status = override_status
891 orig(ui, repo, *dirs, **opts)
889 orig(ui, repo, *dirs, **opts)
892 repo.status = oldstatus
890 repo.status = oldstatus
893
891
894 def override_rollback(orig, ui, repo, **opts):
892 def override_rollback(orig, ui, repo, **opts):
895 result = orig(ui, repo, **opts)
893 result = orig(ui, repo, **opts)
896 merge.update(repo, node=None, branchmerge=False, force=True,
894 merge.update(repo, node=None, branchmerge=False, force=True,
897 partial=lfutil.isstandin)
895 partial=lfutil.isstandin)
898 lfdirstate = lfutil.openlfdirstate(ui, repo)
896 lfdirstate = lfutil.openlfdirstate(ui, repo)
899 lfiles = lfutil.listlfiles(repo)
897 lfiles = lfutil.listlfiles(repo)
900 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
898 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
901 for file in lfiles:
899 for file in lfiles:
902 if file in oldlfiles:
900 if file in oldlfiles:
903 lfdirstate.normallookup(file)
901 lfdirstate.normallookup(file)
904 else:
902 else:
905 lfdirstate.add(file)
903 lfdirstate.add(file)
906 lfdirstate.write()
904 lfdirstate.write()
907 return result
905 return result
908
906
909 def override_transplant(orig, ui, repo, *revs, **opts):
907 def override_transplant(orig, ui, repo, *revs, **opts):
910 result = orig(ui, repo, *revs, **opts)
908 result = orig(ui, repo, *revs, **opts)
911 lfcommands.updatelfiles(repo.ui, repo)
909 lfcommands.updatelfiles(repo.ui, repo)
912 return result
910 return result
@@ -1,874 +1,886 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > largefiles=
3 > largefiles=
4 > purge=
4 > purge=
5 > rebase=
5 > rebase=
6 > transplant=
6 > transplant=
7 > [largefiles]
7 > [largefiles]
8 > minsize=2
8 > minsize=2
9 > patterns=glob:**.dat
9 > patterns=glob:**.dat
10 > EOF
10 > EOF
11
11
12 Create the repo with a couple of revisions of both large and normal
12 Create the repo with a couple of revisions of both large and normal
13 files, testing that status correctly shows largefiles.
13 files, testing that status correctly shows largefiles.
14
14
15 $ hg init a
15 $ hg init a
16 $ cd a
16 $ cd a
17 $ mkdir sub
17 $ mkdir sub
18 $ echo normal1 > normal1
18 $ echo normal1 > normal1
19 $ echo normal2 > sub/normal2
19 $ echo normal2 > sub/normal2
20 $ echo large1 > large1
20 $ echo large1 > large1
21 $ echo large2 > sub/large2
21 $ echo large2 > sub/large2
22 $ hg add normal1 sub/normal2
22 $ hg add normal1 sub/normal2
23 $ hg add --large large1 sub/large2
23 $ hg add --large large1 sub/large2
24 $ hg commit -m "add files"
24 $ hg commit -m "add files"
25 $ echo normal11 > normal1
25 $ echo normal11 > normal1
26 $ echo normal22 > sub/normal2
26 $ echo normal22 > sub/normal2
27 $ echo large11 > large1
27 $ echo large11 > large1
28 $ echo large22 > sub/large2
28 $ echo large22 > sub/large2
29 $ hg st
29 $ hg st
30 M large1
30 M large1
31 M normal1
31 M normal1
32 M sub/large2
32 M sub/large2
33 M sub/normal2
33 M sub/normal2
34 $ hg commit -m "edit files"
34 $ hg commit -m "edit files"
35
35
36 Commit preserved largefile contents.
36 Commit preserved largefile contents.
37
37
38 $ cat normal1
38 $ cat normal1
39 normal11
39 normal11
40 $ cat large1
40 $ cat large1
41 large11
41 large11
42 $ cat sub/normal2
42 $ cat sub/normal2
43 normal22
43 normal22
44 $ cat sub/large2
44 $ cat sub/large2
45 large22
45 large22
46
46
47 Remove both largefiles and normal files.
47 Remove both largefiles and normal files.
48
48
49 $ hg remove normal1 large1
49 $ hg remove normal1 large1
50 $ hg commit -m "remove files"
50 $ hg commit -m "remove files"
51 $ ls
51 $ ls
52 sub
52 sub
53 $ echo "testlargefile" > large1-test
54 $ hg add --large large1-test
55 $ hg st
56 A large1-test
57 $ hg rm large1-test
58 not removing large1-test: file has been marked for add (use forget to undo)
59 $ hg st
60 A large1-test
61 $ hg forget large1-test
62 $ hg st
63 ? large1-test
64 $ rm large1-test
53
65
54 Copy both largefiles and normal files (testing that status output is correct).
66 Copy both largefiles and normal files (testing that status output is correct).
55
67
56 $ hg cp sub/normal2 normal1
68 $ hg cp sub/normal2 normal1
57 $ hg cp sub/large2 large1
69 $ hg cp sub/large2 large1
58 $ hg st
70 $ hg st
59 A large1
71 A large1
60 A normal1
72 A normal1
61 $ hg commit -m "copy files"
73 $ hg commit -m "copy files"
62 $ cat normal1
74 $ cat normal1
63 normal22
75 normal22
64 $ cat large1
76 $ cat large1
65 large22
77 large22
66
78
67 Test moving largefiles and verify that normal files are also unaffected.
79 Test moving largefiles and verify that normal files are also unaffected.
68
80
69 $ hg mv normal1 normal3
81 $ hg mv normal1 normal3
70 $ hg mv large1 large3
82 $ hg mv large1 large3
71 $ hg mv sub/normal2 sub/normal4
83 $ hg mv sub/normal2 sub/normal4
72 $ hg mv sub/large2 sub/large4
84 $ hg mv sub/large2 sub/large4
73 $ hg commit -m "move files"
85 $ hg commit -m "move files"
74 $ cat normal3
86 $ cat normal3
75 normal22
87 normal22
76 $ cat large3
88 $ cat large3
77 large22
89 large22
78 $ cat sub/normal4
90 $ cat sub/normal4
79 normal22
91 normal22
80 $ cat sub/large4
92 $ cat sub/large4
81 large22
93 large22
82
94
83 Test archiving the various revisions. These hit corner cases known with
95 Test archiving the various revisions. These hit corner cases known with
84 archiving.
96 archiving.
85
97
86 $ hg archive -r 0 ../archive0
98 $ hg archive -r 0 ../archive0
87 $ hg archive -r 1 ../archive1
99 $ hg archive -r 1 ../archive1
88 $ hg archive -r 2 ../archive2
100 $ hg archive -r 2 ../archive2
89 $ hg archive -r 3 ../archive3
101 $ hg archive -r 3 ../archive3
90 $ hg archive -r 4 ../archive4
102 $ hg archive -r 4 ../archive4
91 $ cd ../archive0
103 $ cd ../archive0
92 $ cat normal1
104 $ cat normal1
93 normal1
105 normal1
94 $ cat large1
106 $ cat large1
95 large1
107 large1
96 $ cat sub/normal2
108 $ cat sub/normal2
97 normal2
109 normal2
98 $ cat sub/large2
110 $ cat sub/large2
99 large2
111 large2
100 $ cd ../archive1
112 $ cd ../archive1
101 $ cat normal1
113 $ cat normal1
102 normal11
114 normal11
103 $ cat large1
115 $ cat large1
104 large11
116 large11
105 $ cat sub/normal2
117 $ cat sub/normal2
106 normal22
118 normal22
107 $ cat sub/large2
119 $ cat sub/large2
108 large22
120 large22
109 $ cd ../archive2
121 $ cd ../archive2
110 $ ls
122 $ ls
111 sub
123 sub
112 $ cat sub/normal2
124 $ cat sub/normal2
113 normal22
125 normal22
114 $ cat sub/large2
126 $ cat sub/large2
115 large22
127 large22
116 $ cd ../archive3
128 $ cd ../archive3
117 $ cat normal1
129 $ cat normal1
118 normal22
130 normal22
119 $ cat large1
131 $ cat large1
120 large22
132 large22
121 $ cat sub/normal2
133 $ cat sub/normal2
122 normal22
134 normal22
123 $ cat sub/large2
135 $ cat sub/large2
124 large22
136 large22
125 $ cd ../archive4
137 $ cd ../archive4
126 $ cat normal3
138 $ cat normal3
127 normal22
139 normal22
128 $ cat large3
140 $ cat large3
129 large22
141 large22
130 $ cat sub/normal4
142 $ cat sub/normal4
131 normal22
143 normal22
132 $ cat sub/large4
144 $ cat sub/large4
133 large22
145 large22
134
146
135 Commit corner case: specify files to commit.
147 Commit corner case: specify files to commit.
136
148
137 $ cd ../a
149 $ cd ../a
138 $ echo normal3 > normal3
150 $ echo normal3 > normal3
139 $ echo large3 > large3
151 $ echo large3 > large3
140 $ echo normal4 > sub/normal4
152 $ echo normal4 > sub/normal4
141 $ echo large4 > sub/large4
153 $ echo large4 > sub/large4
142 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
154 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
143 $ cat normal3
155 $ cat normal3
144 normal3
156 normal3
145 $ cat large3
157 $ cat large3
146 large3
158 large3
147 $ cat sub/normal4
159 $ cat sub/normal4
148 normal4
160 normal4
149 $ cat sub/large4
161 $ cat sub/large4
150 large4
162 large4
151
163
152 One more commit corner case: commit from a subdirectory.
164 One more commit corner case: commit from a subdirectory.
153
165
154 $ cd ../a
166 $ cd ../a
155 $ echo normal33 > normal3
167 $ echo normal33 > normal3
156 $ echo large33 > large3
168 $ echo large33 > large3
157 $ echo normal44 > sub/normal4
169 $ echo normal44 > sub/normal4
158 $ echo large44 > sub/large4
170 $ echo large44 > sub/large4
159 $ cd sub
171 $ cd sub
160 $ hg commit -m "edit files yet again"
172 $ hg commit -m "edit files yet again"
161 $ cat ../normal3
173 $ cat ../normal3
162 normal33
174 normal33
163 $ cat ../large3
175 $ cat ../large3
164 large33
176 large33
165 $ cat normal4
177 $ cat normal4
166 normal44
178 normal44
167 $ cat large4
179 $ cat large4
168 large44
180 large44
169
181
170 Committing standins is not allowed.
182 Committing standins is not allowed.
171
183
172 $ cd ..
184 $ cd ..
173 $ echo large3 > large3
185 $ echo large3 > large3
174 $ hg commit .hglf/large3 -m "try to commit standin"
186 $ hg commit .hglf/large3 -m "try to commit standin"
175 abort: file ".hglf/large3" is a largefile standin
187 abort: file ".hglf/large3" is a largefile standin
176 (commit the largefile itself instead)
188 (commit the largefile itself instead)
177 [255]
189 [255]
178
190
179 Corner cases for adding largefiles.
191 Corner cases for adding largefiles.
180
192
181 $ echo large5 > large5
193 $ echo large5 > large5
182 $ hg add --large large5
194 $ hg add --large large5
183 $ hg add --large large5
195 $ hg add --large large5
184 large5 already a largefile
196 large5 already a largefile
185 $ mkdir sub2
197 $ mkdir sub2
186 $ echo large6 > sub2/large6
198 $ echo large6 > sub2/large6
187 $ echo large7 > sub2/large7
199 $ echo large7 > sub2/large7
188 $ hg add --large sub2
200 $ hg add --large sub2
189 adding sub2/large6 as a largefile
201 adding sub2/large6 as a largefile
190 adding sub2/large7 as a largefile
202 adding sub2/large7 as a largefile
191 $ hg st
203 $ hg st
192 M large3
204 M large3
193 A large5
205 A large5
194 A sub2/large6
206 A sub2/large6
195 A sub2/large7
207 A sub2/large7
196
208
197 Config settings (pattern **.dat, minsize 2 MB) are respected.
209 Config settings (pattern **.dat, minsize 2 MB) are respected.
198
210
199 $ echo testdata > test.dat
211 $ echo testdata > test.dat
200 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
212 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
201 $ hg add
213 $ hg add
202 adding reallylarge as a largefile
214 adding reallylarge as a largefile
203 adding test.dat as a largefile
215 adding test.dat as a largefile
204
216
205 Test that minsize and --lfsize handle float values;
217 Test that minsize and --lfsize handle float values;
206 also tests that --lfsize overrides largefiles.minsize.
218 also tests that --lfsize overrides largefiles.minsize.
207 (0.250 MB = 256 kB = 262144 B)
219 (0.250 MB = 256 kB = 262144 B)
208
220
209 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
221 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
210 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
222 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
211 $ hg --config largefiles.minsize=.25 add
223 $ hg --config largefiles.minsize=.25 add
212 adding ratherlarge as a largefile
224 adding ratherlarge as a largefile
213 adding medium
225 adding medium
214 $ hg forget medium
226 $ hg forget medium
215 $ hg --config largefiles.minsize=.25 add --lfsize=.125
227 $ hg --config largefiles.minsize=.25 add --lfsize=.125
216 adding medium as a largefile
228 adding medium as a largefile
217 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
229 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
218 $ hg --config largefiles.minsize=.25 add --lfsize=.125
230 $ hg --config largefiles.minsize=.25 add --lfsize=.125
219 adding notlarge
231 adding notlarge
220 $ hg forget notlarge
232 $ hg forget notlarge
221
233
222 Test forget on largefiles.
234 Test forget on largefiles.
223
235
224 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
236 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
225 $ hg st
237 $ hg st
226 A sub2/large6
238 A sub2/large6
227 A sub2/large7
239 A sub2/large7
228 R large3
240 R large3
229 ? large5
241 ? large5
230 ? medium
242 ? medium
231 ? notlarge
243 ? notlarge
232 ? ratherlarge
244 ? ratherlarge
233 ? reallylarge
245 ? reallylarge
234 ? test.dat
246 ? test.dat
235 $ hg commit -m "add/edit more largefiles"
247 $ hg commit -m "add/edit more largefiles"
236 $ hg st
248 $ hg st
237 ? large3
249 ? large3
238 ? large5
250 ? large5
239 ? medium
251 ? medium
240 ? notlarge
252 ? notlarge
241 ? ratherlarge
253 ? ratherlarge
242 ? reallylarge
254 ? reallylarge
243 ? test.dat
255 ? test.dat
244
256
245 Purge with largefiles: verify that largefiles are still in the working
257 Purge with largefiles: verify that largefiles are still in the working
246 dir after a purge.
258 dir after a purge.
247
259
248 $ hg purge --all
260 $ hg purge --all
249 $ cat sub/large4
261 $ cat sub/large4
250 large44
262 large44
251 $ cat sub2/large6
263 $ cat sub2/large6
252 large6
264 large6
253 $ cat sub2/large7
265 $ cat sub2/large7
254 large7
266 large7
255
267
256 Clone a largefiles repo.
268 Clone a largefiles repo.
257
269
258 $ hg clone . ../b
270 $ hg clone . ../b
259 updating to branch default
271 updating to branch default
260 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 getting changed largefiles
273 getting changed largefiles
262 3 largefiles updated, 0 removed
274 3 largefiles updated, 0 removed
263 $ cd ../b
275 $ cd ../b
264 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
276 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
265 7:daea875e9014 add/edit more largefiles
277 7:daea875e9014 add/edit more largefiles
266 6:4355d653f84f edit files yet again
278 6:4355d653f84f edit files yet again
267 5:9d5af5072dbd edit files again
279 5:9d5af5072dbd edit files again
268 4:74c02385b94c move files
280 4:74c02385b94c move files
269 3:9e8fbc4bce62 copy files
281 3:9e8fbc4bce62 copy files
270 2:51a0ae4d5864 remove files
282 2:51a0ae4d5864 remove files
271 1:ce8896473775 edit files
283 1:ce8896473775 edit files
272 0:30d30fe6a5be add files
284 0:30d30fe6a5be add files
273 $ cat normal3
285 $ cat normal3
274 normal33
286 normal33
275 $ cat sub/normal4
287 $ cat sub/normal4
276 normal44
288 normal44
277 $ cat sub/large4
289 $ cat sub/large4
278 large44
290 large44
279 $ cat sub2/large6
291 $ cat sub2/large6
280 large6
292 large6
281 $ cat sub2/large7
293 $ cat sub2/large7
282 large7
294 large7
283 $ cd ..
295 $ cd ..
284 $ hg clone a -r 3 c
296 $ hg clone a -r 3 c
285 adding changesets
297 adding changesets
286 adding manifests
298 adding manifests
287 adding file changes
299 adding file changes
288 added 4 changesets with 10 changes to 4 files
300 added 4 changesets with 10 changes to 4 files
289 updating to branch default
301 updating to branch default
290 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
302 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 getting changed largefiles
303 getting changed largefiles
292 2 largefiles updated, 0 removed
304 2 largefiles updated, 0 removed
293 $ cd c
305 $ cd c
294 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
306 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
295 3:9e8fbc4bce62 copy files
307 3:9e8fbc4bce62 copy files
296 2:51a0ae4d5864 remove files
308 2:51a0ae4d5864 remove files
297 1:ce8896473775 edit files
309 1:ce8896473775 edit files
298 0:30d30fe6a5be add files
310 0:30d30fe6a5be add files
299 $ cat normal1
311 $ cat normal1
300 normal22
312 normal22
301 $ cat large1
313 $ cat large1
302 large22
314 large22
303 $ cat sub/normal2
315 $ cat sub/normal2
304 normal22
316 normal22
305 $ cat sub/large2
317 $ cat sub/large2
306 large22
318 large22
307
319
308 Old revisions of a clone have correct largefiles content (this also
320 Old revisions of a clone have correct largefiles content (this also
309 tests update).
321 tests update).
310
322
311 $ hg update -r 1
323 $ hg update -r 1
312 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 getting changed largefiles
325 getting changed largefiles
314 1 largefiles updated, 0 removed
326 1 largefiles updated, 0 removed
315 $ cat large1
327 $ cat large1
316 large11
328 large11
317 $ cat sub/large2
329 $ cat sub/large2
318 large22
330 large22
319
331
320 Rebasing between two repositories does not revert largefiles to old
332 Rebasing between two repositories does not revert largefiles to old
321 revisions (this was a very bad bug that took a lot of work to fix).
333 revisions (this was a very bad bug that took a lot of work to fix).
322
334
323 $ cd ..
335 $ cd ..
324 $ hg clone a d
336 $ hg clone a d
325 updating to branch default
337 updating to branch default
326 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 getting changed largefiles
339 getting changed largefiles
328 3 largefiles updated, 0 removed
340 3 largefiles updated, 0 removed
329 $ cd b
341 $ cd b
330 $ echo large4-modified > sub/large4
342 $ echo large4-modified > sub/large4
331 $ echo normal3-modified > normal3
343 $ echo normal3-modified > normal3
332 $ hg commit -m "modify normal file and largefile in repo b"
344 $ hg commit -m "modify normal file and largefile in repo b"
333 $ cd ../d
345 $ cd ../d
334 $ echo large6-modified > sub2/large6
346 $ echo large6-modified > sub2/large6
335 $ echo normal4-modified > sub/normal4
347 $ echo normal4-modified > sub/normal4
336 $ hg commit -m "modify normal file largefile in repo d"
348 $ hg commit -m "modify normal file largefile in repo d"
337 $ cd ..
349 $ cd ..
338 $ hg clone d e
350 $ hg clone d e
339 updating to branch default
351 updating to branch default
340 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
352 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 getting changed largefiles
353 getting changed largefiles
342 3 largefiles updated, 0 removed
354 3 largefiles updated, 0 removed
343 $ cd d
355 $ cd d
344 $ hg pull --rebase ../b
356 $ hg pull --rebase ../b
345 pulling from ../b
357 pulling from ../b
346 searching for changes
358 searching for changes
347 adding changesets
359 adding changesets
348 adding manifests
360 adding manifests
349 adding file changes
361 adding file changes
350 added 1 changesets with 2 changes to 2 files (+1 heads)
362 added 1 changesets with 2 changes to 2 files (+1 heads)
351 getting changed largefiles
363 getting changed largefiles
352 1 largefiles updated, 0 removed
364 1 largefiles updated, 0 removed
353 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
365 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
354 nothing to rebase
366 nothing to rebase
355 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
367 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
356 9:598410d3eb9a modify normal file largefile in repo d
368 9:598410d3eb9a modify normal file largefile in repo d
357 8:a381d2c8c80e modify normal file and largefile in repo b
369 8:a381d2c8c80e modify normal file and largefile in repo b
358 7:daea875e9014 add/edit more largefiles
370 7:daea875e9014 add/edit more largefiles
359 6:4355d653f84f edit files yet again
371 6:4355d653f84f edit files yet again
360 5:9d5af5072dbd edit files again
372 5:9d5af5072dbd edit files again
361 4:74c02385b94c move files
373 4:74c02385b94c move files
362 3:9e8fbc4bce62 copy files
374 3:9e8fbc4bce62 copy files
363 2:51a0ae4d5864 remove files
375 2:51a0ae4d5864 remove files
364 1:ce8896473775 edit files
376 1:ce8896473775 edit files
365 0:30d30fe6a5be add files
377 0:30d30fe6a5be add files
366 $ cat normal3
378 $ cat normal3
367 normal3-modified
379 normal3-modified
368 $ cat sub/normal4
380 $ cat sub/normal4
369 normal4-modified
381 normal4-modified
370 $ cat sub/large4
382 $ cat sub/large4
371 large4-modified
383 large4-modified
372 $ cat sub2/large6
384 $ cat sub2/large6
373 large6-modified
385 large6-modified
374 $ cat sub2/large7
386 $ cat sub2/large7
375 large7
387 large7
376 $ cd ../e
388 $ cd ../e
377 $ hg pull ../b
389 $ hg pull ../b
378 pulling from ../b
390 pulling from ../b
379 searching for changes
391 searching for changes
380 adding changesets
392 adding changesets
381 adding manifests
393 adding manifests
382 adding file changes
394 adding file changes
383 added 1 changesets with 2 changes to 2 files (+1 heads)
395 added 1 changesets with 2 changes to 2 files (+1 heads)
384 (run 'hg heads' to see heads, 'hg merge' to merge)
396 (run 'hg heads' to see heads, 'hg merge' to merge)
385 $ hg rebase
397 $ hg rebase
386 getting changed largefiles
398 getting changed largefiles
387 1 largefiles updated, 0 removed
399 1 largefiles updated, 0 removed
388 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
400 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
389 $ hg log
401 $ hg log
390 changeset: 9:598410d3eb9a
402 changeset: 9:598410d3eb9a
391 tag: tip
403 tag: tip
392 user: test
404 user: test
393 date: Thu Jan 01 00:00:00 1970 +0000
405 date: Thu Jan 01 00:00:00 1970 +0000
394 summary: modify normal file largefile in repo d
406 summary: modify normal file largefile in repo d
395
407
396 changeset: 8:a381d2c8c80e
408 changeset: 8:a381d2c8c80e
397 user: test
409 user: test
398 date: Thu Jan 01 00:00:00 1970 +0000
410 date: Thu Jan 01 00:00:00 1970 +0000
399 summary: modify normal file and largefile in repo b
411 summary: modify normal file and largefile in repo b
400
412
401 changeset: 7:daea875e9014
413 changeset: 7:daea875e9014
402 user: test
414 user: test
403 date: Thu Jan 01 00:00:00 1970 +0000
415 date: Thu Jan 01 00:00:00 1970 +0000
404 summary: add/edit more largefiles
416 summary: add/edit more largefiles
405
417
406 changeset: 6:4355d653f84f
418 changeset: 6:4355d653f84f
407 user: test
419 user: test
408 date: Thu Jan 01 00:00:00 1970 +0000
420 date: Thu Jan 01 00:00:00 1970 +0000
409 summary: edit files yet again
421 summary: edit files yet again
410
422
411 changeset: 5:9d5af5072dbd
423 changeset: 5:9d5af5072dbd
412 user: test
424 user: test
413 date: Thu Jan 01 00:00:00 1970 +0000
425 date: Thu Jan 01 00:00:00 1970 +0000
414 summary: edit files again
426 summary: edit files again
415
427
416 changeset: 4:74c02385b94c
428 changeset: 4:74c02385b94c
417 user: test
429 user: test
418 date: Thu Jan 01 00:00:00 1970 +0000
430 date: Thu Jan 01 00:00:00 1970 +0000
419 summary: move files
431 summary: move files
420
432
421 changeset: 3:9e8fbc4bce62
433 changeset: 3:9e8fbc4bce62
422 user: test
434 user: test
423 date: Thu Jan 01 00:00:00 1970 +0000
435 date: Thu Jan 01 00:00:00 1970 +0000
424 summary: copy files
436 summary: copy files
425
437
426 changeset: 2:51a0ae4d5864
438 changeset: 2:51a0ae4d5864
427 user: test
439 user: test
428 date: Thu Jan 01 00:00:00 1970 +0000
440 date: Thu Jan 01 00:00:00 1970 +0000
429 summary: remove files
441 summary: remove files
430
442
431 changeset: 1:ce8896473775
443 changeset: 1:ce8896473775
432 user: test
444 user: test
433 date: Thu Jan 01 00:00:00 1970 +0000
445 date: Thu Jan 01 00:00:00 1970 +0000
434 summary: edit files
446 summary: edit files
435
447
436 changeset: 0:30d30fe6a5be
448 changeset: 0:30d30fe6a5be
437 user: test
449 user: test
438 date: Thu Jan 01 00:00:00 1970 +0000
450 date: Thu Jan 01 00:00:00 1970 +0000
439 summary: add files
451 summary: add files
440
452
441 $ cat normal3
453 $ cat normal3
442 normal3-modified
454 normal3-modified
443 $ cat sub/normal4
455 $ cat sub/normal4
444 normal4-modified
456 normal4-modified
445 $ cat sub/large4
457 $ cat sub/large4
446 large4-modified
458 large4-modified
447 $ cat sub2/large6
459 $ cat sub2/large6
448 large6-modified
460 large6-modified
449 $ cat sub2/large7
461 $ cat sub2/large7
450 large7
462 large7
451
463
452 Rollback on largefiles.
464 Rollback on largefiles.
453
465
454 $ echo large4-modified-again > sub/large4
466 $ echo large4-modified-again > sub/large4
455 $ hg commit -m "Modify large4 again"
467 $ hg commit -m "Modify large4 again"
456 $ hg rollback
468 $ hg rollback
457 repository tip rolled back to revision 9 (undo commit)
469 repository tip rolled back to revision 9 (undo commit)
458 working directory now based on revision 9
470 working directory now based on revision 9
459 $ hg st
471 $ hg st
460 M sub/large4
472 M sub/large4
461 $ hg log
473 $ hg log
462 changeset: 9:598410d3eb9a
474 changeset: 9:598410d3eb9a
463 tag: tip
475 tag: tip
464 user: test
476 user: test
465 date: Thu Jan 01 00:00:00 1970 +0000
477 date: Thu Jan 01 00:00:00 1970 +0000
466 summary: modify normal file largefile in repo d
478 summary: modify normal file largefile in repo d
467
479
468 changeset: 8:a381d2c8c80e
480 changeset: 8:a381d2c8c80e
469 user: test
481 user: test
470 date: Thu Jan 01 00:00:00 1970 +0000
482 date: Thu Jan 01 00:00:00 1970 +0000
471 summary: modify normal file and largefile in repo b
483 summary: modify normal file and largefile in repo b
472
484
473 changeset: 7:daea875e9014
485 changeset: 7:daea875e9014
474 user: test
486 user: test
475 date: Thu Jan 01 00:00:00 1970 +0000
487 date: Thu Jan 01 00:00:00 1970 +0000
476 summary: add/edit more largefiles
488 summary: add/edit more largefiles
477
489
478 changeset: 6:4355d653f84f
490 changeset: 6:4355d653f84f
479 user: test
491 user: test
480 date: Thu Jan 01 00:00:00 1970 +0000
492 date: Thu Jan 01 00:00:00 1970 +0000
481 summary: edit files yet again
493 summary: edit files yet again
482
494
483 changeset: 5:9d5af5072dbd
495 changeset: 5:9d5af5072dbd
484 user: test
496 user: test
485 date: Thu Jan 01 00:00:00 1970 +0000
497 date: Thu Jan 01 00:00:00 1970 +0000
486 summary: edit files again
498 summary: edit files again
487
499
488 changeset: 4:74c02385b94c
500 changeset: 4:74c02385b94c
489 user: test
501 user: test
490 date: Thu Jan 01 00:00:00 1970 +0000
502 date: Thu Jan 01 00:00:00 1970 +0000
491 summary: move files
503 summary: move files
492
504
493 changeset: 3:9e8fbc4bce62
505 changeset: 3:9e8fbc4bce62
494 user: test
506 user: test
495 date: Thu Jan 01 00:00:00 1970 +0000
507 date: Thu Jan 01 00:00:00 1970 +0000
496 summary: copy files
508 summary: copy files
497
509
498 changeset: 2:51a0ae4d5864
510 changeset: 2:51a0ae4d5864
499 user: test
511 user: test
500 date: Thu Jan 01 00:00:00 1970 +0000
512 date: Thu Jan 01 00:00:00 1970 +0000
501 summary: remove files
513 summary: remove files
502
514
503 changeset: 1:ce8896473775
515 changeset: 1:ce8896473775
504 user: test
516 user: test
505 date: Thu Jan 01 00:00:00 1970 +0000
517 date: Thu Jan 01 00:00:00 1970 +0000
506 summary: edit files
518 summary: edit files
507
519
508 changeset: 0:30d30fe6a5be
520 changeset: 0:30d30fe6a5be
509 user: test
521 user: test
510 date: Thu Jan 01 00:00:00 1970 +0000
522 date: Thu Jan 01 00:00:00 1970 +0000
511 summary: add files
523 summary: add files
512
524
513 $ cat sub/large4
525 $ cat sub/large4
514 large4-modified-again
526 large4-modified-again
515
527
516 "update --check" refuses to update with uncommitted changes.
528 "update --check" refuses to update with uncommitted changes.
517 $ hg update --check 8
529 $ hg update --check 8
518 abort: uncommitted local changes
530 abort: uncommitted local changes
519 [255]
531 [255]
520
532
521 "update --clean" leaves correct largefiles in working copy.
533 "update --clean" leaves correct largefiles in working copy.
522
534
523 $ hg update --clean
535 $ hg update --clean
524 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
536 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
525 getting changed largefiles
537 getting changed largefiles
526 1 largefiles updated, 0 removed
538 1 largefiles updated, 0 removed
527 $ cat normal3
539 $ cat normal3
528 normal3-modified
540 normal3-modified
529 $ cat sub/normal4
541 $ cat sub/normal4
530 normal4-modified
542 normal4-modified
531 $ cat sub/large4
543 $ cat sub/large4
532 large4-modified
544 large4-modified
533 $ cat sub2/large6
545 $ cat sub2/large6
534 large6-modified
546 large6-modified
535 $ cat sub2/large7
547 $ cat sub2/large7
536 large7
548 large7
537
549
538 Now "update check" is happy.
550 Now "update check" is happy.
539 $ hg update --check 8
551 $ hg update --check 8
540 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
541 getting changed largefiles
553 getting changed largefiles
542 1 largefiles updated, 0 removed
554 1 largefiles updated, 0 removed
543 $ hg update --check
555 $ hg update --check
544 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
545 getting changed largefiles
557 getting changed largefiles
546 1 largefiles updated, 0 removed
558 1 largefiles updated, 0 removed
547
559
548 "revert" works on largefiles (and normal files too).
560 "revert" works on largefiles (and normal files too).
549 $ echo hack3 >> normal3
561 $ echo hack3 >> normal3
550 $ echo hack4 >> sub/normal4
562 $ echo hack4 >> sub/normal4
551 $ echo hack4 >> sub/large4
563 $ echo hack4 >> sub/large4
552 $ hg rm sub2/large6
564 $ hg rm sub2/large6
553 $ echo new >> sub2/large8
565 $ echo new >> sub2/large8
554 $ hg add --large sub2/large8
566 $ hg add --large sub2/large8
555 # XXX we don't really want to report that we're reverting the standin;
567 # XXX we don't really want to report that we're reverting the standin;
556 # that's just an implementation detail. But I don't see an obvious fix. ;-(
568 # that's just an implementation detail. But I don't see an obvious fix. ;-(
557 $ hg revert sub
569 $ hg revert sub
558 reverting .hglf/sub/large4
570 reverting .hglf/sub/large4
559 reverting sub/normal4
571 reverting sub/normal4
560 $ hg status
572 $ hg status
561 M normal3
573 M normal3
562 A sub2/large8
574 A sub2/large8
563 R sub2/large6
575 R sub2/large6
564 ? sub/large4.orig
576 ? sub/large4.orig
565 ? sub/normal4.orig
577 ? sub/normal4.orig
566 $ cat sub/normal4
578 $ cat sub/normal4
567 normal4-modified
579 normal4-modified
568 $ cat sub/large4
580 $ cat sub/large4
569 large4-modified
581 large4-modified
570 $ hg revert -a --no-backup
582 $ hg revert -a --no-backup
571 undeleting .hglf/sub2/large6
583 undeleting .hglf/sub2/large6
572 forgetting .hglf/sub2/large8
584 forgetting .hglf/sub2/large8
573 reverting normal3
585 reverting normal3
574 $ hg status
586 $ hg status
575 ? sub/large4.orig
587 ? sub/large4.orig
576 ? sub/normal4.orig
588 ? sub/normal4.orig
577 ? sub2/large8
589 ? sub2/large8
578 $ cat normal3
590 $ cat normal3
579 normal3-modified
591 normal3-modified
580 $ cat sub2/large6
592 $ cat sub2/large6
581 large6-modified
593 large6-modified
582 $ rm sub/*.orig sub2/large8
594 $ rm sub/*.orig sub2/large8
583
595
584 revert some files to an older revision
596 revert some files to an older revision
585 $ hg revert --no-backup -r 8 sub2
597 $ hg revert --no-backup -r 8 sub2
586 reverting .hglf/sub2/large6
598 reverting .hglf/sub2/large6
587 $ cat sub2/large6
599 $ cat sub2/large6
588 large6
600 large6
589 $ hg revert --no-backup sub2
601 $ hg revert --no-backup sub2
590 reverting .hglf/sub2/large6
602 reverting .hglf/sub2/large6
591 $ hg status
603 $ hg status
592
604
593 "verify --large" actually verifies largefiles
605 "verify --large" actually verifies largefiles
594
606
595 $ hg verify --large
607 $ hg verify --large
596 checking changesets
608 checking changesets
597 checking manifests
609 checking manifests
598 crosschecking files in changesets and manifests
610 crosschecking files in changesets and manifests
599 checking files
611 checking files
600 10 files, 10 changesets, 28 total revisions
612 10 files, 10 changesets, 28 total revisions
601 searching 1 changesets for largefiles
613 searching 1 changesets for largefiles
602 verified existence of 3 revisions of 3 largefiles
614 verified existence of 3 revisions of 3 largefiles
603
615
604 Merging does not revert to old versions of largefiles (this has also
616 Merging does not revert to old versions of largefiles (this has also
605 been very problematic).
617 been very problematic).
606
618
607 $ cd ..
619 $ cd ..
608 $ hg clone -r 7 e f
620 $ hg clone -r 7 e f
609 adding changesets
621 adding changesets
610 adding manifests
622 adding manifests
611 adding file changes
623 adding file changes
612 added 8 changesets with 24 changes to 10 files
624 added 8 changesets with 24 changes to 10 files
613 updating to branch default
625 updating to branch default
614 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
626 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
615 getting changed largefiles
627 getting changed largefiles
616 3 largefiles updated, 0 removed
628 3 largefiles updated, 0 removed
617 $ cd f
629 $ cd f
618 $ echo "large4-merge-test" > sub/large4
630 $ echo "large4-merge-test" > sub/large4
619 $ hg commit -m "Modify large4 to test merge"
631 $ hg commit -m "Modify large4 to test merge"
620 $ hg pull ../e
632 $ hg pull ../e
621 pulling from ../e
633 pulling from ../e
622 searching for changes
634 searching for changes
623 adding changesets
635 adding changesets
624 adding manifests
636 adding manifests
625 adding file changes
637 adding file changes
626 added 2 changesets with 4 changes to 4 files (+1 heads)
638 added 2 changesets with 4 changes to 4 files (+1 heads)
627 (run 'hg heads' to see heads, 'hg merge' to merge)
639 (run 'hg heads' to see heads, 'hg merge' to merge)
628 $ hg merge
640 $ hg merge
629 merging sub/large4
641 merging sub/large4
630 largefile sub/large4 has a merge conflict
642 largefile sub/large4 has a merge conflict
631 keep (l)ocal or take (o)ther? l
643 keep (l)ocal or take (o)ther? l
632 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
644 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
633 (branch merge, don't forget to commit)
645 (branch merge, don't forget to commit)
634 getting changed largefiles
646 getting changed largefiles
635 1 largefiles updated, 0 removed
647 1 largefiles updated, 0 removed
636 $ hg commit -m "Merge repos e and f"
648 $ hg commit -m "Merge repos e and f"
637 $ cat normal3
649 $ cat normal3
638 normal3-modified
650 normal3-modified
639 $ cat sub/normal4
651 $ cat sub/normal4
640 normal4-modified
652 normal4-modified
641 $ cat sub/large4
653 $ cat sub/large4
642 large4-merge-test
654 large4-merge-test
643 $ cat sub2/large6
655 $ cat sub2/large6
644 large6-modified
656 large6-modified
645 $ cat sub2/large7
657 $ cat sub2/large7
646 large7
658 large7
647
659
648 Test status after merging with a branch that introduces a new largefile:
660 Test status after merging with a branch that introduces a new largefile:
649
661
650 $ echo large > large
662 $ echo large > large
651 $ hg add --large large
663 $ hg add --large large
652 $ hg commit -m 'add largefile'
664 $ hg commit -m 'add largefile'
653 $ hg update -q ".^"
665 $ hg update -q ".^"
654 $ echo change >> normal3
666 $ echo change >> normal3
655 $ hg commit -m 'some change'
667 $ hg commit -m 'some change'
656 created new head
668 created new head
657 $ hg merge
669 $ hg merge
658 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
670 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
659 (branch merge, don't forget to commit)
671 (branch merge, don't forget to commit)
660 getting changed largefiles
672 getting changed largefiles
661 1 largefiles updated, 0 removed
673 1 largefiles updated, 0 removed
662 $ hg status
674 $ hg status
663 M large
675 M large
664
676
665 Test that a normal file and a largefile with the same name and path cannot
677 Test that a normal file and a largefile with the same name and path cannot
666 coexist.
678 coexist.
667
679
668 $ rm sub2/large7
680 $ rm sub2/large7
669 $ echo "largeasnormal" > sub2/large7
681 $ echo "largeasnormal" > sub2/large7
670 $ hg add sub2/large7
682 $ hg add sub2/large7
671 sub2/large7 already a largefile
683 sub2/large7 already a largefile
672
684
673 Test that transplanting a largefile change works correctly.
685 Test that transplanting a largefile change works correctly.
674
686
675 $ cd ..
687 $ cd ..
676 $ hg clone -r 8 d g
688 $ hg clone -r 8 d g
677 adding changesets
689 adding changesets
678 adding manifests
690 adding manifests
679 adding file changes
691 adding file changes
680 added 9 changesets with 26 changes to 10 files
692 added 9 changesets with 26 changes to 10 files
681 updating to branch default
693 updating to branch default
682 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
694 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
683 getting changed largefiles
695 getting changed largefiles
684 3 largefiles updated, 0 removed
696 3 largefiles updated, 0 removed
685 $ cd g
697 $ cd g
686 $ hg transplant -s ../d 598410d3eb9a
698 $ hg transplant -s ../d 598410d3eb9a
687 searching for changes
699 searching for changes
688 searching for changes
700 searching for changes
689 adding changesets
701 adding changesets
690 adding manifests
702 adding manifests
691 adding file changes
703 adding file changes
692 added 1 changesets with 2 changes to 2 files
704 added 1 changesets with 2 changes to 2 files
693 getting changed largefiles
705 getting changed largefiles
694 1 largefiles updated, 0 removed
706 1 largefiles updated, 0 removed
695 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
707 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
696 9:598410d3eb9a modify normal file largefile in repo d
708 9:598410d3eb9a modify normal file largefile in repo d
697 8:a381d2c8c80e modify normal file and largefile in repo b
709 8:a381d2c8c80e modify normal file and largefile in repo b
698 7:daea875e9014 add/edit more largefiles
710 7:daea875e9014 add/edit more largefiles
699 6:4355d653f84f edit files yet again
711 6:4355d653f84f edit files yet again
700 5:9d5af5072dbd edit files again
712 5:9d5af5072dbd edit files again
701 4:74c02385b94c move files
713 4:74c02385b94c move files
702 3:9e8fbc4bce62 copy files
714 3:9e8fbc4bce62 copy files
703 2:51a0ae4d5864 remove files
715 2:51a0ae4d5864 remove files
704 1:ce8896473775 edit files
716 1:ce8896473775 edit files
705 0:30d30fe6a5be add files
717 0:30d30fe6a5be add files
706 $ cat normal3
718 $ cat normal3
707 normal3-modified
719 normal3-modified
708 $ cat sub/normal4
720 $ cat sub/normal4
709 normal4-modified
721 normal4-modified
710 $ cat sub/large4
722 $ cat sub/large4
711 large4-modified
723 large4-modified
712 $ cat sub2/large6
724 $ cat sub2/large6
713 large6-modified
725 large6-modified
714 $ cat sub2/large7
726 $ cat sub2/large7
715 large7
727 large7
716
728
717 Test that renaming a largefile results in correct output for status
729 Test that renaming a largefile results in correct output for status
718
730
719 $ hg rename sub/large4 large4-renamed
731 $ hg rename sub/large4 large4-renamed
720 $ hg st
732 $ hg st
721 A large4-renamed
733 A large4-renamed
722 R sub/large4
734 R sub/large4
723 $ hg commit -m "test rename output"
735 $ hg commit -m "test rename output"
724 $ cat large4-renamed
736 $ cat large4-renamed
725 large4-modified
737 large4-modified
726 $ cd sub2
738 $ cd sub2
727 $ hg rename large6 large6-renamed
739 $ hg rename large6 large6-renamed
728 $ hg st
740 $ hg st
729 A sub2/large6-renamed
741 A sub2/large6-renamed
730 R sub2/large6
742 R sub2/large6
731 $ cd ../..
743 $ cd ../..
732
744
733 vanilla clients not locked out from largefiles servers on vanilla repos
745 vanilla clients not locked out from largefiles servers on vanilla repos
734 $ mkdir r1
746 $ mkdir r1
735 $ cd r1
747 $ cd r1
736 $ hg init
748 $ hg init
737 $ echo c1 > f1
749 $ echo c1 > f1
738 $ hg add f1
750 $ hg add f1
739 $ hg com -m "m1"
751 $ hg com -m "m1"
740 $ cd ..
752 $ cd ..
741 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
753 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
742 $ cat hg.pid >> $DAEMON_PIDS
754 $ cat hg.pid >> $DAEMON_PIDS
743 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
755 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
744 requesting all changes
756 requesting all changes
745 adding changesets
757 adding changesets
746 adding manifests
758 adding manifests
747 adding file changes
759 adding file changes
748 added 1 changesets with 1 changes to 1 files
760 added 1 changesets with 1 changes to 1 files
749 updating to branch default
761 updating to branch default
750 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
762 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
751
763
752 largefiles clients still work with vanilla servers
764 largefiles clients still work with vanilla servers
753 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
765 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
754 $ cat hg.pid >> $DAEMON_PIDS
766 $ cat hg.pid >> $DAEMON_PIDS
755 $ hg clone http://localhost:$HGPORT1 r3
767 $ hg clone http://localhost:$HGPORT1 r3
756 requesting all changes
768 requesting all changes
757 adding changesets
769 adding changesets
758 adding manifests
770 adding manifests
759 adding file changes
771 adding file changes
760 added 1 changesets with 1 changes to 1 files
772 added 1 changesets with 1 changes to 1 files
761 updating to branch default
773 updating to branch default
762 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
774 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
763
775
764 vanilla clients locked out from largefiles http repos
776 vanilla clients locked out from largefiles http repos
765 $ mkdir r4
777 $ mkdir r4
766 $ cd r4
778 $ cd r4
767 $ hg init
779 $ hg init
768 $ echo c1 > f1
780 $ echo c1 > f1
769 $ hg add --large f1
781 $ hg add --large f1
770 $ hg com -m "m1"
782 $ hg com -m "m1"
771 $ cd ..
783 $ cd ..
772 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
784 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
773 $ cat hg.pid >> $DAEMON_PIDS
785 $ cat hg.pid >> $DAEMON_PIDS
774 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
786 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
775 abort: remote error:
787 abort: remote error:
776
788
777 This repository uses the largefiles extension.
789 This repository uses the largefiles extension.
778
790
779 Please enable it in your Mercurial config file.
791 Please enable it in your Mercurial config file.
780 [255]
792 [255]
781
793
782 used all HGPORTs, kill all daemons
794 used all HGPORTs, kill all daemons
783 $ "$TESTDIR/killdaemons.py"
795 $ "$TESTDIR/killdaemons.py"
784
796
785 vanilla clients locked out from largefiles ssh repos
797 vanilla clients locked out from largefiles ssh repos
786 $ hg --config extensions.largefiles=! clone -e "python $TESTDIR/dummyssh" ssh://user@dummy/r4 r5
798 $ hg --config extensions.largefiles=! clone -e "python $TESTDIR/dummyssh" ssh://user@dummy/r4 r5
787 abort: remote error:
799 abort: remote error:
788
800
789 This repository uses the largefiles extension.
801 This repository uses the largefiles extension.
790
802
791 Please enable it in your Mercurial config file.
803 Please enable it in your Mercurial config file.
792 [255]
804 [255]
793
805
794 largefiles clients refuse to push largefiles repos to vanilla servers
806 largefiles clients refuse to push largefiles repos to vanilla servers
795 $ mkdir r6
807 $ mkdir r6
796 $ cd r6
808 $ cd r6
797 $ hg init
809 $ hg init
798 $ echo c1 > f1
810 $ echo c1 > f1
799 $ hg add f1
811 $ hg add f1
800 $ hg com -m "m1"
812 $ hg com -m "m1"
801 $ cat >> .hg/hgrc <<!
813 $ cat >> .hg/hgrc <<!
802 > [web]
814 > [web]
803 > push_ssl = false
815 > push_ssl = false
804 > allow_push = *
816 > allow_push = *
805 > !
817 > !
806 $ cd ..
818 $ cd ..
807 $ hg clone r6 r7
819 $ hg clone r6 r7
808 updating to branch default
820 updating to branch default
809 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
821 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
810 $ cd r7
822 $ cd r7
811 $ echo c2 > f2
823 $ echo c2 > f2
812 $ hg add --large f2
824 $ hg add --large f2
813 $ hg com -m "m2"
825 $ hg com -m "m2"
814 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
826 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
815 $ cat ../hg.pid >> $DAEMON_PIDS
827 $ cat ../hg.pid >> $DAEMON_PIDS
816 $ hg push http://localhost:$HGPORT
828 $ hg push http://localhost:$HGPORT
817 pushing to http://localhost:$HGPORT/
829 pushing to http://localhost:$HGPORT/
818 searching for changes
830 searching for changes
819 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
831 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
820 [255]
832 [255]
821 $ cd ..
833 $ cd ..
822
834
823 Clone a local repository owned by another user
835 Clone a local repository owned by another user
824 We have to simulate that here by setting $HOME and removing write permissions
836 We have to simulate that here by setting $HOME and removing write permissions
825 $ ORIGHOME="$HOME"
837 $ ORIGHOME="$HOME"
826 $ mkdir alice
838 $ mkdir alice
827 $ HOME="`pwd`/alice"
839 $ HOME="`pwd`/alice"
828 $ cd alice
840 $ cd alice
829 $ hg init pubrepo
841 $ hg init pubrepo
830 $ cd pubrepo
842 $ cd pubrepo
831 $ dd if=/dev/urandom bs=1k count=11k > a-large-file 2> /dev/null
843 $ dd if=/dev/urandom bs=1k count=11k > a-large-file 2> /dev/null
832 $ hg add --large a-large-file
844 $ hg add --large a-large-file
833 $ hg commit -m "Add a large file"
845 $ hg commit -m "Add a large file"
834 $ cd ..
846 $ cd ..
835 $ chmod -R a-w pubrepo
847 $ chmod -R a-w pubrepo
836 $ cd ..
848 $ cd ..
837 $ mkdir bob
849 $ mkdir bob
838 $ HOME="`pwd`/bob"
850 $ HOME="`pwd`/bob"
839 $ cd bob
851 $ cd bob
840 $ hg clone --pull ../alice/pubrepo pubrepo
852 $ hg clone --pull ../alice/pubrepo pubrepo
841 requesting all changes
853 requesting all changes
842 adding changesets
854 adding changesets
843 adding manifests
855 adding manifests
844 adding file changes
856 adding file changes
845 added 1 changesets with 1 changes to 1 files
857 added 1 changesets with 1 changes to 1 files
846 updating to branch default
858 updating to branch default
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
859 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 getting changed largefiles
860 getting changed largefiles
849 1 largefiles updated, 0 removed
861 1 largefiles updated, 0 removed
850 $ cd ..
862 $ cd ..
851 $ HOME="$ORIGHOME"
863 $ HOME="$ORIGHOME"
852
864
853 Symlink to a large largefile should behave the same as a symlink to a normal file
865 Symlink to a large largefile should behave the same as a symlink to a normal file
854 $ hg init largesymlink
866 $ hg init largesymlink
855 $ cd largesymlink
867 $ cd largesymlink
856 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
868 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
857 $ hg add --large largefile
869 $ hg add --large largefile
858 $ hg commit -m "commit a large file"
870 $ hg commit -m "commit a large file"
859 $ ln -s largefile largelink
871 $ ln -s largefile largelink
860 $ hg add largelink
872 $ hg add largelink
861 $ hg commit -m "commit a large symlink"
873 $ hg commit -m "commit a large symlink"
862 $ rm -f largelink
874 $ rm -f largelink
863 $ hg up >/dev/null
875 $ hg up >/dev/null
864 $ test -f largelink
876 $ test -f largelink
865 [1]
877 [1]
866 $ test -L largelink
878 $ test -L largelink
867 [1]
879 [1]
868 $ rm -f largelink # make next part of the test independent of the previous
880 $ rm -f largelink # make next part of the test independent of the previous
869 $ hg up -C >/dev/null
881 $ hg up -C >/dev/null
870 $ test -f largelink
882 $ test -f largelink
871 $ test -L largelink
883 $ test -L largelink
872 $ cd ..
884 $ cd ..
873
885
874
886
General Comments 0
You need to be logged in to leave comments. Login now