##// END OF EJS Templates
merge with stable
Matt Mackall -
r22173:d3702a82 merge default
parent child Browse files
Show More
@@ -1,1229 +1,1246 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 archival, merge, pathutil, revset
15 archival, merge, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22 import basestore
22 import basestore
23
23
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
25
25
26 def installnormalfilesmatchfn(manifest):
26 def installnormalfilesmatchfn(manifest):
27 '''installmatchfn with a matchfn that ignores all largefiles'''
27 '''installmatchfn with a matchfn that ignores all largefiles'''
28 def overridematch(ctx, pats=[], opts={}, globbed=False,
28 def overridematch(ctx, pats=[], opts={}, globbed=False,
29 default='relpath'):
29 default='relpath'):
30 match = oldmatch(ctx, pats, opts, globbed, default)
30 match = oldmatch(ctx, pats, opts, globbed, default)
31 m = copy.copy(match)
31 m = copy.copy(match)
32 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 manifest)
33 manifest)
34 m._files = filter(notlfile, m._files)
34 m._files = filter(notlfile, m._files)
35 m._fmap = set(m._files)
35 m._fmap = set(m._files)
36 m._always = False
36 m._always = False
37 origmatchfn = m.matchfn
37 origmatchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(overridematch)
40 oldmatch = installmatchfn(overridematch)
41
41
42 def installmatchfn(f):
42 def installmatchfn(f):
43 '''monkey patch the scmutil module with a custom match function.
43 '''monkey patch the scmutil module with a custom match function.
44 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
44 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
45 oldmatch = scmutil.match
45 oldmatch = scmutil.match
46 setattr(f, 'oldmatch', oldmatch)
46 setattr(f, 'oldmatch', oldmatch)
47 scmutil.match = f
47 scmutil.match = f
48 return oldmatch
48 return oldmatch
49
49
50 def restorematchfn():
50 def restorematchfn():
51 '''restores scmutil.match to what it was before installmatchfn
51 '''restores scmutil.match to what it was before installmatchfn
52 was called. no-op if scmutil.match is its original function.
52 was called. no-op if scmutil.match is its original function.
53
53
54 Note that n calls to installmatchfn will require n calls to
54 Note that n calls to installmatchfn will require n calls to
55 restore matchfn to reverse'''
55 restore matchfn to reverse'''
56 scmutil.match = getattr(scmutil.match, 'oldmatch')
56 scmutil.match = getattr(scmutil.match, 'oldmatch')
57
57
58 def installmatchandpatsfn(f):
58 def installmatchandpatsfn(f):
59 oldmatchandpats = scmutil.matchandpats
59 oldmatchandpats = scmutil.matchandpats
60 setattr(f, 'oldmatchandpats', oldmatchandpats)
60 setattr(f, 'oldmatchandpats', oldmatchandpats)
61 scmutil.matchandpats = f
61 scmutil.matchandpats = f
62 return oldmatchandpats
62 return oldmatchandpats
63
63
64 def restorematchandpatsfn():
64 def restorematchandpatsfn():
65 '''restores scmutil.matchandpats to what it was before
65 '''restores scmutil.matchandpats to what it was before
66 installnormalfilesmatchandpatsfn was called. no-op if scmutil.matchandpats
66 installnormalfilesmatchandpatsfn was called. no-op if scmutil.matchandpats
67 is its original function.
67 is its original function.
68
68
69 Note that n calls to installnormalfilesmatchandpatsfn will require n calls
69 Note that n calls to installnormalfilesmatchandpatsfn will require n calls
70 to restore matchfn to reverse'''
70 to restore matchfn to reverse'''
71 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
71 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
72 scmutil.matchandpats)
72 scmutil.matchandpats)
73
73
74 def addlargefiles(ui, repo, *pats, **opts):
74 def addlargefiles(ui, repo, *pats, **opts):
75 large = opts.pop('large', None)
75 large = opts.pop('large', None)
76 lfsize = lfutil.getminsize(
76 lfsize = lfutil.getminsize(
77 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
77 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
78
78
79 lfmatcher = None
79 lfmatcher = None
80 if lfutil.islfilesrepo(repo):
80 if lfutil.islfilesrepo(repo):
81 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
81 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
82 if lfpats:
82 if lfpats:
83 lfmatcher = match_.match(repo.root, '', list(lfpats))
83 lfmatcher = match_.match(repo.root, '', list(lfpats))
84
84
85 lfnames = []
85 lfnames = []
86 m = scmutil.match(repo[None], pats, opts)
86 m = scmutil.match(repo[None], pats, opts)
87 m.bad = lambda x, y: None
87 m.bad = lambda x, y: None
88 wctx = repo[None]
88 wctx = repo[None]
89 for f in repo.walk(m):
89 for f in repo.walk(m):
90 exact = m.exact(f)
90 exact = m.exact(f)
91 lfile = lfutil.standin(f) in wctx
91 lfile = lfutil.standin(f) in wctx
92 nfile = f in wctx
92 nfile = f in wctx
93 exists = lfile or nfile
93 exists = lfile or nfile
94
94
95 # Don't warn the user when they attempt to add a normal tracked file.
95 # Don't warn the user when they attempt to add a normal tracked file.
96 # The normal add code will do that for us.
96 # The normal add code will do that for us.
97 if exact and exists:
97 if exact and exists:
98 if lfile:
98 if lfile:
99 ui.warn(_('%s already a largefile\n') % f)
99 ui.warn(_('%s already a largefile\n') % f)
100 continue
100 continue
101
101
102 if (exact or not exists) and not lfutil.isstandin(f):
102 if (exact or not exists) and not lfutil.isstandin(f):
103 wfile = repo.wjoin(f)
103 wfile = repo.wjoin(f)
104
104
105 # In case the file was removed previously, but not committed
105 # In case the file was removed previously, but not committed
106 # (issue3507)
106 # (issue3507)
107 if not os.path.exists(wfile):
107 if not os.path.exists(wfile):
108 continue
108 continue
109
109
110 abovemin = (lfsize and
110 abovemin = (lfsize and
111 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
111 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
112 if large or abovemin or (lfmatcher and lfmatcher(f)):
112 if large or abovemin or (lfmatcher and lfmatcher(f)):
113 lfnames.append(f)
113 lfnames.append(f)
114 if ui.verbose or not exact:
114 if ui.verbose or not exact:
115 ui.status(_('adding %s as a largefile\n') % m.rel(f))
115 ui.status(_('adding %s as a largefile\n') % m.rel(f))
116
116
117 bad = []
117 bad = []
118 standins = []
118 standins = []
119
119
120 # Need to lock, otherwise there could be a race condition between
120 # Need to lock, otherwise there could be a race condition between
121 # when standins are created and added to the repo.
121 # when standins are created and added to the repo.
122 wlock = repo.wlock()
122 wlock = repo.wlock()
123 try:
123 try:
124 if not opts.get('dry_run'):
124 if not opts.get('dry_run'):
125 lfdirstate = lfutil.openlfdirstate(ui, repo)
125 lfdirstate = lfutil.openlfdirstate(ui, repo)
126 for f in lfnames:
126 for f in lfnames:
127 standinname = lfutil.standin(f)
127 standinname = lfutil.standin(f)
128 lfutil.writestandin(repo, standinname, hash='',
128 lfutil.writestandin(repo, standinname, hash='',
129 executable=lfutil.getexecutable(repo.wjoin(f)))
129 executable=lfutil.getexecutable(repo.wjoin(f)))
130 standins.append(standinname)
130 standins.append(standinname)
131 if lfdirstate[f] == 'r':
131 if lfdirstate[f] == 'r':
132 lfdirstate.normallookup(f)
132 lfdirstate.normallookup(f)
133 else:
133 else:
134 lfdirstate.add(f)
134 lfdirstate.add(f)
135 lfdirstate.write()
135 lfdirstate.write()
136 bad += [lfutil.splitstandin(f)
136 bad += [lfutil.splitstandin(f)
137 for f in repo[None].add(standins)
137 for f in repo[None].add(standins)
138 if f in m.files()]
138 if f in m.files()]
139 finally:
139 finally:
140 wlock.release()
140 wlock.release()
141 return bad
141 return bad
142
142
143 def removelargefiles(ui, repo, *pats, **opts):
143 def removelargefiles(ui, repo, *pats, **opts):
144 after = opts.get('after')
144 after = opts.get('after')
145 if not pats and not after:
145 if not pats and not after:
146 raise util.Abort(_('no files specified'))
146 raise util.Abort(_('no files specified'))
147 m = scmutil.match(repo[None], pats, opts)
147 m = scmutil.match(repo[None], pats, opts)
148 try:
148 try:
149 repo.lfstatus = True
149 repo.lfstatus = True
150 s = repo.status(match=m, clean=True)
150 s = repo.status(match=m, clean=True)
151 finally:
151 finally:
152 repo.lfstatus = False
152 repo.lfstatus = False
153 manifest = repo[None].manifest()
153 manifest = repo[None].manifest()
154 modified, added, deleted, clean = [[f for f in list
154 modified, added, deleted, clean = [[f for f in list
155 if lfutil.standin(f) in manifest]
155 if lfutil.standin(f) in manifest]
156 for list in [s[0], s[1], s[3], s[6]]]
156 for list in [s[0], s[1], s[3], s[6]]]
157
157
158 def warn(files, msg):
158 def warn(files, msg):
159 for f in files:
159 for f in files:
160 ui.warn(msg % m.rel(f))
160 ui.warn(msg % m.rel(f))
161 return int(len(files) > 0)
161 return int(len(files) > 0)
162
162
163 result = 0
163 result = 0
164
164
165 if after:
165 if after:
166 remove, forget = deleted, []
166 remove, forget = deleted, []
167 result = warn(modified + added + clean,
167 result = warn(modified + added + clean,
168 _('not removing %s: file still exists\n'))
168 _('not removing %s: file still exists\n'))
169 else:
169 else:
170 remove, forget = deleted + clean, []
170 remove, forget = deleted + clean, []
171 result = warn(modified, _('not removing %s: file is modified (use -f'
171 result = warn(modified, _('not removing %s: file is modified (use -f'
172 ' to force removal)\n'))
172 ' to force removal)\n'))
173 result = warn(added, _('not removing %s: file has been marked for add'
173 result = warn(added, _('not removing %s: file has been marked for add'
174 ' (use forget to undo)\n')) or result
174 ' (use forget to undo)\n')) or result
175
175
176 for f in sorted(remove + forget):
176 for f in sorted(remove + forget):
177 if ui.verbose or not m.exact(f):
177 if ui.verbose or not m.exact(f):
178 ui.status(_('removing %s\n') % m.rel(f))
178 ui.status(_('removing %s\n') % m.rel(f))
179
179
180 # Need to lock because standin files are deleted then removed from the
180 # Need to lock because standin files are deleted then removed from the
181 # repository and we could race in-between.
181 # repository and we could race in-between.
182 wlock = repo.wlock()
182 wlock = repo.wlock()
183 try:
183 try:
184 lfdirstate = lfutil.openlfdirstate(ui, repo)
184 lfdirstate = lfutil.openlfdirstate(ui, repo)
185 for f in remove:
185 for f in remove:
186 if not after:
186 if not after:
187 # If this is being called by addremove, notify the user that we
187 # If this is being called by addremove, notify the user that we
188 # are removing the file.
188 # are removing the file.
189 if getattr(repo, "_isaddremove", False):
189 if getattr(repo, "_isaddremove", False):
190 ui.status(_('removing %s\n') % f)
190 ui.status(_('removing %s\n') % f)
191 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
191 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
192 lfdirstate.remove(f)
192 lfdirstate.remove(f)
193 lfdirstate.write()
193 lfdirstate.write()
194 forget = [lfutil.standin(f) for f in forget]
194 forget = [lfutil.standin(f) for f in forget]
195 remove = [lfutil.standin(f) for f in remove]
195 remove = [lfutil.standin(f) for f in remove]
196 repo[None].forget(forget)
196 repo[None].forget(forget)
197 # If this is being called by addremove, let the original addremove
197 # If this is being called by addremove, let the original addremove
198 # function handle this.
198 # function handle this.
199 if not getattr(repo, "_isaddremove", False):
199 if not getattr(repo, "_isaddremove", False):
200 for f in remove:
200 for f in remove:
201 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
201 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
202 repo[None].forget(remove)
202 repo[None].forget(remove)
203 finally:
203 finally:
204 wlock.release()
204 wlock.release()
205
205
206 return result
206 return result
207
207
208 # For overriding mercurial.hgweb.webcommands so that largefiles will
208 # For overriding mercurial.hgweb.webcommands so that largefiles will
209 # appear at their right place in the manifests.
209 # appear at their right place in the manifests.
210 def decodepath(orig, path):
210 def decodepath(orig, path):
211 return lfutil.splitstandin(path) or path
211 return lfutil.splitstandin(path) or path
212
212
213 # -- Wrappers: modify existing commands --------------------------------
213 # -- Wrappers: modify existing commands --------------------------------
214
214
215 # Add works by going through the files that the user wanted to add and
215 # Add works by going through the files that the user wanted to add and
216 # checking if they should be added as largefiles. Then it makes a new
216 # checking if they should be added as largefiles. Then it makes a new
217 # matcher which matches only the normal files and runs the original
217 # matcher which matches only the normal files and runs the original
218 # version of add.
218 # version of add.
219 def overrideadd(orig, ui, repo, *pats, **opts):
219 def overrideadd(orig, ui, repo, *pats, **opts):
220 normal = opts.pop('normal')
220 normal = opts.pop('normal')
221 if normal:
221 if normal:
222 if opts.get('large'):
222 if opts.get('large'):
223 raise util.Abort(_('--normal cannot be used with --large'))
223 raise util.Abort(_('--normal cannot be used with --large'))
224 return orig(ui, repo, *pats, **opts)
224 return orig(ui, repo, *pats, **opts)
225 bad = addlargefiles(ui, repo, *pats, **opts)
225 bad = addlargefiles(ui, repo, *pats, **opts)
226 installnormalfilesmatchfn(repo[None].manifest())
226 installnormalfilesmatchfn(repo[None].manifest())
227 result = orig(ui, repo, *pats, **opts)
227 result = orig(ui, repo, *pats, **opts)
228 restorematchfn()
228 restorematchfn()
229
229
230 return (result == 1 or bad) and 1 or 0
230 return (result == 1 or bad) and 1 or 0
231
231
232 def overrideremove(orig, ui, repo, *pats, **opts):
232 def overrideremove(orig, ui, repo, *pats, **opts):
233 installnormalfilesmatchfn(repo[None].manifest())
233 installnormalfilesmatchfn(repo[None].manifest())
234 result = orig(ui, repo, *pats, **opts)
234 result = orig(ui, repo, *pats, **opts)
235 restorematchfn()
235 restorematchfn()
236 return removelargefiles(ui, repo, *pats, **opts) or result
236 return removelargefiles(ui, repo, *pats, **opts) or result
237
237
238 def overridestatusfn(orig, repo, rev2, **opts):
238 def overridestatusfn(orig, repo, rev2, **opts):
239 try:
239 try:
240 repo._repo.lfstatus = True
240 repo._repo.lfstatus = True
241 return orig(repo, rev2, **opts)
241 return orig(repo, rev2, **opts)
242 finally:
242 finally:
243 repo._repo.lfstatus = False
243 repo._repo.lfstatus = False
244
244
245 def overridestatus(orig, ui, repo, *pats, **opts):
245 def overridestatus(orig, ui, repo, *pats, **opts):
246 try:
246 try:
247 repo.lfstatus = True
247 repo.lfstatus = True
248 return orig(ui, repo, *pats, **opts)
248 return orig(ui, repo, *pats, **opts)
249 finally:
249 finally:
250 repo.lfstatus = False
250 repo.lfstatus = False
251
251
252 def overridedirty(orig, repo, ignoreupdate=False):
252 def overridedirty(orig, repo, ignoreupdate=False):
253 try:
253 try:
254 repo._repo.lfstatus = True
254 repo._repo.lfstatus = True
255 return orig(repo, ignoreupdate)
255 return orig(repo, ignoreupdate)
256 finally:
256 finally:
257 repo._repo.lfstatus = False
257 repo._repo.lfstatus = False
258
258
259 def overridelog(orig, ui, repo, *pats, **opts):
259 def overridelog(orig, ui, repo, *pats, **opts):
260 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
260 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
261 default='relpath'):
261 default='relpath'):
262 """Matcher that merges root directory with .hglf, suitable for log.
262 """Matcher that merges root directory with .hglf, suitable for log.
263 It is still possible to match .hglf directly.
263 It is still possible to match .hglf directly.
264 For any listed files run log on the standin too.
264 For any listed files run log on the standin too.
265 matchfn tries both the given filename and with .hglf stripped.
265 matchfn tries both the given filename and with .hglf stripped.
266 """
266 """
267 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
267 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
268 m, p = copy.copy(matchandpats)
268 m, p = copy.copy(matchandpats)
269
269
270 if m.always():
271 # We want to match everything anyway, so there's no benefit trying
272 # to add standins.
273 return matchandpats
274
270 pats = set(p)
275 pats = set(p)
271 # TODO: handling of patterns in both cases below
276 # TODO: handling of patterns in both cases below
272 if m._cwd:
277 if m._cwd:
273 if os.path.isabs(m._cwd):
278 if os.path.isabs(m._cwd):
274 # TODO: handle largefile magic when invoked from other cwd
279 # TODO: handle largefile magic when invoked from other cwd
275 return matchandpats
280 return matchandpats
276 back = (m._cwd.count('/') + 1) * '../'
281 back = (m._cwd.count('/') + 1) * '../'
277 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
282 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
278 else:
283 else:
279 pats.update(lfutil.standin(f) for f in p)
284 pats.update(lfutil.standin(f) for f in p)
280
285
281 for i in range(0, len(m._files)):
286 for i in range(0, len(m._files)):
282 standin = lfutil.standin(m._files[i])
287 standin = lfutil.standin(m._files[i])
283 if standin in repo[ctx.node()]:
288 if standin in repo[ctx.node()]:
284 m._files[i] = standin
289 m._files[i] = standin
285 elif m._files[i] not in repo[ctx.node()]:
290 elif m._files[i] not in repo[ctx.node()]:
286 m._files.append(standin)
291 m._files.append(standin)
287 pats.add(standin)
292 pats.add(standin)
288
293
289 m._fmap = set(m._files)
294 m._fmap = set(m._files)
290 m._always = False
295 m._always = False
291 origmatchfn = m.matchfn
296 origmatchfn = m.matchfn
292 def lfmatchfn(f):
297 def lfmatchfn(f):
293 lf = lfutil.splitstandin(f)
298 lf = lfutil.splitstandin(f)
294 if lf is not None and origmatchfn(lf):
299 if lf is not None and origmatchfn(lf):
295 return True
300 return True
296 r = origmatchfn(f)
301 r = origmatchfn(f)
297 return r
302 return r
298 m.matchfn = lfmatchfn
303 m.matchfn = lfmatchfn
299
304
300 return m, pats
305 return m, pats
301
306
307 # For hg log --patch, the match object is used in two different senses:
308 # (1) to determine what revisions should be printed out, and
309 # (2) to determine what files to print out diffs for.
310 # The magic matchandpats override should be used for case (1) but not for
311 # case (2).
312 def overridemakelogfilematcher(repo, pats, opts):
313 pctx = repo[None]
314 match, pats = oldmatchandpats(pctx, pats, opts)
315 return lambda rev: match
316
302 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
317 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
318 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
319 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
320
303 try:
321 try:
304 repo.lfstatus = True
305 return orig(ui, repo, *pats, **opts)
322 return orig(ui, repo, *pats, **opts)
306 finally:
323 finally:
307 repo.lfstatus = False
308 restorematchandpatsfn()
324 restorematchandpatsfn()
325 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
309
326
310 def overrideverify(orig, ui, repo, *pats, **opts):
327 def overrideverify(orig, ui, repo, *pats, **opts):
311 large = opts.pop('large', False)
328 large = opts.pop('large', False)
312 all = opts.pop('lfa', False)
329 all = opts.pop('lfa', False)
313 contents = opts.pop('lfc', False)
330 contents = opts.pop('lfc', False)
314
331
315 result = orig(ui, repo, *pats, **opts)
332 result = orig(ui, repo, *pats, **opts)
316 if large or all or contents:
333 if large or all or contents:
317 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
334 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
318 return result
335 return result
319
336
320 def overridedebugstate(orig, ui, repo, *pats, **opts):
337 def overridedebugstate(orig, ui, repo, *pats, **opts):
321 large = opts.pop('large', False)
338 large = opts.pop('large', False)
322 if large:
339 if large:
323 class fakerepo(object):
340 class fakerepo(object):
324 dirstate = lfutil.openlfdirstate(ui, repo)
341 dirstate = lfutil.openlfdirstate(ui, repo)
325 orig(ui, fakerepo, *pats, **opts)
342 orig(ui, fakerepo, *pats, **opts)
326 else:
343 else:
327 orig(ui, repo, *pats, **opts)
344 orig(ui, repo, *pats, **opts)
328
345
329 # Override needs to refresh standins so that update's normal merge
346 # Override needs to refresh standins so that update's normal merge
330 # will go through properly. Then the other update hook (overriding repo.update)
347 # will go through properly. Then the other update hook (overriding repo.update)
331 # will get the new files. Filemerge is also overridden so that the merge
348 # will get the new files. Filemerge is also overridden so that the merge
332 # will merge standins correctly.
349 # will merge standins correctly.
333 def overrideupdate(orig, ui, repo, *pats, **opts):
350 def overrideupdate(orig, ui, repo, *pats, **opts):
334 # Need to lock between the standins getting updated and their
351 # Need to lock between the standins getting updated and their
335 # largefiles getting updated
352 # largefiles getting updated
336 wlock = repo.wlock()
353 wlock = repo.wlock()
337 try:
354 try:
338 lfdirstate = lfutil.openlfdirstate(ui, repo)
355 lfdirstate = lfutil.openlfdirstate(ui, repo)
339 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
356 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
340 [], False, False, False)
357 [], False, False, False)
341 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
358 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
342
359
343 if opts['check']:
360 if opts['check']:
344 mod = len(modified) > 0
361 mod = len(modified) > 0
345 for lfile in unsure:
362 for lfile in unsure:
346 standin = lfutil.standin(lfile)
363 standin = lfutil.standin(lfile)
347 if repo['.'][standin].data().strip() != \
364 if repo['.'][standin].data().strip() != \
348 lfutil.hashfile(repo.wjoin(lfile)):
365 lfutil.hashfile(repo.wjoin(lfile)):
349 mod = True
366 mod = True
350 else:
367 else:
351 lfdirstate.normal(lfile)
368 lfdirstate.normal(lfile)
352 lfdirstate.write()
369 lfdirstate.write()
353 if mod:
370 if mod:
354 raise util.Abort(_('uncommitted changes'))
371 raise util.Abort(_('uncommitted changes'))
355 # XXX handle removed differently
372 # XXX handle removed differently
356 if not opts['clean']:
373 if not opts['clean']:
357 for lfile in unsure + modified + added:
374 for lfile in unsure + modified + added:
358 lfutil.updatestandin(repo, lfutil.standin(lfile))
375 lfutil.updatestandin(repo, lfutil.standin(lfile))
359 return orig(ui, repo, *pats, **opts)
376 return orig(ui, repo, *pats, **opts)
360 finally:
377 finally:
361 wlock.release()
378 wlock.release()
362
379
363 # Before starting the manifest merge, merge.updates will call
380 # Before starting the manifest merge, merge.updates will call
364 # _checkunknown to check if there are any files in the merged-in
381 # _checkunknown to check if there are any files in the merged-in
365 # changeset that collide with unknown files in the working copy.
382 # changeset that collide with unknown files in the working copy.
366 #
383 #
367 # The largefiles are seen as unknown, so this prevents us from merging
384 # The largefiles are seen as unknown, so this prevents us from merging
368 # in a file 'foo' if we already have a largefile with the same name.
385 # in a file 'foo' if we already have a largefile with the same name.
369 #
386 #
370 # The overridden function filters the unknown files by removing any
387 # The overridden function filters the unknown files by removing any
371 # largefiles. This makes the merge proceed and we can then handle this
388 # largefiles. This makes the merge proceed and we can then handle this
372 # case further in the overridden manifestmerge function below.
389 # case further in the overridden manifestmerge function below.
373 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
390 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
374 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
391 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
375 return False
392 return False
376 return origfn(repo, wctx, mctx, f)
393 return origfn(repo, wctx, mctx, f)
377
394
378 # The manifest merge handles conflicts on the manifest level. We want
395 # The manifest merge handles conflicts on the manifest level. We want
379 # to handle changes in largefile-ness of files at this level too.
396 # to handle changes in largefile-ness of files at this level too.
380 #
397 #
381 # The strategy is to run the original manifestmerge and then process
398 # The strategy is to run the original manifestmerge and then process
382 # the action list it outputs. There are two cases we need to deal with:
399 # the action list it outputs. There are two cases we need to deal with:
383 #
400 #
384 # 1. Normal file in p1, largefile in p2. Here the largefile is
401 # 1. Normal file in p1, largefile in p2. Here the largefile is
385 # detected via its standin file, which will enter the working copy
402 # detected via its standin file, which will enter the working copy
386 # with a "get" action. It is not "merge" since the standin is all
403 # with a "get" action. It is not "merge" since the standin is all
387 # Mercurial is concerned with at this level -- the link to the
404 # Mercurial is concerned with at this level -- the link to the
388 # existing normal file is not relevant here.
405 # existing normal file is not relevant here.
389 #
406 #
390 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
407 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
391 # since the largefile will be present in the working copy and
408 # since the largefile will be present in the working copy and
392 # different from the normal file in p2. Mercurial therefore
409 # different from the normal file in p2. Mercurial therefore
393 # triggers a merge action.
410 # triggers a merge action.
394 #
411 #
395 # In both cases, we prompt the user and emit new actions to either
412 # In both cases, we prompt the user and emit new actions to either
396 # remove the standin (if the normal file was kept) or to remove the
413 # remove the standin (if the normal file was kept) or to remove the
397 # normal file and get the standin (if the largefile was kept). The
414 # normal file and get the standin (if the largefile was kept). The
398 # default prompt answer is to use the largefile version since it was
415 # default prompt answer is to use the largefile version since it was
399 # presumably changed on purpose.
416 # presumably changed on purpose.
400 #
417 #
401 # Finally, the merge.applyupdates function will then take care of
418 # Finally, the merge.applyupdates function will then take care of
402 # writing the files into the working copy and lfcommands.updatelfiles
419 # writing the files into the working copy and lfcommands.updatelfiles
403 # will update the largefiles.
420 # will update the largefiles.
404 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
421 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
405 partial, acceptremote, followcopies):
422 partial, acceptremote, followcopies):
406 overwrite = force and not branchmerge
423 overwrite = force and not branchmerge
407 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
424 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
408 acceptremote, followcopies)
425 acceptremote, followcopies)
409
426
410 if overwrite:
427 if overwrite:
411 return actions
428 return actions
412
429
413 removes = set(a[0] for a in actions['r'])
430 removes = set(a[0] for a in actions['r'])
414
431
415 newglist = []
432 newglist = []
416 for action in actions['g']:
433 for action in actions['g']:
417 f, args, msg = action
434 f, args, msg = action
418 splitstandin = f and lfutil.splitstandin(f)
435 splitstandin = f and lfutil.splitstandin(f)
419 if (splitstandin is not None and
436 if (splitstandin is not None and
420 splitstandin in p1 and splitstandin not in removes):
437 splitstandin in p1 and splitstandin not in removes):
421 # Case 1: normal file in the working copy, largefile in
438 # Case 1: normal file in the working copy, largefile in
422 # the second parent
439 # the second parent
423 lfile = splitstandin
440 lfile = splitstandin
424 standin = f
441 standin = f
425 msg = _('remote turned local normal file %s into a largefile\n'
442 msg = _('remote turned local normal file %s into a largefile\n'
426 'use (l)argefile or keep (n)ormal file?'
443 'use (l)argefile or keep (n)ormal file?'
427 '$$ &Largefile $$ &Normal file') % lfile
444 '$$ &Largefile $$ &Normal file') % lfile
428 if repo.ui.promptchoice(msg, 0) == 0:
445 if repo.ui.promptchoice(msg, 0) == 0:
429 actions['r'].append((lfile, None, msg))
446 actions['r'].append((lfile, None, msg))
430 newglist.append((standin, (p2.flags(standin),), msg))
447 newglist.append((standin, (p2.flags(standin),), msg))
431 else:
448 else:
432 actions['r'].append((standin, None, msg))
449 actions['r'].append((standin, None, msg))
433 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
450 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
434 # Case 2: largefile in the working copy, normal file in
451 # Case 2: largefile in the working copy, normal file in
435 # the second parent
452 # the second parent
436 standin = lfutil.standin(f)
453 standin = lfutil.standin(f)
437 lfile = f
454 lfile = f
438 msg = _('remote turned local largefile %s into a normal file\n'
455 msg = _('remote turned local largefile %s into a normal file\n'
439 'keep (l)argefile or use (n)ormal file?'
456 'keep (l)argefile or use (n)ormal file?'
440 '$$ &Largefile $$ &Normal file') % lfile
457 '$$ &Largefile $$ &Normal file') % lfile
441 if repo.ui.promptchoice(msg, 0) == 0:
458 if repo.ui.promptchoice(msg, 0) == 0:
442 actions['r'].append((lfile, None, msg))
459 actions['r'].append((lfile, None, msg))
443 else:
460 else:
444 actions['r'].append((standin, None, msg))
461 actions['r'].append((standin, None, msg))
445 newglist.append((lfile, (p2.flags(lfile),), msg))
462 newglist.append((lfile, (p2.flags(lfile),), msg))
446 else:
463 else:
447 newglist.append(action)
464 newglist.append(action)
448
465
449 newglist.sort()
466 newglist.sort()
450 actions['g'] = newglist
467 actions['g'] = newglist
451
468
452 return actions
469 return actions
453
470
454 # Override filemerge to prompt the user about how they wish to merge
471 # Override filemerge to prompt the user about how they wish to merge
455 # largefiles. This will handle identical edits without prompting the user.
472 # largefiles. This will handle identical edits without prompting the user.
456 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
473 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
457 if not lfutil.isstandin(orig):
474 if not lfutil.isstandin(orig):
458 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
475 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
459
476
460 ahash = fca.data().strip().lower()
477 ahash = fca.data().strip().lower()
461 dhash = fcd.data().strip().lower()
478 dhash = fcd.data().strip().lower()
462 ohash = fco.data().strip().lower()
479 ohash = fco.data().strip().lower()
463 if (ohash != ahash and
480 if (ohash != ahash and
464 ohash != dhash and
481 ohash != dhash and
465 (dhash == ahash or
482 (dhash == ahash or
466 repo.ui.promptchoice(
483 repo.ui.promptchoice(
467 _('largefile %s has a merge conflict\nancestor was %s\n'
484 _('largefile %s has a merge conflict\nancestor was %s\n'
468 'keep (l)ocal %s or\ntake (o)ther %s?'
485 'keep (l)ocal %s or\ntake (o)ther %s?'
469 '$$ &Local $$ &Other') %
486 '$$ &Local $$ &Other') %
470 (lfutil.splitstandin(orig), ahash, dhash, ohash),
487 (lfutil.splitstandin(orig), ahash, dhash, ohash),
471 0) == 1)):
488 0) == 1)):
472 repo.wwrite(fcd.path(), fco.data(), fco.flags())
489 repo.wwrite(fcd.path(), fco.data(), fco.flags())
473 return 0
490 return 0
474
491
475 # Copy first changes the matchers to match standins instead of
492 # Copy first changes the matchers to match standins instead of
476 # largefiles. Then it overrides util.copyfile in that function it
493 # largefiles. Then it overrides util.copyfile in that function it
477 # checks if the destination largefile already exists. It also keeps a
494 # checks if the destination largefile already exists. It also keeps a
478 # list of copied files so that the largefiles can be copied and the
495 # list of copied files so that the largefiles can be copied and the
479 # dirstate updated.
496 # dirstate updated.
480 def overridecopy(orig, ui, repo, pats, opts, rename=False):
497 def overridecopy(orig, ui, repo, pats, opts, rename=False):
481 # doesn't remove largefile on rename
498 # doesn't remove largefile on rename
482 if len(pats) < 2:
499 if len(pats) < 2:
483 # this isn't legal, let the original function deal with it
500 # this isn't legal, let the original function deal with it
484 return orig(ui, repo, pats, opts, rename)
501 return orig(ui, repo, pats, opts, rename)
485
502
486 def makestandin(relpath):
503 def makestandin(relpath):
487 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
504 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
488 return os.path.join(repo.wjoin(lfutil.standin(path)))
505 return os.path.join(repo.wjoin(lfutil.standin(path)))
489
506
490 fullpats = scmutil.expandpats(pats)
507 fullpats = scmutil.expandpats(pats)
491 dest = fullpats[-1]
508 dest = fullpats[-1]
492
509
493 if os.path.isdir(dest):
510 if os.path.isdir(dest):
494 if not os.path.isdir(makestandin(dest)):
511 if not os.path.isdir(makestandin(dest)):
495 os.makedirs(makestandin(dest))
512 os.makedirs(makestandin(dest))
496 # This could copy both lfiles and normal files in one command,
513 # This could copy both lfiles and normal files in one command,
497 # but we don't want to do that. First replace their matcher to
514 # but we don't want to do that. First replace their matcher to
498 # only match normal files and run it, then replace it to just
515 # only match normal files and run it, then replace it to just
499 # match largefiles and run it again.
516 # match largefiles and run it again.
500 nonormalfiles = False
517 nonormalfiles = False
501 nolfiles = False
518 nolfiles = False
502 installnormalfilesmatchfn(repo[None].manifest())
519 installnormalfilesmatchfn(repo[None].manifest())
503 try:
520 try:
504 try:
521 try:
505 result = orig(ui, repo, pats, opts, rename)
522 result = orig(ui, repo, pats, opts, rename)
506 except util.Abort, e:
523 except util.Abort, e:
507 if str(e) != _('no files to copy'):
524 if str(e) != _('no files to copy'):
508 raise e
525 raise e
509 else:
526 else:
510 nonormalfiles = True
527 nonormalfiles = True
511 result = 0
528 result = 0
512 finally:
529 finally:
513 restorematchfn()
530 restorematchfn()
514
531
515 # The first rename can cause our current working directory to be removed.
532 # The first rename can cause our current working directory to be removed.
516 # In that case there is nothing left to copy/rename so just quit.
533 # In that case there is nothing left to copy/rename so just quit.
517 try:
534 try:
518 repo.getcwd()
535 repo.getcwd()
519 except OSError:
536 except OSError:
520 return result
537 return result
521
538
522 try:
539 try:
523 try:
540 try:
524 # When we call orig below it creates the standins but we don't add
541 # When we call orig below it creates the standins but we don't add
525 # them to the dir state until later so lock during that time.
542 # them to the dir state until later so lock during that time.
526 wlock = repo.wlock()
543 wlock = repo.wlock()
527
544
528 manifest = repo[None].manifest()
545 manifest = repo[None].manifest()
529 def overridematch(ctx, pats=[], opts={}, globbed=False,
546 def overridematch(ctx, pats=[], opts={}, globbed=False,
530 default='relpath'):
547 default='relpath'):
531 newpats = []
548 newpats = []
532 # The patterns were previously mangled to add the standin
549 # The patterns were previously mangled to add the standin
533 # directory; we need to remove that now
550 # directory; we need to remove that now
534 for pat in pats:
551 for pat in pats:
535 if match_.patkind(pat) is None and lfutil.shortname in pat:
552 if match_.patkind(pat) is None and lfutil.shortname in pat:
536 newpats.append(pat.replace(lfutil.shortname, ''))
553 newpats.append(pat.replace(lfutil.shortname, ''))
537 else:
554 else:
538 newpats.append(pat)
555 newpats.append(pat)
539 match = oldmatch(ctx, newpats, opts, globbed, default)
556 match = oldmatch(ctx, newpats, opts, globbed, default)
540 m = copy.copy(match)
557 m = copy.copy(match)
541 lfile = lambda f: lfutil.standin(f) in manifest
558 lfile = lambda f: lfutil.standin(f) in manifest
542 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
559 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
543 m._fmap = set(m._files)
560 m._fmap = set(m._files)
544 m._always = False
561 m._always = False
545 origmatchfn = m.matchfn
562 origmatchfn = m.matchfn
546 m.matchfn = lambda f: (lfutil.isstandin(f) and
563 m.matchfn = lambda f: (lfutil.isstandin(f) and
547 (f in manifest) and
564 (f in manifest) and
548 origmatchfn(lfutil.splitstandin(f)) or
565 origmatchfn(lfutil.splitstandin(f)) or
549 None)
566 None)
550 return m
567 return m
551 oldmatch = installmatchfn(overridematch)
568 oldmatch = installmatchfn(overridematch)
552 listpats = []
569 listpats = []
553 for pat in pats:
570 for pat in pats:
554 if match_.patkind(pat) is not None:
571 if match_.patkind(pat) is not None:
555 listpats.append(pat)
572 listpats.append(pat)
556 else:
573 else:
557 listpats.append(makestandin(pat))
574 listpats.append(makestandin(pat))
558
575
559 try:
576 try:
560 origcopyfile = util.copyfile
577 origcopyfile = util.copyfile
561 copiedfiles = []
578 copiedfiles = []
562 def overridecopyfile(src, dest):
579 def overridecopyfile(src, dest):
563 if (lfutil.shortname in src and
580 if (lfutil.shortname in src and
564 dest.startswith(repo.wjoin(lfutil.shortname))):
581 dest.startswith(repo.wjoin(lfutil.shortname))):
565 destlfile = dest.replace(lfutil.shortname, '')
582 destlfile = dest.replace(lfutil.shortname, '')
566 if not opts['force'] and os.path.exists(destlfile):
583 if not opts['force'] and os.path.exists(destlfile):
567 raise IOError('',
584 raise IOError('',
568 _('destination largefile already exists'))
585 _('destination largefile already exists'))
569 copiedfiles.append((src, dest))
586 copiedfiles.append((src, dest))
570 origcopyfile(src, dest)
587 origcopyfile(src, dest)
571
588
572 util.copyfile = overridecopyfile
589 util.copyfile = overridecopyfile
573 result += orig(ui, repo, listpats, opts, rename)
590 result += orig(ui, repo, listpats, opts, rename)
574 finally:
591 finally:
575 util.copyfile = origcopyfile
592 util.copyfile = origcopyfile
576
593
577 lfdirstate = lfutil.openlfdirstate(ui, repo)
594 lfdirstate = lfutil.openlfdirstate(ui, repo)
578 for (src, dest) in copiedfiles:
595 for (src, dest) in copiedfiles:
579 if (lfutil.shortname in src and
596 if (lfutil.shortname in src and
580 dest.startswith(repo.wjoin(lfutil.shortname))):
597 dest.startswith(repo.wjoin(lfutil.shortname))):
581 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
598 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
582 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
599 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
583 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
600 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
584 if not os.path.isdir(destlfiledir):
601 if not os.path.isdir(destlfiledir):
585 os.makedirs(destlfiledir)
602 os.makedirs(destlfiledir)
586 if rename:
603 if rename:
587 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
604 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
588
605
589 # The file is gone, but this deletes any empty parent
606 # The file is gone, but this deletes any empty parent
590 # directories as a side-effect.
607 # directories as a side-effect.
591 util.unlinkpath(repo.wjoin(srclfile), True)
608 util.unlinkpath(repo.wjoin(srclfile), True)
592 lfdirstate.remove(srclfile)
609 lfdirstate.remove(srclfile)
593 else:
610 else:
594 util.copyfile(repo.wjoin(srclfile),
611 util.copyfile(repo.wjoin(srclfile),
595 repo.wjoin(destlfile))
612 repo.wjoin(destlfile))
596
613
597 lfdirstate.add(destlfile)
614 lfdirstate.add(destlfile)
598 lfdirstate.write()
615 lfdirstate.write()
599 except util.Abort, e:
616 except util.Abort, e:
600 if str(e) != _('no files to copy'):
617 if str(e) != _('no files to copy'):
601 raise e
618 raise e
602 else:
619 else:
603 nolfiles = True
620 nolfiles = True
604 finally:
621 finally:
605 restorematchfn()
622 restorematchfn()
606 wlock.release()
623 wlock.release()
607
624
608 if nolfiles and nonormalfiles:
625 if nolfiles and nonormalfiles:
609 raise util.Abort(_('no files to copy'))
626 raise util.Abort(_('no files to copy'))
610
627
611 return result
628 return result
612
629
613 # When the user calls revert, we have to be careful to not revert any
630 # When the user calls revert, we have to be careful to not revert any
614 # changes to other largefiles accidentally. This means we have to keep
631 # changes to other largefiles accidentally. This means we have to keep
615 # track of the largefiles that are being reverted so we only pull down
632 # track of the largefiles that are being reverted so we only pull down
616 # the necessary largefiles.
633 # the necessary largefiles.
617 #
634 #
618 # Standins are only updated (to match the hash of largefiles) before
635 # Standins are only updated (to match the hash of largefiles) before
619 # commits. Update the standins then run the original revert, changing
636 # commits. Update the standins then run the original revert, changing
620 # the matcher to hit standins instead of largefiles. Based on the
637 # the matcher to hit standins instead of largefiles. Based on the
621 # resulting standins update the largefiles.
638 # resulting standins update the largefiles.
622 def overriderevert(orig, ui, repo, *pats, **opts):
639 def overriderevert(orig, ui, repo, *pats, **opts):
623 # Because we put the standins in a bad state (by updating them)
640 # Because we put the standins in a bad state (by updating them)
624 # and then return them to a correct state we need to lock to
641 # and then return them to a correct state we need to lock to
625 # prevent others from changing them in their incorrect state.
642 # prevent others from changing them in their incorrect state.
626 wlock = repo.wlock()
643 wlock = repo.wlock()
627 try:
644 try:
628 lfdirstate = lfutil.openlfdirstate(ui, repo)
645 lfdirstate = lfutil.openlfdirstate(ui, repo)
629 (modified, added, removed, missing, unknown, ignored, clean) = \
646 (modified, added, removed, missing, unknown, ignored, clean) = \
630 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
647 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
631 lfdirstate.write()
648 lfdirstate.write()
632 for lfile in modified:
649 for lfile in modified:
633 lfutil.updatestandin(repo, lfutil.standin(lfile))
650 lfutil.updatestandin(repo, lfutil.standin(lfile))
634 for lfile in missing:
651 for lfile in missing:
635 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
652 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
636 os.unlink(repo.wjoin(lfutil.standin(lfile)))
653 os.unlink(repo.wjoin(lfutil.standin(lfile)))
637
654
638 oldstandins = lfutil.getstandinsstate(repo)
655 oldstandins = lfutil.getstandinsstate(repo)
639
656
640 def overridematch(ctx, pats=[], opts={}, globbed=False,
657 def overridematch(ctx, pats=[], opts={}, globbed=False,
641 default='relpath'):
658 default='relpath'):
642 match = oldmatch(ctx, pats, opts, globbed, default)
659 match = oldmatch(ctx, pats, opts, globbed, default)
643 m = copy.copy(match)
660 m = copy.copy(match)
644 def tostandin(f):
661 def tostandin(f):
645 if lfutil.standin(f) in ctx:
662 if lfutil.standin(f) in ctx:
646 return lfutil.standin(f)
663 return lfutil.standin(f)
647 elif lfutil.standin(f) in repo[None]:
664 elif lfutil.standin(f) in repo[None]:
648 return None
665 return None
649 return f
666 return f
650 m._files = [tostandin(f) for f in m._files]
667 m._files = [tostandin(f) for f in m._files]
651 m._files = [f for f in m._files if f is not None]
668 m._files = [f for f in m._files if f is not None]
652 m._fmap = set(m._files)
669 m._fmap = set(m._files)
653 m._always = False
670 m._always = False
654 origmatchfn = m.matchfn
671 origmatchfn = m.matchfn
655 def matchfn(f):
672 def matchfn(f):
656 if lfutil.isstandin(f):
673 if lfutil.isstandin(f):
657 return (origmatchfn(lfutil.splitstandin(f)) and
674 return (origmatchfn(lfutil.splitstandin(f)) and
658 (f in repo[None] or f in ctx))
675 (f in repo[None] or f in ctx))
659 return origmatchfn(f)
676 return origmatchfn(f)
660 m.matchfn = matchfn
677 m.matchfn = matchfn
661 return m
678 return m
662 oldmatch = installmatchfn(overridematch)
679 oldmatch = installmatchfn(overridematch)
663 try:
680 try:
664 orig(ui, repo, *pats, **opts)
681 orig(ui, repo, *pats, **opts)
665 finally:
682 finally:
666 restorematchfn()
683 restorematchfn()
667
684
668 newstandins = lfutil.getstandinsstate(repo)
685 newstandins = lfutil.getstandinsstate(repo)
669 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
686 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
670 # lfdirstate should be 'normallookup'-ed for updated files,
687 # lfdirstate should be 'normallookup'-ed for updated files,
671 # because reverting doesn't touch dirstate for 'normal' files
688 # because reverting doesn't touch dirstate for 'normal' files
672 # when target revision is explicitly specified: in such case,
689 # when target revision is explicitly specified: in such case,
673 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
690 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
674 # of target (standin) file.
691 # of target (standin) file.
675 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
692 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
676 normallookup=True)
693 normallookup=True)
677
694
678 finally:
695 finally:
679 wlock.release()
696 wlock.release()
680
697
681 def hgupdaterepo(orig, repo, node, overwrite):
698 def hgupdaterepo(orig, repo, node, overwrite):
682 if not overwrite:
699 if not overwrite:
683 # Only call updatelfiles on the standins that have changed to save time
700 # Only call updatelfiles on the standins that have changed to save time
684 oldstandins = lfutil.getstandinsstate(repo)
701 oldstandins = lfutil.getstandinsstate(repo)
685
702
686 result = orig(repo, node, overwrite)
703 result = orig(repo, node, overwrite)
687
704
688 filelist = None
705 filelist = None
689 if not overwrite:
706 if not overwrite:
690 newstandins = lfutil.getstandinsstate(repo)
707 newstandins = lfutil.getstandinsstate(repo)
691 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
708 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
692 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
709 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
693 return result
710 return result
694
711
695 def hgmerge(orig, repo, node, force=None, remind=True):
712 def hgmerge(orig, repo, node, force=None, remind=True):
696 result = orig(repo, node, force, remind)
713 result = orig(repo, node, force, remind)
697 lfcommands.updatelfiles(repo.ui, repo)
714 lfcommands.updatelfiles(repo.ui, repo)
698 return result
715 return result
699
716
700 # When we rebase a repository with remotely changed largefiles, we need to
717 # When we rebase a repository with remotely changed largefiles, we need to
701 # take some extra care so that the largefiles are correctly updated in the
718 # take some extra care so that the largefiles are correctly updated in the
702 # working copy
719 # working copy
703 def overridepull(orig, ui, repo, source=None, **opts):
720 def overridepull(orig, ui, repo, source=None, **opts):
704 revsprepull = len(repo)
721 revsprepull = len(repo)
705 if not source:
722 if not source:
706 source = 'default'
723 source = 'default'
707 repo.lfpullsource = source
724 repo.lfpullsource = source
708 if opts.get('rebase', False):
725 if opts.get('rebase', False):
709 repo._isrebasing = True
726 repo._isrebasing = True
710 try:
727 try:
711 if opts.get('update'):
728 if opts.get('update'):
712 del opts['update']
729 del opts['update']
713 ui.debug('--update and --rebase are not compatible, ignoring '
730 ui.debug('--update and --rebase are not compatible, ignoring '
714 'the update flag\n')
731 'the update flag\n')
715 del opts['rebase']
732 del opts['rebase']
716 origpostincoming = commands.postincoming
733 origpostincoming = commands.postincoming
717 def _dummy(*args, **kwargs):
734 def _dummy(*args, **kwargs):
718 pass
735 pass
719 commands.postincoming = _dummy
736 commands.postincoming = _dummy
720 try:
737 try:
721 result = commands.pull(ui, repo, source, **opts)
738 result = commands.pull(ui, repo, source, **opts)
722 finally:
739 finally:
723 commands.postincoming = origpostincoming
740 commands.postincoming = origpostincoming
724 revspostpull = len(repo)
741 revspostpull = len(repo)
725 if revspostpull > revsprepull:
742 if revspostpull > revsprepull:
726 result = result or rebase.rebase(ui, repo)
743 result = result or rebase.rebase(ui, repo)
727 finally:
744 finally:
728 repo._isrebasing = False
745 repo._isrebasing = False
729 else:
746 else:
730 result = orig(ui, repo, source, **opts)
747 result = orig(ui, repo, source, **opts)
731 revspostpull = len(repo)
748 revspostpull = len(repo)
732 lfrevs = opts.get('lfrev', [])
749 lfrevs = opts.get('lfrev', [])
733 if opts.get('all_largefiles'):
750 if opts.get('all_largefiles'):
734 lfrevs.append('pulled()')
751 lfrevs.append('pulled()')
735 if lfrevs and revspostpull > revsprepull:
752 if lfrevs and revspostpull > revsprepull:
736 numcached = 0
753 numcached = 0
737 repo.firstpulled = revsprepull # for pulled() revset expression
754 repo.firstpulled = revsprepull # for pulled() revset expression
738 try:
755 try:
739 for rev in scmutil.revrange(repo, lfrevs):
756 for rev in scmutil.revrange(repo, lfrevs):
740 ui.note(_('pulling largefiles for revision %s\n') % rev)
757 ui.note(_('pulling largefiles for revision %s\n') % rev)
741 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
758 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
742 numcached += len(cached)
759 numcached += len(cached)
743 finally:
760 finally:
744 del repo.firstpulled
761 del repo.firstpulled
745 ui.status(_("%d largefiles cached\n") % numcached)
762 ui.status(_("%d largefiles cached\n") % numcached)
746 return result
763 return result
747
764
748 def pulledrevsetsymbol(repo, subset, x):
765 def pulledrevsetsymbol(repo, subset, x):
749 """``pulled()``
766 """``pulled()``
750 Changesets that just has been pulled.
767 Changesets that just has been pulled.
751
768
752 Only available with largefiles from pull --lfrev expressions.
769 Only available with largefiles from pull --lfrev expressions.
753
770
754 .. container:: verbose
771 .. container:: verbose
755
772
756 Some examples:
773 Some examples:
757
774
758 - pull largefiles for all new changesets::
775 - pull largefiles for all new changesets::
759
776
760 hg pull -lfrev "pulled()"
777 hg pull -lfrev "pulled()"
761
778
762 - pull largefiles for all new branch heads::
779 - pull largefiles for all new branch heads::
763
780
764 hg pull -lfrev "head(pulled()) and not closed()"
781 hg pull -lfrev "head(pulled()) and not closed()"
765
782
766 """
783 """
767
784
768 try:
785 try:
769 firstpulled = repo.firstpulled
786 firstpulled = repo.firstpulled
770 except AttributeError:
787 except AttributeError:
771 raise util.Abort(_("pulled() only available in --lfrev"))
788 raise util.Abort(_("pulled() only available in --lfrev"))
772 return revset.baseset([r for r in subset if r >= firstpulled])
789 return revset.baseset([r for r in subset if r >= firstpulled])
773
790
774 def overrideclone(orig, ui, source, dest=None, **opts):
791 def overrideclone(orig, ui, source, dest=None, **opts):
775 d = dest
792 d = dest
776 if d is None:
793 if d is None:
777 d = hg.defaultdest(source)
794 d = hg.defaultdest(source)
778 if opts.get('all_largefiles') and not hg.islocal(d):
795 if opts.get('all_largefiles') and not hg.islocal(d):
779 raise util.Abort(_(
796 raise util.Abort(_(
780 '--all-largefiles is incompatible with non-local destination %s') %
797 '--all-largefiles is incompatible with non-local destination %s') %
781 d)
798 d)
782
799
783 return orig(ui, source, dest, **opts)
800 return orig(ui, source, dest, **opts)
784
801
785 def hgclone(orig, ui, opts, *args, **kwargs):
802 def hgclone(orig, ui, opts, *args, **kwargs):
786 result = orig(ui, opts, *args, **kwargs)
803 result = orig(ui, opts, *args, **kwargs)
787
804
788 if result is not None:
805 if result is not None:
789 sourcerepo, destrepo = result
806 sourcerepo, destrepo = result
790 repo = destrepo.local()
807 repo = destrepo.local()
791
808
792 # Caching is implicitly limited to 'rev' option, since the dest repo was
809 # Caching is implicitly limited to 'rev' option, since the dest repo was
793 # truncated at that point. The user may expect a download count with
810 # truncated at that point. The user may expect a download count with
794 # this option, so attempt whether or not this is a largefile repo.
811 # this option, so attempt whether or not this is a largefile repo.
795 if opts.get('all_largefiles'):
812 if opts.get('all_largefiles'):
796 success, missing = lfcommands.downloadlfiles(ui, repo, None)
813 success, missing = lfcommands.downloadlfiles(ui, repo, None)
797
814
798 if missing != 0:
815 if missing != 0:
799 return None
816 return None
800
817
801 return result
818 return result
802
819
803 def overriderebase(orig, ui, repo, **opts):
820 def overriderebase(orig, ui, repo, **opts):
804 repo._isrebasing = True
821 repo._isrebasing = True
805 try:
822 try:
806 return orig(ui, repo, **opts)
823 return orig(ui, repo, **opts)
807 finally:
824 finally:
808 repo._isrebasing = False
825 repo._isrebasing = False
809
826
810 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
827 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
811 prefix=None, mtime=None, subrepos=None):
828 prefix=None, mtime=None, subrepos=None):
812 # No need to lock because we are only reading history and
829 # No need to lock because we are only reading history and
813 # largefile caches, neither of which are modified.
830 # largefile caches, neither of which are modified.
814 lfcommands.cachelfiles(repo.ui, repo, node)
831 lfcommands.cachelfiles(repo.ui, repo, node)
815
832
816 if kind not in archival.archivers:
833 if kind not in archival.archivers:
817 raise util.Abort(_("unknown archive type '%s'") % kind)
834 raise util.Abort(_("unknown archive type '%s'") % kind)
818
835
819 ctx = repo[node]
836 ctx = repo[node]
820
837
821 if kind == 'files':
838 if kind == 'files':
822 if prefix:
839 if prefix:
823 raise util.Abort(
840 raise util.Abort(
824 _('cannot give prefix when archiving to files'))
841 _('cannot give prefix when archiving to files'))
825 else:
842 else:
826 prefix = archival.tidyprefix(dest, kind, prefix)
843 prefix = archival.tidyprefix(dest, kind, prefix)
827
844
828 def write(name, mode, islink, getdata):
845 def write(name, mode, islink, getdata):
829 if matchfn and not matchfn(name):
846 if matchfn and not matchfn(name):
830 return
847 return
831 data = getdata()
848 data = getdata()
832 if decode:
849 if decode:
833 data = repo.wwritedata(name, data)
850 data = repo.wwritedata(name, data)
834 archiver.addfile(prefix + name, mode, islink, data)
851 archiver.addfile(prefix + name, mode, islink, data)
835
852
836 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
853 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
837
854
838 if repo.ui.configbool("ui", "archivemeta", True):
855 if repo.ui.configbool("ui", "archivemeta", True):
839 def metadata():
856 def metadata():
840 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
857 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
841 hex(repo.changelog.node(0)), hex(node), ctx.branch())
858 hex(repo.changelog.node(0)), hex(node), ctx.branch())
842
859
843 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
860 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
844 if repo.tagtype(t) == 'global')
861 if repo.tagtype(t) == 'global')
845 if not tags:
862 if not tags:
846 repo.ui.pushbuffer()
863 repo.ui.pushbuffer()
847 opts = {'template': '{latesttag}\n{latesttagdistance}',
864 opts = {'template': '{latesttag}\n{latesttagdistance}',
848 'style': '', 'patch': None, 'git': None}
865 'style': '', 'patch': None, 'git': None}
849 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
866 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
850 ltags, dist = repo.ui.popbuffer().split('\n')
867 ltags, dist = repo.ui.popbuffer().split('\n')
851 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
868 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
852 tags += 'latesttagdistance: %s\n' % dist
869 tags += 'latesttagdistance: %s\n' % dist
853
870
854 return base + tags
871 return base + tags
855
872
856 write('.hg_archival.txt', 0644, False, metadata)
873 write('.hg_archival.txt', 0644, False, metadata)
857
874
858 for f in ctx:
875 for f in ctx:
859 ff = ctx.flags(f)
876 ff = ctx.flags(f)
860 getdata = ctx[f].data
877 getdata = ctx[f].data
861 if lfutil.isstandin(f):
878 if lfutil.isstandin(f):
862 path = lfutil.findfile(repo, getdata().strip())
879 path = lfutil.findfile(repo, getdata().strip())
863 if path is None:
880 if path is None:
864 raise util.Abort(
881 raise util.Abort(
865 _('largefile %s not found in repo store or system cache')
882 _('largefile %s not found in repo store or system cache')
866 % lfutil.splitstandin(f))
883 % lfutil.splitstandin(f))
867 f = lfutil.splitstandin(f)
884 f = lfutil.splitstandin(f)
868
885
869 def getdatafn():
886 def getdatafn():
870 fd = None
887 fd = None
871 try:
888 try:
872 fd = open(path, 'rb')
889 fd = open(path, 'rb')
873 return fd.read()
890 return fd.read()
874 finally:
891 finally:
875 if fd:
892 if fd:
876 fd.close()
893 fd.close()
877
894
878 getdata = getdatafn
895 getdata = getdatafn
879 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
896 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
880
897
881 if subrepos:
898 if subrepos:
882 for subpath in sorted(ctx.substate):
899 for subpath in sorted(ctx.substate):
883 sub = ctx.sub(subpath)
900 sub = ctx.sub(subpath)
884 submatch = match_.narrowmatcher(subpath, matchfn)
901 submatch = match_.narrowmatcher(subpath, matchfn)
885 sub.archive(repo.ui, archiver, prefix, submatch)
902 sub.archive(repo.ui, archiver, prefix, submatch)
886
903
887 archiver.done()
904 archiver.done()
888
905
889 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
906 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
890 repo._get(repo._state + ('hg',))
907 repo._get(repo._state + ('hg',))
891 rev = repo._state[1]
908 rev = repo._state[1]
892 ctx = repo._repo[rev]
909 ctx = repo._repo[rev]
893
910
894 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
911 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
895
912
896 def write(name, mode, islink, getdata):
913 def write(name, mode, islink, getdata):
897 # At this point, the standin has been replaced with the largefile name,
914 # At this point, the standin has been replaced with the largefile name,
898 # so the normal matcher works here without the lfutil variants.
915 # so the normal matcher works here without the lfutil variants.
899 if match and not match(f):
916 if match and not match(f):
900 return
917 return
901 data = getdata()
918 data = getdata()
902
919
903 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
920 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
904
921
905 for f in ctx:
922 for f in ctx:
906 ff = ctx.flags(f)
923 ff = ctx.flags(f)
907 getdata = ctx[f].data
924 getdata = ctx[f].data
908 if lfutil.isstandin(f):
925 if lfutil.isstandin(f):
909 path = lfutil.findfile(repo._repo, getdata().strip())
926 path = lfutil.findfile(repo._repo, getdata().strip())
910 if path is None:
927 if path is None:
911 raise util.Abort(
928 raise util.Abort(
912 _('largefile %s not found in repo store or system cache')
929 _('largefile %s not found in repo store or system cache')
913 % lfutil.splitstandin(f))
930 % lfutil.splitstandin(f))
914 f = lfutil.splitstandin(f)
931 f = lfutil.splitstandin(f)
915
932
916 def getdatafn():
933 def getdatafn():
917 fd = None
934 fd = None
918 try:
935 try:
919 fd = open(os.path.join(prefix, path), 'rb')
936 fd = open(os.path.join(prefix, path), 'rb')
920 return fd.read()
937 return fd.read()
921 finally:
938 finally:
922 if fd:
939 if fd:
923 fd.close()
940 fd.close()
924
941
925 getdata = getdatafn
942 getdata = getdatafn
926
943
927 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
944 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
928
945
929 for subpath in sorted(ctx.substate):
946 for subpath in sorted(ctx.substate):
930 sub = ctx.sub(subpath)
947 sub = ctx.sub(subpath)
931 submatch = match_.narrowmatcher(subpath, match)
948 submatch = match_.narrowmatcher(subpath, match)
932 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
949 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
933 submatch)
950 submatch)
934
951
935 # If a largefile is modified, the change is not reflected in its
952 # If a largefile is modified, the change is not reflected in its
936 # standin until a commit. cmdutil.bailifchanged() raises an exception
953 # standin until a commit. cmdutil.bailifchanged() raises an exception
937 # if the repo has uncommitted changes. Wrap it to also check if
954 # if the repo has uncommitted changes. Wrap it to also check if
938 # largefiles were changed. This is used by bisect and backout.
955 # largefiles were changed. This is used by bisect and backout.
939 def overridebailifchanged(orig, repo):
956 def overridebailifchanged(orig, repo):
940 orig(repo)
957 orig(repo)
941 repo.lfstatus = True
958 repo.lfstatus = True
942 modified, added, removed, deleted = repo.status()[:4]
959 modified, added, removed, deleted = repo.status()[:4]
943 repo.lfstatus = False
960 repo.lfstatus = False
944 if modified or added or removed or deleted:
961 if modified or added or removed or deleted:
945 raise util.Abort(_('uncommitted changes'))
962 raise util.Abort(_('uncommitted changes'))
946
963
947 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
964 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
948 def overridefetch(orig, ui, repo, *pats, **opts):
965 def overridefetch(orig, ui, repo, *pats, **opts):
949 repo.lfstatus = True
966 repo.lfstatus = True
950 modified, added, removed, deleted = repo.status()[:4]
967 modified, added, removed, deleted = repo.status()[:4]
951 repo.lfstatus = False
968 repo.lfstatus = False
952 if modified or added or removed or deleted:
969 if modified or added or removed or deleted:
953 raise util.Abort(_('uncommitted changes'))
970 raise util.Abort(_('uncommitted changes'))
954 return orig(ui, repo, *pats, **opts)
971 return orig(ui, repo, *pats, **opts)
955
972
956 def overrideforget(orig, ui, repo, *pats, **opts):
973 def overrideforget(orig, ui, repo, *pats, **opts):
957 installnormalfilesmatchfn(repo[None].manifest())
974 installnormalfilesmatchfn(repo[None].manifest())
958 result = orig(ui, repo, *pats, **opts)
975 result = orig(ui, repo, *pats, **opts)
959 restorematchfn()
976 restorematchfn()
960 m = scmutil.match(repo[None], pats, opts)
977 m = scmutil.match(repo[None], pats, opts)
961
978
962 try:
979 try:
963 repo.lfstatus = True
980 repo.lfstatus = True
964 s = repo.status(match=m, clean=True)
981 s = repo.status(match=m, clean=True)
965 finally:
982 finally:
966 repo.lfstatus = False
983 repo.lfstatus = False
967 forget = sorted(s[0] + s[1] + s[3] + s[6])
984 forget = sorted(s[0] + s[1] + s[3] + s[6])
968 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
985 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
969
986
970 for f in forget:
987 for f in forget:
971 if lfutil.standin(f) not in repo.dirstate and not \
988 if lfutil.standin(f) not in repo.dirstate and not \
972 os.path.isdir(m.rel(lfutil.standin(f))):
989 os.path.isdir(m.rel(lfutil.standin(f))):
973 ui.warn(_('not removing %s: file is already untracked\n')
990 ui.warn(_('not removing %s: file is already untracked\n')
974 % m.rel(f))
991 % m.rel(f))
975 result = 1
992 result = 1
976
993
977 for f in forget:
994 for f in forget:
978 if ui.verbose or not m.exact(f):
995 if ui.verbose or not m.exact(f):
979 ui.status(_('removing %s\n') % m.rel(f))
996 ui.status(_('removing %s\n') % m.rel(f))
980
997
981 # Need to lock because standin files are deleted then removed from the
998 # Need to lock because standin files are deleted then removed from the
982 # repository and we could race in-between.
999 # repository and we could race in-between.
983 wlock = repo.wlock()
1000 wlock = repo.wlock()
984 try:
1001 try:
985 lfdirstate = lfutil.openlfdirstate(ui, repo)
1002 lfdirstate = lfutil.openlfdirstate(ui, repo)
986 for f in forget:
1003 for f in forget:
987 if lfdirstate[f] == 'a':
1004 if lfdirstate[f] == 'a':
988 lfdirstate.drop(f)
1005 lfdirstate.drop(f)
989 else:
1006 else:
990 lfdirstate.remove(f)
1007 lfdirstate.remove(f)
991 lfdirstate.write()
1008 lfdirstate.write()
992 standins = [lfutil.standin(f) for f in forget]
1009 standins = [lfutil.standin(f) for f in forget]
993 for f in standins:
1010 for f in standins:
994 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1011 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
995 repo[None].forget(standins)
1012 repo[None].forget(standins)
996 finally:
1013 finally:
997 wlock.release()
1014 wlock.release()
998
1015
999 return result
1016 return result
1000
1017
1001 def _getoutgoings(repo, other, missing, addfunc):
1018 def _getoutgoings(repo, other, missing, addfunc):
1002 """get pairs of filename and largefile hash in outgoing revisions
1019 """get pairs of filename and largefile hash in outgoing revisions
1003 in 'missing'.
1020 in 'missing'.
1004
1021
1005 largefiles already existing on 'other' repository are ignored.
1022 largefiles already existing on 'other' repository are ignored.
1006
1023
1007 'addfunc' is invoked with each unique pairs of filename and
1024 'addfunc' is invoked with each unique pairs of filename and
1008 largefile hash value.
1025 largefile hash value.
1009 """
1026 """
1010 knowns = set()
1027 knowns = set()
1011 lfhashes = set()
1028 lfhashes = set()
1012 def dedup(fn, lfhash):
1029 def dedup(fn, lfhash):
1013 k = (fn, lfhash)
1030 k = (fn, lfhash)
1014 if k not in knowns:
1031 if k not in knowns:
1015 knowns.add(k)
1032 knowns.add(k)
1016 lfhashes.add(lfhash)
1033 lfhashes.add(lfhash)
1017 lfutil.getlfilestoupload(repo, missing, dedup)
1034 lfutil.getlfilestoupload(repo, missing, dedup)
1018 if lfhashes:
1035 if lfhashes:
1019 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1036 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1020 for fn, lfhash in knowns:
1037 for fn, lfhash in knowns:
1021 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1038 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1022 addfunc(fn, lfhash)
1039 addfunc(fn, lfhash)
1023
1040
1024 def outgoinghook(ui, repo, other, opts, missing):
1041 def outgoinghook(ui, repo, other, opts, missing):
1025 if opts.pop('large', None):
1042 if opts.pop('large', None):
1026 lfhashes = set()
1043 lfhashes = set()
1027 if ui.debugflag:
1044 if ui.debugflag:
1028 toupload = {}
1045 toupload = {}
1029 def addfunc(fn, lfhash):
1046 def addfunc(fn, lfhash):
1030 if fn not in toupload:
1047 if fn not in toupload:
1031 toupload[fn] = []
1048 toupload[fn] = []
1032 toupload[fn].append(lfhash)
1049 toupload[fn].append(lfhash)
1033 lfhashes.add(lfhash)
1050 lfhashes.add(lfhash)
1034 def showhashes(fn):
1051 def showhashes(fn):
1035 for lfhash in sorted(toupload[fn]):
1052 for lfhash in sorted(toupload[fn]):
1036 ui.debug(' %s\n' % (lfhash))
1053 ui.debug(' %s\n' % (lfhash))
1037 else:
1054 else:
1038 toupload = set()
1055 toupload = set()
1039 def addfunc(fn, lfhash):
1056 def addfunc(fn, lfhash):
1040 toupload.add(fn)
1057 toupload.add(fn)
1041 lfhashes.add(lfhash)
1058 lfhashes.add(lfhash)
1042 def showhashes(fn):
1059 def showhashes(fn):
1043 pass
1060 pass
1044 _getoutgoings(repo, other, missing, addfunc)
1061 _getoutgoings(repo, other, missing, addfunc)
1045
1062
1046 if not toupload:
1063 if not toupload:
1047 ui.status(_('largefiles: no files to upload\n'))
1064 ui.status(_('largefiles: no files to upload\n'))
1048 else:
1065 else:
1049 ui.status(_('largefiles to upload (%d entities):\n')
1066 ui.status(_('largefiles to upload (%d entities):\n')
1050 % (len(lfhashes)))
1067 % (len(lfhashes)))
1051 for file in sorted(toupload):
1068 for file in sorted(toupload):
1052 ui.status(lfutil.splitstandin(file) + '\n')
1069 ui.status(lfutil.splitstandin(file) + '\n')
1053 showhashes(file)
1070 showhashes(file)
1054 ui.status('\n')
1071 ui.status('\n')
1055
1072
1056 def summaryremotehook(ui, repo, opts, changes):
1073 def summaryremotehook(ui, repo, opts, changes):
1057 largeopt = opts.get('large', False)
1074 largeopt = opts.get('large', False)
1058 if changes is None:
1075 if changes is None:
1059 if largeopt:
1076 if largeopt:
1060 return (False, True) # only outgoing check is needed
1077 return (False, True) # only outgoing check is needed
1061 else:
1078 else:
1062 return (False, False)
1079 return (False, False)
1063 elif largeopt:
1080 elif largeopt:
1064 url, branch, peer, outgoing = changes[1]
1081 url, branch, peer, outgoing = changes[1]
1065 if peer is None:
1082 if peer is None:
1066 # i18n: column positioning for "hg summary"
1083 # i18n: column positioning for "hg summary"
1067 ui.status(_('largefiles: (no remote repo)\n'))
1084 ui.status(_('largefiles: (no remote repo)\n'))
1068 return
1085 return
1069
1086
1070 toupload = set()
1087 toupload = set()
1071 lfhashes = set()
1088 lfhashes = set()
1072 def addfunc(fn, lfhash):
1089 def addfunc(fn, lfhash):
1073 toupload.add(fn)
1090 toupload.add(fn)
1074 lfhashes.add(lfhash)
1091 lfhashes.add(lfhash)
1075 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1092 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1076
1093
1077 if not toupload:
1094 if not toupload:
1078 # i18n: column positioning for "hg summary"
1095 # i18n: column positioning for "hg summary"
1079 ui.status(_('largefiles: (no files to upload)\n'))
1096 ui.status(_('largefiles: (no files to upload)\n'))
1080 else:
1097 else:
1081 # i18n: column positioning for "hg summary"
1098 # i18n: column positioning for "hg summary"
1082 ui.status(_('largefiles: %d entities for %d files to upload\n')
1099 ui.status(_('largefiles: %d entities for %d files to upload\n')
1083 % (len(lfhashes), len(toupload)))
1100 % (len(lfhashes), len(toupload)))
1084
1101
1085 def overridesummary(orig, ui, repo, *pats, **opts):
1102 def overridesummary(orig, ui, repo, *pats, **opts):
1086 try:
1103 try:
1087 repo.lfstatus = True
1104 repo.lfstatus = True
1088 orig(ui, repo, *pats, **opts)
1105 orig(ui, repo, *pats, **opts)
1089 finally:
1106 finally:
1090 repo.lfstatus = False
1107 repo.lfstatus = False
1091
1108
1092 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1109 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1093 similarity=None):
1110 similarity=None):
1094 if not lfutil.islfilesrepo(repo):
1111 if not lfutil.islfilesrepo(repo):
1095 return orig(repo, pats, opts, dry_run, similarity)
1112 return orig(repo, pats, opts, dry_run, similarity)
1096 # Get the list of missing largefiles so we can remove them
1113 # Get the list of missing largefiles so we can remove them
1097 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1114 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1098 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1115 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1099 False, False)
1116 False, False)
1100 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1117 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1101
1118
1102 # Call into the normal remove code, but the removing of the standin, we want
1119 # Call into the normal remove code, but the removing of the standin, we want
1103 # to have handled by original addremove. Monkey patching here makes sure
1120 # to have handled by original addremove. Monkey patching here makes sure
1104 # we don't remove the standin in the largefiles code, preventing a very
1121 # we don't remove the standin in the largefiles code, preventing a very
1105 # confused state later.
1122 # confused state later.
1106 if missing:
1123 if missing:
1107 m = [repo.wjoin(f) for f in missing]
1124 m = [repo.wjoin(f) for f in missing]
1108 repo._isaddremove = True
1125 repo._isaddremove = True
1109 removelargefiles(repo.ui, repo, *m, **opts)
1126 removelargefiles(repo.ui, repo, *m, **opts)
1110 repo._isaddremove = False
1127 repo._isaddremove = False
1111 # Call into the normal add code, and any files that *should* be added as
1128 # Call into the normal add code, and any files that *should* be added as
1112 # largefiles will be
1129 # largefiles will be
1113 addlargefiles(repo.ui, repo, *pats, **opts)
1130 addlargefiles(repo.ui, repo, *pats, **opts)
1114 # Now that we've handled largefiles, hand off to the original addremove
1131 # Now that we've handled largefiles, hand off to the original addremove
1115 # function to take care of the rest. Make sure it doesn't do anything with
1132 # function to take care of the rest. Make sure it doesn't do anything with
1116 # largefiles by installing a matcher that will ignore them.
1133 # largefiles by installing a matcher that will ignore them.
1117 installnormalfilesmatchfn(repo[None].manifest())
1134 installnormalfilesmatchfn(repo[None].manifest())
1118 result = orig(repo, pats, opts, dry_run, similarity)
1135 result = orig(repo, pats, opts, dry_run, similarity)
1119 restorematchfn()
1136 restorematchfn()
1120 return result
1137 return result
1121
1138
1122 # Calling purge with --all will cause the largefiles to be deleted.
1139 # Calling purge with --all will cause the largefiles to be deleted.
1123 # Override repo.status to prevent this from happening.
1140 # Override repo.status to prevent this from happening.
1124 def overridepurge(orig, ui, repo, *dirs, **opts):
1141 def overridepurge(orig, ui, repo, *dirs, **opts):
1125 # XXX large file status is buggy when used on repo proxy.
1142 # XXX large file status is buggy when used on repo proxy.
1126 # XXX this needs to be investigate.
1143 # XXX this needs to be investigate.
1127 repo = repo.unfiltered()
1144 repo = repo.unfiltered()
1128 oldstatus = repo.status
1145 oldstatus = repo.status
1129 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1146 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1130 clean=False, unknown=False, listsubrepos=False):
1147 clean=False, unknown=False, listsubrepos=False):
1131 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1148 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1132 listsubrepos)
1149 listsubrepos)
1133 lfdirstate = lfutil.openlfdirstate(ui, repo)
1150 lfdirstate = lfutil.openlfdirstate(ui, repo)
1134 modified, added, removed, deleted, unknown, ignored, clean = r
1151 modified, added, removed, deleted, unknown, ignored, clean = r
1135 unknown = [f for f in unknown if lfdirstate[f] == '?']
1152 unknown = [f for f in unknown if lfdirstate[f] == '?']
1136 ignored = [f for f in ignored if lfdirstate[f] == '?']
1153 ignored = [f for f in ignored if lfdirstate[f] == '?']
1137 return modified, added, removed, deleted, unknown, ignored, clean
1154 return modified, added, removed, deleted, unknown, ignored, clean
1138 repo.status = overridestatus
1155 repo.status = overridestatus
1139 orig(ui, repo, *dirs, **opts)
1156 orig(ui, repo, *dirs, **opts)
1140 repo.status = oldstatus
1157 repo.status = oldstatus
1141
1158
1142 def overriderollback(orig, ui, repo, **opts):
1159 def overriderollback(orig, ui, repo, **opts):
1143 wlock = repo.wlock()
1160 wlock = repo.wlock()
1144 try:
1161 try:
1145 result = orig(ui, repo, **opts)
1162 result = orig(ui, repo, **opts)
1146 merge.update(repo, node=None, branchmerge=False, force=True,
1163 merge.update(repo, node=None, branchmerge=False, force=True,
1147 partial=lfutil.isstandin)
1164 partial=lfutil.isstandin)
1148
1165
1149 lfdirstate = lfutil.openlfdirstate(ui, repo)
1166 lfdirstate = lfutil.openlfdirstate(ui, repo)
1150 orphans = set(lfdirstate)
1167 orphans = set(lfdirstate)
1151 lfiles = lfutil.listlfiles(repo)
1168 lfiles = lfutil.listlfiles(repo)
1152 for file in lfiles:
1169 for file in lfiles:
1153 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1170 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1154 orphans.discard(file)
1171 orphans.discard(file)
1155 for lfile in orphans:
1172 for lfile in orphans:
1156 lfdirstate.drop(lfile)
1173 lfdirstate.drop(lfile)
1157 lfdirstate.write()
1174 lfdirstate.write()
1158 finally:
1175 finally:
1159 wlock.release()
1176 wlock.release()
1160 return result
1177 return result
1161
1178
1162 def overridetransplant(orig, ui, repo, *revs, **opts):
1179 def overridetransplant(orig, ui, repo, *revs, **opts):
1163 try:
1180 try:
1164 oldstandins = lfutil.getstandinsstate(repo)
1181 oldstandins = lfutil.getstandinsstate(repo)
1165 repo._istransplanting = True
1182 repo._istransplanting = True
1166 result = orig(ui, repo, *revs, **opts)
1183 result = orig(ui, repo, *revs, **opts)
1167 newstandins = lfutil.getstandinsstate(repo)
1184 newstandins = lfutil.getstandinsstate(repo)
1168 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1185 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1169 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1186 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1170 printmessage=True)
1187 printmessage=True)
1171 finally:
1188 finally:
1172 repo._istransplanting = False
1189 repo._istransplanting = False
1173 return result
1190 return result
1174
1191
1175 def overridecat(orig, ui, repo, file1, *pats, **opts):
1192 def overridecat(orig, ui, repo, file1, *pats, **opts):
1176 ctx = scmutil.revsingle(repo, opts.get('rev'))
1193 ctx = scmutil.revsingle(repo, opts.get('rev'))
1177 err = 1
1194 err = 1
1178 notbad = set()
1195 notbad = set()
1179 m = scmutil.match(ctx, (file1,) + pats, opts)
1196 m = scmutil.match(ctx, (file1,) + pats, opts)
1180 origmatchfn = m.matchfn
1197 origmatchfn = m.matchfn
1181 def lfmatchfn(f):
1198 def lfmatchfn(f):
1182 if origmatchfn(f):
1199 if origmatchfn(f):
1183 return True
1200 return True
1184 lf = lfutil.splitstandin(f)
1201 lf = lfutil.splitstandin(f)
1185 if lf is None:
1202 if lf is None:
1186 return False
1203 return False
1187 notbad.add(lf)
1204 notbad.add(lf)
1188 return origmatchfn(lf)
1205 return origmatchfn(lf)
1189 m.matchfn = lfmatchfn
1206 m.matchfn = lfmatchfn
1190 origbadfn = m.bad
1207 origbadfn = m.bad
1191 def lfbadfn(f, msg):
1208 def lfbadfn(f, msg):
1192 if not f in notbad:
1209 if not f in notbad:
1193 origbadfn(f, msg)
1210 origbadfn(f, msg)
1194 m.bad = lfbadfn
1211 m.bad = lfbadfn
1195 for f in ctx.walk(m):
1212 for f in ctx.walk(m):
1196 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1213 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1197 pathname=f)
1214 pathname=f)
1198 lf = lfutil.splitstandin(f)
1215 lf = lfutil.splitstandin(f)
1199 if lf is None or origmatchfn(f):
1216 if lf is None or origmatchfn(f):
1200 # duplicating unreachable code from commands.cat
1217 # duplicating unreachable code from commands.cat
1201 data = ctx[f].data()
1218 data = ctx[f].data()
1202 if opts.get('decode'):
1219 if opts.get('decode'):
1203 data = repo.wwritedata(f, data)
1220 data = repo.wwritedata(f, data)
1204 fp.write(data)
1221 fp.write(data)
1205 else:
1222 else:
1206 hash = lfutil.readstandin(repo, lf, ctx.rev())
1223 hash = lfutil.readstandin(repo, lf, ctx.rev())
1207 if not lfutil.inusercache(repo.ui, hash):
1224 if not lfutil.inusercache(repo.ui, hash):
1208 store = basestore._openstore(repo)
1225 store = basestore._openstore(repo)
1209 success, missing = store.get([(lf, hash)])
1226 success, missing = store.get([(lf, hash)])
1210 if len(success) != 1:
1227 if len(success) != 1:
1211 raise util.Abort(
1228 raise util.Abort(
1212 _('largefile %s is not in cache and could not be '
1229 _('largefile %s is not in cache and could not be '
1213 'downloaded') % lf)
1230 'downloaded') % lf)
1214 path = lfutil.usercachepath(repo.ui, hash)
1231 path = lfutil.usercachepath(repo.ui, hash)
1215 fpin = open(path, "rb")
1232 fpin = open(path, "rb")
1216 for chunk in util.filechunkiter(fpin, 128 * 1024):
1233 for chunk in util.filechunkiter(fpin, 128 * 1024):
1217 fp.write(chunk)
1234 fp.write(chunk)
1218 fpin.close()
1235 fpin.close()
1219 fp.close()
1236 fp.close()
1220 err = 0
1237 err = 0
1221 return err
1238 return err
1222
1239
1223 def mercurialsinkbefore(orig, sink):
1240 def mercurialsinkbefore(orig, sink):
1224 sink.repo._isconverting = True
1241 sink.repo._isconverting = True
1225 orig(sink)
1242 orig(sink)
1226
1243
1227 def mercurialsinkafter(orig, sink):
1244 def mercurialsinkafter(orig, sink):
1228 sink.repo._isconverting = False
1245 sink.repo._isconverting = False
1229 orig(sink)
1246 orig(sink)
@@ -1,2678 +1,2685 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
14 import changelog
14 import changelog
15 import bookmarks
15 import bookmarks
16 import lock as lockmod
16 import lock as lockmod
17
17
18 def parsealiases(cmd):
18 def parsealiases(cmd):
19 return cmd.lstrip("^").split("|")
19 return cmd.lstrip("^").split("|")
20
20
21 def findpossible(cmd, table, strict=False):
21 def findpossible(cmd, table, strict=False):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29
29
30 if cmd in table:
30 if cmd in table:
31 # short-circuit exact matches, "log" alias beats "^log|history"
31 # short-circuit exact matches, "log" alias beats "^log|history"
32 keys = [cmd]
32 keys = [cmd]
33 else:
33 else:
34 keys = table.keys()
34 keys = table.keys()
35
35
36 for e in keys:
36 for e in keys:
37 aliases = parsealiases(e)
37 aliases = parsealiases(e)
38 found = None
38 found = None
39 if cmd in aliases:
39 if cmd in aliases:
40 found = cmd
40 found = cmd
41 elif not strict:
41 elif not strict:
42 for a in aliases:
42 for a in aliases:
43 if a.startswith(cmd):
43 if a.startswith(cmd):
44 found = a
44 found = a
45 break
45 break
46 if found is not None:
46 if found is not None:
47 if aliases[0].startswith("debug") or found.startswith("debug"):
47 if aliases[0].startswith("debug") or found.startswith("debug"):
48 debugchoice[found] = (aliases, table[e])
48 debugchoice[found] = (aliases, table[e])
49 else:
49 else:
50 choice[found] = (aliases, table[e])
50 choice[found] = (aliases, table[e])
51
51
52 if not choice and debugchoice:
52 if not choice and debugchoice:
53 choice = debugchoice
53 choice = debugchoice
54
54
55 return choice
55 return choice
56
56
57 def findcmd(cmd, table, strict=True):
57 def findcmd(cmd, table, strict=True):
58 """Return (aliases, command table entry) for command string."""
58 """Return (aliases, command table entry) for command string."""
59 choice = findpossible(cmd, table, strict)
59 choice = findpossible(cmd, table, strict)
60
60
61 if cmd in choice:
61 if cmd in choice:
62 return choice[cmd]
62 return choice[cmd]
63
63
64 if len(choice) > 1:
64 if len(choice) > 1:
65 clist = choice.keys()
65 clist = choice.keys()
66 clist.sort()
66 clist.sort()
67 raise error.AmbiguousCommand(cmd, clist)
67 raise error.AmbiguousCommand(cmd, clist)
68
68
69 if choice:
69 if choice:
70 return choice.values()[0]
70 return choice.values()[0]
71
71
72 raise error.UnknownCommand(cmd)
72 raise error.UnknownCommand(cmd)
73
73
74 def findrepo(p):
74 def findrepo(p):
75 while not os.path.isdir(os.path.join(p, ".hg")):
75 while not os.path.isdir(os.path.join(p, ".hg")):
76 oldp, p = p, os.path.dirname(p)
76 oldp, p = p, os.path.dirname(p)
77 if p == oldp:
77 if p == oldp:
78 return None
78 return None
79
79
80 return p
80 return p
81
81
82 def bailifchanged(repo):
82 def bailifchanged(repo):
83 if repo.dirstate.p2() != nullid:
83 if repo.dirstate.p2() != nullid:
84 raise util.Abort(_('outstanding uncommitted merge'))
84 raise util.Abort(_('outstanding uncommitted merge'))
85 modified, added, removed, deleted = repo.status()[:4]
85 modified, added, removed, deleted = repo.status()[:4]
86 if modified or added or removed or deleted:
86 if modified or added or removed or deleted:
87 raise util.Abort(_('uncommitted changes'))
87 raise util.Abort(_('uncommitted changes'))
88 ctx = repo[None]
88 ctx = repo[None]
89 for s in sorted(ctx.substate):
89 for s in sorted(ctx.substate):
90 if ctx.sub(s).dirty():
90 if ctx.sub(s).dirty():
91 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
91 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
92
92
93 def logmessage(ui, opts):
93 def logmessage(ui, opts):
94 """ get the log message according to -m and -l option """
94 """ get the log message according to -m and -l option """
95 message = opts.get('message')
95 message = opts.get('message')
96 logfile = opts.get('logfile')
96 logfile = opts.get('logfile')
97
97
98 if message and logfile:
98 if message and logfile:
99 raise util.Abort(_('options --message and --logfile are mutually '
99 raise util.Abort(_('options --message and --logfile are mutually '
100 'exclusive'))
100 'exclusive'))
101 if not message and logfile:
101 if not message and logfile:
102 try:
102 try:
103 if logfile == '-':
103 if logfile == '-':
104 message = ui.fin.read()
104 message = ui.fin.read()
105 else:
105 else:
106 message = '\n'.join(util.readfile(logfile).splitlines())
106 message = '\n'.join(util.readfile(logfile).splitlines())
107 except IOError, inst:
107 except IOError, inst:
108 raise util.Abort(_("can't read commit message '%s': %s") %
108 raise util.Abort(_("can't read commit message '%s': %s") %
109 (logfile, inst.strerror))
109 (logfile, inst.strerror))
110 return message
110 return message
111
111
112 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
112 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
113 editform='', **opts):
113 editform='', **opts):
114 """get appropriate commit message editor according to '--edit' option
114 """get appropriate commit message editor according to '--edit' option
115
115
116 'finishdesc' is a function to be called with edited commit message
116 'finishdesc' is a function to be called with edited commit message
117 (= 'description' of the new changeset) just after editing, but
117 (= 'description' of the new changeset) just after editing, but
118 before checking empty-ness. It should return actual text to be
118 before checking empty-ness. It should return actual text to be
119 stored into history. This allows to change description before
119 stored into history. This allows to change description before
120 storing.
120 storing.
121
121
122 'extramsg' is a extra message to be shown in the editor instead of
122 'extramsg' is a extra message to be shown in the editor instead of
123 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
123 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
124 is automatically added.
124 is automatically added.
125
125
126 'editform' is a dot-separated list of names, to distinguish
126 'editform' is a dot-separated list of names, to distinguish
127 the purpose of commit text editing.
127 the purpose of commit text editing.
128
128
129 'getcommiteditor' returns 'commitforceeditor' regardless of
129 'getcommiteditor' returns 'commitforceeditor' regardless of
130 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
130 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
131 they are specific for usage in MQ.
131 they are specific for usage in MQ.
132 """
132 """
133 if edit or finishdesc or extramsg:
133 if edit or finishdesc or extramsg:
134 return lambda r, c, s: commitforceeditor(r, c, s,
134 return lambda r, c, s: commitforceeditor(r, c, s,
135 finishdesc=finishdesc,
135 finishdesc=finishdesc,
136 extramsg=extramsg,
136 extramsg=extramsg,
137 editform=editform)
137 editform=editform)
138 elif editform:
138 elif editform:
139 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
139 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
140 else:
140 else:
141 return commiteditor
141 return commiteditor
142
142
143 def loglimit(opts):
143 def loglimit(opts):
144 """get the log limit according to option -l/--limit"""
144 """get the log limit according to option -l/--limit"""
145 limit = opts.get('limit')
145 limit = opts.get('limit')
146 if limit:
146 if limit:
147 try:
147 try:
148 limit = int(limit)
148 limit = int(limit)
149 except ValueError:
149 except ValueError:
150 raise util.Abort(_('limit must be a positive integer'))
150 raise util.Abort(_('limit must be a positive integer'))
151 if limit <= 0:
151 if limit <= 0:
152 raise util.Abort(_('limit must be positive'))
152 raise util.Abort(_('limit must be positive'))
153 else:
153 else:
154 limit = None
154 limit = None
155 return limit
155 return limit
156
156
157 def makefilename(repo, pat, node, desc=None,
157 def makefilename(repo, pat, node, desc=None,
158 total=None, seqno=None, revwidth=None, pathname=None):
158 total=None, seqno=None, revwidth=None, pathname=None):
159 node_expander = {
159 node_expander = {
160 'H': lambda: hex(node),
160 'H': lambda: hex(node),
161 'R': lambda: str(repo.changelog.rev(node)),
161 'R': lambda: str(repo.changelog.rev(node)),
162 'h': lambda: short(node),
162 'h': lambda: short(node),
163 'm': lambda: re.sub('[^\w]', '_', str(desc))
163 'm': lambda: re.sub('[^\w]', '_', str(desc))
164 }
164 }
165 expander = {
165 expander = {
166 '%': lambda: '%',
166 '%': lambda: '%',
167 'b': lambda: os.path.basename(repo.root),
167 'b': lambda: os.path.basename(repo.root),
168 }
168 }
169
169
170 try:
170 try:
171 if node:
171 if node:
172 expander.update(node_expander)
172 expander.update(node_expander)
173 if node:
173 if node:
174 expander['r'] = (lambda:
174 expander['r'] = (lambda:
175 str(repo.changelog.rev(node)).zfill(revwidth or 0))
175 str(repo.changelog.rev(node)).zfill(revwidth or 0))
176 if total is not None:
176 if total is not None:
177 expander['N'] = lambda: str(total)
177 expander['N'] = lambda: str(total)
178 if seqno is not None:
178 if seqno is not None:
179 expander['n'] = lambda: str(seqno)
179 expander['n'] = lambda: str(seqno)
180 if total is not None and seqno is not None:
180 if total is not None and seqno is not None:
181 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
181 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
182 if pathname is not None:
182 if pathname is not None:
183 expander['s'] = lambda: os.path.basename(pathname)
183 expander['s'] = lambda: os.path.basename(pathname)
184 expander['d'] = lambda: os.path.dirname(pathname) or '.'
184 expander['d'] = lambda: os.path.dirname(pathname) or '.'
185 expander['p'] = lambda: pathname
185 expander['p'] = lambda: pathname
186
186
187 newname = []
187 newname = []
188 patlen = len(pat)
188 patlen = len(pat)
189 i = 0
189 i = 0
190 while i < patlen:
190 while i < patlen:
191 c = pat[i]
191 c = pat[i]
192 if c == '%':
192 if c == '%':
193 i += 1
193 i += 1
194 c = pat[i]
194 c = pat[i]
195 c = expander[c]()
195 c = expander[c]()
196 newname.append(c)
196 newname.append(c)
197 i += 1
197 i += 1
198 return ''.join(newname)
198 return ''.join(newname)
199 except KeyError, inst:
199 except KeyError, inst:
200 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
200 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
201 inst.args[0])
201 inst.args[0])
202
202
203 def makefileobj(repo, pat, node=None, desc=None, total=None,
203 def makefileobj(repo, pat, node=None, desc=None, total=None,
204 seqno=None, revwidth=None, mode='wb', modemap=None,
204 seqno=None, revwidth=None, mode='wb', modemap=None,
205 pathname=None):
205 pathname=None):
206
206
207 writable = mode not in ('r', 'rb')
207 writable = mode not in ('r', 'rb')
208
208
209 if not pat or pat == '-':
209 if not pat or pat == '-':
210 fp = writable and repo.ui.fout or repo.ui.fin
210 fp = writable and repo.ui.fout or repo.ui.fin
211 if util.safehasattr(fp, 'fileno'):
211 if util.safehasattr(fp, 'fileno'):
212 return os.fdopen(os.dup(fp.fileno()), mode)
212 return os.fdopen(os.dup(fp.fileno()), mode)
213 else:
213 else:
214 # if this fp can't be duped properly, return
214 # if this fp can't be duped properly, return
215 # a dummy object that can be closed
215 # a dummy object that can be closed
216 class wrappedfileobj(object):
216 class wrappedfileobj(object):
217 noop = lambda x: None
217 noop = lambda x: None
218 def __init__(self, f):
218 def __init__(self, f):
219 self.f = f
219 self.f = f
220 def __getattr__(self, attr):
220 def __getattr__(self, attr):
221 if attr == 'close':
221 if attr == 'close':
222 return self.noop
222 return self.noop
223 else:
223 else:
224 return getattr(self.f, attr)
224 return getattr(self.f, attr)
225
225
226 return wrappedfileobj(fp)
226 return wrappedfileobj(fp)
227 if util.safehasattr(pat, 'write') and writable:
227 if util.safehasattr(pat, 'write') and writable:
228 return pat
228 return pat
229 if util.safehasattr(pat, 'read') and 'r' in mode:
229 if util.safehasattr(pat, 'read') and 'r' in mode:
230 return pat
230 return pat
231 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
231 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
232 if modemap is not None:
232 if modemap is not None:
233 mode = modemap.get(fn, mode)
233 mode = modemap.get(fn, mode)
234 if mode == 'wb':
234 if mode == 'wb':
235 modemap[fn] = 'ab'
235 modemap[fn] = 'ab'
236 return open(fn, mode)
236 return open(fn, mode)
237
237
238 def openrevlog(repo, cmd, file_, opts):
238 def openrevlog(repo, cmd, file_, opts):
239 """opens the changelog, manifest, a filelog or a given revlog"""
239 """opens the changelog, manifest, a filelog or a given revlog"""
240 cl = opts['changelog']
240 cl = opts['changelog']
241 mf = opts['manifest']
241 mf = opts['manifest']
242 msg = None
242 msg = None
243 if cl and mf:
243 if cl and mf:
244 msg = _('cannot specify --changelog and --manifest at the same time')
244 msg = _('cannot specify --changelog and --manifest at the same time')
245 elif cl or mf:
245 elif cl or mf:
246 if file_:
246 if file_:
247 msg = _('cannot specify filename with --changelog or --manifest')
247 msg = _('cannot specify filename with --changelog or --manifest')
248 elif not repo:
248 elif not repo:
249 msg = _('cannot specify --changelog or --manifest '
249 msg = _('cannot specify --changelog or --manifest '
250 'without a repository')
250 'without a repository')
251 if msg:
251 if msg:
252 raise util.Abort(msg)
252 raise util.Abort(msg)
253
253
254 r = None
254 r = None
255 if repo:
255 if repo:
256 if cl:
256 if cl:
257 r = repo.unfiltered().changelog
257 r = repo.unfiltered().changelog
258 elif mf:
258 elif mf:
259 r = repo.manifest
259 r = repo.manifest
260 elif file_:
260 elif file_:
261 filelog = repo.file(file_)
261 filelog = repo.file(file_)
262 if len(filelog):
262 if len(filelog):
263 r = filelog
263 r = filelog
264 if not r:
264 if not r:
265 if not file_:
265 if not file_:
266 raise error.CommandError(cmd, _('invalid arguments'))
266 raise error.CommandError(cmd, _('invalid arguments'))
267 if not os.path.isfile(file_):
267 if not os.path.isfile(file_):
268 raise util.Abort(_("revlog '%s' not found") % file_)
268 raise util.Abort(_("revlog '%s' not found") % file_)
269 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
269 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
270 file_[:-2] + ".i")
270 file_[:-2] + ".i")
271 return r
271 return r
272
272
273 def copy(ui, repo, pats, opts, rename=False):
273 def copy(ui, repo, pats, opts, rename=False):
274 # called with the repo lock held
274 # called with the repo lock held
275 #
275 #
276 # hgsep => pathname that uses "/" to separate directories
276 # hgsep => pathname that uses "/" to separate directories
277 # ossep => pathname that uses os.sep to separate directories
277 # ossep => pathname that uses os.sep to separate directories
278 cwd = repo.getcwd()
278 cwd = repo.getcwd()
279 targets = {}
279 targets = {}
280 after = opts.get("after")
280 after = opts.get("after")
281 dryrun = opts.get("dry_run")
281 dryrun = opts.get("dry_run")
282 wctx = repo[None]
282 wctx = repo[None]
283
283
284 def walkpat(pat):
284 def walkpat(pat):
285 srcs = []
285 srcs = []
286 badstates = after and '?' or '?r'
286 badstates = after and '?' or '?r'
287 m = scmutil.match(repo[None], [pat], opts, globbed=True)
287 m = scmutil.match(repo[None], [pat], opts, globbed=True)
288 for abs in repo.walk(m):
288 for abs in repo.walk(m):
289 state = repo.dirstate[abs]
289 state = repo.dirstate[abs]
290 rel = m.rel(abs)
290 rel = m.rel(abs)
291 exact = m.exact(abs)
291 exact = m.exact(abs)
292 if state in badstates:
292 if state in badstates:
293 if exact and state == '?':
293 if exact and state == '?':
294 ui.warn(_('%s: not copying - file is not managed\n') % rel)
294 ui.warn(_('%s: not copying - file is not managed\n') % rel)
295 if exact and state == 'r':
295 if exact and state == 'r':
296 ui.warn(_('%s: not copying - file has been marked for'
296 ui.warn(_('%s: not copying - file has been marked for'
297 ' remove\n') % rel)
297 ' remove\n') % rel)
298 continue
298 continue
299 # abs: hgsep
299 # abs: hgsep
300 # rel: ossep
300 # rel: ossep
301 srcs.append((abs, rel, exact))
301 srcs.append((abs, rel, exact))
302 return srcs
302 return srcs
303
303
304 # abssrc: hgsep
304 # abssrc: hgsep
305 # relsrc: ossep
305 # relsrc: ossep
306 # otarget: ossep
306 # otarget: ossep
307 def copyfile(abssrc, relsrc, otarget, exact):
307 def copyfile(abssrc, relsrc, otarget, exact):
308 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
308 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
309 if '/' in abstarget:
309 if '/' in abstarget:
310 # We cannot normalize abstarget itself, this would prevent
310 # We cannot normalize abstarget itself, this would prevent
311 # case only renames, like a => A.
311 # case only renames, like a => A.
312 abspath, absname = abstarget.rsplit('/', 1)
312 abspath, absname = abstarget.rsplit('/', 1)
313 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
313 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
314 reltarget = repo.pathto(abstarget, cwd)
314 reltarget = repo.pathto(abstarget, cwd)
315 target = repo.wjoin(abstarget)
315 target = repo.wjoin(abstarget)
316 src = repo.wjoin(abssrc)
316 src = repo.wjoin(abssrc)
317 state = repo.dirstate[abstarget]
317 state = repo.dirstate[abstarget]
318
318
319 scmutil.checkportable(ui, abstarget)
319 scmutil.checkportable(ui, abstarget)
320
320
321 # check for collisions
321 # check for collisions
322 prevsrc = targets.get(abstarget)
322 prevsrc = targets.get(abstarget)
323 if prevsrc is not None:
323 if prevsrc is not None:
324 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
324 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
325 (reltarget, repo.pathto(abssrc, cwd),
325 (reltarget, repo.pathto(abssrc, cwd),
326 repo.pathto(prevsrc, cwd)))
326 repo.pathto(prevsrc, cwd)))
327 return
327 return
328
328
329 # check for overwrites
329 # check for overwrites
330 exists = os.path.lexists(target)
330 exists = os.path.lexists(target)
331 samefile = False
331 samefile = False
332 if exists and abssrc != abstarget:
332 if exists and abssrc != abstarget:
333 if (repo.dirstate.normalize(abssrc) ==
333 if (repo.dirstate.normalize(abssrc) ==
334 repo.dirstate.normalize(abstarget)):
334 repo.dirstate.normalize(abstarget)):
335 if not rename:
335 if not rename:
336 ui.warn(_("%s: can't copy - same file\n") % reltarget)
336 ui.warn(_("%s: can't copy - same file\n") % reltarget)
337 return
337 return
338 exists = False
338 exists = False
339 samefile = True
339 samefile = True
340
340
341 if not after and exists or after and state in 'mn':
341 if not after and exists or after and state in 'mn':
342 if not opts['force']:
342 if not opts['force']:
343 ui.warn(_('%s: not overwriting - file exists\n') %
343 ui.warn(_('%s: not overwriting - file exists\n') %
344 reltarget)
344 reltarget)
345 return
345 return
346
346
347 if after:
347 if after:
348 if not exists:
348 if not exists:
349 if rename:
349 if rename:
350 ui.warn(_('%s: not recording move - %s does not exist\n') %
350 ui.warn(_('%s: not recording move - %s does not exist\n') %
351 (relsrc, reltarget))
351 (relsrc, reltarget))
352 else:
352 else:
353 ui.warn(_('%s: not recording copy - %s does not exist\n') %
353 ui.warn(_('%s: not recording copy - %s does not exist\n') %
354 (relsrc, reltarget))
354 (relsrc, reltarget))
355 return
355 return
356 elif not dryrun:
356 elif not dryrun:
357 try:
357 try:
358 if exists:
358 if exists:
359 os.unlink(target)
359 os.unlink(target)
360 targetdir = os.path.dirname(target) or '.'
360 targetdir = os.path.dirname(target) or '.'
361 if not os.path.isdir(targetdir):
361 if not os.path.isdir(targetdir):
362 os.makedirs(targetdir)
362 os.makedirs(targetdir)
363 if samefile:
363 if samefile:
364 tmp = target + "~hgrename"
364 tmp = target + "~hgrename"
365 os.rename(src, tmp)
365 os.rename(src, tmp)
366 os.rename(tmp, target)
366 os.rename(tmp, target)
367 else:
367 else:
368 util.copyfile(src, target)
368 util.copyfile(src, target)
369 srcexists = True
369 srcexists = True
370 except IOError, inst:
370 except IOError, inst:
371 if inst.errno == errno.ENOENT:
371 if inst.errno == errno.ENOENT:
372 ui.warn(_('%s: deleted in working copy\n') % relsrc)
372 ui.warn(_('%s: deleted in working copy\n') % relsrc)
373 srcexists = False
373 srcexists = False
374 else:
374 else:
375 ui.warn(_('%s: cannot copy - %s\n') %
375 ui.warn(_('%s: cannot copy - %s\n') %
376 (relsrc, inst.strerror))
376 (relsrc, inst.strerror))
377 return True # report a failure
377 return True # report a failure
378
378
379 if ui.verbose or not exact:
379 if ui.verbose or not exact:
380 if rename:
380 if rename:
381 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
381 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
382 else:
382 else:
383 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
383 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
384
384
385 targets[abstarget] = abssrc
385 targets[abstarget] = abssrc
386
386
387 # fix up dirstate
387 # fix up dirstate
388 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
388 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
389 dryrun=dryrun, cwd=cwd)
389 dryrun=dryrun, cwd=cwd)
390 if rename and not dryrun:
390 if rename and not dryrun:
391 if not after and srcexists and not samefile:
391 if not after and srcexists and not samefile:
392 util.unlinkpath(repo.wjoin(abssrc))
392 util.unlinkpath(repo.wjoin(abssrc))
393 wctx.forget([abssrc])
393 wctx.forget([abssrc])
394
394
395 # pat: ossep
395 # pat: ossep
396 # dest ossep
396 # dest ossep
397 # srcs: list of (hgsep, hgsep, ossep, bool)
397 # srcs: list of (hgsep, hgsep, ossep, bool)
398 # return: function that takes hgsep and returns ossep
398 # return: function that takes hgsep and returns ossep
399 def targetpathfn(pat, dest, srcs):
399 def targetpathfn(pat, dest, srcs):
400 if os.path.isdir(pat):
400 if os.path.isdir(pat):
401 abspfx = pathutil.canonpath(repo.root, cwd, pat)
401 abspfx = pathutil.canonpath(repo.root, cwd, pat)
402 abspfx = util.localpath(abspfx)
402 abspfx = util.localpath(abspfx)
403 if destdirexists:
403 if destdirexists:
404 striplen = len(os.path.split(abspfx)[0])
404 striplen = len(os.path.split(abspfx)[0])
405 else:
405 else:
406 striplen = len(abspfx)
406 striplen = len(abspfx)
407 if striplen:
407 if striplen:
408 striplen += len(os.sep)
408 striplen += len(os.sep)
409 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
409 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
410 elif destdirexists:
410 elif destdirexists:
411 res = lambda p: os.path.join(dest,
411 res = lambda p: os.path.join(dest,
412 os.path.basename(util.localpath(p)))
412 os.path.basename(util.localpath(p)))
413 else:
413 else:
414 res = lambda p: dest
414 res = lambda p: dest
415 return res
415 return res
416
416
417 # pat: ossep
417 # pat: ossep
418 # dest ossep
418 # dest ossep
419 # srcs: list of (hgsep, hgsep, ossep, bool)
419 # srcs: list of (hgsep, hgsep, ossep, bool)
420 # return: function that takes hgsep and returns ossep
420 # return: function that takes hgsep and returns ossep
421 def targetpathafterfn(pat, dest, srcs):
421 def targetpathafterfn(pat, dest, srcs):
422 if matchmod.patkind(pat):
422 if matchmod.patkind(pat):
423 # a mercurial pattern
423 # a mercurial pattern
424 res = lambda p: os.path.join(dest,
424 res = lambda p: os.path.join(dest,
425 os.path.basename(util.localpath(p)))
425 os.path.basename(util.localpath(p)))
426 else:
426 else:
427 abspfx = pathutil.canonpath(repo.root, cwd, pat)
427 abspfx = pathutil.canonpath(repo.root, cwd, pat)
428 if len(abspfx) < len(srcs[0][0]):
428 if len(abspfx) < len(srcs[0][0]):
429 # A directory. Either the target path contains the last
429 # A directory. Either the target path contains the last
430 # component of the source path or it does not.
430 # component of the source path or it does not.
431 def evalpath(striplen):
431 def evalpath(striplen):
432 score = 0
432 score = 0
433 for s in srcs:
433 for s in srcs:
434 t = os.path.join(dest, util.localpath(s[0])[striplen:])
434 t = os.path.join(dest, util.localpath(s[0])[striplen:])
435 if os.path.lexists(t):
435 if os.path.lexists(t):
436 score += 1
436 score += 1
437 return score
437 return score
438
438
439 abspfx = util.localpath(abspfx)
439 abspfx = util.localpath(abspfx)
440 striplen = len(abspfx)
440 striplen = len(abspfx)
441 if striplen:
441 if striplen:
442 striplen += len(os.sep)
442 striplen += len(os.sep)
443 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
443 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
444 score = evalpath(striplen)
444 score = evalpath(striplen)
445 striplen1 = len(os.path.split(abspfx)[0])
445 striplen1 = len(os.path.split(abspfx)[0])
446 if striplen1:
446 if striplen1:
447 striplen1 += len(os.sep)
447 striplen1 += len(os.sep)
448 if evalpath(striplen1) > score:
448 if evalpath(striplen1) > score:
449 striplen = striplen1
449 striplen = striplen1
450 res = lambda p: os.path.join(dest,
450 res = lambda p: os.path.join(dest,
451 util.localpath(p)[striplen:])
451 util.localpath(p)[striplen:])
452 else:
452 else:
453 # a file
453 # a file
454 if destdirexists:
454 if destdirexists:
455 res = lambda p: os.path.join(dest,
455 res = lambda p: os.path.join(dest,
456 os.path.basename(util.localpath(p)))
456 os.path.basename(util.localpath(p)))
457 else:
457 else:
458 res = lambda p: dest
458 res = lambda p: dest
459 return res
459 return res
460
460
461
461
462 pats = scmutil.expandpats(pats)
462 pats = scmutil.expandpats(pats)
463 if not pats:
463 if not pats:
464 raise util.Abort(_('no source or destination specified'))
464 raise util.Abort(_('no source or destination specified'))
465 if len(pats) == 1:
465 if len(pats) == 1:
466 raise util.Abort(_('no destination specified'))
466 raise util.Abort(_('no destination specified'))
467 dest = pats.pop()
467 dest = pats.pop()
468 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
468 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
469 if not destdirexists:
469 if not destdirexists:
470 if len(pats) > 1 or matchmod.patkind(pats[0]):
470 if len(pats) > 1 or matchmod.patkind(pats[0]):
471 raise util.Abort(_('with multiple sources, destination must be an '
471 raise util.Abort(_('with multiple sources, destination must be an '
472 'existing directory'))
472 'existing directory'))
473 if util.endswithsep(dest):
473 if util.endswithsep(dest):
474 raise util.Abort(_('destination %s is not a directory') % dest)
474 raise util.Abort(_('destination %s is not a directory') % dest)
475
475
476 tfn = targetpathfn
476 tfn = targetpathfn
477 if after:
477 if after:
478 tfn = targetpathafterfn
478 tfn = targetpathafterfn
479 copylist = []
479 copylist = []
480 for pat in pats:
480 for pat in pats:
481 srcs = walkpat(pat)
481 srcs = walkpat(pat)
482 if not srcs:
482 if not srcs:
483 continue
483 continue
484 copylist.append((tfn(pat, dest, srcs), srcs))
484 copylist.append((tfn(pat, dest, srcs), srcs))
485 if not copylist:
485 if not copylist:
486 raise util.Abort(_('no files to copy'))
486 raise util.Abort(_('no files to copy'))
487
487
488 errors = 0
488 errors = 0
489 for targetpath, srcs in copylist:
489 for targetpath, srcs in copylist:
490 for abssrc, relsrc, exact in srcs:
490 for abssrc, relsrc, exact in srcs:
491 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
491 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
492 errors += 1
492 errors += 1
493
493
494 if errors:
494 if errors:
495 ui.warn(_('(consider using --after)\n'))
495 ui.warn(_('(consider using --after)\n'))
496
496
497 return errors != 0
497 return errors != 0
498
498
499 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
499 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
500 runargs=None, appendpid=False):
500 runargs=None, appendpid=False):
501 '''Run a command as a service.'''
501 '''Run a command as a service.'''
502
502
503 def writepid(pid):
503 def writepid(pid):
504 if opts['pid_file']:
504 if opts['pid_file']:
505 mode = appendpid and 'a' or 'w'
505 mode = appendpid and 'a' or 'w'
506 fp = open(opts['pid_file'], mode)
506 fp = open(opts['pid_file'], mode)
507 fp.write(str(pid) + '\n')
507 fp.write(str(pid) + '\n')
508 fp.close()
508 fp.close()
509
509
510 if opts['daemon'] and not opts['daemon_pipefds']:
510 if opts['daemon'] and not opts['daemon_pipefds']:
511 # Signal child process startup with file removal
511 # Signal child process startup with file removal
512 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
512 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
513 os.close(lockfd)
513 os.close(lockfd)
514 try:
514 try:
515 if not runargs:
515 if not runargs:
516 runargs = util.hgcmd() + sys.argv[1:]
516 runargs = util.hgcmd() + sys.argv[1:]
517 runargs.append('--daemon-pipefds=%s' % lockpath)
517 runargs.append('--daemon-pipefds=%s' % lockpath)
518 # Don't pass --cwd to the child process, because we've already
518 # Don't pass --cwd to the child process, because we've already
519 # changed directory.
519 # changed directory.
520 for i in xrange(1, len(runargs)):
520 for i in xrange(1, len(runargs)):
521 if runargs[i].startswith('--cwd='):
521 if runargs[i].startswith('--cwd='):
522 del runargs[i]
522 del runargs[i]
523 break
523 break
524 elif runargs[i].startswith('--cwd'):
524 elif runargs[i].startswith('--cwd'):
525 del runargs[i:i + 2]
525 del runargs[i:i + 2]
526 break
526 break
527 def condfn():
527 def condfn():
528 return not os.path.exists(lockpath)
528 return not os.path.exists(lockpath)
529 pid = util.rundetached(runargs, condfn)
529 pid = util.rundetached(runargs, condfn)
530 if pid < 0:
530 if pid < 0:
531 raise util.Abort(_('child process failed to start'))
531 raise util.Abort(_('child process failed to start'))
532 writepid(pid)
532 writepid(pid)
533 finally:
533 finally:
534 try:
534 try:
535 os.unlink(lockpath)
535 os.unlink(lockpath)
536 except OSError, e:
536 except OSError, e:
537 if e.errno != errno.ENOENT:
537 if e.errno != errno.ENOENT:
538 raise
538 raise
539 if parentfn:
539 if parentfn:
540 return parentfn(pid)
540 return parentfn(pid)
541 else:
541 else:
542 return
542 return
543
543
544 if initfn:
544 if initfn:
545 initfn()
545 initfn()
546
546
547 if not opts['daemon']:
547 if not opts['daemon']:
548 writepid(os.getpid())
548 writepid(os.getpid())
549
549
550 if opts['daemon_pipefds']:
550 if opts['daemon_pipefds']:
551 lockpath = opts['daemon_pipefds']
551 lockpath = opts['daemon_pipefds']
552 try:
552 try:
553 os.setsid()
553 os.setsid()
554 except AttributeError:
554 except AttributeError:
555 pass
555 pass
556 os.unlink(lockpath)
556 os.unlink(lockpath)
557 util.hidewindow()
557 util.hidewindow()
558 sys.stdout.flush()
558 sys.stdout.flush()
559 sys.stderr.flush()
559 sys.stderr.flush()
560
560
561 nullfd = os.open(os.devnull, os.O_RDWR)
561 nullfd = os.open(os.devnull, os.O_RDWR)
562 logfilefd = nullfd
562 logfilefd = nullfd
563 if logfile:
563 if logfile:
564 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
564 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
565 os.dup2(nullfd, 0)
565 os.dup2(nullfd, 0)
566 os.dup2(logfilefd, 1)
566 os.dup2(logfilefd, 1)
567 os.dup2(logfilefd, 2)
567 os.dup2(logfilefd, 2)
568 if nullfd not in (0, 1, 2):
568 if nullfd not in (0, 1, 2):
569 os.close(nullfd)
569 os.close(nullfd)
570 if logfile and logfilefd not in (0, 1, 2):
570 if logfile and logfilefd not in (0, 1, 2):
571 os.close(logfilefd)
571 os.close(logfilefd)
572
572
573 if runfn:
573 if runfn:
574 return runfn()
574 return runfn()
575
575
576 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
576 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
577 """Utility function used by commands.import to import a single patch
577 """Utility function used by commands.import to import a single patch
578
578
579 This function is explicitly defined here to help the evolve extension to
579 This function is explicitly defined here to help the evolve extension to
580 wrap this part of the import logic.
580 wrap this part of the import logic.
581
581
582 The API is currently a bit ugly because it a simple code translation from
582 The API is currently a bit ugly because it a simple code translation from
583 the import command. Feel free to make it better.
583 the import command. Feel free to make it better.
584
584
585 :hunk: a patch (as a binary string)
585 :hunk: a patch (as a binary string)
586 :parents: nodes that will be parent of the created commit
586 :parents: nodes that will be parent of the created commit
587 :opts: the full dict of option passed to the import command
587 :opts: the full dict of option passed to the import command
588 :msgs: list to save commit message to.
588 :msgs: list to save commit message to.
589 (used in case we need to save it when failing)
589 (used in case we need to save it when failing)
590 :updatefunc: a function that update a repo to a given node
590 :updatefunc: a function that update a repo to a given node
591 updatefunc(<repo>, <node>)
591 updatefunc(<repo>, <node>)
592 """
592 """
593 tmpname, message, user, date, branch, nodeid, p1, p2 = \
593 tmpname, message, user, date, branch, nodeid, p1, p2 = \
594 patch.extract(ui, hunk)
594 patch.extract(ui, hunk)
595
595
596 editor = getcommiteditor(editform='import.normal', **opts)
596 editor = getcommiteditor(editform='import.normal', **opts)
597 update = not opts.get('bypass')
597 update = not opts.get('bypass')
598 strip = opts["strip"]
598 strip = opts["strip"]
599 sim = float(opts.get('similarity') or 0)
599 sim = float(opts.get('similarity') or 0)
600 if not tmpname:
600 if not tmpname:
601 return (None, None, False)
601 return (None, None, False)
602 msg = _('applied to working directory')
602 msg = _('applied to working directory')
603
603
604 rejects = False
604 rejects = False
605
605
606 try:
606 try:
607 cmdline_message = logmessage(ui, opts)
607 cmdline_message = logmessage(ui, opts)
608 if cmdline_message:
608 if cmdline_message:
609 # pickup the cmdline msg
609 # pickup the cmdline msg
610 message = cmdline_message
610 message = cmdline_message
611 elif message:
611 elif message:
612 # pickup the patch msg
612 # pickup the patch msg
613 message = message.strip()
613 message = message.strip()
614 else:
614 else:
615 # launch the editor
615 # launch the editor
616 message = None
616 message = None
617 ui.debug('message:\n%s\n' % message)
617 ui.debug('message:\n%s\n' % message)
618
618
619 if len(parents) == 1:
619 if len(parents) == 1:
620 parents.append(repo[nullid])
620 parents.append(repo[nullid])
621 if opts.get('exact'):
621 if opts.get('exact'):
622 if not nodeid or not p1:
622 if not nodeid or not p1:
623 raise util.Abort(_('not a Mercurial patch'))
623 raise util.Abort(_('not a Mercurial patch'))
624 p1 = repo[p1]
624 p1 = repo[p1]
625 p2 = repo[p2 or nullid]
625 p2 = repo[p2 or nullid]
626 elif p2:
626 elif p2:
627 try:
627 try:
628 p1 = repo[p1]
628 p1 = repo[p1]
629 p2 = repo[p2]
629 p2 = repo[p2]
630 # Without any options, consider p2 only if the
630 # Without any options, consider p2 only if the
631 # patch is being applied on top of the recorded
631 # patch is being applied on top of the recorded
632 # first parent.
632 # first parent.
633 if p1 != parents[0]:
633 if p1 != parents[0]:
634 p1 = parents[0]
634 p1 = parents[0]
635 p2 = repo[nullid]
635 p2 = repo[nullid]
636 except error.RepoError:
636 except error.RepoError:
637 p1, p2 = parents
637 p1, p2 = parents
638 else:
638 else:
639 p1, p2 = parents
639 p1, p2 = parents
640
640
641 n = None
641 n = None
642 if update:
642 if update:
643 if p1 != parents[0]:
643 if p1 != parents[0]:
644 updatefunc(repo, p1.node())
644 updatefunc(repo, p1.node())
645 if p2 != parents[1]:
645 if p2 != parents[1]:
646 repo.setparents(p1.node(), p2.node())
646 repo.setparents(p1.node(), p2.node())
647
647
648 if opts.get('exact') or opts.get('import_branch'):
648 if opts.get('exact') or opts.get('import_branch'):
649 repo.dirstate.setbranch(branch or 'default')
649 repo.dirstate.setbranch(branch or 'default')
650
650
651 partial = opts.get('partial', False)
651 partial = opts.get('partial', False)
652 files = set()
652 files = set()
653 try:
653 try:
654 patch.patch(ui, repo, tmpname, strip=strip, files=files,
654 patch.patch(ui, repo, tmpname, strip=strip, files=files,
655 eolmode=None, similarity=sim / 100.0)
655 eolmode=None, similarity=sim / 100.0)
656 except patch.PatchError, e:
656 except patch.PatchError, e:
657 if not partial:
657 if not partial:
658 raise util.Abort(str(e))
658 raise util.Abort(str(e))
659 if partial:
659 if partial:
660 rejects = True
660 rejects = True
661
661
662 files = list(files)
662 files = list(files)
663 if opts.get('no_commit'):
663 if opts.get('no_commit'):
664 if message:
664 if message:
665 msgs.append(message)
665 msgs.append(message)
666 else:
666 else:
667 if opts.get('exact') or p2:
667 if opts.get('exact') or p2:
668 # If you got here, you either use --force and know what
668 # If you got here, you either use --force and know what
669 # you are doing or used --exact or a merge patch while
669 # you are doing or used --exact or a merge patch while
670 # being updated to its first parent.
670 # being updated to its first parent.
671 m = None
671 m = None
672 else:
672 else:
673 m = scmutil.matchfiles(repo, files or [])
673 m = scmutil.matchfiles(repo, files or [])
674 n = repo.commit(message, opts.get('user') or user,
674 n = repo.commit(message, opts.get('user') or user,
675 opts.get('date') or date, match=m,
675 opts.get('date') or date, match=m,
676 editor=editor, force=partial)
676 editor=editor, force=partial)
677 else:
677 else:
678 if opts.get('exact') or opts.get('import_branch'):
678 if opts.get('exact') or opts.get('import_branch'):
679 branch = branch or 'default'
679 branch = branch or 'default'
680 else:
680 else:
681 branch = p1.branch()
681 branch = p1.branch()
682 store = patch.filestore()
682 store = patch.filestore()
683 try:
683 try:
684 files = set()
684 files = set()
685 try:
685 try:
686 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
686 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
687 files, eolmode=None)
687 files, eolmode=None)
688 except patch.PatchError, e:
688 except patch.PatchError, e:
689 raise util.Abort(str(e))
689 raise util.Abort(str(e))
690 editor = getcommiteditor(editform='import.bypass')
690 editor = getcommiteditor(editform='import.bypass')
691 memctx = context.makememctx(repo, (p1.node(), p2.node()),
691 memctx = context.makememctx(repo, (p1.node(), p2.node()),
692 message,
692 message,
693 opts.get('user') or user,
693 opts.get('user') or user,
694 opts.get('date') or date,
694 opts.get('date') or date,
695 branch, files, store,
695 branch, files, store,
696 editor=editor)
696 editor=editor)
697 n = memctx.commit()
697 n = memctx.commit()
698 finally:
698 finally:
699 store.close()
699 store.close()
700 if opts.get('exact') and hex(n) != nodeid:
700 if opts.get('exact') and hex(n) != nodeid:
701 raise util.Abort(_('patch is damaged or loses information'))
701 raise util.Abort(_('patch is damaged or loses information'))
702 if n:
702 if n:
703 # i18n: refers to a short changeset id
703 # i18n: refers to a short changeset id
704 msg = _('created %s') % short(n)
704 msg = _('created %s') % short(n)
705 return (msg, n, rejects)
705 return (msg, n, rejects)
706 finally:
706 finally:
707 os.unlink(tmpname)
707 os.unlink(tmpname)
708
708
709 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
709 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
710 opts=None):
710 opts=None):
711 '''export changesets as hg patches.'''
711 '''export changesets as hg patches.'''
712
712
713 total = len(revs)
713 total = len(revs)
714 revwidth = max([len(str(rev)) for rev in revs])
714 revwidth = max([len(str(rev)) for rev in revs])
715 filemode = {}
715 filemode = {}
716
716
717 def single(rev, seqno, fp):
717 def single(rev, seqno, fp):
718 ctx = repo[rev]
718 ctx = repo[rev]
719 node = ctx.node()
719 node = ctx.node()
720 parents = [p.node() for p in ctx.parents() if p]
720 parents = [p.node() for p in ctx.parents() if p]
721 branch = ctx.branch()
721 branch = ctx.branch()
722 if switch_parent:
722 if switch_parent:
723 parents.reverse()
723 parents.reverse()
724 prev = (parents and parents[0]) or nullid
724 prev = (parents and parents[0]) or nullid
725
725
726 shouldclose = False
726 shouldclose = False
727 if not fp and len(template) > 0:
727 if not fp and len(template) > 0:
728 desc_lines = ctx.description().rstrip().split('\n')
728 desc_lines = ctx.description().rstrip().split('\n')
729 desc = desc_lines[0] #Commit always has a first line.
729 desc = desc_lines[0] #Commit always has a first line.
730 fp = makefileobj(repo, template, node, desc=desc, total=total,
730 fp = makefileobj(repo, template, node, desc=desc, total=total,
731 seqno=seqno, revwidth=revwidth, mode='wb',
731 seqno=seqno, revwidth=revwidth, mode='wb',
732 modemap=filemode)
732 modemap=filemode)
733 if fp != template:
733 if fp != template:
734 shouldclose = True
734 shouldclose = True
735 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
735 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
736 repo.ui.note("%s\n" % fp.name)
736 repo.ui.note("%s\n" % fp.name)
737
737
738 if not fp:
738 if not fp:
739 write = repo.ui.write
739 write = repo.ui.write
740 else:
740 else:
741 def write(s, **kw):
741 def write(s, **kw):
742 fp.write(s)
742 fp.write(s)
743
743
744
744
745 write("# HG changeset patch\n")
745 write("# HG changeset patch\n")
746 write("# User %s\n" % ctx.user())
746 write("# User %s\n" % ctx.user())
747 write("# Date %d %d\n" % ctx.date())
747 write("# Date %d %d\n" % ctx.date())
748 write("# %s\n" % util.datestr(ctx.date()))
748 write("# %s\n" % util.datestr(ctx.date()))
749 if branch and branch != 'default':
749 if branch and branch != 'default':
750 write("# Branch %s\n" % branch)
750 write("# Branch %s\n" % branch)
751 write("# Node ID %s\n" % hex(node))
751 write("# Node ID %s\n" % hex(node))
752 write("# Parent %s\n" % hex(prev))
752 write("# Parent %s\n" % hex(prev))
753 if len(parents) > 1:
753 if len(parents) > 1:
754 write("# Parent %s\n" % hex(parents[1]))
754 write("# Parent %s\n" % hex(parents[1]))
755 write(ctx.description().rstrip())
755 write(ctx.description().rstrip())
756 write("\n\n")
756 write("\n\n")
757
757
758 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
758 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
759 write(chunk, label=label)
759 write(chunk, label=label)
760
760
761 if shouldclose:
761 if shouldclose:
762 fp.close()
762 fp.close()
763
763
764 for seqno, rev in enumerate(revs):
764 for seqno, rev in enumerate(revs):
765 single(rev, seqno + 1, fp)
765 single(rev, seqno + 1, fp)
766
766
767 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
767 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
768 changes=None, stat=False, fp=None, prefix='',
768 changes=None, stat=False, fp=None, prefix='',
769 listsubrepos=False):
769 listsubrepos=False):
770 '''show diff or diffstat.'''
770 '''show diff or diffstat.'''
771 if fp is None:
771 if fp is None:
772 write = ui.write
772 write = ui.write
773 else:
773 else:
774 def write(s, **kw):
774 def write(s, **kw):
775 fp.write(s)
775 fp.write(s)
776
776
777 if stat:
777 if stat:
778 diffopts = diffopts.copy(context=0)
778 diffopts = diffopts.copy(context=0)
779 width = 80
779 width = 80
780 if not ui.plain():
780 if not ui.plain():
781 width = ui.termwidth()
781 width = ui.termwidth()
782 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
782 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
783 prefix=prefix)
783 prefix=prefix)
784 for chunk, label in patch.diffstatui(util.iterlines(chunks),
784 for chunk, label in patch.diffstatui(util.iterlines(chunks),
785 width=width,
785 width=width,
786 git=diffopts.git):
786 git=diffopts.git):
787 write(chunk, label=label)
787 write(chunk, label=label)
788 else:
788 else:
789 for chunk, label in patch.diffui(repo, node1, node2, match,
789 for chunk, label in patch.diffui(repo, node1, node2, match,
790 changes, diffopts, prefix=prefix):
790 changes, diffopts, prefix=prefix):
791 write(chunk, label=label)
791 write(chunk, label=label)
792
792
793 if listsubrepos:
793 if listsubrepos:
794 ctx1 = repo[node1]
794 ctx1 = repo[node1]
795 ctx2 = repo[node2]
795 ctx2 = repo[node2]
796 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
796 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
797 tempnode2 = node2
797 tempnode2 = node2
798 try:
798 try:
799 if node2 is not None:
799 if node2 is not None:
800 tempnode2 = ctx2.substate[subpath][1]
800 tempnode2 = ctx2.substate[subpath][1]
801 except KeyError:
801 except KeyError:
802 # A subrepo that existed in node1 was deleted between node1 and
802 # A subrepo that existed in node1 was deleted between node1 and
803 # node2 (inclusive). Thus, ctx2's substate won't contain that
803 # node2 (inclusive). Thus, ctx2's substate won't contain that
804 # subpath. The best we can do is to ignore it.
804 # subpath. The best we can do is to ignore it.
805 tempnode2 = None
805 tempnode2 = None
806 submatch = matchmod.narrowmatcher(subpath, match)
806 submatch = matchmod.narrowmatcher(subpath, match)
807 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
807 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
808 stat=stat, fp=fp, prefix=prefix)
808 stat=stat, fp=fp, prefix=prefix)
809
809
810 class changeset_printer(object):
810 class changeset_printer(object):
811 '''show changeset information when templating not requested.'''
811 '''show changeset information when templating not requested.'''
812
812
813 def __init__(self, ui, repo, patch, diffopts, buffered):
813 def __init__(self, ui, repo, patch, diffopts, buffered):
814 self.ui = ui
814 self.ui = ui
815 self.repo = repo
815 self.repo = repo
816 self.buffered = buffered
816 self.buffered = buffered
817 self.patch = patch
817 self.patch = patch
818 self.diffopts = diffopts
818 self.diffopts = diffopts
819 self.header = {}
819 self.header = {}
820 self.hunk = {}
820 self.hunk = {}
821 self.lastheader = None
821 self.lastheader = None
822 self.footer = None
822 self.footer = None
823
823
824 def flush(self, rev):
824 def flush(self, rev):
825 if rev in self.header:
825 if rev in self.header:
826 h = self.header[rev]
826 h = self.header[rev]
827 if h != self.lastheader:
827 if h != self.lastheader:
828 self.lastheader = h
828 self.lastheader = h
829 self.ui.write(h)
829 self.ui.write(h)
830 del self.header[rev]
830 del self.header[rev]
831 if rev in self.hunk:
831 if rev in self.hunk:
832 self.ui.write(self.hunk[rev])
832 self.ui.write(self.hunk[rev])
833 del self.hunk[rev]
833 del self.hunk[rev]
834 return 1
834 return 1
835 return 0
835 return 0
836
836
837 def close(self):
837 def close(self):
838 if self.footer:
838 if self.footer:
839 self.ui.write(self.footer)
839 self.ui.write(self.footer)
840
840
841 def show(self, ctx, copies=None, matchfn=None, **props):
841 def show(self, ctx, copies=None, matchfn=None, **props):
842 if self.buffered:
842 if self.buffered:
843 self.ui.pushbuffer()
843 self.ui.pushbuffer()
844 self._show(ctx, copies, matchfn, props)
844 self._show(ctx, copies, matchfn, props)
845 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
845 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
846 else:
846 else:
847 self._show(ctx, copies, matchfn, props)
847 self._show(ctx, copies, matchfn, props)
848
848
849 def _show(self, ctx, copies, matchfn, props):
849 def _show(self, ctx, copies, matchfn, props):
850 '''show a single changeset or file revision'''
850 '''show a single changeset or file revision'''
851 changenode = ctx.node()
851 changenode = ctx.node()
852 rev = ctx.rev()
852 rev = ctx.rev()
853
853
854 if self.ui.quiet:
854 if self.ui.quiet:
855 self.ui.write("%d:%s\n" % (rev, short(changenode)),
855 self.ui.write("%d:%s\n" % (rev, short(changenode)),
856 label='log.node')
856 label='log.node')
857 return
857 return
858
858
859 log = self.repo.changelog
859 log = self.repo.changelog
860 date = util.datestr(ctx.date())
860 date = util.datestr(ctx.date())
861
861
862 hexfunc = self.ui.debugflag and hex or short
862 hexfunc = self.ui.debugflag and hex or short
863
863
864 parents = [(p, hexfunc(log.node(p)))
864 parents = [(p, hexfunc(log.node(p)))
865 for p in self._meaningful_parentrevs(log, rev)]
865 for p in self._meaningful_parentrevs(log, rev)]
866
866
867 # i18n: column positioning for "hg log"
867 # i18n: column positioning for "hg log"
868 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
868 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
869 label='log.changeset changeset.%s' % ctx.phasestr())
869 label='log.changeset changeset.%s' % ctx.phasestr())
870
870
871 branch = ctx.branch()
871 branch = ctx.branch()
872 # don't show the default branch name
872 # don't show the default branch name
873 if branch != 'default':
873 if branch != 'default':
874 # i18n: column positioning for "hg log"
874 # i18n: column positioning for "hg log"
875 self.ui.write(_("branch: %s\n") % branch,
875 self.ui.write(_("branch: %s\n") % branch,
876 label='log.branch')
876 label='log.branch')
877 for bookmark in self.repo.nodebookmarks(changenode):
877 for bookmark in self.repo.nodebookmarks(changenode):
878 # i18n: column positioning for "hg log"
878 # i18n: column positioning for "hg log"
879 self.ui.write(_("bookmark: %s\n") % bookmark,
879 self.ui.write(_("bookmark: %s\n") % bookmark,
880 label='log.bookmark')
880 label='log.bookmark')
881 for tag in self.repo.nodetags(changenode):
881 for tag in self.repo.nodetags(changenode):
882 # i18n: column positioning for "hg log"
882 # i18n: column positioning for "hg log"
883 self.ui.write(_("tag: %s\n") % tag,
883 self.ui.write(_("tag: %s\n") % tag,
884 label='log.tag')
884 label='log.tag')
885 if self.ui.debugflag and ctx.phase():
885 if self.ui.debugflag and ctx.phase():
886 # i18n: column positioning for "hg log"
886 # i18n: column positioning for "hg log"
887 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
887 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
888 label='log.phase')
888 label='log.phase')
889 for parent in parents:
889 for parent in parents:
890 # i18n: column positioning for "hg log"
890 # i18n: column positioning for "hg log"
891 self.ui.write(_("parent: %d:%s\n") % parent,
891 self.ui.write(_("parent: %d:%s\n") % parent,
892 label='log.parent changeset.%s' % ctx.phasestr())
892 label='log.parent changeset.%s' % ctx.phasestr())
893
893
894 if self.ui.debugflag:
894 if self.ui.debugflag:
895 mnode = ctx.manifestnode()
895 mnode = ctx.manifestnode()
896 # i18n: column positioning for "hg log"
896 # i18n: column positioning for "hg log"
897 self.ui.write(_("manifest: %d:%s\n") %
897 self.ui.write(_("manifest: %d:%s\n") %
898 (self.repo.manifest.rev(mnode), hex(mnode)),
898 (self.repo.manifest.rev(mnode), hex(mnode)),
899 label='ui.debug log.manifest')
899 label='ui.debug log.manifest')
900 # i18n: column positioning for "hg log"
900 # i18n: column positioning for "hg log"
901 self.ui.write(_("user: %s\n") % ctx.user(),
901 self.ui.write(_("user: %s\n") % ctx.user(),
902 label='log.user')
902 label='log.user')
903 # i18n: column positioning for "hg log"
903 # i18n: column positioning for "hg log"
904 self.ui.write(_("date: %s\n") % date,
904 self.ui.write(_("date: %s\n") % date,
905 label='log.date')
905 label='log.date')
906
906
907 if self.ui.debugflag:
907 if self.ui.debugflag:
908 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
908 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
909 for key, value in zip([# i18n: column positioning for "hg log"
909 for key, value in zip([# i18n: column positioning for "hg log"
910 _("files:"),
910 _("files:"),
911 # i18n: column positioning for "hg log"
911 # i18n: column positioning for "hg log"
912 _("files+:"),
912 _("files+:"),
913 # i18n: column positioning for "hg log"
913 # i18n: column positioning for "hg log"
914 _("files-:")], files):
914 _("files-:")], files):
915 if value:
915 if value:
916 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
916 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
917 label='ui.debug log.files')
917 label='ui.debug log.files')
918 elif ctx.files() and self.ui.verbose:
918 elif ctx.files() and self.ui.verbose:
919 # i18n: column positioning for "hg log"
919 # i18n: column positioning for "hg log"
920 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
920 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
921 label='ui.note log.files')
921 label='ui.note log.files')
922 if copies and self.ui.verbose:
922 if copies and self.ui.verbose:
923 copies = ['%s (%s)' % c for c in copies]
923 copies = ['%s (%s)' % c for c in copies]
924 # i18n: column positioning for "hg log"
924 # i18n: column positioning for "hg log"
925 self.ui.write(_("copies: %s\n") % ' '.join(copies),
925 self.ui.write(_("copies: %s\n") % ' '.join(copies),
926 label='ui.note log.copies')
926 label='ui.note log.copies')
927
927
928 extra = ctx.extra()
928 extra = ctx.extra()
929 if extra and self.ui.debugflag:
929 if extra and self.ui.debugflag:
930 for key, value in sorted(extra.items()):
930 for key, value in sorted(extra.items()):
931 # i18n: column positioning for "hg log"
931 # i18n: column positioning for "hg log"
932 self.ui.write(_("extra: %s=%s\n")
932 self.ui.write(_("extra: %s=%s\n")
933 % (key, value.encode('string_escape')),
933 % (key, value.encode('string_escape')),
934 label='ui.debug log.extra')
934 label='ui.debug log.extra')
935
935
936 description = ctx.description().strip()
936 description = ctx.description().strip()
937 if description:
937 if description:
938 if self.ui.verbose:
938 if self.ui.verbose:
939 self.ui.write(_("description:\n"),
939 self.ui.write(_("description:\n"),
940 label='ui.note log.description')
940 label='ui.note log.description')
941 self.ui.write(description,
941 self.ui.write(description,
942 label='ui.note log.description')
942 label='ui.note log.description')
943 self.ui.write("\n\n")
943 self.ui.write("\n\n")
944 else:
944 else:
945 # i18n: column positioning for "hg log"
945 # i18n: column positioning for "hg log"
946 self.ui.write(_("summary: %s\n") %
946 self.ui.write(_("summary: %s\n") %
947 description.splitlines()[0],
947 description.splitlines()[0],
948 label='log.summary')
948 label='log.summary')
949 self.ui.write("\n")
949 self.ui.write("\n")
950
950
951 self.showpatch(changenode, matchfn)
951 self.showpatch(changenode, matchfn)
952
952
953 def showpatch(self, node, matchfn):
953 def showpatch(self, node, matchfn):
954 if not matchfn:
954 if not matchfn:
955 matchfn = self.patch
955 matchfn = self.patch
956 if matchfn:
956 if matchfn:
957 stat = self.diffopts.get('stat')
957 stat = self.diffopts.get('stat')
958 diff = self.diffopts.get('patch')
958 diff = self.diffopts.get('patch')
959 diffopts = patch.diffopts(self.ui, self.diffopts)
959 diffopts = patch.diffopts(self.ui, self.diffopts)
960 prev = self.repo.changelog.parents(node)[0]
960 prev = self.repo.changelog.parents(node)[0]
961 if stat:
961 if stat:
962 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
962 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
963 match=matchfn, stat=True)
963 match=matchfn, stat=True)
964 if diff:
964 if diff:
965 if stat:
965 if stat:
966 self.ui.write("\n")
966 self.ui.write("\n")
967 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
967 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
968 match=matchfn, stat=False)
968 match=matchfn, stat=False)
969 self.ui.write("\n")
969 self.ui.write("\n")
970
970
971 def _meaningful_parentrevs(self, log, rev):
971 def _meaningful_parentrevs(self, log, rev):
972 """Return list of meaningful (or all if debug) parentrevs for rev.
972 """Return list of meaningful (or all if debug) parentrevs for rev.
973
973
974 For merges (two non-nullrev revisions) both parents are meaningful.
974 For merges (two non-nullrev revisions) both parents are meaningful.
975 Otherwise the first parent revision is considered meaningful if it
975 Otherwise the first parent revision is considered meaningful if it
976 is not the preceding revision.
976 is not the preceding revision.
977 """
977 """
978 parents = log.parentrevs(rev)
978 parents = log.parentrevs(rev)
979 if not self.ui.debugflag and parents[1] == nullrev:
979 if not self.ui.debugflag and parents[1] == nullrev:
980 if parents[0] >= rev - 1:
980 if parents[0] >= rev - 1:
981 parents = []
981 parents = []
982 else:
982 else:
983 parents = [parents[0]]
983 parents = [parents[0]]
984 return parents
984 return parents
985
985
986
986
987 class changeset_templater(changeset_printer):
987 class changeset_templater(changeset_printer):
988 '''format changeset information.'''
988 '''format changeset information.'''
989
989
990 def __init__(self, ui, repo, patch, diffopts, tmpl, mapfile, buffered):
990 def __init__(self, ui, repo, patch, diffopts, tmpl, mapfile, buffered):
991 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
991 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
992 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
992 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
993 defaulttempl = {
993 defaulttempl = {
994 'parent': '{rev}:{node|formatnode} ',
994 'parent': '{rev}:{node|formatnode} ',
995 'manifest': '{rev}:{node|formatnode}',
995 'manifest': '{rev}:{node|formatnode}',
996 'file_copy': '{name} ({source})',
996 'file_copy': '{name} ({source})',
997 'extra': '{key}={value|stringescape}'
997 'extra': '{key}={value|stringescape}'
998 }
998 }
999 # filecopy is preserved for compatibility reasons
999 # filecopy is preserved for compatibility reasons
1000 defaulttempl['filecopy'] = defaulttempl['file_copy']
1000 defaulttempl['filecopy'] = defaulttempl['file_copy']
1001 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1001 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1002 cache=defaulttempl)
1002 cache=defaulttempl)
1003 if tmpl:
1003 if tmpl:
1004 self.t.cache['changeset'] = tmpl
1004 self.t.cache['changeset'] = tmpl
1005
1005
1006 self.cache = {}
1006 self.cache = {}
1007
1007
1008 def _meaningful_parentrevs(self, ctx):
1008 def _meaningful_parentrevs(self, ctx):
1009 """Return list of meaningful (or all if debug) parentrevs for rev.
1009 """Return list of meaningful (or all if debug) parentrevs for rev.
1010 """
1010 """
1011 parents = ctx.parents()
1011 parents = ctx.parents()
1012 if len(parents) > 1:
1012 if len(parents) > 1:
1013 return parents
1013 return parents
1014 if self.ui.debugflag:
1014 if self.ui.debugflag:
1015 return [parents[0], self.repo['null']]
1015 return [parents[0], self.repo['null']]
1016 if parents[0].rev() >= ctx.rev() - 1:
1016 if parents[0].rev() >= ctx.rev() - 1:
1017 return []
1017 return []
1018 return parents
1018 return parents
1019
1019
1020 def _show(self, ctx, copies, matchfn, props):
1020 def _show(self, ctx, copies, matchfn, props):
1021 '''show a single changeset or file revision'''
1021 '''show a single changeset or file revision'''
1022
1022
1023 showlist = templatekw.showlist
1023 showlist = templatekw.showlist
1024
1024
1025 # showparents() behaviour depends on ui trace level which
1025 # showparents() behaviour depends on ui trace level which
1026 # causes unexpected behaviours at templating level and makes
1026 # causes unexpected behaviours at templating level and makes
1027 # it harder to extract it in a standalone function. Its
1027 # it harder to extract it in a standalone function. Its
1028 # behaviour cannot be changed so leave it here for now.
1028 # behaviour cannot be changed so leave it here for now.
1029 def showparents(**args):
1029 def showparents(**args):
1030 ctx = args['ctx']
1030 ctx = args['ctx']
1031 parents = [[('rev', p.rev()), ('node', p.hex())]
1031 parents = [[('rev', p.rev()), ('node', p.hex())]
1032 for p in self._meaningful_parentrevs(ctx)]
1032 for p in self._meaningful_parentrevs(ctx)]
1033 return showlist('parent', parents, **args)
1033 return showlist('parent', parents, **args)
1034
1034
1035 props = props.copy()
1035 props = props.copy()
1036 props.update(templatekw.keywords)
1036 props.update(templatekw.keywords)
1037 props['parents'] = showparents
1037 props['parents'] = showparents
1038 props['templ'] = self.t
1038 props['templ'] = self.t
1039 props['ctx'] = ctx
1039 props['ctx'] = ctx
1040 props['repo'] = self.repo
1040 props['repo'] = self.repo
1041 props['revcache'] = {'copies': copies}
1041 props['revcache'] = {'copies': copies}
1042 props['cache'] = self.cache
1042 props['cache'] = self.cache
1043
1043
1044 # find correct templates for current mode
1044 # find correct templates for current mode
1045
1045
1046 tmplmodes = [
1046 tmplmodes = [
1047 (True, None),
1047 (True, None),
1048 (self.ui.verbose, 'verbose'),
1048 (self.ui.verbose, 'verbose'),
1049 (self.ui.quiet, 'quiet'),
1049 (self.ui.quiet, 'quiet'),
1050 (self.ui.debugflag, 'debug'),
1050 (self.ui.debugflag, 'debug'),
1051 ]
1051 ]
1052
1052
1053 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1053 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1054 for mode, postfix in tmplmodes:
1054 for mode, postfix in tmplmodes:
1055 for type in types:
1055 for type in types:
1056 cur = postfix and ('%s_%s' % (type, postfix)) or type
1056 cur = postfix and ('%s_%s' % (type, postfix)) or type
1057 if mode and cur in self.t:
1057 if mode and cur in self.t:
1058 types[type] = cur
1058 types[type] = cur
1059
1059
1060 try:
1060 try:
1061
1061
1062 # write header
1062 # write header
1063 if types['header']:
1063 if types['header']:
1064 h = templater.stringify(self.t(types['header'], **props))
1064 h = templater.stringify(self.t(types['header'], **props))
1065 if self.buffered:
1065 if self.buffered:
1066 self.header[ctx.rev()] = h
1066 self.header[ctx.rev()] = h
1067 else:
1067 else:
1068 if self.lastheader != h:
1068 if self.lastheader != h:
1069 self.lastheader = h
1069 self.lastheader = h
1070 self.ui.write(h)
1070 self.ui.write(h)
1071
1071
1072 # write changeset metadata, then patch if requested
1072 # write changeset metadata, then patch if requested
1073 key = types['changeset']
1073 key = types['changeset']
1074 self.ui.write(templater.stringify(self.t(key, **props)))
1074 self.ui.write(templater.stringify(self.t(key, **props)))
1075 self.showpatch(ctx.node(), matchfn)
1075 self.showpatch(ctx.node(), matchfn)
1076
1076
1077 if types['footer']:
1077 if types['footer']:
1078 if not self.footer:
1078 if not self.footer:
1079 self.footer = templater.stringify(self.t(types['footer'],
1079 self.footer = templater.stringify(self.t(types['footer'],
1080 **props))
1080 **props))
1081
1081
1082 except KeyError, inst:
1082 except KeyError, inst:
1083 msg = _("%s: no key named '%s'")
1083 msg = _("%s: no key named '%s'")
1084 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1084 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1085 except SyntaxError, inst:
1085 except SyntaxError, inst:
1086 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1086 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1087
1087
1088 def gettemplate(ui, tmpl, style):
1088 def gettemplate(ui, tmpl, style):
1089 """
1089 """
1090 Find the template matching the given template spec or style.
1090 Find the template matching the given template spec or style.
1091 """
1091 """
1092
1092
1093 # ui settings
1093 # ui settings
1094 if not tmpl and not style:
1094 if not tmpl and not style:
1095 tmpl = ui.config('ui', 'logtemplate')
1095 tmpl = ui.config('ui', 'logtemplate')
1096 if tmpl:
1096 if tmpl:
1097 try:
1097 try:
1098 tmpl = templater.parsestring(tmpl)
1098 tmpl = templater.parsestring(tmpl)
1099 except SyntaxError:
1099 except SyntaxError:
1100 tmpl = templater.parsestring(tmpl, quoted=False)
1100 tmpl = templater.parsestring(tmpl, quoted=False)
1101 return tmpl, None
1101 return tmpl, None
1102 else:
1102 else:
1103 style = util.expandpath(ui.config('ui', 'style', ''))
1103 style = util.expandpath(ui.config('ui', 'style', ''))
1104
1104
1105 if style:
1105 if style:
1106 mapfile = style
1106 mapfile = style
1107 if not os.path.split(mapfile)[0]:
1107 if not os.path.split(mapfile)[0]:
1108 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1108 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1109 or templater.templatepath(mapfile))
1109 or templater.templatepath(mapfile))
1110 if mapname:
1110 if mapname:
1111 mapfile = mapname
1111 mapfile = mapname
1112 return None, mapfile
1112 return None, mapfile
1113
1113
1114 if not tmpl:
1114 if not tmpl:
1115 return None, None
1115 return None, None
1116
1116
1117 # looks like a literal template?
1117 # looks like a literal template?
1118 if '{' in tmpl:
1118 if '{' in tmpl:
1119 return tmpl, None
1119 return tmpl, None
1120
1120
1121 # perhaps a stock style?
1121 # perhaps a stock style?
1122 if not os.path.split(tmpl)[0]:
1122 if not os.path.split(tmpl)[0]:
1123 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1123 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1124 or templater.templatepath(tmpl))
1124 or templater.templatepath(tmpl))
1125 if mapname and os.path.isfile(mapname):
1125 if mapname and os.path.isfile(mapname):
1126 return None, mapname
1126 return None, mapname
1127
1127
1128 # perhaps it's a reference to [templates]
1128 # perhaps it's a reference to [templates]
1129 t = ui.config('templates', tmpl)
1129 t = ui.config('templates', tmpl)
1130 if t:
1130 if t:
1131 try:
1131 try:
1132 tmpl = templater.parsestring(t)
1132 tmpl = templater.parsestring(t)
1133 except SyntaxError:
1133 except SyntaxError:
1134 tmpl = templater.parsestring(t, quoted=False)
1134 tmpl = templater.parsestring(t, quoted=False)
1135 return tmpl, None
1135 return tmpl, None
1136
1136
1137 if tmpl == 'list':
1137 if tmpl == 'list':
1138 ui.write(_("available styles: %s\n") % templater.stylelist())
1138 ui.write(_("available styles: %s\n") % templater.stylelist())
1139 raise util.Abort(_("specify a template"))
1139 raise util.Abort(_("specify a template"))
1140
1140
1141 # perhaps it's a path to a map or a template
1141 # perhaps it's a path to a map or a template
1142 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1142 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1143 # is it a mapfile for a style?
1143 # is it a mapfile for a style?
1144 if os.path.basename(tmpl).startswith("map-"):
1144 if os.path.basename(tmpl).startswith("map-"):
1145 return None, os.path.realpath(tmpl)
1145 return None, os.path.realpath(tmpl)
1146 tmpl = open(tmpl).read()
1146 tmpl = open(tmpl).read()
1147 return tmpl, None
1147 return tmpl, None
1148
1148
1149 # constant string?
1149 # constant string?
1150 return tmpl, None
1150 return tmpl, None
1151
1151
1152 def show_changeset(ui, repo, opts, buffered=False):
1152 def show_changeset(ui, repo, opts, buffered=False):
1153 """show one changeset using template or regular display.
1153 """show one changeset using template or regular display.
1154
1154
1155 Display format will be the first non-empty hit of:
1155 Display format will be the first non-empty hit of:
1156 1. option 'template'
1156 1. option 'template'
1157 2. option 'style'
1157 2. option 'style'
1158 3. [ui] setting 'logtemplate'
1158 3. [ui] setting 'logtemplate'
1159 4. [ui] setting 'style'
1159 4. [ui] setting 'style'
1160 If all of these values are either the unset or the empty string,
1160 If all of these values are either the unset or the empty string,
1161 regular display via changeset_printer() is done.
1161 regular display via changeset_printer() is done.
1162 """
1162 """
1163 # options
1163 # options
1164 patch = None
1164 patch = None
1165 if opts.get('patch') or opts.get('stat'):
1165 if opts.get('patch') or opts.get('stat'):
1166 patch = scmutil.matchall(repo)
1166 patch = scmutil.matchall(repo)
1167
1167
1168 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1168 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1169
1169
1170 if not tmpl and not mapfile:
1170 if not tmpl and not mapfile:
1171 return changeset_printer(ui, repo, patch, opts, buffered)
1171 return changeset_printer(ui, repo, patch, opts, buffered)
1172
1172
1173 try:
1173 try:
1174 t = changeset_templater(ui, repo, patch, opts, tmpl, mapfile, buffered)
1174 t = changeset_templater(ui, repo, patch, opts, tmpl, mapfile, buffered)
1175 except SyntaxError, inst:
1175 except SyntaxError, inst:
1176 raise util.Abort(inst.args[0])
1176 raise util.Abort(inst.args[0])
1177 return t
1177 return t
1178
1178
1179 def showmarker(ui, marker):
1179 def showmarker(ui, marker):
1180 """utility function to display obsolescence marker in a readable way
1180 """utility function to display obsolescence marker in a readable way
1181
1181
1182 To be used by debug function."""
1182 To be used by debug function."""
1183 ui.write(hex(marker.precnode()))
1183 ui.write(hex(marker.precnode()))
1184 for repl in marker.succnodes():
1184 for repl in marker.succnodes():
1185 ui.write(' ')
1185 ui.write(' ')
1186 ui.write(hex(repl))
1186 ui.write(hex(repl))
1187 ui.write(' %X ' % marker._data[2])
1187 ui.write(' %X ' % marker._data[2])
1188 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1188 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1189 sorted(marker.metadata().items()))))
1189 sorted(marker.metadata().items()))))
1190 ui.write('\n')
1190 ui.write('\n')
1191
1191
1192 def finddate(ui, repo, date):
1192 def finddate(ui, repo, date):
1193 """Find the tipmost changeset that matches the given date spec"""
1193 """Find the tipmost changeset that matches the given date spec"""
1194
1194
1195 df = util.matchdate(date)
1195 df = util.matchdate(date)
1196 m = scmutil.matchall(repo)
1196 m = scmutil.matchall(repo)
1197 results = {}
1197 results = {}
1198
1198
1199 def prep(ctx, fns):
1199 def prep(ctx, fns):
1200 d = ctx.date()
1200 d = ctx.date()
1201 if df(d[0]):
1201 if df(d[0]):
1202 results[ctx.rev()] = d
1202 results[ctx.rev()] = d
1203
1203
1204 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1204 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1205 rev = ctx.rev()
1205 rev = ctx.rev()
1206 if rev in results:
1206 if rev in results:
1207 ui.status(_("found revision %s from %s\n") %
1207 ui.status(_("found revision %s from %s\n") %
1208 (rev, util.datestr(results[rev])))
1208 (rev, util.datestr(results[rev])))
1209 return str(rev)
1209 return str(rev)
1210
1210
1211 raise util.Abort(_("revision matching date not found"))
1211 raise util.Abort(_("revision matching date not found"))
1212
1212
1213 def increasingwindows(windowsize=8, sizelimit=512):
1213 def increasingwindows(windowsize=8, sizelimit=512):
1214 while True:
1214 while True:
1215 yield windowsize
1215 yield windowsize
1216 if windowsize < sizelimit:
1216 if windowsize < sizelimit:
1217 windowsize *= 2
1217 windowsize *= 2
1218
1218
1219 class FileWalkError(Exception):
1219 class FileWalkError(Exception):
1220 pass
1220 pass
1221
1221
1222 def walkfilerevs(repo, match, follow, revs, fncache):
1222 def walkfilerevs(repo, match, follow, revs, fncache):
1223 '''Walks the file history for the matched files.
1223 '''Walks the file history for the matched files.
1224
1224
1225 Returns the changeset revs that are involved in the file history.
1225 Returns the changeset revs that are involved in the file history.
1226
1226
1227 Throws FileWalkError if the file history can't be walked using
1227 Throws FileWalkError if the file history can't be walked using
1228 filelogs alone.
1228 filelogs alone.
1229 '''
1229 '''
1230 wanted = set()
1230 wanted = set()
1231 copies = []
1231 copies = []
1232 minrev, maxrev = min(revs), max(revs)
1232 minrev, maxrev = min(revs), max(revs)
1233 def filerevgen(filelog, last):
1233 def filerevgen(filelog, last):
1234 """
1234 """
1235 Only files, no patterns. Check the history of each file.
1235 Only files, no patterns. Check the history of each file.
1236
1236
1237 Examines filelog entries within minrev, maxrev linkrev range
1237 Examines filelog entries within minrev, maxrev linkrev range
1238 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1238 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1239 tuples in backwards order
1239 tuples in backwards order
1240 """
1240 """
1241 cl_count = len(repo)
1241 cl_count = len(repo)
1242 revs = []
1242 revs = []
1243 for j in xrange(0, last + 1):
1243 for j in xrange(0, last + 1):
1244 linkrev = filelog.linkrev(j)
1244 linkrev = filelog.linkrev(j)
1245 if linkrev < minrev:
1245 if linkrev < minrev:
1246 continue
1246 continue
1247 # only yield rev for which we have the changelog, it can
1247 # only yield rev for which we have the changelog, it can
1248 # happen while doing "hg log" during a pull or commit
1248 # happen while doing "hg log" during a pull or commit
1249 if linkrev >= cl_count:
1249 if linkrev >= cl_count:
1250 break
1250 break
1251
1251
1252 parentlinkrevs = []
1252 parentlinkrevs = []
1253 for p in filelog.parentrevs(j):
1253 for p in filelog.parentrevs(j):
1254 if p != nullrev:
1254 if p != nullrev:
1255 parentlinkrevs.append(filelog.linkrev(p))
1255 parentlinkrevs.append(filelog.linkrev(p))
1256 n = filelog.node(j)
1256 n = filelog.node(j)
1257 revs.append((linkrev, parentlinkrevs,
1257 revs.append((linkrev, parentlinkrevs,
1258 follow and filelog.renamed(n)))
1258 follow and filelog.renamed(n)))
1259
1259
1260 return reversed(revs)
1260 return reversed(revs)
1261 def iterfiles():
1261 def iterfiles():
1262 pctx = repo['.']
1262 pctx = repo['.']
1263 for filename in match.files():
1263 for filename in match.files():
1264 if follow:
1264 if follow:
1265 if filename not in pctx:
1265 if filename not in pctx:
1266 raise util.Abort(_('cannot follow file not in parent '
1266 raise util.Abort(_('cannot follow file not in parent '
1267 'revision: "%s"') % filename)
1267 'revision: "%s"') % filename)
1268 yield filename, pctx[filename].filenode()
1268 yield filename, pctx[filename].filenode()
1269 else:
1269 else:
1270 yield filename, None
1270 yield filename, None
1271 for filename_node in copies:
1271 for filename_node in copies:
1272 yield filename_node
1272 yield filename_node
1273
1273
1274 for file_, node in iterfiles():
1274 for file_, node in iterfiles():
1275 filelog = repo.file(file_)
1275 filelog = repo.file(file_)
1276 if not len(filelog):
1276 if not len(filelog):
1277 if node is None:
1277 if node is None:
1278 # A zero count may be a directory or deleted file, so
1278 # A zero count may be a directory or deleted file, so
1279 # try to find matching entries on the slow path.
1279 # try to find matching entries on the slow path.
1280 if follow:
1280 if follow:
1281 raise util.Abort(
1281 raise util.Abort(
1282 _('cannot follow nonexistent file: "%s"') % file_)
1282 _('cannot follow nonexistent file: "%s"') % file_)
1283 raise FileWalkError("Cannot walk via filelog")
1283 raise FileWalkError("Cannot walk via filelog")
1284 else:
1284 else:
1285 continue
1285 continue
1286
1286
1287 if node is None:
1287 if node is None:
1288 last = len(filelog) - 1
1288 last = len(filelog) - 1
1289 else:
1289 else:
1290 last = filelog.rev(node)
1290 last = filelog.rev(node)
1291
1291
1292
1292
1293 # keep track of all ancestors of the file
1293 # keep track of all ancestors of the file
1294 ancestors = set([filelog.linkrev(last)])
1294 ancestors = set([filelog.linkrev(last)])
1295
1295
1296 # iterate from latest to oldest revision
1296 # iterate from latest to oldest revision
1297 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1297 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1298 if not follow:
1298 if not follow:
1299 if rev > maxrev:
1299 if rev > maxrev:
1300 continue
1300 continue
1301 else:
1301 else:
1302 # Note that last might not be the first interesting
1302 # Note that last might not be the first interesting
1303 # rev to us:
1303 # rev to us:
1304 # if the file has been changed after maxrev, we'll
1304 # if the file has been changed after maxrev, we'll
1305 # have linkrev(last) > maxrev, and we still need
1305 # have linkrev(last) > maxrev, and we still need
1306 # to explore the file graph
1306 # to explore the file graph
1307 if rev not in ancestors:
1307 if rev not in ancestors:
1308 continue
1308 continue
1309 # XXX insert 1327 fix here
1309 # XXX insert 1327 fix here
1310 if flparentlinkrevs:
1310 if flparentlinkrevs:
1311 ancestors.update(flparentlinkrevs)
1311 ancestors.update(flparentlinkrevs)
1312
1312
1313 fncache.setdefault(rev, []).append(file_)
1313 fncache.setdefault(rev, []).append(file_)
1314 wanted.add(rev)
1314 wanted.add(rev)
1315 if copied:
1315 if copied:
1316 copies.append(copied)
1316 copies.append(copied)
1317
1317
1318 return wanted
1318 return wanted
1319
1319
1320 def walkchangerevs(repo, match, opts, prepare):
1320 def walkchangerevs(repo, match, opts, prepare):
1321 '''Iterate over files and the revs in which they changed.
1321 '''Iterate over files and the revs in which they changed.
1322
1322
1323 Callers most commonly need to iterate backwards over the history
1323 Callers most commonly need to iterate backwards over the history
1324 in which they are interested. Doing so has awful (quadratic-looking)
1324 in which they are interested. Doing so has awful (quadratic-looking)
1325 performance, so we use iterators in a "windowed" way.
1325 performance, so we use iterators in a "windowed" way.
1326
1326
1327 We walk a window of revisions in the desired order. Within the
1327 We walk a window of revisions in the desired order. Within the
1328 window, we first walk forwards to gather data, then in the desired
1328 window, we first walk forwards to gather data, then in the desired
1329 order (usually backwards) to display it.
1329 order (usually backwards) to display it.
1330
1330
1331 This function returns an iterator yielding contexts. Before
1331 This function returns an iterator yielding contexts. Before
1332 yielding each context, the iterator will first call the prepare
1332 yielding each context, the iterator will first call the prepare
1333 function on each context in the window in forward order.'''
1333 function on each context in the window in forward order.'''
1334
1334
1335 follow = opts.get('follow') or opts.get('follow_first')
1335 follow = opts.get('follow') or opts.get('follow_first')
1336
1336
1337 if opts.get('rev'):
1337 if opts.get('rev'):
1338 revs = scmutil.revrange(repo, opts.get('rev'))
1338 revs = scmutil.revrange(repo, opts.get('rev'))
1339 elif follow:
1339 elif follow:
1340 revs = repo.revs('reverse(:.)')
1340 revs = repo.revs('reverse(:.)')
1341 else:
1341 else:
1342 revs = revset.spanset(repo)
1342 revs = revset.spanset(repo)
1343 revs.reverse()
1343 revs.reverse()
1344 if not revs:
1344 if not revs:
1345 return []
1345 return []
1346 wanted = set()
1346 wanted = set()
1347 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1347 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1348 fncache = {}
1348 fncache = {}
1349 change = repo.changectx
1349 change = repo.changectx
1350
1350
1351 # First step is to fill wanted, the set of revisions that we want to yield.
1351 # First step is to fill wanted, the set of revisions that we want to yield.
1352 # When it does not induce extra cost, we also fill fncache for revisions in
1352 # When it does not induce extra cost, we also fill fncache for revisions in
1353 # wanted: a cache of filenames that were changed (ctx.files()) and that
1353 # wanted: a cache of filenames that were changed (ctx.files()) and that
1354 # match the file filtering conditions.
1354 # match the file filtering conditions.
1355
1355
1356 if not slowpath and not match.files():
1356 if not slowpath and not match.files():
1357 # No files, no patterns. Display all revs.
1357 # No files, no patterns. Display all revs.
1358 wanted = revs
1358 wanted = revs
1359
1359
1360 if not slowpath and match.files():
1360 if not slowpath and match.files():
1361 # We only have to read through the filelog to find wanted revisions
1361 # We only have to read through the filelog to find wanted revisions
1362
1362
1363 try:
1363 try:
1364 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1364 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1365 except FileWalkError:
1365 except FileWalkError:
1366 slowpath = True
1366 slowpath = True
1367
1367
1368 # We decided to fall back to the slowpath because at least one
1368 # We decided to fall back to the slowpath because at least one
1369 # of the paths was not a file. Check to see if at least one of them
1369 # of the paths was not a file. Check to see if at least one of them
1370 # existed in history, otherwise simply return
1370 # existed in history, otherwise simply return
1371 for path in match.files():
1371 for path in match.files():
1372 if path == '.' or path in repo.store:
1372 if path == '.' or path in repo.store:
1373 break
1373 break
1374 else:
1374 else:
1375 return []
1375 return []
1376
1376
1377 if slowpath:
1377 if slowpath:
1378 # We have to read the changelog to match filenames against
1378 # We have to read the changelog to match filenames against
1379 # changed files
1379 # changed files
1380
1380
1381 if follow:
1381 if follow:
1382 raise util.Abort(_('can only follow copies/renames for explicit '
1382 raise util.Abort(_('can only follow copies/renames for explicit '
1383 'filenames'))
1383 'filenames'))
1384
1384
1385 # The slow path checks files modified in every changeset.
1385 # The slow path checks files modified in every changeset.
1386 # This is really slow on large repos, so compute the set lazily.
1386 # This is really slow on large repos, so compute the set lazily.
1387 class lazywantedset(object):
1387 class lazywantedset(object):
1388 def __init__(self):
1388 def __init__(self):
1389 self.set = set()
1389 self.set = set()
1390 self.revs = set(revs)
1390 self.revs = set(revs)
1391
1391
1392 # No need to worry about locality here because it will be accessed
1392 # No need to worry about locality here because it will be accessed
1393 # in the same order as the increasing window below.
1393 # in the same order as the increasing window below.
1394 def __contains__(self, value):
1394 def __contains__(self, value):
1395 if value in self.set:
1395 if value in self.set:
1396 return True
1396 return True
1397 elif not value in self.revs:
1397 elif not value in self.revs:
1398 return False
1398 return False
1399 else:
1399 else:
1400 self.revs.discard(value)
1400 self.revs.discard(value)
1401 ctx = change(value)
1401 ctx = change(value)
1402 matches = filter(match, ctx.files())
1402 matches = filter(match, ctx.files())
1403 if matches:
1403 if matches:
1404 fncache[value] = matches
1404 fncache[value] = matches
1405 self.set.add(value)
1405 self.set.add(value)
1406 return True
1406 return True
1407 return False
1407 return False
1408
1408
1409 def discard(self, value):
1409 def discard(self, value):
1410 self.revs.discard(value)
1410 self.revs.discard(value)
1411 self.set.discard(value)
1411 self.set.discard(value)
1412
1412
1413 wanted = lazywantedset()
1413 wanted = lazywantedset()
1414
1414
1415 class followfilter(object):
1415 class followfilter(object):
1416 def __init__(self, onlyfirst=False):
1416 def __init__(self, onlyfirst=False):
1417 self.startrev = nullrev
1417 self.startrev = nullrev
1418 self.roots = set()
1418 self.roots = set()
1419 self.onlyfirst = onlyfirst
1419 self.onlyfirst = onlyfirst
1420
1420
1421 def match(self, rev):
1421 def match(self, rev):
1422 def realparents(rev):
1422 def realparents(rev):
1423 if self.onlyfirst:
1423 if self.onlyfirst:
1424 return repo.changelog.parentrevs(rev)[0:1]
1424 return repo.changelog.parentrevs(rev)[0:1]
1425 else:
1425 else:
1426 return filter(lambda x: x != nullrev,
1426 return filter(lambda x: x != nullrev,
1427 repo.changelog.parentrevs(rev))
1427 repo.changelog.parentrevs(rev))
1428
1428
1429 if self.startrev == nullrev:
1429 if self.startrev == nullrev:
1430 self.startrev = rev
1430 self.startrev = rev
1431 return True
1431 return True
1432
1432
1433 if rev > self.startrev:
1433 if rev > self.startrev:
1434 # forward: all descendants
1434 # forward: all descendants
1435 if not self.roots:
1435 if not self.roots:
1436 self.roots.add(self.startrev)
1436 self.roots.add(self.startrev)
1437 for parent in realparents(rev):
1437 for parent in realparents(rev):
1438 if parent in self.roots:
1438 if parent in self.roots:
1439 self.roots.add(rev)
1439 self.roots.add(rev)
1440 return True
1440 return True
1441 else:
1441 else:
1442 # backwards: all parents
1442 # backwards: all parents
1443 if not self.roots:
1443 if not self.roots:
1444 self.roots.update(realparents(self.startrev))
1444 self.roots.update(realparents(self.startrev))
1445 if rev in self.roots:
1445 if rev in self.roots:
1446 self.roots.remove(rev)
1446 self.roots.remove(rev)
1447 self.roots.update(realparents(rev))
1447 self.roots.update(realparents(rev))
1448 return True
1448 return True
1449
1449
1450 return False
1450 return False
1451
1451
1452 # it might be worthwhile to do this in the iterator if the rev range
1452 # it might be worthwhile to do this in the iterator if the rev range
1453 # is descending and the prune args are all within that range
1453 # is descending and the prune args are all within that range
1454 for rev in opts.get('prune', ()):
1454 for rev in opts.get('prune', ()):
1455 rev = repo[rev].rev()
1455 rev = repo[rev].rev()
1456 ff = followfilter()
1456 ff = followfilter()
1457 stop = min(revs[0], revs[-1])
1457 stop = min(revs[0], revs[-1])
1458 for x in xrange(rev, stop - 1, -1):
1458 for x in xrange(rev, stop - 1, -1):
1459 if ff.match(x):
1459 if ff.match(x):
1460 wanted = wanted - [x]
1460 wanted = wanted - [x]
1461
1461
1462 # Now that wanted is correctly initialized, we can iterate over the
1462 # Now that wanted is correctly initialized, we can iterate over the
1463 # revision range, yielding only revisions in wanted.
1463 # revision range, yielding only revisions in wanted.
1464 def iterate():
1464 def iterate():
1465 if follow and not match.files():
1465 if follow and not match.files():
1466 ff = followfilter(onlyfirst=opts.get('follow_first'))
1466 ff = followfilter(onlyfirst=opts.get('follow_first'))
1467 def want(rev):
1467 def want(rev):
1468 return ff.match(rev) and rev in wanted
1468 return ff.match(rev) and rev in wanted
1469 else:
1469 else:
1470 def want(rev):
1470 def want(rev):
1471 return rev in wanted
1471 return rev in wanted
1472
1472
1473 it = iter(revs)
1473 it = iter(revs)
1474 stopiteration = False
1474 stopiteration = False
1475 for windowsize in increasingwindows():
1475 for windowsize in increasingwindows():
1476 nrevs = []
1476 nrevs = []
1477 for i in xrange(windowsize):
1477 for i in xrange(windowsize):
1478 try:
1478 try:
1479 rev = it.next()
1479 rev = it.next()
1480 if want(rev):
1480 if want(rev):
1481 nrevs.append(rev)
1481 nrevs.append(rev)
1482 except (StopIteration):
1482 except (StopIteration):
1483 stopiteration = True
1483 stopiteration = True
1484 break
1484 break
1485 for rev in sorted(nrevs):
1485 for rev in sorted(nrevs):
1486 fns = fncache.get(rev)
1486 fns = fncache.get(rev)
1487 ctx = change(rev)
1487 ctx = change(rev)
1488 if not fns:
1488 if not fns:
1489 def fns_generator():
1489 def fns_generator():
1490 for f in ctx.files():
1490 for f in ctx.files():
1491 if match(f):
1491 if match(f):
1492 yield f
1492 yield f
1493 fns = fns_generator()
1493 fns = fns_generator()
1494 prepare(ctx, fns)
1494 prepare(ctx, fns)
1495 for rev in nrevs:
1495 for rev in nrevs:
1496 yield change(rev)
1496 yield change(rev)
1497
1497
1498 if stopiteration:
1498 if stopiteration:
1499 break
1499 break
1500
1500
1501 return iterate()
1501 return iterate()
1502
1502
1503 def _makelogfilematcher(repo, files, followfirst):
1503 def _makefollowlogfilematcher(repo, files, followfirst):
1504 # When displaying a revision with --patch --follow FILE, we have
1504 # When displaying a revision with --patch --follow FILE, we have
1505 # to know which file of the revision must be diffed. With
1505 # to know which file of the revision must be diffed. With
1506 # --follow, we want the names of the ancestors of FILE in the
1506 # --follow, we want the names of the ancestors of FILE in the
1507 # revision, stored in "fcache". "fcache" is populated by
1507 # revision, stored in "fcache". "fcache" is populated by
1508 # reproducing the graph traversal already done by --follow revset
1508 # reproducing the graph traversal already done by --follow revset
1509 # and relating linkrevs to file names (which is not "correct" but
1509 # and relating linkrevs to file names (which is not "correct" but
1510 # good enough).
1510 # good enough).
1511 fcache = {}
1511 fcache = {}
1512 fcacheready = [False]
1512 fcacheready = [False]
1513 pctx = repo['.']
1513 pctx = repo['.']
1514
1514
1515 def populate():
1515 def populate():
1516 for fn in files:
1516 for fn in files:
1517 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1517 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1518 for c in i:
1518 for c in i:
1519 fcache.setdefault(c.linkrev(), set()).add(c.path())
1519 fcache.setdefault(c.linkrev(), set()).add(c.path())
1520
1520
1521 def filematcher(rev):
1521 def filematcher(rev):
1522 if not fcacheready[0]:
1522 if not fcacheready[0]:
1523 # Lazy initialization
1523 # Lazy initialization
1524 fcacheready[0] = True
1524 fcacheready[0] = True
1525 populate()
1525 populate()
1526 return scmutil.matchfiles(repo, fcache.get(rev, []))
1526 return scmutil.matchfiles(repo, fcache.get(rev, []))
1527
1527
1528 return filematcher
1528 return filematcher
1529
1529
1530 def _makenofollowlogfilematcher(repo, pats, opts):
1531 '''hook for extensions to override the filematcher for non-follow cases'''
1532 return None
1533
1530 def _makelogrevset(repo, pats, opts, revs):
1534 def _makelogrevset(repo, pats, opts, revs):
1531 """Return (expr, filematcher) where expr is a revset string built
1535 """Return (expr, filematcher) where expr is a revset string built
1532 from log options and file patterns or None. If --stat or --patch
1536 from log options and file patterns or None. If --stat or --patch
1533 are not passed filematcher is None. Otherwise it is a callable
1537 are not passed filematcher is None. Otherwise it is a callable
1534 taking a revision number and returning a match objects filtering
1538 taking a revision number and returning a match objects filtering
1535 the files to be detailed when displaying the revision.
1539 the files to be detailed when displaying the revision.
1536 """
1540 """
1537 opt2revset = {
1541 opt2revset = {
1538 'no_merges': ('not merge()', None),
1542 'no_merges': ('not merge()', None),
1539 'only_merges': ('merge()', None),
1543 'only_merges': ('merge()', None),
1540 '_ancestors': ('ancestors(%(val)s)', None),
1544 '_ancestors': ('ancestors(%(val)s)', None),
1541 '_fancestors': ('_firstancestors(%(val)s)', None),
1545 '_fancestors': ('_firstancestors(%(val)s)', None),
1542 '_descendants': ('descendants(%(val)s)', None),
1546 '_descendants': ('descendants(%(val)s)', None),
1543 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1547 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1544 '_matchfiles': ('_matchfiles(%(val)s)', None),
1548 '_matchfiles': ('_matchfiles(%(val)s)', None),
1545 'date': ('date(%(val)r)', None),
1549 'date': ('date(%(val)r)', None),
1546 'branch': ('branch(%(val)r)', ' or '),
1550 'branch': ('branch(%(val)r)', ' or '),
1547 '_patslog': ('filelog(%(val)r)', ' or '),
1551 '_patslog': ('filelog(%(val)r)', ' or '),
1548 '_patsfollow': ('follow(%(val)r)', ' or '),
1552 '_patsfollow': ('follow(%(val)r)', ' or '),
1549 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1553 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1550 'keyword': ('keyword(%(val)r)', ' or '),
1554 'keyword': ('keyword(%(val)r)', ' or '),
1551 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1555 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1552 'user': ('user(%(val)r)', ' or '),
1556 'user': ('user(%(val)r)', ' or '),
1553 }
1557 }
1554
1558
1555 opts = dict(opts)
1559 opts = dict(opts)
1556 # follow or not follow?
1560 # follow or not follow?
1557 follow = opts.get('follow') or opts.get('follow_first')
1561 follow = opts.get('follow') or opts.get('follow_first')
1558 followfirst = opts.get('follow_first') and 1 or 0
1562 followfirst = opts.get('follow_first') and 1 or 0
1559 # --follow with FILE behaviour depends on revs...
1563 # --follow with FILE behaviour depends on revs...
1560 it = iter(revs)
1564 it = iter(revs)
1561 startrev = it.next()
1565 startrev = it.next()
1562 try:
1566 try:
1563 followdescendants = startrev < it.next()
1567 followdescendants = startrev < it.next()
1564 except (StopIteration):
1568 except (StopIteration):
1565 followdescendants = False
1569 followdescendants = False
1566
1570
1567 # branch and only_branch are really aliases and must be handled at
1571 # branch and only_branch are really aliases and must be handled at
1568 # the same time
1572 # the same time
1569 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1573 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1570 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1574 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1571 # pats/include/exclude are passed to match.match() directly in
1575 # pats/include/exclude are passed to match.match() directly in
1572 # _matchfiles() revset but walkchangerevs() builds its matcher with
1576 # _matchfiles() revset but walkchangerevs() builds its matcher with
1573 # scmutil.match(). The difference is input pats are globbed on
1577 # scmutil.match(). The difference is input pats are globbed on
1574 # platforms without shell expansion (windows).
1578 # platforms without shell expansion (windows).
1575 pctx = repo[None]
1579 pctx = repo[None]
1576 match, pats = scmutil.matchandpats(pctx, pats, opts)
1580 match, pats = scmutil.matchandpats(pctx, pats, opts)
1577 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1581 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1578 if not slowpath:
1582 if not slowpath:
1579 for f in match.files():
1583 for f in match.files():
1580 if follow and f not in pctx:
1584 if follow and f not in pctx:
1581 # If the file exists, it may be a directory, so let it
1585 # If the file exists, it may be a directory, so let it
1582 # take the slow path.
1586 # take the slow path.
1583 if os.path.exists(repo.wjoin(f)):
1587 if os.path.exists(repo.wjoin(f)):
1584 slowpath = True
1588 slowpath = True
1585 continue
1589 continue
1586 else:
1590 else:
1587 raise util.Abort(_('cannot follow file not in parent '
1591 raise util.Abort(_('cannot follow file not in parent '
1588 'revision: "%s"') % f)
1592 'revision: "%s"') % f)
1589 filelog = repo.file(f)
1593 filelog = repo.file(f)
1590 if not filelog:
1594 if not filelog:
1591 # A zero count may be a directory or deleted file, so
1595 # A zero count may be a directory or deleted file, so
1592 # try to find matching entries on the slow path.
1596 # try to find matching entries on the slow path.
1593 if follow:
1597 if follow:
1594 raise util.Abort(
1598 raise util.Abort(
1595 _('cannot follow nonexistent file: "%s"') % f)
1599 _('cannot follow nonexistent file: "%s"') % f)
1596 slowpath = True
1600 slowpath = True
1597
1601
1598 # We decided to fall back to the slowpath because at least one
1602 # We decided to fall back to the slowpath because at least one
1599 # of the paths was not a file. Check to see if at least one of them
1603 # of the paths was not a file. Check to see if at least one of them
1600 # existed in history - in that case, we'll continue down the
1604 # existed in history - in that case, we'll continue down the
1601 # slowpath; otherwise, we can turn off the slowpath
1605 # slowpath; otherwise, we can turn off the slowpath
1602 if slowpath:
1606 if slowpath:
1603 for path in match.files():
1607 for path in match.files():
1604 if path == '.' or path in repo.store:
1608 if path == '.' or path in repo.store:
1605 break
1609 break
1606 else:
1610 else:
1607 slowpath = False
1611 slowpath = False
1608
1612
1609 if slowpath:
1613 if slowpath:
1610 # See walkchangerevs() slow path.
1614 # See walkchangerevs() slow path.
1611 #
1615 #
1612 # pats/include/exclude cannot be represented as separate
1616 # pats/include/exclude cannot be represented as separate
1613 # revset expressions as their filtering logic applies at file
1617 # revset expressions as their filtering logic applies at file
1614 # level. For instance "-I a -X a" matches a revision touching
1618 # level. For instance "-I a -X a" matches a revision touching
1615 # "a" and "b" while "file(a) and not file(b)" does
1619 # "a" and "b" while "file(a) and not file(b)" does
1616 # not. Besides, filesets are evaluated against the working
1620 # not. Besides, filesets are evaluated against the working
1617 # directory.
1621 # directory.
1618 matchargs = ['r:', 'd:relpath']
1622 matchargs = ['r:', 'd:relpath']
1619 for p in pats:
1623 for p in pats:
1620 matchargs.append('p:' + p)
1624 matchargs.append('p:' + p)
1621 for p in opts.get('include', []):
1625 for p in opts.get('include', []):
1622 matchargs.append('i:' + p)
1626 matchargs.append('i:' + p)
1623 for p in opts.get('exclude', []):
1627 for p in opts.get('exclude', []):
1624 matchargs.append('x:' + p)
1628 matchargs.append('x:' + p)
1625 matchargs = ','.join(('%r' % p) for p in matchargs)
1629 matchargs = ','.join(('%r' % p) for p in matchargs)
1626 opts['_matchfiles'] = matchargs
1630 opts['_matchfiles'] = matchargs
1627 else:
1631 else:
1628 if follow:
1632 if follow:
1629 fpats = ('_patsfollow', '_patsfollowfirst')
1633 fpats = ('_patsfollow', '_patsfollowfirst')
1630 fnopats = (('_ancestors', '_fancestors'),
1634 fnopats = (('_ancestors', '_fancestors'),
1631 ('_descendants', '_fdescendants'))
1635 ('_descendants', '_fdescendants'))
1632 if pats:
1636 if pats:
1633 # follow() revset interprets its file argument as a
1637 # follow() revset interprets its file argument as a
1634 # manifest entry, so use match.files(), not pats.
1638 # manifest entry, so use match.files(), not pats.
1635 opts[fpats[followfirst]] = list(match.files())
1639 opts[fpats[followfirst]] = list(match.files())
1636 else:
1640 else:
1637 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1641 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1638 else:
1642 else:
1639 opts['_patslog'] = list(pats)
1643 opts['_patslog'] = list(pats)
1640
1644
1641 filematcher = None
1645 filematcher = None
1642 if opts.get('patch') or opts.get('stat'):
1646 if opts.get('patch') or opts.get('stat'):
1643 # When following files, track renames via a special matcher.
1647 # When following files, track renames via a special matcher.
1644 # If we're forced to take the slowpath it means we're following
1648 # If we're forced to take the slowpath it means we're following
1645 # at least one pattern/directory, so don't bother with rename tracking.
1649 # at least one pattern/directory, so don't bother with rename tracking.
1646 if follow and not match.always() and not slowpath:
1650 if follow and not match.always() and not slowpath:
1647 # _makelogfilematcher expects its files argument to be relative to
1651 # _makelogfilematcher expects its files argument to be relative to
1648 # the repo root, so use match.files(), not pats.
1652 # the repo root, so use match.files(), not pats.
1649 filematcher = _makelogfilematcher(repo, match.files(), followfirst)
1653 filematcher = _makefollowlogfilematcher(repo, match.files(),
1654 followfirst)
1650 else:
1655 else:
1651 filematcher = lambda rev: match
1656 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
1657 if filematcher is None:
1658 filematcher = lambda rev: match
1652
1659
1653 expr = []
1660 expr = []
1654 for op, val in opts.iteritems():
1661 for op, val in opts.iteritems():
1655 if not val:
1662 if not val:
1656 continue
1663 continue
1657 if op not in opt2revset:
1664 if op not in opt2revset:
1658 continue
1665 continue
1659 revop, andor = opt2revset[op]
1666 revop, andor = opt2revset[op]
1660 if '%(val)' not in revop:
1667 if '%(val)' not in revop:
1661 expr.append(revop)
1668 expr.append(revop)
1662 else:
1669 else:
1663 if not isinstance(val, list):
1670 if not isinstance(val, list):
1664 e = revop % {'val': val}
1671 e = revop % {'val': val}
1665 else:
1672 else:
1666 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1673 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1667 expr.append(e)
1674 expr.append(e)
1668
1675
1669 if expr:
1676 if expr:
1670 expr = '(' + ' and '.join(expr) + ')'
1677 expr = '(' + ' and '.join(expr) + ')'
1671 else:
1678 else:
1672 expr = None
1679 expr = None
1673 return expr, filematcher
1680 return expr, filematcher
1674
1681
1675 def getgraphlogrevs(repo, pats, opts):
1682 def getgraphlogrevs(repo, pats, opts):
1676 """Return (revs, expr, filematcher) where revs is an iterable of
1683 """Return (revs, expr, filematcher) where revs is an iterable of
1677 revision numbers, expr is a revset string built from log options
1684 revision numbers, expr is a revset string built from log options
1678 and file patterns or None, and used to filter 'revs'. If --stat or
1685 and file patterns or None, and used to filter 'revs'. If --stat or
1679 --patch are not passed filematcher is None. Otherwise it is a
1686 --patch are not passed filematcher is None. Otherwise it is a
1680 callable taking a revision number and returning a match objects
1687 callable taking a revision number and returning a match objects
1681 filtering the files to be detailed when displaying the revision.
1688 filtering the files to be detailed when displaying the revision.
1682 """
1689 """
1683 if not len(repo):
1690 if not len(repo):
1684 return [], None, None
1691 return [], None, None
1685 limit = loglimit(opts)
1692 limit = loglimit(opts)
1686 # Default --rev value depends on --follow but --follow behaviour
1693 # Default --rev value depends on --follow but --follow behaviour
1687 # depends on revisions resolved from --rev...
1694 # depends on revisions resolved from --rev...
1688 follow = opts.get('follow') or opts.get('follow_first')
1695 follow = opts.get('follow') or opts.get('follow_first')
1689 possiblyunsorted = False # whether revs might need sorting
1696 possiblyunsorted = False # whether revs might need sorting
1690 if opts.get('rev'):
1697 if opts.get('rev'):
1691 revs = scmutil.revrange(repo, opts['rev'])
1698 revs = scmutil.revrange(repo, opts['rev'])
1692 # Don't sort here because _makelogrevset might depend on the
1699 # Don't sort here because _makelogrevset might depend on the
1693 # order of revs
1700 # order of revs
1694 possiblyunsorted = True
1701 possiblyunsorted = True
1695 else:
1702 else:
1696 if follow and len(repo) > 0:
1703 if follow and len(repo) > 0:
1697 revs = repo.revs('reverse(:.)')
1704 revs = repo.revs('reverse(:.)')
1698 else:
1705 else:
1699 revs = revset.spanset(repo)
1706 revs = revset.spanset(repo)
1700 revs.reverse()
1707 revs.reverse()
1701 if not revs:
1708 if not revs:
1702 return revset.baseset(), None, None
1709 return revset.baseset(), None, None
1703 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1710 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1704 if possiblyunsorted:
1711 if possiblyunsorted:
1705 revs.sort(reverse=True)
1712 revs.sort(reverse=True)
1706 if expr:
1713 if expr:
1707 # Revset matchers often operate faster on revisions in changelog
1714 # Revset matchers often operate faster on revisions in changelog
1708 # order, because most filters deal with the changelog.
1715 # order, because most filters deal with the changelog.
1709 revs.reverse()
1716 revs.reverse()
1710 matcher = revset.match(repo.ui, expr)
1717 matcher = revset.match(repo.ui, expr)
1711 # Revset matches can reorder revisions. "A or B" typically returns
1718 # Revset matches can reorder revisions. "A or B" typically returns
1712 # returns the revision matching A then the revision matching B. Sort
1719 # returns the revision matching A then the revision matching B. Sort
1713 # again to fix that.
1720 # again to fix that.
1714 revs = matcher(repo, revs)
1721 revs = matcher(repo, revs)
1715 revs.sort(reverse=True)
1722 revs.sort(reverse=True)
1716 if limit is not None:
1723 if limit is not None:
1717 limitedrevs = revset.baseset()
1724 limitedrevs = revset.baseset()
1718 for idx, rev in enumerate(revs):
1725 for idx, rev in enumerate(revs):
1719 if idx >= limit:
1726 if idx >= limit:
1720 break
1727 break
1721 limitedrevs.append(rev)
1728 limitedrevs.append(rev)
1722 revs = limitedrevs
1729 revs = limitedrevs
1723
1730
1724 return revs, expr, filematcher
1731 return revs, expr, filematcher
1725
1732
1726 def getlogrevs(repo, pats, opts):
1733 def getlogrevs(repo, pats, opts):
1727 """Return (revs, expr, filematcher) where revs is an iterable of
1734 """Return (revs, expr, filematcher) where revs is an iterable of
1728 revision numbers, expr is a revset string built from log options
1735 revision numbers, expr is a revset string built from log options
1729 and file patterns or None, and used to filter 'revs'. If --stat or
1736 and file patterns or None, and used to filter 'revs'. If --stat or
1730 --patch are not passed filematcher is None. Otherwise it is a
1737 --patch are not passed filematcher is None. Otherwise it is a
1731 callable taking a revision number and returning a match objects
1738 callable taking a revision number and returning a match objects
1732 filtering the files to be detailed when displaying the revision.
1739 filtering the files to be detailed when displaying the revision.
1733 """
1740 """
1734 limit = loglimit(opts)
1741 limit = loglimit(opts)
1735 # Default --rev value depends on --follow but --follow behaviour
1742 # Default --rev value depends on --follow but --follow behaviour
1736 # depends on revisions resolved from --rev...
1743 # depends on revisions resolved from --rev...
1737 follow = opts.get('follow') or opts.get('follow_first')
1744 follow = opts.get('follow') or opts.get('follow_first')
1738 if opts.get('rev'):
1745 if opts.get('rev'):
1739 revs = scmutil.revrange(repo, opts['rev'])
1746 revs = scmutil.revrange(repo, opts['rev'])
1740 elif follow:
1747 elif follow:
1741 revs = repo.revs('reverse(:.)')
1748 revs = repo.revs('reverse(:.)')
1742 else:
1749 else:
1743 revs = revset.spanset(repo)
1750 revs = revset.spanset(repo)
1744 revs.reverse()
1751 revs.reverse()
1745 if not revs:
1752 if not revs:
1746 return revset.baseset([]), None, None
1753 return revset.baseset([]), None, None
1747 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1754 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1748 if expr:
1755 if expr:
1749 # Revset matchers often operate faster on revisions in changelog
1756 # Revset matchers often operate faster on revisions in changelog
1750 # order, because most filters deal with the changelog.
1757 # order, because most filters deal with the changelog.
1751 if not opts.get('rev'):
1758 if not opts.get('rev'):
1752 revs.reverse()
1759 revs.reverse()
1753 matcher = revset.match(repo.ui, expr)
1760 matcher = revset.match(repo.ui, expr)
1754 # Revset matches can reorder revisions. "A or B" typically returns
1761 # Revset matches can reorder revisions. "A or B" typically returns
1755 # returns the revision matching A then the revision matching B. Sort
1762 # returns the revision matching A then the revision matching B. Sort
1756 # again to fix that.
1763 # again to fix that.
1757 revs = matcher(repo, revs)
1764 revs = matcher(repo, revs)
1758 if not opts.get('rev'):
1765 if not opts.get('rev'):
1759 revs.sort(reverse=True)
1766 revs.sort(reverse=True)
1760 if limit is not None:
1767 if limit is not None:
1761 count = 0
1768 count = 0
1762 limitedrevs = revset.baseset([])
1769 limitedrevs = revset.baseset([])
1763 it = iter(revs)
1770 it = iter(revs)
1764 while count < limit:
1771 while count < limit:
1765 try:
1772 try:
1766 limitedrevs.append(it.next())
1773 limitedrevs.append(it.next())
1767 except (StopIteration):
1774 except (StopIteration):
1768 break
1775 break
1769 count += 1
1776 count += 1
1770 revs = limitedrevs
1777 revs = limitedrevs
1771
1778
1772 return revs, expr, filematcher
1779 return revs, expr, filematcher
1773
1780
1774 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1781 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1775 filematcher=None):
1782 filematcher=None):
1776 seen, state = [], graphmod.asciistate()
1783 seen, state = [], graphmod.asciistate()
1777 for rev, type, ctx, parents in dag:
1784 for rev, type, ctx, parents in dag:
1778 char = 'o'
1785 char = 'o'
1779 if ctx.node() in showparents:
1786 if ctx.node() in showparents:
1780 char = '@'
1787 char = '@'
1781 elif ctx.obsolete():
1788 elif ctx.obsolete():
1782 char = 'x'
1789 char = 'x'
1783 copies = None
1790 copies = None
1784 if getrenamed and ctx.rev():
1791 if getrenamed and ctx.rev():
1785 copies = []
1792 copies = []
1786 for fn in ctx.files():
1793 for fn in ctx.files():
1787 rename = getrenamed(fn, ctx.rev())
1794 rename = getrenamed(fn, ctx.rev())
1788 if rename:
1795 if rename:
1789 copies.append((fn, rename[0]))
1796 copies.append((fn, rename[0]))
1790 revmatchfn = None
1797 revmatchfn = None
1791 if filematcher is not None:
1798 if filematcher is not None:
1792 revmatchfn = filematcher(ctx.rev())
1799 revmatchfn = filematcher(ctx.rev())
1793 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1800 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1794 lines = displayer.hunk.pop(rev).split('\n')
1801 lines = displayer.hunk.pop(rev).split('\n')
1795 if not lines[-1]:
1802 if not lines[-1]:
1796 del lines[-1]
1803 del lines[-1]
1797 displayer.flush(rev)
1804 displayer.flush(rev)
1798 edges = edgefn(type, char, lines, seen, rev, parents)
1805 edges = edgefn(type, char, lines, seen, rev, parents)
1799 for type, char, lines, coldata in edges:
1806 for type, char, lines, coldata in edges:
1800 graphmod.ascii(ui, state, type, char, lines, coldata)
1807 graphmod.ascii(ui, state, type, char, lines, coldata)
1801 displayer.close()
1808 displayer.close()
1802
1809
1803 def graphlog(ui, repo, *pats, **opts):
1810 def graphlog(ui, repo, *pats, **opts):
1804 # Parameters are identical to log command ones
1811 # Parameters are identical to log command ones
1805 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1812 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1806 revdag = graphmod.dagwalker(repo, revs)
1813 revdag = graphmod.dagwalker(repo, revs)
1807
1814
1808 getrenamed = None
1815 getrenamed = None
1809 if opts.get('copies'):
1816 if opts.get('copies'):
1810 endrev = None
1817 endrev = None
1811 if opts.get('rev'):
1818 if opts.get('rev'):
1812 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
1819 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
1813 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1820 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1814 displayer = show_changeset(ui, repo, opts, buffered=True)
1821 displayer = show_changeset(ui, repo, opts, buffered=True)
1815 showparents = [ctx.node() for ctx in repo[None].parents()]
1822 showparents = [ctx.node() for ctx in repo[None].parents()]
1816 displaygraph(ui, revdag, displayer, showparents,
1823 displaygraph(ui, revdag, displayer, showparents,
1817 graphmod.asciiedges, getrenamed, filematcher)
1824 graphmod.asciiedges, getrenamed, filematcher)
1818
1825
1819 def checkunsupportedgraphflags(pats, opts):
1826 def checkunsupportedgraphflags(pats, opts):
1820 for op in ["newest_first"]:
1827 for op in ["newest_first"]:
1821 if op in opts and opts[op]:
1828 if op in opts and opts[op]:
1822 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1829 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1823 % op.replace("_", "-"))
1830 % op.replace("_", "-"))
1824
1831
1825 def graphrevs(repo, nodes, opts):
1832 def graphrevs(repo, nodes, opts):
1826 limit = loglimit(opts)
1833 limit = loglimit(opts)
1827 nodes.reverse()
1834 nodes.reverse()
1828 if limit is not None:
1835 if limit is not None:
1829 nodes = nodes[:limit]
1836 nodes = nodes[:limit]
1830 return graphmod.nodes(repo, nodes)
1837 return graphmod.nodes(repo, nodes)
1831
1838
1832 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1839 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1833 join = lambda f: os.path.join(prefix, f)
1840 join = lambda f: os.path.join(prefix, f)
1834 bad = []
1841 bad = []
1835 oldbad = match.bad
1842 oldbad = match.bad
1836 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1843 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1837 names = []
1844 names = []
1838 wctx = repo[None]
1845 wctx = repo[None]
1839 cca = None
1846 cca = None
1840 abort, warn = scmutil.checkportabilityalert(ui)
1847 abort, warn = scmutil.checkportabilityalert(ui)
1841 if abort or warn:
1848 if abort or warn:
1842 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1849 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1843 for f in repo.walk(match):
1850 for f in repo.walk(match):
1844 exact = match.exact(f)
1851 exact = match.exact(f)
1845 if exact or not explicitonly and f not in repo.dirstate:
1852 if exact or not explicitonly and f not in repo.dirstate:
1846 if cca:
1853 if cca:
1847 cca(f)
1854 cca(f)
1848 names.append(f)
1855 names.append(f)
1849 if ui.verbose or not exact:
1856 if ui.verbose or not exact:
1850 ui.status(_('adding %s\n') % match.rel(join(f)))
1857 ui.status(_('adding %s\n') % match.rel(join(f)))
1851
1858
1852 for subpath in sorted(wctx.substate):
1859 for subpath in sorted(wctx.substate):
1853 sub = wctx.sub(subpath)
1860 sub = wctx.sub(subpath)
1854 try:
1861 try:
1855 submatch = matchmod.narrowmatcher(subpath, match)
1862 submatch = matchmod.narrowmatcher(subpath, match)
1856 if listsubrepos:
1863 if listsubrepos:
1857 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1864 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1858 False))
1865 False))
1859 else:
1866 else:
1860 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1867 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1861 True))
1868 True))
1862 except error.LookupError:
1869 except error.LookupError:
1863 ui.status(_("skipping missing subrepository: %s\n")
1870 ui.status(_("skipping missing subrepository: %s\n")
1864 % join(subpath))
1871 % join(subpath))
1865
1872
1866 if not dryrun:
1873 if not dryrun:
1867 rejected = wctx.add(names, prefix)
1874 rejected = wctx.add(names, prefix)
1868 bad.extend(f for f in rejected if f in match.files())
1875 bad.extend(f for f in rejected if f in match.files())
1869 return bad
1876 return bad
1870
1877
1871 def forget(ui, repo, match, prefix, explicitonly):
1878 def forget(ui, repo, match, prefix, explicitonly):
1872 join = lambda f: os.path.join(prefix, f)
1879 join = lambda f: os.path.join(prefix, f)
1873 bad = []
1880 bad = []
1874 oldbad = match.bad
1881 oldbad = match.bad
1875 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1882 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1876 wctx = repo[None]
1883 wctx = repo[None]
1877 forgot = []
1884 forgot = []
1878 s = repo.status(match=match, clean=True)
1885 s = repo.status(match=match, clean=True)
1879 forget = sorted(s[0] + s[1] + s[3] + s[6])
1886 forget = sorted(s[0] + s[1] + s[3] + s[6])
1880 if explicitonly:
1887 if explicitonly:
1881 forget = [f for f in forget if match.exact(f)]
1888 forget = [f for f in forget if match.exact(f)]
1882
1889
1883 for subpath in sorted(wctx.substate):
1890 for subpath in sorted(wctx.substate):
1884 sub = wctx.sub(subpath)
1891 sub = wctx.sub(subpath)
1885 try:
1892 try:
1886 submatch = matchmod.narrowmatcher(subpath, match)
1893 submatch = matchmod.narrowmatcher(subpath, match)
1887 subbad, subforgot = sub.forget(ui, submatch, prefix)
1894 subbad, subforgot = sub.forget(ui, submatch, prefix)
1888 bad.extend([subpath + '/' + f for f in subbad])
1895 bad.extend([subpath + '/' + f for f in subbad])
1889 forgot.extend([subpath + '/' + f for f in subforgot])
1896 forgot.extend([subpath + '/' + f for f in subforgot])
1890 except error.LookupError:
1897 except error.LookupError:
1891 ui.status(_("skipping missing subrepository: %s\n")
1898 ui.status(_("skipping missing subrepository: %s\n")
1892 % join(subpath))
1899 % join(subpath))
1893
1900
1894 if not explicitonly:
1901 if not explicitonly:
1895 for f in match.files():
1902 for f in match.files():
1896 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1903 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1897 if f not in forgot:
1904 if f not in forgot:
1898 if os.path.exists(match.rel(join(f))):
1905 if os.path.exists(match.rel(join(f))):
1899 ui.warn(_('not removing %s: '
1906 ui.warn(_('not removing %s: '
1900 'file is already untracked\n')
1907 'file is already untracked\n')
1901 % match.rel(join(f)))
1908 % match.rel(join(f)))
1902 bad.append(f)
1909 bad.append(f)
1903
1910
1904 for f in forget:
1911 for f in forget:
1905 if ui.verbose or not match.exact(f):
1912 if ui.verbose or not match.exact(f):
1906 ui.status(_('removing %s\n') % match.rel(join(f)))
1913 ui.status(_('removing %s\n') % match.rel(join(f)))
1907
1914
1908 rejected = wctx.forget(forget, prefix)
1915 rejected = wctx.forget(forget, prefix)
1909 bad.extend(f for f in rejected if f in match.files())
1916 bad.extend(f for f in rejected if f in match.files())
1910 forgot.extend(forget)
1917 forgot.extend(forget)
1911 return bad, forgot
1918 return bad, forgot
1912
1919
1913 def cat(ui, repo, ctx, matcher, prefix, **opts):
1920 def cat(ui, repo, ctx, matcher, prefix, **opts):
1914 err = 1
1921 err = 1
1915
1922
1916 def write(path):
1923 def write(path):
1917 fp = makefileobj(repo, opts.get('output'), ctx.node(),
1924 fp = makefileobj(repo, opts.get('output'), ctx.node(),
1918 pathname=os.path.join(prefix, path))
1925 pathname=os.path.join(prefix, path))
1919 data = ctx[path].data()
1926 data = ctx[path].data()
1920 if opts.get('decode'):
1927 if opts.get('decode'):
1921 data = repo.wwritedata(path, data)
1928 data = repo.wwritedata(path, data)
1922 fp.write(data)
1929 fp.write(data)
1923 fp.close()
1930 fp.close()
1924
1931
1925 # Automation often uses hg cat on single files, so special case it
1932 # Automation often uses hg cat on single files, so special case it
1926 # for performance to avoid the cost of parsing the manifest.
1933 # for performance to avoid the cost of parsing the manifest.
1927 if len(matcher.files()) == 1 and not matcher.anypats():
1934 if len(matcher.files()) == 1 and not matcher.anypats():
1928 file = matcher.files()[0]
1935 file = matcher.files()[0]
1929 mf = repo.manifest
1936 mf = repo.manifest
1930 mfnode = ctx._changeset[0]
1937 mfnode = ctx._changeset[0]
1931 if mf.find(mfnode, file)[0]:
1938 if mf.find(mfnode, file)[0]:
1932 write(file)
1939 write(file)
1933 return 0
1940 return 0
1934
1941
1935 # Don't warn about "missing" files that are really in subrepos
1942 # Don't warn about "missing" files that are really in subrepos
1936 bad = matcher.bad
1943 bad = matcher.bad
1937
1944
1938 def badfn(path, msg):
1945 def badfn(path, msg):
1939 for subpath in ctx.substate:
1946 for subpath in ctx.substate:
1940 if path.startswith(subpath):
1947 if path.startswith(subpath):
1941 return
1948 return
1942 bad(path, msg)
1949 bad(path, msg)
1943
1950
1944 matcher.bad = badfn
1951 matcher.bad = badfn
1945
1952
1946 for abs in ctx.walk(matcher):
1953 for abs in ctx.walk(matcher):
1947 write(abs)
1954 write(abs)
1948 err = 0
1955 err = 0
1949
1956
1950 matcher.bad = bad
1957 matcher.bad = bad
1951
1958
1952 for subpath in sorted(ctx.substate):
1959 for subpath in sorted(ctx.substate):
1953 sub = ctx.sub(subpath)
1960 sub = ctx.sub(subpath)
1954 try:
1961 try:
1955 submatch = matchmod.narrowmatcher(subpath, matcher)
1962 submatch = matchmod.narrowmatcher(subpath, matcher)
1956
1963
1957 if not sub.cat(ui, submatch, os.path.join(prefix, sub._path),
1964 if not sub.cat(ui, submatch, os.path.join(prefix, sub._path),
1958 **opts):
1965 **opts):
1959 err = 0
1966 err = 0
1960 except error.RepoLookupError:
1967 except error.RepoLookupError:
1961 ui.status(_("skipping missing subrepository: %s\n")
1968 ui.status(_("skipping missing subrepository: %s\n")
1962 % os.path.join(prefix, subpath))
1969 % os.path.join(prefix, subpath))
1963
1970
1964 return err
1971 return err
1965
1972
1966 def duplicatecopies(repo, rev, fromrev, skiprev=None):
1973 def duplicatecopies(repo, rev, fromrev, skiprev=None):
1967 '''reproduce copies from fromrev to rev in the dirstate
1974 '''reproduce copies from fromrev to rev in the dirstate
1968
1975
1969 If skiprev is specified, it's a revision that should be used to
1976 If skiprev is specified, it's a revision that should be used to
1970 filter copy records. Any copies that occur between fromrev and
1977 filter copy records. Any copies that occur between fromrev and
1971 skiprev will not be duplicated, even if they appear in the set of
1978 skiprev will not be duplicated, even if they appear in the set of
1972 copies between fromrev and rev.
1979 copies between fromrev and rev.
1973 '''
1980 '''
1974 exclude = {}
1981 exclude = {}
1975 if skiprev is not None:
1982 if skiprev is not None:
1976 exclude = copies.pathcopies(repo[fromrev], repo[skiprev])
1983 exclude = copies.pathcopies(repo[fromrev], repo[skiprev])
1977 for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
1984 for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
1978 # copies.pathcopies returns backward renames, so dst might not
1985 # copies.pathcopies returns backward renames, so dst might not
1979 # actually be in the dirstate
1986 # actually be in the dirstate
1980 if dst in exclude:
1987 if dst in exclude:
1981 continue
1988 continue
1982 if repo.dirstate[dst] in "nma":
1989 if repo.dirstate[dst] in "nma":
1983 repo.dirstate.copy(src, dst)
1990 repo.dirstate.copy(src, dst)
1984
1991
1985 def commit(ui, repo, commitfunc, pats, opts):
1992 def commit(ui, repo, commitfunc, pats, opts):
1986 '''commit the specified files or all outstanding changes'''
1993 '''commit the specified files or all outstanding changes'''
1987 date = opts.get('date')
1994 date = opts.get('date')
1988 if date:
1995 if date:
1989 opts['date'] = util.parsedate(date)
1996 opts['date'] = util.parsedate(date)
1990 message = logmessage(ui, opts)
1997 message = logmessage(ui, opts)
1991
1998
1992 # extract addremove carefully -- this function can be called from a command
1999 # extract addremove carefully -- this function can be called from a command
1993 # that doesn't support addremove
2000 # that doesn't support addremove
1994 if opts.get('addremove'):
2001 if opts.get('addremove'):
1995 scmutil.addremove(repo, pats, opts)
2002 scmutil.addremove(repo, pats, opts)
1996
2003
1997 return commitfunc(ui, repo, message,
2004 return commitfunc(ui, repo, message,
1998 scmutil.match(repo[None], pats, opts), opts)
2005 scmutil.match(repo[None], pats, opts), opts)
1999
2006
2000 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2007 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2001 ui.note(_('amending changeset %s\n') % old)
2008 ui.note(_('amending changeset %s\n') % old)
2002 base = old.p1()
2009 base = old.p1()
2003
2010
2004 wlock = lock = newid = None
2011 wlock = lock = newid = None
2005 try:
2012 try:
2006 wlock = repo.wlock()
2013 wlock = repo.wlock()
2007 lock = repo.lock()
2014 lock = repo.lock()
2008 tr = repo.transaction('amend')
2015 tr = repo.transaction('amend')
2009 try:
2016 try:
2010 # See if we got a message from -m or -l, if not, open the editor
2017 # See if we got a message from -m or -l, if not, open the editor
2011 # with the message of the changeset to amend
2018 # with the message of the changeset to amend
2012 message = logmessage(ui, opts)
2019 message = logmessage(ui, opts)
2013 # ensure logfile does not conflict with later enforcement of the
2020 # ensure logfile does not conflict with later enforcement of the
2014 # message. potential logfile content has been processed by
2021 # message. potential logfile content has been processed by
2015 # `logmessage` anyway.
2022 # `logmessage` anyway.
2016 opts.pop('logfile')
2023 opts.pop('logfile')
2017 # First, do a regular commit to record all changes in the working
2024 # First, do a regular commit to record all changes in the working
2018 # directory (if there are any)
2025 # directory (if there are any)
2019 ui.callhooks = False
2026 ui.callhooks = False
2020 currentbookmark = repo._bookmarkcurrent
2027 currentbookmark = repo._bookmarkcurrent
2021 try:
2028 try:
2022 repo._bookmarkcurrent = None
2029 repo._bookmarkcurrent = None
2023 opts['message'] = 'temporary amend commit for %s' % old
2030 opts['message'] = 'temporary amend commit for %s' % old
2024 node = commit(ui, repo, commitfunc, pats, opts)
2031 node = commit(ui, repo, commitfunc, pats, opts)
2025 finally:
2032 finally:
2026 repo._bookmarkcurrent = currentbookmark
2033 repo._bookmarkcurrent = currentbookmark
2027 ui.callhooks = True
2034 ui.callhooks = True
2028 ctx = repo[node]
2035 ctx = repo[node]
2029
2036
2030 # Participating changesets:
2037 # Participating changesets:
2031 #
2038 #
2032 # node/ctx o - new (intermediate) commit that contains changes
2039 # node/ctx o - new (intermediate) commit that contains changes
2033 # | from working dir to go into amending commit
2040 # | from working dir to go into amending commit
2034 # | (or a workingctx if there were no changes)
2041 # | (or a workingctx if there were no changes)
2035 # |
2042 # |
2036 # old o - changeset to amend
2043 # old o - changeset to amend
2037 # |
2044 # |
2038 # base o - parent of amending changeset
2045 # base o - parent of amending changeset
2039
2046
2040 # Update extra dict from amended commit (e.g. to preserve graft
2047 # Update extra dict from amended commit (e.g. to preserve graft
2041 # source)
2048 # source)
2042 extra.update(old.extra())
2049 extra.update(old.extra())
2043
2050
2044 # Also update it from the intermediate commit or from the wctx
2051 # Also update it from the intermediate commit or from the wctx
2045 extra.update(ctx.extra())
2052 extra.update(ctx.extra())
2046
2053
2047 if len(old.parents()) > 1:
2054 if len(old.parents()) > 1:
2048 # ctx.files() isn't reliable for merges, so fall back to the
2055 # ctx.files() isn't reliable for merges, so fall back to the
2049 # slower repo.status() method
2056 # slower repo.status() method
2050 files = set([fn for st in repo.status(base, old)[:3]
2057 files = set([fn for st in repo.status(base, old)[:3]
2051 for fn in st])
2058 for fn in st])
2052 else:
2059 else:
2053 files = set(old.files())
2060 files = set(old.files())
2054
2061
2055 # Second, we use either the commit we just did, or if there were no
2062 # Second, we use either the commit we just did, or if there were no
2056 # changes the parent of the working directory as the version of the
2063 # changes the parent of the working directory as the version of the
2057 # files in the final amend commit
2064 # files in the final amend commit
2058 if node:
2065 if node:
2059 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2066 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2060
2067
2061 user = ctx.user()
2068 user = ctx.user()
2062 date = ctx.date()
2069 date = ctx.date()
2063 # Recompute copies (avoid recording a -> b -> a)
2070 # Recompute copies (avoid recording a -> b -> a)
2064 copied = copies.pathcopies(base, ctx)
2071 copied = copies.pathcopies(base, ctx)
2065
2072
2066 # Prune files which were reverted by the updates: if old
2073 # Prune files which were reverted by the updates: if old
2067 # introduced file X and our intermediate commit, node,
2074 # introduced file X and our intermediate commit, node,
2068 # renamed that file, then those two files are the same and
2075 # renamed that file, then those two files are the same and
2069 # we can discard X from our list of files. Likewise if X
2076 # we can discard X from our list of files. Likewise if X
2070 # was deleted, it's no longer relevant
2077 # was deleted, it's no longer relevant
2071 files.update(ctx.files())
2078 files.update(ctx.files())
2072
2079
2073 def samefile(f):
2080 def samefile(f):
2074 if f in ctx.manifest():
2081 if f in ctx.manifest():
2075 a = ctx.filectx(f)
2082 a = ctx.filectx(f)
2076 if f in base.manifest():
2083 if f in base.manifest():
2077 b = base.filectx(f)
2084 b = base.filectx(f)
2078 return (not a.cmp(b)
2085 return (not a.cmp(b)
2079 and a.flags() == b.flags())
2086 and a.flags() == b.flags())
2080 else:
2087 else:
2081 return False
2088 return False
2082 else:
2089 else:
2083 return f not in base.manifest()
2090 return f not in base.manifest()
2084 files = [f for f in files if not samefile(f)]
2091 files = [f for f in files if not samefile(f)]
2085
2092
2086 def filectxfn(repo, ctx_, path):
2093 def filectxfn(repo, ctx_, path):
2087 try:
2094 try:
2088 fctx = ctx[path]
2095 fctx = ctx[path]
2089 flags = fctx.flags()
2096 flags = fctx.flags()
2090 mctx = context.memfilectx(repo,
2097 mctx = context.memfilectx(repo,
2091 fctx.path(), fctx.data(),
2098 fctx.path(), fctx.data(),
2092 islink='l' in flags,
2099 islink='l' in flags,
2093 isexec='x' in flags,
2100 isexec='x' in flags,
2094 copied=copied.get(path))
2101 copied=copied.get(path))
2095 return mctx
2102 return mctx
2096 except KeyError:
2103 except KeyError:
2097 raise IOError
2104 raise IOError
2098 else:
2105 else:
2099 ui.note(_('copying changeset %s to %s\n') % (old, base))
2106 ui.note(_('copying changeset %s to %s\n') % (old, base))
2100
2107
2101 # Use version of files as in the old cset
2108 # Use version of files as in the old cset
2102 def filectxfn(repo, ctx_, path):
2109 def filectxfn(repo, ctx_, path):
2103 try:
2110 try:
2104 return old.filectx(path)
2111 return old.filectx(path)
2105 except KeyError:
2112 except KeyError:
2106 raise IOError
2113 raise IOError
2107
2114
2108 user = opts.get('user') or old.user()
2115 user = opts.get('user') or old.user()
2109 date = opts.get('date') or old.date()
2116 date = opts.get('date') or old.date()
2110 editform = 'commit.amend'
2117 editform = 'commit.amend'
2111 editor = getcommiteditor(editform=editform, **opts)
2118 editor = getcommiteditor(editform=editform, **opts)
2112 if not message:
2119 if not message:
2113 editor = getcommiteditor(edit=True, editform=editform)
2120 editor = getcommiteditor(edit=True, editform=editform)
2114 message = old.description()
2121 message = old.description()
2115
2122
2116 pureextra = extra.copy()
2123 pureextra = extra.copy()
2117 extra['amend_source'] = old.hex()
2124 extra['amend_source'] = old.hex()
2118
2125
2119 new = context.memctx(repo,
2126 new = context.memctx(repo,
2120 parents=[base.node(), old.p2().node()],
2127 parents=[base.node(), old.p2().node()],
2121 text=message,
2128 text=message,
2122 files=files,
2129 files=files,
2123 filectxfn=filectxfn,
2130 filectxfn=filectxfn,
2124 user=user,
2131 user=user,
2125 date=date,
2132 date=date,
2126 extra=extra,
2133 extra=extra,
2127 editor=editor)
2134 editor=editor)
2128
2135
2129 newdesc = changelog.stripdesc(new.description())
2136 newdesc = changelog.stripdesc(new.description())
2130 if ((not node)
2137 if ((not node)
2131 and newdesc == old.description()
2138 and newdesc == old.description()
2132 and user == old.user()
2139 and user == old.user()
2133 and date == old.date()
2140 and date == old.date()
2134 and pureextra == old.extra()):
2141 and pureextra == old.extra()):
2135 # nothing changed. continuing here would create a new node
2142 # nothing changed. continuing here would create a new node
2136 # anyway because of the amend_source noise.
2143 # anyway because of the amend_source noise.
2137 #
2144 #
2138 # This not what we expect from amend.
2145 # This not what we expect from amend.
2139 return old.node()
2146 return old.node()
2140
2147
2141 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2148 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2142 try:
2149 try:
2143 if opts.get('secret'):
2150 if opts.get('secret'):
2144 commitphase = 'secret'
2151 commitphase = 'secret'
2145 else:
2152 else:
2146 commitphase = old.phase()
2153 commitphase = old.phase()
2147 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2154 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2148 newid = repo.commitctx(new)
2155 newid = repo.commitctx(new)
2149 finally:
2156 finally:
2150 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2157 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2151 if newid != old.node():
2158 if newid != old.node():
2152 # Reroute the working copy parent to the new changeset
2159 # Reroute the working copy parent to the new changeset
2153 repo.setparents(newid, nullid)
2160 repo.setparents(newid, nullid)
2154
2161
2155 # Move bookmarks from old parent to amend commit
2162 # Move bookmarks from old parent to amend commit
2156 bms = repo.nodebookmarks(old.node())
2163 bms = repo.nodebookmarks(old.node())
2157 if bms:
2164 if bms:
2158 marks = repo._bookmarks
2165 marks = repo._bookmarks
2159 for bm in bms:
2166 for bm in bms:
2160 marks[bm] = newid
2167 marks[bm] = newid
2161 marks.write()
2168 marks.write()
2162 #commit the whole amend process
2169 #commit the whole amend process
2163 if obsolete._enabled and newid != old.node():
2170 if obsolete._enabled and newid != old.node():
2164 # mark the new changeset as successor of the rewritten one
2171 # mark the new changeset as successor of the rewritten one
2165 new = repo[newid]
2172 new = repo[newid]
2166 obs = [(old, (new,))]
2173 obs = [(old, (new,))]
2167 if node:
2174 if node:
2168 obs.append((ctx, ()))
2175 obs.append((ctx, ()))
2169
2176
2170 obsolete.createmarkers(repo, obs)
2177 obsolete.createmarkers(repo, obs)
2171 tr.close()
2178 tr.close()
2172 finally:
2179 finally:
2173 tr.release()
2180 tr.release()
2174 if (not obsolete._enabled) and newid != old.node():
2181 if (not obsolete._enabled) and newid != old.node():
2175 # Strip the intermediate commit (if there was one) and the amended
2182 # Strip the intermediate commit (if there was one) and the amended
2176 # commit
2183 # commit
2177 if node:
2184 if node:
2178 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2185 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2179 ui.note(_('stripping amended changeset %s\n') % old)
2186 ui.note(_('stripping amended changeset %s\n') % old)
2180 repair.strip(ui, repo, old.node(), topic='amend-backup')
2187 repair.strip(ui, repo, old.node(), topic='amend-backup')
2181 finally:
2188 finally:
2182 if newid is None:
2189 if newid is None:
2183 repo.dirstate.invalidate()
2190 repo.dirstate.invalidate()
2184 lockmod.release(lock, wlock)
2191 lockmod.release(lock, wlock)
2185 return newid
2192 return newid
2186
2193
2187 def commiteditor(repo, ctx, subs, editform=''):
2194 def commiteditor(repo, ctx, subs, editform=''):
2188 if ctx.description():
2195 if ctx.description():
2189 return ctx.description()
2196 return ctx.description()
2190 return commitforceeditor(repo, ctx, subs, editform=editform)
2197 return commitforceeditor(repo, ctx, subs, editform=editform)
2191
2198
2192 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2199 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2193 editform=''):
2200 editform=''):
2194 if not extramsg:
2201 if not extramsg:
2195 extramsg = _("Leave message empty to abort commit.")
2202 extramsg = _("Leave message empty to abort commit.")
2196
2203
2197 forms = [e for e in editform.split('.') if e]
2204 forms = [e for e in editform.split('.') if e]
2198 forms.insert(0, 'changeset')
2205 forms.insert(0, 'changeset')
2199 while forms:
2206 while forms:
2200 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2207 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2201 if tmpl:
2208 if tmpl:
2202 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2209 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2203 break
2210 break
2204 forms.pop()
2211 forms.pop()
2205 else:
2212 else:
2206 committext = buildcommittext(repo, ctx, subs, extramsg)
2213 committext = buildcommittext(repo, ctx, subs, extramsg)
2207
2214
2208 # run editor in the repository root
2215 # run editor in the repository root
2209 olddir = os.getcwd()
2216 olddir = os.getcwd()
2210 os.chdir(repo.root)
2217 os.chdir(repo.root)
2211 text = repo.ui.edit(committext, ctx.user(), ctx.extra())
2218 text = repo.ui.edit(committext, ctx.user(), ctx.extra())
2212 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2219 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2213 os.chdir(olddir)
2220 os.chdir(olddir)
2214
2221
2215 if finishdesc:
2222 if finishdesc:
2216 text = finishdesc(text)
2223 text = finishdesc(text)
2217 if not text.strip():
2224 if not text.strip():
2218 raise util.Abort(_("empty commit message"))
2225 raise util.Abort(_("empty commit message"))
2219
2226
2220 return text
2227 return text
2221
2228
2222 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2229 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2223 ui = repo.ui
2230 ui = repo.ui
2224 tmpl, mapfile = gettemplate(ui, tmpl, None)
2231 tmpl, mapfile = gettemplate(ui, tmpl, None)
2225
2232
2226 try:
2233 try:
2227 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2234 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2228 except SyntaxError, inst:
2235 except SyntaxError, inst:
2229 raise util.Abort(inst.args[0])
2236 raise util.Abort(inst.args[0])
2230
2237
2231 for k, v in repo.ui.configitems('committemplate'):
2238 for k, v in repo.ui.configitems('committemplate'):
2232 if k != 'changeset':
2239 if k != 'changeset':
2233 t.t.cache[k] = v
2240 t.t.cache[k] = v
2234
2241
2235 if not extramsg:
2242 if not extramsg:
2236 extramsg = '' # ensure that extramsg is string
2243 extramsg = '' # ensure that extramsg is string
2237
2244
2238 ui.pushbuffer()
2245 ui.pushbuffer()
2239 t.show(ctx, extramsg=extramsg)
2246 t.show(ctx, extramsg=extramsg)
2240 return ui.popbuffer()
2247 return ui.popbuffer()
2241
2248
2242 def buildcommittext(repo, ctx, subs, extramsg):
2249 def buildcommittext(repo, ctx, subs, extramsg):
2243 edittext = []
2250 edittext = []
2244 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2251 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2245 if ctx.description():
2252 if ctx.description():
2246 edittext.append(ctx.description())
2253 edittext.append(ctx.description())
2247 edittext.append("")
2254 edittext.append("")
2248 edittext.append("") # Empty line between message and comments.
2255 edittext.append("") # Empty line between message and comments.
2249 edittext.append(_("HG: Enter commit message."
2256 edittext.append(_("HG: Enter commit message."
2250 " Lines beginning with 'HG:' are removed."))
2257 " Lines beginning with 'HG:' are removed."))
2251 edittext.append("HG: %s" % extramsg)
2258 edittext.append("HG: %s" % extramsg)
2252 edittext.append("HG: --")
2259 edittext.append("HG: --")
2253 edittext.append(_("HG: user: %s") % ctx.user())
2260 edittext.append(_("HG: user: %s") % ctx.user())
2254 if ctx.p2():
2261 if ctx.p2():
2255 edittext.append(_("HG: branch merge"))
2262 edittext.append(_("HG: branch merge"))
2256 if ctx.branch():
2263 if ctx.branch():
2257 edittext.append(_("HG: branch '%s'") % ctx.branch())
2264 edittext.append(_("HG: branch '%s'") % ctx.branch())
2258 if bookmarks.iscurrent(repo):
2265 if bookmarks.iscurrent(repo):
2259 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2266 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2260 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2267 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2261 edittext.extend([_("HG: added %s") % f for f in added])
2268 edittext.extend([_("HG: added %s") % f for f in added])
2262 edittext.extend([_("HG: changed %s") % f for f in modified])
2269 edittext.extend([_("HG: changed %s") % f for f in modified])
2263 edittext.extend([_("HG: removed %s") % f for f in removed])
2270 edittext.extend([_("HG: removed %s") % f for f in removed])
2264 if not added and not modified and not removed:
2271 if not added and not modified and not removed:
2265 edittext.append(_("HG: no files changed"))
2272 edittext.append(_("HG: no files changed"))
2266 edittext.append("")
2273 edittext.append("")
2267
2274
2268 return "\n".join(edittext)
2275 return "\n".join(edittext)
2269
2276
2270 def commitstatus(repo, node, branch, bheads=None, opts={}):
2277 def commitstatus(repo, node, branch, bheads=None, opts={}):
2271 ctx = repo[node]
2278 ctx = repo[node]
2272 parents = ctx.parents()
2279 parents = ctx.parents()
2273
2280
2274 if (not opts.get('amend') and bheads and node not in bheads and not
2281 if (not opts.get('amend') and bheads and node not in bheads and not
2275 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2282 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2276 repo.ui.status(_('created new head\n'))
2283 repo.ui.status(_('created new head\n'))
2277 # The message is not printed for initial roots. For the other
2284 # The message is not printed for initial roots. For the other
2278 # changesets, it is printed in the following situations:
2285 # changesets, it is printed in the following situations:
2279 #
2286 #
2280 # Par column: for the 2 parents with ...
2287 # Par column: for the 2 parents with ...
2281 # N: null or no parent
2288 # N: null or no parent
2282 # B: parent is on another named branch
2289 # B: parent is on another named branch
2283 # C: parent is a regular non head changeset
2290 # C: parent is a regular non head changeset
2284 # H: parent was a branch head of the current branch
2291 # H: parent was a branch head of the current branch
2285 # Msg column: whether we print "created new head" message
2292 # Msg column: whether we print "created new head" message
2286 # In the following, it is assumed that there already exists some
2293 # In the following, it is assumed that there already exists some
2287 # initial branch heads of the current branch, otherwise nothing is
2294 # initial branch heads of the current branch, otherwise nothing is
2288 # printed anyway.
2295 # printed anyway.
2289 #
2296 #
2290 # Par Msg Comment
2297 # Par Msg Comment
2291 # N N y additional topo root
2298 # N N y additional topo root
2292 #
2299 #
2293 # B N y additional branch root
2300 # B N y additional branch root
2294 # C N y additional topo head
2301 # C N y additional topo head
2295 # H N n usual case
2302 # H N n usual case
2296 #
2303 #
2297 # B B y weird additional branch root
2304 # B B y weird additional branch root
2298 # C B y branch merge
2305 # C B y branch merge
2299 # H B n merge with named branch
2306 # H B n merge with named branch
2300 #
2307 #
2301 # C C y additional head from merge
2308 # C C y additional head from merge
2302 # C H n merge with a head
2309 # C H n merge with a head
2303 #
2310 #
2304 # H H n head merge: head count decreases
2311 # H H n head merge: head count decreases
2305
2312
2306 if not opts.get('close_branch'):
2313 if not opts.get('close_branch'):
2307 for r in parents:
2314 for r in parents:
2308 if r.closesbranch() and r.branch() == branch:
2315 if r.closesbranch() and r.branch() == branch:
2309 repo.ui.status(_('reopening closed branch head %d\n') % r)
2316 repo.ui.status(_('reopening closed branch head %d\n') % r)
2310
2317
2311 if repo.ui.debugflag:
2318 if repo.ui.debugflag:
2312 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2319 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2313 elif repo.ui.verbose:
2320 elif repo.ui.verbose:
2314 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2321 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2315
2322
2316 def revert(ui, repo, ctx, parents, *pats, **opts):
2323 def revert(ui, repo, ctx, parents, *pats, **opts):
2317 parent, p2 = parents
2324 parent, p2 = parents
2318 node = ctx.node()
2325 node = ctx.node()
2319
2326
2320 mf = ctx.manifest()
2327 mf = ctx.manifest()
2321 if node == p2:
2328 if node == p2:
2322 parent = p2
2329 parent = p2
2323 if node == parent:
2330 if node == parent:
2324 pmf = mf
2331 pmf = mf
2325 else:
2332 else:
2326 pmf = None
2333 pmf = None
2327
2334
2328 # need all matching names in dirstate and manifest of target rev,
2335 # need all matching names in dirstate and manifest of target rev,
2329 # so have to walk both. do not print errors if files exist in one
2336 # so have to walk both. do not print errors if files exist in one
2330 # but not other.
2337 # but not other.
2331
2338
2332 # `names` is a mapping for all elements in working copy and target revision
2339 # `names` is a mapping for all elements in working copy and target revision
2333 # The mapping is in the form:
2340 # The mapping is in the form:
2334 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2341 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2335 names = {}
2342 names = {}
2336
2343
2337 wlock = repo.wlock()
2344 wlock = repo.wlock()
2338 try:
2345 try:
2339 ## filling of the `names` mapping
2346 ## filling of the `names` mapping
2340 # walk dirstate to fill `names`
2347 # walk dirstate to fill `names`
2341
2348
2342 m = scmutil.match(repo[None], pats, opts)
2349 m = scmutil.match(repo[None], pats, opts)
2343 m.bad = lambda x, y: False
2350 m.bad = lambda x, y: False
2344 for abs in repo.walk(m):
2351 for abs in repo.walk(m):
2345 names[abs] = m.rel(abs), m.exact(abs)
2352 names[abs] = m.rel(abs), m.exact(abs)
2346
2353
2347 # walk target manifest to fill `names`
2354 # walk target manifest to fill `names`
2348
2355
2349 def badfn(path, msg):
2356 def badfn(path, msg):
2350 if path in names:
2357 if path in names:
2351 return
2358 return
2352 if path in ctx.substate:
2359 if path in ctx.substate:
2353 return
2360 return
2354 path_ = path + '/'
2361 path_ = path + '/'
2355 for f in names:
2362 for f in names:
2356 if f.startswith(path_):
2363 if f.startswith(path_):
2357 return
2364 return
2358 ui.warn("%s: %s\n" % (m.rel(path), msg))
2365 ui.warn("%s: %s\n" % (m.rel(path), msg))
2359
2366
2360 m = scmutil.match(ctx, pats, opts)
2367 m = scmutil.match(ctx, pats, opts)
2361 m.bad = badfn
2368 m.bad = badfn
2362 for abs in ctx.walk(m):
2369 for abs in ctx.walk(m):
2363 if abs not in names:
2370 if abs not in names:
2364 names[abs] = m.rel(abs), m.exact(abs)
2371 names[abs] = m.rel(abs), m.exact(abs)
2365
2372
2366 # get the list of subrepos that must be reverted
2373 # get the list of subrepos that must be reverted
2367 targetsubs = sorted(s for s in ctx.substate if m(s))
2374 targetsubs = sorted(s for s in ctx.substate if m(s))
2368
2375
2369 # Find status of all file in `names`.
2376 # Find status of all file in `names`.
2370 m = scmutil.matchfiles(repo, names)
2377 m = scmutil.matchfiles(repo, names)
2371
2378
2372 changes = repo.status(node1=node, match=m, clean=True)
2379 changes = repo.status(node1=node, match=m, clean=True)
2373 modified = set(changes[0])
2380 modified = set(changes[0])
2374 added = set(changes[1])
2381 added = set(changes[1])
2375 removed = set(changes[2])
2382 removed = set(changes[2])
2376 deleted = set(changes[3])
2383 deleted = set(changes[3])
2377
2384
2378 # We need to account for the state of file in the dirstate
2385 # We need to account for the state of file in the dirstate
2379 #
2386 #
2380 # Even, when we revert agains something else than parent. this will
2387 # Even, when we revert agains something else than parent. this will
2381 # slightly alter the behavior of revert (doing back up or not, delete
2388 # slightly alter the behavior of revert (doing back up or not, delete
2382 # or just forget etc)
2389 # or just forget etc)
2383 if parent == node:
2390 if parent == node:
2384 dsmodified = modified
2391 dsmodified = modified
2385 dsadded = added
2392 dsadded = added
2386 dsremoved = removed
2393 dsremoved = removed
2387 modified, added, removed = set(), set(), set()
2394 modified, added, removed = set(), set(), set()
2388 else:
2395 else:
2389 changes = repo.status(node1=parent, match=m)
2396 changes = repo.status(node1=parent, match=m)
2390 dsmodified = set(changes[0])
2397 dsmodified = set(changes[0])
2391 dsadded = set(changes[1])
2398 dsadded = set(changes[1])
2392 dsremoved = set(changes[2])
2399 dsremoved = set(changes[2])
2393
2400
2394 # if f is a rename, update `names` to also revert the source
2401 # if f is a rename, update `names` to also revert the source
2395 cwd = repo.getcwd()
2402 cwd = repo.getcwd()
2396 for f in dsadded:
2403 for f in dsadded:
2397 src = repo.dirstate.copied(f)
2404 src = repo.dirstate.copied(f)
2398 if src and src not in names and repo.dirstate[src] == 'r':
2405 if src and src not in names and repo.dirstate[src] == 'r':
2399 dsremoved.add(src)
2406 dsremoved.add(src)
2400 names[src] = (repo.pathto(src, cwd), True)
2407 names[src] = (repo.pathto(src, cwd), True)
2401
2408
2402 ## computation of the action to performs on `names` content.
2409 ## computation of the action to performs on `names` content.
2403
2410
2404 def removeforget(abs):
2411 def removeforget(abs):
2405 if repo.dirstate[abs] == 'a':
2412 if repo.dirstate[abs] == 'a':
2406 return _('forgetting %s\n')
2413 return _('forgetting %s\n')
2407 return _('removing %s\n')
2414 return _('removing %s\n')
2408
2415
2409 # split between files known in target manifest and the others
2416 # split between files known in target manifest and the others
2410 smf = set(mf)
2417 smf = set(mf)
2411
2418
2412 missingmodified = dsmodified - smf
2419 missingmodified = dsmodified - smf
2413 dsmodified -= missingmodified
2420 dsmodified -= missingmodified
2414 missingadded = dsadded - smf
2421 missingadded = dsadded - smf
2415 dsadded -= missingadded
2422 dsadded -= missingadded
2416 missingremoved = dsremoved - smf
2423 missingremoved = dsremoved - smf
2417 dsremoved -= missingremoved
2424 dsremoved -= missingremoved
2418 missingdeleted = deleted - smf
2425 missingdeleted = deleted - smf
2419 deleted -= missingdeleted
2426 deleted -= missingdeleted
2420
2427
2421 # action to be actually performed by revert
2428 # action to be actually performed by revert
2422 # (<list of file>, message>) tuple
2429 # (<list of file>, message>) tuple
2423 actions = {'revert': ([], _('reverting %s\n')),
2430 actions = {'revert': ([], _('reverting %s\n')),
2424 'add': ([], _('adding %s\n')),
2431 'add': ([], _('adding %s\n')),
2425 'remove': ([], removeforget),
2432 'remove': ([], removeforget),
2426 'undelete': ([], _('undeleting %s\n'))}
2433 'undelete': ([], _('undeleting %s\n'))}
2427
2434
2428 disptable = (
2435 disptable = (
2429 # dispatch table:
2436 # dispatch table:
2430 # file state
2437 # file state
2431 # action
2438 # action
2432 # make backup
2439 # make backup
2433 (dsmodified, (actions['revert'], True)),
2440 (dsmodified, (actions['revert'], True)),
2434 (missingmodified, (actions['remove'], True)),
2441 (missingmodified, (actions['remove'], True)),
2435 (dsadded, (actions['revert'], True)),
2442 (dsadded, (actions['revert'], True)),
2436 (missingadded, (actions['remove'], False)),
2443 (missingadded, (actions['remove'], False)),
2437 (dsremoved, (actions['undelete'], True)),
2444 (dsremoved, (actions['undelete'], True)),
2438 (missingremoved, (None, False)),
2445 (missingremoved, (None, False)),
2439 (deleted, (actions['revert'], False)),
2446 (deleted, (actions['revert'], False)),
2440 (missingdeleted, (actions['remove'], False)),
2447 (missingdeleted, (actions['remove'], False)),
2441 )
2448 )
2442
2449
2443 for abs, (rel, exact) in sorted(names.items()):
2450 for abs, (rel, exact) in sorted(names.items()):
2444 # hash on file in target manifest (or None if missing from target)
2451 # hash on file in target manifest (or None if missing from target)
2445 mfentry = mf.get(abs)
2452 mfentry = mf.get(abs)
2446 # target file to be touch on disk (relative to cwd)
2453 # target file to be touch on disk (relative to cwd)
2447 target = repo.wjoin(abs)
2454 target = repo.wjoin(abs)
2448 def handle(xlist, dobackup):
2455 def handle(xlist, dobackup):
2449 xlist[0].append(abs)
2456 xlist[0].append(abs)
2450 if (dobackup and not opts.get('no_backup') and
2457 if (dobackup and not opts.get('no_backup') and
2451 os.path.lexists(target) and
2458 os.path.lexists(target) and
2452 abs in ctx and repo[None][abs].cmp(ctx[abs])):
2459 abs in ctx and repo[None][abs].cmp(ctx[abs])):
2453 bakname = "%s.orig" % rel
2460 bakname = "%s.orig" % rel
2454 ui.note(_('saving current version of %s as %s\n') %
2461 ui.note(_('saving current version of %s as %s\n') %
2455 (rel, bakname))
2462 (rel, bakname))
2456 if not opts.get('dry_run'):
2463 if not opts.get('dry_run'):
2457 util.rename(target, bakname)
2464 util.rename(target, bakname)
2458 if ui.verbose or not exact:
2465 if ui.verbose or not exact:
2459 msg = xlist[1]
2466 msg = xlist[1]
2460 if not isinstance(msg, basestring):
2467 if not isinstance(msg, basestring):
2461 msg = msg(abs)
2468 msg = msg(abs)
2462 ui.status(msg % rel)
2469 ui.status(msg % rel)
2463 # search the entry in the dispatch table.
2470 # search the entry in the dispatch table.
2464 # if the file is in any of this sets, it was touched in the working
2471 # if the file is in any of this sets, it was touched in the working
2465 # directory parent and we are sure it needs to be reverted.
2472 # directory parent and we are sure it needs to be reverted.
2466 for table, (action, backup) in disptable:
2473 for table, (action, backup) in disptable:
2467 if abs not in table:
2474 if abs not in table:
2468 continue
2475 continue
2469 if action is None:
2476 if action is None:
2470 if exact:
2477 if exact:
2471 ui.warn(_('no changes needed to %s\n') % rel)
2478 ui.warn(_('no changes needed to %s\n') % rel)
2472
2479
2473 else:
2480 else:
2474 handle(action, backup)
2481 handle(action, backup)
2475 break
2482 break
2476 else:
2483 else:
2477 # Not touched in current dirstate.
2484 # Not touched in current dirstate.
2478
2485
2479 # file is unknown in parent, restore older version or ignore.
2486 # file is unknown in parent, restore older version or ignore.
2480 if abs not in repo.dirstate:
2487 if abs not in repo.dirstate:
2481 if mfentry:
2488 if mfentry:
2482 handle(actions['add'], True)
2489 handle(actions['add'], True)
2483 elif exact:
2490 elif exact:
2484 ui.warn(_('file not managed: %s\n') % rel)
2491 ui.warn(_('file not managed: %s\n') % rel)
2485 continue
2492 continue
2486
2493
2487 # parent is target, no changes mean no changes
2494 # parent is target, no changes mean no changes
2488 if node == parent:
2495 if node == parent:
2489 if exact:
2496 if exact:
2490 ui.warn(_('no changes needed to %s\n') % rel)
2497 ui.warn(_('no changes needed to %s\n') % rel)
2491 continue
2498 continue
2492 # no change in dirstate but parent and target may differ
2499 # no change in dirstate but parent and target may differ
2493 if pmf is None:
2500 if pmf is None:
2494 # only need parent manifest in this unlikely case,
2501 # only need parent manifest in this unlikely case,
2495 # so do not read by default
2502 # so do not read by default
2496 pmf = repo[parent].manifest()
2503 pmf = repo[parent].manifest()
2497 if abs in pmf and mfentry:
2504 if abs in pmf and mfentry:
2498 # if version of file is same in parent and target
2505 # if version of file is same in parent and target
2499 # manifests, do nothing
2506 # manifests, do nothing
2500 if (pmf[abs] != mfentry or
2507 if (pmf[abs] != mfentry or
2501 pmf.flags(abs) != mf.flags(abs)):
2508 pmf.flags(abs) != mf.flags(abs)):
2502 handle(actions['revert'], False)
2509 handle(actions['revert'], False)
2503 else:
2510 else:
2504 handle(actions['remove'], False)
2511 handle(actions['remove'], False)
2505
2512
2506 if not opts.get('dry_run'):
2513 if not opts.get('dry_run'):
2507 _performrevert(repo, parents, ctx, actions)
2514 _performrevert(repo, parents, ctx, actions)
2508
2515
2509 if targetsubs:
2516 if targetsubs:
2510 # Revert the subrepos on the revert list
2517 # Revert the subrepos on the revert list
2511 for sub in targetsubs:
2518 for sub in targetsubs:
2512 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2519 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2513 finally:
2520 finally:
2514 wlock.release()
2521 wlock.release()
2515
2522
2516 def _performrevert(repo, parents, ctx, actions):
2523 def _performrevert(repo, parents, ctx, actions):
2517 """function that actually perform all the actions computed for revert
2524 """function that actually perform all the actions computed for revert
2518
2525
2519 This is an independent function to let extension to plug in and react to
2526 This is an independent function to let extension to plug in and react to
2520 the imminent revert.
2527 the imminent revert.
2521
2528
2522 Make sure you have the working directory locked when calling this function.
2529 Make sure you have the working directory locked when calling this function.
2523 """
2530 """
2524 parent, p2 = parents
2531 parent, p2 = parents
2525 node = ctx.node()
2532 node = ctx.node()
2526 def checkout(f):
2533 def checkout(f):
2527 fc = ctx[f]
2534 fc = ctx[f]
2528 repo.wwrite(f, fc.data(), fc.flags())
2535 repo.wwrite(f, fc.data(), fc.flags())
2529
2536
2530 audit_path = pathutil.pathauditor(repo.root)
2537 audit_path = pathutil.pathauditor(repo.root)
2531 for f in actions['remove'][0]:
2538 for f in actions['remove'][0]:
2532 if repo.dirstate[f] == 'a':
2539 if repo.dirstate[f] == 'a':
2533 repo.dirstate.drop(f)
2540 repo.dirstate.drop(f)
2534 continue
2541 continue
2535 audit_path(f)
2542 audit_path(f)
2536 try:
2543 try:
2537 util.unlinkpath(repo.wjoin(f))
2544 util.unlinkpath(repo.wjoin(f))
2538 except OSError:
2545 except OSError:
2539 pass
2546 pass
2540 repo.dirstate.remove(f)
2547 repo.dirstate.remove(f)
2541
2548
2542 normal = None
2549 normal = None
2543 if node == parent:
2550 if node == parent:
2544 # We're reverting to our parent. If possible, we'd like status
2551 # We're reverting to our parent. If possible, we'd like status
2545 # to report the file as clean. We have to use normallookup for
2552 # to report the file as clean. We have to use normallookup for
2546 # merges to avoid losing information about merged/dirty files.
2553 # merges to avoid losing information about merged/dirty files.
2547 if p2 != nullid:
2554 if p2 != nullid:
2548 normal = repo.dirstate.normallookup
2555 normal = repo.dirstate.normallookup
2549 else:
2556 else:
2550 normal = repo.dirstate.normal
2557 normal = repo.dirstate.normal
2551 for f in actions['revert'][0]:
2558 for f in actions['revert'][0]:
2552 checkout(f)
2559 checkout(f)
2553 if normal:
2560 if normal:
2554 normal(f)
2561 normal(f)
2555
2562
2556 for f in actions['add'][0]:
2563 for f in actions['add'][0]:
2557 checkout(f)
2564 checkout(f)
2558 repo.dirstate.add(f)
2565 repo.dirstate.add(f)
2559
2566
2560 normal = repo.dirstate.normallookup
2567 normal = repo.dirstate.normallookup
2561 if node == parent and p2 == nullid:
2568 if node == parent and p2 == nullid:
2562 normal = repo.dirstate.normal
2569 normal = repo.dirstate.normal
2563 for f in actions['undelete'][0]:
2570 for f in actions['undelete'][0]:
2564 checkout(f)
2571 checkout(f)
2565 normal(f)
2572 normal(f)
2566
2573
2567 copied = copies.pathcopies(repo[parent], ctx)
2574 copied = copies.pathcopies(repo[parent], ctx)
2568
2575
2569 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
2576 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
2570 if f in copied:
2577 if f in copied:
2571 repo.dirstate.copy(copied[f], f)
2578 repo.dirstate.copy(copied[f], f)
2572
2579
2573 def command(table):
2580 def command(table):
2574 """Returns a function object to be used as a decorator for making commands.
2581 """Returns a function object to be used as a decorator for making commands.
2575
2582
2576 This function receives a command table as its argument. The table should
2583 This function receives a command table as its argument. The table should
2577 be a dict.
2584 be a dict.
2578
2585
2579 The returned function can be used as a decorator for adding commands
2586 The returned function can be used as a decorator for adding commands
2580 to that command table. This function accepts multiple arguments to define
2587 to that command table. This function accepts multiple arguments to define
2581 a command.
2588 a command.
2582
2589
2583 The first argument is the command name.
2590 The first argument is the command name.
2584
2591
2585 The options argument is an iterable of tuples defining command arguments.
2592 The options argument is an iterable of tuples defining command arguments.
2586 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
2593 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
2587
2594
2588 The synopsis argument defines a short, one line summary of how to use the
2595 The synopsis argument defines a short, one line summary of how to use the
2589 command. This shows up in the help output.
2596 command. This shows up in the help output.
2590
2597
2591 The norepo argument defines whether the command does not require a
2598 The norepo argument defines whether the command does not require a
2592 local repository. Most commands operate against a repository, thus the
2599 local repository. Most commands operate against a repository, thus the
2593 default is False.
2600 default is False.
2594
2601
2595 The optionalrepo argument defines whether the command optionally requires
2602 The optionalrepo argument defines whether the command optionally requires
2596 a local repository.
2603 a local repository.
2597
2604
2598 The inferrepo argument defines whether to try to find a repository from the
2605 The inferrepo argument defines whether to try to find a repository from the
2599 command line arguments. If True, arguments will be examined for potential
2606 command line arguments. If True, arguments will be examined for potential
2600 repository locations. See ``findrepo()``. If a repository is found, it
2607 repository locations. See ``findrepo()``. If a repository is found, it
2601 will be used.
2608 will be used.
2602 """
2609 """
2603 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
2610 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
2604 inferrepo=False):
2611 inferrepo=False):
2605 def decorator(func):
2612 def decorator(func):
2606 if synopsis:
2613 if synopsis:
2607 table[name] = func, list(options), synopsis
2614 table[name] = func, list(options), synopsis
2608 else:
2615 else:
2609 table[name] = func, list(options)
2616 table[name] = func, list(options)
2610
2617
2611 if norepo:
2618 if norepo:
2612 # Avoid import cycle.
2619 # Avoid import cycle.
2613 import commands
2620 import commands
2614 commands.norepo += ' %s' % ' '.join(parsealiases(name))
2621 commands.norepo += ' %s' % ' '.join(parsealiases(name))
2615
2622
2616 if optionalrepo:
2623 if optionalrepo:
2617 import commands
2624 import commands
2618 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
2625 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
2619
2626
2620 if inferrepo:
2627 if inferrepo:
2621 import commands
2628 import commands
2622 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
2629 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
2623
2630
2624 return func
2631 return func
2625 return decorator
2632 return decorator
2626
2633
2627 return cmd
2634 return cmd
2628
2635
2629 # a list of (ui, repo, otherpeer, opts, missing) functions called by
2636 # a list of (ui, repo, otherpeer, opts, missing) functions called by
2630 # commands.outgoing. "missing" is "missing" of the result of
2637 # commands.outgoing. "missing" is "missing" of the result of
2631 # "findcommonoutgoing()"
2638 # "findcommonoutgoing()"
2632 outgoinghooks = util.hooks()
2639 outgoinghooks = util.hooks()
2633
2640
2634 # a list of (ui, repo) functions called by commands.summary
2641 # a list of (ui, repo) functions called by commands.summary
2635 summaryhooks = util.hooks()
2642 summaryhooks = util.hooks()
2636
2643
2637 # a list of (ui, repo, opts, changes) functions called by commands.summary.
2644 # a list of (ui, repo, opts, changes) functions called by commands.summary.
2638 #
2645 #
2639 # functions should return tuple of booleans below, if 'changes' is None:
2646 # functions should return tuple of booleans below, if 'changes' is None:
2640 # (whether-incomings-are-needed, whether-outgoings-are-needed)
2647 # (whether-incomings-are-needed, whether-outgoings-are-needed)
2641 #
2648 #
2642 # otherwise, 'changes' is a tuple of tuples below:
2649 # otherwise, 'changes' is a tuple of tuples below:
2643 # - (sourceurl, sourcebranch, sourcepeer, incoming)
2650 # - (sourceurl, sourcebranch, sourcepeer, incoming)
2644 # - (desturl, destbranch, destpeer, outgoing)
2651 # - (desturl, destbranch, destpeer, outgoing)
2645 summaryremotehooks = util.hooks()
2652 summaryremotehooks = util.hooks()
2646
2653
2647 # A list of state files kept by multistep operations like graft.
2654 # A list of state files kept by multistep operations like graft.
2648 # Since graft cannot be aborted, it is considered 'clearable' by update.
2655 # Since graft cannot be aborted, it is considered 'clearable' by update.
2649 # note: bisect is intentionally excluded
2656 # note: bisect is intentionally excluded
2650 # (state file, clearable, allowcommit, error, hint)
2657 # (state file, clearable, allowcommit, error, hint)
2651 unfinishedstates = [
2658 unfinishedstates = [
2652 ('graftstate', True, False, _('graft in progress'),
2659 ('graftstate', True, False, _('graft in progress'),
2653 _("use 'hg graft --continue' or 'hg update' to abort")),
2660 _("use 'hg graft --continue' or 'hg update' to abort")),
2654 ('updatestate', True, False, _('last update was interrupted'),
2661 ('updatestate', True, False, _('last update was interrupted'),
2655 _("use 'hg update' to get a consistent checkout"))
2662 _("use 'hg update' to get a consistent checkout"))
2656 ]
2663 ]
2657
2664
2658 def checkunfinished(repo, commit=False):
2665 def checkunfinished(repo, commit=False):
2659 '''Look for an unfinished multistep operation, like graft, and abort
2666 '''Look for an unfinished multistep operation, like graft, and abort
2660 if found. It's probably good to check this right before
2667 if found. It's probably good to check this right before
2661 bailifchanged().
2668 bailifchanged().
2662 '''
2669 '''
2663 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2670 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2664 if commit and allowcommit:
2671 if commit and allowcommit:
2665 continue
2672 continue
2666 if repo.vfs.exists(f):
2673 if repo.vfs.exists(f):
2667 raise util.Abort(msg, hint=hint)
2674 raise util.Abort(msg, hint=hint)
2668
2675
2669 def clearunfinished(repo):
2676 def clearunfinished(repo):
2670 '''Check for unfinished operations (as above), and clear the ones
2677 '''Check for unfinished operations (as above), and clear the ones
2671 that are clearable.
2678 that are clearable.
2672 '''
2679 '''
2673 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2680 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2674 if not clearable and repo.vfs.exists(f):
2681 if not clearable and repo.vfs.exists(f):
2675 raise util.Abort(msg, hint=hint)
2682 raise util.Abort(msg, hint=hint)
2676 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2683 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2677 if clearable and repo.vfs.exists(f):
2684 if clearable and repo.vfs.exists(f):
2678 util.unlink(repo.join(f))
2685 util.unlink(repo.join(f))
@@ -1,1634 +1,1825 b''
1 This file used to contains all largefile tests.
1 This file used to contains all largefile tests.
2 Do not add any new tests in this file as it his already far too long to run.
2 Do not add any new tests in this file as it his already far too long to run.
3
3
4 It contains all the testing of the basic concepts of large file in a single block.
4 It contains all the testing of the basic concepts of large file in a single block.
5
5
6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 $ mkdir "${USERCACHE}"
7 $ mkdir "${USERCACHE}"
8 $ cat >> $HGRCPATH <<EOF
8 $ cat >> $HGRCPATH <<EOF
9 > [extensions]
9 > [extensions]
10 > largefiles=
10 > largefiles=
11 > purge=
11 > purge=
12 > rebase=
12 > rebase=
13 > transplant=
13 > transplant=
14 > [phases]
14 > [phases]
15 > publish=False
15 > publish=False
16 > [largefiles]
16 > [largefiles]
17 > minsize=2
17 > minsize=2
18 > patterns=glob:**.dat
18 > patterns=glob:**.dat
19 > usercache=${USERCACHE}
19 > usercache=${USERCACHE}
20 > [hooks]
20 > [hooks]
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 > EOF
22 > EOF
23
23
24 Create the repo with a couple of revisions of both large and normal
24 Create the repo with a couple of revisions of both large and normal
25 files.
25 files.
26 Test status and dirstate of largefiles and that summary output is correct.
26 Test status and dirstate of largefiles and that summary output is correct.
27
27
28 $ hg init a
28 $ hg init a
29 $ cd a
29 $ cd a
30 $ mkdir sub
30 $ mkdir sub
31 $ echo normal1 > normal1
31 $ echo normal1 > normal1
32 $ echo normal2 > sub/normal2
32 $ echo normal2 > sub/normal2
33 $ echo large1 > large1
33 $ echo large1 > large1
34 $ echo large2 > sub/large2
34 $ echo large2 > sub/large2
35 $ hg add normal1 sub/normal2
35 $ hg add normal1 sub/normal2
36 $ hg add --large large1 sub/large2
36 $ hg add --large large1 sub/large2
37 $ hg commit -m "add files"
37 $ hg commit -m "add files"
38 Invoking status precommit hook
38 Invoking status precommit hook
39 A large1
39 A large1
40 A normal1
40 A normal1
41 A sub/large2
41 A sub/large2
42 A sub/normal2
42 A sub/normal2
43 $ touch large1 sub/large2
43 $ touch large1 sub/large2
44 $ sleep 1
44 $ sleep 1
45 $ hg st
45 $ hg st
46 $ hg debugstate --nodates
46 $ hg debugstate --nodates
47 n 644 41 .hglf/large1
47 n 644 41 .hglf/large1
48 n 644 41 .hglf/sub/large2
48 n 644 41 .hglf/sub/large2
49 n 644 8 normal1
49 n 644 8 normal1
50 n 644 8 sub/normal2
50 n 644 8 sub/normal2
51 $ hg debugstate --large --nodates
51 $ hg debugstate --large --nodates
52 n 644 7 large1
52 n 644 7 large1
53 n 644 7 sub/large2
53 n 644 7 sub/large2
54 $ echo normal11 > normal1
54 $ echo normal11 > normal1
55 $ echo normal22 > sub/normal2
55 $ echo normal22 > sub/normal2
56 $ echo large11 > large1
56 $ echo large11 > large1
57 $ echo large22 > sub/large2
57 $ echo large22 > sub/large2
58 $ hg commit -m "edit files"
58 $ hg commit -m "edit files"
59 Invoking status precommit hook
59 Invoking status precommit hook
60 M large1
60 M large1
61 M normal1
61 M normal1
62 M sub/large2
62 M sub/large2
63 M sub/normal2
63 M sub/normal2
64 $ hg sum --large
64 $ hg sum --large
65 parent: 1:ce8896473775 tip
65 parent: 1:ce8896473775 tip
66 edit files
66 edit files
67 branch: default
67 branch: default
68 commit: (clean)
68 commit: (clean)
69 update: (current)
69 update: (current)
70 largefiles: (no remote repo)
70 largefiles: (no remote repo)
71
71
72 Commit preserved largefile contents.
72 Commit preserved largefile contents.
73
73
74 $ cat normal1
74 $ cat normal1
75 normal11
75 normal11
76 $ cat large1
76 $ cat large1
77 large11
77 large11
78 $ cat sub/normal2
78 $ cat sub/normal2
79 normal22
79 normal22
80 $ cat sub/large2
80 $ cat sub/large2
81 large22
81 large22
82
82
83 Test status, subdir and unknown files
83 Test status, subdir and unknown files
84
84
85 $ echo unknown > sub/unknown
85 $ echo unknown > sub/unknown
86 $ hg st --all
86 $ hg st --all
87 ? sub/unknown
87 ? sub/unknown
88 C large1
88 C large1
89 C normal1
89 C normal1
90 C sub/large2
90 C sub/large2
91 C sub/normal2
91 C sub/normal2
92 $ hg st --all sub
92 $ hg st --all sub
93 ? sub/unknown
93 ? sub/unknown
94 C sub/large2
94 C sub/large2
95 C sub/normal2
95 C sub/normal2
96 $ rm sub/unknown
96 $ rm sub/unknown
97
97
98 Test messages and exit codes for remove warning cases
98 Test messages and exit codes for remove warning cases
99
99
100 $ hg remove -A large1
100 $ hg remove -A large1
101 not removing large1: file still exists
101 not removing large1: file still exists
102 [1]
102 [1]
103 $ echo 'modified' > large1
103 $ echo 'modified' > large1
104 $ hg remove large1
104 $ hg remove large1
105 not removing large1: file is modified (use -f to force removal)
105 not removing large1: file is modified (use -f to force removal)
106 [1]
106 [1]
107 $ echo 'new' > normalnew
107 $ echo 'new' > normalnew
108 $ hg add normalnew
108 $ hg add normalnew
109 $ echo 'new' > largenew
109 $ echo 'new' > largenew
110 $ hg add --large normalnew
110 $ hg add --large normalnew
111 normalnew already tracked!
111 normalnew already tracked!
112 $ hg remove normalnew largenew
112 $ hg remove normalnew largenew
113 not removing largenew: file is untracked
113 not removing largenew: file is untracked
114 not removing normalnew: file has been marked for add (use forget to undo)
114 not removing normalnew: file has been marked for add (use forget to undo)
115 [1]
115 [1]
116 $ rm normalnew largenew
116 $ rm normalnew largenew
117 $ hg up -Cq
117 $ hg up -Cq
118
118
119 Remove both largefiles and normal files.
119 Remove both largefiles and normal files.
120
120
121 $ hg remove normal1 large1
121 $ hg remove normal1 large1
122 $ hg status large1
122 $ hg status large1
123 R large1
123 R large1
124 $ hg commit -m "remove files"
124 $ hg commit -m "remove files"
125 Invoking status precommit hook
125 Invoking status precommit hook
126 R large1
126 R large1
127 R normal1
127 R normal1
128 $ ls
128 $ ls
129 sub
129 sub
130 $ echo "testlargefile" > large1-test
130 $ echo "testlargefile" > large1-test
131 $ hg add --large large1-test
131 $ hg add --large large1-test
132 $ hg st
132 $ hg st
133 A large1-test
133 A large1-test
134 $ hg rm large1-test
134 $ hg rm large1-test
135 not removing large1-test: file has been marked for add (use forget to undo)
135 not removing large1-test: file has been marked for add (use forget to undo)
136 [1]
136 [1]
137 $ hg st
137 $ hg st
138 A large1-test
138 A large1-test
139 $ hg forget large1-test
139 $ hg forget large1-test
140 $ hg st
140 $ hg st
141 ? large1-test
141 ? large1-test
142 $ hg remove large1-test
142 $ hg remove large1-test
143 not removing large1-test: file is untracked
143 not removing large1-test: file is untracked
144 [1]
144 [1]
145 $ hg forget large1-test
145 $ hg forget large1-test
146 not removing large1-test: file is already untracked
146 not removing large1-test: file is already untracked
147 [1]
147 [1]
148 $ rm large1-test
148 $ rm large1-test
149
149
150 Copy both largefiles and normal files (testing that status output is correct).
150 Copy both largefiles and normal files (testing that status output is correct).
151
151
152 $ hg cp sub/normal2 normal1
152 $ hg cp sub/normal2 normal1
153 $ hg cp sub/large2 large1
153 $ hg cp sub/large2 large1
154 $ hg commit -m "copy files"
154 $ hg commit -m "copy files"
155 Invoking status precommit hook
155 Invoking status precommit hook
156 A large1
156 A large1
157 A normal1
157 A normal1
158 $ cat normal1
158 $ cat normal1
159 normal22
159 normal22
160 $ cat large1
160 $ cat large1
161 large22
161 large22
162
162
163 Test moving largefiles and verify that normal files are also unaffected.
163 Test moving largefiles and verify that normal files are also unaffected.
164
164
165 $ hg mv normal1 normal3
165 $ hg mv normal1 normal3
166 $ hg mv large1 large3
166 $ hg mv large1 large3
167 $ hg mv sub/normal2 sub/normal4
167 $ hg mv sub/normal2 sub/normal4
168 $ hg mv sub/large2 sub/large4
168 $ hg mv sub/large2 sub/large4
169 $ hg commit -m "move files"
169 $ hg commit -m "move files"
170 Invoking status precommit hook
170 Invoking status precommit hook
171 A large3
171 A large3
172 A normal3
172 A normal3
173 A sub/large4
173 A sub/large4
174 A sub/normal4
174 A sub/normal4
175 R large1
175 R large1
176 R normal1
176 R normal1
177 R sub/large2
177 R sub/large2
178 R sub/normal2
178 R sub/normal2
179 $ cat normal3
179 $ cat normal3
180 normal22
180 normal22
181 $ cat large3
181 $ cat large3
182 large22
182 large22
183 $ cat sub/normal4
183 $ cat sub/normal4
184 normal22
184 normal22
185 $ cat sub/large4
185 $ cat sub/large4
186 large22
186 large22
187
187
188
188
189 #if serve
189 #if serve
190 Test display of largefiles in hgweb
190 Test display of largefiles in hgweb
191
191
192 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
192 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
193 $ cat ../hg.pid >> $DAEMON_PIDS
193 $ cat ../hg.pid >> $DAEMON_PIDS
194 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/?style=raw'
194 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/?style=raw'
195 200 Script output follows
195 200 Script output follows
196
196
197
197
198 drwxr-xr-x sub
198 drwxr-xr-x sub
199 -rw-r--r-- 41 large3
199 -rw-r--r-- 41 large3
200 -rw-r--r-- 9 normal3
200 -rw-r--r-- 9 normal3
201
201
202
202
203 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/sub/?style=raw'
203 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/sub/?style=raw'
204 200 Script output follows
204 200 Script output follows
205
205
206
206
207 -rw-r--r-- 41 large4
207 -rw-r--r-- 41 large4
208 -rw-r--r-- 9 normal4
208 -rw-r--r-- 9 normal4
209
209
210
210
211 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
211 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
212 #endif
212 #endif
213
213
214 Test archiving the various revisions. These hit corner cases known with
214 Test archiving the various revisions. These hit corner cases known with
215 archiving.
215 archiving.
216
216
217 $ hg archive -r 0 ../archive0
217 $ hg archive -r 0 ../archive0
218 $ hg archive -r 1 ../archive1
218 $ hg archive -r 1 ../archive1
219 $ hg archive -r 2 ../archive2
219 $ hg archive -r 2 ../archive2
220 $ hg archive -r 3 ../archive3
220 $ hg archive -r 3 ../archive3
221 $ hg archive -r 4 ../archive4
221 $ hg archive -r 4 ../archive4
222 $ cd ../archive0
222 $ cd ../archive0
223 $ cat normal1
223 $ cat normal1
224 normal1
224 normal1
225 $ cat large1
225 $ cat large1
226 large1
226 large1
227 $ cat sub/normal2
227 $ cat sub/normal2
228 normal2
228 normal2
229 $ cat sub/large2
229 $ cat sub/large2
230 large2
230 large2
231 $ cd ../archive1
231 $ cd ../archive1
232 $ cat normal1
232 $ cat normal1
233 normal11
233 normal11
234 $ cat large1
234 $ cat large1
235 large11
235 large11
236 $ cat sub/normal2
236 $ cat sub/normal2
237 normal22
237 normal22
238 $ cat sub/large2
238 $ cat sub/large2
239 large22
239 large22
240 $ cd ../archive2
240 $ cd ../archive2
241 $ ls
241 $ ls
242 sub
242 sub
243 $ cat sub/normal2
243 $ cat sub/normal2
244 normal22
244 normal22
245 $ cat sub/large2
245 $ cat sub/large2
246 large22
246 large22
247 $ cd ../archive3
247 $ cd ../archive3
248 $ cat normal1
248 $ cat normal1
249 normal22
249 normal22
250 $ cat large1
250 $ cat large1
251 large22
251 large22
252 $ cat sub/normal2
252 $ cat sub/normal2
253 normal22
253 normal22
254 $ cat sub/large2
254 $ cat sub/large2
255 large22
255 large22
256 $ cd ../archive4
256 $ cd ../archive4
257 $ cat normal3
257 $ cat normal3
258 normal22
258 normal22
259 $ cat large3
259 $ cat large3
260 large22
260 large22
261 $ cat sub/normal4
261 $ cat sub/normal4
262 normal22
262 normal22
263 $ cat sub/large4
263 $ cat sub/large4
264 large22
264 large22
265
265
266 Commit corner case: specify files to commit.
266 Commit corner case: specify files to commit.
267
267
268 $ cd ../a
268 $ cd ../a
269 $ echo normal3 > normal3
269 $ echo normal3 > normal3
270 $ echo large3 > large3
270 $ echo large3 > large3
271 $ echo normal4 > sub/normal4
271 $ echo normal4 > sub/normal4
272 $ echo large4 > sub/large4
272 $ echo large4 > sub/large4
273 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
273 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
274 Invoking status precommit hook
274 Invoking status precommit hook
275 M large3
275 M large3
276 M normal3
276 M normal3
277 M sub/large4
277 M sub/large4
278 M sub/normal4
278 M sub/normal4
279 $ cat normal3
279 $ cat normal3
280 normal3
280 normal3
281 $ cat large3
281 $ cat large3
282 large3
282 large3
283 $ cat sub/normal4
283 $ cat sub/normal4
284 normal4
284 normal4
285 $ cat sub/large4
285 $ cat sub/large4
286 large4
286 large4
287
287
288 One more commit corner case: commit from a subdirectory.
288 One more commit corner case: commit from a subdirectory.
289
289
290 $ cd ../a
290 $ cd ../a
291 $ echo normal33 > normal3
291 $ echo normal33 > normal3
292 $ echo large33 > large3
292 $ echo large33 > large3
293 $ echo normal44 > sub/normal4
293 $ echo normal44 > sub/normal4
294 $ echo large44 > sub/large4
294 $ echo large44 > sub/large4
295 $ cd sub
295 $ cd sub
296 $ hg commit -m "edit files yet again"
296 $ hg commit -m "edit files yet again"
297 Invoking status precommit hook
297 Invoking status precommit hook
298 M large3
298 M large3
299 M normal3
299 M normal3
300 M sub/large4
300 M sub/large4
301 M sub/normal4
301 M sub/normal4
302 $ cat ../normal3
302 $ cat ../normal3
303 normal33
303 normal33
304 $ cat ../large3
304 $ cat ../large3
305 large33
305 large33
306 $ cat normal4
306 $ cat normal4
307 normal44
307 normal44
308 $ cat large4
308 $ cat large4
309 large44
309 large44
310
310
311 Committing standins is not allowed.
311 Committing standins is not allowed.
312
312
313 $ cd ..
313 $ cd ..
314 $ echo large3 > large3
314 $ echo large3 > large3
315 $ hg commit .hglf/large3 -m "try to commit standin"
315 $ hg commit .hglf/large3 -m "try to commit standin"
316 abort: file ".hglf/large3" is a largefile standin
316 abort: file ".hglf/large3" is a largefile standin
317 (commit the largefile itself instead)
317 (commit the largefile itself instead)
318 [255]
318 [255]
319
319
320 Corner cases for adding largefiles.
320 Corner cases for adding largefiles.
321
321
322 $ echo large5 > large5
322 $ echo large5 > large5
323 $ hg add --large large5
323 $ hg add --large large5
324 $ hg add --large large5
324 $ hg add --large large5
325 large5 already a largefile
325 large5 already a largefile
326 $ mkdir sub2
326 $ mkdir sub2
327 $ echo large6 > sub2/large6
327 $ echo large6 > sub2/large6
328 $ echo large7 > sub2/large7
328 $ echo large7 > sub2/large7
329 $ hg add --large sub2
329 $ hg add --large sub2
330 adding sub2/large6 as a largefile (glob)
330 adding sub2/large6 as a largefile (glob)
331 adding sub2/large7 as a largefile (glob)
331 adding sub2/large7 as a largefile (glob)
332 $ hg st
332 $ hg st
333 M large3
333 M large3
334 A large5
334 A large5
335 A sub2/large6
335 A sub2/large6
336 A sub2/large7
336 A sub2/large7
337
337
338 Committing directories containing only largefiles.
338 Committing directories containing only largefiles.
339
339
340 $ mkdir -p z/y/x/m
340 $ mkdir -p z/y/x/m
341 $ touch z/y/x/m/large1
341 $ touch z/y/x/m/large1
342 $ touch z/y/x/large2
342 $ touch z/y/x/large2
343 $ hg add --large z/y/x/m/large1 z/y/x/large2
343 $ hg add --large z/y/x/m/large1 z/y/x/large2
344 $ hg commit -m "Subdir with directory only containing largefiles" z
344 $ hg commit -m "Subdir with directory only containing largefiles" z
345 Invoking status precommit hook
345 Invoking status precommit hook
346 M large3
346 M large3
347 A large5
347 A large5
348 A sub2/large6
348 A sub2/large6
349 A sub2/large7
349 A sub2/large7
350 A z/y/x/large2
350 A z/y/x/large2
351 A z/y/x/m/large1
351 A z/y/x/m/large1
352
352
353 (and a bit of log testing)
353 (and a bit of log testing)
354
354
355 $ hg log -T '{rev}\n' z/y/x/m/large1
355 $ hg log -T '{rev}\n' z/y/x/m/large1
356 7
356 7
357 $ hg log -T '{rev}\n' z/y/x/m # with only a largefile
357 $ hg log -T '{rev}\n' z/y/x/m # with only a largefile
358 7
358 7
359
359
360 $ hg rollback --quiet
360 $ hg rollback --quiet
361 $ touch z/y/x/m/normal
361 $ touch z/y/x/m/normal
362 $ hg add z/y/x/m/normal
362 $ hg add z/y/x/m/normal
363 $ hg commit -m "Subdir with mixed contents" z
363 $ hg commit -m "Subdir with mixed contents" z
364 Invoking status precommit hook
364 Invoking status precommit hook
365 M large3
365 M large3
366 A large5
366 A large5
367 A sub2/large6
367 A sub2/large6
368 A sub2/large7
368 A sub2/large7
369 A z/y/x/large2
369 A z/y/x/large2
370 A z/y/x/m/large1
370 A z/y/x/m/large1
371 A z/y/x/m/normal
371 A z/y/x/m/normal
372 $ hg st
372 $ hg st
373 M large3
373 M large3
374 A large5
374 A large5
375 A sub2/large6
375 A sub2/large6
376 A sub2/large7
376 A sub2/large7
377 $ hg rollback --quiet
377 $ hg rollback --quiet
378 $ hg revert z/y/x/large2 z/y/x/m/large1
378 $ hg revert z/y/x/large2 z/y/x/m/large1
379 $ rm z/y/x/large2 z/y/x/m/large1
379 $ rm z/y/x/large2 z/y/x/m/large1
380 $ hg commit -m "Subdir with normal contents" z
380 $ hg commit -m "Subdir with normal contents" z
381 Invoking status precommit hook
381 Invoking status precommit hook
382 M large3
382 M large3
383 A large5
383 A large5
384 A sub2/large6
384 A sub2/large6
385 A sub2/large7
385 A sub2/large7
386 A z/y/x/m/normal
386 A z/y/x/m/normal
387 $ hg st
387 $ hg st
388 M large3
388 M large3
389 A large5
389 A large5
390 A sub2/large6
390 A sub2/large6
391 A sub2/large7
391 A sub2/large7
392 $ hg rollback --quiet
392 $ hg rollback --quiet
393 $ hg revert --quiet z
393 $ hg revert --quiet z
394 $ hg commit -m "Empty subdir" z
394 $ hg commit -m "Empty subdir" z
395 abort: z: no match under directory!
395 abort: z: no match under directory!
396 [255]
396 [255]
397 $ rm -rf z
397 $ rm -rf z
398 $ hg ci -m "standin" .hglf
398 $ hg ci -m "standin" .hglf
399 abort: file ".hglf" is a largefile standin
399 abort: file ".hglf" is a largefile standin
400 (commit the largefile itself instead)
400 (commit the largefile itself instead)
401 [255]
401 [255]
402
402
403 Test "hg status" with combination of 'file pattern' and 'directory
403 Test "hg status" with combination of 'file pattern' and 'directory
404 pattern' for largefiles:
404 pattern' for largefiles:
405
405
406 $ hg status sub2/large6 sub2
406 $ hg status sub2/large6 sub2
407 A sub2/large6
407 A sub2/large6
408 A sub2/large7
408 A sub2/large7
409
409
410 Config settings (pattern **.dat, minsize 2 MB) are respected.
410 Config settings (pattern **.dat, minsize 2 MB) are respected.
411
411
412 $ echo testdata > test.dat
412 $ echo testdata > test.dat
413 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
413 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
414 $ hg add
414 $ hg add
415 adding reallylarge as a largefile
415 adding reallylarge as a largefile
416 adding test.dat as a largefile
416 adding test.dat as a largefile
417
417
418 Test that minsize and --lfsize handle float values;
418 Test that minsize and --lfsize handle float values;
419 also tests that --lfsize overrides largefiles.minsize.
419 also tests that --lfsize overrides largefiles.minsize.
420 (0.250 MB = 256 kB = 262144 B)
420 (0.250 MB = 256 kB = 262144 B)
421
421
422 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
422 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
423 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
423 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
424 $ hg --config largefiles.minsize=.25 add
424 $ hg --config largefiles.minsize=.25 add
425 adding ratherlarge as a largefile
425 adding ratherlarge as a largefile
426 adding medium
426 adding medium
427 $ hg forget medium
427 $ hg forget medium
428 $ hg --config largefiles.minsize=.25 add --lfsize=.125
428 $ hg --config largefiles.minsize=.25 add --lfsize=.125
429 adding medium as a largefile
429 adding medium as a largefile
430 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
430 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
431 $ hg --config largefiles.minsize=.25 add --lfsize=.125
431 $ hg --config largefiles.minsize=.25 add --lfsize=.125
432 adding notlarge
432 adding notlarge
433 $ hg forget notlarge
433 $ hg forget notlarge
434
434
435 Test forget on largefiles.
435 Test forget on largefiles.
436
436
437 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
437 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
438 $ hg commit -m "add/edit more largefiles"
438 $ hg commit -m "add/edit more largefiles"
439 Invoking status precommit hook
439 Invoking status precommit hook
440 A sub2/large6
440 A sub2/large6
441 A sub2/large7
441 A sub2/large7
442 R large3
442 R large3
443 ? large5
443 ? large5
444 ? medium
444 ? medium
445 ? notlarge
445 ? notlarge
446 ? ratherlarge
446 ? ratherlarge
447 ? reallylarge
447 ? reallylarge
448 ? test.dat
448 ? test.dat
449 $ hg st
449 $ hg st
450 ? large3
450 ? large3
451 ? large5
451 ? large5
452 ? medium
452 ? medium
453 ? notlarge
453 ? notlarge
454 ? ratherlarge
454 ? ratherlarge
455 ? reallylarge
455 ? reallylarge
456 ? test.dat
456 ? test.dat
457
457
458 Purge with largefiles: verify that largefiles are still in the working
458 Purge with largefiles: verify that largefiles are still in the working
459 dir after a purge.
459 dir after a purge.
460
460
461 $ hg purge --all
461 $ hg purge --all
462 $ cat sub/large4
462 $ cat sub/large4
463 large44
463 large44
464 $ cat sub2/large6
464 $ cat sub2/large6
465 large6
465 large6
466 $ cat sub2/large7
466 $ cat sub2/large7
467 large7
467 large7
468
468
469 Test addremove: verify that files that should be added as largefiles are added as
469 Test addremove: verify that files that should be added as largefiles are added as
470 such and that already-existing largefiles are not added as normal files by
470 such and that already-existing largefiles are not added as normal files by
471 accident.
471 accident.
472
472
473 $ rm normal3
473 $ rm normal3
474 $ rm sub/large4
474 $ rm sub/large4
475 $ echo "testing addremove with patterns" > testaddremove.dat
475 $ echo "testing addremove with patterns" > testaddremove.dat
476 $ echo "normaladdremove" > normaladdremove
476 $ echo "normaladdremove" > normaladdremove
477 $ hg addremove
477 $ hg addremove
478 removing sub/large4
478 removing sub/large4
479 adding testaddremove.dat as a largefile
479 adding testaddremove.dat as a largefile
480 removing normal3
480 removing normal3
481 adding normaladdremove
481 adding normaladdremove
482
482
483 Test addremove with -R
483 Test addremove with -R
484
484
485 $ hg up -C
485 $ hg up -C
486 getting changed largefiles
486 getting changed largefiles
487 1 largefiles updated, 0 removed
487 1 largefiles updated, 0 removed
488 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
488 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 $ rm normal3
489 $ rm normal3
490 $ rm sub/large4
490 $ rm sub/large4
491 $ echo "testing addremove with patterns" > testaddremove.dat
491 $ echo "testing addremove with patterns" > testaddremove.dat
492 $ echo "normaladdremove" > normaladdremove
492 $ echo "normaladdremove" > normaladdremove
493 $ cd ..
493 $ cd ..
494 $ hg -R a addremove
494 $ hg -R a addremove
495 removing sub/large4
495 removing sub/large4
496 adding a/testaddremove.dat as a largefile (glob)
496 adding a/testaddremove.dat as a largefile (glob)
497 removing normal3
497 removing normal3
498 adding normaladdremove
498 adding normaladdremove
499 $ cd a
499 $ cd a
500
500
501 Test 3364
501 Test 3364
502 $ hg clone . ../addrm
502 $ hg clone . ../addrm
503 updating to branch default
503 updating to branch default
504 getting changed largefiles
504 getting changed largefiles
505 3 largefiles updated, 0 removed
505 3 largefiles updated, 0 removed
506 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
506 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
507 $ cd ../addrm
507 $ cd ../addrm
508 $ cat >> .hg/hgrc <<EOF
508 $ cat >> .hg/hgrc <<EOF
509 > [hooks]
509 > [hooks]
510 > post-commit.stat=sh -c "echo \\"Invoking status postcommit hook\\"; hg status -A"
510 > post-commit.stat=sh -c "echo \\"Invoking status postcommit hook\\"; hg status -A"
511 > EOF
511 > EOF
512 $ touch foo
512 $ touch foo
513 $ hg add --large foo
513 $ hg add --large foo
514 $ hg ci -m "add foo"
514 $ hg ci -m "add foo"
515 Invoking status precommit hook
515 Invoking status precommit hook
516 A foo
516 A foo
517 Invoking status postcommit hook
517 Invoking status postcommit hook
518 C foo
518 C foo
519 C normal3
519 C normal3
520 C sub/large4
520 C sub/large4
521 C sub/normal4
521 C sub/normal4
522 C sub2/large6
522 C sub2/large6
523 C sub2/large7
523 C sub2/large7
524 $ rm foo
524 $ rm foo
525 $ hg st
525 $ hg st
526 ! foo
526 ! foo
527 hmm.. no precommit invoked, but there is a postcommit??
527 hmm.. no precommit invoked, but there is a postcommit??
528 $ hg ci -m "will not checkin"
528 $ hg ci -m "will not checkin"
529 nothing changed
529 nothing changed
530 Invoking status postcommit hook
530 Invoking status postcommit hook
531 ! foo
531 ! foo
532 C normal3
532 C normal3
533 C sub/large4
533 C sub/large4
534 C sub/normal4
534 C sub/normal4
535 C sub2/large6
535 C sub2/large6
536 C sub2/large7
536 C sub2/large7
537 [1]
537 [1]
538 $ hg addremove
538 $ hg addremove
539 removing foo
539 removing foo
540 $ hg st
540 $ hg st
541 R foo
541 R foo
542 $ hg ci -m "used to say nothing changed"
542 $ hg ci -m "used to say nothing changed"
543 Invoking status precommit hook
543 Invoking status precommit hook
544 R foo
544 R foo
545 Invoking status postcommit hook
545 Invoking status postcommit hook
546 C normal3
546 C normal3
547 C sub/large4
547 C sub/large4
548 C sub/normal4
548 C sub/normal4
549 C sub2/large6
549 C sub2/large6
550 C sub2/large7
550 C sub2/large7
551 $ hg st
551 $ hg st
552
552
553 Test 3507 (both normal files and largefiles were a problem)
553 Test 3507 (both normal files and largefiles were a problem)
554
554
555 $ touch normal
555 $ touch normal
556 $ touch large
556 $ touch large
557 $ hg add normal
557 $ hg add normal
558 $ hg add --large large
558 $ hg add --large large
559 $ hg ci -m "added"
559 $ hg ci -m "added"
560 Invoking status precommit hook
560 Invoking status precommit hook
561 A large
561 A large
562 A normal
562 A normal
563 Invoking status postcommit hook
563 Invoking status postcommit hook
564 C large
564 C large
565 C normal
565 C normal
566 C normal3
566 C normal3
567 C sub/large4
567 C sub/large4
568 C sub/normal4
568 C sub/normal4
569 C sub2/large6
569 C sub2/large6
570 C sub2/large7
570 C sub2/large7
571 $ hg remove normal
571 $ hg remove normal
572 $ hg addremove --traceback
572 $ hg addremove --traceback
573 $ hg ci -m "addremoved normal"
573 $ hg ci -m "addremoved normal"
574 Invoking status precommit hook
574 Invoking status precommit hook
575 R normal
575 R normal
576 Invoking status postcommit hook
576 Invoking status postcommit hook
577 C large
577 C large
578 C normal3
578 C normal3
579 C sub/large4
579 C sub/large4
580 C sub/normal4
580 C sub/normal4
581 C sub2/large6
581 C sub2/large6
582 C sub2/large7
582 C sub2/large7
583 $ hg up -C '.^'
583 $ hg up -C '.^'
584 getting changed largefiles
584 getting changed largefiles
585 0 largefiles updated, 0 removed
585 0 largefiles updated, 0 removed
586 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
587 $ hg remove large
587 $ hg remove large
588 $ hg addremove --traceback
588 $ hg addremove --traceback
589 $ hg ci -m "removed large"
589 $ hg ci -m "removed large"
590 Invoking status precommit hook
590 Invoking status precommit hook
591 R large
591 R large
592 created new head
592 created new head
593 Invoking status postcommit hook
593 Invoking status postcommit hook
594 C normal
594 C normal
595 C normal3
595 C normal3
596 C sub/large4
596 C sub/large4
597 C sub/normal4
597 C sub/normal4
598 C sub2/large6
598 C sub2/large6
599 C sub2/large7
599 C sub2/large7
600
600
601 Test commit -A (issue 3542)
601 Test commit -A (issue 3542)
602 $ echo large8 > large8
602 $ echo large8 > large8
603 $ hg add --large large8
603 $ hg add --large large8
604 $ hg ci -Am 'this used to add large8 as normal and commit both'
604 $ hg ci -Am 'this used to add large8 as normal and commit both'
605 Invoking status precommit hook
605 Invoking status precommit hook
606 A large8
606 A large8
607 Invoking status postcommit hook
607 Invoking status postcommit hook
608 C large8
608 C large8
609 C normal
609 C normal
610 C normal3
610 C normal3
611 C sub/large4
611 C sub/large4
612 C sub/normal4
612 C sub/normal4
613 C sub2/large6
613 C sub2/large6
614 C sub2/large7
614 C sub2/large7
615 $ rm large8
615 $ rm large8
616 $ hg ci -Am 'this used to not notice the rm'
616 $ hg ci -Am 'this used to not notice the rm'
617 removing large8
617 removing large8
618 Invoking status precommit hook
618 Invoking status precommit hook
619 R large8
619 R large8
620 Invoking status postcommit hook
620 Invoking status postcommit hook
621 C normal
621 C normal
622 C normal3
622 C normal3
623 C sub/large4
623 C sub/large4
624 C sub/normal4
624 C sub/normal4
625 C sub2/large6
625 C sub2/large6
626 C sub2/large7
626 C sub2/large7
627
627
628 Test that a standin can't be added as a large file
628 Test that a standin can't be added as a large file
629
629
630 $ touch large
630 $ touch large
631 $ hg add --large large
631 $ hg add --large large
632 $ hg ci -m "add"
632 $ hg ci -m "add"
633 Invoking status precommit hook
633 Invoking status precommit hook
634 A large
634 A large
635 Invoking status postcommit hook
635 Invoking status postcommit hook
636 C large
636 C large
637 C normal
637 C normal
638 C normal3
638 C normal3
639 C sub/large4
639 C sub/large4
640 C sub/normal4
640 C sub/normal4
641 C sub2/large6
641 C sub2/large6
642 C sub2/large7
642 C sub2/large7
643 $ hg remove large
643 $ hg remove large
644 $ touch large
644 $ touch large
645 $ hg addremove --config largefiles.patterns=**large --traceback
645 $ hg addremove --config largefiles.patterns=**large --traceback
646 adding large as a largefile
646 adding large as a largefile
647
647
648 Test that outgoing --large works (with revsets too)
648 Test that outgoing --large works (with revsets too)
649 $ hg outgoing --rev '.^' --large
649 $ hg outgoing --rev '.^' --large
650 comparing with $TESTTMP/a (glob)
650 comparing with $TESTTMP/a (glob)
651 searching for changes
651 searching for changes
652 changeset: 8:c02fd3b77ec4
652 changeset: 8:c02fd3b77ec4
653 user: test
653 user: test
654 date: Thu Jan 01 00:00:00 1970 +0000
654 date: Thu Jan 01 00:00:00 1970 +0000
655 summary: add foo
655 summary: add foo
656
656
657 changeset: 9:289dd08c9bbb
657 changeset: 9:289dd08c9bbb
658 user: test
658 user: test
659 date: Thu Jan 01 00:00:00 1970 +0000
659 date: Thu Jan 01 00:00:00 1970 +0000
660 summary: used to say nothing changed
660 summary: used to say nothing changed
661
661
662 changeset: 10:34f23ac6ac12
662 changeset: 10:34f23ac6ac12
663 user: test
663 user: test
664 date: Thu Jan 01 00:00:00 1970 +0000
664 date: Thu Jan 01 00:00:00 1970 +0000
665 summary: added
665 summary: added
666
666
667 changeset: 12:710c1b2f523c
667 changeset: 12:710c1b2f523c
668 parent: 10:34f23ac6ac12
668 parent: 10:34f23ac6ac12
669 user: test
669 user: test
670 date: Thu Jan 01 00:00:00 1970 +0000
670 date: Thu Jan 01 00:00:00 1970 +0000
671 summary: removed large
671 summary: removed large
672
672
673 changeset: 13:0a3e75774479
673 changeset: 13:0a3e75774479
674 user: test
674 user: test
675 date: Thu Jan 01 00:00:00 1970 +0000
675 date: Thu Jan 01 00:00:00 1970 +0000
676 summary: this used to add large8 as normal and commit both
676 summary: this used to add large8 as normal and commit both
677
677
678 changeset: 14:84f3d378175c
678 changeset: 14:84f3d378175c
679 user: test
679 user: test
680 date: Thu Jan 01 00:00:00 1970 +0000
680 date: Thu Jan 01 00:00:00 1970 +0000
681 summary: this used to not notice the rm
681 summary: this used to not notice the rm
682
682
683 largefiles to upload (1 entities):
683 largefiles to upload (1 entities):
684 large8
684 large8
685
685
686 $ cd ../a
686 $ cd ../a
687
687
688 Clone a largefiles repo.
688 Clone a largefiles repo.
689
689
690 $ hg clone . ../b
690 $ hg clone . ../b
691 updating to branch default
691 updating to branch default
692 getting changed largefiles
692 getting changed largefiles
693 3 largefiles updated, 0 removed
693 3 largefiles updated, 0 removed
694 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
694 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
695 $ cd ../b
695 $ cd ../b
696 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
696 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
697 7:daea875e9014 add/edit more largefiles
697 7:daea875e9014 add/edit more largefiles
698 6:4355d653f84f edit files yet again
698 6:4355d653f84f edit files yet again
699 5:9d5af5072dbd edit files again
699 5:9d5af5072dbd edit files again
700 4:74c02385b94c move files
700 4:74c02385b94c move files
701 3:9e8fbc4bce62 copy files
701 3:9e8fbc4bce62 copy files
702 2:51a0ae4d5864 remove files
702 2:51a0ae4d5864 remove files
703 1:ce8896473775 edit files
703 1:ce8896473775 edit files
704 0:30d30fe6a5be add files
704 0:30d30fe6a5be add files
705 $ cat normal3
705 $ cat normal3
706 normal33
706 normal33
707
707
708 Test graph log
708 Test graph log
709
709
710 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
710 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
711 @ 7:daea875e9014 add/edit more largefiles
711 @ 7:daea875e9014 add/edit more largefiles
712 |
712 |
713 o 6:4355d653f84f edit files yet again
713 o 6:4355d653f84f edit files yet again
714 |
714 |
715 o 5:9d5af5072dbd edit files again
715 o 5:9d5af5072dbd edit files again
716 |
716 |
717 o 4:74c02385b94c move files
717 o 4:74c02385b94c move files
718 |
718 |
719 o 3:9e8fbc4bce62 copy files
719 o 3:9e8fbc4bce62 copy files
720 |
720 |
721 o 2:51a0ae4d5864 remove files
721 o 2:51a0ae4d5864 remove files
722 |
722 |
723 o 1:ce8896473775 edit files
723 o 1:ce8896473775 edit files
724 |
724 |
725 o 0:30d30fe6a5be add files
725 o 0:30d30fe6a5be add files
726
726
727
728 Test log with --patch
729
730 $ hg log --patch -r 6::7
731 changeset: 6:4355d653f84f
732 user: test
733 date: Thu Jan 01 00:00:00 1970 +0000
734 summary: edit files yet again
735
736 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/large3
737 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
738 +++ b/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
739 @@ -1,1 +1,1 @@
740 -baaf12afde9d8d67f25dab6dced0d2bf77dba47c
741 +7838695e10da2bb75ac1156565f40a2595fa2fa0
742 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
743 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
744 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
745 @@ -1,1 +1,1 @@
746 -aeb2210d19f02886dde00dac279729a48471e2f9
747 +971fb41e78fea4f8e0ba5244784239371cb00591
748 diff -r 9d5af5072dbd -r 4355d653f84f normal3
749 --- a/normal3 Thu Jan 01 00:00:00 1970 +0000
750 +++ b/normal3 Thu Jan 01 00:00:00 1970 +0000
751 @@ -1,1 +1,1 @@
752 -normal3
753 +normal33
754 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
755 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
756 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
757 @@ -1,1 +1,1 @@
758 -normal4
759 +normal44
760
761 changeset: 7:daea875e9014
762 tag: tip
763 user: test
764 date: Thu Jan 01 00:00:00 1970 +0000
765 summary: add/edit more largefiles
766
767 diff -r 4355d653f84f -r daea875e9014 .hglf/large3
768 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
769 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
770 @@ -1,1 +0,0 @@
771 -7838695e10da2bb75ac1156565f40a2595fa2fa0
772 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large6
773 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
774 +++ b/.hglf/sub2/large6 Thu Jan 01 00:00:00 1970 +0000
775 @@ -0,0 +1,1 @@
776 +0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30
777 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large7
778 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
779 +++ b/.hglf/sub2/large7 Thu Jan 01 00:00:00 1970 +0000
780 @@ -0,0 +1,1 @@
781 +bb3151689acb10f0c3125c560d5e63df914bc1af
782
783
784 $ hg log --patch -r 6::7 sub/
785 changeset: 6:4355d653f84f
786 user: test
787 date: Thu Jan 01 00:00:00 1970 +0000
788 summary: edit files yet again
789
790 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
791 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
792 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
793 @@ -1,1 +1,1 @@
794 -aeb2210d19f02886dde00dac279729a48471e2f9
795 +971fb41e78fea4f8e0ba5244784239371cb00591
796 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
797 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
798 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
799 @@ -1,1 +1,1 @@
800 -normal4
801 +normal44
802
803
804 log with both --follow and --patch
805
806 $ hg log --follow --patch --limit 2
807 changeset: 7:daea875e9014
808 tag: tip
809 user: test
810 date: Thu Jan 01 00:00:00 1970 +0000
811 summary: add/edit more largefiles
812
813 diff -r 4355d653f84f -r daea875e9014 .hglf/large3
814 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
815 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
816 @@ -1,1 +0,0 @@
817 -7838695e10da2bb75ac1156565f40a2595fa2fa0
818 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large6
819 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
820 +++ b/.hglf/sub2/large6 Thu Jan 01 00:00:00 1970 +0000
821 @@ -0,0 +1,1 @@
822 +0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30
823 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large7
824 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
825 +++ b/.hglf/sub2/large7 Thu Jan 01 00:00:00 1970 +0000
826 @@ -0,0 +1,1 @@
827 +bb3151689acb10f0c3125c560d5e63df914bc1af
828
829 changeset: 6:4355d653f84f
830 user: test
831 date: Thu Jan 01 00:00:00 1970 +0000
832 summary: edit files yet again
833
834 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/large3
835 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
836 +++ b/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
837 @@ -1,1 +1,1 @@
838 -baaf12afde9d8d67f25dab6dced0d2bf77dba47c
839 +7838695e10da2bb75ac1156565f40a2595fa2fa0
840 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
841 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
842 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
843 @@ -1,1 +1,1 @@
844 -aeb2210d19f02886dde00dac279729a48471e2f9
845 +971fb41e78fea4f8e0ba5244784239371cb00591
846 diff -r 9d5af5072dbd -r 4355d653f84f normal3
847 --- a/normal3 Thu Jan 01 00:00:00 1970 +0000
848 +++ b/normal3 Thu Jan 01 00:00:00 1970 +0000
849 @@ -1,1 +1,1 @@
850 -normal3
851 +normal33
852 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
853 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
854 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
855 @@ -1,1 +1,1 @@
856 -normal4
857 +normal44
858
859 $ hg log --follow --patch sub/large4
860 changeset: 6:4355d653f84f
861 user: test
862 date: Thu Jan 01 00:00:00 1970 +0000
863 summary: edit files yet again
864
865 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
866 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
867 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
868 @@ -1,1 +1,1 @@
869 -aeb2210d19f02886dde00dac279729a48471e2f9
870 +971fb41e78fea4f8e0ba5244784239371cb00591
871
872 changeset: 5:9d5af5072dbd
873 user: test
874 date: Thu Jan 01 00:00:00 1970 +0000
875 summary: edit files again
876
877 diff -r 74c02385b94c -r 9d5af5072dbd .hglf/sub/large4
878 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
879 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
880 @@ -1,1 +1,1 @@
881 -eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
882 +aeb2210d19f02886dde00dac279729a48471e2f9
883
884 changeset: 4:74c02385b94c
885 user: test
886 date: Thu Jan 01 00:00:00 1970 +0000
887 summary: move files
888
889 diff -r 9e8fbc4bce62 -r 74c02385b94c .hglf/sub/large4
890 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
891 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
892 @@ -0,0 +1,1 @@
893 +eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
894
895 changeset: 1:ce8896473775
896 user: test
897 date: Thu Jan 01 00:00:00 1970 +0000
898 summary: edit files
899
900 diff -r 30d30fe6a5be -r ce8896473775 .hglf/sub/large2
901 --- a/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
902 +++ b/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
903 @@ -1,1 +1,1 @@
904 -1deebade43c8c498a3c8daddac0244dc55d1331d
905 +eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
906
907 changeset: 0:30d30fe6a5be
908 user: test
909 date: Thu Jan 01 00:00:00 1970 +0000
910 summary: add files
911
912 diff -r 000000000000 -r 30d30fe6a5be .hglf/sub/large2
913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
914 +++ b/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
915 @@ -0,0 +1,1 @@
916 +1deebade43c8c498a3c8daddac0244dc55d1331d
917
727 $ cat sub/normal4
918 $ cat sub/normal4
728 normal44
919 normal44
729 $ cat sub/large4
920 $ cat sub/large4
730 large44
921 large44
731 $ cat sub2/large6
922 $ cat sub2/large6
732 large6
923 large6
733 $ cat sub2/large7
924 $ cat sub2/large7
734 large7
925 large7
735 $ hg log -qf sub2/large7
926 $ hg log -qf sub2/large7
736 7:daea875e9014
927 7:daea875e9014
737 $ hg log -Gqf sub2/large7
928 $ hg log -Gqf sub2/large7
738 @ 7:daea875e9014
929 @ 7:daea875e9014
739 |
930 |
740 $ cd ..
931 $ cd ..
741
932
742 Test log from outside repo
933 Test log from outside repo
743
934
744 $ hg log b/sub -T '{rev}:{node|short} {desc|firstline}\n'
935 $ hg log b/sub -T '{rev}:{node|short} {desc|firstline}\n'
745 6:4355d653f84f edit files yet again
936 6:4355d653f84f edit files yet again
746 5:9d5af5072dbd edit files again
937 5:9d5af5072dbd edit files again
747 4:74c02385b94c move files
938 4:74c02385b94c move files
748 1:ce8896473775 edit files
939 1:ce8896473775 edit files
749 0:30d30fe6a5be add files
940 0:30d30fe6a5be add files
750
941
751 Test clone at revision
942 Test clone at revision
752
943
753 $ hg clone a -r 3 c
944 $ hg clone a -r 3 c
754 adding changesets
945 adding changesets
755 adding manifests
946 adding manifests
756 adding file changes
947 adding file changes
757 added 4 changesets with 10 changes to 4 files
948 added 4 changesets with 10 changes to 4 files
758 updating to branch default
949 updating to branch default
759 getting changed largefiles
950 getting changed largefiles
760 2 largefiles updated, 0 removed
951 2 largefiles updated, 0 removed
761 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
952 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
762 $ cd c
953 $ cd c
763 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
954 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
764 3:9e8fbc4bce62 copy files
955 3:9e8fbc4bce62 copy files
765 2:51a0ae4d5864 remove files
956 2:51a0ae4d5864 remove files
766 1:ce8896473775 edit files
957 1:ce8896473775 edit files
767 0:30d30fe6a5be add files
958 0:30d30fe6a5be add files
768 $ cat normal1
959 $ cat normal1
769 normal22
960 normal22
770 $ cat large1
961 $ cat large1
771 large22
962 large22
772 $ cat sub/normal2
963 $ cat sub/normal2
773 normal22
964 normal22
774 $ cat sub/large2
965 $ cat sub/large2
775 large22
966 large22
776
967
777 Old revisions of a clone have correct largefiles content (this also
968 Old revisions of a clone have correct largefiles content (this also
778 tests update).
969 tests update).
779
970
780 $ hg update -r 1
971 $ hg update -r 1
781 getting changed largefiles
972 getting changed largefiles
782 1 largefiles updated, 0 removed
973 1 largefiles updated, 0 removed
783 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
974 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
784 $ cat large1
975 $ cat large1
785 large11
976 large11
786 $ cat sub/large2
977 $ cat sub/large2
787 large22
978 large22
788 $ cd ..
979 $ cd ..
789
980
790 Test cloning with --all-largefiles flag
981 Test cloning with --all-largefiles flag
791
982
792 $ rm "${USERCACHE}"/*
983 $ rm "${USERCACHE}"/*
793 $ hg clone --all-largefiles a a-backup
984 $ hg clone --all-largefiles a a-backup
794 updating to branch default
985 updating to branch default
795 getting changed largefiles
986 getting changed largefiles
796 3 largefiles updated, 0 removed
987 3 largefiles updated, 0 removed
797 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
988 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
798 8 additional largefiles cached
989 8 additional largefiles cached
799
990
800 $ rm "${USERCACHE}"/*
991 $ rm "${USERCACHE}"/*
801 $ hg clone --all-largefiles -u 0 a a-clone0
992 $ hg clone --all-largefiles -u 0 a a-clone0
802 updating to branch default
993 updating to branch default
803 getting changed largefiles
994 getting changed largefiles
804 2 largefiles updated, 0 removed
995 2 largefiles updated, 0 removed
805 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
996 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
806 9 additional largefiles cached
997 9 additional largefiles cached
807 $ hg -R a-clone0 sum
998 $ hg -R a-clone0 sum
808 parent: 0:30d30fe6a5be
999 parent: 0:30d30fe6a5be
809 add files
1000 add files
810 branch: default
1001 branch: default
811 commit: (clean)
1002 commit: (clean)
812 update: 7 new changesets (update)
1003 update: 7 new changesets (update)
813
1004
814 $ rm "${USERCACHE}"/*
1005 $ rm "${USERCACHE}"/*
815 $ hg clone --all-largefiles -u 1 a a-clone1
1006 $ hg clone --all-largefiles -u 1 a a-clone1
816 updating to branch default
1007 updating to branch default
817 getting changed largefiles
1008 getting changed largefiles
818 2 largefiles updated, 0 removed
1009 2 largefiles updated, 0 removed
819 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1010 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
820 8 additional largefiles cached
1011 8 additional largefiles cached
821 $ hg -R a-clone1 verify --large --lfa --lfc
1012 $ hg -R a-clone1 verify --large --lfa --lfc
822 checking changesets
1013 checking changesets
823 checking manifests
1014 checking manifests
824 crosschecking files in changesets and manifests
1015 crosschecking files in changesets and manifests
825 checking files
1016 checking files
826 10 files, 8 changesets, 24 total revisions
1017 10 files, 8 changesets, 24 total revisions
827 searching 8 changesets for largefiles
1018 searching 8 changesets for largefiles
828 verified contents of 13 revisions of 6 largefiles
1019 verified contents of 13 revisions of 6 largefiles
829 $ hg -R a-clone1 sum
1020 $ hg -R a-clone1 sum
830 parent: 1:ce8896473775
1021 parent: 1:ce8896473775
831 edit files
1022 edit files
832 branch: default
1023 branch: default
833 commit: (clean)
1024 commit: (clean)
834 update: 6 new changesets (update)
1025 update: 6 new changesets (update)
835
1026
836 $ rm "${USERCACHE}"/*
1027 $ rm "${USERCACHE}"/*
837 $ hg clone --all-largefiles -U a a-clone-u
1028 $ hg clone --all-largefiles -U a a-clone-u
838 11 additional largefiles cached
1029 11 additional largefiles cached
839 $ hg -R a-clone-u sum
1030 $ hg -R a-clone-u sum
840 parent: -1:000000000000 (no revision checked out)
1031 parent: -1:000000000000 (no revision checked out)
841 branch: default
1032 branch: default
842 commit: (clean)
1033 commit: (clean)
843 update: 8 new changesets (update)
1034 update: 8 new changesets (update)
844
1035
845 Show computed destination directory:
1036 Show computed destination directory:
846
1037
847 $ mkdir xyz
1038 $ mkdir xyz
848 $ cd xyz
1039 $ cd xyz
849 $ hg clone ../a
1040 $ hg clone ../a
850 destination directory: a
1041 destination directory: a
851 updating to branch default
1042 updating to branch default
852 getting changed largefiles
1043 getting changed largefiles
853 3 largefiles updated, 0 removed
1044 3 largefiles updated, 0 removed
854 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1045 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
855 $ cd ..
1046 $ cd ..
856
1047
857 Clone URL without path:
1048 Clone URL without path:
858
1049
859 $ hg clone file://
1050 $ hg clone file://
860 abort: repository / not found!
1051 abort: repository / not found!
861 [255]
1052 [255]
862
1053
863 Ensure base clone command argument validation
1054 Ensure base clone command argument validation
864
1055
865 $ hg clone -U -u 0 a a-clone-failure
1056 $ hg clone -U -u 0 a a-clone-failure
866 abort: cannot specify both --noupdate and --updaterev
1057 abort: cannot specify both --noupdate and --updaterev
867 [255]
1058 [255]
868
1059
869 $ hg clone --all-largefiles a ssh://localhost/a
1060 $ hg clone --all-largefiles a ssh://localhost/a
870 abort: --all-largefiles is incompatible with non-local destination ssh://localhost/a
1061 abort: --all-largefiles is incompatible with non-local destination ssh://localhost/a
871 [255]
1062 [255]
872
1063
873 Test pulling with --all-largefiles flag. Also test that the largefiles are
1064 Test pulling with --all-largefiles flag. Also test that the largefiles are
874 downloaded from 'default' instead of 'default-push' when no source is specified
1065 downloaded from 'default' instead of 'default-push' when no source is specified
875 (issue3584)
1066 (issue3584)
876
1067
877 $ rm -Rf a-backup
1068 $ rm -Rf a-backup
878 $ hg clone -r 1 a a-backup
1069 $ hg clone -r 1 a a-backup
879 adding changesets
1070 adding changesets
880 adding manifests
1071 adding manifests
881 adding file changes
1072 adding file changes
882 added 2 changesets with 8 changes to 4 files
1073 added 2 changesets with 8 changes to 4 files
883 updating to branch default
1074 updating to branch default
884 getting changed largefiles
1075 getting changed largefiles
885 2 largefiles updated, 0 removed
1076 2 largefiles updated, 0 removed
886 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1077 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
887 $ rm "${USERCACHE}"/*
1078 $ rm "${USERCACHE}"/*
888 $ cd a-backup
1079 $ cd a-backup
889 $ hg pull --all-largefiles --config paths.default-push=bogus/path
1080 $ hg pull --all-largefiles --config paths.default-push=bogus/path
890 pulling from $TESTTMP/a (glob)
1081 pulling from $TESTTMP/a (glob)
891 searching for changes
1082 searching for changes
892 adding changesets
1083 adding changesets
893 adding manifests
1084 adding manifests
894 adding file changes
1085 adding file changes
895 added 6 changesets with 16 changes to 8 files
1086 added 6 changesets with 16 changes to 8 files
896 (run 'hg update' to get a working copy)
1087 (run 'hg update' to get a working copy)
897 6 largefiles cached
1088 6 largefiles cached
898
1089
899 redo pull with --lfrev and check it pulls largefiles for the right revs
1090 redo pull with --lfrev and check it pulls largefiles for the right revs
900
1091
901 $ hg rollback
1092 $ hg rollback
902 repository tip rolled back to revision 1 (undo pull)
1093 repository tip rolled back to revision 1 (undo pull)
903 $ hg pull -v --lfrev 'heads(pulled())+min(pulled())'
1094 $ hg pull -v --lfrev 'heads(pulled())+min(pulled())'
904 pulling from $TESTTMP/a (glob)
1095 pulling from $TESTTMP/a (glob)
905 searching for changes
1096 searching for changes
906 all local heads known remotely
1097 all local heads known remotely
907 6 changesets found
1098 6 changesets found
908 adding changesets
1099 adding changesets
909 adding manifests
1100 adding manifests
910 adding file changes
1101 adding file changes
911 added 6 changesets with 16 changes to 8 files
1102 added 6 changesets with 16 changes to 8 files
912 calling hook changegroup.lfiles: hgext.largefiles.reposetup.checkrequireslfiles
1103 calling hook changegroup.lfiles: hgext.largefiles.reposetup.checkrequireslfiles
913 (run 'hg update' to get a working copy)
1104 (run 'hg update' to get a working copy)
914 pulling largefiles for revision 7
1105 pulling largefiles for revision 7
915 found 971fb41e78fea4f8e0ba5244784239371cb00591 in store
1106 found 971fb41e78fea4f8e0ba5244784239371cb00591 in store
916 found 0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30 in store
1107 found 0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30 in store
917 found bb3151689acb10f0c3125c560d5e63df914bc1af in store
1108 found bb3151689acb10f0c3125c560d5e63df914bc1af in store
918 pulling largefiles for revision 2
1109 pulling largefiles for revision 2
919 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1110 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
920 0 largefiles cached
1111 0 largefiles cached
921
1112
922 lfpull
1113 lfpull
923
1114
924 $ hg lfpull -r : --config largefiles.usercache=usercache-lfpull
1115 $ hg lfpull -r : --config largefiles.usercache=usercache-lfpull
925 2 largefiles cached
1116 2 largefiles cached
926 $ hg lfpull -v -r 4+2 --config largefiles.usercache=usercache-lfpull
1117 $ hg lfpull -v -r 4+2 --config largefiles.usercache=usercache-lfpull
927 pulling largefiles for revision 4
1118 pulling largefiles for revision 4
928 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1119 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
929 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1120 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
930 pulling largefiles for revision 2
1121 pulling largefiles for revision 2
931 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1122 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
932 0 largefiles cached
1123 0 largefiles cached
933
1124
934 $ ls usercache-lfpull/* | sort
1125 $ ls usercache-lfpull/* | sort
935 usercache-lfpull/1deebade43c8c498a3c8daddac0244dc55d1331d
1126 usercache-lfpull/1deebade43c8c498a3c8daddac0244dc55d1331d
936 usercache-lfpull/4669e532d5b2c093a78eca010077e708a071bb64
1127 usercache-lfpull/4669e532d5b2c093a78eca010077e708a071bb64
937
1128
938 $ cd ..
1129 $ cd ..
939
1130
940 Rebasing between two repositories does not revert largefiles to old
1131 Rebasing between two repositories does not revert largefiles to old
941 revisions (this was a very bad bug that took a lot of work to fix).
1132 revisions (this was a very bad bug that took a lot of work to fix).
942
1133
943 $ hg clone a d
1134 $ hg clone a d
944 updating to branch default
1135 updating to branch default
945 getting changed largefiles
1136 getting changed largefiles
946 3 largefiles updated, 0 removed
1137 3 largefiles updated, 0 removed
947 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1138 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
948 $ cd b
1139 $ cd b
949 $ echo large4-modified > sub/large4
1140 $ echo large4-modified > sub/large4
950 $ echo normal3-modified > normal3
1141 $ echo normal3-modified > normal3
951 $ hg commit -m "modify normal file and largefile in repo b"
1142 $ hg commit -m "modify normal file and largefile in repo b"
952 Invoking status precommit hook
1143 Invoking status precommit hook
953 M normal3
1144 M normal3
954 M sub/large4
1145 M sub/large4
955 $ cd ../d
1146 $ cd ../d
956 $ echo large6-modified > sub2/large6
1147 $ echo large6-modified > sub2/large6
957 $ echo normal4-modified > sub/normal4
1148 $ echo normal4-modified > sub/normal4
958 $ hg commit -m "modify normal file largefile in repo d"
1149 $ hg commit -m "modify normal file largefile in repo d"
959 Invoking status precommit hook
1150 Invoking status precommit hook
960 M sub/normal4
1151 M sub/normal4
961 M sub2/large6
1152 M sub2/large6
962 $ cd ..
1153 $ cd ..
963 $ hg clone d e
1154 $ hg clone d e
964 updating to branch default
1155 updating to branch default
965 getting changed largefiles
1156 getting changed largefiles
966 3 largefiles updated, 0 removed
1157 3 largefiles updated, 0 removed
967 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1158 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
968 $ cd d
1159 $ cd d
969
1160
970 More rebase testing, but also test that the largefiles are downloaded from
1161 More rebase testing, but also test that the largefiles are downloaded from
971 'default-push' when no source is specified (issue3584). (The largefile from the
1162 'default-push' when no source is specified (issue3584). (The largefile from the
972 pulled revision is however not downloaded but found in the local cache.)
1163 pulled revision is however not downloaded but found in the local cache.)
973 Largefiles are fetched for the new pulled revision, not for existing revisions,
1164 Largefiles are fetched for the new pulled revision, not for existing revisions,
974 rebased or not.
1165 rebased or not.
975
1166
976 $ [ ! -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
1167 $ [ ! -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
977 $ hg pull --rebase --all-largefiles --config paths.default-push=bogus/path --config paths.default=../b
1168 $ hg pull --rebase --all-largefiles --config paths.default-push=bogus/path --config paths.default=../b
978 pulling from $TESTTMP/b (glob)
1169 pulling from $TESTTMP/b (glob)
979 searching for changes
1170 searching for changes
980 adding changesets
1171 adding changesets
981 adding manifests
1172 adding manifests
982 adding file changes
1173 adding file changes
983 added 1 changesets with 2 changes to 2 files (+1 heads)
1174 added 1 changesets with 2 changes to 2 files (+1 heads)
984 Invoking status precommit hook
1175 Invoking status precommit hook
985 M sub/normal4
1176 M sub/normal4
986 M sub2/large6
1177 M sub2/large6
987 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg (glob)
1178 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg (glob)
988 0 largefiles cached
1179 0 largefiles cached
989 nothing to rebase - working directory parent is also destination
1180 nothing to rebase - working directory parent is also destination
990 $ [ -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
1181 $ [ -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
991 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1182 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
992 9:598410d3eb9a modify normal file largefile in repo d
1183 9:598410d3eb9a modify normal file largefile in repo d
993 8:a381d2c8c80e modify normal file and largefile in repo b
1184 8:a381d2c8c80e modify normal file and largefile in repo b
994 7:daea875e9014 add/edit more largefiles
1185 7:daea875e9014 add/edit more largefiles
995 6:4355d653f84f edit files yet again
1186 6:4355d653f84f edit files yet again
996 5:9d5af5072dbd edit files again
1187 5:9d5af5072dbd edit files again
997 4:74c02385b94c move files
1188 4:74c02385b94c move files
998 3:9e8fbc4bce62 copy files
1189 3:9e8fbc4bce62 copy files
999 2:51a0ae4d5864 remove files
1190 2:51a0ae4d5864 remove files
1000 1:ce8896473775 edit files
1191 1:ce8896473775 edit files
1001 0:30d30fe6a5be add files
1192 0:30d30fe6a5be add files
1002 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
1193 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
1003 @ 9:598410d3eb9a modify normal file largefile in repo d
1194 @ 9:598410d3eb9a modify normal file largefile in repo d
1004 |
1195 |
1005 o 8:a381d2c8c80e modify normal file and largefile in repo b
1196 o 8:a381d2c8c80e modify normal file and largefile in repo b
1006 |
1197 |
1007 o 7:daea875e9014 add/edit more largefiles
1198 o 7:daea875e9014 add/edit more largefiles
1008 |
1199 |
1009 o 6:4355d653f84f edit files yet again
1200 o 6:4355d653f84f edit files yet again
1010 |
1201 |
1011 o 5:9d5af5072dbd edit files again
1202 o 5:9d5af5072dbd edit files again
1012 |
1203 |
1013 o 4:74c02385b94c move files
1204 o 4:74c02385b94c move files
1014 |
1205 |
1015 o 3:9e8fbc4bce62 copy files
1206 o 3:9e8fbc4bce62 copy files
1016 |
1207 |
1017 o 2:51a0ae4d5864 remove files
1208 o 2:51a0ae4d5864 remove files
1018 |
1209 |
1019 o 1:ce8896473775 edit files
1210 o 1:ce8896473775 edit files
1020 |
1211 |
1021 o 0:30d30fe6a5be add files
1212 o 0:30d30fe6a5be add files
1022
1213
1023 $ cat normal3
1214 $ cat normal3
1024 normal3-modified
1215 normal3-modified
1025 $ cat sub/normal4
1216 $ cat sub/normal4
1026 normal4-modified
1217 normal4-modified
1027 $ cat sub/large4
1218 $ cat sub/large4
1028 large4-modified
1219 large4-modified
1029 $ cat sub2/large6
1220 $ cat sub2/large6
1030 large6-modified
1221 large6-modified
1031 $ cat sub2/large7
1222 $ cat sub2/large7
1032 large7
1223 large7
1033 $ cd ../e
1224 $ cd ../e
1034 $ hg pull ../b
1225 $ hg pull ../b
1035 pulling from ../b
1226 pulling from ../b
1036 searching for changes
1227 searching for changes
1037 adding changesets
1228 adding changesets
1038 adding manifests
1229 adding manifests
1039 adding file changes
1230 adding file changes
1040 added 1 changesets with 2 changes to 2 files (+1 heads)
1231 added 1 changesets with 2 changes to 2 files (+1 heads)
1041 (run 'hg heads' to see heads, 'hg merge' to merge)
1232 (run 'hg heads' to see heads, 'hg merge' to merge)
1042 $ hg rebase
1233 $ hg rebase
1043 Invoking status precommit hook
1234 Invoking status precommit hook
1044 M sub/normal4
1235 M sub/normal4
1045 M sub2/large6
1236 M sub2/large6
1046 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg (glob)
1237 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg (glob)
1047 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1238 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1048 9:598410d3eb9a modify normal file largefile in repo d
1239 9:598410d3eb9a modify normal file largefile in repo d
1049 8:a381d2c8c80e modify normal file and largefile in repo b
1240 8:a381d2c8c80e modify normal file and largefile in repo b
1050 7:daea875e9014 add/edit more largefiles
1241 7:daea875e9014 add/edit more largefiles
1051 6:4355d653f84f edit files yet again
1242 6:4355d653f84f edit files yet again
1052 5:9d5af5072dbd edit files again
1243 5:9d5af5072dbd edit files again
1053 4:74c02385b94c move files
1244 4:74c02385b94c move files
1054 3:9e8fbc4bce62 copy files
1245 3:9e8fbc4bce62 copy files
1055 2:51a0ae4d5864 remove files
1246 2:51a0ae4d5864 remove files
1056 1:ce8896473775 edit files
1247 1:ce8896473775 edit files
1057 0:30d30fe6a5be add files
1248 0:30d30fe6a5be add files
1058 $ cat normal3
1249 $ cat normal3
1059 normal3-modified
1250 normal3-modified
1060 $ cat sub/normal4
1251 $ cat sub/normal4
1061 normal4-modified
1252 normal4-modified
1062 $ cat sub/large4
1253 $ cat sub/large4
1063 large4-modified
1254 large4-modified
1064 $ cat sub2/large6
1255 $ cat sub2/large6
1065 large6-modified
1256 large6-modified
1066 $ cat sub2/large7
1257 $ cat sub2/large7
1067 large7
1258 large7
1068
1259
1069 Log on largefiles
1260 Log on largefiles
1070
1261
1071 - same output
1262 - same output
1072 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1263 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1073 8:a381d2c8c80e modify normal file and largefile in repo b
1264 8:a381d2c8c80e modify normal file and largefile in repo b
1074 6:4355d653f84f edit files yet again
1265 6:4355d653f84f edit files yet again
1075 5:9d5af5072dbd edit files again
1266 5:9d5af5072dbd edit files again
1076 4:74c02385b94c move files
1267 4:74c02385b94c move files
1077 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1268 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1078 o 8:a381d2c8c80e modify normal file and largefile in repo b
1269 o 8:a381d2c8c80e modify normal file and largefile in repo b
1079 |
1270 |
1080 o 6:4355d653f84f edit files yet again
1271 o 6:4355d653f84f edit files yet again
1081 |
1272 |
1082 o 5:9d5af5072dbd edit files again
1273 o 5:9d5af5072dbd edit files again
1083 |
1274 |
1084 o 4:74c02385b94c move files
1275 o 4:74c02385b94c move files
1085 |
1276 |
1086 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub/large4
1277 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub/large4
1087 8:a381d2c8c80e modify normal file and largefile in repo b
1278 8:a381d2c8c80e modify normal file and largefile in repo b
1088 6:4355d653f84f edit files yet again
1279 6:4355d653f84f edit files yet again
1089 5:9d5af5072dbd edit files again
1280 5:9d5af5072dbd edit files again
1090 4:74c02385b94c move files
1281 4:74c02385b94c move files
1091 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1282 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1092 o 8:a381d2c8c80e modify normal file and largefile in repo b
1283 o 8:a381d2c8c80e modify normal file and largefile in repo b
1093 |
1284 |
1094 o 6:4355d653f84f edit files yet again
1285 o 6:4355d653f84f edit files yet again
1095 |
1286 |
1096 o 5:9d5af5072dbd edit files again
1287 o 5:9d5af5072dbd edit files again
1097 |
1288 |
1098 o 4:74c02385b94c move files
1289 o 4:74c02385b94c move files
1099 |
1290 |
1100
1291
1101 - .hglf only matches largefiles, without .hglf it matches 9 bco sub/normal
1292 - .hglf only matches largefiles, without .hglf it matches 9 bco sub/normal
1102 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1293 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1103 8:a381d2c8c80e modify normal file and largefile in repo b
1294 8:a381d2c8c80e modify normal file and largefile in repo b
1104 6:4355d653f84f edit files yet again
1295 6:4355d653f84f edit files yet again
1105 5:9d5af5072dbd edit files again
1296 5:9d5af5072dbd edit files again
1106 4:74c02385b94c move files
1297 4:74c02385b94c move files
1107 1:ce8896473775 edit files
1298 1:ce8896473775 edit files
1108 0:30d30fe6a5be add files
1299 0:30d30fe6a5be add files
1109 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1300 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1110 o 8:a381d2c8c80e modify normal file and largefile in repo b
1301 o 8:a381d2c8c80e modify normal file and largefile in repo b
1111 |
1302 |
1112 o 6:4355d653f84f edit files yet again
1303 o 6:4355d653f84f edit files yet again
1113 |
1304 |
1114 o 5:9d5af5072dbd edit files again
1305 o 5:9d5af5072dbd edit files again
1115 |
1306 |
1116 o 4:74c02385b94c move files
1307 o 4:74c02385b94c move files
1117 |
1308 |
1118 o 1:ce8896473775 edit files
1309 o 1:ce8896473775 edit files
1119 |
1310 |
1120 o 0:30d30fe6a5be add files
1311 o 0:30d30fe6a5be add files
1121
1312
1122 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub
1313 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub
1123 9:598410d3eb9a modify normal file largefile in repo d
1314 9:598410d3eb9a modify normal file largefile in repo d
1124 8:a381d2c8c80e modify normal file and largefile in repo b
1315 8:a381d2c8c80e modify normal file and largefile in repo b
1125 6:4355d653f84f edit files yet again
1316 6:4355d653f84f edit files yet again
1126 5:9d5af5072dbd edit files again
1317 5:9d5af5072dbd edit files again
1127 4:74c02385b94c move files
1318 4:74c02385b94c move files
1128 1:ce8896473775 edit files
1319 1:ce8896473775 edit files
1129 0:30d30fe6a5be add files
1320 0:30d30fe6a5be add files
1130 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' sub
1321 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' sub
1131 @ 9:598410d3eb9a modify normal file largefile in repo d
1322 @ 9:598410d3eb9a modify normal file largefile in repo d
1132 |
1323 |
1133 o 8:a381d2c8c80e modify normal file and largefile in repo b
1324 o 8:a381d2c8c80e modify normal file and largefile in repo b
1134 |
1325 |
1135 o 6:4355d653f84f edit files yet again
1326 o 6:4355d653f84f edit files yet again
1136 |
1327 |
1137 o 5:9d5af5072dbd edit files again
1328 o 5:9d5af5072dbd edit files again
1138 |
1329 |
1139 o 4:74c02385b94c move files
1330 o 4:74c02385b94c move files
1140 |
1331 |
1141 o 1:ce8896473775 edit files
1332 o 1:ce8896473775 edit files
1142 |
1333 |
1143 o 0:30d30fe6a5be add files
1334 o 0:30d30fe6a5be add files
1144
1335
1145 - globbing gives same result
1336 - globbing gives same result
1146 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1337 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1147 9:598410d3eb9a modify normal file largefile in repo d
1338 9:598410d3eb9a modify normal file largefile in repo d
1148 8:a381d2c8c80e modify normal file and largefile in repo b
1339 8:a381d2c8c80e modify normal file and largefile in repo b
1149 6:4355d653f84f edit files yet again
1340 6:4355d653f84f edit files yet again
1150 5:9d5af5072dbd edit files again
1341 5:9d5af5072dbd edit files again
1151 4:74c02385b94c move files
1342 4:74c02385b94c move files
1152 1:ce8896473775 edit files
1343 1:ce8896473775 edit files
1153 0:30d30fe6a5be add files
1344 0:30d30fe6a5be add files
1154 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1345 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1155 @ 9:598410d3eb9a modify normal file largefile in repo d
1346 @ 9:598410d3eb9a modify normal file largefile in repo d
1156 |
1347 |
1157 o 8:a381d2c8c80e modify normal file and largefile in repo b
1348 o 8:a381d2c8c80e modify normal file and largefile in repo b
1158 |
1349 |
1159 o 6:4355d653f84f edit files yet again
1350 o 6:4355d653f84f edit files yet again
1160 |
1351 |
1161 o 5:9d5af5072dbd edit files again
1352 o 5:9d5af5072dbd edit files again
1162 |
1353 |
1163 o 4:74c02385b94c move files
1354 o 4:74c02385b94c move files
1164 |
1355 |
1165 o 1:ce8896473775 edit files
1356 o 1:ce8896473775 edit files
1166 |
1357 |
1167 o 0:30d30fe6a5be add files
1358 o 0:30d30fe6a5be add files
1168
1359
1169 Rollback on largefiles.
1360 Rollback on largefiles.
1170
1361
1171 $ echo large4-modified-again > sub/large4
1362 $ echo large4-modified-again > sub/large4
1172 $ hg commit -m "Modify large4 again"
1363 $ hg commit -m "Modify large4 again"
1173 Invoking status precommit hook
1364 Invoking status precommit hook
1174 M sub/large4
1365 M sub/large4
1175 $ hg rollback
1366 $ hg rollback
1176 repository tip rolled back to revision 9 (undo commit)
1367 repository tip rolled back to revision 9 (undo commit)
1177 working directory now based on revision 9
1368 working directory now based on revision 9
1178 $ hg st
1369 $ hg st
1179 M sub/large4
1370 M sub/large4
1180 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1371 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1181 9:598410d3eb9a modify normal file largefile in repo d
1372 9:598410d3eb9a modify normal file largefile in repo d
1182 8:a381d2c8c80e modify normal file and largefile in repo b
1373 8:a381d2c8c80e modify normal file and largefile in repo b
1183 7:daea875e9014 add/edit more largefiles
1374 7:daea875e9014 add/edit more largefiles
1184 6:4355d653f84f edit files yet again
1375 6:4355d653f84f edit files yet again
1185 5:9d5af5072dbd edit files again
1376 5:9d5af5072dbd edit files again
1186 4:74c02385b94c move files
1377 4:74c02385b94c move files
1187 3:9e8fbc4bce62 copy files
1378 3:9e8fbc4bce62 copy files
1188 2:51a0ae4d5864 remove files
1379 2:51a0ae4d5864 remove files
1189 1:ce8896473775 edit files
1380 1:ce8896473775 edit files
1190 0:30d30fe6a5be add files
1381 0:30d30fe6a5be add files
1191 $ cat sub/large4
1382 $ cat sub/large4
1192 large4-modified-again
1383 large4-modified-again
1193
1384
1194 "update --check" refuses to update with uncommitted changes.
1385 "update --check" refuses to update with uncommitted changes.
1195 $ hg update --check 8
1386 $ hg update --check 8
1196 abort: uncommitted changes
1387 abort: uncommitted changes
1197 [255]
1388 [255]
1198
1389
1199 "update --clean" leaves correct largefiles in working copy, even when there is
1390 "update --clean" leaves correct largefiles in working copy, even when there is
1200 .orig files from revert in .hglf.
1391 .orig files from revert in .hglf.
1201
1392
1202 $ echo mistake > sub2/large7
1393 $ echo mistake > sub2/large7
1203 $ hg revert sub2/large7
1394 $ hg revert sub2/large7
1204 $ cat sub2/large7
1395 $ cat sub2/large7
1205 large7
1396 large7
1206 $ cat sub2/large7.orig
1397 $ cat sub2/large7.orig
1207 mistake
1398 mistake
1208 $ test ! -f .hglf/sub2/large7.orig
1399 $ test ! -f .hglf/sub2/large7.orig
1209
1400
1210 $ hg -q update --clean -r null
1401 $ hg -q update --clean -r null
1211 $ hg update --clean
1402 $ hg update --clean
1212 getting changed largefiles
1403 getting changed largefiles
1213 3 largefiles updated, 0 removed
1404 3 largefiles updated, 0 removed
1214 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1405 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1215 $ cat normal3
1406 $ cat normal3
1216 normal3-modified
1407 normal3-modified
1217 $ cat sub/normal4
1408 $ cat sub/normal4
1218 normal4-modified
1409 normal4-modified
1219 $ cat sub/large4
1410 $ cat sub/large4
1220 large4-modified
1411 large4-modified
1221 $ cat sub2/large6
1412 $ cat sub2/large6
1222 large6-modified
1413 large6-modified
1223 $ cat sub2/large7
1414 $ cat sub2/large7
1224 large7
1415 large7
1225 $ cat sub2/large7.orig
1416 $ cat sub2/large7.orig
1226 mistake
1417 mistake
1227 $ test ! -f .hglf/sub2/large7.orig
1418 $ test ! -f .hglf/sub2/large7.orig
1228
1419
1229 verify that largefile .orig file no longer is overwritten on every update -C:
1420 verify that largefile .orig file no longer is overwritten on every update -C:
1230 $ hg update --clean
1421 $ hg update --clean
1231 getting changed largefiles
1422 getting changed largefiles
1232 0 largefiles updated, 0 removed
1423 0 largefiles updated, 0 removed
1233 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1424 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1234 $ cat sub2/large7.orig
1425 $ cat sub2/large7.orig
1235 mistake
1426 mistake
1236 $ rm sub2/large7.orig
1427 $ rm sub2/large7.orig
1237
1428
1238 Now "update check" is happy.
1429 Now "update check" is happy.
1239 $ hg update --check 8
1430 $ hg update --check 8
1240 getting changed largefiles
1431 getting changed largefiles
1241 1 largefiles updated, 0 removed
1432 1 largefiles updated, 0 removed
1242 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1433 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1243 $ hg update --check
1434 $ hg update --check
1244 getting changed largefiles
1435 getting changed largefiles
1245 1 largefiles updated, 0 removed
1436 1 largefiles updated, 0 removed
1246 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1437 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1247
1438
1248 Test removing empty largefiles directories on update
1439 Test removing empty largefiles directories on update
1249 $ test -d sub2 && echo "sub2 exists"
1440 $ test -d sub2 && echo "sub2 exists"
1250 sub2 exists
1441 sub2 exists
1251 $ hg update -q null
1442 $ hg update -q null
1252 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1443 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1253 [1]
1444 [1]
1254 $ hg update -q
1445 $ hg update -q
1255
1446
1256 Test hg remove removes empty largefiles directories
1447 Test hg remove removes empty largefiles directories
1257 $ test -d sub2 && echo "sub2 exists"
1448 $ test -d sub2 && echo "sub2 exists"
1258 sub2 exists
1449 sub2 exists
1259 $ hg remove sub2/*
1450 $ hg remove sub2/*
1260 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1451 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1261 [1]
1452 [1]
1262 $ hg revert sub2/large6 sub2/large7
1453 $ hg revert sub2/large6 sub2/large7
1263
1454
1264 "revert" works on largefiles (and normal files too).
1455 "revert" works on largefiles (and normal files too).
1265 $ echo hack3 >> normal3
1456 $ echo hack3 >> normal3
1266 $ echo hack4 >> sub/normal4
1457 $ echo hack4 >> sub/normal4
1267 $ echo hack4 >> sub/large4
1458 $ echo hack4 >> sub/large4
1268 $ rm sub2/large6
1459 $ rm sub2/large6
1269 $ hg revert sub2/large6
1460 $ hg revert sub2/large6
1270 $ hg rm sub2/large6
1461 $ hg rm sub2/large6
1271 $ echo new >> sub2/large8
1462 $ echo new >> sub2/large8
1272 $ hg add --large sub2/large8
1463 $ hg add --large sub2/large8
1273 # XXX we don't really want to report that we're reverting the standin;
1464 # XXX we don't really want to report that we're reverting the standin;
1274 # that's just an implementation detail. But I don't see an obvious fix. ;-(
1465 # that's just an implementation detail. But I don't see an obvious fix. ;-(
1275 $ hg revert sub
1466 $ hg revert sub
1276 reverting .hglf/sub/large4 (glob)
1467 reverting .hglf/sub/large4 (glob)
1277 reverting sub/normal4 (glob)
1468 reverting sub/normal4 (glob)
1278 $ hg status
1469 $ hg status
1279 M normal3
1470 M normal3
1280 A sub2/large8
1471 A sub2/large8
1281 R sub2/large6
1472 R sub2/large6
1282 ? sub/large4.orig
1473 ? sub/large4.orig
1283 ? sub/normal4.orig
1474 ? sub/normal4.orig
1284 $ cat sub/normal4
1475 $ cat sub/normal4
1285 normal4-modified
1476 normal4-modified
1286 $ cat sub/large4
1477 $ cat sub/large4
1287 large4-modified
1478 large4-modified
1288 $ hg revert -a --no-backup
1479 $ hg revert -a --no-backup
1289 undeleting .hglf/sub2/large6 (glob)
1480 undeleting .hglf/sub2/large6 (glob)
1290 forgetting .hglf/sub2/large8 (glob)
1481 forgetting .hglf/sub2/large8 (glob)
1291 reverting normal3
1482 reverting normal3
1292 $ hg status
1483 $ hg status
1293 ? sub/large4.orig
1484 ? sub/large4.orig
1294 ? sub/normal4.orig
1485 ? sub/normal4.orig
1295 ? sub2/large8
1486 ? sub2/large8
1296 $ cat normal3
1487 $ cat normal3
1297 normal3-modified
1488 normal3-modified
1298 $ cat sub2/large6
1489 $ cat sub2/large6
1299 large6-modified
1490 large6-modified
1300 $ rm sub/*.orig sub2/large8
1491 $ rm sub/*.orig sub2/large8
1301
1492
1302 revert some files to an older revision
1493 revert some files to an older revision
1303 $ hg revert --no-backup -r 8 sub2
1494 $ hg revert --no-backup -r 8 sub2
1304 reverting .hglf/sub2/large6 (glob)
1495 reverting .hglf/sub2/large6 (glob)
1305 $ cat sub2/large6
1496 $ cat sub2/large6
1306 large6
1497 large6
1307 $ hg revert --no-backup -C -r '.^' sub2
1498 $ hg revert --no-backup -C -r '.^' sub2
1308 reverting .hglf/sub2/large6 (glob)
1499 reverting .hglf/sub2/large6 (glob)
1309 $ hg revert --no-backup sub2
1500 $ hg revert --no-backup sub2
1310 reverting .hglf/sub2/large6 (glob)
1501 reverting .hglf/sub2/large6 (glob)
1311 $ hg status
1502 $ hg status
1312
1503
1313 "verify --large" actually verifies largefiles
1504 "verify --large" actually verifies largefiles
1314
1505
1315 - Where Do We Come From? What Are We? Where Are We Going?
1506 - Where Do We Come From? What Are We? Where Are We Going?
1316 $ pwd
1507 $ pwd
1317 $TESTTMP/e
1508 $TESTTMP/e
1318 $ hg paths
1509 $ hg paths
1319 default = $TESTTMP/d (glob)
1510 default = $TESTTMP/d (glob)
1320
1511
1321 $ hg verify --large
1512 $ hg verify --large
1322 checking changesets
1513 checking changesets
1323 checking manifests
1514 checking manifests
1324 crosschecking files in changesets and manifests
1515 crosschecking files in changesets and manifests
1325 checking files
1516 checking files
1326 10 files, 10 changesets, 28 total revisions
1517 10 files, 10 changesets, 28 total revisions
1327 searching 1 changesets for largefiles
1518 searching 1 changesets for largefiles
1328 verified existence of 3 revisions of 3 largefiles
1519 verified existence of 3 revisions of 3 largefiles
1329
1520
1330 - introduce missing blob in local store repo and make sure that this is caught:
1521 - introduce missing blob in local store repo and make sure that this is caught:
1331 $ mv $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 .
1522 $ mv $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 .
1332 $ hg verify --large
1523 $ hg verify --large
1333 checking changesets
1524 checking changesets
1334 checking manifests
1525 checking manifests
1335 crosschecking files in changesets and manifests
1526 crosschecking files in changesets and manifests
1336 checking files
1527 checking files
1337 10 files, 10 changesets, 28 total revisions
1528 10 files, 10 changesets, 28 total revisions
1338 searching 1 changesets for largefiles
1529 searching 1 changesets for largefiles
1339 changeset 9:598410d3eb9a: sub/large4 references missing $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
1530 changeset 9:598410d3eb9a: sub/large4 references missing $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
1340 verified existence of 3 revisions of 3 largefiles
1531 verified existence of 3 revisions of 3 largefiles
1341 [1]
1532 [1]
1342
1533
1343 - introduce corruption and make sure that it is caught when checking content:
1534 - introduce corruption and make sure that it is caught when checking content:
1344 $ echo '5 cents' > $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1535 $ echo '5 cents' > $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1345 $ hg verify -q --large --lfc
1536 $ hg verify -q --large --lfc
1346 changeset 9:598410d3eb9a: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
1537 changeset 9:598410d3eb9a: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
1347 [1]
1538 [1]
1348
1539
1349 - cleanup
1540 - cleanup
1350 $ mv e166e74c7303192238d60af5a9c4ce9bef0b7928 $TESTTMP/d/.hg/largefiles/
1541 $ mv e166e74c7303192238d60af5a9c4ce9bef0b7928 $TESTTMP/d/.hg/largefiles/
1351
1542
1352 - verifying all revisions will fail because we didn't clone all largefiles to d:
1543 - verifying all revisions will fail because we didn't clone all largefiles to d:
1353 $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1544 $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1354 $ hg verify -q --lfa --lfc
1545 $ hg verify -q --lfa --lfc
1355 changeset 0:30d30fe6a5be: large1 references missing $TESTTMP/d/.hg/largefiles/4669e532d5b2c093a78eca010077e708a071bb64 (glob)
1546 changeset 0:30d30fe6a5be: large1 references missing $TESTTMP/d/.hg/largefiles/4669e532d5b2c093a78eca010077e708a071bb64 (glob)
1356 changeset 0:30d30fe6a5be: sub/large2 references missing $TESTTMP/d/.hg/largefiles/1deebade43c8c498a3c8daddac0244dc55d1331d (glob)
1547 changeset 0:30d30fe6a5be: sub/large2 references missing $TESTTMP/d/.hg/largefiles/1deebade43c8c498a3c8daddac0244dc55d1331d (glob)
1357 changeset 1:ce8896473775: large1 references missing $TESTTMP/d/.hg/largefiles/5f78770c0e77ba4287ad6ef3071c9bf9c379742f (glob)
1548 changeset 1:ce8896473775: large1 references missing $TESTTMP/d/.hg/largefiles/5f78770c0e77ba4287ad6ef3071c9bf9c379742f (glob)
1358 changeset 1:ce8896473775: sub/large2 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1549 changeset 1:ce8896473775: sub/large2 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1359 changeset 3:9e8fbc4bce62: large1 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1550 changeset 3:9e8fbc4bce62: large1 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1360 changeset 4:74c02385b94c: large3 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1551 changeset 4:74c02385b94c: large3 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1361 changeset 4:74c02385b94c: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1552 changeset 4:74c02385b94c: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1362 changeset 5:9d5af5072dbd: large3 references missing $TESTTMP/d/.hg/largefiles/baaf12afde9d8d67f25dab6dced0d2bf77dba47c (glob)
1553 changeset 5:9d5af5072dbd: large3 references missing $TESTTMP/d/.hg/largefiles/baaf12afde9d8d67f25dab6dced0d2bf77dba47c (glob)
1363 changeset 5:9d5af5072dbd: sub/large4 references missing $TESTTMP/d/.hg/largefiles/aeb2210d19f02886dde00dac279729a48471e2f9 (glob)
1554 changeset 5:9d5af5072dbd: sub/large4 references missing $TESTTMP/d/.hg/largefiles/aeb2210d19f02886dde00dac279729a48471e2f9 (glob)
1364 changeset 6:4355d653f84f: large3 references missing $TESTTMP/d/.hg/largefiles/7838695e10da2bb75ac1156565f40a2595fa2fa0 (glob)
1555 changeset 6:4355d653f84f: large3 references missing $TESTTMP/d/.hg/largefiles/7838695e10da2bb75ac1156565f40a2595fa2fa0 (glob)
1365 [1]
1556 [1]
1366
1557
1367 - cleanup
1558 - cleanup
1368 $ rm $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1559 $ rm $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1369 $ rm -f .hglf/sub/*.orig
1560 $ rm -f .hglf/sub/*.orig
1370
1561
1371 Update to revision with missing largefile - and make sure it really is missing
1562 Update to revision with missing largefile - and make sure it really is missing
1372
1563
1373 $ rm ${USERCACHE}/7838695e10da2bb75ac1156565f40a2595fa2fa0
1564 $ rm ${USERCACHE}/7838695e10da2bb75ac1156565f40a2595fa2fa0
1374 $ hg up -r 6
1565 $ hg up -r 6
1375 getting changed largefiles
1566 getting changed largefiles
1376 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1567 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1377 1 largefiles updated, 2 removed
1568 1 largefiles updated, 2 removed
1378 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
1569 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
1379 $ rm normal3
1570 $ rm normal3
1380 $ echo >> sub/normal4
1571 $ echo >> sub/normal4
1381 $ hg ci -m 'commit with missing files'
1572 $ hg ci -m 'commit with missing files'
1382 Invoking status precommit hook
1573 Invoking status precommit hook
1383 M sub/normal4
1574 M sub/normal4
1384 ! large3
1575 ! large3
1385 ! normal3
1576 ! normal3
1386 created new head
1577 created new head
1387 $ hg st
1578 $ hg st
1388 ! large3
1579 ! large3
1389 ! normal3
1580 ! normal3
1390 $ hg up -r.
1581 $ hg up -r.
1391 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1582 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1392 $ hg st
1583 $ hg st
1393 ! large3
1584 ! large3
1394 ! normal3
1585 ! normal3
1395 $ hg up -Cr.
1586 $ hg up -Cr.
1396 getting changed largefiles
1587 getting changed largefiles
1397 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1588 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1398 0 largefiles updated, 0 removed
1589 0 largefiles updated, 0 removed
1399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1590 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1400 $ hg st
1591 $ hg st
1401 ! large3
1592 ! large3
1402 $ hg rollback
1593 $ hg rollback
1403 repository tip rolled back to revision 9 (undo commit)
1594 repository tip rolled back to revision 9 (undo commit)
1404 working directory now based on revision 6
1595 working directory now based on revision 6
1405
1596
1406 Merge with revision with missing largefile - and make sure it tries to fetch it.
1597 Merge with revision with missing largefile - and make sure it tries to fetch it.
1407
1598
1408 $ hg up -Cqr null
1599 $ hg up -Cqr null
1409 $ echo f > f
1600 $ echo f > f
1410 $ hg ci -Am branch
1601 $ hg ci -Am branch
1411 adding f
1602 adding f
1412 Invoking status precommit hook
1603 Invoking status precommit hook
1413 A f
1604 A f
1414 created new head
1605 created new head
1415 $ hg merge -r 6
1606 $ hg merge -r 6
1416 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1607 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1417 (branch merge, don't forget to commit)
1608 (branch merge, don't forget to commit)
1418 getting changed largefiles
1609 getting changed largefiles
1419 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1610 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1420 1 largefiles updated, 0 removed
1611 1 largefiles updated, 0 removed
1421
1612
1422 $ hg rollback -q
1613 $ hg rollback -q
1423 $ hg up -Cq
1614 $ hg up -Cq
1424
1615
1425 Pulling 0 revisions with --all-largefiles should not fetch for all revisions
1616 Pulling 0 revisions with --all-largefiles should not fetch for all revisions
1426
1617
1427 $ hg pull --all-largefiles
1618 $ hg pull --all-largefiles
1428 pulling from $TESTTMP/d (glob)
1619 pulling from $TESTTMP/d (glob)
1429 searching for changes
1620 searching for changes
1430 no changes found
1621 no changes found
1431
1622
1432 Merging does not revert to old versions of largefiles and also check
1623 Merging does not revert to old versions of largefiles and also check
1433 that merging after having pulled from a non-default remote works
1624 that merging after having pulled from a non-default remote works
1434 correctly.
1625 correctly.
1435
1626
1436 $ cd ..
1627 $ cd ..
1437 $ hg clone -r 7 e temp
1628 $ hg clone -r 7 e temp
1438 adding changesets
1629 adding changesets
1439 adding manifests
1630 adding manifests
1440 adding file changes
1631 adding file changes
1441 added 8 changesets with 24 changes to 10 files
1632 added 8 changesets with 24 changes to 10 files
1442 updating to branch default
1633 updating to branch default
1443 getting changed largefiles
1634 getting changed largefiles
1444 3 largefiles updated, 0 removed
1635 3 largefiles updated, 0 removed
1445 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1636 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1446 $ hg clone temp f
1637 $ hg clone temp f
1447 updating to branch default
1638 updating to branch default
1448 getting changed largefiles
1639 getting changed largefiles
1449 3 largefiles updated, 0 removed
1640 3 largefiles updated, 0 removed
1450 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1641 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1451 # Delete the largefiles in the largefiles system cache so that we have an
1642 # Delete the largefiles in the largefiles system cache so that we have an
1452 # opportunity to test that caching after a pull works.
1643 # opportunity to test that caching after a pull works.
1453 $ rm "${USERCACHE}"/*
1644 $ rm "${USERCACHE}"/*
1454 $ cd f
1645 $ cd f
1455 $ echo "large4-merge-test" > sub/large4
1646 $ echo "large4-merge-test" > sub/large4
1456 $ hg commit -m "Modify large4 to test merge"
1647 $ hg commit -m "Modify large4 to test merge"
1457 Invoking status precommit hook
1648 Invoking status precommit hook
1458 M sub/large4
1649 M sub/large4
1459 # Test --cache-largefiles flag
1650 # Test --cache-largefiles flag
1460 $ hg pull --lfrev 'heads(pulled())' ../e
1651 $ hg pull --lfrev 'heads(pulled())' ../e
1461 pulling from ../e
1652 pulling from ../e
1462 searching for changes
1653 searching for changes
1463 adding changesets
1654 adding changesets
1464 adding manifests
1655 adding manifests
1465 adding file changes
1656 adding file changes
1466 added 2 changesets with 4 changes to 4 files (+1 heads)
1657 added 2 changesets with 4 changes to 4 files (+1 heads)
1467 (run 'hg heads' to see heads, 'hg merge' to merge)
1658 (run 'hg heads' to see heads, 'hg merge' to merge)
1468 2 largefiles cached
1659 2 largefiles cached
1469 $ hg merge
1660 $ hg merge
1470 largefile sub/large4 has a merge conflict
1661 largefile sub/large4 has a merge conflict
1471 ancestor was 971fb41e78fea4f8e0ba5244784239371cb00591
1662 ancestor was 971fb41e78fea4f8e0ba5244784239371cb00591
1472 keep (l)ocal d846f26643bfa8ec210be40cc93cc6b7ff1128ea or
1663 keep (l)ocal d846f26643bfa8ec210be40cc93cc6b7ff1128ea or
1473 take (o)ther e166e74c7303192238d60af5a9c4ce9bef0b7928? l
1664 take (o)ther e166e74c7303192238d60af5a9c4ce9bef0b7928? l
1474 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
1665 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
1475 (branch merge, don't forget to commit)
1666 (branch merge, don't forget to commit)
1476 getting changed largefiles
1667 getting changed largefiles
1477 1 largefiles updated, 0 removed
1668 1 largefiles updated, 0 removed
1478 $ hg commit -m "Merge repos e and f"
1669 $ hg commit -m "Merge repos e and f"
1479 Invoking status precommit hook
1670 Invoking status precommit hook
1480 M normal3
1671 M normal3
1481 M sub/normal4
1672 M sub/normal4
1482 M sub2/large6
1673 M sub2/large6
1483 $ cat normal3
1674 $ cat normal3
1484 normal3-modified
1675 normal3-modified
1485 $ cat sub/normal4
1676 $ cat sub/normal4
1486 normal4-modified
1677 normal4-modified
1487 $ cat sub/large4
1678 $ cat sub/large4
1488 large4-merge-test
1679 large4-merge-test
1489 $ cat sub2/large6
1680 $ cat sub2/large6
1490 large6-modified
1681 large6-modified
1491 $ cat sub2/large7
1682 $ cat sub2/large7
1492 large7
1683 large7
1493
1684
1494 Test status after merging with a branch that introduces a new largefile:
1685 Test status after merging with a branch that introduces a new largefile:
1495
1686
1496 $ echo large > large
1687 $ echo large > large
1497 $ hg add --large large
1688 $ hg add --large large
1498 $ hg commit -m 'add largefile'
1689 $ hg commit -m 'add largefile'
1499 Invoking status precommit hook
1690 Invoking status precommit hook
1500 A large
1691 A large
1501 $ hg update -q ".^"
1692 $ hg update -q ".^"
1502 $ echo change >> normal3
1693 $ echo change >> normal3
1503 $ hg commit -m 'some change'
1694 $ hg commit -m 'some change'
1504 Invoking status precommit hook
1695 Invoking status precommit hook
1505 M normal3
1696 M normal3
1506 created new head
1697 created new head
1507 $ hg merge
1698 $ hg merge
1508 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1699 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1509 (branch merge, don't forget to commit)
1700 (branch merge, don't forget to commit)
1510 getting changed largefiles
1701 getting changed largefiles
1511 1 largefiles updated, 0 removed
1702 1 largefiles updated, 0 removed
1512 $ hg status
1703 $ hg status
1513 M large
1704 M large
1514
1705
1515 - make sure update of merge with removed largefiles fails as expected
1706 - make sure update of merge with removed largefiles fails as expected
1516 $ hg rm sub2/large6
1707 $ hg rm sub2/large6
1517 $ hg up -r.
1708 $ hg up -r.
1518 abort: outstanding uncommitted merges
1709 abort: outstanding uncommitted merges
1519 [255]
1710 [255]
1520
1711
1521 - revert should be able to revert files introduced in a pending merge
1712 - revert should be able to revert files introduced in a pending merge
1522 $ hg revert --all -r .
1713 $ hg revert --all -r .
1523 removing .hglf/large (glob)
1714 removing .hglf/large (glob)
1524 undeleting .hglf/sub2/large6 (glob)
1715 undeleting .hglf/sub2/large6 (glob)
1525
1716
1526 Test that a normal file and a largefile with the same name and path cannot
1717 Test that a normal file and a largefile with the same name and path cannot
1527 coexist.
1718 coexist.
1528
1719
1529 $ rm sub2/large7
1720 $ rm sub2/large7
1530 $ echo "largeasnormal" > sub2/large7
1721 $ echo "largeasnormal" > sub2/large7
1531 $ hg add sub2/large7
1722 $ hg add sub2/large7
1532 sub2/large7 already a largefile
1723 sub2/large7 already a largefile
1533
1724
1534 Test that transplanting a largefile change works correctly.
1725 Test that transplanting a largefile change works correctly.
1535
1726
1536 $ cd ..
1727 $ cd ..
1537 $ hg clone -r 8 d g
1728 $ hg clone -r 8 d g
1538 adding changesets
1729 adding changesets
1539 adding manifests
1730 adding manifests
1540 adding file changes
1731 adding file changes
1541 added 9 changesets with 26 changes to 10 files
1732 added 9 changesets with 26 changes to 10 files
1542 updating to branch default
1733 updating to branch default
1543 getting changed largefiles
1734 getting changed largefiles
1544 3 largefiles updated, 0 removed
1735 3 largefiles updated, 0 removed
1545 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1736 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1546 $ cd g
1737 $ cd g
1547 $ hg transplant -s ../d 598410d3eb9a
1738 $ hg transplant -s ../d 598410d3eb9a
1548 searching for changes
1739 searching for changes
1549 searching for changes
1740 searching for changes
1550 adding changesets
1741 adding changesets
1551 adding manifests
1742 adding manifests
1552 adding file changes
1743 adding file changes
1553 added 1 changesets with 2 changes to 2 files
1744 added 1 changesets with 2 changes to 2 files
1554 getting changed largefiles
1745 getting changed largefiles
1555 1 largefiles updated, 0 removed
1746 1 largefiles updated, 0 removed
1556 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1747 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1557 9:598410d3eb9a modify normal file largefile in repo d
1748 9:598410d3eb9a modify normal file largefile in repo d
1558 8:a381d2c8c80e modify normal file and largefile in repo b
1749 8:a381d2c8c80e modify normal file and largefile in repo b
1559 7:daea875e9014 add/edit more largefiles
1750 7:daea875e9014 add/edit more largefiles
1560 6:4355d653f84f edit files yet again
1751 6:4355d653f84f edit files yet again
1561 5:9d5af5072dbd edit files again
1752 5:9d5af5072dbd edit files again
1562 4:74c02385b94c move files
1753 4:74c02385b94c move files
1563 3:9e8fbc4bce62 copy files
1754 3:9e8fbc4bce62 copy files
1564 2:51a0ae4d5864 remove files
1755 2:51a0ae4d5864 remove files
1565 1:ce8896473775 edit files
1756 1:ce8896473775 edit files
1566 0:30d30fe6a5be add files
1757 0:30d30fe6a5be add files
1567 $ cat normal3
1758 $ cat normal3
1568 normal3-modified
1759 normal3-modified
1569 $ cat sub/normal4
1760 $ cat sub/normal4
1570 normal4-modified
1761 normal4-modified
1571 $ cat sub/large4
1762 $ cat sub/large4
1572 large4-modified
1763 large4-modified
1573 $ cat sub2/large6
1764 $ cat sub2/large6
1574 large6-modified
1765 large6-modified
1575 $ cat sub2/large7
1766 $ cat sub2/large7
1576 large7
1767 large7
1577
1768
1578 Cat a largefile
1769 Cat a largefile
1579 $ hg cat normal3
1770 $ hg cat normal3
1580 normal3-modified
1771 normal3-modified
1581 $ hg cat sub/large4
1772 $ hg cat sub/large4
1582 large4-modified
1773 large4-modified
1583 $ rm "${USERCACHE}"/*
1774 $ rm "${USERCACHE}"/*
1584 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
1775 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
1585 $ cat cat.out
1776 $ cat cat.out
1586 large4-modified
1777 large4-modified
1587 $ rm cat.out
1778 $ rm cat.out
1588 $ hg cat -r a381d2c8c80e normal3
1779 $ hg cat -r a381d2c8c80e normal3
1589 normal3-modified
1780 normal3-modified
1590 $ hg cat -r '.^' normal3
1781 $ hg cat -r '.^' normal3
1591 normal3-modified
1782 normal3-modified
1592 $ hg cat -r '.^' sub/large4 doesntexist
1783 $ hg cat -r '.^' sub/large4 doesntexist
1593 large4-modified
1784 large4-modified
1594 doesntexist: no such file in rev a381d2c8c80e
1785 doesntexist: no such file in rev a381d2c8c80e
1595 $ hg --cwd sub cat -r '.^' large4
1786 $ hg --cwd sub cat -r '.^' large4
1596 large4-modified
1787 large4-modified
1597 $ hg --cwd sub cat -r '.^' ../normal3
1788 $ hg --cwd sub cat -r '.^' ../normal3
1598 normal3-modified
1789 normal3-modified
1599 Cat a standin
1790 Cat a standin
1600 $ hg cat .hglf/sub/large4
1791 $ hg cat .hglf/sub/large4
1601 e166e74c7303192238d60af5a9c4ce9bef0b7928
1792 e166e74c7303192238d60af5a9c4ce9bef0b7928
1602 $ hg cat .hglf/normal3
1793 $ hg cat .hglf/normal3
1603 .hglf/normal3: no such file in rev 598410d3eb9a
1794 .hglf/normal3: no such file in rev 598410d3eb9a
1604 [1]
1795 [1]
1605
1796
1606 Test that renaming a largefile results in correct output for status
1797 Test that renaming a largefile results in correct output for status
1607
1798
1608 $ hg rename sub/large4 large4-renamed
1799 $ hg rename sub/large4 large4-renamed
1609 $ hg commit -m "test rename output"
1800 $ hg commit -m "test rename output"
1610 Invoking status precommit hook
1801 Invoking status precommit hook
1611 A large4-renamed
1802 A large4-renamed
1612 R sub/large4
1803 R sub/large4
1613 $ cat large4-renamed
1804 $ cat large4-renamed
1614 large4-modified
1805 large4-modified
1615 $ cd sub2
1806 $ cd sub2
1616 $ hg rename large6 large6-renamed
1807 $ hg rename large6 large6-renamed
1617 $ hg st
1808 $ hg st
1618 A sub2/large6-renamed
1809 A sub2/large6-renamed
1619 R sub2/large6
1810 R sub2/large6
1620 $ cd ..
1811 $ cd ..
1621
1812
1622 Test --normal flag
1813 Test --normal flag
1623
1814
1624 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
1815 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
1625 $ hg add --normal --large new-largefile
1816 $ hg add --normal --large new-largefile
1626 abort: --normal cannot be used with --large
1817 abort: --normal cannot be used with --large
1627 [255]
1818 [255]
1628 $ hg add --normal new-largefile
1819 $ hg add --normal new-largefile
1629 new-largefile: up to 69 MB of RAM may be required to manage this file
1820 new-largefile: up to 69 MB of RAM may be required to manage this file
1630 (use 'hg revert new-largefile' to cancel the pending addition)
1821 (use 'hg revert new-largefile' to cancel the pending addition)
1631 $ cd ..
1822 $ cd ..
1632
1823
1633
1824
1634
1825
@@ -1,359 +1,359 b''
1 This file tests the behavior of run-tests.py itself.
1 This file tests the behavior of run-tests.py itself.
2
2
3 Smoke test
3 Smoke test
4 ============
4 ============
5
5
6 $ $TESTDIR/run-tests.py
6 $ $TESTDIR/run-tests.py
7
7
8 # Ran 0 tests, 0 skipped, 0 warned, 0 failed.
8 # Ran 0 tests, 0 skipped, 0 warned, 0 failed.
9
9
10 a succesful test
10 a succesful test
11 =======================
11 =======================
12
12
13 $ cat > test-success.t << EOF
13 $ cat > test-success.t << EOF
14 > $ echo babar
14 > $ echo babar
15 > babar
15 > babar
16 > $ echo xyzzy
16 > $ echo xyzzy
17 > xyzzy
17 > xyzzy
18 > EOF
18 > EOF
19
19
20 $ $TESTDIR/run-tests.py --with-hg=`which hg`
20 $ $TESTDIR/run-tests.py --with-hg=`which hg`
21 .
21 .
22 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
22 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
23
23
24 failing test
24 failing test
25 ==================
25 ==================
26
26
27 $ cat > test-failure.t << EOF
27 $ cat > test-failure.t << EOF
28 > $ echo babar
28 > $ echo babar
29 > rataxes
29 > rataxes
30 > This is a noop statement so that
30 > This is a noop statement so that
31 > this test is still more bytes than success.
31 > this test is still more bytes than success.
32 > EOF
32 > EOF
33
33
34 $ $TESTDIR/run-tests.py --with-hg=`which hg`
34 $ $TESTDIR/run-tests.py --with-hg=`which hg`
35
35
36 --- $TESTTMP/test-failure.t
36 --- $TESTTMP/test-failure.t (glob)
37 +++ $TESTTMP/test-failure.t.err
37 +++ $TESTTMP/test-failure.t.err (glob)
38 @@ -1,4 +1,4 @@
38 @@ -1,4 +1,4 @@
39 $ echo babar
39 $ echo babar
40 - rataxes
40 - rataxes
41 + babar
41 + babar
42 This is a noop statement so that
42 This is a noop statement so that
43 this test is still more bytes than success.
43 this test is still more bytes than success.
44
44
45 ERROR: test-failure.t output changed
45 ERROR: test-failure.t output changed
46 !.
46 !.
47 Failed test-failure.t: output changed
47 Failed test-failure.t: output changed
48 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
48 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
49 python hash seed: * (glob)
49 python hash seed: * (glob)
50 [1]
50 [1]
51 test --xunit support
51 test --xunit support
52 $ $TESTDIR/run-tests.py --with-hg=`which hg` --xunit=xunit.xml
52 $ $TESTDIR/run-tests.py --with-hg=`which hg` --xunit=xunit.xml
53
53
54 --- $TESTTMP/test-failure.t
54 --- $TESTTMP/test-failure.t
55 +++ $TESTTMP/test-failure.t.err
55 +++ $TESTTMP/test-failure.t.err
56 @@ -1,4 +1,4 @@
56 @@ -1,4 +1,4 @@
57 $ echo babar
57 $ echo babar
58 - rataxes
58 - rataxes
59 + babar
59 + babar
60 This is a noop statement so that
60 This is a noop statement so that
61 this test is still more bytes than success.
61 this test is still more bytes than success.
62
62
63 ERROR: test-failure.t output changed
63 ERROR: test-failure.t output changed
64 !.
64 !.
65 Failed test-failure.t: output changed
65 Failed test-failure.t: output changed
66 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
66 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
67 python hash seed: * (glob)
67 python hash seed: * (glob)
68 [1]
68 [1]
69 $ cat xunit.xml
69 $ cat xunit.xml
70 <?xml version="1.0" encoding="utf-8"?>
70 <?xml version="1.0" encoding="utf-8"?>
71 <testsuite errors="0" failures="1" name="run-tests" skipped="0" tests="2">
71 <testsuite errors="0" failures="1" name="run-tests" skipped="0" tests="2">
72 <testcase name="test-success.t" time="*"/> (glob)
72 <testcase name="test-success.t" time="*"/> (glob)
73 <testcase name="test-failure.t" time="*"> (glob)
73 <testcase name="test-failure.t" time="*"> (glob)
74 <![CDATA[--- $TESTTMP/test-failure.t
74 <![CDATA[--- $TESTTMP/test-failure.t
75 +++ $TESTTMP/test-failure.t.err
75 +++ $TESTTMP/test-failure.t.err
76 @@ -1,4 +1,4 @@
76 @@ -1,4 +1,4 @@
77 $ echo babar
77 $ echo babar
78 - rataxes
78 - rataxes
79 + babar
79 + babar
80 This is a noop statement so that
80 This is a noop statement so that
81 this test is still more bytes than success.
81 this test is still more bytes than success.
82 ]]> </testcase>
82 ]]> </testcase>
83 </testsuite>
83 </testsuite>
84
84
85 test for --retest
85 test for --retest
86 ====================
86 ====================
87
87
88 $ $TESTDIR/run-tests.py --with-hg=`which hg` --retest
88 $ $TESTDIR/run-tests.py --with-hg=`which hg` --retest
89
89
90 --- $TESTTMP/test-failure.t
90 --- $TESTTMP/test-failure.t (glob)
91 +++ $TESTTMP/test-failure.t.err
91 +++ $TESTTMP/test-failure.t.err (glob)
92 @@ -1,4 +1,4 @@
92 @@ -1,4 +1,4 @@
93 $ echo babar
93 $ echo babar
94 - rataxes
94 - rataxes
95 + babar
95 + babar
96 This is a noop statement so that
96 This is a noop statement so that
97 this test is still more bytes than success.
97 this test is still more bytes than success.
98
98
99 ERROR: test-failure.t output changed
99 ERROR: test-failure.t output changed
100 !
100 !
101 Failed test-failure.t: output changed
101 Failed test-failure.t: output changed
102 # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
102 # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
103 python hash seed: * (glob)
103 python hash seed: * (glob)
104 [1]
104 [1]
105
105
106 Selecting Tests To Run
106 Selecting Tests To Run
107 ======================
107 ======================
108
108
109 successful
109 successful
110
110
111 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t
111 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t
112 .
112 .
113 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
113 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
114
114
115 success w/ keyword
115 success w/ keyword
116 $ $TESTDIR/run-tests.py --with-hg=`which hg` -k xyzzy
116 $ $TESTDIR/run-tests.py --with-hg=`which hg` -k xyzzy
117 .
117 .
118 # Ran 2 tests, 1 skipped, 0 warned, 0 failed.
118 # Ran 2 tests, 1 skipped, 0 warned, 0 failed.
119
119
120 failed
120 failed
121
121
122 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-failure.t
122 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-failure.t
123
123
124 --- $TESTTMP/test-failure.t
124 --- $TESTTMP/test-failure.t (glob)
125 +++ $TESTTMP/test-failure.t.err
125 +++ $TESTTMP/test-failure.t.err (glob)
126 @@ -1,4 +1,4 @@
126 @@ -1,4 +1,4 @@
127 $ echo babar
127 $ echo babar
128 - rataxes
128 - rataxes
129 + babar
129 + babar
130 This is a noop statement so that
130 This is a noop statement so that
131 this test is still more bytes than success.
131 this test is still more bytes than success.
132
132
133 ERROR: test-failure.t output changed
133 ERROR: test-failure.t output changed
134 !
134 !
135 Failed test-failure.t: output changed
135 Failed test-failure.t: output changed
136 # Ran 1 tests, 0 skipped, 0 warned, 1 failed.
136 # Ran 1 tests, 0 skipped, 0 warned, 1 failed.
137 python hash seed: * (glob)
137 python hash seed: * (glob)
138 [1]
138 [1]
139
139
140 failure w/ keyword
140 failure w/ keyword
141 $ $TESTDIR/run-tests.py --with-hg=`which hg` -k rataxes
141 $ $TESTDIR/run-tests.py --with-hg=`which hg` -k rataxes
142
142
143 --- $TESTTMP/test-failure.t
143 --- $TESTTMP/test-failure.t
144 +++ $TESTTMP/test-failure.t.err
144 +++ $TESTTMP/test-failure.t.err
145 @@ -1,4 +1,4 @@
145 @@ -1,4 +1,4 @@
146 $ echo babar
146 $ echo babar
147 - rataxes
147 - rataxes
148 + babar
148 + babar
149 This is a noop statement so that
149 This is a noop statement so that
150 this test is still more bytes than success.
150 this test is still more bytes than success.
151
151
152 ERROR: test-failure.t output changed
152 ERROR: test-failure.t output changed
153 !
153 !
154 Failed test-failure.t: output changed
154 Failed test-failure.t: output changed
155 # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
155 # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
156 python hash seed: * (glob)
156 python hash seed: * (glob)
157 [1]
157 [1]
158
158
159 Running In Debug Mode
159 Running In Debug Mode
160 ======================
160 ======================
161
161
162 $ $TESTDIR/run-tests.py --with-hg=`which hg` --debug 2>&1 | grep -v pwd
162 $ $TESTDIR/run-tests.py --with-hg=`which hg` --debug 2>&1 | grep -v pwd
163 + echo SALT* 0 0 (glob)
163 + echo SALT* 0 0 (glob)
164 SALT* 0 0 (glob)
164 SALT* 0 0 (glob)
165 + echo babar
165 + echo babar
166 babar
166 babar
167 + echo SALT* 4 0 (glob)
167 + echo SALT* 4 0 (glob)
168 SALT* 4 0 (glob)
168 SALT* 4 0 (glob)
169 .+ echo SALT* 0 0 (glob)
169 .+ echo SALT* 0 0 (glob)
170 SALT* 0 0 (glob)
170 SALT* 0 0 (glob)
171 + echo babar
171 + echo babar
172 babar
172 babar
173 + echo SALT* 2 0 (glob)
173 + echo SALT* 2 0 (glob)
174 SALT* 2 0 (glob)
174 SALT* 2 0 (glob)
175 + echo xyzzy
175 + echo xyzzy
176 xyzzy
176 xyzzy
177 + echo SALT* 4 0 (glob)
177 + echo SALT* 4 0 (glob)
178 SALT* 4 0 (glob)
178 SALT* 4 0 (glob)
179 .
179 .
180 # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
180 # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
181
181
182 Parallel runs
182 Parallel runs
183 ==============
183 ==============
184
184
185 (duplicate the failing test to get predictable output)
185 (duplicate the failing test to get predictable output)
186 $ cp test-failure.t test-failure-copy.t
186 $ cp test-failure.t test-failure-copy.t
187
187
188 $ $TESTDIR/run-tests.py --with-hg=`which hg` --jobs 2 test-failure*.t
188 $ $TESTDIR/run-tests.py --with-hg=`which hg` --jobs 2 test-failure*.t
189
189
190 --- $TESTTMP/test-failure*.t (glob)
190 --- $TESTTMP/test-failure*.t (glob)
191 +++ $TESTTMP/test-failure*.t.err (glob)
191 +++ $TESTTMP/test-failure*.t.err (glob)
192 @@ -1,4 +1,4 @@
192 @@ -1,4 +1,4 @@
193 $ echo babar
193 $ echo babar
194 - rataxes
194 - rataxes
195 + babar
195 + babar
196 This is a noop statement so that
196 This is a noop statement so that
197 this test is still more bytes than success.
197 this test is still more bytes than success.
198
198
199 ERROR: test-failure*.t output changed (glob)
199 ERROR: test-failure*.t output changed (glob)
200 !
200 !
201 --- $TESTTMP/test-failure*.t (glob)
201 --- $TESTTMP/test-failure*.t (glob)
202 +++ $TESTTMP/test-failure*.t.err (glob)
202 +++ $TESTTMP/test-failure*.t.err (glob)
203 @@ -1,4 +1,4 @@
203 @@ -1,4 +1,4 @@
204 $ echo babar
204 $ echo babar
205 - rataxes
205 - rataxes
206 + babar
206 + babar
207 This is a noop statement so that
207 This is a noop statement so that
208 this test is still more bytes than success.
208 this test is still more bytes than success.
209
209
210 ERROR: test-failure*.t output changed (glob)
210 ERROR: test-failure*.t output changed (glob)
211 !
211 !
212 Failed test-failure*.t: output changed (glob)
212 Failed test-failure*.t: output changed (glob)
213 Failed test-failure*.t: output changed (glob)
213 Failed test-failure*.t: output changed (glob)
214 # Ran 2 tests, 0 skipped, 0 warned, 2 failed.
214 # Ran 2 tests, 0 skipped, 0 warned, 2 failed.
215 python hash seed: * (glob)
215 python hash seed: * (glob)
216 [1]
216 [1]
217
217
218 (delete the duplicated test file)
218 (delete the duplicated test file)
219 $ rm test-failure-copy.t
219 $ rm test-failure-copy.t
220
220
221
221
222 Interactive run
222 Interactive run
223 ===============
223 ===============
224
224
225 (backup the failing test)
225 (backup the failing test)
226 $ cp test-failure.t backup
226 $ cp test-failure.t backup
227
227
228 Refuse the fix
228 Refuse the fix
229
229
230 $ echo 'n' | $TESTDIR/run-tests.py --with-hg=`which hg` -i
230 $ echo 'n' | $TESTDIR/run-tests.py --with-hg=`which hg` -i
231
231
232 --- $TESTTMP/test-failure.t
232 --- $TESTTMP/test-failure.t
233 +++ $TESTTMP/test-failure.t.err
233 +++ $TESTTMP/test-failure.t.err
234 @@ -1,4 +1,4 @@
234 @@ -1,4 +1,4 @@
235 $ echo babar
235 $ echo babar
236 - rataxes
236 - rataxes
237 + babar
237 + babar
238 This is a noop statement so that
238 This is a noop statement so that
239 this test is still more bytes than success.
239 this test is still more bytes than success.
240 Accept this change? [n]
240 Accept this change? [n]
241 ERROR: test-failure.t output changed
241 ERROR: test-failure.t output changed
242 !.
242 !.
243 Failed test-failure.t: output changed
243 Failed test-failure.t: output changed
244 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
244 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
245 python hash seed: * (glob)
245 python hash seed: * (glob)
246 [1]
246 [1]
247
247
248 $ cat test-failure.t
248 $ cat test-failure.t
249 $ echo babar
249 $ echo babar
250 rataxes
250 rataxes
251 This is a noop statement so that
251 This is a noop statement so that
252 this test is still more bytes than success.
252 this test is still more bytes than success.
253
253
254 View the fix
254 View the fix
255
255
256 $ echo 'y' | $TESTDIR/run-tests.py --with-hg=`which hg` --view echo
256 $ echo 'y' | $TESTDIR/run-tests.py --with-hg=`which hg` --view echo
257 $TESTTMP/test-failure.t $TESTTMP/test-failure.t.err
257 $TESTTMP/test-failure.t $TESTTMP/test-failure.t.err
258
258
259 ERROR: test-failure.t output changed
259 ERROR: test-failure.t output changed
260 !.
260 !.
261 Failed test-failure.t: output changed
261 Failed test-failure.t: output changed
262 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
262 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
263 python hash seed: * (glob)
263 python hash seed: * (glob)
264 [1]
264 [1]
265
265
266 Accept the fix
266 Accept the fix
267
267
268 $ echo 'y' | $TESTDIR/run-tests.py --with-hg=`which hg` -i
268 $ echo 'y' | $TESTDIR/run-tests.py --with-hg=`which hg` -i
269
269
270 --- $TESTTMP/test-failure.t
270 --- $TESTTMP/test-failure.t
271 +++ $TESTTMP/test-failure.t.err
271 +++ $TESTTMP/test-failure.t.err
272 @@ -1,4 +1,4 @@
272 @@ -1,4 +1,4 @@
273 $ echo babar
273 $ echo babar
274 - rataxes
274 - rataxes
275 + babar
275 + babar
276 This is a noop statement so that
276 This is a noop statement so that
277 this test is still more bytes than success.
277 this test is still more bytes than success.
278 Accept this change? [n] ..
278 Accept this change? [n] ..
279 # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
279 # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
280
280
281 $ cat test-failure.t
281 $ cat test-failure.t
282 $ echo babar
282 $ echo babar
283 babar
283 babar
284 This is a noop statement so that
284 This is a noop statement so that
285 this test is still more bytes than success.
285 this test is still more bytes than success.
286
286
287 (reinstall)
287 (reinstall)
288 $ mv backup test-failure.t
288 $ mv backup test-failure.t
289
289
290 No Diff
290 No Diff
291 ===============
291 ===============
292
292
293 $ $TESTDIR/run-tests.py --with-hg=`which hg` --nodiff
293 $ $TESTDIR/run-tests.py --with-hg=`which hg` --nodiff
294 !.
294 !.
295 Failed test-failure.t: output changed
295 Failed test-failure.t: output changed
296 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
296 # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
297 python hash seed: * (glob)
297 python hash seed: * (glob)
298 [1]
298 [1]
299
299
300 test for --time
300 test for --time
301 ==================
301 ==================
302
302
303 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t --time
303 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t --time
304 .
304 .
305 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
305 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
306 # Producing time report
306 # Producing time report
307 cuser csys real Test
307 cuser csys real Test
308 \s*[\d\.]{5} \s*[\d\.]{5} \s*[\d\.]{5} test-success.t (re)
308 \s*[\d\.]{5} \s*[\d\.]{5} \s*[\d\.]{5} test-success.t (re)
309
309
310 test for --time with --job enabled
310 test for --time with --job enabled
311 ====================================
311 ====================================
312
312
313 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t --time --jobs 2
313 $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t --time --jobs 2
314 .
314 .
315 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
315 # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
316 # Producing time report
316 # Producing time report
317 cuser csys real Test
317 cuser csys real Test
318 \s*[\d\.]{5} \s*[\d\.]{5} \s*[\d\.]{5} test-success.t (re)
318 \s*[\d\.]{5} \s*[\d\.]{5} \s*[\d\.]{5} test-success.t (re)
319
319
320 Skips
320 Skips
321 ================
321 ================
322 $ cat > test-skip.t <<EOF
322 $ cat > test-skip.t <<EOF
323 > $ echo xyzzy
323 > $ echo xyzzy
324 > #require false
324 > #require false
325 > EOF
325 > EOF
326 $ $TESTDIR/run-tests.py --with-hg=`which hg` --nodiff
326 $ $TESTDIR/run-tests.py --with-hg=`which hg` --nodiff
327 !.s
327 !.s
328 Skipped test-skip.t: irrelevant
328 Skipped test-skip.t: irrelevant
329 Failed test-failure.t: output changed
329 Failed test-failure.t: output changed
330 # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
330 # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
331 python hash seed: * (glob)
331 python hash seed: * (glob)
332 [1]
332 [1]
333
333
334 $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy
334 $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy
335 .s
335 .s
336 Skipped test-skip.t: irrelevant
336 Skipped test-skip.t: irrelevant
337 # Ran 2 tests, 2 skipped, 0 warned, 0 failed.
337 # Ran 2 tests, 2 skipped, 0 warned, 0 failed.
338
338
339 Skips with xml
339 Skips with xml
340 $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy \
340 $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy \
341 > --xunit=xunit.xml
341 > --xunit=xunit.xml
342 .s
342 .s
343 Skipped test-skip.t: irrelevant
343 Skipped test-skip.t: irrelevant
344 # Ran 2 tests, 2 skipped, 0 warned, 0 failed.
344 # Ran 2 tests, 2 skipped, 0 warned, 0 failed.
345 $ cat xunit.xml
345 $ cat xunit.xml
346 <?xml version="1.0" encoding="utf-8"?>
346 <?xml version="1.0" encoding="utf-8"?>
347 <testsuite errors="0" failures="0" name="run-tests" skipped="2" tests="2">
347 <testsuite errors="0" failures="0" name="run-tests" skipped="2" tests="2">
348 <testcase name="test-success.t" time="*"/> (glob)
348 <testcase name="test-success.t" time="*"/> (glob)
349 </testsuite>
349 </testsuite>
350
350
351 Missing skips or blacklisted skips don't count as executed:
351 Missing skips or blacklisted skips don't count as executed:
352 $ echo test-failure.t > blacklist
352 $ echo test-failure.t > blacklist
353 $ $TESTDIR/run-tests.py --with-hg=`which hg` --blacklist=blacklist \
353 $ $TESTDIR/run-tests.py --with-hg=`which hg` --blacklist=blacklist \
354 > test-failure.t test-bogus.t
354 > test-failure.t test-bogus.t
355 ss
355 ss
356 Skipped test-bogus.t: Doesn't exist
356 Skipped test-bogus.t: Doesn't exist
357 Skipped test-failure.t: blacklisted
357 Skipped test-failure.t: blacklisted
358 # Ran 0 tests, 2 skipped, 0 warned, 0 failed.
358 # Ran 0 tests, 2 skipped, 0 warned, 0 failed.
359
359
General Comments 0
You need to be logged in to leave comments. Login now