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