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