##// END OF EJS Templates
bundlerepo: reintroduce dirstate
Matt Mackall -
r7853:af062a9f default
parent child Browse files
Show More
@@ -1,532 +1,536 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 # $Id$
8 # $Id$
9 #
9 #
10 # Keyword expansion hack against the grain of a DSCM
10 # Keyword expansion hack against the grain of a DSCM
11 #
11 #
12 # There are many good reasons why this is not needed in a distributed
12 # There are many good reasons why this is not needed in a distributed
13 # SCM, still it may be useful in very small projects based on single
13 # SCM, still it may be useful in very small projects based on single
14 # files (like LaTeX packages), that are mostly addressed to an audience
14 # files (like LaTeX packages), that are mostly addressed to an audience
15 # not running a version control system.
15 # not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 #
19 #
20 # Keyword expansion is based on Mercurial's changeset template mappings.
20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 #
21 #
22 # Binary files are not touched.
22 # Binary files are not touched.
23 #
23 #
24 # Setup in hgrc:
24 # Setup in hgrc:
25 #
25 #
26 # [extensions]
26 # [extensions]
27 # # enable extension
27 # # enable extension
28 # hgext.keyword =
28 # hgext.keyword =
29 #
29 #
30 # Files to act upon/ignore are specified in the [keyword] section.
30 # Files to act upon/ignore are specified in the [keyword] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
32 #
32 #
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34
34
35 '''keyword expansion in local repositories
35 '''keyword expansion in local repositories
36
36
37 This extension expands RCS/CVS-like or self-customized $Keywords$
37 This extension expands RCS/CVS-like or self-customized $Keywords$
38 in tracked text files selected by your configuration.
38 in tracked text files selected by your configuration.
39
39
40 Keywords are only expanded in local repositories and not stored in
40 Keywords are only expanded in local repositories and not stored in
41 the change history. The mechanism can be regarded as a convenience
41 the change history. The mechanism can be regarded as a convenience
42 for the current user or for archive distribution.
42 for the current user or for archive distribution.
43
43
44 Configuration is done in the [keyword] and [keywordmaps] sections
44 Configuration is done in the [keyword] and [keywordmaps] sections
45 of hgrc files.
45 of hgrc files.
46
46
47 Example:
47 Example:
48
48
49 [keyword]
49 [keyword]
50 # expand keywords in every python file except those matching "x*"
50 # expand keywords in every python file except those matching "x*"
51 **.py =
51 **.py =
52 x* = ignore
52 x* = ignore
53
53
54 Note: the more specific you are in your filename patterns
54 Note: the more specific you are in your filename patterns
55 the less you lose speed in huge repos.
55 the less you lose speed in huge repos.
56
56
57 For [keywordmaps] template mapping and expansion demonstration and
57 For [keywordmaps] template mapping and expansion demonstration and
58 control run "hg kwdemo".
58 control run "hg kwdemo".
59
59
60 An additional date template filter {date|utcdate} is provided.
60 An additional date template filter {date|utcdate} is provided.
61
61
62 The default template mappings (view with "hg kwdemo -d") can be replaced
62 The default template mappings (view with "hg kwdemo -d") can be replaced
63 with customized keywords and templates.
63 with customized keywords and templates.
64 Again, run "hg kwdemo" to control the results of your config changes.
64 Again, run "hg kwdemo" to control the results of your config changes.
65
65
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 the risk of inadvertedly storing expanded keywords in the change history.
67 the risk of inadvertedly storing expanded keywords in the change history.
68
68
69 To force expansion after enabling it, or a configuration change, run
69 To force expansion after enabling it, or a configuration change, run
70 "hg kwexpand".
70 "hg kwexpand".
71
71
72 Also, when committing with the record extension or using mq's qrecord, be aware
72 Also, when committing with the record extension or using mq's qrecord, be aware
73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
74 question to update keyword expansions after all changes have been checked in.
74 question to update keyword expansions after all changes have been checked in.
75
75
76 Expansions spanning more than one line and incremental expansions,
76 Expansions spanning more than one line and incremental expansions,
77 like CVS' $Log$, are not supported. A keyword template map
77 like CVS' $Log$, are not supported. A keyword template map
78 "Log = {desc}" expands to the first line of the changeset description.
78 "Log = {desc}" expands to the first line of the changeset description.
79 '''
79 '''
80
80
81 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
81 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
82 from mercurial import patch, localrepo, templater, templatefilters, util
82 from mercurial import patch, localrepo, templater, templatefilters, util
83 from mercurial.hgweb import webcommands
83 from mercurial.hgweb import webcommands
84 from mercurial.node import nullid, hex
84 from mercurial.node import nullid, hex
85 from mercurial.i18n import _
85 from mercurial.i18n import _
86 import re, shutil, tempfile, time
86 import re, shutil, tempfile, time
87
87
88 commands.optionalrepo += ' kwdemo'
88 commands.optionalrepo += ' kwdemo'
89
89
90 # hg commands that do not act on keywords
90 # hg commands that do not act on keywords
91 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
91 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
92 ' log outgoing push rename rollback tip verify'
92 ' log outgoing push rename rollback tip verify'
93 ' convert email glog')
93 ' convert email glog')
94
94
95 # hg commands that trigger expansion only when writing to working dir,
95 # hg commands that trigger expansion only when writing to working dir,
96 # not when reading filelog, and unexpand when reading from working dir
96 # not when reading filelog, and unexpand when reading from working dir
97 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
97 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
98
98
99 def utcdate(date):
99 def utcdate(date):
100 '''Returns hgdate in cvs-like UTC format.'''
100 '''Returns hgdate in cvs-like UTC format.'''
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102
102
103 # make keyword tools accessible
103 # make keyword tools accessible
104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
105
105
106
106
107 class kwtemplater(object):
107 class kwtemplater(object):
108 '''
108 '''
109 Sets up keyword templates, corresponding keyword regex, and
109 Sets up keyword templates, corresponding keyword regex, and
110 provides keyword substitution functions.
110 provides keyword substitution functions.
111 '''
111 '''
112 templates = {
112 templates = {
113 'Revision': '{node|short}',
113 'Revision': '{node|short}',
114 'Author': '{author|user}',
114 'Author': '{author|user}',
115 'Date': '{date|utcdate}',
115 'Date': '{date|utcdate}',
116 'RCSFile': '{file|basename},v',
116 'RCSFile': '{file|basename},v',
117 'Source': '{root}/{file},v',
117 'Source': '{root}/{file},v',
118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 }
120 }
121
121
122 def __init__(self, ui, repo):
122 def __init__(self, ui, repo):
123 self.ui = ui
123 self.ui = ui
124 self.repo = repo
124 self.repo = repo
125 self.matcher = util.matcher(repo.root,
125 self.matcher = util.matcher(repo.root,
126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
127 self.restrict = kwtools['hgcmd'] in restricted.split()
127 self.restrict = kwtools['hgcmd'] in restricted.split()
128
128
129 kwmaps = self.ui.configitems('keywordmaps')
129 kwmaps = self.ui.configitems('keywordmaps')
130 if kwmaps: # override default templates
130 if kwmaps: # override default templates
131 kwmaps = [(k, templater.parsestring(v, False))
131 kwmaps = [(k, templater.parsestring(v, False))
132 for (k, v) in kwmaps]
132 for (k, v) in kwmaps]
133 self.templates = dict(kwmaps)
133 self.templates = dict(kwmaps)
134 escaped = map(re.escape, self.templates.keys())
134 escaped = map(re.escape, self.templates.keys())
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 self.re_kw = re.compile(kwpat)
136 self.re_kw = re.compile(kwpat)
137
137
138 templatefilters.filters['utcdate'] = utcdate
138 templatefilters.filters['utcdate'] = utcdate
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 False, None, '', False)
140 False, None, '', False)
141
141
142 def substitute(self, data, path, ctx, subfunc):
142 def substitute(self, data, path, ctx, subfunc):
143 '''Replaces keywords in data with expanded template.'''
143 '''Replaces keywords in data with expanded template.'''
144 def kwsub(mobj):
144 def kwsub(mobj):
145 kw = mobj.group(1)
145 kw = mobj.group(1)
146 self.ct.use_template(self.templates[kw])
146 self.ct.use_template(self.templates[kw])
147 self.ui.pushbuffer()
147 self.ui.pushbuffer()
148 self.ct.show(ctx, root=self.repo.root, file=path)
148 self.ct.show(ctx, root=self.repo.root, file=path)
149 ekw = templatefilters.firstline(self.ui.popbuffer())
149 ekw = templatefilters.firstline(self.ui.popbuffer())
150 return '$%s: %s $' % (kw, ekw)
150 return '$%s: %s $' % (kw, ekw)
151 return subfunc(kwsub, data)
151 return subfunc(kwsub, data)
152
152
153 def expand(self, path, node, data):
153 def expand(self, path, node, data):
154 '''Returns data with keywords expanded.'''
154 '''Returns data with keywords expanded.'''
155 if not self.restrict and self.matcher(path) and not util.binary(data):
155 if not self.restrict and self.matcher(path) and not util.binary(data):
156 ctx = self.repo.filectx(path, fileid=node).changectx()
156 ctx = self.repo.filectx(path, fileid=node).changectx()
157 return self.substitute(data, path, ctx, self.re_kw.sub)
157 return self.substitute(data, path, ctx, self.re_kw.sub)
158 return data
158 return data
159
159
160 def iskwfile(self, path, flagfunc):
160 def iskwfile(self, path, flagfunc):
161 '''Returns true if path matches [keyword] pattern
161 '''Returns true if path matches [keyword] pattern
162 and is not a symbolic link.
162 and is not a symbolic link.
163 Caveat: localrepository._link fails on Windows.'''
163 Caveat: localrepository._link fails on Windows.'''
164 return self.matcher(path) and not 'l' in flagfunc(path)
164 return self.matcher(path) and not 'l' in flagfunc(path)
165
165
166 def overwrite(self, node, expand, files):
166 def overwrite(self, node, expand, files):
167 '''Overwrites selected files expanding/shrinking keywords.'''
167 '''Overwrites selected files expanding/shrinking keywords.'''
168 ctx = self.repo[node]
168 ctx = self.repo[node]
169 mf = ctx.manifest()
169 mf = ctx.manifest()
170 if node is not None: # commit
170 if node is not None: # commit
171 files = [f for f in ctx.files() if f in mf]
171 files = [f for f in ctx.files() if f in mf]
172 notify = self.ui.debug
172 notify = self.ui.debug
173 else: # kwexpand/kwshrink
173 else: # kwexpand/kwshrink
174 notify = self.ui.note
174 notify = self.ui.note
175 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
175 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
176 if candidates:
176 if candidates:
177 self.restrict = True # do not expand when reading
177 self.restrict = True # do not expand when reading
178 action = expand and 'expanding' or 'shrinking'
178 action = expand and 'expanding' or 'shrinking'
179 for f in candidates:
179 for f in candidates:
180 fp = self.repo.file(f)
180 fp = self.repo.file(f)
181 data = fp.read(mf[f])
181 data = fp.read(mf[f])
182 if util.binary(data):
182 if util.binary(data):
183 continue
183 continue
184 if expand:
184 if expand:
185 if node is None:
185 if node is None:
186 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
186 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
187 data, found = self.substitute(data, f, ctx,
187 data, found = self.substitute(data, f, ctx,
188 self.re_kw.subn)
188 self.re_kw.subn)
189 else:
189 else:
190 found = self.re_kw.search(data)
190 found = self.re_kw.search(data)
191 if found:
191 if found:
192 notify(_('overwriting %s %s keywords\n') % (f, action))
192 notify(_('overwriting %s %s keywords\n') % (f, action))
193 self.repo.wwrite(f, data, mf.flags(f))
193 self.repo.wwrite(f, data, mf.flags(f))
194 self.repo.dirstate.normal(f)
194 self.repo.dirstate.normal(f)
195 self.restrict = False
195 self.restrict = False
196
196
197 def shrinktext(self, text):
197 def shrinktext(self, text):
198 '''Unconditionally removes all keyword substitutions from text.'''
198 '''Unconditionally removes all keyword substitutions from text.'''
199 return self.re_kw.sub(r'$\1$', text)
199 return self.re_kw.sub(r'$\1$', text)
200
200
201 def shrink(self, fname, text):
201 def shrink(self, fname, text):
202 '''Returns text with all keyword substitutions removed.'''
202 '''Returns text with all keyword substitutions removed.'''
203 if self.matcher(fname) and not util.binary(text):
203 if self.matcher(fname) and not util.binary(text):
204 return self.shrinktext(text)
204 return self.shrinktext(text)
205 return text
205 return text
206
206
207 def shrinklines(self, fname, lines):
207 def shrinklines(self, fname, lines):
208 '''Returns lines with keyword substitutions removed.'''
208 '''Returns lines with keyword substitutions removed.'''
209 if self.matcher(fname):
209 if self.matcher(fname):
210 text = ''.join(lines)
210 text = ''.join(lines)
211 if not util.binary(text):
211 if not util.binary(text):
212 return self.shrinktext(text).splitlines(True)
212 return self.shrinktext(text).splitlines(True)
213 return lines
213 return lines
214
214
215 def wread(self, fname, data):
215 def wread(self, fname, data):
216 '''If in restricted mode returns data read from wdir with
216 '''If in restricted mode returns data read from wdir with
217 keyword substitutions removed.'''
217 keyword substitutions removed.'''
218 return self.restrict and self.shrink(fname, data) or data
218 return self.restrict and self.shrink(fname, data) or data
219
219
220 class kwfilelog(filelog.filelog):
220 class kwfilelog(filelog.filelog):
221 '''
221 '''
222 Subclass of filelog to hook into its read, add, cmp methods.
222 Subclass of filelog to hook into its read, add, cmp methods.
223 Keywords are "stored" unexpanded, and processed on reading.
223 Keywords are "stored" unexpanded, and processed on reading.
224 '''
224 '''
225 def __init__(self, opener, kwt, path):
225 def __init__(self, opener, kwt, path):
226 super(kwfilelog, self).__init__(opener, path)
226 super(kwfilelog, self).__init__(opener, path)
227 self.kwt = kwt
227 self.kwt = kwt
228 self.path = path
228 self.path = path
229
229
230 def read(self, node):
230 def read(self, node):
231 '''Expands keywords when reading filelog.'''
231 '''Expands keywords when reading filelog.'''
232 data = super(kwfilelog, self).read(node)
232 data = super(kwfilelog, self).read(node)
233 return self.kwt.expand(self.path, node, data)
233 return self.kwt.expand(self.path, node, data)
234
234
235 def add(self, text, meta, tr, link, p1=None, p2=None):
235 def add(self, text, meta, tr, link, p1=None, p2=None):
236 '''Removes keyword substitutions when adding to filelog.'''
236 '''Removes keyword substitutions when adding to filelog.'''
237 text = self.kwt.shrink(self.path, text)
237 text = self.kwt.shrink(self.path, text)
238 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
238 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
239
239
240 def cmp(self, node, text):
240 def cmp(self, node, text):
241 '''Removes keyword substitutions for comparison.'''
241 '''Removes keyword substitutions for comparison.'''
242 text = self.kwt.shrink(self.path, text)
242 text = self.kwt.shrink(self.path, text)
243 if self.renamed(node):
243 if self.renamed(node):
244 t2 = super(kwfilelog, self).read(node)
244 t2 = super(kwfilelog, self).read(node)
245 return t2 != text
245 return t2 != text
246 return revlog.revlog.cmp(self, node, text)
246 return revlog.revlog.cmp(self, node, text)
247
247
248 def _status(ui, repo, kwt, unknown, *pats, **opts):
248 def _status(ui, repo, kwt, unknown, *pats, **opts):
249 '''Bails out if [keyword] configuration is not active.
249 '''Bails out if [keyword] configuration is not active.
250 Returns status of working directory.'''
250 Returns status of working directory.'''
251 if kwt:
251 if kwt:
252 matcher = cmdutil.match(repo, pats, opts)
252 matcher = cmdutil.match(repo, pats, opts)
253 return repo.status(match=matcher, unknown=unknown, clean=True)
253 return repo.status(match=matcher, unknown=unknown, clean=True)
254 if ui.configitems('keyword'):
254 if ui.configitems('keyword'):
255 raise util.Abort(_('[keyword] patterns cannot match'))
255 raise util.Abort(_('[keyword] patterns cannot match'))
256 raise util.Abort(_('no [keyword] patterns configured'))
256 raise util.Abort(_('no [keyword] patterns configured'))
257
257
258 def _kwfwrite(ui, repo, expand, *pats, **opts):
258 def _kwfwrite(ui, repo, expand, *pats, **opts):
259 '''Selects files and passes them to kwtemplater.overwrite.'''
259 '''Selects files and passes them to kwtemplater.overwrite.'''
260 if repo.dirstate.parents()[1] != nullid:
260 if repo.dirstate.parents()[1] != nullid:
261 raise util.Abort(_('outstanding uncommitted merge'))
261 raise util.Abort(_('outstanding uncommitted merge'))
262 kwt = kwtools['templater']
262 kwt = kwtools['templater']
263 status = _status(ui, repo, kwt, False, *pats, **opts)
263 status = _status(ui, repo, kwt, False, *pats, **opts)
264 modified, added, removed, deleted = status[:4]
264 modified, added, removed, deleted = status[:4]
265 if modified or added or removed or deleted:
265 if modified or added or removed or deleted:
266 raise util.Abort(_('outstanding uncommitted changes'))
266 raise util.Abort(_('outstanding uncommitted changes'))
267 wlock = lock = None
267 wlock = lock = None
268 try:
268 try:
269 wlock = repo.wlock()
269 wlock = repo.wlock()
270 lock = repo.lock()
270 lock = repo.lock()
271 kwt.overwrite(None, expand, status[6])
271 kwt.overwrite(None, expand, status[6])
272 finally:
272 finally:
273 del wlock, lock
273 del wlock, lock
274
274
275
275
276 def demo(ui, repo, *args, **opts):
276 def demo(ui, repo, *args, **opts):
277 '''print [keywordmaps] configuration and an expansion example
277 '''print [keywordmaps] configuration and an expansion example
278
278
279 Show current, custom, or default keyword template maps
279 Show current, custom, or default keyword template maps
280 and their expansion.
280 and their expansion.
281
281
282 Extend current configuration by specifying maps as arguments
282 Extend current configuration by specifying maps as arguments
283 and optionally by reading from an additional hgrc file.
283 and optionally by reading from an additional hgrc file.
284
284
285 Override current keyword template maps with "default" option.
285 Override current keyword template maps with "default" option.
286 '''
286 '''
287 def demostatus(stat):
287 def demostatus(stat):
288 ui.status(_('\n\t%s\n') % stat)
288 ui.status(_('\n\t%s\n') % stat)
289
289
290 def demoitems(section, items):
290 def demoitems(section, items):
291 ui.write('[%s]\n' % section)
291 ui.write('[%s]\n' % section)
292 for k, v in items:
292 for k, v in items:
293 ui.write('%s = %s\n' % (k, v))
293 ui.write('%s = %s\n' % (k, v))
294
294
295 msg = 'hg keyword config and expansion example'
295 msg = 'hg keyword config and expansion example'
296 kwstatus = 'current'
296 kwstatus = 'current'
297 fn = 'demo.txt'
297 fn = 'demo.txt'
298 branchname = 'demobranch'
298 branchname = 'demobranch'
299 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
299 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
300 ui.note(_('creating temporary repo at %s\n') % tmpdir)
300 ui.note(_('creating temporary repo at %s\n') % tmpdir)
301 repo = localrepo.localrepository(ui, tmpdir, True)
301 repo = localrepo.localrepository(ui, tmpdir, True)
302 ui.setconfig('keyword', fn, '')
302 ui.setconfig('keyword', fn, '')
303 if args or opts.get('rcfile'):
303 if args or opts.get('rcfile'):
304 kwstatus = 'custom'
304 kwstatus = 'custom'
305 if opts.get('rcfile'):
305 if opts.get('rcfile'):
306 ui.readconfig(opts.get('rcfile'))
306 ui.readconfig(opts.get('rcfile'))
307 if opts.get('default'):
307 if opts.get('default'):
308 kwstatus = 'default'
308 kwstatus = 'default'
309 kwmaps = kwtemplater.templates
309 kwmaps = kwtemplater.templates
310 if ui.configitems('keywordmaps'):
310 if ui.configitems('keywordmaps'):
311 # override maps from optional rcfile
311 # override maps from optional rcfile
312 for k, v in kwmaps.iteritems():
312 for k, v in kwmaps.iteritems():
313 ui.setconfig('keywordmaps', k, v)
313 ui.setconfig('keywordmaps', k, v)
314 elif args:
314 elif args:
315 # simulate hgrc parsing
315 # simulate hgrc parsing
316 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
316 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
317 fp = repo.opener('hgrc', 'w')
317 fp = repo.opener('hgrc', 'w')
318 fp.writelines(rcmaps)
318 fp.writelines(rcmaps)
319 fp.close()
319 fp.close()
320 ui.readconfig(repo.join('hgrc'))
320 ui.readconfig(repo.join('hgrc'))
321 if not opts.get('default'):
321 if not opts.get('default'):
322 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
322 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
323 uisetup(ui)
323 uisetup(ui)
324 reposetup(ui, repo)
324 reposetup(ui, repo)
325 for k, v in ui.configitems('extensions'):
325 for k, v in ui.configitems('extensions'):
326 if k.endswith('keyword'):
326 if k.endswith('keyword'):
327 extension = '%s = %s' % (k, v)
327 extension = '%s = %s' % (k, v)
328 break
328 break
329 demostatus('config using %s keyword template maps' % kwstatus)
329 demostatus('config using %s keyword template maps' % kwstatus)
330 ui.write('[extensions]\n%s\n' % extension)
330 ui.write('[extensions]\n%s\n' % extension)
331 demoitems('keyword', ui.configitems('keyword'))
331 demoitems('keyword', ui.configitems('keyword'))
332 demoitems('keywordmaps', kwmaps.iteritems())
332 demoitems('keywordmaps', kwmaps.iteritems())
333 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
333 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
334 repo.wopener(fn, 'w').write(keywords)
334 repo.wopener(fn, 'w').write(keywords)
335 repo.add([fn])
335 repo.add([fn])
336 path = repo.wjoin(fn)
336 path = repo.wjoin(fn)
337 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
337 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
338 ui.note(keywords)
338 ui.note(keywords)
339 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
339 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
340 # silence branch command if not verbose
340 # silence branch command if not verbose
341 quiet = ui.quiet
341 quiet = ui.quiet
342 ui.quiet = not ui.verbose
342 ui.quiet = not ui.verbose
343 commands.branch(ui, repo, branchname)
343 commands.branch(ui, repo, branchname)
344 ui.quiet = quiet
344 ui.quiet = quiet
345 for name, cmd in ui.configitems('hooks'):
345 for name, cmd in ui.configitems('hooks'):
346 if name.split('.', 1)[0].find('commit') > -1:
346 if name.split('.', 1)[0].find('commit') > -1:
347 repo.ui.setconfig('hooks', name, '')
347 repo.ui.setconfig('hooks', name, '')
348 ui.note(_('unhooked all commit hooks\n'))
348 ui.note(_('unhooked all commit hooks\n'))
349 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
349 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
350 repo.commit(text=msg)
350 repo.commit(text=msg)
351 fmt = ui.verbose and ' in %s' % path or ''
351 fmt = ui.verbose and ' in %s' % path or ''
352 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
352 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
353 ui.write(repo.wread(fn))
353 ui.write(repo.wread(fn))
354 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
354 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
355 shutil.rmtree(tmpdir, ignore_errors=True)
355 shutil.rmtree(tmpdir, ignore_errors=True)
356
356
357 def expand(ui, repo, *pats, **opts):
357 def expand(ui, repo, *pats, **opts):
358 '''expand keywords in working directory
358 '''expand keywords in working directory
359
359
360 Run after (re)enabling keyword expansion.
360 Run after (re)enabling keyword expansion.
361
361
362 kwexpand refuses to run if given files contain local changes.
362 kwexpand refuses to run if given files contain local changes.
363 '''
363 '''
364 # 3rd argument sets expansion to True
364 # 3rd argument sets expansion to True
365 _kwfwrite(ui, repo, True, *pats, **opts)
365 _kwfwrite(ui, repo, True, *pats, **opts)
366
366
367 def files(ui, repo, *pats, **opts):
367 def files(ui, repo, *pats, **opts):
368 '''print files currently configured for keyword expansion
368 '''print files currently configured for keyword expansion
369
369
370 Crosscheck which files in working directory are potential targets for
370 Crosscheck which files in working directory are potential targets for
371 keyword expansion.
371 keyword expansion.
372 That is, files matched by [keyword] config patterns but not symlinks.
372 That is, files matched by [keyword] config patterns but not symlinks.
373 '''
373 '''
374 kwt = kwtools['templater']
374 kwt = kwtools['templater']
375 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
375 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
376 modified, added, removed, deleted, unknown, ignored, clean = status
376 modified, added, removed, deleted, unknown, ignored, clean = status
377 files = util.sort(modified + added + clean + unknown)
377 files = util.sort(modified + added + clean + unknown)
378 wctx = repo[None]
378 wctx = repo[None]
379 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
379 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
380 cwd = pats and repo.getcwd() or ''
380 cwd = pats and repo.getcwd() or ''
381 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
381 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
382 if opts.get('all') or opts.get('ignore'):
382 if opts.get('all') or opts.get('ignore'):
383 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
383 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
384 for char, filenames in kwfstats:
384 for char, filenames in kwfstats:
385 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
385 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
386 for f in filenames:
386 for f in filenames:
387 ui.write(fmt % repo.pathto(f, cwd))
387 ui.write(fmt % repo.pathto(f, cwd))
388
388
389 def shrink(ui, repo, *pats, **opts):
389 def shrink(ui, repo, *pats, **opts):
390 '''revert expanded keywords in working directory
390 '''revert expanded keywords in working directory
391
391
392 Run before changing/disabling active keywords
392 Run before changing/disabling active keywords
393 or if you experience problems with "hg import" or "hg merge".
393 or if you experience problems with "hg import" or "hg merge".
394
394
395 kwshrink refuses to run if given files contain local changes.
395 kwshrink refuses to run if given files contain local changes.
396 '''
396 '''
397 # 3rd argument sets expansion to False
397 # 3rd argument sets expansion to False
398 _kwfwrite(ui, repo, False, *pats, **opts)
398 _kwfwrite(ui, repo, False, *pats, **opts)
399
399
400
400
401 def uisetup(ui):
401 def uisetup(ui):
402 '''Collects [keyword] config in kwtools.
402 '''Collects [keyword] config in kwtools.
403 Monkeypatches dispatch._parse if needed.'''
403 Monkeypatches dispatch._parse if needed.'''
404
404
405 for pat, opt in ui.configitems('keyword'):
405 for pat, opt in ui.configitems('keyword'):
406 if opt != 'ignore':
406 if opt != 'ignore':
407 kwtools['inc'].append(pat)
407 kwtools['inc'].append(pat)
408 else:
408 else:
409 kwtools['exc'].append(pat)
409 kwtools['exc'].append(pat)
410
410
411 if kwtools['inc']:
411 if kwtools['inc']:
412 def kwdispatch_parse(orig, ui, args):
412 def kwdispatch_parse(orig, ui, args):
413 '''Monkeypatch dispatch._parse to obtain running hg command.'''
413 '''Monkeypatch dispatch._parse to obtain running hg command.'''
414 cmd, func, args, options, cmdoptions = orig(ui, args)
414 cmd, func, args, options, cmdoptions = orig(ui, args)
415 kwtools['hgcmd'] = cmd
415 kwtools['hgcmd'] = cmd
416 return cmd, func, args, options, cmdoptions
416 return cmd, func, args, options, cmdoptions
417
417
418 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
418 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
419
419
420 def reposetup(ui, repo):
420 def reposetup(ui, repo):
421 '''Sets up repo as kwrepo for keyword substitution.
421 '''Sets up repo as kwrepo for keyword substitution.
422 Overrides file method to return kwfilelog instead of filelog
422 Overrides file method to return kwfilelog instead of filelog
423 if file matches user configuration.
423 if file matches user configuration.
424 Wraps commit to overwrite configured files with updated
424 Wraps commit to overwrite configured files with updated
425 keyword substitutions.
425 keyword substitutions.
426 Monkeypatches patch and webcommands.'''
426 Monkeypatches patch and webcommands.'''
427
427
428 if (not hasattr(repo, 'dirstate') or not kwtools['inc']
428 try:
429 or kwtools['hgcmd'] in nokwcommands.split()
429 if (not repo.local() or not kwtools['inc']
430 or '.hg' in util.splitpath(repo.root)):
430 or kwtools['hgcmd'] in nokwcommands.split()
431 return
431 or '.hg' in util.splitpath(repo.root)
432 or repo._url.startswith('bundle:')):
433 return
434 except AttributeError:
435 pass
432
436
433 kwtools['templater'] = kwt = kwtemplater(ui, repo)
437 kwtools['templater'] = kwt = kwtemplater(ui, repo)
434
438
435 class kwrepo(repo.__class__):
439 class kwrepo(repo.__class__):
436 def file(self, f):
440 def file(self, f):
437 if f[0] == '/':
441 if f[0] == '/':
438 f = f[1:]
442 f = f[1:]
439 return kwfilelog(self.sopener, kwt, f)
443 return kwfilelog(self.sopener, kwt, f)
440
444
441 def wread(self, filename):
445 def wread(self, filename):
442 data = super(kwrepo, self).wread(filename)
446 data = super(kwrepo, self).wread(filename)
443 return kwt.wread(filename, data)
447 return kwt.wread(filename, data)
444
448
445 def commit(self, files=None, text='', user=None, date=None,
449 def commit(self, files=None, text='', user=None, date=None,
446 match=None, force=False, force_editor=False,
450 match=None, force=False, force_editor=False,
447 p1=None, p2=None, extra={}, empty_ok=False):
451 p1=None, p2=None, extra={}, empty_ok=False):
448 wlock = lock = None
452 wlock = lock = None
449 _p1 = _p2 = None
453 _p1 = _p2 = None
450 try:
454 try:
451 wlock = self.wlock()
455 wlock = self.wlock()
452 lock = self.lock()
456 lock = self.lock()
453 # store and postpone commit hooks
457 # store and postpone commit hooks
454 commithooks = {}
458 commithooks = {}
455 for name, cmd in ui.configitems('hooks'):
459 for name, cmd in ui.configitems('hooks'):
456 if name.split('.', 1)[0] == 'commit':
460 if name.split('.', 1)[0] == 'commit':
457 commithooks[name] = cmd
461 commithooks[name] = cmd
458 ui.setconfig('hooks', name, None)
462 ui.setconfig('hooks', name, None)
459 if commithooks:
463 if commithooks:
460 # store parents for commit hook environment
464 # store parents for commit hook environment
461 if p1 is None:
465 if p1 is None:
462 _p1, _p2 = repo.dirstate.parents()
466 _p1, _p2 = repo.dirstate.parents()
463 else:
467 else:
464 _p1, _p2 = p1, p2 or nullid
468 _p1, _p2 = p1, p2 or nullid
465 _p1 = hex(_p1)
469 _p1 = hex(_p1)
466 if _p2 == nullid:
470 if _p2 == nullid:
467 _p2 = ''
471 _p2 = ''
468 else:
472 else:
469 _p2 = hex(_p2)
473 _p2 = hex(_p2)
470
474
471 n = super(kwrepo, self).commit(files, text, user, date, match,
475 n = super(kwrepo, self).commit(files, text, user, date, match,
472 force, force_editor, p1, p2,
476 force, force_editor, p1, p2,
473 extra, empty_ok)
477 extra, empty_ok)
474
478
475 # restore commit hooks
479 # restore commit hooks
476 for name, cmd in commithooks.iteritems():
480 for name, cmd in commithooks.iteritems():
477 ui.setconfig('hooks', name, cmd)
481 ui.setconfig('hooks', name, cmd)
478 if n is not None:
482 if n is not None:
479 kwt.overwrite(n, True, None)
483 kwt.overwrite(n, True, None)
480 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
484 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
481 return n
485 return n
482 finally:
486 finally:
483 del wlock, lock
487 del wlock, lock
484
488
485 # monkeypatches
489 # monkeypatches
486 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False):
490 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False):
487 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
491 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
488 rejects or conflicts due to expanded keywords in working dir.'''
492 rejects or conflicts due to expanded keywords in working dir.'''
489 orig(self, ui, fname, opener, missing)
493 orig(self, ui, fname, opener, missing)
490 # shrink keywords read from working dir
494 # shrink keywords read from working dir
491 self.lines = kwt.shrinklines(self.fname, self.lines)
495 self.lines = kwt.shrinklines(self.fname, self.lines)
492
496
493 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
497 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
494 opts=None):
498 opts=None):
495 '''Monkeypatch patch.diff to avoid expansion except when
499 '''Monkeypatch patch.diff to avoid expansion except when
496 comparing against working dir.'''
500 comparing against working dir.'''
497 if node2 is not None:
501 if node2 is not None:
498 kwt.matcher = util.never
502 kwt.matcher = util.never
499 elif node1 is not None and node1 != repo['.'].node():
503 elif node1 is not None and node1 != repo['.'].node():
500 kwt.restrict = True
504 kwt.restrict = True
501 return orig(repo, node1, node2, match, changes, opts)
505 return orig(repo, node1, node2, match, changes, opts)
502
506
503 def kwweb_skip(orig, web, req, tmpl):
507 def kwweb_skip(orig, web, req, tmpl):
504 '''Wraps webcommands.x turning off keyword expansion.'''
508 '''Wraps webcommands.x turning off keyword expansion.'''
505 kwt.matcher = util.never
509 kwt.matcher = util.never
506 return orig(web, req, tmpl)
510 return orig(web, req, tmpl)
507
511
508 repo.__class__ = kwrepo
512 repo.__class__ = kwrepo
509
513
510 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
514 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
511 extensions.wrapfunction(patch, 'diff', kw_diff)
515 extensions.wrapfunction(patch, 'diff', kw_diff)
512 for c in 'annotate changeset rev filediff diff'.split():
516 for c in 'annotate changeset rev filediff diff'.split():
513 extensions.wrapfunction(webcommands, c, kwweb_skip)
517 extensions.wrapfunction(webcommands, c, kwweb_skip)
514
518
515 cmdtable = {
519 cmdtable = {
516 'kwdemo':
520 'kwdemo':
517 (demo,
521 (demo,
518 [('d', 'default', None, _('show default keyword template maps')),
522 [('d', 'default', None, _('show default keyword template maps')),
519 ('f', 'rcfile', [], _('read maps from rcfile'))],
523 ('f', 'rcfile', [], _('read maps from rcfile'))],
520 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
524 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
521 'kwexpand': (expand, commands.walkopts,
525 'kwexpand': (expand, commands.walkopts,
522 _('hg kwexpand [OPTION]... [FILE]...')),
526 _('hg kwexpand [OPTION]... [FILE]...')),
523 'kwfiles':
527 'kwfiles':
524 (files,
528 (files,
525 [('a', 'all', None, _('show keyword status flags of all files')),
529 [('a', 'all', None, _('show keyword status flags of all files')),
526 ('i', 'ignore', None, _('show files excluded from expansion')),
530 ('i', 'ignore', None, _('show files excluded from expansion')),
527 ('u', 'untracked', None, _('additionally show untracked files')),
531 ('u', 'untracked', None, _('additionally show untracked files')),
528 ] + commands.walkopts,
532 ] + commands.walkopts,
529 _('hg kwfiles [OPTION]... [FILE]...')),
533 _('hg kwfiles [OPTION]... [FILE]...')),
530 'kwshrink': (shrink, commands.walkopts,
534 'kwshrink': (shrink, commands.walkopts,
531 _('hg kwshrink [OPTION]... [FILE]...')),
535 _('hg kwshrink [OPTION]... [FILE]...')),
532 }
536 }
@@ -1,298 +1,298 b''
1 """
1 """
2 bundlerepo.py - repository class for viewing uncompressed bundles
2 bundlerepo.py - repository class for viewing uncompressed bundles
3
3
4 This provides a read-only repository interface to bundles as if
4 This provides a read-only repository interface to bundles as if
5 they were part of the actual repository.
5 they were part of the actual repository.
6
6
7 Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
7 Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License, incorporated herein by reference.
10 of the GNU General Public License, incorporated herein by reference.
11 """
11 """
12
12
13 from node import hex, nullid, short
13 from node import hex, nullid, short
14 from i18n import _
14 from i18n import _
15 import changegroup, util, os, struct, bz2, zlib, tempfile, shutil, mdiff
15 import changegroup, util, os, struct, bz2, zlib, tempfile, shutil, mdiff
16 import repo, localrepo, changelog, manifest, filelog, revlog, context, error
16 import repo, localrepo, changelog, manifest, filelog, revlog, context, error
17
17
18 class bundlerevlog(revlog.revlog):
18 class bundlerevlog(revlog.revlog):
19 def __init__(self, opener, indexfile, bundlefile,
19 def __init__(self, opener, indexfile, bundlefile,
20 linkmapper=None):
20 linkmapper=None):
21 # How it works:
21 # How it works:
22 # to retrieve a revision, we need to know the offset of
22 # to retrieve a revision, we need to know the offset of
23 # the revision in the bundlefile (an opened file).
23 # the revision in the bundlefile (an opened file).
24 #
24 #
25 # We store this offset in the index (start), to differentiate a
25 # We store this offset in the index (start), to differentiate a
26 # rev in the bundle and from a rev in the revlog, we check
26 # rev in the bundle and from a rev in the revlog, we check
27 # len(index[r]). If the tuple is bigger than 7, it is a bundle
27 # len(index[r]). If the tuple is bigger than 7, it is a bundle
28 # (it is bigger since we store the node to which the delta is)
28 # (it is bigger since we store the node to which the delta is)
29 #
29 #
30 revlog.revlog.__init__(self, opener, indexfile)
30 revlog.revlog.__init__(self, opener, indexfile)
31 self.bundlefile = bundlefile
31 self.bundlefile = bundlefile
32 self.basemap = {}
32 self.basemap = {}
33 def chunkpositer():
33 def chunkpositer():
34 for chunk in changegroup.chunkiter(bundlefile):
34 for chunk in changegroup.chunkiter(bundlefile):
35 pos = bundlefile.tell()
35 pos = bundlefile.tell()
36 yield chunk, pos - len(chunk)
36 yield chunk, pos - len(chunk)
37 n = len(self)
37 n = len(self)
38 prev = None
38 prev = None
39 for chunk, start in chunkpositer():
39 for chunk, start in chunkpositer():
40 size = len(chunk)
40 size = len(chunk)
41 if size < 80:
41 if size < 80:
42 raise util.Abort(_("invalid changegroup"))
42 raise util.Abort(_("invalid changegroup"))
43 start += 80
43 start += 80
44 size -= 80
44 size -= 80
45 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
45 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
46 if node in self.nodemap:
46 if node in self.nodemap:
47 prev = node
47 prev = node
48 continue
48 continue
49 for p in (p1, p2):
49 for p in (p1, p2):
50 if not p in self.nodemap:
50 if not p in self.nodemap:
51 raise error.LookupError(p1, self.indexfile,
51 raise error.LookupError(p1, self.indexfile,
52 _("unknown parent"))
52 _("unknown parent"))
53 if linkmapper is None:
53 if linkmapper is None:
54 link = n
54 link = n
55 else:
55 else:
56 link = linkmapper(cs)
56 link = linkmapper(cs)
57
57
58 if not prev:
58 if not prev:
59 prev = p1
59 prev = p1
60 # start, size, full unc. size, base (unused), link, p1, p2, node
60 # start, size, full unc. size, base (unused), link, p1, p2, node
61 e = (revlog.offset_type(start, 0), size, -1, -1, link,
61 e = (revlog.offset_type(start, 0), size, -1, -1, link,
62 self.rev(p1), self.rev(p2), node)
62 self.rev(p1), self.rev(p2), node)
63 self.basemap[n] = prev
63 self.basemap[n] = prev
64 self.index.insert(-1, e)
64 self.index.insert(-1, e)
65 self.nodemap[node] = n
65 self.nodemap[node] = n
66 prev = node
66 prev = node
67 n += 1
67 n += 1
68
68
69 def bundle(self, rev):
69 def bundle(self, rev):
70 """is rev from the bundle"""
70 """is rev from the bundle"""
71 if rev < 0:
71 if rev < 0:
72 return False
72 return False
73 return rev in self.basemap
73 return rev in self.basemap
74 def bundlebase(self, rev): return self.basemap[rev]
74 def bundlebase(self, rev): return self.basemap[rev]
75 def chunk(self, rev, df=None, cachelen=4096):
75 def chunk(self, rev, df=None, cachelen=4096):
76 # Warning: in case of bundle, the diff is against bundlebase,
76 # Warning: in case of bundle, the diff is against bundlebase,
77 # not against rev - 1
77 # not against rev - 1
78 # XXX: could use some caching
78 # XXX: could use some caching
79 if not self.bundle(rev):
79 if not self.bundle(rev):
80 return revlog.revlog.chunk(self, rev, df)
80 return revlog.revlog.chunk(self, rev, df)
81 self.bundlefile.seek(self.start(rev))
81 self.bundlefile.seek(self.start(rev))
82 return self.bundlefile.read(self.length(rev))
82 return self.bundlefile.read(self.length(rev))
83
83
84 def revdiff(self, rev1, rev2):
84 def revdiff(self, rev1, rev2):
85 """return or calculate a delta between two revisions"""
85 """return or calculate a delta between two revisions"""
86 if self.bundle(rev1) and self.bundle(rev2):
86 if self.bundle(rev1) and self.bundle(rev2):
87 # hot path for bundle
87 # hot path for bundle
88 revb = self.rev(self.bundlebase(rev2))
88 revb = self.rev(self.bundlebase(rev2))
89 if revb == rev1:
89 if revb == rev1:
90 return self.chunk(rev2)
90 return self.chunk(rev2)
91 elif not self.bundle(rev1) and not self.bundle(rev2):
91 elif not self.bundle(rev1) and not self.bundle(rev2):
92 return revlog.revlog.revdiff(self, rev1, rev2)
92 return revlog.revlog.revdiff(self, rev1, rev2)
93
93
94 return mdiff.textdiff(self.revision(self.node(rev1)),
94 return mdiff.textdiff(self.revision(self.node(rev1)),
95 self.revision(self.node(rev2)))
95 self.revision(self.node(rev2)))
96
96
97 def revision(self, node):
97 def revision(self, node):
98 """return an uncompressed revision of a given"""
98 """return an uncompressed revision of a given"""
99 if node == nullid: return ""
99 if node == nullid: return ""
100
100
101 text = None
101 text = None
102 chain = []
102 chain = []
103 iter_node = node
103 iter_node = node
104 rev = self.rev(iter_node)
104 rev = self.rev(iter_node)
105 # reconstruct the revision if it is from a changegroup
105 # reconstruct the revision if it is from a changegroup
106 while self.bundle(rev):
106 while self.bundle(rev):
107 if self._cache and self._cache[0] == iter_node:
107 if self._cache and self._cache[0] == iter_node:
108 text = self._cache[2]
108 text = self._cache[2]
109 break
109 break
110 chain.append(rev)
110 chain.append(rev)
111 iter_node = self.bundlebase(rev)
111 iter_node = self.bundlebase(rev)
112 rev = self.rev(iter_node)
112 rev = self.rev(iter_node)
113 if text is None:
113 if text is None:
114 text = revlog.revlog.revision(self, iter_node)
114 text = revlog.revlog.revision(self, iter_node)
115
115
116 while chain:
116 while chain:
117 delta = self.chunk(chain.pop())
117 delta = self.chunk(chain.pop())
118 text = mdiff.patches(text, [delta])
118 text = mdiff.patches(text, [delta])
119
119
120 p1, p2 = self.parents(node)
120 p1, p2 = self.parents(node)
121 if node != revlog.hash(text, p1, p2):
121 if node != revlog.hash(text, p1, p2):
122 raise error.RevlogError(_("integrity check failed on %s:%d")
122 raise error.RevlogError(_("integrity check failed on %s:%d")
123 % (self.datafile, self.rev(node)))
123 % (self.datafile, self.rev(node)))
124
124
125 self._cache = (node, self.rev(node), text)
125 self._cache = (node, self.rev(node), text)
126 return text
126 return text
127
127
128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
129 raise NotImplementedError
129 raise NotImplementedError
130 def addgroup(self, revs, linkmapper, transaction):
130 def addgroup(self, revs, linkmapper, transaction):
131 raise NotImplementedError
131 raise NotImplementedError
132 def strip(self, rev, minlink):
132 def strip(self, rev, minlink):
133 raise NotImplementedError
133 raise NotImplementedError
134 def checksize(self):
134 def checksize(self):
135 raise NotImplementedError
135 raise NotImplementedError
136
136
137 class bundlechangelog(bundlerevlog, changelog.changelog):
137 class bundlechangelog(bundlerevlog, changelog.changelog):
138 def __init__(self, opener, bundlefile):
138 def __init__(self, opener, bundlefile):
139 changelog.changelog.__init__(self, opener)
139 changelog.changelog.__init__(self, opener)
140 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile)
140 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile)
141
141
142 class bundlemanifest(bundlerevlog, manifest.manifest):
142 class bundlemanifest(bundlerevlog, manifest.manifest):
143 def __init__(self, opener, bundlefile, linkmapper):
143 def __init__(self, opener, bundlefile, linkmapper):
144 manifest.manifest.__init__(self, opener)
144 manifest.manifest.__init__(self, opener)
145 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
145 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
146 linkmapper)
146 linkmapper)
147
147
148 class bundlefilelog(bundlerevlog, filelog.filelog):
148 class bundlefilelog(bundlerevlog, filelog.filelog):
149 def __init__(self, opener, path, bundlefile, linkmapper):
149 def __init__(self, opener, path, bundlefile, linkmapper):
150 filelog.filelog.__init__(self, opener, path)
150 filelog.filelog.__init__(self, opener, path)
151 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
151 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
152 linkmapper)
152 linkmapper)
153
153
154 class bundlerepository(localrepo.localrepository):
154 class bundlerepository(localrepo.localrepository):
155 def __init__(self, ui, path, bundlename):
155 def __init__(self, ui, path, bundlename):
156 self._tempparent = None
156 self._tempparent = None
157 try:
157 try:
158 localrepo.localrepository.__init__(self, ui, path)
158 localrepo.localrepository.__init__(self, ui, path)
159 except error.RepoError:
159 except error.RepoError:
160 self._tempparent = tempfile.mkdtemp()
160 self._tempparent = tempfile.mkdtemp()
161 tmprepo = localrepo.instance(ui,self._tempparent,1)
161 tmprepo = localrepo.instance(ui,self._tempparent,1)
162 localrepo.localrepository.__init__(self, ui, self._tempparent)
162 localrepo.localrepository.__init__(self, ui, self._tempparent)
163
163
164 if path:
164 if path:
165 self._url = 'bundle:' + path + '+' + bundlename
165 self._url = 'bundle:' + path + '+' + bundlename
166 else:
166 else:
167 self._url = 'bundle:' + bundlename
167 self._url = 'bundle:' + bundlename
168
168
169 self.tempfile = None
169 self.tempfile = None
170 self.bundlefile = open(bundlename, "rb")
170 self.bundlefile = open(bundlename, "rb")
171 header = self.bundlefile.read(6)
171 header = self.bundlefile.read(6)
172 if not header.startswith("HG"):
172 if not header.startswith("HG"):
173 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
173 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
174 elif not header.startswith("HG10"):
174 elif not header.startswith("HG10"):
175 raise util.Abort(_("%s: unknown bundle version") % bundlename)
175 raise util.Abort(_("%s: unknown bundle version") % bundlename)
176 elif (header == "HG10BZ") or (header == "HG10GZ"):
176 elif (header == "HG10BZ") or (header == "HG10GZ"):
177 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
177 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
178 suffix=".hg10un", dir=self.path)
178 suffix=".hg10un", dir=self.path)
179 self.tempfile = temp
179 self.tempfile = temp
180 fptemp = os.fdopen(fdtemp, 'wb')
180 fptemp = os.fdopen(fdtemp, 'wb')
181 def generator(f):
181 def generator(f):
182 if header == "HG10BZ":
182 if header == "HG10BZ":
183 zd = bz2.BZ2Decompressor()
183 zd = bz2.BZ2Decompressor()
184 zd.decompress("BZ")
184 zd.decompress("BZ")
185 elif header == "HG10GZ":
185 elif header == "HG10GZ":
186 zd = zlib.decompressobj()
186 zd = zlib.decompressobj()
187 for chunk in f:
187 for chunk in f:
188 yield zd.decompress(chunk)
188 yield zd.decompress(chunk)
189 gen = generator(util.filechunkiter(self.bundlefile, 4096))
189 gen = generator(util.filechunkiter(self.bundlefile, 4096))
190
190
191 try:
191 try:
192 fptemp.write("HG10UN")
192 fptemp.write("HG10UN")
193 for chunk in gen:
193 for chunk in gen:
194 fptemp.write(chunk)
194 fptemp.write(chunk)
195 finally:
195 finally:
196 fptemp.close()
196 fptemp.close()
197 self.bundlefile.close()
197 self.bundlefile.close()
198
198
199 self.bundlefile = open(self.tempfile, "rb")
199 self.bundlefile = open(self.tempfile, "rb")
200 # seek right after the header
200 # seek right after the header
201 self.bundlefile.seek(6)
201 self.bundlefile.seek(6)
202 elif header == "HG10UN":
202 elif header == "HG10UN":
203 # nothing to do
203 # nothing to do
204 pass
204 pass
205 else:
205 else:
206 raise util.Abort(_("%s: unknown bundle compression type")
206 raise util.Abort(_("%s: unknown bundle compression type")
207 % bundlename)
207 % bundlename)
208 # dict with the mapping 'filename' -> position in the bundle
208 # dict with the mapping 'filename' -> position in the bundle
209 self.bundlefilespos = {}
209 self.bundlefilespos = {}
210
210
211 def __getattr__(self, name):
211 def __getattr__(self, name):
212 if name == 'changelog':
212 if name == 'changelog':
213 self.changelog = bundlechangelog(self.sopener, self.bundlefile)
213 self.changelog = bundlechangelog(self.sopener, self.bundlefile)
214 self.manstart = self.bundlefile.tell()
214 self.manstart = self.bundlefile.tell()
215 return self.changelog
215 return self.changelog
216 elif name == 'manifest':
216 elif name == 'manifest':
217 self.bundlefile.seek(self.manstart)
217 self.bundlefile.seek(self.manstart)
218 self.manifest = bundlemanifest(self.sopener, self.bundlefile,
218 self.manifest = bundlemanifest(self.sopener, self.bundlefile,
219 self.changelog.rev)
219 self.changelog.rev)
220 self.filestart = self.bundlefile.tell()
220 self.filestart = self.bundlefile.tell()
221 return self.manifest
221 return self.manifest
222 elif name == 'manstart':
222 elif name == 'manstart':
223 self.changelog
223 self.changelog
224 return self.manstart
224 return self.manstart
225 elif name == 'filestart':
225 elif name == 'filestart':
226 self.manifest
226 self.manifest
227 return self.filestart
227 return self.filestart
228 else:
228 else:
229 raise AttributeError(name)
229 return localrepo.localrepository.__getattr__(self, name)
230
230
231 def url(self):
231 def url(self):
232 return self._url
232 return self._url
233
233
234 def file(self, f):
234 def file(self, f):
235 if not self.bundlefilespos:
235 if not self.bundlefilespos:
236 self.bundlefile.seek(self.filestart)
236 self.bundlefile.seek(self.filestart)
237 while 1:
237 while 1:
238 chunk = changegroup.getchunk(self.bundlefile)
238 chunk = changegroup.getchunk(self.bundlefile)
239 if not chunk:
239 if not chunk:
240 break
240 break
241 self.bundlefilespos[chunk] = self.bundlefile.tell()
241 self.bundlefilespos[chunk] = self.bundlefile.tell()
242 for c in changegroup.chunkiter(self.bundlefile):
242 for c in changegroup.chunkiter(self.bundlefile):
243 pass
243 pass
244
244
245 if f[0] == '/':
245 if f[0] == '/':
246 f = f[1:]
246 f = f[1:]
247 if f in self.bundlefilespos:
247 if f in self.bundlefilespos:
248 self.bundlefile.seek(self.bundlefilespos[f])
248 self.bundlefile.seek(self.bundlefilespos[f])
249 return bundlefilelog(self.sopener, f, self.bundlefile,
249 return bundlefilelog(self.sopener, f, self.bundlefile,
250 self.changelog.rev)
250 self.changelog.rev)
251 else:
251 else:
252 return filelog.filelog(self.sopener, f)
252 return filelog.filelog(self.sopener, f)
253
253
254 def close(self):
254 def close(self):
255 """Close assigned bundle file immediately."""
255 """Close assigned bundle file immediately."""
256 self.bundlefile.close()
256 self.bundlefile.close()
257
257
258 def __del__(self):
258 def __del__(self):
259 bundlefile = getattr(self, 'bundlefile', None)
259 bundlefile = getattr(self, 'bundlefile', None)
260 if bundlefile and not bundlefile.closed:
260 if bundlefile and not bundlefile.closed:
261 bundlefile.close()
261 bundlefile.close()
262 tempfile = getattr(self, 'tempfile', None)
262 tempfile = getattr(self, 'tempfile', None)
263 if tempfile is not None:
263 if tempfile is not None:
264 os.unlink(tempfile)
264 os.unlink(tempfile)
265 if self._tempparent:
265 if self._tempparent:
266 shutil.rmtree(self._tempparent, True)
266 shutil.rmtree(self._tempparent, True)
267
267
268 def cancopy(self):
268 def cancopy(self):
269 return False
269 return False
270
270
271 def getcwd(self):
271 def getcwd(self):
272 return os.getcwd() # always outside the repo
272 return os.getcwd() # always outside the repo
273
273
274 def instance(ui, path, create):
274 def instance(ui, path, create):
275 if create:
275 if create:
276 raise util.Abort(_('cannot create new bundle repository'))
276 raise util.Abort(_('cannot create new bundle repository'))
277 parentpath = ui.config("bundle", "mainreporoot", "")
277 parentpath = ui.config("bundle", "mainreporoot", "")
278 if parentpath:
278 if parentpath:
279 # Try to make the full path relative so we get a nice, short URL.
279 # Try to make the full path relative so we get a nice, short URL.
280 # In particular, we don't want temp dir names in test outputs.
280 # In particular, we don't want temp dir names in test outputs.
281 cwd = os.getcwd()
281 cwd = os.getcwd()
282 if parentpath == cwd:
282 if parentpath == cwd:
283 parentpath = ''
283 parentpath = ''
284 else:
284 else:
285 cwd = os.path.join(cwd,'')
285 cwd = os.path.join(cwd,'')
286 if parentpath.startswith(cwd):
286 if parentpath.startswith(cwd):
287 parentpath = parentpath[len(cwd):]
287 parentpath = parentpath[len(cwd):]
288 path = util.drop_scheme('file', path)
288 path = util.drop_scheme('file', path)
289 if path.startswith('bundle:'):
289 if path.startswith('bundle:'):
290 path = util.drop_scheme('bundle', path)
290 path = util.drop_scheme('bundle', path)
291 s = path.split("+", 1)
291 s = path.split("+", 1)
292 if len(s) == 1:
292 if len(s) == 1:
293 repopath, bundlename = parentpath, s[0]
293 repopath, bundlename = parentpath, s[0]
294 else:
294 else:
295 repopath, bundlename = s
295 repopath, bundlename = s
296 else:
296 else:
297 repopath, bundlename = parentpath, path
297 repopath, bundlename = parentpath, path
298 return bundlerepository(ui, repopath, bundlename)
298 return bundlerepository(ui, repopath, bundlename)
@@ -1,142 +1,149 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 cp "$TESTDIR"/printenv.py .
3 cp "$TESTDIR"/printenv.py .
4
4
5 echo "====== Setting up test"
5 echo "====== Setting up test"
6 hg init test
6 hg init test
7 cd test
7 cd test
8 echo 0 > afile
8 echo 0 > afile
9 hg add afile
9 hg add afile
10 hg commit -m "0.0" -d "1000000 0"
10 hg commit -m "0.0" -d "1000000 0"
11 echo 1 >> afile
11 echo 1 >> afile
12 hg commit -m "0.1" -d "1000000 0"
12 hg commit -m "0.1" -d "1000000 0"
13 echo 2 >> afile
13 echo 2 >> afile
14 hg commit -m "0.2" -d "1000000 0"
14 hg commit -m "0.2" -d "1000000 0"
15 echo 3 >> afile
15 echo 3 >> afile
16 hg commit -m "0.3" -d "1000000 0"
16 hg commit -m "0.3" -d "1000000 0"
17 hg update -C 0
17 hg update -C 0
18 echo 1 >> afile
18 echo 1 >> afile
19 hg commit -m "1.1" -d "1000000 0"
19 hg commit -m "1.1" -d "1000000 0"
20 echo 2 >> afile
20 echo 2 >> afile
21 hg commit -m "1.2" -d "1000000 0"
21 hg commit -m "1.2" -d "1000000 0"
22 echo "a line" > fred
22 echo "a line" > fred
23 echo 3 >> afile
23 echo 3 >> afile
24 hg add fred
24 hg add fred
25 hg commit -m "1.3" -d "1000000 0"
25 hg commit -m "1.3" -d "1000000 0"
26 hg mv afile adifferentfile
26 hg mv afile adifferentfile
27 hg commit -m "1.3m" -d "1000000 0"
27 hg commit -m "1.3m" -d "1000000 0"
28 hg update -C 3
28 hg update -C 3
29 hg mv afile anotherfile
29 hg mv afile anotherfile
30 hg commit -m "0.3m" -d "1000000 0"
30 hg commit -m "0.3m" -d "1000000 0"
31 hg verify
31 hg verify
32 cd ..
32 cd ..
33 hg init empty
33 hg init empty
34
34
35 echo "====== Bundle --all"
35 echo "====== Bundle --all"
36 hg -R test bundle --all all.hg
36 hg -R test bundle --all all.hg
37
37 echo "====== Bundle test to full.hg"
38 echo "====== Bundle test to full.hg"
38 hg -R test bundle full.hg empty
39 hg -R test bundle full.hg empty
39 echo "====== Unbundle full.hg in test"
40 echo "====== Unbundle full.hg in test"
40 hg -R test unbundle full.hg
41 hg -R test unbundle full.hg
41 echo "====== Verify empty"
42 echo "====== Verify empty"
42 hg -R empty heads
43 hg -R empty heads
43 hg -R empty verify
44 hg -R empty verify
44
45
45 echo "====== Pull full.hg into test (using --cwd)"
46 echo "====== Pull full.hg into test (using --cwd)"
46 hg --cwd test pull ../full.hg
47 hg --cwd test pull ../full.hg
47 echo "====== Pull full.hg into empty (using --cwd)"
48 echo "====== Pull full.hg into empty (using --cwd)"
48 hg --cwd empty pull ../full.hg
49 hg --cwd empty pull ../full.hg
49 echo "====== Rollback empty"
50 echo "====== Rollback empty"
50 hg -R empty rollback
51 hg -R empty rollback
51 echo "====== Pull full.hg into empty again (using --cwd)"
52 echo "====== Pull full.hg into empty again (using --cwd)"
52 hg --cwd empty pull ../full.hg
53 hg --cwd empty pull ../full.hg
53
54
54 echo "====== Pull full.hg into test (using -R)"
55 echo "====== Pull full.hg into test (using -R)"
55 hg -R test pull full.hg
56 hg -R test pull full.hg
56 echo "====== Pull full.hg into empty (using -R)"
57 echo "====== Pull full.hg into empty (using -R)"
57 hg -R empty pull full.hg
58 hg -R empty pull full.hg
58 echo "====== Rollback empty"
59 echo "====== Rollback empty"
59 hg -R empty rollback
60 hg -R empty rollback
60 echo "====== Pull full.hg into empty again (using -R)"
61 echo "====== Pull full.hg into empty again (using -R)"
61 hg -R empty pull full.hg
62 hg -R empty pull full.hg
62
63
63 echo "====== Log -R full.hg in fresh empty"
64 echo "====== Log -R full.hg in fresh empty"
64 rm -r empty
65 rm -r empty
65 hg init empty
66 hg init empty
66 cd empty
67 cd empty
67 hg -R bundle://../full.hg log
68 hg -R bundle://../full.hg log
68
69
69 echo "====== Pull ../full.hg into empty (with hook)"
70 echo "====== Pull ../full.hg into empty (with hook)"
70 echo '[hooks]' >> .hg/hgrc
71 echo '[hooks]' >> .hg/hgrc
71 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
72 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
72 #doesn't work (yet ?)
73 #doesn't work (yet ?)
73 #hg -R bundle://../full.hg verify
74 #hg -R bundle://../full.hg verify
74 hg pull bundle://../full.hg
75 hg pull bundle://../full.hg
75 echo "====== Rollback empty"
76 echo "====== Rollback empty"
76 hg rollback
77 hg rollback
77 cd ..
78 cd ..
78 echo "====== Log -R bundle:empty+full.hg"
79 echo "====== Log -R bundle:empty+full.hg"
79 hg -R bundle:empty+full.hg log --template="{rev} "
80 hg -R bundle:empty+full.hg log --template="{rev} "
80 echo ""
81 echo ""
81 echo "====== Pull full.hg into empty again (using -R; with hook)"
82 echo "====== Pull full.hg into empty again (using -R; with hook)"
82 hg -R empty pull full.hg
83 hg -R empty pull full.hg
83
84
84 echo "====== Create partial clones"
85 echo "====== Create partial clones"
85 rm -r empty
86 rm -r empty
86 hg init empty
87 hg init empty
87 hg clone -r 3 test partial
88 hg clone -r 3 test partial
88 hg clone partial partial2
89 hg clone partial partial2
89 cd partial
90 cd partial
90 echo "====== Log -R full.hg in partial"
91 echo "====== Log -R full.hg in partial"
91 hg -R bundle://../full.hg log
92 hg -R bundle://../full.hg log
92 echo "====== Incoming full.hg in partial"
93 echo "====== Incoming full.hg in partial"
93 hg incoming bundle://../full.hg
94 hg incoming bundle://../full.hg
94 echo "====== Outgoing -R full.hg vs partial2 in partial"
95 echo "====== Outgoing -R full.hg vs partial2 in partial"
95 hg -R bundle://../full.hg outgoing ../partial2
96 hg -R bundle://../full.hg outgoing ../partial2
96 echo "====== Outgoing -R does-not-exist.hg vs partial2 in partial"
97 echo "====== Outgoing -R does-not-exist.hg vs partial2 in partial"
97 hg -R bundle://../does-not-exist.hg outgoing ../partial2
98 hg -R bundle://../does-not-exist.hg outgoing ../partial2
98 cd ..
99 cd ..
99
100
100 echo "====== Direct clone from bundle (all-history)"
101 echo "====== Direct clone from bundle (all-history)"
101 hg clone full.hg full-clone
102 hg clone full.hg full-clone
102 hg -R full-clone heads
103 hg -R full-clone heads
103 rm -r full-clone
104 rm -r full-clone
104
105
105 # test for http://www.selenic.com/mercurial/bts/issue216
106 # test for http://www.selenic.com/mercurial/bts/issue216
106 echo "====== Unbundle incremental bundles into fresh empty in one go"
107 echo "====== Unbundle incremental bundles into fresh empty in one go"
107 rm -r empty
108 rm -r empty
108 hg init empty
109 hg init empty
109 hg -R test bundle --base null -r 0 ../0.hg
110 hg -R test bundle --base null -r 0 ../0.hg
110 hg -R test bundle --base 0 -r 1 ../1.hg
111 hg -R test bundle --base 0 -r 1 ../1.hg
111 hg -R empty unbundle -u ../0.hg ../1.hg
112 hg -R empty unbundle -u ../0.hg ../1.hg
112
113
113 # test for 540d1059c802
114 # test for 540d1059c802
114 echo "====== test for 540d1059c802"
115 echo "====== test for 540d1059c802"
115 hg init orig
116 hg init orig
116 cd orig
117 cd orig
117 echo foo > foo
118 echo foo > foo
118 hg add foo
119 hg add foo
119 hg ci -m 'add foo' -d '0 0'
120 hg ci -m 'add foo' -d '0 0'
120
121
121 hg clone . ../copy
122 hg clone . ../copy
122 hg tag -d '0 0' foo
123 hg tag -d '0 0' foo
123
124
124 cd ../copy
125 cd ../copy
125 echo >> foo
126 echo >> foo
126 hg ci -m 'change foo' -d '0 0'
127 hg ci -m 'change foo' -d '0 0'
127 hg bundle ../bundle.hg ../orig
128 hg bundle ../bundle.hg ../orig
128
129
129 cd ../orig
130 cd ../orig
130 hg incoming ../bundle.hg
131 hg incoming ../bundle.hg
131 cd ..
132 cd ..
132
133
133 # test for http://www.selenic.com/mercurial/bts/issue1144
134 # test for http://www.selenic.com/mercurial/bts/issue1144
134 echo "===== test that verify bundle does not traceback"
135 echo "===== test that verify bundle does not traceback"
135 # partial history bundle, fails w/ unkown parent
136 # partial history bundle, fails w/ unkown parent
136 hg -R bundle.hg verify
137 hg -R bundle.hg verify
137 # full history bundle, refuses to verify non-local repo
138 # full history bundle, refuses to verify non-local repo
138 hg -R all.hg verify
139 hg -R all.hg verify
139 # but, regular verify must continue to work
140 # but, regular verify must continue to work
140 hg -R orig verify
141 hg -R orig verify
141
142
143 echo "====== diff against bundle"
144 hg init b
145 cd b
146 hg -R ../all.hg diff -r tip
147 cd ..
142
148
149
@@ -1,319 +1,328 b''
1 ====== Setting up test
1 ====== Setting up test
2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 created new head
3 created new head
4 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
4 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
5 checking changesets
5 checking changesets
6 checking manifests
6 checking manifests
7 crosschecking files in changesets and manifests
7 crosschecking files in changesets and manifests
8 checking files
8 checking files
9 4 files, 9 changesets, 7 total revisions
9 4 files, 9 changesets, 7 total revisions
10 ====== Bundle --all
10 ====== Bundle --all
11 9 changesets found
11 9 changesets found
12 ====== Bundle test to full.hg
12 ====== Bundle test to full.hg
13 searching for changes
13 searching for changes
14 9 changesets found
14 9 changesets found
15 ====== Unbundle full.hg in test
15 ====== Unbundle full.hg in test
16 adding changesets
16 adding changesets
17 adding manifests
17 adding manifests
18 adding file changes
18 adding file changes
19 added 0 changesets with 0 changes to 4 files
19 added 0 changesets with 0 changes to 4 files
20 (run 'hg update' to get a working copy)
20 (run 'hg update' to get a working copy)
21 ====== Verify empty
21 ====== Verify empty
22 changeset: -1:000000000000
22 changeset: -1:000000000000
23 tag: tip
23 tag: tip
24 user:
24 user:
25 date: Thu Jan 01 00:00:00 1970 +0000
25 date: Thu Jan 01 00:00:00 1970 +0000
26
26
27 checking changesets
27 checking changesets
28 checking manifests
28 checking manifests
29 crosschecking files in changesets and manifests
29 crosschecking files in changesets and manifests
30 checking files
30 checking files
31 0 files, 0 changesets, 0 total revisions
31 0 files, 0 changesets, 0 total revisions
32 ====== Pull full.hg into test (using --cwd)
32 ====== Pull full.hg into test (using --cwd)
33 pulling from ../full.hg
33 pulling from ../full.hg
34 searching for changes
34 searching for changes
35 no changes found
35 no changes found
36 ====== Pull full.hg into empty (using --cwd)
36 ====== Pull full.hg into empty (using --cwd)
37 pulling from ../full.hg
37 pulling from ../full.hg
38 requesting all changes
38 requesting all changes
39 adding changesets
39 adding changesets
40 adding manifests
40 adding manifests
41 adding file changes
41 adding file changes
42 added 9 changesets with 7 changes to 4 files (+1 heads)
42 added 9 changesets with 7 changes to 4 files (+1 heads)
43 (run 'hg heads' to see heads, 'hg merge' to merge)
43 (run 'hg heads' to see heads, 'hg merge' to merge)
44 ====== Rollback empty
44 ====== Rollback empty
45 rolling back last transaction
45 rolling back last transaction
46 ====== Pull full.hg into empty again (using --cwd)
46 ====== Pull full.hg into empty again (using --cwd)
47 pulling from ../full.hg
47 pulling from ../full.hg
48 requesting all changes
48 requesting all changes
49 adding changesets
49 adding changesets
50 adding manifests
50 adding manifests
51 adding file changes
51 adding file changes
52 added 9 changesets with 7 changes to 4 files (+1 heads)
52 added 9 changesets with 7 changes to 4 files (+1 heads)
53 (run 'hg heads' to see heads, 'hg merge' to merge)
53 (run 'hg heads' to see heads, 'hg merge' to merge)
54 ====== Pull full.hg into test (using -R)
54 ====== Pull full.hg into test (using -R)
55 pulling from full.hg
55 pulling from full.hg
56 searching for changes
56 searching for changes
57 no changes found
57 no changes found
58 ====== Pull full.hg into empty (using -R)
58 ====== Pull full.hg into empty (using -R)
59 pulling from full.hg
59 pulling from full.hg
60 searching for changes
60 searching for changes
61 no changes found
61 no changes found
62 ====== Rollback empty
62 ====== Rollback empty
63 rolling back last transaction
63 rolling back last transaction
64 ====== Pull full.hg into empty again (using -R)
64 ====== Pull full.hg into empty again (using -R)
65 pulling from full.hg
65 pulling from full.hg
66 requesting all changes
66 requesting all changes
67 adding changesets
67 adding changesets
68 adding manifests
68 adding manifests
69 adding file changes
69 adding file changes
70 added 9 changesets with 7 changes to 4 files (+1 heads)
70 added 9 changesets with 7 changes to 4 files (+1 heads)
71 (run 'hg heads' to see heads, 'hg merge' to merge)
71 (run 'hg heads' to see heads, 'hg merge' to merge)
72 ====== Log -R full.hg in fresh empty
72 ====== Log -R full.hg in fresh empty
73 changeset: 8:836ac62537ab
73 changeset: 8:836ac62537ab
74 tag: tip
74 tag: tip
75 parent: 3:ac69c658229d
75 parent: 3:ac69c658229d
76 user: test
76 user: test
77 date: Mon Jan 12 13:46:40 1970 +0000
77 date: Mon Jan 12 13:46:40 1970 +0000
78 summary: 0.3m
78 summary: 0.3m
79
79
80 changeset: 7:80fe151401c2
80 changeset: 7:80fe151401c2
81 user: test
81 user: test
82 date: Mon Jan 12 13:46:40 1970 +0000
82 date: Mon Jan 12 13:46:40 1970 +0000
83 summary: 1.3m
83 summary: 1.3m
84
84
85 changeset: 6:1e3f6b843bd6
85 changeset: 6:1e3f6b843bd6
86 user: test
86 user: test
87 date: Mon Jan 12 13:46:40 1970 +0000
87 date: Mon Jan 12 13:46:40 1970 +0000
88 summary: 1.3
88 summary: 1.3
89
89
90 changeset: 5:024e4e7df376
90 changeset: 5:024e4e7df376
91 user: test
91 user: test
92 date: Mon Jan 12 13:46:40 1970 +0000
92 date: Mon Jan 12 13:46:40 1970 +0000
93 summary: 1.2
93 summary: 1.2
94
94
95 changeset: 4:5f4f3ceb285e
95 changeset: 4:5f4f3ceb285e
96 parent: 0:5649c9d34dd8
96 parent: 0:5649c9d34dd8
97 user: test
97 user: test
98 date: Mon Jan 12 13:46:40 1970 +0000
98 date: Mon Jan 12 13:46:40 1970 +0000
99 summary: 1.1
99 summary: 1.1
100
100
101 changeset: 3:ac69c658229d
101 changeset: 3:ac69c658229d
102 user: test
102 user: test
103 date: Mon Jan 12 13:46:40 1970 +0000
103 date: Mon Jan 12 13:46:40 1970 +0000
104 summary: 0.3
104 summary: 0.3
105
105
106 changeset: 2:d62976ca1e50
106 changeset: 2:d62976ca1e50
107 user: test
107 user: test
108 date: Mon Jan 12 13:46:40 1970 +0000
108 date: Mon Jan 12 13:46:40 1970 +0000
109 summary: 0.2
109 summary: 0.2
110
110
111 changeset: 1:10b2180f755b
111 changeset: 1:10b2180f755b
112 user: test
112 user: test
113 date: Mon Jan 12 13:46:40 1970 +0000
113 date: Mon Jan 12 13:46:40 1970 +0000
114 summary: 0.1
114 summary: 0.1
115
115
116 changeset: 0:5649c9d34dd8
116 changeset: 0:5649c9d34dd8
117 user: test
117 user: test
118 date: Mon Jan 12 13:46:40 1970 +0000
118 date: Mon Jan 12 13:46:40 1970 +0000
119 summary: 0.0
119 summary: 0.0
120
120
121 ====== Pull ../full.hg into empty (with hook)
121 ====== Pull ../full.hg into empty (with hook)
122 changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:../full.hg
122 changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:../full.hg
123 pulling from bundle://../full.hg
123 pulling from bundle://../full.hg
124 requesting all changes
124 requesting all changes
125 adding changesets
125 adding changesets
126 adding manifests
126 adding manifests
127 adding file changes
127 adding file changes
128 added 9 changesets with 7 changes to 4 files (+1 heads)
128 added 9 changesets with 7 changes to 4 files (+1 heads)
129 (run 'hg heads' to see heads, 'hg merge' to merge)
129 (run 'hg heads' to see heads, 'hg merge' to merge)
130 ====== Rollback empty
130 ====== Rollback empty
131 rolling back last transaction
131 rolling back last transaction
132 ====== Log -R bundle:empty+full.hg
132 ====== Log -R bundle:empty+full.hg
133 8 7 6 5 4 3 2 1 0
133 8 7 6 5 4 3 2 1 0
134 ====== Pull full.hg into empty again (using -R; with hook)
134 ====== Pull full.hg into empty again (using -R; with hook)
135 changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
135 changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
136 pulling from full.hg
136 pulling from full.hg
137 requesting all changes
137 requesting all changes
138 adding changesets
138 adding changesets
139 adding manifests
139 adding manifests
140 adding file changes
140 adding file changes
141 added 9 changesets with 7 changes to 4 files (+1 heads)
141 added 9 changesets with 7 changes to 4 files (+1 heads)
142 (run 'hg heads' to see heads, 'hg merge' to merge)
142 (run 'hg heads' to see heads, 'hg merge' to merge)
143 ====== Create partial clones
143 ====== Create partial clones
144 requesting all changes
144 requesting all changes
145 adding changesets
145 adding changesets
146 adding manifests
146 adding manifests
147 adding file changes
147 adding file changes
148 added 4 changesets with 4 changes to 1 files
148 added 4 changesets with 4 changes to 1 files
149 updating working directory
149 updating working directory
150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 updating working directory
151 updating working directory
152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 ====== Log -R full.hg in partial
153 ====== Log -R full.hg in partial
154 changeset: 8:836ac62537ab
154 changeset: 8:836ac62537ab
155 tag: tip
155 tag: tip
156 parent: 3:ac69c658229d
156 parent: 3:ac69c658229d
157 user: test
157 user: test
158 date: Mon Jan 12 13:46:40 1970 +0000
158 date: Mon Jan 12 13:46:40 1970 +0000
159 summary: 0.3m
159 summary: 0.3m
160
160
161 changeset: 7:80fe151401c2
161 changeset: 7:80fe151401c2
162 user: test
162 user: test
163 date: Mon Jan 12 13:46:40 1970 +0000
163 date: Mon Jan 12 13:46:40 1970 +0000
164 summary: 1.3m
164 summary: 1.3m
165
165
166 changeset: 6:1e3f6b843bd6
166 changeset: 6:1e3f6b843bd6
167 user: test
167 user: test
168 date: Mon Jan 12 13:46:40 1970 +0000
168 date: Mon Jan 12 13:46:40 1970 +0000
169 summary: 1.3
169 summary: 1.3
170
170
171 changeset: 5:024e4e7df376
171 changeset: 5:024e4e7df376
172 user: test
172 user: test
173 date: Mon Jan 12 13:46:40 1970 +0000
173 date: Mon Jan 12 13:46:40 1970 +0000
174 summary: 1.2
174 summary: 1.2
175
175
176 changeset: 4:5f4f3ceb285e
176 changeset: 4:5f4f3ceb285e
177 parent: 0:5649c9d34dd8
177 parent: 0:5649c9d34dd8
178 user: test
178 user: test
179 date: Mon Jan 12 13:46:40 1970 +0000
179 date: Mon Jan 12 13:46:40 1970 +0000
180 summary: 1.1
180 summary: 1.1
181
181
182 changeset: 3:ac69c658229d
182 changeset: 3:ac69c658229d
183 user: test
183 user: test
184 date: Mon Jan 12 13:46:40 1970 +0000
184 date: Mon Jan 12 13:46:40 1970 +0000
185 summary: 0.3
185 summary: 0.3
186
186
187 changeset: 2:d62976ca1e50
187 changeset: 2:d62976ca1e50
188 user: test
188 user: test
189 date: Mon Jan 12 13:46:40 1970 +0000
189 date: Mon Jan 12 13:46:40 1970 +0000
190 summary: 0.2
190 summary: 0.2
191
191
192 changeset: 1:10b2180f755b
192 changeset: 1:10b2180f755b
193 user: test
193 user: test
194 date: Mon Jan 12 13:46:40 1970 +0000
194 date: Mon Jan 12 13:46:40 1970 +0000
195 summary: 0.1
195 summary: 0.1
196
196
197 changeset: 0:5649c9d34dd8
197 changeset: 0:5649c9d34dd8
198 user: test
198 user: test
199 date: Mon Jan 12 13:46:40 1970 +0000
199 date: Mon Jan 12 13:46:40 1970 +0000
200 summary: 0.0
200 summary: 0.0
201
201
202 ====== Incoming full.hg in partial
202 ====== Incoming full.hg in partial
203 comparing with bundle://../full.hg
203 comparing with bundle://../full.hg
204 searching for changes
204 searching for changes
205 changeset: 4:5f4f3ceb285e
205 changeset: 4:5f4f3ceb285e
206 parent: 0:5649c9d34dd8
206 parent: 0:5649c9d34dd8
207 user: test
207 user: test
208 date: Mon Jan 12 13:46:40 1970 +0000
208 date: Mon Jan 12 13:46:40 1970 +0000
209 summary: 1.1
209 summary: 1.1
210
210
211 changeset: 5:024e4e7df376
211 changeset: 5:024e4e7df376
212 user: test
212 user: test
213 date: Mon Jan 12 13:46:40 1970 +0000
213 date: Mon Jan 12 13:46:40 1970 +0000
214 summary: 1.2
214 summary: 1.2
215
215
216 changeset: 6:1e3f6b843bd6
216 changeset: 6:1e3f6b843bd6
217 user: test
217 user: test
218 date: Mon Jan 12 13:46:40 1970 +0000
218 date: Mon Jan 12 13:46:40 1970 +0000
219 summary: 1.3
219 summary: 1.3
220
220
221 changeset: 7:80fe151401c2
221 changeset: 7:80fe151401c2
222 user: test
222 user: test
223 date: Mon Jan 12 13:46:40 1970 +0000
223 date: Mon Jan 12 13:46:40 1970 +0000
224 summary: 1.3m
224 summary: 1.3m
225
225
226 changeset: 8:836ac62537ab
226 changeset: 8:836ac62537ab
227 tag: tip
227 tag: tip
228 parent: 3:ac69c658229d
228 parent: 3:ac69c658229d
229 user: test
229 user: test
230 date: Mon Jan 12 13:46:40 1970 +0000
230 date: Mon Jan 12 13:46:40 1970 +0000
231 summary: 0.3m
231 summary: 0.3m
232
232
233 ====== Outgoing -R full.hg vs partial2 in partial
233 ====== Outgoing -R full.hg vs partial2 in partial
234 comparing with ../partial2
234 comparing with ../partial2
235 searching for changes
235 searching for changes
236 changeset: 4:5f4f3ceb285e
236 changeset: 4:5f4f3ceb285e
237 parent: 0:5649c9d34dd8
237 parent: 0:5649c9d34dd8
238 user: test
238 user: test
239 date: Mon Jan 12 13:46:40 1970 +0000
239 date: Mon Jan 12 13:46:40 1970 +0000
240 summary: 1.1
240 summary: 1.1
241
241
242 changeset: 5:024e4e7df376
242 changeset: 5:024e4e7df376
243 user: test
243 user: test
244 date: Mon Jan 12 13:46:40 1970 +0000
244 date: Mon Jan 12 13:46:40 1970 +0000
245 summary: 1.2
245 summary: 1.2
246
246
247 changeset: 6:1e3f6b843bd6
247 changeset: 6:1e3f6b843bd6
248 user: test
248 user: test
249 date: Mon Jan 12 13:46:40 1970 +0000
249 date: Mon Jan 12 13:46:40 1970 +0000
250 summary: 1.3
250 summary: 1.3
251
251
252 changeset: 7:80fe151401c2
252 changeset: 7:80fe151401c2
253 user: test
253 user: test
254 date: Mon Jan 12 13:46:40 1970 +0000
254 date: Mon Jan 12 13:46:40 1970 +0000
255 summary: 1.3m
255 summary: 1.3m
256
256
257 changeset: 8:836ac62537ab
257 changeset: 8:836ac62537ab
258 tag: tip
258 tag: tip
259 parent: 3:ac69c658229d
259 parent: 3:ac69c658229d
260 user: test
260 user: test
261 date: Mon Jan 12 13:46:40 1970 +0000
261 date: Mon Jan 12 13:46:40 1970 +0000
262 summary: 0.3m
262 summary: 0.3m
263
263
264 ====== Outgoing -R does-not-exist.hg vs partial2 in partial
264 ====== Outgoing -R does-not-exist.hg vs partial2 in partial
265 abort: No such file or directory: ../does-not-exist.hg
265 abort: No such file or directory: ../does-not-exist.hg
266 ====== Direct clone from bundle (all-history)
266 ====== Direct clone from bundle (all-history)
267 requesting all changes
267 requesting all changes
268 adding changesets
268 adding changesets
269 adding manifests
269 adding manifests
270 adding file changes
270 adding file changes
271 added 9 changesets with 7 changes to 4 files (+1 heads)
271 added 9 changesets with 7 changes to 4 files (+1 heads)
272 updating working directory
272 updating working directory
273 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
273 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 changeset: 8:836ac62537ab
274 changeset: 8:836ac62537ab
275 tag: tip
275 tag: tip
276 parent: 3:ac69c658229d
276 parent: 3:ac69c658229d
277 user: test
277 user: test
278 date: Mon Jan 12 13:46:40 1970 +0000
278 date: Mon Jan 12 13:46:40 1970 +0000
279 summary: 0.3m
279 summary: 0.3m
280
280
281 changeset: 7:80fe151401c2
281 changeset: 7:80fe151401c2
282 user: test
282 user: test
283 date: Mon Jan 12 13:46:40 1970 +0000
283 date: Mon Jan 12 13:46:40 1970 +0000
284 summary: 1.3m
284 summary: 1.3m
285
285
286 ====== Unbundle incremental bundles into fresh empty in one go
286 ====== Unbundle incremental bundles into fresh empty in one go
287 1 changesets found
287 1 changesets found
288 1 changesets found
288 1 changesets found
289 adding changesets
289 adding changesets
290 adding manifests
290 adding manifests
291 adding file changes
291 adding file changes
292 added 1 changesets with 1 changes to 1 files
292 added 1 changesets with 1 changes to 1 files
293 adding changesets
293 adding changesets
294 adding manifests
294 adding manifests
295 adding file changes
295 adding file changes
296 added 1 changesets with 1 changes to 1 files
296 added 1 changesets with 1 changes to 1 files
297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 ====== test for 540d1059c802
298 ====== test for 540d1059c802
299 updating working directory
299 updating working directory
300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 searching for changes
301 searching for changes
302 1 changesets found
302 1 changesets found
303 comparing with ../bundle.hg
303 comparing with ../bundle.hg
304 searching for changes
304 searching for changes
305 changeset: 2:ed1b79f46b9a
305 changeset: 2:ed1b79f46b9a
306 tag: tip
306 tag: tip
307 parent: 0:bbd179dfa0a7
307 parent: 0:bbd179dfa0a7
308 user: test
308 user: test
309 date: Thu Jan 01 00:00:00 1970 +0000
309 date: Thu Jan 01 00:00:00 1970 +0000
310 summary: change foo
310 summary: change foo
311
311
312 ===== test that verify bundle does not traceback
312 ===== test that verify bundle does not traceback
313 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
313 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
314 abort: cannot verify bundle or remote repos
314 abort: cannot verify bundle or remote repos
315 checking changesets
315 checking changesets
316 checking manifests
316 checking manifests
317 crosschecking files in changesets and manifests
317 crosschecking files in changesets and manifests
318 checking files
318 checking files
319 2 files, 2 changesets, 2 total revisions
319 2 files, 2 changesets, 2 total revisions
320 ====== diff against bundle
321 diff -r 836ac62537ab anotherfile
322 --- a/anotherfile Mon Jan 12 13:46:40 1970 +0000
323 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
324 @@ -1,4 +0,0 @@
325 -0
326 -1
327 -2
328 -3
General Comments 0
You need to be logged in to leave comments. Login now