##// END OF EJS Templates
diff: recurse into subrepositories with --subrepos/-S flag
Martin Geisler -
r12167:d2c5b092 default
parent child Browse files
Show More
@@ -1,575 +1,575 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
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 # $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
14 # files (like LaTeX packages), that are mostly addressed to an
15 # audience not running a version control system.
15 # audience not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
18 # <http://mercurial.selenic.com/wiki/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 # Files to act upon/ignore are specified in the [keyword] section.
24 # Files to act upon/ignore are specified in the [keyword] section.
25 # Customized keyword template mappings in the [keywordmaps] section.
25 # Customized keyword template mappings in the [keywordmaps] section.
26 #
26 #
27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
28
28
29 '''expand keywords in tracked files
29 '''expand keywords in tracked files
30
30
31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
32 tracked text files selected by your configuration.
32 tracked text files selected by your configuration.
33
33
34 Keywords are only expanded in local repositories and not stored in the
34 Keywords are only expanded in local repositories and not stored in the
35 change history. The mechanism can be regarded as a convenience for the
35 change history. The mechanism can be regarded as a convenience for the
36 current user or for archive distribution.
36 current user or for archive distribution.
37
37
38 Configuration is done in the [keyword], [keywordset] and [keywordmaps]
38 Configuration is done in the [keyword], [keywordset] and [keywordmaps]
39 sections of hgrc files.
39 sections of hgrc files.
40
40
41 Example::
41 Example::
42
42
43 [keyword]
43 [keyword]
44 # expand keywords in every python file except those matching "x*"
44 # expand keywords in every python file except those matching "x*"
45 **.py =
45 **.py =
46 x* = ignore
46 x* = ignore
47
47
48 [keywordset]
48 [keywordset]
49 # prefer svn- over cvs-like default keywordmaps
49 # prefer svn- over cvs-like default keywordmaps
50 svn = True
50 svn = True
51
51
52 NOTE: the more specific you are in your filename patterns the less you
52 NOTE: the more specific you are in your filename patterns the less you
53 lose speed in huge repositories.
53 lose speed in huge repositories.
54
54
55 For [keywordmaps] template mapping and expansion demonstration and
55 For [keywordmaps] template mapping and expansion demonstration and
56 control run :hg:`kwdemo`. See :hg:`help templates` for a list of
56 control run :hg:`kwdemo`. See :hg:`help templates` for a list of
57 available templates and filters.
57 available templates and filters.
58
58
59 Three additional date template filters are provided::
59 Three additional date template filters are provided::
60
60
61 utcdate "2006/09/18 15:13:13"
61 utcdate "2006/09/18 15:13:13"
62 svnutcdate "2006-09-18 15:13:13Z"
62 svnutcdate "2006-09-18 15:13:13Z"
63 svnisodate "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"
63 svnisodate "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"
64
64
65 The default template mappings (view with :hg:`kwdemo -d`) can be
65 The default template mappings (view with :hg:`kwdemo -d`) can be
66 replaced with customized keywords and templates. Again, run
66 replaced with customized keywords and templates. Again, run
67 :hg:`kwdemo` to control the results of your config changes.
67 :hg:`kwdemo` to control the results of your config changes.
68
68
69 Before changing/disabling active keywords, run :hg:`kwshrink` to avoid
69 Before changing/disabling active keywords, run :hg:`kwshrink` to avoid
70 the risk of inadvertently storing expanded keywords in the change
70 the risk of inadvertently storing expanded keywords in the change
71 history.
71 history.
72
72
73 To force expansion after enabling it, or a configuration change, run
73 To force expansion after enabling it, or a configuration change, run
74 :hg:`kwexpand`.
74 :hg:`kwexpand`.
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 "Log =
77 like CVS' $Log$, are not supported. A keyword template map "Log =
78 {desc}" expands to the first line of the changeset description.
78 {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, match
82 from mercurial import patch, localrepo, templater, templatefilters, util, match
83 from mercurial.hgweb import webcommands
83 from mercurial.hgweb import webcommands
84 from mercurial.i18n import _
84 from mercurial.i18n import _
85 import re, shutil, tempfile
85 import re, shutil, tempfile
86
86
87 commands.optionalrepo += ' kwdemo'
87 commands.optionalrepo += ' kwdemo'
88
88
89 # hg commands that do not act on keywords
89 # hg commands that do not act on keywords
90 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
90 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
91 ' log outgoing push rename rollback tip verify'
91 ' log outgoing push rename rollback tip verify'
92 ' convert email glog')
92 ' convert email glog')
93
93
94 # hg commands that trigger expansion only when writing to working dir,
94 # hg commands that trigger expansion only when writing to working dir,
95 # not when reading filelog, and unexpand when reading from working dir
95 # not when reading filelog, and unexpand when reading from working dir
96 restricted = 'merge record qrecord resolve transplant'
96 restricted = 'merge record qrecord resolve transplant'
97
97
98 # commands using dorecord
98 # commands using dorecord
99 recordcommands = 'record qrecord'
99 recordcommands = 'record qrecord'
100 # names of extensions using dorecord
100 # names of extensions using dorecord
101 recordextensions = 'record'
101 recordextensions = 'record'
102
102
103 # date like in cvs' $Date
103 # date like in cvs' $Date
104 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
104 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
105 # date like in svn's $Date
105 # date like in svn's $Date
106 svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
106 svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
107 # date like in svn's $Id
107 # date like in svn's $Id
108 svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')
108 svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')
109
109
110 # make keyword tools accessible
110 # make keyword tools accessible
111 kwtools = {'templater': None, 'hgcmd': ''}
111 kwtools = {'templater': None, 'hgcmd': ''}
112
112
113
113
114 def _defaultkwmaps(ui):
114 def _defaultkwmaps(ui):
115 '''Returns default keywordmaps according to keywordset configuration.'''
115 '''Returns default keywordmaps according to keywordset configuration.'''
116 templates = {
116 templates = {
117 'Revision': '{node|short}',
117 'Revision': '{node|short}',
118 'Author': '{author|user}',
118 'Author': '{author|user}',
119 }
119 }
120 kwsets = ({
120 kwsets = ({
121 'Date': '{date|utcdate}',
121 'Date': '{date|utcdate}',
122 'RCSfile': '{file|basename},v',
122 'RCSfile': '{file|basename},v',
123 'RCSFile': '{file|basename},v', # kept for backwards compatibility
123 'RCSFile': '{file|basename},v', # kept for backwards compatibility
124 # with hg-keyword
124 # with hg-keyword
125 'Source': '{root}/{file},v',
125 'Source': '{root}/{file},v',
126 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
126 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
127 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
127 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
128 }, {
128 }, {
129 'Date': '{date|svnisodate}',
129 'Date': '{date|svnisodate}',
130 'Id': '{file|basename},v {node|short} {date|svnutcdate} {author|user}',
130 'Id': '{file|basename},v {node|short} {date|svnutcdate} {author|user}',
131 'LastChangedRevision': '{node|short}',
131 'LastChangedRevision': '{node|short}',
132 'LastChangedBy': '{author|user}',
132 'LastChangedBy': '{author|user}',
133 'LastChangedDate': '{date|svnisodate}',
133 'LastChangedDate': '{date|svnisodate}',
134 })
134 })
135 templates.update(kwsets[ui.configbool('keywordset', 'svn')])
135 templates.update(kwsets[ui.configbool('keywordset', 'svn')])
136 return templates
136 return templates
137
137
138 class kwtemplater(object):
138 class kwtemplater(object):
139 '''
139 '''
140 Sets up keyword templates, corresponding keyword regex, and
140 Sets up keyword templates, corresponding keyword regex, and
141 provides keyword substitution functions.
141 provides keyword substitution functions.
142 '''
142 '''
143
143
144 def __init__(self, ui, repo, inc, exc):
144 def __init__(self, ui, repo, inc, exc):
145 self.ui = ui
145 self.ui = ui
146 self.repo = repo
146 self.repo = repo
147 self.match = match.match(repo.root, '', [], inc, exc)
147 self.match = match.match(repo.root, '', [], inc, exc)
148 self.restrict = kwtools['hgcmd'] in restricted.split()
148 self.restrict = kwtools['hgcmd'] in restricted.split()
149 self.record = kwtools['hgcmd'] in recordcommands.split()
149 self.record = kwtools['hgcmd'] in recordcommands.split()
150
150
151 kwmaps = self.ui.configitems('keywordmaps')
151 kwmaps = self.ui.configitems('keywordmaps')
152 if kwmaps: # override default templates
152 if kwmaps: # override default templates
153 self.templates = dict((k, templater.parsestring(v, False))
153 self.templates = dict((k, templater.parsestring(v, False))
154 for k, v in kwmaps)
154 for k, v in kwmaps)
155 else:
155 else:
156 self.templates = _defaultkwmaps(self.ui)
156 self.templates = _defaultkwmaps(self.ui)
157 escaped = map(re.escape, self.templates.keys())
157 escaped = map(re.escape, self.templates.keys())
158 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
158 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
159 self.re_kw = re.compile(kwpat)
159 self.re_kw = re.compile(kwpat)
160
160
161 templatefilters.filters.update({'utcdate': utcdate,
161 templatefilters.filters.update({'utcdate': utcdate,
162 'svnisodate': svnisodate,
162 'svnisodate': svnisodate,
163 'svnutcdate': svnutcdate})
163 'svnutcdate': svnutcdate})
164
164
165 def substitute(self, data, path, ctx, subfunc):
165 def substitute(self, data, path, ctx, subfunc):
166 '''Replaces keywords in data with expanded template.'''
166 '''Replaces keywords in data with expanded template.'''
167 def kwsub(mobj):
167 def kwsub(mobj):
168 kw = mobj.group(1)
168 kw = mobj.group(1)
169 ct = cmdutil.changeset_templater(self.ui, self.repo,
169 ct = cmdutil.changeset_templater(self.ui, self.repo,
170 False, None, '', False)
170 False, None, '', False)
171 ct.use_template(self.templates[kw])
171 ct.use_template(self.templates[kw])
172 self.ui.pushbuffer()
172 self.ui.pushbuffer()
173 ct.show(ctx, root=self.repo.root, file=path)
173 ct.show(ctx, root=self.repo.root, file=path)
174 ekw = templatefilters.firstline(self.ui.popbuffer())
174 ekw = templatefilters.firstline(self.ui.popbuffer())
175 return '$%s: %s $' % (kw, ekw)
175 return '$%s: %s $' % (kw, ekw)
176 return subfunc(kwsub, data)
176 return subfunc(kwsub, data)
177
177
178 def expand(self, path, node, data):
178 def expand(self, path, node, data):
179 '''Returns data with keywords expanded.'''
179 '''Returns data with keywords expanded.'''
180 if not self.restrict and self.match(path) and not util.binary(data):
180 if not self.restrict and self.match(path) and not util.binary(data):
181 ctx = self.repo.filectx(path, fileid=node).changectx()
181 ctx = self.repo.filectx(path, fileid=node).changectx()
182 return self.substitute(data, path, ctx, self.re_kw.sub)
182 return self.substitute(data, path, ctx, self.re_kw.sub)
183 return data
183 return data
184
184
185 def iskwfile(self, path, flagfunc):
185 def iskwfile(self, path, flagfunc):
186 '''Returns true if path matches [keyword] pattern
186 '''Returns true if path matches [keyword] pattern
187 and is not a symbolic link.
187 and is not a symbolic link.
188 Caveat: localrepository._link fails on Windows.'''
188 Caveat: localrepository._link fails on Windows.'''
189 return self.match(path) and not 'l' in flagfunc(path)
189 return self.match(path) and not 'l' in flagfunc(path)
190
190
191 def overwrite(self, ctx, candidates, iswctx, expand):
191 def overwrite(self, ctx, candidates, iswctx, expand):
192 '''Overwrites selected files expanding/shrinking keywords.'''
192 '''Overwrites selected files expanding/shrinking keywords.'''
193 if self.record:
193 if self.record:
194 candidates = [f for f in ctx.files() if f in ctx]
194 candidates = [f for f in ctx.files() if f in ctx]
195 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)]
195 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)]
196 if candidates:
196 if candidates:
197 self.restrict = True # do not expand when reading
197 self.restrict = True # do not expand when reading
198 mf = ctx.manifest()
198 mf = ctx.manifest()
199 msg = (expand and _('overwriting %s expanding keywords\n')
199 msg = (expand and _('overwriting %s expanding keywords\n')
200 or _('overwriting %s shrinking keywords\n'))
200 or _('overwriting %s shrinking keywords\n'))
201 for f in candidates:
201 for f in candidates:
202 if not self.record:
202 if not self.record:
203 data = self.repo.file(f).read(mf[f])
203 data = self.repo.file(f).read(mf[f])
204 else:
204 else:
205 data = self.repo.wread(f)
205 data = self.repo.wread(f)
206 if util.binary(data):
206 if util.binary(data):
207 continue
207 continue
208 if expand:
208 if expand:
209 if iswctx:
209 if iswctx:
210 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
210 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
211 data, found = self.substitute(data, f, ctx,
211 data, found = self.substitute(data, f, ctx,
212 self.re_kw.subn)
212 self.re_kw.subn)
213 else:
213 else:
214 found = self.re_kw.search(data)
214 found = self.re_kw.search(data)
215 if found:
215 if found:
216 self.ui.note(msg % f)
216 self.ui.note(msg % f)
217 self.repo.wwrite(f, data, mf.flags(f))
217 self.repo.wwrite(f, data, mf.flags(f))
218 if iswctx:
218 if iswctx:
219 self.repo.dirstate.normal(f)
219 self.repo.dirstate.normal(f)
220 elif self.record:
220 elif self.record:
221 self.repo.dirstate.normallookup(f)
221 self.repo.dirstate.normallookup(f)
222 self.restrict = False
222 self.restrict = False
223
223
224 def shrinktext(self, text):
224 def shrinktext(self, text):
225 '''Unconditionally removes all keyword substitutions from text.'''
225 '''Unconditionally removes all keyword substitutions from text.'''
226 return self.re_kw.sub(r'$\1$', text)
226 return self.re_kw.sub(r'$\1$', text)
227
227
228 def shrink(self, fname, text):
228 def shrink(self, fname, text):
229 '''Returns text with all keyword substitutions removed.'''
229 '''Returns text with all keyword substitutions removed.'''
230 if self.match(fname) and not util.binary(text):
230 if self.match(fname) and not util.binary(text):
231 return self.shrinktext(text)
231 return self.shrinktext(text)
232 return text
232 return text
233
233
234 def shrinklines(self, fname, lines):
234 def shrinklines(self, fname, lines):
235 '''Returns lines with keyword substitutions removed.'''
235 '''Returns lines with keyword substitutions removed.'''
236 if self.match(fname):
236 if self.match(fname):
237 text = ''.join(lines)
237 text = ''.join(lines)
238 if not util.binary(text):
238 if not util.binary(text):
239 return self.shrinktext(text).splitlines(True)
239 return self.shrinktext(text).splitlines(True)
240 return lines
240 return lines
241
241
242 def wread(self, fname, data):
242 def wread(self, fname, data):
243 '''If in restricted mode returns data read from wdir with
243 '''If in restricted mode returns data read from wdir with
244 keyword substitutions removed.'''
244 keyword substitutions removed.'''
245 return self.restrict and self.shrink(fname, data) or data
245 return self.restrict and self.shrink(fname, data) or data
246
246
247 class kwfilelog(filelog.filelog):
247 class kwfilelog(filelog.filelog):
248 '''
248 '''
249 Subclass of filelog to hook into its read, add, cmp methods.
249 Subclass of filelog to hook into its read, add, cmp methods.
250 Keywords are "stored" unexpanded, and processed on reading.
250 Keywords are "stored" unexpanded, and processed on reading.
251 '''
251 '''
252 def __init__(self, opener, kwt, path):
252 def __init__(self, opener, kwt, path):
253 super(kwfilelog, self).__init__(opener, path)
253 super(kwfilelog, self).__init__(opener, path)
254 self.kwt = kwt
254 self.kwt = kwt
255 self.path = path
255 self.path = path
256
256
257 def read(self, node):
257 def read(self, node):
258 '''Expands keywords when reading filelog.'''
258 '''Expands keywords when reading filelog.'''
259 data = super(kwfilelog, self).read(node)
259 data = super(kwfilelog, self).read(node)
260 return self.kwt.expand(self.path, node, data)
260 return self.kwt.expand(self.path, node, data)
261
261
262 def add(self, text, meta, tr, link, p1=None, p2=None):
262 def add(self, text, meta, tr, link, p1=None, p2=None):
263 '''Removes keyword substitutions when adding to filelog.'''
263 '''Removes keyword substitutions when adding to filelog.'''
264 text = self.kwt.shrink(self.path, text)
264 text = self.kwt.shrink(self.path, text)
265 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
265 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
266
266
267 def cmp(self, node, text):
267 def cmp(self, node, text):
268 '''Removes keyword substitutions for comparison.'''
268 '''Removes keyword substitutions for comparison.'''
269 text = self.kwt.shrink(self.path, text)
269 text = self.kwt.shrink(self.path, text)
270 if self.renamed(node):
270 if self.renamed(node):
271 t2 = super(kwfilelog, self).read(node)
271 t2 = super(kwfilelog, self).read(node)
272 return t2 != text
272 return t2 != text
273 return revlog.revlog.cmp(self, node, text)
273 return revlog.revlog.cmp(self, node, text)
274
274
275 def _status(ui, repo, kwt, *pats, **opts):
275 def _status(ui, repo, kwt, *pats, **opts):
276 '''Bails out if [keyword] configuration is not active.
276 '''Bails out if [keyword] configuration is not active.
277 Returns status of working directory.'''
277 Returns status of working directory.'''
278 if kwt:
278 if kwt:
279 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
279 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
280 unknown=opts.get('unknown') or opts.get('all'))
280 unknown=opts.get('unknown') or opts.get('all'))
281 if ui.configitems('keyword'):
281 if ui.configitems('keyword'):
282 raise util.Abort(_('[keyword] patterns cannot match'))
282 raise util.Abort(_('[keyword] patterns cannot match'))
283 raise util.Abort(_('no [keyword] patterns configured'))
283 raise util.Abort(_('no [keyword] patterns configured'))
284
284
285 def _kwfwrite(ui, repo, expand, *pats, **opts):
285 def _kwfwrite(ui, repo, expand, *pats, **opts):
286 '''Selects files and passes them to kwtemplater.overwrite.'''
286 '''Selects files and passes them to kwtemplater.overwrite.'''
287 wctx = repo[None]
287 wctx = repo[None]
288 if len(wctx.parents()) > 1:
288 if len(wctx.parents()) > 1:
289 raise util.Abort(_('outstanding uncommitted merge'))
289 raise util.Abort(_('outstanding uncommitted merge'))
290 kwt = kwtools['templater']
290 kwt = kwtools['templater']
291 wlock = repo.wlock()
291 wlock = repo.wlock()
292 try:
292 try:
293 status = _status(ui, repo, kwt, *pats, **opts)
293 status = _status(ui, repo, kwt, *pats, **opts)
294 modified, added, removed, deleted, unknown, ignored, clean = status
294 modified, added, removed, deleted, unknown, ignored, clean = status
295 if modified or added or removed or deleted:
295 if modified or added or removed or deleted:
296 raise util.Abort(_('outstanding uncommitted changes'))
296 raise util.Abort(_('outstanding uncommitted changes'))
297 kwt.overwrite(wctx, clean, True, expand)
297 kwt.overwrite(wctx, clean, True, expand)
298 finally:
298 finally:
299 wlock.release()
299 wlock.release()
300
300
301 def demo(ui, repo, *args, **opts):
301 def demo(ui, repo, *args, **opts):
302 '''print [keywordmaps] configuration and an expansion example
302 '''print [keywordmaps] configuration and an expansion example
303
303
304 Show current, custom, or default keyword template maps and their
304 Show current, custom, or default keyword template maps and their
305 expansions.
305 expansions.
306
306
307 Extend the current configuration by specifying maps as arguments
307 Extend the current configuration by specifying maps as arguments
308 and using -f/--rcfile to source an external hgrc file.
308 and using -f/--rcfile to source an external hgrc file.
309
309
310 Use -d/--default to disable current configuration.
310 Use -d/--default to disable current configuration.
311
311
312 See :hg:`help templates` for information on templates and filters.
312 See :hg:`help templates` for information on templates and filters.
313 '''
313 '''
314 def demoitems(section, items):
314 def demoitems(section, items):
315 ui.write('[%s]\n' % section)
315 ui.write('[%s]\n' % section)
316 for k, v in sorted(items):
316 for k, v in sorted(items):
317 ui.write('%s = %s\n' % (k, v))
317 ui.write('%s = %s\n' % (k, v))
318
318
319 fn = 'demo.txt'
319 fn = 'demo.txt'
320 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
320 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
321 ui.note(_('creating temporary repository at %s\n') % tmpdir)
321 ui.note(_('creating temporary repository at %s\n') % tmpdir)
322 repo = localrepo.localrepository(ui, tmpdir, True)
322 repo = localrepo.localrepository(ui, tmpdir, True)
323 ui.setconfig('keyword', fn, '')
323 ui.setconfig('keyword', fn, '')
324
324
325 uikwmaps = ui.configitems('keywordmaps')
325 uikwmaps = ui.configitems('keywordmaps')
326 if args or opts.get('rcfile'):
326 if args or opts.get('rcfile'):
327 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
327 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
328 if uikwmaps:
328 if uikwmaps:
329 ui.status(_('\textending current template maps\n'))
329 ui.status(_('\textending current template maps\n'))
330 if opts.get('default') or not uikwmaps:
330 if opts.get('default') or not uikwmaps:
331 ui.status(_('\toverriding default template maps\n'))
331 ui.status(_('\toverriding default template maps\n'))
332 if opts.get('rcfile'):
332 if opts.get('rcfile'):
333 ui.readconfig(opts.get('rcfile'))
333 ui.readconfig(opts.get('rcfile'))
334 if args:
334 if args:
335 # simulate hgrc parsing
335 # simulate hgrc parsing
336 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
336 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
337 fp = repo.opener('hgrc', 'w')
337 fp = repo.opener('hgrc', 'w')
338 fp.writelines(rcmaps)
338 fp.writelines(rcmaps)
339 fp.close()
339 fp.close()
340 ui.readconfig(repo.join('hgrc'))
340 ui.readconfig(repo.join('hgrc'))
341 kwmaps = dict(ui.configitems('keywordmaps'))
341 kwmaps = dict(ui.configitems('keywordmaps'))
342 elif opts.get('default'):
342 elif opts.get('default'):
343 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
343 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
344 kwmaps = _defaultkwmaps(ui)
344 kwmaps = _defaultkwmaps(ui)
345 if uikwmaps:
345 if uikwmaps:
346 ui.status(_('\tdisabling current template maps\n'))
346 ui.status(_('\tdisabling current template maps\n'))
347 for k, v in kwmaps.iteritems():
347 for k, v in kwmaps.iteritems():
348 ui.setconfig('keywordmaps', k, v)
348 ui.setconfig('keywordmaps', k, v)
349 else:
349 else:
350 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
350 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
351 kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
351 kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
352
352
353 uisetup(ui)
353 uisetup(ui)
354 reposetup(ui, repo)
354 reposetup(ui, repo)
355 ui.write('[extensions]\nkeyword =\n')
355 ui.write('[extensions]\nkeyword =\n')
356 demoitems('keyword', ui.configitems('keyword'))
356 demoitems('keyword', ui.configitems('keyword'))
357 demoitems('keywordmaps', kwmaps.iteritems())
357 demoitems('keywordmaps', kwmaps.iteritems())
358 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
358 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
359 repo.wopener(fn, 'w').write(keywords)
359 repo.wopener(fn, 'w').write(keywords)
360 repo[None].add([fn])
360 repo[None].add([fn])
361 ui.note(_('\nkeywords written to %s:\n') % fn)
361 ui.note(_('\nkeywords written to %s:\n') % fn)
362 ui.note(keywords)
362 ui.note(keywords)
363 repo.dirstate.setbranch('demobranch')
363 repo.dirstate.setbranch('demobranch')
364 for name, cmd in ui.configitems('hooks'):
364 for name, cmd in ui.configitems('hooks'):
365 if name.split('.', 1)[0].find('commit') > -1:
365 if name.split('.', 1)[0].find('commit') > -1:
366 repo.ui.setconfig('hooks', name, '')
366 repo.ui.setconfig('hooks', name, '')
367 msg = _('hg keyword configuration and expansion example')
367 msg = _('hg keyword configuration and expansion example')
368 ui.note("hg ci -m '%s'\n" % msg)
368 ui.note("hg ci -m '%s'\n" % msg)
369 repo.commit(text=msg)
369 repo.commit(text=msg)
370 ui.status(_('\n\tkeywords expanded\n'))
370 ui.status(_('\n\tkeywords expanded\n'))
371 ui.write(repo.wread(fn))
371 ui.write(repo.wread(fn))
372 shutil.rmtree(tmpdir, ignore_errors=True)
372 shutil.rmtree(tmpdir, ignore_errors=True)
373
373
374 def expand(ui, repo, *pats, **opts):
374 def expand(ui, repo, *pats, **opts):
375 '''expand keywords in the working directory
375 '''expand keywords in the working directory
376
376
377 Run after (re)enabling keyword expansion.
377 Run after (re)enabling keyword expansion.
378
378
379 kwexpand refuses to run if given files contain local changes.
379 kwexpand refuses to run if given files contain local changes.
380 '''
380 '''
381 # 3rd argument sets expansion to True
381 # 3rd argument sets expansion to True
382 _kwfwrite(ui, repo, True, *pats, **opts)
382 _kwfwrite(ui, repo, True, *pats, **opts)
383
383
384 def files(ui, repo, *pats, **opts):
384 def files(ui, repo, *pats, **opts):
385 '''show files configured for keyword expansion
385 '''show files configured for keyword expansion
386
386
387 List which files in the working directory are matched by the
387 List which files in the working directory are matched by the
388 [keyword] configuration patterns.
388 [keyword] configuration patterns.
389
389
390 Useful to prevent inadvertent keyword expansion and to speed up
390 Useful to prevent inadvertent keyword expansion and to speed up
391 execution by including only files that are actual candidates for
391 execution by including only files that are actual candidates for
392 expansion.
392 expansion.
393
393
394 See :hg:`help keyword` on how to construct patterns both for
394 See :hg:`help keyword` on how to construct patterns both for
395 inclusion and exclusion of files.
395 inclusion and exclusion of files.
396
396
397 With -A/--all and -v/--verbose the codes used to show the status
397 With -A/--all and -v/--verbose the codes used to show the status
398 of files are::
398 of files are::
399
399
400 K = keyword expansion candidate
400 K = keyword expansion candidate
401 k = keyword expansion candidate (not tracked)
401 k = keyword expansion candidate (not tracked)
402 I = ignored
402 I = ignored
403 i = ignored (not tracked)
403 i = ignored (not tracked)
404 '''
404 '''
405 kwt = kwtools['templater']
405 kwt = kwtools['templater']
406 status = _status(ui, repo, kwt, *pats, **opts)
406 status = _status(ui, repo, kwt, *pats, **opts)
407 cwd = pats and repo.getcwd() or ''
407 cwd = pats and repo.getcwd() or ''
408 modified, added, removed, deleted, unknown, ignored, clean = status
408 modified, added, removed, deleted, unknown, ignored, clean = status
409 files = []
409 files = []
410 if not opts.get('unknown') or opts.get('all'):
410 if not opts.get('unknown') or opts.get('all'):
411 files = sorted(modified + added + clean)
411 files = sorted(modified + added + clean)
412 wctx = repo[None]
412 wctx = repo[None]
413 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
413 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
414 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
414 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
415 if not opts.get('ignore') or opts.get('all'):
415 if not opts.get('ignore') or opts.get('all'):
416 showfiles = kwfiles, kwunknown
416 showfiles = kwfiles, kwunknown
417 else:
417 else:
418 showfiles = [], []
418 showfiles = [], []
419 if opts.get('all') or opts.get('ignore'):
419 if opts.get('all') or opts.get('ignore'):
420 showfiles += ([f for f in files if f not in kwfiles],
420 showfiles += ([f for f in files if f not in kwfiles],
421 [f for f in unknown if f not in kwunknown])
421 [f for f in unknown if f not in kwunknown])
422 for char, filenames in zip('KkIi', showfiles):
422 for char, filenames in zip('KkIi', showfiles):
423 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
423 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
424 for f in filenames:
424 for f in filenames:
425 ui.write(fmt % repo.pathto(f, cwd))
425 ui.write(fmt % repo.pathto(f, cwd))
426
426
427 def shrink(ui, repo, *pats, **opts):
427 def shrink(ui, repo, *pats, **opts):
428 '''revert expanded keywords in the working directory
428 '''revert expanded keywords in the working directory
429
429
430 Run before changing/disabling active keywords or if you experience
430 Run before changing/disabling active keywords or if you experience
431 problems with :hg:`import` or :hg:`merge`.
431 problems with :hg:`import` or :hg:`merge`.
432
432
433 kwshrink refuses to run if given files contain local changes.
433 kwshrink refuses to run if given files contain local changes.
434 '''
434 '''
435 # 3rd argument sets expansion to False
435 # 3rd argument sets expansion to False
436 _kwfwrite(ui, repo, False, *pats, **opts)
436 _kwfwrite(ui, repo, False, *pats, **opts)
437
437
438
438
439 def uisetup(ui):
439 def uisetup(ui):
440 ''' Monkeypatches dispatch._parse to retrieve user command.'''
440 ''' Monkeypatches dispatch._parse to retrieve user command.'''
441
441
442 def kwdispatch_parse(orig, ui, args):
442 def kwdispatch_parse(orig, ui, args):
443 '''Monkeypatch dispatch._parse to obtain running hg command.'''
443 '''Monkeypatch dispatch._parse to obtain running hg command.'''
444 cmd, func, args, options, cmdoptions = orig(ui, args)
444 cmd, func, args, options, cmdoptions = orig(ui, args)
445 kwtools['hgcmd'] = cmd
445 kwtools['hgcmd'] = cmd
446 return cmd, func, args, options, cmdoptions
446 return cmd, func, args, options, cmdoptions
447
447
448 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
448 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
449
449
450 def reposetup(ui, repo):
450 def reposetup(ui, repo):
451 '''Sets up repo as kwrepo for keyword substitution.
451 '''Sets up repo as kwrepo for keyword substitution.
452 Overrides file method to return kwfilelog instead of filelog
452 Overrides file method to return kwfilelog instead of filelog
453 if file matches user configuration.
453 if file matches user configuration.
454 Wraps commit to overwrite configured files with updated
454 Wraps commit to overwrite configured files with updated
455 keyword substitutions.
455 keyword substitutions.
456 Monkeypatches patch and webcommands.'''
456 Monkeypatches patch and webcommands.'''
457
457
458 try:
458 try:
459 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
459 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
460 or '.hg' in util.splitpath(repo.root)
460 or '.hg' in util.splitpath(repo.root)
461 or repo._url.startswith('bundle:')):
461 or repo._url.startswith('bundle:')):
462 return
462 return
463 except AttributeError:
463 except AttributeError:
464 pass
464 pass
465
465
466 inc, exc = [], ['.hg*']
466 inc, exc = [], ['.hg*']
467 for pat, opt in ui.configitems('keyword'):
467 for pat, opt in ui.configitems('keyword'):
468 if opt != 'ignore':
468 if opt != 'ignore':
469 inc.append(pat)
469 inc.append(pat)
470 else:
470 else:
471 exc.append(pat)
471 exc.append(pat)
472 if not inc:
472 if not inc:
473 return
473 return
474
474
475 kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
475 kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
476
476
477 class kwrepo(repo.__class__):
477 class kwrepo(repo.__class__):
478 def file(self, f):
478 def file(self, f):
479 if f[0] == '/':
479 if f[0] == '/':
480 f = f[1:]
480 f = f[1:]
481 return kwfilelog(self.sopener, kwt, f)
481 return kwfilelog(self.sopener, kwt, f)
482
482
483 def wread(self, filename):
483 def wread(self, filename):
484 data = super(kwrepo, self).wread(filename)
484 data = super(kwrepo, self).wread(filename)
485 return kwt.wread(filename, data)
485 return kwt.wread(filename, data)
486
486
487 def commit(self, *args, **opts):
487 def commit(self, *args, **opts):
488 # use custom commitctx for user commands
488 # use custom commitctx for user commands
489 # other extensions can still wrap repo.commitctx directly
489 # other extensions can still wrap repo.commitctx directly
490 self.commitctx = self.kwcommitctx
490 self.commitctx = self.kwcommitctx
491 try:
491 try:
492 return super(kwrepo, self).commit(*args, **opts)
492 return super(kwrepo, self).commit(*args, **opts)
493 finally:
493 finally:
494 del self.commitctx
494 del self.commitctx
495
495
496 def kwcommitctx(self, ctx, error=False):
496 def kwcommitctx(self, ctx, error=False):
497 n = super(kwrepo, self).commitctx(ctx, error)
497 n = super(kwrepo, self).commitctx(ctx, error)
498 # no lock needed, only called from repo.commit() which already locks
498 # no lock needed, only called from repo.commit() which already locks
499 if not kwt.record:
499 if not kwt.record:
500 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
500 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
501 False, True)
501 False, True)
502 return n
502 return n
503
503
504 # monkeypatches
504 # monkeypatches
505 def kwpatchfile_init(orig, self, ui, fname, opener,
505 def kwpatchfile_init(orig, self, ui, fname, opener,
506 missing=False, eolmode=None):
506 missing=False, eolmode=None):
507 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
507 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
508 rejects or conflicts due to expanded keywords in working dir.'''
508 rejects or conflicts due to expanded keywords in working dir.'''
509 orig(self, ui, fname, opener, missing, eolmode)
509 orig(self, ui, fname, opener, missing, eolmode)
510 # shrink keywords read from working dir
510 # shrink keywords read from working dir
511 self.lines = kwt.shrinklines(self.fname, self.lines)
511 self.lines = kwt.shrinklines(self.fname, self.lines)
512
512
513 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
513 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
514 opts=None):
514 opts=None, prefix=''):
515 '''Monkeypatch patch.diff to avoid expansion except when
515 '''Monkeypatch patch.diff to avoid expansion except when
516 comparing against working dir.'''
516 comparing against working dir.'''
517 if node2 is not None:
517 if node2 is not None:
518 kwt.match = util.never
518 kwt.match = util.never
519 elif node1 is not None and node1 != repo['.'].node():
519 elif node1 is not None and node1 != repo['.'].node():
520 kwt.restrict = True
520 kwt.restrict = True
521 return orig(repo, node1, node2, match, changes, opts)
521 return orig(repo, node1, node2, match, changes, opts, prefix)
522
522
523 def kwweb_skip(orig, web, req, tmpl):
523 def kwweb_skip(orig, web, req, tmpl):
524 '''Wraps webcommands.x turning off keyword expansion.'''
524 '''Wraps webcommands.x turning off keyword expansion.'''
525 kwt.match = util.never
525 kwt.match = util.never
526 return orig(web, req, tmpl)
526 return orig(web, req, tmpl)
527
527
528 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
528 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
529 '''Wraps record.dorecord expanding keywords after recording.'''
529 '''Wraps record.dorecord expanding keywords after recording.'''
530 wlock = repo.wlock()
530 wlock = repo.wlock()
531 try:
531 try:
532 # record returns 0 even when nothing has changed
532 # record returns 0 even when nothing has changed
533 # therefore compare nodes before and after
533 # therefore compare nodes before and after
534 ctx = repo['.']
534 ctx = repo['.']
535 ret = orig(ui, repo, commitfunc, *pats, **opts)
535 ret = orig(ui, repo, commitfunc, *pats, **opts)
536 recordctx = repo['.']
536 recordctx = repo['.']
537 if ctx != recordctx:
537 if ctx != recordctx:
538 kwt.overwrite(recordctx, None, False, True)
538 kwt.overwrite(recordctx, None, False, True)
539 return ret
539 return ret
540 finally:
540 finally:
541 wlock.release()
541 wlock.release()
542
542
543 repo.__class__ = kwrepo
543 repo.__class__ = kwrepo
544
544
545 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
545 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
546 if not kwt.restrict:
546 if not kwt.restrict:
547 extensions.wrapfunction(patch, 'diff', kw_diff)
547 extensions.wrapfunction(patch, 'diff', kw_diff)
548 for c in 'annotate changeset rev filediff diff'.split():
548 for c in 'annotate changeset rev filediff diff'.split():
549 extensions.wrapfunction(webcommands, c, kwweb_skip)
549 extensions.wrapfunction(webcommands, c, kwweb_skip)
550 for name in recordextensions.split():
550 for name in recordextensions.split():
551 try:
551 try:
552 record = extensions.find(name)
552 record = extensions.find(name)
553 extensions.wrapfunction(record, 'dorecord', kw_dorecord)
553 extensions.wrapfunction(record, 'dorecord', kw_dorecord)
554 except KeyError:
554 except KeyError:
555 pass
555 pass
556
556
557 cmdtable = {
557 cmdtable = {
558 'kwdemo':
558 'kwdemo':
559 (demo,
559 (demo,
560 [('d', 'default', None, _('show default keyword template maps')),
560 [('d', 'default', None, _('show default keyword template maps')),
561 ('f', 'rcfile', '',
561 ('f', 'rcfile', '',
562 _('read maps from rcfile'), _('FILE'))],
562 _('read maps from rcfile'), _('FILE'))],
563 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
563 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
564 'kwexpand': (expand, commands.walkopts,
564 'kwexpand': (expand, commands.walkopts,
565 _('hg kwexpand [OPTION]... [FILE]...')),
565 _('hg kwexpand [OPTION]... [FILE]...')),
566 'kwfiles':
566 'kwfiles':
567 (files,
567 (files,
568 [('A', 'all', None, _('show keyword status flags of all files')),
568 [('A', 'all', None, _('show keyword status flags of all files')),
569 ('i', 'ignore', None, _('show files excluded from expansion')),
569 ('i', 'ignore', None, _('show files excluded from expansion')),
570 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
570 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
571 ] + commands.walkopts,
571 ] + commands.walkopts,
572 _('hg kwfiles [OPTION]... [FILE]...')),
572 _('hg kwfiles [OPTION]... [FILE]...')),
573 'kwshrink': (shrink, commands.walkopts,
573 'kwshrink': (shrink, commands.walkopts,
574 _('hg kwshrink [OPTION]... [FILE]...')),
574 _('hg kwshrink [OPTION]... [FILE]...')),
575 }
575 }
@@ -1,1281 +1,1293 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, bin, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, glob, tempfile
10 import os, sys, errno, re, glob, tempfile
11 import util, templater, patch, error, encoding, templatekw
11 import util, templater, patch, error, encoding, templatekw
12 import match as matchmod
12 import match as matchmod
13 import similar, revset
13 import similar, revset
14
14
15 revrangesep = ':'
15 revrangesep = ':'
16
16
17 def parsealiases(cmd):
17 def parsealiases(cmd):
18 return cmd.lstrip("^").split("|")
18 return cmd.lstrip("^").split("|")
19
19
20 def findpossible(cmd, table, strict=False):
20 def findpossible(cmd, table, strict=False):
21 """
21 """
22 Return cmd -> (aliases, command table entry)
22 Return cmd -> (aliases, command table entry)
23 for each matching command.
23 for each matching command.
24 Return debug commands (or their aliases) only if no normal command matches.
24 Return debug commands (or their aliases) only if no normal command matches.
25 """
25 """
26 choice = {}
26 choice = {}
27 debugchoice = {}
27 debugchoice = {}
28 for e in table.keys():
28 for e in table.keys():
29 aliases = parsealiases(e)
29 aliases = parsealiases(e)
30 found = None
30 found = None
31 if cmd in aliases:
31 if cmd in aliases:
32 found = cmd
32 found = cmd
33 elif not strict:
33 elif not strict:
34 for a in aliases:
34 for a in aliases:
35 if a.startswith(cmd):
35 if a.startswith(cmd):
36 found = a
36 found = a
37 break
37 break
38 if found is not None:
38 if found is not None:
39 if aliases[0].startswith("debug") or found.startswith("debug"):
39 if aliases[0].startswith("debug") or found.startswith("debug"):
40 debugchoice[found] = (aliases, table[e])
40 debugchoice[found] = (aliases, table[e])
41 else:
41 else:
42 choice[found] = (aliases, table[e])
42 choice[found] = (aliases, table[e])
43
43
44 if not choice and debugchoice:
44 if not choice and debugchoice:
45 choice = debugchoice
45 choice = debugchoice
46
46
47 return choice
47 return choice
48
48
49 def findcmd(cmd, table, strict=True):
49 def findcmd(cmd, table, strict=True):
50 """Return (aliases, command table entry) for command string."""
50 """Return (aliases, command table entry) for command string."""
51 choice = findpossible(cmd, table, strict)
51 choice = findpossible(cmd, table, strict)
52
52
53 if cmd in choice:
53 if cmd in choice:
54 return choice[cmd]
54 return choice[cmd]
55
55
56 if len(choice) > 1:
56 if len(choice) > 1:
57 clist = choice.keys()
57 clist = choice.keys()
58 clist.sort()
58 clist.sort()
59 raise error.AmbiguousCommand(cmd, clist)
59 raise error.AmbiguousCommand(cmd, clist)
60
60
61 if choice:
61 if choice:
62 return choice.values()[0]
62 return choice.values()[0]
63
63
64 raise error.UnknownCommand(cmd)
64 raise error.UnknownCommand(cmd)
65
65
66 def findrepo(p):
66 def findrepo(p):
67 while not os.path.isdir(os.path.join(p, ".hg")):
67 while not os.path.isdir(os.path.join(p, ".hg")):
68 oldp, p = p, os.path.dirname(p)
68 oldp, p = p, os.path.dirname(p)
69 if p == oldp:
69 if p == oldp:
70 return None
70 return None
71
71
72 return p
72 return p
73
73
74 def bail_if_changed(repo):
74 def bail_if_changed(repo):
75 if repo.dirstate.parents()[1] != nullid:
75 if repo.dirstate.parents()[1] != nullid:
76 raise util.Abort(_('outstanding uncommitted merge'))
76 raise util.Abort(_('outstanding uncommitted merge'))
77 modified, added, removed, deleted = repo.status()[:4]
77 modified, added, removed, deleted = repo.status()[:4]
78 if modified or added or removed or deleted:
78 if modified or added or removed or deleted:
79 raise util.Abort(_("outstanding uncommitted changes"))
79 raise util.Abort(_("outstanding uncommitted changes"))
80
80
81 def logmessage(opts):
81 def logmessage(opts):
82 """ get the log message according to -m and -l option """
82 """ get the log message according to -m and -l option """
83 message = opts.get('message')
83 message = opts.get('message')
84 logfile = opts.get('logfile')
84 logfile = opts.get('logfile')
85
85
86 if message and logfile:
86 if message and logfile:
87 raise util.Abort(_('options --message and --logfile are mutually '
87 raise util.Abort(_('options --message and --logfile are mutually '
88 'exclusive'))
88 'exclusive'))
89 if not message and logfile:
89 if not message and logfile:
90 try:
90 try:
91 if logfile == '-':
91 if logfile == '-':
92 message = sys.stdin.read()
92 message = sys.stdin.read()
93 else:
93 else:
94 message = open(logfile).read()
94 message = open(logfile).read()
95 except IOError, inst:
95 except IOError, inst:
96 raise util.Abort(_("can't read commit message '%s': %s") %
96 raise util.Abort(_("can't read commit message '%s': %s") %
97 (logfile, inst.strerror))
97 (logfile, inst.strerror))
98 return message
98 return message
99
99
100 def loglimit(opts):
100 def loglimit(opts):
101 """get the log limit according to option -l/--limit"""
101 """get the log limit according to option -l/--limit"""
102 limit = opts.get('limit')
102 limit = opts.get('limit')
103 if limit:
103 if limit:
104 try:
104 try:
105 limit = int(limit)
105 limit = int(limit)
106 except ValueError:
106 except ValueError:
107 raise util.Abort(_('limit must be a positive integer'))
107 raise util.Abort(_('limit must be a positive integer'))
108 if limit <= 0:
108 if limit <= 0:
109 raise util.Abort(_('limit must be positive'))
109 raise util.Abort(_('limit must be positive'))
110 else:
110 else:
111 limit = None
111 limit = None
112 return limit
112 return limit
113
113
114 def revpair(repo, revs):
114 def revpair(repo, revs):
115 '''return pair of nodes, given list of revisions. second item can
115 '''return pair of nodes, given list of revisions. second item can
116 be None, meaning use working dir.'''
116 be None, meaning use working dir.'''
117
117
118 def revfix(repo, val, defval):
118 def revfix(repo, val, defval):
119 if not val and val != 0 and defval is not None:
119 if not val and val != 0 and defval is not None:
120 val = defval
120 val = defval
121 return repo.lookup(val)
121 return repo.lookup(val)
122
122
123 if not revs:
123 if not revs:
124 return repo.dirstate.parents()[0], None
124 return repo.dirstate.parents()[0], None
125 end = None
125 end = None
126 if len(revs) == 1:
126 if len(revs) == 1:
127 if revrangesep in revs[0]:
127 if revrangesep in revs[0]:
128 start, end = revs[0].split(revrangesep, 1)
128 start, end = revs[0].split(revrangesep, 1)
129 start = revfix(repo, start, 0)
129 start = revfix(repo, start, 0)
130 end = revfix(repo, end, len(repo) - 1)
130 end = revfix(repo, end, len(repo) - 1)
131 else:
131 else:
132 start = revfix(repo, revs[0], None)
132 start = revfix(repo, revs[0], None)
133 elif len(revs) == 2:
133 elif len(revs) == 2:
134 if revrangesep in revs[0] or revrangesep in revs[1]:
134 if revrangesep in revs[0] or revrangesep in revs[1]:
135 raise util.Abort(_('too many revisions specified'))
135 raise util.Abort(_('too many revisions specified'))
136 start = revfix(repo, revs[0], None)
136 start = revfix(repo, revs[0], None)
137 end = revfix(repo, revs[1], None)
137 end = revfix(repo, revs[1], None)
138 else:
138 else:
139 raise util.Abort(_('too many revisions specified'))
139 raise util.Abort(_('too many revisions specified'))
140 return start, end
140 return start, end
141
141
142 def revrange(repo, revs):
142 def revrange(repo, revs):
143 """Yield revision as strings from a list of revision specifications."""
143 """Yield revision as strings from a list of revision specifications."""
144
144
145 def revfix(repo, val, defval):
145 def revfix(repo, val, defval):
146 if not val and val != 0 and defval is not None:
146 if not val and val != 0 and defval is not None:
147 return defval
147 return defval
148 return repo.changelog.rev(repo.lookup(val))
148 return repo.changelog.rev(repo.lookup(val))
149
149
150 seen, l = set(), []
150 seen, l = set(), []
151 for spec in revs:
151 for spec in revs:
152 # attempt to parse old-style ranges first to deal with
152 # attempt to parse old-style ranges first to deal with
153 # things like old-tag which contain query metacharacters
153 # things like old-tag which contain query metacharacters
154 try:
154 try:
155 if revrangesep in spec:
155 if revrangesep in spec:
156 start, end = spec.split(revrangesep, 1)
156 start, end = spec.split(revrangesep, 1)
157 start = revfix(repo, start, 0)
157 start = revfix(repo, start, 0)
158 end = revfix(repo, end, len(repo) - 1)
158 end = revfix(repo, end, len(repo) - 1)
159 step = start > end and -1 or 1
159 step = start > end and -1 or 1
160 for rev in xrange(start, end + step, step):
160 for rev in xrange(start, end + step, step):
161 if rev in seen:
161 if rev in seen:
162 continue
162 continue
163 seen.add(rev)
163 seen.add(rev)
164 l.append(rev)
164 l.append(rev)
165 continue
165 continue
166 elif spec and spec in repo: # single unquoted rev
166 elif spec and spec in repo: # single unquoted rev
167 rev = revfix(repo, spec, None)
167 rev = revfix(repo, spec, None)
168 if rev in seen:
168 if rev in seen:
169 continue
169 continue
170 seen.add(rev)
170 seen.add(rev)
171 l.append(rev)
171 l.append(rev)
172 continue
172 continue
173 except error.RepoLookupError:
173 except error.RepoLookupError:
174 pass
174 pass
175
175
176 # fall through to new-style queries if old-style fails
176 # fall through to new-style queries if old-style fails
177 m = revset.match(spec)
177 m = revset.match(spec)
178 for r in m(repo, range(len(repo))):
178 for r in m(repo, range(len(repo))):
179 if r not in seen:
179 if r not in seen:
180 l.append(r)
180 l.append(r)
181 seen.update(l)
181 seen.update(l)
182
182
183 return l
183 return l
184
184
185 def make_filename(repo, pat, node,
185 def make_filename(repo, pat, node,
186 total=None, seqno=None, revwidth=None, pathname=None):
186 total=None, seqno=None, revwidth=None, pathname=None):
187 node_expander = {
187 node_expander = {
188 'H': lambda: hex(node),
188 'H': lambda: hex(node),
189 'R': lambda: str(repo.changelog.rev(node)),
189 'R': lambda: str(repo.changelog.rev(node)),
190 'h': lambda: short(node),
190 'h': lambda: short(node),
191 }
191 }
192 expander = {
192 expander = {
193 '%': lambda: '%',
193 '%': lambda: '%',
194 'b': lambda: os.path.basename(repo.root),
194 'b': lambda: os.path.basename(repo.root),
195 }
195 }
196
196
197 try:
197 try:
198 if node:
198 if node:
199 expander.update(node_expander)
199 expander.update(node_expander)
200 if node:
200 if node:
201 expander['r'] = (lambda:
201 expander['r'] = (lambda:
202 str(repo.changelog.rev(node)).zfill(revwidth or 0))
202 str(repo.changelog.rev(node)).zfill(revwidth or 0))
203 if total is not None:
203 if total is not None:
204 expander['N'] = lambda: str(total)
204 expander['N'] = lambda: str(total)
205 if seqno is not None:
205 if seqno is not None:
206 expander['n'] = lambda: str(seqno)
206 expander['n'] = lambda: str(seqno)
207 if total is not None and seqno is not None:
207 if total is not None and seqno is not None:
208 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
208 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
209 if pathname is not None:
209 if pathname is not None:
210 expander['s'] = lambda: os.path.basename(pathname)
210 expander['s'] = lambda: os.path.basename(pathname)
211 expander['d'] = lambda: os.path.dirname(pathname) or '.'
211 expander['d'] = lambda: os.path.dirname(pathname) or '.'
212 expander['p'] = lambda: pathname
212 expander['p'] = lambda: pathname
213
213
214 newname = []
214 newname = []
215 patlen = len(pat)
215 patlen = len(pat)
216 i = 0
216 i = 0
217 while i < patlen:
217 while i < patlen:
218 c = pat[i]
218 c = pat[i]
219 if c == '%':
219 if c == '%':
220 i += 1
220 i += 1
221 c = pat[i]
221 c = pat[i]
222 c = expander[c]()
222 c = expander[c]()
223 newname.append(c)
223 newname.append(c)
224 i += 1
224 i += 1
225 return ''.join(newname)
225 return ''.join(newname)
226 except KeyError, inst:
226 except KeyError, inst:
227 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
227 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
228 inst.args[0])
228 inst.args[0])
229
229
230 def make_file(repo, pat, node=None,
230 def make_file(repo, pat, node=None,
231 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
231 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
232
232
233 writable = 'w' in mode or 'a' in mode
233 writable = 'w' in mode or 'a' in mode
234
234
235 if not pat or pat == '-':
235 if not pat or pat == '-':
236 return writable and sys.stdout or sys.stdin
236 return writable and sys.stdout or sys.stdin
237 if hasattr(pat, 'write') and writable:
237 if hasattr(pat, 'write') and writable:
238 return pat
238 return pat
239 if hasattr(pat, 'read') and 'r' in mode:
239 if hasattr(pat, 'read') and 'r' in mode:
240 return pat
240 return pat
241 return open(make_filename(repo, pat, node, total, seqno, revwidth,
241 return open(make_filename(repo, pat, node, total, seqno, revwidth,
242 pathname),
242 pathname),
243 mode)
243 mode)
244
244
245 def expandpats(pats):
245 def expandpats(pats):
246 if not util.expandglobs:
246 if not util.expandglobs:
247 return list(pats)
247 return list(pats)
248 ret = []
248 ret = []
249 for p in pats:
249 for p in pats:
250 kind, name = matchmod._patsplit(p, None)
250 kind, name = matchmod._patsplit(p, None)
251 if kind is None:
251 if kind is None:
252 try:
252 try:
253 globbed = glob.glob(name)
253 globbed = glob.glob(name)
254 except re.error:
254 except re.error:
255 globbed = [name]
255 globbed = [name]
256 if globbed:
256 if globbed:
257 ret.extend(globbed)
257 ret.extend(globbed)
258 continue
258 continue
259 ret.append(p)
259 ret.append(p)
260 return ret
260 return ret
261
261
262 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
262 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
263 if not globbed and default == 'relpath':
263 if not globbed and default == 'relpath':
264 pats = expandpats(pats or [])
264 pats = expandpats(pats or [])
265 m = matchmod.match(repo.root, repo.getcwd(), pats,
265 m = matchmod.match(repo.root, repo.getcwd(), pats,
266 opts.get('include'), opts.get('exclude'), default,
266 opts.get('include'), opts.get('exclude'), default,
267 auditor=repo.auditor)
267 auditor=repo.auditor)
268 def badfn(f, msg):
268 def badfn(f, msg):
269 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
269 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
270 m.bad = badfn
270 m.bad = badfn
271 return m
271 return m
272
272
273 def matchall(repo):
273 def matchall(repo):
274 return matchmod.always(repo.root, repo.getcwd())
274 return matchmod.always(repo.root, repo.getcwd())
275
275
276 def matchfiles(repo, files):
276 def matchfiles(repo, files):
277 return matchmod.exact(repo.root, repo.getcwd(), files)
277 return matchmod.exact(repo.root, repo.getcwd(), files)
278
278
279 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
279 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
280 if dry_run is None:
280 if dry_run is None:
281 dry_run = opts.get('dry_run')
281 dry_run = opts.get('dry_run')
282 if similarity is None:
282 if similarity is None:
283 similarity = float(opts.get('similarity') or 0)
283 similarity = float(opts.get('similarity') or 0)
284 # we'd use status here, except handling of symlinks and ignore is tricky
284 # we'd use status here, except handling of symlinks and ignore is tricky
285 added, unknown, deleted, removed = [], [], [], []
285 added, unknown, deleted, removed = [], [], [], []
286 audit_path = util.path_auditor(repo.root)
286 audit_path = util.path_auditor(repo.root)
287 m = match(repo, pats, opts)
287 m = match(repo, pats, opts)
288 for abs in repo.walk(m):
288 for abs in repo.walk(m):
289 target = repo.wjoin(abs)
289 target = repo.wjoin(abs)
290 good = True
290 good = True
291 try:
291 try:
292 audit_path(abs)
292 audit_path(abs)
293 except:
293 except:
294 good = False
294 good = False
295 rel = m.rel(abs)
295 rel = m.rel(abs)
296 exact = m.exact(abs)
296 exact = m.exact(abs)
297 if good and abs not in repo.dirstate:
297 if good and abs not in repo.dirstate:
298 unknown.append(abs)
298 unknown.append(abs)
299 if repo.ui.verbose or not exact:
299 if repo.ui.verbose or not exact:
300 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
300 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
301 elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
301 elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
302 or (os.path.isdir(target) and not os.path.islink(target))):
302 or (os.path.isdir(target) and not os.path.islink(target))):
303 deleted.append(abs)
303 deleted.append(abs)
304 if repo.ui.verbose or not exact:
304 if repo.ui.verbose or not exact:
305 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
305 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
306 # for finding renames
306 # for finding renames
307 elif repo.dirstate[abs] == 'r':
307 elif repo.dirstate[abs] == 'r':
308 removed.append(abs)
308 removed.append(abs)
309 elif repo.dirstate[abs] == 'a':
309 elif repo.dirstate[abs] == 'a':
310 added.append(abs)
310 added.append(abs)
311 copies = {}
311 copies = {}
312 if similarity > 0:
312 if similarity > 0:
313 for old, new, score in similar.findrenames(repo,
313 for old, new, score in similar.findrenames(repo,
314 added + unknown, removed + deleted, similarity):
314 added + unknown, removed + deleted, similarity):
315 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
315 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
316 repo.ui.status(_('recording removal of %s as rename to %s '
316 repo.ui.status(_('recording removal of %s as rename to %s '
317 '(%d%% similar)\n') %
317 '(%d%% similar)\n') %
318 (m.rel(old), m.rel(new), score * 100))
318 (m.rel(old), m.rel(new), score * 100))
319 copies[new] = old
319 copies[new] = old
320
320
321 if not dry_run:
321 if not dry_run:
322 wctx = repo[None]
322 wctx = repo[None]
323 wlock = repo.wlock()
323 wlock = repo.wlock()
324 try:
324 try:
325 wctx.remove(deleted)
325 wctx.remove(deleted)
326 wctx.add(unknown)
326 wctx.add(unknown)
327 for new, old in copies.iteritems():
327 for new, old in copies.iteritems():
328 wctx.copy(old, new)
328 wctx.copy(old, new)
329 finally:
329 finally:
330 wlock.release()
330 wlock.release()
331
331
332 def copy(ui, repo, pats, opts, rename=False):
332 def copy(ui, repo, pats, opts, rename=False):
333 # called with the repo lock held
333 # called with the repo lock held
334 #
334 #
335 # hgsep => pathname that uses "/" to separate directories
335 # hgsep => pathname that uses "/" to separate directories
336 # ossep => pathname that uses os.sep to separate directories
336 # ossep => pathname that uses os.sep to separate directories
337 cwd = repo.getcwd()
337 cwd = repo.getcwd()
338 targets = {}
338 targets = {}
339 after = opts.get("after")
339 after = opts.get("after")
340 dryrun = opts.get("dry_run")
340 dryrun = opts.get("dry_run")
341 wctx = repo[None]
341 wctx = repo[None]
342
342
343 def walkpat(pat):
343 def walkpat(pat):
344 srcs = []
344 srcs = []
345 badstates = after and '?' or '?r'
345 badstates = after and '?' or '?r'
346 m = match(repo, [pat], opts, globbed=True)
346 m = match(repo, [pat], opts, globbed=True)
347 for abs in repo.walk(m):
347 for abs in repo.walk(m):
348 state = repo.dirstate[abs]
348 state = repo.dirstate[abs]
349 rel = m.rel(abs)
349 rel = m.rel(abs)
350 exact = m.exact(abs)
350 exact = m.exact(abs)
351 if state in badstates:
351 if state in badstates:
352 if exact and state == '?':
352 if exact and state == '?':
353 ui.warn(_('%s: not copying - file is not managed\n') % rel)
353 ui.warn(_('%s: not copying - file is not managed\n') % rel)
354 if exact and state == 'r':
354 if exact and state == 'r':
355 ui.warn(_('%s: not copying - file has been marked for'
355 ui.warn(_('%s: not copying - file has been marked for'
356 ' remove\n') % rel)
356 ' remove\n') % rel)
357 continue
357 continue
358 # abs: hgsep
358 # abs: hgsep
359 # rel: ossep
359 # rel: ossep
360 srcs.append((abs, rel, exact))
360 srcs.append((abs, rel, exact))
361 return srcs
361 return srcs
362
362
363 # abssrc: hgsep
363 # abssrc: hgsep
364 # relsrc: ossep
364 # relsrc: ossep
365 # otarget: ossep
365 # otarget: ossep
366 def copyfile(abssrc, relsrc, otarget, exact):
366 def copyfile(abssrc, relsrc, otarget, exact):
367 abstarget = util.canonpath(repo.root, cwd, otarget)
367 abstarget = util.canonpath(repo.root, cwd, otarget)
368 reltarget = repo.pathto(abstarget, cwd)
368 reltarget = repo.pathto(abstarget, cwd)
369 target = repo.wjoin(abstarget)
369 target = repo.wjoin(abstarget)
370 src = repo.wjoin(abssrc)
370 src = repo.wjoin(abssrc)
371 state = repo.dirstate[abstarget]
371 state = repo.dirstate[abstarget]
372
372
373 # check for collisions
373 # check for collisions
374 prevsrc = targets.get(abstarget)
374 prevsrc = targets.get(abstarget)
375 if prevsrc is not None:
375 if prevsrc is not None:
376 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
376 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
377 (reltarget, repo.pathto(abssrc, cwd),
377 (reltarget, repo.pathto(abssrc, cwd),
378 repo.pathto(prevsrc, cwd)))
378 repo.pathto(prevsrc, cwd)))
379 return
379 return
380
380
381 # check for overwrites
381 # check for overwrites
382 exists = os.path.exists(target)
382 exists = os.path.exists(target)
383 if not after and exists or after and state in 'mn':
383 if not after and exists or after and state in 'mn':
384 if not opts['force']:
384 if not opts['force']:
385 ui.warn(_('%s: not overwriting - file exists\n') %
385 ui.warn(_('%s: not overwriting - file exists\n') %
386 reltarget)
386 reltarget)
387 return
387 return
388
388
389 if after:
389 if after:
390 if not exists:
390 if not exists:
391 if rename:
391 if rename:
392 ui.warn(_('%s: not recording move - %s does not exist\n') %
392 ui.warn(_('%s: not recording move - %s does not exist\n') %
393 (relsrc, reltarget))
393 (relsrc, reltarget))
394 else:
394 else:
395 ui.warn(_('%s: not recording copy - %s does not exist\n') %
395 ui.warn(_('%s: not recording copy - %s does not exist\n') %
396 (relsrc, reltarget))
396 (relsrc, reltarget))
397 return
397 return
398 elif not dryrun:
398 elif not dryrun:
399 try:
399 try:
400 if exists:
400 if exists:
401 os.unlink(target)
401 os.unlink(target)
402 targetdir = os.path.dirname(target) or '.'
402 targetdir = os.path.dirname(target) or '.'
403 if not os.path.isdir(targetdir):
403 if not os.path.isdir(targetdir):
404 os.makedirs(targetdir)
404 os.makedirs(targetdir)
405 util.copyfile(src, target)
405 util.copyfile(src, target)
406 except IOError, inst:
406 except IOError, inst:
407 if inst.errno == errno.ENOENT:
407 if inst.errno == errno.ENOENT:
408 ui.warn(_('%s: deleted in working copy\n') % relsrc)
408 ui.warn(_('%s: deleted in working copy\n') % relsrc)
409 else:
409 else:
410 ui.warn(_('%s: cannot copy - %s\n') %
410 ui.warn(_('%s: cannot copy - %s\n') %
411 (relsrc, inst.strerror))
411 (relsrc, inst.strerror))
412 return True # report a failure
412 return True # report a failure
413
413
414 if ui.verbose or not exact:
414 if ui.verbose or not exact:
415 if rename:
415 if rename:
416 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
416 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
417 else:
417 else:
418 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
418 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
419
419
420 targets[abstarget] = abssrc
420 targets[abstarget] = abssrc
421
421
422 # fix up dirstate
422 # fix up dirstate
423 origsrc = repo.dirstate.copied(abssrc) or abssrc
423 origsrc = repo.dirstate.copied(abssrc) or abssrc
424 if abstarget == origsrc: # copying back a copy?
424 if abstarget == origsrc: # copying back a copy?
425 if state not in 'mn' and not dryrun:
425 if state not in 'mn' and not dryrun:
426 repo.dirstate.normallookup(abstarget)
426 repo.dirstate.normallookup(abstarget)
427 else:
427 else:
428 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
428 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
429 if not ui.quiet:
429 if not ui.quiet:
430 ui.warn(_("%s has not been committed yet, so no copy "
430 ui.warn(_("%s has not been committed yet, so no copy "
431 "data will be stored for %s.\n")
431 "data will be stored for %s.\n")
432 % (repo.pathto(origsrc, cwd), reltarget))
432 % (repo.pathto(origsrc, cwd), reltarget))
433 if repo.dirstate[abstarget] in '?r' and not dryrun:
433 if repo.dirstate[abstarget] in '?r' and not dryrun:
434 wctx.add([abstarget])
434 wctx.add([abstarget])
435 elif not dryrun:
435 elif not dryrun:
436 wctx.copy(origsrc, abstarget)
436 wctx.copy(origsrc, abstarget)
437
437
438 if rename and not dryrun:
438 if rename and not dryrun:
439 wctx.remove([abssrc], not after)
439 wctx.remove([abssrc], not after)
440
440
441 # pat: ossep
441 # pat: ossep
442 # dest ossep
442 # dest ossep
443 # srcs: list of (hgsep, hgsep, ossep, bool)
443 # srcs: list of (hgsep, hgsep, ossep, bool)
444 # return: function that takes hgsep and returns ossep
444 # return: function that takes hgsep and returns ossep
445 def targetpathfn(pat, dest, srcs):
445 def targetpathfn(pat, dest, srcs):
446 if os.path.isdir(pat):
446 if os.path.isdir(pat):
447 abspfx = util.canonpath(repo.root, cwd, pat)
447 abspfx = util.canonpath(repo.root, cwd, pat)
448 abspfx = util.localpath(abspfx)
448 abspfx = util.localpath(abspfx)
449 if destdirexists:
449 if destdirexists:
450 striplen = len(os.path.split(abspfx)[0])
450 striplen = len(os.path.split(abspfx)[0])
451 else:
451 else:
452 striplen = len(abspfx)
452 striplen = len(abspfx)
453 if striplen:
453 if striplen:
454 striplen += len(os.sep)
454 striplen += len(os.sep)
455 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
455 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
456 elif destdirexists:
456 elif destdirexists:
457 res = lambda p: os.path.join(dest,
457 res = lambda p: os.path.join(dest,
458 os.path.basename(util.localpath(p)))
458 os.path.basename(util.localpath(p)))
459 else:
459 else:
460 res = lambda p: dest
460 res = lambda p: dest
461 return res
461 return res
462
462
463 # pat: ossep
463 # pat: ossep
464 # dest ossep
464 # dest ossep
465 # srcs: list of (hgsep, hgsep, ossep, bool)
465 # srcs: list of (hgsep, hgsep, ossep, bool)
466 # return: function that takes hgsep and returns ossep
466 # return: function that takes hgsep and returns ossep
467 def targetpathafterfn(pat, dest, srcs):
467 def targetpathafterfn(pat, dest, srcs):
468 if matchmod.patkind(pat):
468 if matchmod.patkind(pat):
469 # a mercurial pattern
469 # a mercurial pattern
470 res = lambda p: os.path.join(dest,
470 res = lambda p: os.path.join(dest,
471 os.path.basename(util.localpath(p)))
471 os.path.basename(util.localpath(p)))
472 else:
472 else:
473 abspfx = util.canonpath(repo.root, cwd, pat)
473 abspfx = util.canonpath(repo.root, cwd, pat)
474 if len(abspfx) < len(srcs[0][0]):
474 if len(abspfx) < len(srcs[0][0]):
475 # A directory. Either the target path contains the last
475 # A directory. Either the target path contains the last
476 # component of the source path or it does not.
476 # component of the source path or it does not.
477 def evalpath(striplen):
477 def evalpath(striplen):
478 score = 0
478 score = 0
479 for s in srcs:
479 for s in srcs:
480 t = os.path.join(dest, util.localpath(s[0])[striplen:])
480 t = os.path.join(dest, util.localpath(s[0])[striplen:])
481 if os.path.exists(t):
481 if os.path.exists(t):
482 score += 1
482 score += 1
483 return score
483 return score
484
484
485 abspfx = util.localpath(abspfx)
485 abspfx = util.localpath(abspfx)
486 striplen = len(abspfx)
486 striplen = len(abspfx)
487 if striplen:
487 if striplen:
488 striplen += len(os.sep)
488 striplen += len(os.sep)
489 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
489 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
490 score = evalpath(striplen)
490 score = evalpath(striplen)
491 striplen1 = len(os.path.split(abspfx)[0])
491 striplen1 = len(os.path.split(abspfx)[0])
492 if striplen1:
492 if striplen1:
493 striplen1 += len(os.sep)
493 striplen1 += len(os.sep)
494 if evalpath(striplen1) > score:
494 if evalpath(striplen1) > score:
495 striplen = striplen1
495 striplen = striplen1
496 res = lambda p: os.path.join(dest,
496 res = lambda p: os.path.join(dest,
497 util.localpath(p)[striplen:])
497 util.localpath(p)[striplen:])
498 else:
498 else:
499 # a file
499 # a file
500 if destdirexists:
500 if destdirexists:
501 res = lambda p: os.path.join(dest,
501 res = lambda p: os.path.join(dest,
502 os.path.basename(util.localpath(p)))
502 os.path.basename(util.localpath(p)))
503 else:
503 else:
504 res = lambda p: dest
504 res = lambda p: dest
505 return res
505 return res
506
506
507
507
508 pats = expandpats(pats)
508 pats = expandpats(pats)
509 if not pats:
509 if not pats:
510 raise util.Abort(_('no source or destination specified'))
510 raise util.Abort(_('no source or destination specified'))
511 if len(pats) == 1:
511 if len(pats) == 1:
512 raise util.Abort(_('no destination specified'))
512 raise util.Abort(_('no destination specified'))
513 dest = pats.pop()
513 dest = pats.pop()
514 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
514 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
515 if not destdirexists:
515 if not destdirexists:
516 if len(pats) > 1 or matchmod.patkind(pats[0]):
516 if len(pats) > 1 or matchmod.patkind(pats[0]):
517 raise util.Abort(_('with multiple sources, destination must be an '
517 raise util.Abort(_('with multiple sources, destination must be an '
518 'existing directory'))
518 'existing directory'))
519 if util.endswithsep(dest):
519 if util.endswithsep(dest):
520 raise util.Abort(_('destination %s is not a directory') % dest)
520 raise util.Abort(_('destination %s is not a directory') % dest)
521
521
522 tfn = targetpathfn
522 tfn = targetpathfn
523 if after:
523 if after:
524 tfn = targetpathafterfn
524 tfn = targetpathafterfn
525 copylist = []
525 copylist = []
526 for pat in pats:
526 for pat in pats:
527 srcs = walkpat(pat)
527 srcs = walkpat(pat)
528 if not srcs:
528 if not srcs:
529 continue
529 continue
530 copylist.append((tfn(pat, dest, srcs), srcs))
530 copylist.append((tfn(pat, dest, srcs), srcs))
531 if not copylist:
531 if not copylist:
532 raise util.Abort(_('no files to copy'))
532 raise util.Abort(_('no files to copy'))
533
533
534 errors = 0
534 errors = 0
535 for targetpath, srcs in copylist:
535 for targetpath, srcs in copylist:
536 for abssrc, relsrc, exact in srcs:
536 for abssrc, relsrc, exact in srcs:
537 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
537 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
538 errors += 1
538 errors += 1
539
539
540 if errors:
540 if errors:
541 ui.warn(_('(consider using --after)\n'))
541 ui.warn(_('(consider using --after)\n'))
542
542
543 return errors != 0
543 return errors != 0
544
544
545 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
545 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
546 runargs=None, appendpid=False):
546 runargs=None, appendpid=False):
547 '''Run a command as a service.'''
547 '''Run a command as a service.'''
548
548
549 if opts['daemon'] and not opts['daemon_pipefds']:
549 if opts['daemon'] and not opts['daemon_pipefds']:
550 # Signal child process startup with file removal
550 # Signal child process startup with file removal
551 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
551 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
552 os.close(lockfd)
552 os.close(lockfd)
553 try:
553 try:
554 if not runargs:
554 if not runargs:
555 runargs = util.hgcmd() + sys.argv[1:]
555 runargs = util.hgcmd() + sys.argv[1:]
556 runargs.append('--daemon-pipefds=%s' % lockpath)
556 runargs.append('--daemon-pipefds=%s' % lockpath)
557 # Don't pass --cwd to the child process, because we've already
557 # Don't pass --cwd to the child process, because we've already
558 # changed directory.
558 # changed directory.
559 for i in xrange(1, len(runargs)):
559 for i in xrange(1, len(runargs)):
560 if runargs[i].startswith('--cwd='):
560 if runargs[i].startswith('--cwd='):
561 del runargs[i]
561 del runargs[i]
562 break
562 break
563 elif runargs[i].startswith('--cwd'):
563 elif runargs[i].startswith('--cwd'):
564 del runargs[i:i + 2]
564 del runargs[i:i + 2]
565 break
565 break
566 def condfn():
566 def condfn():
567 return not os.path.exists(lockpath)
567 return not os.path.exists(lockpath)
568 pid = util.rundetached(runargs, condfn)
568 pid = util.rundetached(runargs, condfn)
569 if pid < 0:
569 if pid < 0:
570 raise util.Abort(_('child process failed to start'))
570 raise util.Abort(_('child process failed to start'))
571 finally:
571 finally:
572 try:
572 try:
573 os.unlink(lockpath)
573 os.unlink(lockpath)
574 except OSError, e:
574 except OSError, e:
575 if e.errno != errno.ENOENT:
575 if e.errno != errno.ENOENT:
576 raise
576 raise
577 if parentfn:
577 if parentfn:
578 return parentfn(pid)
578 return parentfn(pid)
579 else:
579 else:
580 return
580 return
581
581
582 if initfn:
582 if initfn:
583 initfn()
583 initfn()
584
584
585 if opts['pid_file']:
585 if opts['pid_file']:
586 mode = appendpid and 'a' or 'w'
586 mode = appendpid and 'a' or 'w'
587 fp = open(opts['pid_file'], mode)
587 fp = open(opts['pid_file'], mode)
588 fp.write(str(os.getpid()) + '\n')
588 fp.write(str(os.getpid()) + '\n')
589 fp.close()
589 fp.close()
590
590
591 if opts['daemon_pipefds']:
591 if opts['daemon_pipefds']:
592 lockpath = opts['daemon_pipefds']
592 lockpath = opts['daemon_pipefds']
593 try:
593 try:
594 os.setsid()
594 os.setsid()
595 except AttributeError:
595 except AttributeError:
596 pass
596 pass
597 os.unlink(lockpath)
597 os.unlink(lockpath)
598 util.hidewindow()
598 util.hidewindow()
599 sys.stdout.flush()
599 sys.stdout.flush()
600 sys.stderr.flush()
600 sys.stderr.flush()
601
601
602 nullfd = os.open(util.nulldev, os.O_RDWR)
602 nullfd = os.open(util.nulldev, os.O_RDWR)
603 logfilefd = nullfd
603 logfilefd = nullfd
604 if logfile:
604 if logfile:
605 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
605 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
606 os.dup2(nullfd, 0)
606 os.dup2(nullfd, 0)
607 os.dup2(logfilefd, 1)
607 os.dup2(logfilefd, 1)
608 os.dup2(logfilefd, 2)
608 os.dup2(logfilefd, 2)
609 if nullfd not in (0, 1, 2):
609 if nullfd not in (0, 1, 2):
610 os.close(nullfd)
610 os.close(nullfd)
611 if logfile and logfilefd not in (0, 1, 2):
611 if logfile and logfilefd not in (0, 1, 2):
612 os.close(logfilefd)
612 os.close(logfilefd)
613
613
614 if runfn:
614 if runfn:
615 return runfn()
615 return runfn()
616
616
617 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
617 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
618 opts=None):
618 opts=None):
619 '''export changesets as hg patches.'''
619 '''export changesets as hg patches.'''
620
620
621 total = len(revs)
621 total = len(revs)
622 revwidth = max([len(str(rev)) for rev in revs])
622 revwidth = max([len(str(rev)) for rev in revs])
623
623
624 def single(rev, seqno, fp):
624 def single(rev, seqno, fp):
625 ctx = repo[rev]
625 ctx = repo[rev]
626 node = ctx.node()
626 node = ctx.node()
627 parents = [p.node() for p in ctx.parents() if p]
627 parents = [p.node() for p in ctx.parents() if p]
628 branch = ctx.branch()
628 branch = ctx.branch()
629 if switch_parent:
629 if switch_parent:
630 parents.reverse()
630 parents.reverse()
631 prev = (parents and parents[0]) or nullid
631 prev = (parents and parents[0]) or nullid
632
632
633 if not fp:
633 if not fp:
634 fp = make_file(repo, template, node, total=total, seqno=seqno,
634 fp = make_file(repo, template, node, total=total, seqno=seqno,
635 revwidth=revwidth, mode='ab')
635 revwidth=revwidth, mode='ab')
636 if fp != sys.stdout and hasattr(fp, 'name'):
636 if fp != sys.stdout and hasattr(fp, 'name'):
637 repo.ui.note("%s\n" % fp.name)
637 repo.ui.note("%s\n" % fp.name)
638
638
639 fp.write("# HG changeset patch\n")
639 fp.write("# HG changeset patch\n")
640 fp.write("# User %s\n" % ctx.user())
640 fp.write("# User %s\n" % ctx.user())
641 fp.write("# Date %d %d\n" % ctx.date())
641 fp.write("# Date %d %d\n" % ctx.date())
642 if branch and branch != 'default':
642 if branch and branch != 'default':
643 fp.write("# Branch %s\n" % branch)
643 fp.write("# Branch %s\n" % branch)
644 fp.write("# Node ID %s\n" % hex(node))
644 fp.write("# Node ID %s\n" % hex(node))
645 fp.write("# Parent %s\n" % hex(prev))
645 fp.write("# Parent %s\n" % hex(prev))
646 if len(parents) > 1:
646 if len(parents) > 1:
647 fp.write("# Parent %s\n" % hex(parents[1]))
647 fp.write("# Parent %s\n" % hex(parents[1]))
648 fp.write(ctx.description().rstrip())
648 fp.write(ctx.description().rstrip())
649 fp.write("\n\n")
649 fp.write("\n\n")
650
650
651 for chunk in patch.diff(repo, prev, node, opts=opts):
651 for chunk in patch.diff(repo, prev, node, opts=opts):
652 fp.write(chunk)
652 fp.write(chunk)
653
653
654 for seqno, rev in enumerate(revs):
654 for seqno, rev in enumerate(revs):
655 single(rev, seqno + 1, fp)
655 single(rev, seqno + 1, fp)
656
656
657 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
657 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
658 changes=None, stat=False, fp=None):
658 changes=None, stat=False, fp=None, prefix='',
659 listsubrepos=False):
659 '''show diff or diffstat.'''
660 '''show diff or diffstat.'''
660 if fp is None:
661 if fp is None:
661 write = ui.write
662 write = ui.write
662 else:
663 else:
663 def write(s, **kw):
664 def write(s, **kw):
664 fp.write(s)
665 fp.write(s)
665
666
666 if stat:
667 if stat:
667 diffopts = diffopts.copy(context=0)
668 diffopts = diffopts.copy(context=0)
668 width = 80
669 width = 80
669 if not ui.plain():
670 if not ui.plain():
670 width = util.termwidth()
671 width = util.termwidth()
671 chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
672 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
673 prefix=prefix)
672 for chunk, label in patch.diffstatui(util.iterlines(chunks),
674 for chunk, label in patch.diffstatui(util.iterlines(chunks),
673 width=width,
675 width=width,
674 git=diffopts.git):
676 git=diffopts.git):
675 write(chunk, label=label)
677 write(chunk, label=label)
676 else:
678 else:
677 for chunk, label in patch.diffui(repo, node1, node2, match,
679 for chunk, label in patch.diffui(repo, node1, node2, match,
678 changes, diffopts):
680 changes, diffopts, prefix=prefix):
679 write(chunk, label=label)
681 write(chunk, label=label)
680
682
683 if listsubrepos:
684 ctx1 = repo[node1]
685 for subpath in ctx1.substate:
686 sub = ctx1.sub(subpath)
687 if node2 is not None:
688 node2 = bin(repo[node2].substate[subpath][1])
689 submatch = matchmod.narrowmatcher(subpath, match)
690 sub.diff(diffopts, node2, submatch, changes=changes,
691 stat=stat, fp=fp, prefix=prefix)
692
681 class changeset_printer(object):
693 class changeset_printer(object):
682 '''show changeset information when templating not requested.'''
694 '''show changeset information when templating not requested.'''
683
695
684 def __init__(self, ui, repo, patch, diffopts, buffered):
696 def __init__(self, ui, repo, patch, diffopts, buffered):
685 self.ui = ui
697 self.ui = ui
686 self.repo = repo
698 self.repo = repo
687 self.buffered = buffered
699 self.buffered = buffered
688 self.patch = patch
700 self.patch = patch
689 self.diffopts = diffopts
701 self.diffopts = diffopts
690 self.header = {}
702 self.header = {}
691 self.hunk = {}
703 self.hunk = {}
692 self.lastheader = None
704 self.lastheader = None
693 self.footer = None
705 self.footer = None
694
706
695 def flush(self, rev):
707 def flush(self, rev):
696 if rev in self.header:
708 if rev in self.header:
697 h = self.header[rev]
709 h = self.header[rev]
698 if h != self.lastheader:
710 if h != self.lastheader:
699 self.lastheader = h
711 self.lastheader = h
700 self.ui.write(h)
712 self.ui.write(h)
701 del self.header[rev]
713 del self.header[rev]
702 if rev in self.hunk:
714 if rev in self.hunk:
703 self.ui.write(self.hunk[rev])
715 self.ui.write(self.hunk[rev])
704 del self.hunk[rev]
716 del self.hunk[rev]
705 return 1
717 return 1
706 return 0
718 return 0
707
719
708 def close(self):
720 def close(self):
709 if self.footer:
721 if self.footer:
710 self.ui.write(self.footer)
722 self.ui.write(self.footer)
711
723
712 def show(self, ctx, copies=None, matchfn=None, **props):
724 def show(self, ctx, copies=None, matchfn=None, **props):
713 if self.buffered:
725 if self.buffered:
714 self.ui.pushbuffer()
726 self.ui.pushbuffer()
715 self._show(ctx, copies, matchfn, props)
727 self._show(ctx, copies, matchfn, props)
716 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
728 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
717 else:
729 else:
718 self._show(ctx, copies, matchfn, props)
730 self._show(ctx, copies, matchfn, props)
719
731
720 def _show(self, ctx, copies, matchfn, props):
732 def _show(self, ctx, copies, matchfn, props):
721 '''show a single changeset or file revision'''
733 '''show a single changeset or file revision'''
722 changenode = ctx.node()
734 changenode = ctx.node()
723 rev = ctx.rev()
735 rev = ctx.rev()
724
736
725 if self.ui.quiet:
737 if self.ui.quiet:
726 self.ui.write("%d:%s\n" % (rev, short(changenode)),
738 self.ui.write("%d:%s\n" % (rev, short(changenode)),
727 label='log.node')
739 label='log.node')
728 return
740 return
729
741
730 log = self.repo.changelog
742 log = self.repo.changelog
731 date = util.datestr(ctx.date())
743 date = util.datestr(ctx.date())
732
744
733 hexfunc = self.ui.debugflag and hex or short
745 hexfunc = self.ui.debugflag and hex or short
734
746
735 parents = [(p, hexfunc(log.node(p)))
747 parents = [(p, hexfunc(log.node(p)))
736 for p in self._meaningful_parentrevs(log, rev)]
748 for p in self._meaningful_parentrevs(log, rev)]
737
749
738 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
750 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
739 label='log.changeset')
751 label='log.changeset')
740
752
741 branch = ctx.branch()
753 branch = ctx.branch()
742 # don't show the default branch name
754 # don't show the default branch name
743 if branch != 'default':
755 if branch != 'default':
744 branch = encoding.tolocal(branch)
756 branch = encoding.tolocal(branch)
745 self.ui.write(_("branch: %s\n") % branch,
757 self.ui.write(_("branch: %s\n") % branch,
746 label='log.branch')
758 label='log.branch')
747 for tag in self.repo.nodetags(changenode):
759 for tag in self.repo.nodetags(changenode):
748 self.ui.write(_("tag: %s\n") % tag,
760 self.ui.write(_("tag: %s\n") % tag,
749 label='log.tag')
761 label='log.tag')
750 for parent in parents:
762 for parent in parents:
751 self.ui.write(_("parent: %d:%s\n") % parent,
763 self.ui.write(_("parent: %d:%s\n") % parent,
752 label='log.parent')
764 label='log.parent')
753
765
754 if self.ui.debugflag:
766 if self.ui.debugflag:
755 mnode = ctx.manifestnode()
767 mnode = ctx.manifestnode()
756 self.ui.write(_("manifest: %d:%s\n") %
768 self.ui.write(_("manifest: %d:%s\n") %
757 (self.repo.manifest.rev(mnode), hex(mnode)),
769 (self.repo.manifest.rev(mnode), hex(mnode)),
758 label='ui.debug log.manifest')
770 label='ui.debug log.manifest')
759 self.ui.write(_("user: %s\n") % ctx.user(),
771 self.ui.write(_("user: %s\n") % ctx.user(),
760 label='log.user')
772 label='log.user')
761 self.ui.write(_("date: %s\n") % date,
773 self.ui.write(_("date: %s\n") % date,
762 label='log.date')
774 label='log.date')
763
775
764 if self.ui.debugflag:
776 if self.ui.debugflag:
765 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
777 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
766 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
778 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
767 files):
779 files):
768 if value:
780 if value:
769 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
781 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
770 label='ui.debug log.files')
782 label='ui.debug log.files')
771 elif ctx.files() and self.ui.verbose:
783 elif ctx.files() and self.ui.verbose:
772 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
784 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
773 label='ui.note log.files')
785 label='ui.note log.files')
774 if copies and self.ui.verbose:
786 if copies and self.ui.verbose:
775 copies = ['%s (%s)' % c for c in copies]
787 copies = ['%s (%s)' % c for c in copies]
776 self.ui.write(_("copies: %s\n") % ' '.join(copies),
788 self.ui.write(_("copies: %s\n") % ' '.join(copies),
777 label='ui.note log.copies')
789 label='ui.note log.copies')
778
790
779 extra = ctx.extra()
791 extra = ctx.extra()
780 if extra and self.ui.debugflag:
792 if extra and self.ui.debugflag:
781 for key, value in sorted(extra.items()):
793 for key, value in sorted(extra.items()):
782 self.ui.write(_("extra: %s=%s\n")
794 self.ui.write(_("extra: %s=%s\n")
783 % (key, value.encode('string_escape')),
795 % (key, value.encode('string_escape')),
784 label='ui.debug log.extra')
796 label='ui.debug log.extra')
785
797
786 description = ctx.description().strip()
798 description = ctx.description().strip()
787 if description:
799 if description:
788 if self.ui.verbose:
800 if self.ui.verbose:
789 self.ui.write(_("description:\n"),
801 self.ui.write(_("description:\n"),
790 label='ui.note log.description')
802 label='ui.note log.description')
791 self.ui.write(description,
803 self.ui.write(description,
792 label='ui.note log.description')
804 label='ui.note log.description')
793 self.ui.write("\n\n")
805 self.ui.write("\n\n")
794 else:
806 else:
795 self.ui.write(_("summary: %s\n") %
807 self.ui.write(_("summary: %s\n") %
796 description.splitlines()[0],
808 description.splitlines()[0],
797 label='log.summary')
809 label='log.summary')
798 self.ui.write("\n")
810 self.ui.write("\n")
799
811
800 self.showpatch(changenode, matchfn)
812 self.showpatch(changenode, matchfn)
801
813
802 def showpatch(self, node, matchfn):
814 def showpatch(self, node, matchfn):
803 if not matchfn:
815 if not matchfn:
804 matchfn = self.patch
816 matchfn = self.patch
805 if matchfn:
817 if matchfn:
806 stat = self.diffopts.get('stat')
818 stat = self.diffopts.get('stat')
807 diff = self.diffopts.get('patch')
819 diff = self.diffopts.get('patch')
808 diffopts = patch.diffopts(self.ui, self.diffopts)
820 diffopts = patch.diffopts(self.ui, self.diffopts)
809 prev = self.repo.changelog.parents(node)[0]
821 prev = self.repo.changelog.parents(node)[0]
810 if stat:
822 if stat:
811 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
823 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
812 match=matchfn, stat=True)
824 match=matchfn, stat=True)
813 if diff:
825 if diff:
814 if stat:
826 if stat:
815 self.ui.write("\n")
827 self.ui.write("\n")
816 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
828 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
817 match=matchfn, stat=False)
829 match=matchfn, stat=False)
818 self.ui.write("\n")
830 self.ui.write("\n")
819
831
820 def _meaningful_parentrevs(self, log, rev):
832 def _meaningful_parentrevs(self, log, rev):
821 """Return list of meaningful (or all if debug) parentrevs for rev.
833 """Return list of meaningful (or all if debug) parentrevs for rev.
822
834
823 For merges (two non-nullrev revisions) both parents are meaningful.
835 For merges (two non-nullrev revisions) both parents are meaningful.
824 Otherwise the first parent revision is considered meaningful if it
836 Otherwise the first parent revision is considered meaningful if it
825 is not the preceding revision.
837 is not the preceding revision.
826 """
838 """
827 parents = log.parentrevs(rev)
839 parents = log.parentrevs(rev)
828 if not self.ui.debugflag and parents[1] == nullrev:
840 if not self.ui.debugflag and parents[1] == nullrev:
829 if parents[0] >= rev - 1:
841 if parents[0] >= rev - 1:
830 parents = []
842 parents = []
831 else:
843 else:
832 parents = [parents[0]]
844 parents = [parents[0]]
833 return parents
845 return parents
834
846
835
847
836 class changeset_templater(changeset_printer):
848 class changeset_templater(changeset_printer):
837 '''format changeset information.'''
849 '''format changeset information.'''
838
850
839 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
851 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
840 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
852 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
841 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
853 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
842 defaulttempl = {
854 defaulttempl = {
843 'parent': '{rev}:{node|formatnode} ',
855 'parent': '{rev}:{node|formatnode} ',
844 'manifest': '{rev}:{node|formatnode}',
856 'manifest': '{rev}:{node|formatnode}',
845 'file_copy': '{name} ({source})',
857 'file_copy': '{name} ({source})',
846 'extra': '{key}={value|stringescape}'
858 'extra': '{key}={value|stringescape}'
847 }
859 }
848 # filecopy is preserved for compatibility reasons
860 # filecopy is preserved for compatibility reasons
849 defaulttempl['filecopy'] = defaulttempl['file_copy']
861 defaulttempl['filecopy'] = defaulttempl['file_copy']
850 self.t = templater.templater(mapfile, {'formatnode': formatnode},
862 self.t = templater.templater(mapfile, {'formatnode': formatnode},
851 cache=defaulttempl)
863 cache=defaulttempl)
852 self.cache = {}
864 self.cache = {}
853
865
854 def use_template(self, t):
866 def use_template(self, t):
855 '''set template string to use'''
867 '''set template string to use'''
856 self.t.cache['changeset'] = t
868 self.t.cache['changeset'] = t
857
869
858 def _meaningful_parentrevs(self, ctx):
870 def _meaningful_parentrevs(self, ctx):
859 """Return list of meaningful (or all if debug) parentrevs for rev.
871 """Return list of meaningful (or all if debug) parentrevs for rev.
860 """
872 """
861 parents = ctx.parents()
873 parents = ctx.parents()
862 if len(parents) > 1:
874 if len(parents) > 1:
863 return parents
875 return parents
864 if self.ui.debugflag:
876 if self.ui.debugflag:
865 return [parents[0], self.repo['null']]
877 return [parents[0], self.repo['null']]
866 if parents[0].rev() >= ctx.rev() - 1:
878 if parents[0].rev() >= ctx.rev() - 1:
867 return []
879 return []
868 return parents
880 return parents
869
881
870 def _show(self, ctx, copies, matchfn, props):
882 def _show(self, ctx, copies, matchfn, props):
871 '''show a single changeset or file revision'''
883 '''show a single changeset or file revision'''
872
884
873 showlist = templatekw.showlist
885 showlist = templatekw.showlist
874
886
875 # showparents() behaviour depends on ui trace level which
887 # showparents() behaviour depends on ui trace level which
876 # causes unexpected behaviours at templating level and makes
888 # causes unexpected behaviours at templating level and makes
877 # it harder to extract it in a standalone function. Its
889 # it harder to extract it in a standalone function. Its
878 # behaviour cannot be changed so leave it here for now.
890 # behaviour cannot be changed so leave it here for now.
879 def showparents(**args):
891 def showparents(**args):
880 ctx = args['ctx']
892 ctx = args['ctx']
881 parents = [[('rev', p.rev()), ('node', p.hex())]
893 parents = [[('rev', p.rev()), ('node', p.hex())]
882 for p in self._meaningful_parentrevs(ctx)]
894 for p in self._meaningful_parentrevs(ctx)]
883 return showlist('parent', parents, **args)
895 return showlist('parent', parents, **args)
884
896
885 props = props.copy()
897 props = props.copy()
886 props.update(templatekw.keywords)
898 props.update(templatekw.keywords)
887 props['parents'] = showparents
899 props['parents'] = showparents
888 props['templ'] = self.t
900 props['templ'] = self.t
889 props['ctx'] = ctx
901 props['ctx'] = ctx
890 props['repo'] = self.repo
902 props['repo'] = self.repo
891 props['revcache'] = {'copies': copies}
903 props['revcache'] = {'copies': copies}
892 props['cache'] = self.cache
904 props['cache'] = self.cache
893
905
894 # find correct templates for current mode
906 # find correct templates for current mode
895
907
896 tmplmodes = [
908 tmplmodes = [
897 (True, None),
909 (True, None),
898 (self.ui.verbose, 'verbose'),
910 (self.ui.verbose, 'verbose'),
899 (self.ui.quiet, 'quiet'),
911 (self.ui.quiet, 'quiet'),
900 (self.ui.debugflag, 'debug'),
912 (self.ui.debugflag, 'debug'),
901 ]
913 ]
902
914
903 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
915 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
904 for mode, postfix in tmplmodes:
916 for mode, postfix in tmplmodes:
905 for type in types:
917 for type in types:
906 cur = postfix and ('%s_%s' % (type, postfix)) or type
918 cur = postfix and ('%s_%s' % (type, postfix)) or type
907 if mode and cur in self.t:
919 if mode and cur in self.t:
908 types[type] = cur
920 types[type] = cur
909
921
910 try:
922 try:
911
923
912 # write header
924 # write header
913 if types['header']:
925 if types['header']:
914 h = templater.stringify(self.t(types['header'], **props))
926 h = templater.stringify(self.t(types['header'], **props))
915 if self.buffered:
927 if self.buffered:
916 self.header[ctx.rev()] = h
928 self.header[ctx.rev()] = h
917 else:
929 else:
918 if self.lastheader != h:
930 if self.lastheader != h:
919 self.lastheader = h
931 self.lastheader = h
920 self.ui.write(h)
932 self.ui.write(h)
921
933
922 # write changeset metadata, then patch if requested
934 # write changeset metadata, then patch if requested
923 key = types['changeset']
935 key = types['changeset']
924 self.ui.write(templater.stringify(self.t(key, **props)))
936 self.ui.write(templater.stringify(self.t(key, **props)))
925 self.showpatch(ctx.node(), matchfn)
937 self.showpatch(ctx.node(), matchfn)
926
938
927 if types['footer']:
939 if types['footer']:
928 if not self.footer:
940 if not self.footer:
929 self.footer = templater.stringify(self.t(types['footer'],
941 self.footer = templater.stringify(self.t(types['footer'],
930 **props))
942 **props))
931
943
932 except KeyError, inst:
944 except KeyError, inst:
933 msg = _("%s: no key named '%s'")
945 msg = _("%s: no key named '%s'")
934 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
946 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
935 except SyntaxError, inst:
947 except SyntaxError, inst:
936 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
948 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
937
949
938 def show_changeset(ui, repo, opts, buffered=False):
950 def show_changeset(ui, repo, opts, buffered=False):
939 """show one changeset using template or regular display.
951 """show one changeset using template or regular display.
940
952
941 Display format will be the first non-empty hit of:
953 Display format will be the first non-empty hit of:
942 1. option 'template'
954 1. option 'template'
943 2. option 'style'
955 2. option 'style'
944 3. [ui] setting 'logtemplate'
956 3. [ui] setting 'logtemplate'
945 4. [ui] setting 'style'
957 4. [ui] setting 'style'
946 If all of these values are either the unset or the empty string,
958 If all of these values are either the unset or the empty string,
947 regular display via changeset_printer() is done.
959 regular display via changeset_printer() is done.
948 """
960 """
949 # options
961 # options
950 patch = False
962 patch = False
951 if opts.get('patch') or opts.get('stat'):
963 if opts.get('patch') or opts.get('stat'):
952 patch = matchall(repo)
964 patch = matchall(repo)
953
965
954 tmpl = opts.get('template')
966 tmpl = opts.get('template')
955 style = None
967 style = None
956 if tmpl:
968 if tmpl:
957 tmpl = templater.parsestring(tmpl, quoted=False)
969 tmpl = templater.parsestring(tmpl, quoted=False)
958 else:
970 else:
959 style = opts.get('style')
971 style = opts.get('style')
960
972
961 # ui settings
973 # ui settings
962 if not (tmpl or style):
974 if not (tmpl or style):
963 tmpl = ui.config('ui', 'logtemplate')
975 tmpl = ui.config('ui', 'logtemplate')
964 if tmpl:
976 if tmpl:
965 tmpl = templater.parsestring(tmpl)
977 tmpl = templater.parsestring(tmpl)
966 else:
978 else:
967 style = util.expandpath(ui.config('ui', 'style', ''))
979 style = util.expandpath(ui.config('ui', 'style', ''))
968
980
969 if not (tmpl or style):
981 if not (tmpl or style):
970 return changeset_printer(ui, repo, patch, opts, buffered)
982 return changeset_printer(ui, repo, patch, opts, buffered)
971
983
972 mapfile = None
984 mapfile = None
973 if style and not tmpl:
985 if style and not tmpl:
974 mapfile = style
986 mapfile = style
975 if not os.path.split(mapfile)[0]:
987 if not os.path.split(mapfile)[0]:
976 mapname = (templater.templatepath('map-cmdline.' + mapfile)
988 mapname = (templater.templatepath('map-cmdline.' + mapfile)
977 or templater.templatepath(mapfile))
989 or templater.templatepath(mapfile))
978 if mapname:
990 if mapname:
979 mapfile = mapname
991 mapfile = mapname
980
992
981 try:
993 try:
982 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
994 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
983 except SyntaxError, inst:
995 except SyntaxError, inst:
984 raise util.Abort(inst.args[0])
996 raise util.Abort(inst.args[0])
985 if tmpl:
997 if tmpl:
986 t.use_template(tmpl)
998 t.use_template(tmpl)
987 return t
999 return t
988
1000
989 def finddate(ui, repo, date):
1001 def finddate(ui, repo, date):
990 """Find the tipmost changeset that matches the given date spec"""
1002 """Find the tipmost changeset that matches the given date spec"""
991
1003
992 df = util.matchdate(date)
1004 df = util.matchdate(date)
993 m = matchall(repo)
1005 m = matchall(repo)
994 results = {}
1006 results = {}
995
1007
996 def prep(ctx, fns):
1008 def prep(ctx, fns):
997 d = ctx.date()
1009 d = ctx.date()
998 if df(d[0]):
1010 if df(d[0]):
999 results[ctx.rev()] = d
1011 results[ctx.rev()] = d
1000
1012
1001 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1013 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1002 rev = ctx.rev()
1014 rev = ctx.rev()
1003 if rev in results:
1015 if rev in results:
1004 ui.status(_("Found revision %s from %s\n") %
1016 ui.status(_("Found revision %s from %s\n") %
1005 (rev, util.datestr(results[rev])))
1017 (rev, util.datestr(results[rev])))
1006 return str(rev)
1018 return str(rev)
1007
1019
1008 raise util.Abort(_("revision matching date not found"))
1020 raise util.Abort(_("revision matching date not found"))
1009
1021
1010 def walkchangerevs(repo, match, opts, prepare):
1022 def walkchangerevs(repo, match, opts, prepare):
1011 '''Iterate over files and the revs in which they changed.
1023 '''Iterate over files and the revs in which they changed.
1012
1024
1013 Callers most commonly need to iterate backwards over the history
1025 Callers most commonly need to iterate backwards over the history
1014 in which they are interested. Doing so has awful (quadratic-looking)
1026 in which they are interested. Doing so has awful (quadratic-looking)
1015 performance, so we use iterators in a "windowed" way.
1027 performance, so we use iterators in a "windowed" way.
1016
1028
1017 We walk a window of revisions in the desired order. Within the
1029 We walk a window of revisions in the desired order. Within the
1018 window, we first walk forwards to gather data, then in the desired
1030 window, we first walk forwards to gather data, then in the desired
1019 order (usually backwards) to display it.
1031 order (usually backwards) to display it.
1020
1032
1021 This function returns an iterator yielding contexts. Before
1033 This function returns an iterator yielding contexts. Before
1022 yielding each context, the iterator will first call the prepare
1034 yielding each context, the iterator will first call the prepare
1023 function on each context in the window in forward order.'''
1035 function on each context in the window in forward order.'''
1024
1036
1025 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1037 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1026 if start < end:
1038 if start < end:
1027 while start < end:
1039 while start < end:
1028 yield start, min(windowsize, end - start)
1040 yield start, min(windowsize, end - start)
1029 start += windowsize
1041 start += windowsize
1030 if windowsize < sizelimit:
1042 if windowsize < sizelimit:
1031 windowsize *= 2
1043 windowsize *= 2
1032 else:
1044 else:
1033 while start > end:
1045 while start > end:
1034 yield start, min(windowsize, start - end - 1)
1046 yield start, min(windowsize, start - end - 1)
1035 start -= windowsize
1047 start -= windowsize
1036 if windowsize < sizelimit:
1048 if windowsize < sizelimit:
1037 windowsize *= 2
1049 windowsize *= 2
1038
1050
1039 follow = opts.get('follow') or opts.get('follow_first')
1051 follow = opts.get('follow') or opts.get('follow_first')
1040
1052
1041 if not len(repo):
1053 if not len(repo):
1042 return []
1054 return []
1043
1055
1044 if follow:
1056 if follow:
1045 defrange = '%s:0' % repo['.'].rev()
1057 defrange = '%s:0' % repo['.'].rev()
1046 else:
1058 else:
1047 defrange = '-1:0'
1059 defrange = '-1:0'
1048 revs = revrange(repo, opts['rev'] or [defrange])
1060 revs = revrange(repo, opts['rev'] or [defrange])
1049 if not revs:
1061 if not revs:
1050 return []
1062 return []
1051 wanted = set()
1063 wanted = set()
1052 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1064 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1053 fncache = {}
1065 fncache = {}
1054 change = util.cachefunc(repo.changectx)
1066 change = util.cachefunc(repo.changectx)
1055
1067
1056 # First step is to fill wanted, the set of revisions that we want to yield.
1068 # First step is to fill wanted, the set of revisions that we want to yield.
1057 # When it does not induce extra cost, we also fill fncache for revisions in
1069 # When it does not induce extra cost, we also fill fncache for revisions in
1058 # wanted: a cache of filenames that were changed (ctx.files()) and that
1070 # wanted: a cache of filenames that were changed (ctx.files()) and that
1059 # match the file filtering conditions.
1071 # match the file filtering conditions.
1060
1072
1061 if not slowpath and not match.files():
1073 if not slowpath and not match.files():
1062 # No files, no patterns. Display all revs.
1074 # No files, no patterns. Display all revs.
1063 wanted = set(revs)
1075 wanted = set(revs)
1064 copies = []
1076 copies = []
1065
1077
1066 if not slowpath:
1078 if not slowpath:
1067 # We only have to read through the filelog to find wanted revisions
1079 # We only have to read through the filelog to find wanted revisions
1068
1080
1069 minrev, maxrev = min(revs), max(revs)
1081 minrev, maxrev = min(revs), max(revs)
1070 def filerevgen(filelog, last):
1082 def filerevgen(filelog, last):
1071 """
1083 """
1072 Only files, no patterns. Check the history of each file.
1084 Only files, no patterns. Check the history of each file.
1073
1085
1074 Examines filelog entries within minrev, maxrev linkrev range
1086 Examines filelog entries within minrev, maxrev linkrev range
1075 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1087 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1076 tuples in backwards order
1088 tuples in backwards order
1077 """
1089 """
1078 cl_count = len(repo)
1090 cl_count = len(repo)
1079 revs = []
1091 revs = []
1080 for j in xrange(0, last + 1):
1092 for j in xrange(0, last + 1):
1081 linkrev = filelog.linkrev(j)
1093 linkrev = filelog.linkrev(j)
1082 if linkrev < minrev:
1094 if linkrev < minrev:
1083 continue
1095 continue
1084 # only yield rev for which we have the changelog, it can
1096 # only yield rev for which we have the changelog, it can
1085 # happen while doing "hg log" during a pull or commit
1097 # happen while doing "hg log" during a pull or commit
1086 if linkrev > maxrev or linkrev >= cl_count:
1098 if linkrev > maxrev or linkrev >= cl_count:
1087 break
1099 break
1088
1100
1089 parentlinkrevs = []
1101 parentlinkrevs = []
1090 for p in filelog.parentrevs(j):
1102 for p in filelog.parentrevs(j):
1091 if p != nullrev:
1103 if p != nullrev:
1092 parentlinkrevs.append(filelog.linkrev(p))
1104 parentlinkrevs.append(filelog.linkrev(p))
1093 n = filelog.node(j)
1105 n = filelog.node(j)
1094 revs.append((linkrev, parentlinkrevs,
1106 revs.append((linkrev, parentlinkrevs,
1095 follow and filelog.renamed(n)))
1107 follow and filelog.renamed(n)))
1096
1108
1097 return reversed(revs)
1109 return reversed(revs)
1098 def iterfiles():
1110 def iterfiles():
1099 for filename in match.files():
1111 for filename in match.files():
1100 yield filename, None
1112 yield filename, None
1101 for filename_node in copies:
1113 for filename_node in copies:
1102 yield filename_node
1114 yield filename_node
1103 for file_, node in iterfiles():
1115 for file_, node in iterfiles():
1104 filelog = repo.file(file_)
1116 filelog = repo.file(file_)
1105 if not len(filelog):
1117 if not len(filelog):
1106 if node is None:
1118 if node is None:
1107 # A zero count may be a directory or deleted file, so
1119 # A zero count may be a directory or deleted file, so
1108 # try to find matching entries on the slow path.
1120 # try to find matching entries on the slow path.
1109 if follow:
1121 if follow:
1110 raise util.Abort(
1122 raise util.Abort(
1111 _('cannot follow nonexistent file: "%s"') % file_)
1123 _('cannot follow nonexistent file: "%s"') % file_)
1112 slowpath = True
1124 slowpath = True
1113 break
1125 break
1114 else:
1126 else:
1115 continue
1127 continue
1116
1128
1117 if node is None:
1129 if node is None:
1118 last = len(filelog) - 1
1130 last = len(filelog) - 1
1119 else:
1131 else:
1120 last = filelog.rev(node)
1132 last = filelog.rev(node)
1121
1133
1122
1134
1123 # keep track of all ancestors of the file
1135 # keep track of all ancestors of the file
1124 ancestors = set([filelog.linkrev(last)])
1136 ancestors = set([filelog.linkrev(last)])
1125
1137
1126 # iterate from latest to oldest revision
1138 # iterate from latest to oldest revision
1127 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1139 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1128 if rev not in ancestors:
1140 if rev not in ancestors:
1129 continue
1141 continue
1130 # XXX insert 1327 fix here
1142 # XXX insert 1327 fix here
1131 if flparentlinkrevs:
1143 if flparentlinkrevs:
1132 ancestors.update(flparentlinkrevs)
1144 ancestors.update(flparentlinkrevs)
1133
1145
1134 fncache.setdefault(rev, []).append(file_)
1146 fncache.setdefault(rev, []).append(file_)
1135 wanted.add(rev)
1147 wanted.add(rev)
1136 if copied:
1148 if copied:
1137 copies.append(copied)
1149 copies.append(copied)
1138 if slowpath:
1150 if slowpath:
1139 # We have to read the changelog to match filenames against
1151 # We have to read the changelog to match filenames against
1140 # changed files
1152 # changed files
1141
1153
1142 if follow:
1154 if follow:
1143 raise util.Abort(_('can only follow copies/renames for explicit '
1155 raise util.Abort(_('can only follow copies/renames for explicit '
1144 'filenames'))
1156 'filenames'))
1145
1157
1146 # The slow path checks files modified in every changeset.
1158 # The slow path checks files modified in every changeset.
1147 for i in sorted(revs):
1159 for i in sorted(revs):
1148 ctx = change(i)
1160 ctx = change(i)
1149 matches = filter(match, ctx.files())
1161 matches = filter(match, ctx.files())
1150 if matches:
1162 if matches:
1151 fncache[i] = matches
1163 fncache[i] = matches
1152 wanted.add(i)
1164 wanted.add(i)
1153
1165
1154 class followfilter(object):
1166 class followfilter(object):
1155 def __init__(self, onlyfirst=False):
1167 def __init__(self, onlyfirst=False):
1156 self.startrev = nullrev
1168 self.startrev = nullrev
1157 self.roots = set()
1169 self.roots = set()
1158 self.onlyfirst = onlyfirst
1170 self.onlyfirst = onlyfirst
1159
1171
1160 def match(self, rev):
1172 def match(self, rev):
1161 def realparents(rev):
1173 def realparents(rev):
1162 if self.onlyfirst:
1174 if self.onlyfirst:
1163 return repo.changelog.parentrevs(rev)[0:1]
1175 return repo.changelog.parentrevs(rev)[0:1]
1164 else:
1176 else:
1165 return filter(lambda x: x != nullrev,
1177 return filter(lambda x: x != nullrev,
1166 repo.changelog.parentrevs(rev))
1178 repo.changelog.parentrevs(rev))
1167
1179
1168 if self.startrev == nullrev:
1180 if self.startrev == nullrev:
1169 self.startrev = rev
1181 self.startrev = rev
1170 return True
1182 return True
1171
1183
1172 if rev > self.startrev:
1184 if rev > self.startrev:
1173 # forward: all descendants
1185 # forward: all descendants
1174 if not self.roots:
1186 if not self.roots:
1175 self.roots.add(self.startrev)
1187 self.roots.add(self.startrev)
1176 for parent in realparents(rev):
1188 for parent in realparents(rev):
1177 if parent in self.roots:
1189 if parent in self.roots:
1178 self.roots.add(rev)
1190 self.roots.add(rev)
1179 return True
1191 return True
1180 else:
1192 else:
1181 # backwards: all parents
1193 # backwards: all parents
1182 if not self.roots:
1194 if not self.roots:
1183 self.roots.update(realparents(self.startrev))
1195 self.roots.update(realparents(self.startrev))
1184 if rev in self.roots:
1196 if rev in self.roots:
1185 self.roots.remove(rev)
1197 self.roots.remove(rev)
1186 self.roots.update(realparents(rev))
1198 self.roots.update(realparents(rev))
1187 return True
1199 return True
1188
1200
1189 return False
1201 return False
1190
1202
1191 # it might be worthwhile to do this in the iterator if the rev range
1203 # it might be worthwhile to do this in the iterator if the rev range
1192 # is descending and the prune args are all within that range
1204 # is descending and the prune args are all within that range
1193 for rev in opts.get('prune', ()):
1205 for rev in opts.get('prune', ()):
1194 rev = repo.changelog.rev(repo.lookup(rev))
1206 rev = repo.changelog.rev(repo.lookup(rev))
1195 ff = followfilter()
1207 ff = followfilter()
1196 stop = min(revs[0], revs[-1])
1208 stop = min(revs[0], revs[-1])
1197 for x in xrange(rev, stop - 1, -1):
1209 for x in xrange(rev, stop - 1, -1):
1198 if ff.match(x):
1210 if ff.match(x):
1199 wanted.discard(x)
1211 wanted.discard(x)
1200
1212
1201 # Now that wanted is correctly initialized, we can iterate over the
1213 # Now that wanted is correctly initialized, we can iterate over the
1202 # revision range, yielding only revisions in wanted.
1214 # revision range, yielding only revisions in wanted.
1203 def iterate():
1215 def iterate():
1204 if follow and not match.files():
1216 if follow and not match.files():
1205 ff = followfilter(onlyfirst=opts.get('follow_first'))
1217 ff = followfilter(onlyfirst=opts.get('follow_first'))
1206 def want(rev):
1218 def want(rev):
1207 return ff.match(rev) and rev in wanted
1219 return ff.match(rev) and rev in wanted
1208 else:
1220 else:
1209 def want(rev):
1221 def want(rev):
1210 return rev in wanted
1222 return rev in wanted
1211
1223
1212 for i, window in increasing_windows(0, len(revs)):
1224 for i, window in increasing_windows(0, len(revs)):
1213 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1225 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1214 for rev in sorted(nrevs):
1226 for rev in sorted(nrevs):
1215 fns = fncache.get(rev)
1227 fns = fncache.get(rev)
1216 ctx = change(rev)
1228 ctx = change(rev)
1217 if not fns:
1229 if not fns:
1218 def fns_generator():
1230 def fns_generator():
1219 for f in ctx.files():
1231 for f in ctx.files():
1220 if match(f):
1232 if match(f):
1221 yield f
1233 yield f
1222 fns = fns_generator()
1234 fns = fns_generator()
1223 prepare(ctx, fns)
1235 prepare(ctx, fns)
1224 for rev in nrevs:
1236 for rev in nrevs:
1225 yield change(rev)
1237 yield change(rev)
1226 return iterate()
1238 return iterate()
1227
1239
1228 def commit(ui, repo, commitfunc, pats, opts):
1240 def commit(ui, repo, commitfunc, pats, opts):
1229 '''commit the specified files or all outstanding changes'''
1241 '''commit the specified files or all outstanding changes'''
1230 date = opts.get('date')
1242 date = opts.get('date')
1231 if date:
1243 if date:
1232 opts['date'] = util.parsedate(date)
1244 opts['date'] = util.parsedate(date)
1233 message = logmessage(opts)
1245 message = logmessage(opts)
1234
1246
1235 # extract addremove carefully -- this function can be called from a command
1247 # extract addremove carefully -- this function can be called from a command
1236 # that doesn't support addremove
1248 # that doesn't support addremove
1237 if opts.get('addremove'):
1249 if opts.get('addremove'):
1238 addremove(repo, pats, opts)
1250 addremove(repo, pats, opts)
1239
1251
1240 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1252 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1241
1253
1242 def commiteditor(repo, ctx, subs):
1254 def commiteditor(repo, ctx, subs):
1243 if ctx.description():
1255 if ctx.description():
1244 return ctx.description()
1256 return ctx.description()
1245 return commitforceeditor(repo, ctx, subs)
1257 return commitforceeditor(repo, ctx, subs)
1246
1258
1247 def commitforceeditor(repo, ctx, subs):
1259 def commitforceeditor(repo, ctx, subs):
1248 edittext = []
1260 edittext = []
1249 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1261 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1250 if ctx.description():
1262 if ctx.description():
1251 edittext.append(ctx.description())
1263 edittext.append(ctx.description())
1252 edittext.append("")
1264 edittext.append("")
1253 edittext.append("") # Empty line between message and comments.
1265 edittext.append("") # Empty line between message and comments.
1254 edittext.append(_("HG: Enter commit message."
1266 edittext.append(_("HG: Enter commit message."
1255 " Lines beginning with 'HG:' are removed."))
1267 " Lines beginning with 'HG:' are removed."))
1256 edittext.append(_("HG: Leave message empty to abort commit."))
1268 edittext.append(_("HG: Leave message empty to abort commit."))
1257 edittext.append("HG: --")
1269 edittext.append("HG: --")
1258 edittext.append(_("HG: user: %s") % ctx.user())
1270 edittext.append(_("HG: user: %s") % ctx.user())
1259 if ctx.p2():
1271 if ctx.p2():
1260 edittext.append(_("HG: branch merge"))
1272 edittext.append(_("HG: branch merge"))
1261 if ctx.branch():
1273 if ctx.branch():
1262 edittext.append(_("HG: branch '%s'")
1274 edittext.append(_("HG: branch '%s'")
1263 % encoding.tolocal(ctx.branch()))
1275 % encoding.tolocal(ctx.branch()))
1264 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1276 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1265 edittext.extend([_("HG: added %s") % f for f in added])
1277 edittext.extend([_("HG: added %s") % f for f in added])
1266 edittext.extend([_("HG: changed %s") % f for f in modified])
1278 edittext.extend([_("HG: changed %s") % f for f in modified])
1267 edittext.extend([_("HG: removed %s") % f for f in removed])
1279 edittext.extend([_("HG: removed %s") % f for f in removed])
1268 if not added and not modified and not removed:
1280 if not added and not modified and not removed:
1269 edittext.append(_("HG: no files changed"))
1281 edittext.append(_("HG: no files changed"))
1270 edittext.append("")
1282 edittext.append("")
1271 # run editor in the repository root
1283 # run editor in the repository root
1272 olddir = os.getcwd()
1284 olddir = os.getcwd()
1273 os.chdir(repo.root)
1285 os.chdir(repo.root)
1274 text = repo.ui.edit("\n".join(edittext), ctx.user())
1286 text = repo.ui.edit("\n".join(edittext), ctx.user())
1275 text = re.sub("(?m)^HG:.*\n", "", text)
1287 text = re.sub("(?m)^HG:.*\n", "", text)
1276 os.chdir(olddir)
1288 os.chdir(olddir)
1277
1289
1278 if not text.strip():
1290 if not text.strip():
1279 raise util.Abort(_("empty commit message"))
1291 raise util.Abort(_("empty commit message"))
1280
1292
1281 return text
1293 return text
@@ -1,4506 +1,4507 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for 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 lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw, discovery
13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset
16 import minirst, revset
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 bad = []
49 bad = []
50 names = []
50 names = []
51 m = cmdutil.match(repo, pats, opts)
51 m = cmdutil.match(repo, pats, opts)
52 oldbad = m.bad
52 oldbad = m.bad
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
54
54
55 for f in repo.walk(m):
55 for f in repo.walk(m):
56 exact = m.exact(f)
56 exact = m.exact(f)
57 if exact or f not in repo.dirstate:
57 if exact or f not in repo.dirstate:
58 names.append(f)
58 names.append(f)
59 if ui.verbose or not exact:
59 if ui.verbose or not exact:
60 ui.status(_('adding %s\n') % m.rel(f))
60 ui.status(_('adding %s\n') % m.rel(f))
61 if not opts.get('dry_run'):
61 if not opts.get('dry_run'):
62 bad += [f for f in repo[None].add(names) if f in m.files()]
62 bad += [f for f in repo[None].add(names) if f in m.files()]
63 return bad and 1 or 0
63 return bad and 1 or 0
64
64
65 def addremove(ui, repo, *pats, **opts):
65 def addremove(ui, repo, *pats, **opts):
66 """add all new files, delete all missing files
66 """add all new files, delete all missing files
67
67
68 Add all new files and remove all missing files from the
68 Add all new files and remove all missing files from the
69 repository.
69 repository.
70
70
71 New files are ignored if they match any of the patterns in
71 New files are ignored if they match any of the patterns in
72 .hgignore. As with add, these changes take effect at the next
72 .hgignore. As with add, these changes take effect at the next
73 commit.
73 commit.
74
74
75 Use the -s/--similarity option to detect renamed files. With a
75 Use the -s/--similarity option to detect renamed files. With a
76 parameter greater than 0, this compares every removed file with
76 parameter greater than 0, this compares every removed file with
77 every added file and records those similar enough as renames. This
77 every added file and records those similar enough as renames. This
78 option takes a percentage between 0 (disabled) and 100 (files must
78 option takes a percentage between 0 (disabled) and 100 (files must
79 be identical) as its parameter. Detecting renamed files this way
79 be identical) as its parameter. Detecting renamed files this way
80 can be expensive. After using this option, :hg:`status -C` can be
80 can be expensive. After using this option, :hg:`status -C` can be
81 used to check which files were identified as moved or renamed.
81 used to check which files were identified as moved or renamed.
82
82
83 Returns 0 if all files are successfully added.
83 Returns 0 if all files are successfully added.
84 """
84 """
85 try:
85 try:
86 sim = float(opts.get('similarity') or 100)
86 sim = float(opts.get('similarity') or 100)
87 except ValueError:
87 except ValueError:
88 raise util.Abort(_('similarity must be a number'))
88 raise util.Abort(_('similarity must be a number'))
89 if sim < 0 or sim > 100:
89 if sim < 0 or sim > 100:
90 raise util.Abort(_('similarity must be between 0 and 100'))
90 raise util.Abort(_('similarity must be between 0 and 100'))
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
92
92
93 def annotate(ui, repo, *pats, **opts):
93 def annotate(ui, repo, *pats, **opts):
94 """show changeset information by line for each file
94 """show changeset information by line for each file
95
95
96 List changes in files, showing the revision id responsible for
96 List changes in files, showing the revision id responsible for
97 each line
97 each line
98
98
99 This command is useful for discovering when a change was made and
99 This command is useful for discovering when a change was made and
100 by whom.
100 by whom.
101
101
102 Without the -a/--text option, annotate will avoid processing files
102 Without the -a/--text option, annotate will avoid processing files
103 it detects as binary. With -a, annotate will annotate the file
103 it detects as binary. With -a, annotate will annotate the file
104 anyway, although the results will probably be neither useful
104 anyway, although the results will probably be neither useful
105 nor desirable.
105 nor desirable.
106
106
107 Returns 0 on success.
107 Returns 0 on success.
108 """
108 """
109 if opts.get('follow'):
109 if opts.get('follow'):
110 # --follow is deprecated and now just an alias for -f/--file
110 # --follow is deprecated and now just an alias for -f/--file
111 # to mimic the behavior of Mercurial before version 1.5
111 # to mimic the behavior of Mercurial before version 1.5
112 opts['file'] = 1
112 opts['file'] = 1
113
113
114 datefunc = ui.quiet and util.shortdate or util.datestr
114 datefunc = ui.quiet and util.shortdate or util.datestr
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
116
116
117 if not pats:
117 if not pats:
118 raise util.Abort(_('at least one filename or pattern is required'))
118 raise util.Abort(_('at least one filename or pattern is required'))
119
119
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
121 ('number', lambda x: str(x[0].rev())),
121 ('number', lambda x: str(x[0].rev())),
122 ('changeset', lambda x: short(x[0].node())),
122 ('changeset', lambda x: short(x[0].node())),
123 ('date', getdate),
123 ('date', getdate),
124 ('file', lambda x: x[0].path()),
124 ('file', lambda x: x[0].path()),
125 ]
125 ]
126
126
127 if (not opts.get('user') and not opts.get('changeset')
127 if (not opts.get('user') and not opts.get('changeset')
128 and not opts.get('date') and not opts.get('file')):
128 and not opts.get('date') and not opts.get('file')):
129 opts['number'] = 1
129 opts['number'] = 1
130
130
131 linenumber = opts.get('line_number') is not None
131 linenumber = opts.get('line_number') is not None
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
134
134
135 funcmap = [func for op, func in opmap if opts.get(op)]
135 funcmap = [func for op, func in opmap if opts.get(op)]
136 if linenumber:
136 if linenumber:
137 lastfunc = funcmap[-1]
137 lastfunc = funcmap[-1]
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
139
139
140 ctx = repo[opts.get('rev')]
140 ctx = repo[opts.get('rev')]
141 m = cmdutil.match(repo, pats, opts)
141 m = cmdutil.match(repo, pats, opts)
142 follow = not opts.get('no_follow')
142 follow = not opts.get('no_follow')
143 for abs in ctx.walk(m):
143 for abs in ctx.walk(m):
144 fctx = ctx[abs]
144 fctx = ctx[abs]
145 if not opts.get('text') and util.binary(fctx.data()):
145 if not opts.get('text') and util.binary(fctx.data()):
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
147 continue
147 continue
148
148
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
150 pieces = []
150 pieces = []
151
151
152 for f in funcmap:
152 for f in funcmap:
153 l = [f(n) for n, dummy in lines]
153 l = [f(n) for n, dummy in lines]
154 if l:
154 if l:
155 sized = [(x, encoding.colwidth(x)) for x in l]
155 sized = [(x, encoding.colwidth(x)) for x in l]
156 ml = max([w for x, w in sized])
156 ml = max([w for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
158
158
159 if pieces:
159 if pieces:
160 for p, l in zip(zip(*pieces), lines):
160 for p, l in zip(zip(*pieces), lines):
161 ui.write("%s: %s" % (" ".join(p), l[1]))
161 ui.write("%s: %s" % (" ".join(p), l[1]))
162
162
163 def archive(ui, repo, dest, **opts):
163 def archive(ui, repo, dest, **opts):
164 '''create an unversioned archive of a repository revision
164 '''create an unversioned archive of a repository revision
165
165
166 By default, the revision used is the parent of the working
166 By default, the revision used is the parent of the working
167 directory; use -r/--rev to specify a different revision.
167 directory; use -r/--rev to specify a different revision.
168
168
169 The archive type is automatically detected based on file
169 The archive type is automatically detected based on file
170 extension (or override using -t/--type).
170 extension (or override using -t/--type).
171
171
172 Valid types are:
172 Valid types are:
173
173
174 :``files``: a directory full of files (default)
174 :``files``: a directory full of files (default)
175 :``tar``: tar archive, uncompressed
175 :``tar``: tar archive, uncompressed
176 :``tbz2``: tar archive, compressed using bzip2
176 :``tbz2``: tar archive, compressed using bzip2
177 :``tgz``: tar archive, compressed using gzip
177 :``tgz``: tar archive, compressed using gzip
178 :``uzip``: zip archive, uncompressed
178 :``uzip``: zip archive, uncompressed
179 :``zip``: zip archive, compressed using deflate
179 :``zip``: zip archive, compressed using deflate
180
180
181 The exact name of the destination archive or directory is given
181 The exact name of the destination archive or directory is given
182 using a format string; see :hg:`help export` for details.
182 using a format string; see :hg:`help export` for details.
183
183
184 Each member added to an archive file has a directory prefix
184 Each member added to an archive file has a directory prefix
185 prepended. Use -p/--prefix to specify a format string for the
185 prepended. Use -p/--prefix to specify a format string for the
186 prefix. The default is the basename of the archive, with suffixes
186 prefix. The default is the basename of the archive, with suffixes
187 removed.
187 removed.
188
188
189 Returns 0 on success.
189 Returns 0 on success.
190 '''
190 '''
191
191
192 ctx = repo[opts.get('rev')]
192 ctx = repo[opts.get('rev')]
193 if not ctx:
193 if not ctx:
194 raise util.Abort(_('no working directory: please specify a revision'))
194 raise util.Abort(_('no working directory: please specify a revision'))
195 node = ctx.node()
195 node = ctx.node()
196 dest = cmdutil.make_filename(repo, dest, node)
196 dest = cmdutil.make_filename(repo, dest, node)
197 if os.path.realpath(dest) == repo.root:
197 if os.path.realpath(dest) == repo.root:
198 raise util.Abort(_('repository root cannot be destination'))
198 raise util.Abort(_('repository root cannot be destination'))
199
199
200 kind = opts.get('type') or archival.guesskind(dest) or 'files'
200 kind = opts.get('type') or archival.guesskind(dest) or 'files'
201 prefix = opts.get('prefix')
201 prefix = opts.get('prefix')
202
202
203 if dest == '-':
203 if dest == '-':
204 if kind == 'files':
204 if kind == 'files':
205 raise util.Abort(_('cannot archive plain files to stdout'))
205 raise util.Abort(_('cannot archive plain files to stdout'))
206 dest = sys.stdout
206 dest = sys.stdout
207 if not prefix:
207 if not prefix:
208 prefix = os.path.basename(repo.root) + '-%h'
208 prefix = os.path.basename(repo.root) + '-%h'
209
209
210 prefix = cmdutil.make_filename(repo, prefix, node)
210 prefix = cmdutil.make_filename(repo, prefix, node)
211 matchfn = cmdutil.match(repo, [], opts)
211 matchfn = cmdutil.match(repo, [], opts)
212 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
212 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
213 matchfn, prefix)
213 matchfn, prefix)
214
214
215 def backout(ui, repo, node=None, rev=None, **opts):
215 def backout(ui, repo, node=None, rev=None, **opts):
216 '''reverse effect of earlier changeset
216 '''reverse effect of earlier changeset
217
217
218 Commit the backed out changes as a new changeset. The new
218 Commit the backed out changes as a new changeset. The new
219 changeset is a child of the backed out changeset.
219 changeset is a child of the backed out changeset.
220
220
221 If you backout a changeset other than the tip, a new head is
221 If you backout a changeset other than the tip, a new head is
222 created. This head will be the new tip and you should merge this
222 created. This head will be the new tip and you should merge this
223 backout changeset with another head.
223 backout changeset with another head.
224
224
225 The --merge option remembers the parent of the working directory
225 The --merge option remembers the parent of the working directory
226 before starting the backout, then merges the new head with that
226 before starting the backout, then merges the new head with that
227 changeset afterwards. This saves you from doing the merge by hand.
227 changeset afterwards. This saves you from doing the merge by hand.
228 The result of this merge is not committed, as with a normal merge.
228 The result of this merge is not committed, as with a normal merge.
229
229
230 See :hg:`help dates` for a list of formats valid for -d/--date.
230 See :hg:`help dates` for a list of formats valid for -d/--date.
231
231
232 Returns 0 on success.
232 Returns 0 on success.
233 '''
233 '''
234 if rev and node:
234 if rev and node:
235 raise util.Abort(_("please specify just one revision"))
235 raise util.Abort(_("please specify just one revision"))
236
236
237 if not rev:
237 if not rev:
238 rev = node
238 rev = node
239
239
240 if not rev:
240 if not rev:
241 raise util.Abort(_("please specify a revision to backout"))
241 raise util.Abort(_("please specify a revision to backout"))
242
242
243 date = opts.get('date')
243 date = opts.get('date')
244 if date:
244 if date:
245 opts['date'] = util.parsedate(date)
245 opts['date'] = util.parsedate(date)
246
246
247 cmdutil.bail_if_changed(repo)
247 cmdutil.bail_if_changed(repo)
248 node = repo.lookup(rev)
248 node = repo.lookup(rev)
249
249
250 op1, op2 = repo.dirstate.parents()
250 op1, op2 = repo.dirstate.parents()
251 a = repo.changelog.ancestor(op1, node)
251 a = repo.changelog.ancestor(op1, node)
252 if a != node:
252 if a != node:
253 raise util.Abort(_('cannot backout change on a different branch'))
253 raise util.Abort(_('cannot backout change on a different branch'))
254
254
255 p1, p2 = repo.changelog.parents(node)
255 p1, p2 = repo.changelog.parents(node)
256 if p1 == nullid:
256 if p1 == nullid:
257 raise util.Abort(_('cannot backout a change with no parents'))
257 raise util.Abort(_('cannot backout a change with no parents'))
258 if p2 != nullid:
258 if p2 != nullid:
259 if not opts.get('parent'):
259 if not opts.get('parent'):
260 raise util.Abort(_('cannot backout a merge changeset without '
260 raise util.Abort(_('cannot backout a merge changeset without '
261 '--parent'))
261 '--parent'))
262 p = repo.lookup(opts['parent'])
262 p = repo.lookup(opts['parent'])
263 if p not in (p1, p2):
263 if p not in (p1, p2):
264 raise util.Abort(_('%s is not a parent of %s') %
264 raise util.Abort(_('%s is not a parent of %s') %
265 (short(p), short(node)))
265 (short(p), short(node)))
266 parent = p
266 parent = p
267 else:
267 else:
268 if opts.get('parent'):
268 if opts.get('parent'):
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
270 parent = p1
270 parent = p1
271
271
272 # the backout should appear on the same branch
272 # the backout should appear on the same branch
273 branch = repo.dirstate.branch()
273 branch = repo.dirstate.branch()
274 hg.clean(repo, node, show_stats=False)
274 hg.clean(repo, node, show_stats=False)
275 repo.dirstate.setbranch(branch)
275 repo.dirstate.setbranch(branch)
276 revert_opts = opts.copy()
276 revert_opts = opts.copy()
277 revert_opts['date'] = None
277 revert_opts['date'] = None
278 revert_opts['all'] = True
278 revert_opts['all'] = True
279 revert_opts['rev'] = hex(parent)
279 revert_opts['rev'] = hex(parent)
280 revert_opts['no_backup'] = None
280 revert_opts['no_backup'] = None
281 revert(ui, repo, **revert_opts)
281 revert(ui, repo, **revert_opts)
282 commit_opts = opts.copy()
282 commit_opts = opts.copy()
283 commit_opts['addremove'] = False
283 commit_opts['addremove'] = False
284 if not commit_opts['message'] and not commit_opts['logfile']:
284 if not commit_opts['message'] and not commit_opts['logfile']:
285 # we don't translate commit messages
285 # we don't translate commit messages
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
287 commit_opts['force_editor'] = True
287 commit_opts['force_editor'] = True
288 commit(ui, repo, **commit_opts)
288 commit(ui, repo, **commit_opts)
289 def nice(node):
289 def nice(node):
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
291 ui.status(_('changeset %s backs out changeset %s\n') %
291 ui.status(_('changeset %s backs out changeset %s\n') %
292 (nice(repo.changelog.tip()), nice(node)))
292 (nice(repo.changelog.tip()), nice(node)))
293 if op1 != node:
293 if op1 != node:
294 hg.clean(repo, op1, show_stats=False)
294 hg.clean(repo, op1, show_stats=False)
295 if opts.get('merge'):
295 if opts.get('merge'):
296 ui.status(_('merging with changeset %s\n')
296 ui.status(_('merging with changeset %s\n')
297 % nice(repo.changelog.tip()))
297 % nice(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
299 else:
299 else:
300 ui.status(_('the backout changeset is a new head - '
300 ui.status(_('the backout changeset is a new head - '
301 'do not forget to merge\n'))
301 'do not forget to merge\n'))
302 ui.status(_('(use "backout --merge" '
302 ui.status(_('(use "backout --merge" '
303 'if you want to auto-merge)\n'))
303 'if you want to auto-merge)\n'))
304
304
305 def bisect(ui, repo, rev=None, extra=None, command=None,
305 def bisect(ui, repo, rev=None, extra=None, command=None,
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
307 """subdivision search of changesets
307 """subdivision search of changesets
308
308
309 This command helps to find changesets which introduce problems. To
309 This command helps to find changesets which introduce problems. To
310 use, mark the earliest changeset you know exhibits the problem as
310 use, mark the earliest changeset you know exhibits the problem as
311 bad, then mark the latest changeset which is free from the problem
311 bad, then mark the latest changeset which is free from the problem
312 as good. Bisect will update your working directory to a revision
312 as good. Bisect will update your working directory to a revision
313 for testing (unless the -U/--noupdate option is specified). Once
313 for testing (unless the -U/--noupdate option is specified). Once
314 you have performed tests, mark the working directory as good or
314 you have performed tests, mark the working directory as good or
315 bad, and bisect will either update to another candidate changeset
315 bad, and bisect will either update to another candidate changeset
316 or announce that it has found the bad revision.
316 or announce that it has found the bad revision.
317
317
318 As a shortcut, you can also use the revision argument to mark a
318 As a shortcut, you can also use the revision argument to mark a
319 revision as good or bad without checking it out first.
319 revision as good or bad without checking it out first.
320
320
321 If you supply a command, it will be used for automatic bisection.
321 If you supply a command, it will be used for automatic bisection.
322 Its exit status will be used to mark revisions as good or bad:
322 Its exit status will be used to mark revisions as good or bad:
323 status 0 means good, 125 means to skip the revision, 127
323 status 0 means good, 125 means to skip the revision, 127
324 (command not found) will abort the bisection, and any other
324 (command not found) will abort the bisection, and any other
325 non-zero exit status means the revision is bad.
325 non-zero exit status means the revision is bad.
326
326
327 Returns 0 on success.
327 Returns 0 on success.
328 """
328 """
329 def print_result(nodes, good):
329 def print_result(nodes, good):
330 displayer = cmdutil.show_changeset(ui, repo, {})
330 displayer = cmdutil.show_changeset(ui, repo, {})
331 if len(nodes) == 1:
331 if len(nodes) == 1:
332 # narrowed it down to a single revision
332 # narrowed it down to a single revision
333 if good:
333 if good:
334 ui.write(_("The first good revision is:\n"))
334 ui.write(_("The first good revision is:\n"))
335 else:
335 else:
336 ui.write(_("The first bad revision is:\n"))
336 ui.write(_("The first bad revision is:\n"))
337 displayer.show(repo[nodes[0]])
337 displayer.show(repo[nodes[0]])
338 else:
338 else:
339 # multiple possible revisions
339 # multiple possible revisions
340 if good:
340 if good:
341 ui.write(_("Due to skipped revisions, the first "
341 ui.write(_("Due to skipped revisions, the first "
342 "good revision could be any of:\n"))
342 "good revision could be any of:\n"))
343 else:
343 else:
344 ui.write(_("Due to skipped revisions, the first "
344 ui.write(_("Due to skipped revisions, the first "
345 "bad revision could be any of:\n"))
345 "bad revision could be any of:\n"))
346 for n in nodes:
346 for n in nodes:
347 displayer.show(repo[n])
347 displayer.show(repo[n])
348 displayer.close()
348 displayer.close()
349
349
350 def check_state(state, interactive=True):
350 def check_state(state, interactive=True):
351 if not state['good'] or not state['bad']:
351 if not state['good'] or not state['bad']:
352 if (good or bad or skip or reset) and interactive:
352 if (good or bad or skip or reset) and interactive:
353 return
353 return
354 if not state['good']:
354 if not state['good']:
355 raise util.Abort(_('cannot bisect (no known good revisions)'))
355 raise util.Abort(_('cannot bisect (no known good revisions)'))
356 else:
356 else:
357 raise util.Abort(_('cannot bisect (no known bad revisions)'))
357 raise util.Abort(_('cannot bisect (no known bad revisions)'))
358 return True
358 return True
359
359
360 # backward compatibility
360 # backward compatibility
361 if rev in "good bad reset init".split():
361 if rev in "good bad reset init".split():
362 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
362 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
363 cmd, rev, extra = rev, extra, None
363 cmd, rev, extra = rev, extra, None
364 if cmd == "good":
364 if cmd == "good":
365 good = True
365 good = True
366 elif cmd == "bad":
366 elif cmd == "bad":
367 bad = True
367 bad = True
368 else:
368 else:
369 reset = True
369 reset = True
370 elif extra or good + bad + skip + reset + bool(command) > 1:
370 elif extra or good + bad + skip + reset + bool(command) > 1:
371 raise util.Abort(_('incompatible arguments'))
371 raise util.Abort(_('incompatible arguments'))
372
372
373 if reset:
373 if reset:
374 p = repo.join("bisect.state")
374 p = repo.join("bisect.state")
375 if os.path.exists(p):
375 if os.path.exists(p):
376 os.unlink(p)
376 os.unlink(p)
377 return
377 return
378
378
379 state = hbisect.load_state(repo)
379 state = hbisect.load_state(repo)
380
380
381 if command:
381 if command:
382 changesets = 1
382 changesets = 1
383 try:
383 try:
384 while changesets:
384 while changesets:
385 # update state
385 # update state
386 status = util.system(command)
386 status = util.system(command)
387 if status == 125:
387 if status == 125:
388 transition = "skip"
388 transition = "skip"
389 elif status == 0:
389 elif status == 0:
390 transition = "good"
390 transition = "good"
391 # status < 0 means process was killed
391 # status < 0 means process was killed
392 elif status == 127:
392 elif status == 127:
393 raise util.Abort(_("failed to execute %s") % command)
393 raise util.Abort(_("failed to execute %s") % command)
394 elif status < 0:
394 elif status < 0:
395 raise util.Abort(_("%s killed") % command)
395 raise util.Abort(_("%s killed") % command)
396 else:
396 else:
397 transition = "bad"
397 transition = "bad"
398 ctx = repo[rev or '.']
398 ctx = repo[rev or '.']
399 state[transition].append(ctx.node())
399 state[transition].append(ctx.node())
400 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
400 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
401 check_state(state, interactive=False)
401 check_state(state, interactive=False)
402 # bisect
402 # bisect
403 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
403 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
404 # update to next check
404 # update to next check
405 cmdutil.bail_if_changed(repo)
405 cmdutil.bail_if_changed(repo)
406 hg.clean(repo, nodes[0], show_stats=False)
406 hg.clean(repo, nodes[0], show_stats=False)
407 finally:
407 finally:
408 hbisect.save_state(repo, state)
408 hbisect.save_state(repo, state)
409 print_result(nodes, good)
409 print_result(nodes, good)
410 return
410 return
411
411
412 # update state
412 # update state
413 node = repo.lookup(rev or '.')
413 node = repo.lookup(rev or '.')
414 if good or bad or skip:
414 if good or bad or skip:
415 if good:
415 if good:
416 state['good'].append(node)
416 state['good'].append(node)
417 elif bad:
417 elif bad:
418 state['bad'].append(node)
418 state['bad'].append(node)
419 elif skip:
419 elif skip:
420 state['skip'].append(node)
420 state['skip'].append(node)
421 hbisect.save_state(repo, state)
421 hbisect.save_state(repo, state)
422
422
423 if not check_state(state):
423 if not check_state(state):
424 return
424 return
425
425
426 # actually bisect
426 # actually bisect
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
428 if changesets == 0:
428 if changesets == 0:
429 print_result(nodes, good)
429 print_result(nodes, good)
430 else:
430 else:
431 assert len(nodes) == 1 # only a single node can be tested next
431 assert len(nodes) == 1 # only a single node can be tested next
432 node = nodes[0]
432 node = nodes[0]
433 # compute the approximate number of remaining tests
433 # compute the approximate number of remaining tests
434 tests, size = 0, 2
434 tests, size = 0, 2
435 while size <= changesets:
435 while size <= changesets:
436 tests, size = tests + 1, size * 2
436 tests, size = tests + 1, size * 2
437 rev = repo.changelog.rev(node)
437 rev = repo.changelog.rev(node)
438 ui.write(_("Testing changeset %d:%s "
438 ui.write(_("Testing changeset %d:%s "
439 "(%d changesets remaining, ~%d tests)\n")
439 "(%d changesets remaining, ~%d tests)\n")
440 % (rev, short(node), changesets, tests))
440 % (rev, short(node), changesets, tests))
441 if not noupdate:
441 if not noupdate:
442 cmdutil.bail_if_changed(repo)
442 cmdutil.bail_if_changed(repo)
443 return hg.clean(repo, node)
443 return hg.clean(repo, node)
444
444
445 def branch(ui, repo, label=None, **opts):
445 def branch(ui, repo, label=None, **opts):
446 """set or show the current branch name
446 """set or show the current branch name
447
447
448 With no argument, show the current branch name. With one argument,
448 With no argument, show the current branch name. With one argument,
449 set the working directory branch name (the branch will not exist
449 set the working directory branch name (the branch will not exist
450 in the repository until the next commit). Standard practice
450 in the repository until the next commit). Standard practice
451 recommends that primary development take place on the 'default'
451 recommends that primary development take place on the 'default'
452 branch.
452 branch.
453
453
454 Unless -f/--force is specified, branch will not let you set a
454 Unless -f/--force is specified, branch will not let you set a
455 branch name that already exists, even if it's inactive.
455 branch name that already exists, even if it's inactive.
456
456
457 Use -C/--clean to reset the working directory branch to that of
457 Use -C/--clean to reset the working directory branch to that of
458 the parent of the working directory, negating a previous branch
458 the parent of the working directory, negating a previous branch
459 change.
459 change.
460
460
461 Use the command :hg:`update` to switch to an existing branch. Use
461 Use the command :hg:`update` to switch to an existing branch. Use
462 :hg:`commit --close-branch` to mark this branch as closed.
462 :hg:`commit --close-branch` to mark this branch as closed.
463
463
464 Returns 0 on success.
464 Returns 0 on success.
465 """
465 """
466
466
467 if opts.get('clean'):
467 if opts.get('clean'):
468 label = repo[None].parents()[0].branch()
468 label = repo[None].parents()[0].branch()
469 repo.dirstate.setbranch(label)
469 repo.dirstate.setbranch(label)
470 ui.status(_('reset working directory to branch %s\n') % label)
470 ui.status(_('reset working directory to branch %s\n') % label)
471 elif label:
471 elif label:
472 utflabel = encoding.fromlocal(label)
472 utflabel = encoding.fromlocal(label)
473 if not opts.get('force') and utflabel in repo.branchtags():
473 if not opts.get('force') and utflabel in repo.branchtags():
474 if label not in [p.branch() for p in repo.parents()]:
474 if label not in [p.branch() for p in repo.parents()]:
475 raise util.Abort(_('a branch of the same name already exists'
475 raise util.Abort(_('a branch of the same name already exists'
476 " (use 'hg update' to switch to it)"))
476 " (use 'hg update' to switch to it)"))
477 repo.dirstate.setbranch(utflabel)
477 repo.dirstate.setbranch(utflabel)
478 ui.status(_('marked working directory as branch %s\n') % label)
478 ui.status(_('marked working directory as branch %s\n') % label)
479 else:
479 else:
480 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
480 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
481
481
482 def branches(ui, repo, active=False, closed=False):
482 def branches(ui, repo, active=False, closed=False):
483 """list repository named branches
483 """list repository named branches
484
484
485 List the repository's named branches, indicating which ones are
485 List the repository's named branches, indicating which ones are
486 inactive. If -c/--closed is specified, also list branches which have
486 inactive. If -c/--closed is specified, also list branches which have
487 been marked closed (see :hg:`commit --close-branch`).
487 been marked closed (see :hg:`commit --close-branch`).
488
488
489 If -a/--active is specified, only show active branches. A branch
489 If -a/--active is specified, only show active branches. A branch
490 is considered active if it contains repository heads.
490 is considered active if it contains repository heads.
491
491
492 Use the command :hg:`update` to switch to an existing branch.
492 Use the command :hg:`update` to switch to an existing branch.
493
493
494 Returns 0.
494 Returns 0.
495 """
495 """
496
496
497 hexfunc = ui.debugflag and hex or short
497 hexfunc = ui.debugflag and hex or short
498 activebranches = [repo[n].branch() for n in repo.heads()]
498 activebranches = [repo[n].branch() for n in repo.heads()]
499 def testactive(tag, node):
499 def testactive(tag, node):
500 realhead = tag in activebranches
500 realhead = tag in activebranches
501 open = node in repo.branchheads(tag, closed=False)
501 open = node in repo.branchheads(tag, closed=False)
502 return realhead and open
502 return realhead and open
503 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
503 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
504 for tag, node in repo.branchtags().items()],
504 for tag, node in repo.branchtags().items()],
505 reverse=True)
505 reverse=True)
506
506
507 for isactive, node, tag in branches:
507 for isactive, node, tag in branches:
508 if (not active) or isactive:
508 if (not active) or isactive:
509 encodedtag = encoding.tolocal(tag)
509 encodedtag = encoding.tolocal(tag)
510 if ui.quiet:
510 if ui.quiet:
511 ui.write("%s\n" % encodedtag)
511 ui.write("%s\n" % encodedtag)
512 else:
512 else:
513 hn = repo.lookup(node)
513 hn = repo.lookup(node)
514 if isactive:
514 if isactive:
515 label = 'branches.active'
515 label = 'branches.active'
516 notice = ''
516 notice = ''
517 elif hn not in repo.branchheads(tag, closed=False):
517 elif hn not in repo.branchheads(tag, closed=False):
518 if not closed:
518 if not closed:
519 continue
519 continue
520 label = 'branches.closed'
520 label = 'branches.closed'
521 notice = _(' (closed)')
521 notice = _(' (closed)')
522 else:
522 else:
523 label = 'branches.inactive'
523 label = 'branches.inactive'
524 notice = _(' (inactive)')
524 notice = _(' (inactive)')
525 if tag == repo.dirstate.branch():
525 if tag == repo.dirstate.branch():
526 label = 'branches.current'
526 label = 'branches.current'
527 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
527 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
528 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
528 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
529 encodedtag = ui.label(encodedtag, label)
529 encodedtag = ui.label(encodedtag, label)
530 ui.write("%s %s%s\n" % (encodedtag, rev, notice))
530 ui.write("%s %s%s\n" % (encodedtag, rev, notice))
531
531
532 def bundle(ui, repo, fname, dest=None, **opts):
532 def bundle(ui, repo, fname, dest=None, **opts):
533 """create a changegroup file
533 """create a changegroup file
534
534
535 Generate a compressed changegroup file collecting changesets not
535 Generate a compressed changegroup file collecting changesets not
536 known to be in another repository.
536 known to be in another repository.
537
537
538 If you omit the destination repository, then hg assumes the
538 If you omit the destination repository, then hg assumes the
539 destination will have all the nodes you specify with --base
539 destination will have all the nodes you specify with --base
540 parameters. To create a bundle containing all changesets, use
540 parameters. To create a bundle containing all changesets, use
541 -a/--all (or --base null).
541 -a/--all (or --base null).
542
542
543 You can change compression method with the -t/--type option.
543 You can change compression method with the -t/--type option.
544 The available compression methods are: none, bzip2, and
544 The available compression methods are: none, bzip2, and
545 gzip (by default, bundles are compressed using bzip2).
545 gzip (by default, bundles are compressed using bzip2).
546
546
547 The bundle file can then be transferred using conventional means
547 The bundle file can then be transferred using conventional means
548 and applied to another repository with the unbundle or pull
548 and applied to another repository with the unbundle or pull
549 command. This is useful when direct push and pull are not
549 command. This is useful when direct push and pull are not
550 available or when exporting an entire repository is undesirable.
550 available or when exporting an entire repository is undesirable.
551
551
552 Applying bundles preserves all changeset contents including
552 Applying bundles preserves all changeset contents including
553 permissions, copy/rename information, and revision history.
553 permissions, copy/rename information, and revision history.
554
554
555 Returns 0 on success, 1 if no changes found.
555 Returns 0 on success, 1 if no changes found.
556 """
556 """
557 revs = opts.get('rev') or None
557 revs = opts.get('rev') or None
558 if opts.get('all'):
558 if opts.get('all'):
559 base = ['null']
559 base = ['null']
560 else:
560 else:
561 base = opts.get('base')
561 base = opts.get('base')
562 if base:
562 if base:
563 if dest:
563 if dest:
564 raise util.Abort(_("--base is incompatible with specifying "
564 raise util.Abort(_("--base is incompatible with specifying "
565 "a destination"))
565 "a destination"))
566 base = [repo.lookup(rev) for rev in base]
566 base = [repo.lookup(rev) for rev in base]
567 # create the right base
567 # create the right base
568 # XXX: nodesbetween / changegroup* should be "fixed" instead
568 # XXX: nodesbetween / changegroup* should be "fixed" instead
569 o = []
569 o = []
570 has = set((nullid,))
570 has = set((nullid,))
571 for n in base:
571 for n in base:
572 has.update(repo.changelog.reachable(n))
572 has.update(repo.changelog.reachable(n))
573 if revs:
573 if revs:
574 revs = [repo.lookup(rev) for rev in revs]
574 revs = [repo.lookup(rev) for rev in revs]
575 visit = revs[:]
575 visit = revs[:]
576 has.difference_update(visit)
576 has.difference_update(visit)
577 else:
577 else:
578 visit = repo.changelog.heads()
578 visit = repo.changelog.heads()
579 seen = {}
579 seen = {}
580 while visit:
580 while visit:
581 n = visit.pop(0)
581 n = visit.pop(0)
582 parents = [p for p in repo.changelog.parents(n) if p not in has]
582 parents = [p for p in repo.changelog.parents(n) if p not in has]
583 if len(parents) == 0:
583 if len(parents) == 0:
584 if n not in has:
584 if n not in has:
585 o.append(n)
585 o.append(n)
586 else:
586 else:
587 for p in parents:
587 for p in parents:
588 if p not in seen:
588 if p not in seen:
589 seen[p] = 1
589 seen[p] = 1
590 visit.append(p)
590 visit.append(p)
591 else:
591 else:
592 dest = ui.expandpath(dest or 'default-push', dest or 'default')
592 dest = ui.expandpath(dest or 'default-push', dest or 'default')
593 dest, branches = hg.parseurl(dest, opts.get('branch'))
593 dest, branches = hg.parseurl(dest, opts.get('branch'))
594 other = hg.repository(hg.remoteui(repo, opts), dest)
594 other = hg.repository(hg.remoteui(repo, opts), dest)
595 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
595 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
596 if revs:
596 if revs:
597 revs = [repo.lookup(rev) for rev in revs]
597 revs = [repo.lookup(rev) for rev in revs]
598 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
598 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
599
599
600 if not o:
600 if not o:
601 ui.status(_("no changes found\n"))
601 ui.status(_("no changes found\n"))
602 return 1
602 return 1
603
603
604 if revs:
604 if revs:
605 cg = repo.changegroupsubset(o, revs, 'bundle')
605 cg = repo.changegroupsubset(o, revs, 'bundle')
606 else:
606 else:
607 cg = repo.changegroup(o, 'bundle')
607 cg = repo.changegroup(o, 'bundle')
608
608
609 bundletype = opts.get('type', 'bzip2').lower()
609 bundletype = opts.get('type', 'bzip2').lower()
610 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
610 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
611 bundletype = btypes.get(bundletype)
611 bundletype = btypes.get(bundletype)
612 if bundletype not in changegroup.bundletypes:
612 if bundletype not in changegroup.bundletypes:
613 raise util.Abort(_('unknown bundle type specified with --type'))
613 raise util.Abort(_('unknown bundle type specified with --type'))
614
614
615 changegroup.writebundle(cg, fname, bundletype)
615 changegroup.writebundle(cg, fname, bundletype)
616
616
617 def cat(ui, repo, file1, *pats, **opts):
617 def cat(ui, repo, file1, *pats, **opts):
618 """output the current or given revision of files
618 """output the current or given revision of files
619
619
620 Print the specified files as they were at the given revision. If
620 Print the specified files as they were at the given revision. If
621 no revision is given, the parent of the working directory is used,
621 no revision is given, the parent of the working directory is used,
622 or tip if no revision is checked out.
622 or tip if no revision is checked out.
623
623
624 Output may be to a file, in which case the name of the file is
624 Output may be to a file, in which case the name of the file is
625 given using a format string. The formatting rules are the same as
625 given using a format string. The formatting rules are the same as
626 for the export command, with the following additions:
626 for the export command, with the following additions:
627
627
628 :``%s``: basename of file being printed
628 :``%s``: basename of file being printed
629 :``%d``: dirname of file being printed, or '.' if in repository root
629 :``%d``: dirname of file being printed, or '.' if in repository root
630 :``%p``: root-relative path name of file being printed
630 :``%p``: root-relative path name of file being printed
631
631
632 Returns 0 on success.
632 Returns 0 on success.
633 """
633 """
634 ctx = repo[opts.get('rev')]
634 ctx = repo[opts.get('rev')]
635 err = 1
635 err = 1
636 m = cmdutil.match(repo, (file1,) + pats, opts)
636 m = cmdutil.match(repo, (file1,) + pats, opts)
637 for abs in ctx.walk(m):
637 for abs in ctx.walk(m):
638 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
638 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
639 data = ctx[abs].data()
639 data = ctx[abs].data()
640 if opts.get('decode'):
640 if opts.get('decode'):
641 data = repo.wwritedata(abs, data)
641 data = repo.wwritedata(abs, data)
642 fp.write(data)
642 fp.write(data)
643 err = 0
643 err = 0
644 return err
644 return err
645
645
646 def clone(ui, source, dest=None, **opts):
646 def clone(ui, source, dest=None, **opts):
647 """make a copy of an existing repository
647 """make a copy of an existing repository
648
648
649 Create a copy of an existing repository in a new directory.
649 Create a copy of an existing repository in a new directory.
650
650
651 If no destination directory name is specified, it defaults to the
651 If no destination directory name is specified, it defaults to the
652 basename of the source.
652 basename of the source.
653
653
654 The location of the source is added to the new repository's
654 The location of the source is added to the new repository's
655 .hg/hgrc file, as the default to be used for future pulls.
655 .hg/hgrc file, as the default to be used for future pulls.
656
656
657 See :hg:`help urls` for valid source format details.
657 See :hg:`help urls` for valid source format details.
658
658
659 It is possible to specify an ``ssh://`` URL as the destination, but no
659 It is possible to specify an ``ssh://`` URL as the destination, but no
660 .hg/hgrc and working directory will be created on the remote side.
660 .hg/hgrc and working directory will be created on the remote side.
661 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
661 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
662
662
663 A set of changesets (tags, or branch names) to pull may be specified
663 A set of changesets (tags, or branch names) to pull may be specified
664 by listing each changeset (tag, or branch name) with -r/--rev.
664 by listing each changeset (tag, or branch name) with -r/--rev.
665 If -r/--rev is used, the cloned repository will contain only a subset
665 If -r/--rev is used, the cloned repository will contain only a subset
666 of the changesets of the source repository. Only the set of changesets
666 of the changesets of the source repository. Only the set of changesets
667 defined by all -r/--rev options (including all their ancestors)
667 defined by all -r/--rev options (including all their ancestors)
668 will be pulled into the destination repository.
668 will be pulled into the destination repository.
669 No subsequent changesets (including subsequent tags) will be present
669 No subsequent changesets (including subsequent tags) will be present
670 in the destination.
670 in the destination.
671
671
672 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
672 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
673 local source repositories.
673 local source repositories.
674
674
675 For efficiency, hardlinks are used for cloning whenever the source
675 For efficiency, hardlinks are used for cloning whenever the source
676 and destination are on the same filesystem (note this applies only
676 and destination are on the same filesystem (note this applies only
677 to the repository data, not to the working directory). Some
677 to the repository data, not to the working directory). Some
678 filesystems, such as AFS, implement hardlinking incorrectly, but
678 filesystems, such as AFS, implement hardlinking incorrectly, but
679 do not report errors. In these cases, use the --pull option to
679 do not report errors. In these cases, use the --pull option to
680 avoid hardlinking.
680 avoid hardlinking.
681
681
682 In some cases, you can clone repositories and the working directory
682 In some cases, you can clone repositories and the working directory
683 using full hardlinks with ::
683 using full hardlinks with ::
684
684
685 $ cp -al REPO REPOCLONE
685 $ cp -al REPO REPOCLONE
686
686
687 This is the fastest way to clone, but it is not always safe. The
687 This is the fastest way to clone, but it is not always safe. The
688 operation is not atomic (making sure REPO is not modified during
688 operation is not atomic (making sure REPO is not modified during
689 the operation is up to you) and you have to make sure your editor
689 the operation is up to you) and you have to make sure your editor
690 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
690 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
691 this is not compatible with certain extensions that place their
691 this is not compatible with certain extensions that place their
692 metadata under the .hg directory, such as mq.
692 metadata under the .hg directory, such as mq.
693
693
694 Mercurial will update the working directory to the first applicable
694 Mercurial will update the working directory to the first applicable
695 revision from this list:
695 revision from this list:
696
696
697 a) null if -U or the source repository has no changesets
697 a) null if -U or the source repository has no changesets
698 b) if -u . and the source repository is local, the first parent of
698 b) if -u . and the source repository is local, the first parent of
699 the source repository's working directory
699 the source repository's working directory
700 c) the changeset specified with -u (if a branch name, this means the
700 c) the changeset specified with -u (if a branch name, this means the
701 latest head of that branch)
701 latest head of that branch)
702 d) the changeset specified with -r
702 d) the changeset specified with -r
703 e) the tipmost head specified with -b
703 e) the tipmost head specified with -b
704 f) the tipmost head specified with the url#branch source syntax
704 f) the tipmost head specified with the url#branch source syntax
705 g) the tipmost head of the default branch
705 g) the tipmost head of the default branch
706 h) tip
706 h) tip
707
707
708 Returns 0 on success.
708 Returns 0 on success.
709 """
709 """
710 if opts.get('noupdate') and opts.get('updaterev'):
710 if opts.get('noupdate') and opts.get('updaterev'):
711 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
711 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
712
712
713 r = hg.clone(hg.remoteui(ui, opts), source, dest,
713 r = hg.clone(hg.remoteui(ui, opts), source, dest,
714 pull=opts.get('pull'),
714 pull=opts.get('pull'),
715 stream=opts.get('uncompressed'),
715 stream=opts.get('uncompressed'),
716 rev=opts.get('rev'),
716 rev=opts.get('rev'),
717 update=opts.get('updaterev') or not opts.get('noupdate'),
717 update=opts.get('updaterev') or not opts.get('noupdate'),
718 branch=opts.get('branch'))
718 branch=opts.get('branch'))
719
719
720 return r is None
720 return r is None
721
721
722 def commit(ui, repo, *pats, **opts):
722 def commit(ui, repo, *pats, **opts):
723 """commit the specified files or all outstanding changes
723 """commit the specified files or all outstanding changes
724
724
725 Commit changes to the given files into the repository. Unlike a
725 Commit changes to the given files into the repository. Unlike a
726 centralized RCS, this operation is a local operation. See
726 centralized RCS, this operation is a local operation. See
727 :hg:`push` for a way to actively distribute your changes.
727 :hg:`push` for a way to actively distribute your changes.
728
728
729 If a list of files is omitted, all changes reported by :hg:`status`
729 If a list of files is omitted, all changes reported by :hg:`status`
730 will be committed.
730 will be committed.
731
731
732 If you are committing the result of a merge, do not provide any
732 If you are committing the result of a merge, do not provide any
733 filenames or -I/-X filters.
733 filenames or -I/-X filters.
734
734
735 If no commit message is specified, Mercurial starts your
735 If no commit message is specified, Mercurial starts your
736 configured editor where you can enter a message. In case your
736 configured editor where you can enter a message. In case your
737 commit fails, you will find a backup of your message in
737 commit fails, you will find a backup of your message in
738 ``.hg/last-message.txt``.
738 ``.hg/last-message.txt``.
739
739
740 See :hg:`help dates` for a list of formats valid for -d/--date.
740 See :hg:`help dates` for a list of formats valid for -d/--date.
741
741
742 Returns 0 on success, 1 if nothing changed.
742 Returns 0 on success, 1 if nothing changed.
743 """
743 """
744 extra = {}
744 extra = {}
745 if opts.get('close_branch'):
745 if opts.get('close_branch'):
746 if repo['.'].node() not in repo.branchheads():
746 if repo['.'].node() not in repo.branchheads():
747 # The topo heads set is included in the branch heads set of the
747 # The topo heads set is included in the branch heads set of the
748 # current branch, so it's sufficient to test branchheads
748 # current branch, so it's sufficient to test branchheads
749 raise util.Abort(_('can only close branch heads'))
749 raise util.Abort(_('can only close branch heads'))
750 extra['close'] = 1
750 extra['close'] = 1
751 e = cmdutil.commiteditor
751 e = cmdutil.commiteditor
752 if opts.get('force_editor'):
752 if opts.get('force_editor'):
753 e = cmdutil.commitforceeditor
753 e = cmdutil.commitforceeditor
754
754
755 def commitfunc(ui, repo, message, match, opts):
755 def commitfunc(ui, repo, message, match, opts):
756 return repo.commit(message, opts.get('user'), opts.get('date'), match,
756 return repo.commit(message, opts.get('user'), opts.get('date'), match,
757 editor=e, extra=extra)
757 editor=e, extra=extra)
758
758
759 branch = repo[None].branch()
759 branch = repo[None].branch()
760 bheads = repo.branchheads(branch)
760 bheads = repo.branchheads(branch)
761
761
762 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
762 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
763 if not node:
763 if not node:
764 ui.status(_("nothing changed\n"))
764 ui.status(_("nothing changed\n"))
765 return 1
765 return 1
766
766
767 ctx = repo[node]
767 ctx = repo[node]
768 parents = ctx.parents()
768 parents = ctx.parents()
769
769
770 if bheads and not [x for x in parents
770 if bheads and not [x for x in parents
771 if x.node() in bheads and x.branch() == branch]:
771 if x.node() in bheads and x.branch() == branch]:
772 ui.status(_('created new head\n'))
772 ui.status(_('created new head\n'))
773 # The message is not printed for initial roots. For the other
773 # The message is not printed for initial roots. For the other
774 # changesets, it is printed in the following situations:
774 # changesets, it is printed in the following situations:
775 #
775 #
776 # Par column: for the 2 parents with ...
776 # Par column: for the 2 parents with ...
777 # N: null or no parent
777 # N: null or no parent
778 # B: parent is on another named branch
778 # B: parent is on another named branch
779 # C: parent is a regular non head changeset
779 # C: parent is a regular non head changeset
780 # H: parent was a branch head of the current branch
780 # H: parent was a branch head of the current branch
781 # Msg column: whether we print "created new head" message
781 # Msg column: whether we print "created new head" message
782 # In the following, it is assumed that there already exists some
782 # In the following, it is assumed that there already exists some
783 # initial branch heads of the current branch, otherwise nothing is
783 # initial branch heads of the current branch, otherwise nothing is
784 # printed anyway.
784 # printed anyway.
785 #
785 #
786 # Par Msg Comment
786 # Par Msg Comment
787 # NN y additional topo root
787 # NN y additional topo root
788 #
788 #
789 # BN y additional branch root
789 # BN y additional branch root
790 # CN y additional topo head
790 # CN y additional topo head
791 # HN n usual case
791 # HN n usual case
792 #
792 #
793 # BB y weird additional branch root
793 # BB y weird additional branch root
794 # CB y branch merge
794 # CB y branch merge
795 # HB n merge with named branch
795 # HB n merge with named branch
796 #
796 #
797 # CC y additional head from merge
797 # CC y additional head from merge
798 # CH n merge with a head
798 # CH n merge with a head
799 #
799 #
800 # HH n head merge: head count decreases
800 # HH n head merge: head count decreases
801
801
802 if not opts.get('close_branch'):
802 if not opts.get('close_branch'):
803 for r in parents:
803 for r in parents:
804 if r.extra().get('close') and r.branch() == branch:
804 if r.extra().get('close') and r.branch() == branch:
805 ui.status(_('reopening closed branch head %d\n') % r)
805 ui.status(_('reopening closed branch head %d\n') % r)
806
806
807 if ui.debugflag:
807 if ui.debugflag:
808 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
808 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
809 elif ui.verbose:
809 elif ui.verbose:
810 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
810 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
811
811
812 def copy(ui, repo, *pats, **opts):
812 def copy(ui, repo, *pats, **opts):
813 """mark files as copied for the next commit
813 """mark files as copied for the next commit
814
814
815 Mark dest as having copies of source files. If dest is a
815 Mark dest as having copies of source files. If dest is a
816 directory, copies are put in that directory. If dest is a file,
816 directory, copies are put in that directory. If dest is a file,
817 the source must be a single file.
817 the source must be a single file.
818
818
819 By default, this command copies the contents of files as they
819 By default, this command copies the contents of files as they
820 exist in the working directory. If invoked with -A/--after, the
820 exist in the working directory. If invoked with -A/--after, the
821 operation is recorded, but no copying is performed.
821 operation is recorded, but no copying is performed.
822
822
823 This command takes effect with the next commit. To undo a copy
823 This command takes effect with the next commit. To undo a copy
824 before that, see :hg:`revert`.
824 before that, see :hg:`revert`.
825
825
826 Returns 0 on success, 1 if errors are encountered.
826 Returns 0 on success, 1 if errors are encountered.
827 """
827 """
828 wlock = repo.wlock(False)
828 wlock = repo.wlock(False)
829 try:
829 try:
830 return cmdutil.copy(ui, repo, pats, opts)
830 return cmdutil.copy(ui, repo, pats, opts)
831 finally:
831 finally:
832 wlock.release()
832 wlock.release()
833
833
834 def debugancestor(ui, repo, *args):
834 def debugancestor(ui, repo, *args):
835 """find the ancestor revision of two revisions in a given index"""
835 """find the ancestor revision of two revisions in a given index"""
836 if len(args) == 3:
836 if len(args) == 3:
837 index, rev1, rev2 = args
837 index, rev1, rev2 = args
838 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
838 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
839 lookup = r.lookup
839 lookup = r.lookup
840 elif len(args) == 2:
840 elif len(args) == 2:
841 if not repo:
841 if not repo:
842 raise util.Abort(_("there is no Mercurial repository here "
842 raise util.Abort(_("there is no Mercurial repository here "
843 "(.hg not found)"))
843 "(.hg not found)"))
844 rev1, rev2 = args
844 rev1, rev2 = args
845 r = repo.changelog
845 r = repo.changelog
846 lookup = repo.lookup
846 lookup = repo.lookup
847 else:
847 else:
848 raise util.Abort(_('either two or three arguments required'))
848 raise util.Abort(_('either two or three arguments required'))
849 a = r.ancestor(lookup(rev1), lookup(rev2))
849 a = r.ancestor(lookup(rev1), lookup(rev2))
850 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
850 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
851
851
852 def debugbuilddag(ui, repo, text,
852 def debugbuilddag(ui, repo, text,
853 mergeable_file=False,
853 mergeable_file=False,
854 appended_file=False,
854 appended_file=False,
855 overwritten_file=False,
855 overwritten_file=False,
856 new_file=False):
856 new_file=False):
857 """builds a repo with a given dag from scratch in the current empty repo
857 """builds a repo with a given dag from scratch in the current empty repo
858
858
859 Elements:
859 Elements:
860
860
861 - "+n" is a linear run of n nodes based on the current default parent
861 - "+n" is a linear run of n nodes based on the current default parent
862 - "." is a single node based on the current default parent
862 - "." is a single node based on the current default parent
863 - "$" resets the default parent to null (implied at the start);
863 - "$" resets the default parent to null (implied at the start);
864 otherwise the default parent is always the last node created
864 otherwise the default parent is always the last node created
865 - "<p" sets the default parent to the backref p
865 - "<p" sets the default parent to the backref p
866 - "*p" is a fork at parent p, which is a backref
866 - "*p" is a fork at parent p, which is a backref
867 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
867 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
868 - "/p2" is a merge of the preceding node and p2
868 - "/p2" is a merge of the preceding node and p2
869 - ":tag" defines a local tag for the preceding node
869 - ":tag" defines a local tag for the preceding node
870 - "@branch" sets the named branch for subsequent nodes
870 - "@branch" sets the named branch for subsequent nodes
871 - "!command" runs the command using your shell
871 - "!command" runs the command using your shell
872 - "!!my command\\n" is like "!", but to the end of the line
872 - "!!my command\\n" is like "!", but to the end of the line
873 - "#...\\n" is a comment up to the end of the line
873 - "#...\\n" is a comment up to the end of the line
874
874
875 Whitespace between the above elements is ignored.
875 Whitespace between the above elements is ignored.
876
876
877 A backref is either
877 A backref is either
878
878
879 - a number n, which references the node curr-n, where curr is the current
879 - a number n, which references the node curr-n, where curr is the current
880 node, or
880 node, or
881 - the name of a local tag you placed earlier using ":tag", or
881 - the name of a local tag you placed earlier using ":tag", or
882 - empty to denote the default parent.
882 - empty to denote the default parent.
883
883
884 All string valued-elements are either strictly alphanumeric, or must
884 All string valued-elements are either strictly alphanumeric, or must
885 be enclosed in double quotes ("..."), with "\\" as escape character.
885 be enclosed in double quotes ("..."), with "\\" as escape character.
886
886
887 Note that the --overwritten-file and --appended-file options imply the
887 Note that the --overwritten-file and --appended-file options imply the
888 use of "HGMERGE=internal:local" during DAG buildup.
888 use of "HGMERGE=internal:local" during DAG buildup.
889 """
889 """
890
890
891 if not (mergeable_file or appended_file or overwritten_file or new_file):
891 if not (mergeable_file or appended_file or overwritten_file or new_file):
892 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
892 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
893
893
894 if len(repo.changelog) > 0:
894 if len(repo.changelog) > 0:
895 raise util.Abort(_('repository is not empty'))
895 raise util.Abort(_('repository is not empty'))
896
896
897 if overwritten_file or appended_file:
897 if overwritten_file or appended_file:
898 # we don't want to fail in merges during buildup
898 # we don't want to fail in merges during buildup
899 os.environ['HGMERGE'] = 'internal:local'
899 os.environ['HGMERGE'] = 'internal:local'
900
900
901 def writefile(fname, text, fmode="wb"):
901 def writefile(fname, text, fmode="wb"):
902 f = open(fname, fmode)
902 f = open(fname, fmode)
903 try:
903 try:
904 f.write(text)
904 f.write(text)
905 finally:
905 finally:
906 f.close()
906 f.close()
907
907
908 if mergeable_file:
908 if mergeable_file:
909 linesperrev = 2
909 linesperrev = 2
910 # determine number of revs in DAG
910 # determine number of revs in DAG
911 n = 0
911 n = 0
912 for type, data in dagparser.parsedag(text):
912 for type, data in dagparser.parsedag(text):
913 if type == 'n':
913 if type == 'n':
914 n += 1
914 n += 1
915 # make a file with k lines per rev
915 # make a file with k lines per rev
916 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
916 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
917 + "\n")
917 + "\n")
918
918
919 at = -1
919 at = -1
920 atbranch = 'default'
920 atbranch = 'default'
921 for type, data in dagparser.parsedag(text):
921 for type, data in dagparser.parsedag(text):
922 if type == 'n':
922 if type == 'n':
923 ui.status('node %s\n' % str(data))
923 ui.status('node %s\n' % str(data))
924 id, ps = data
924 id, ps = data
925 p1 = ps[0]
925 p1 = ps[0]
926 if p1 != at:
926 if p1 != at:
927 update(ui, repo, node=p1, clean=True)
927 update(ui, repo, node=p1, clean=True)
928 at = p1
928 at = p1
929 if repo.dirstate.branch() != atbranch:
929 if repo.dirstate.branch() != atbranch:
930 branch(ui, repo, atbranch, force=True)
930 branch(ui, repo, atbranch, force=True)
931 if len(ps) > 1:
931 if len(ps) > 1:
932 p2 = ps[1]
932 p2 = ps[1]
933 merge(ui, repo, node=p2)
933 merge(ui, repo, node=p2)
934
934
935 if mergeable_file:
935 if mergeable_file:
936 f = open("mf", "rb+")
936 f = open("mf", "rb+")
937 try:
937 try:
938 lines = f.read().split("\n")
938 lines = f.read().split("\n")
939 lines[id * linesperrev] += " r%i" % id
939 lines[id * linesperrev] += " r%i" % id
940 f.seek(0)
940 f.seek(0)
941 f.write("\n".join(lines))
941 f.write("\n".join(lines))
942 finally:
942 finally:
943 f.close()
943 f.close()
944
944
945 if appended_file:
945 if appended_file:
946 writefile("af", "r%i\n" % id, "ab")
946 writefile("af", "r%i\n" % id, "ab")
947
947
948 if overwritten_file:
948 if overwritten_file:
949 writefile("of", "r%i\n" % id)
949 writefile("of", "r%i\n" % id)
950
950
951 if new_file:
951 if new_file:
952 writefile("nf%i" % id, "r%i\n" % id)
952 writefile("nf%i" % id, "r%i\n" % id)
953
953
954 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
954 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
955 at = id
955 at = id
956 elif type == 'l':
956 elif type == 'l':
957 id, name = data
957 id, name = data
958 ui.status('tag %s\n' % name)
958 ui.status('tag %s\n' % name)
959 tag(ui, repo, name, local=True)
959 tag(ui, repo, name, local=True)
960 elif type == 'a':
960 elif type == 'a':
961 ui.status('branch %s\n' % data)
961 ui.status('branch %s\n' % data)
962 atbranch = data
962 atbranch = data
963 elif type in 'cC':
963 elif type in 'cC':
964 r = util.system(data, cwd=repo.root)
964 r = util.system(data, cwd=repo.root)
965 if r:
965 if r:
966 desc, r = util.explain_exit(r)
966 desc, r = util.explain_exit(r)
967 raise util.Abort(_('%s command %s') % (data, desc))
967 raise util.Abort(_('%s command %s') % (data, desc))
968
968
969 def debugcommands(ui, cmd='', *args):
969 def debugcommands(ui, cmd='', *args):
970 """list all available commands and options"""
970 """list all available commands and options"""
971 for cmd, vals in sorted(table.iteritems()):
971 for cmd, vals in sorted(table.iteritems()):
972 cmd = cmd.split('|')[0].strip('^')
972 cmd = cmd.split('|')[0].strip('^')
973 opts = ', '.join([i[1] for i in vals[1]])
973 opts = ', '.join([i[1] for i in vals[1]])
974 ui.write('%s: %s\n' % (cmd, opts))
974 ui.write('%s: %s\n' % (cmd, opts))
975
975
976 def debugcomplete(ui, cmd='', **opts):
976 def debugcomplete(ui, cmd='', **opts):
977 """returns the completion list associated with the given command"""
977 """returns the completion list associated with the given command"""
978
978
979 if opts.get('options'):
979 if opts.get('options'):
980 options = []
980 options = []
981 otables = [globalopts]
981 otables = [globalopts]
982 if cmd:
982 if cmd:
983 aliases, entry = cmdutil.findcmd(cmd, table, False)
983 aliases, entry = cmdutil.findcmd(cmd, table, False)
984 otables.append(entry[1])
984 otables.append(entry[1])
985 for t in otables:
985 for t in otables:
986 for o in t:
986 for o in t:
987 if "(DEPRECATED)" in o[3]:
987 if "(DEPRECATED)" in o[3]:
988 continue
988 continue
989 if o[0]:
989 if o[0]:
990 options.append('-%s' % o[0])
990 options.append('-%s' % o[0])
991 options.append('--%s' % o[1])
991 options.append('--%s' % o[1])
992 ui.write("%s\n" % "\n".join(options))
992 ui.write("%s\n" % "\n".join(options))
993 return
993 return
994
994
995 cmdlist = cmdutil.findpossible(cmd, table)
995 cmdlist = cmdutil.findpossible(cmd, table)
996 if ui.verbose:
996 if ui.verbose:
997 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
997 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
998 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
998 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
999
999
1000 def debugfsinfo(ui, path = "."):
1000 def debugfsinfo(ui, path = "."):
1001 """show information detected about current filesystem"""
1001 """show information detected about current filesystem"""
1002 open('.debugfsinfo', 'w').write('')
1002 open('.debugfsinfo', 'w').write('')
1003 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1003 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1004 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1004 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1005 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1005 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1006 and 'yes' or 'no'))
1006 and 'yes' or 'no'))
1007 os.unlink('.debugfsinfo')
1007 os.unlink('.debugfsinfo')
1008
1008
1009 def debugrebuildstate(ui, repo, rev="tip"):
1009 def debugrebuildstate(ui, repo, rev="tip"):
1010 """rebuild the dirstate as it would look like for the given revision"""
1010 """rebuild the dirstate as it would look like for the given revision"""
1011 ctx = repo[rev]
1011 ctx = repo[rev]
1012 wlock = repo.wlock()
1012 wlock = repo.wlock()
1013 try:
1013 try:
1014 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1014 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1015 finally:
1015 finally:
1016 wlock.release()
1016 wlock.release()
1017
1017
1018 def debugcheckstate(ui, repo):
1018 def debugcheckstate(ui, repo):
1019 """validate the correctness of the current dirstate"""
1019 """validate the correctness of the current dirstate"""
1020 parent1, parent2 = repo.dirstate.parents()
1020 parent1, parent2 = repo.dirstate.parents()
1021 m1 = repo[parent1].manifest()
1021 m1 = repo[parent1].manifest()
1022 m2 = repo[parent2].manifest()
1022 m2 = repo[parent2].manifest()
1023 errors = 0
1023 errors = 0
1024 for f in repo.dirstate:
1024 for f in repo.dirstate:
1025 state = repo.dirstate[f]
1025 state = repo.dirstate[f]
1026 if state in "nr" and f not in m1:
1026 if state in "nr" and f not in m1:
1027 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1027 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1028 errors += 1
1028 errors += 1
1029 if state in "a" and f in m1:
1029 if state in "a" and f in m1:
1030 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1030 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1031 errors += 1
1031 errors += 1
1032 if state in "m" and f not in m1 and f not in m2:
1032 if state in "m" and f not in m1 and f not in m2:
1033 ui.warn(_("%s in state %s, but not in either manifest\n") %
1033 ui.warn(_("%s in state %s, but not in either manifest\n") %
1034 (f, state))
1034 (f, state))
1035 errors += 1
1035 errors += 1
1036 for f in m1:
1036 for f in m1:
1037 state = repo.dirstate[f]
1037 state = repo.dirstate[f]
1038 if state not in "nrm":
1038 if state not in "nrm":
1039 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1039 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1040 errors += 1
1040 errors += 1
1041 if errors:
1041 if errors:
1042 error = _(".hg/dirstate inconsistent with current parent's manifest")
1042 error = _(".hg/dirstate inconsistent with current parent's manifest")
1043 raise util.Abort(error)
1043 raise util.Abort(error)
1044
1044
1045 def showconfig(ui, repo, *values, **opts):
1045 def showconfig(ui, repo, *values, **opts):
1046 """show combined config settings from all hgrc files
1046 """show combined config settings from all hgrc files
1047
1047
1048 With no arguments, print names and values of all config items.
1048 With no arguments, print names and values of all config items.
1049
1049
1050 With one argument of the form section.name, print just the value
1050 With one argument of the form section.name, print just the value
1051 of that config item.
1051 of that config item.
1052
1052
1053 With multiple arguments, print names and values of all config
1053 With multiple arguments, print names and values of all config
1054 items with matching section names.
1054 items with matching section names.
1055
1055
1056 With --debug, the source (filename and line number) is printed
1056 With --debug, the source (filename and line number) is printed
1057 for each config item.
1057 for each config item.
1058
1058
1059 Returns 0 on success.
1059 Returns 0 on success.
1060 """
1060 """
1061
1061
1062 for f in util.rcpath():
1062 for f in util.rcpath():
1063 ui.debug(_('read config from: %s\n') % f)
1063 ui.debug(_('read config from: %s\n') % f)
1064 untrusted = bool(opts.get('untrusted'))
1064 untrusted = bool(opts.get('untrusted'))
1065 if values:
1065 if values:
1066 if len([v for v in values if '.' in v]) > 1:
1066 if len([v for v in values if '.' in v]) > 1:
1067 raise util.Abort(_('only one config item permitted'))
1067 raise util.Abort(_('only one config item permitted'))
1068 for section, name, value in ui.walkconfig(untrusted=untrusted):
1068 for section, name, value in ui.walkconfig(untrusted=untrusted):
1069 sectname = section + '.' + name
1069 sectname = section + '.' + name
1070 if values:
1070 if values:
1071 for v in values:
1071 for v in values:
1072 if v == section:
1072 if v == section:
1073 ui.debug('%s: ' %
1073 ui.debug('%s: ' %
1074 ui.configsource(section, name, untrusted))
1074 ui.configsource(section, name, untrusted))
1075 ui.write('%s=%s\n' % (sectname, value))
1075 ui.write('%s=%s\n' % (sectname, value))
1076 elif v == sectname:
1076 elif v == sectname:
1077 ui.debug('%s: ' %
1077 ui.debug('%s: ' %
1078 ui.configsource(section, name, untrusted))
1078 ui.configsource(section, name, untrusted))
1079 ui.write(value, '\n')
1079 ui.write(value, '\n')
1080 else:
1080 else:
1081 ui.debug('%s: ' %
1081 ui.debug('%s: ' %
1082 ui.configsource(section, name, untrusted))
1082 ui.configsource(section, name, untrusted))
1083 ui.write('%s=%s\n' % (sectname, value))
1083 ui.write('%s=%s\n' % (sectname, value))
1084
1084
1085 def debugpushkey(ui, repopath, namespace, *keyinfo):
1085 def debugpushkey(ui, repopath, namespace, *keyinfo):
1086 '''access the pushkey key/value protocol
1086 '''access the pushkey key/value protocol
1087
1087
1088 With two args, list the keys in the given namespace.
1088 With two args, list the keys in the given namespace.
1089
1089
1090 With five args, set a key to new if it currently is set to old.
1090 With five args, set a key to new if it currently is set to old.
1091 Reports success or failure.
1091 Reports success or failure.
1092 '''
1092 '''
1093
1093
1094 target = hg.repository(ui, repopath)
1094 target = hg.repository(ui, repopath)
1095 if keyinfo:
1095 if keyinfo:
1096 key, old, new = keyinfo
1096 key, old, new = keyinfo
1097 r = target.pushkey(namespace, key, old, new)
1097 r = target.pushkey(namespace, key, old, new)
1098 ui.status(str(r) + '\n')
1098 ui.status(str(r) + '\n')
1099 return not(r)
1099 return not(r)
1100 else:
1100 else:
1101 for k, v in target.listkeys(namespace).iteritems():
1101 for k, v in target.listkeys(namespace).iteritems():
1102 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1102 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1103 v.encode('string-escape')))
1103 v.encode('string-escape')))
1104
1104
1105 def debugrevspec(ui, repo, expr):
1105 def debugrevspec(ui, repo, expr):
1106 '''parse and apply a revision specification'''
1106 '''parse and apply a revision specification'''
1107 if ui.verbose:
1107 if ui.verbose:
1108 tree = revset.parse(expr)
1108 tree = revset.parse(expr)
1109 ui.note(tree, "\n")
1109 ui.note(tree, "\n")
1110 func = revset.match(expr)
1110 func = revset.match(expr)
1111 for c in func(repo, range(len(repo))):
1111 for c in func(repo, range(len(repo))):
1112 ui.write("%s\n" % c)
1112 ui.write("%s\n" % c)
1113
1113
1114 def debugsetparents(ui, repo, rev1, rev2=None):
1114 def debugsetparents(ui, repo, rev1, rev2=None):
1115 """manually set the parents of the current working directory
1115 """manually set the parents of the current working directory
1116
1116
1117 This is useful for writing repository conversion tools, but should
1117 This is useful for writing repository conversion tools, but should
1118 be used with care.
1118 be used with care.
1119
1119
1120 Returns 0 on success.
1120 Returns 0 on success.
1121 """
1121 """
1122
1122
1123 if not rev2:
1123 if not rev2:
1124 rev2 = hex(nullid)
1124 rev2 = hex(nullid)
1125
1125
1126 wlock = repo.wlock()
1126 wlock = repo.wlock()
1127 try:
1127 try:
1128 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1128 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1129 finally:
1129 finally:
1130 wlock.release()
1130 wlock.release()
1131
1131
1132 def debugstate(ui, repo, nodates=None):
1132 def debugstate(ui, repo, nodates=None):
1133 """show the contents of the current dirstate"""
1133 """show the contents of the current dirstate"""
1134 timestr = ""
1134 timestr = ""
1135 showdate = not nodates
1135 showdate = not nodates
1136 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1136 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1137 if showdate:
1137 if showdate:
1138 if ent[3] == -1:
1138 if ent[3] == -1:
1139 # Pad or slice to locale representation
1139 # Pad or slice to locale representation
1140 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1140 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1141 time.localtime(0)))
1141 time.localtime(0)))
1142 timestr = 'unset'
1142 timestr = 'unset'
1143 timestr = (timestr[:locale_len] +
1143 timestr = (timestr[:locale_len] +
1144 ' ' * (locale_len - len(timestr)))
1144 ' ' * (locale_len - len(timestr)))
1145 else:
1145 else:
1146 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1146 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1147 time.localtime(ent[3]))
1147 time.localtime(ent[3]))
1148 if ent[1] & 020000:
1148 if ent[1] & 020000:
1149 mode = 'lnk'
1149 mode = 'lnk'
1150 else:
1150 else:
1151 mode = '%3o' % (ent[1] & 0777)
1151 mode = '%3o' % (ent[1] & 0777)
1152 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1152 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1153 for f in repo.dirstate.copies():
1153 for f in repo.dirstate.copies():
1154 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1154 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1155
1155
1156 def debugsub(ui, repo, rev=None):
1156 def debugsub(ui, repo, rev=None):
1157 if rev == '':
1157 if rev == '':
1158 rev = None
1158 rev = None
1159 for k, v in sorted(repo[rev].substate.items()):
1159 for k, v in sorted(repo[rev].substate.items()):
1160 ui.write('path %s\n' % k)
1160 ui.write('path %s\n' % k)
1161 ui.write(' source %s\n' % v[0])
1161 ui.write(' source %s\n' % v[0])
1162 ui.write(' revision %s\n' % v[1])
1162 ui.write(' revision %s\n' % v[1])
1163
1163
1164 def debugdag(ui, repo, file_=None, *revs, **opts):
1164 def debugdag(ui, repo, file_=None, *revs, **opts):
1165 """format the changelog or an index DAG as a concise textual description
1165 """format the changelog or an index DAG as a concise textual description
1166
1166
1167 If you pass a revlog index, the revlog's DAG is emitted. If you list
1167 If you pass a revlog index, the revlog's DAG is emitted. If you list
1168 revision numbers, they get labelled in the output as rN.
1168 revision numbers, they get labelled in the output as rN.
1169
1169
1170 Otherwise, the changelog DAG of the current repo is emitted.
1170 Otherwise, the changelog DAG of the current repo is emitted.
1171 """
1171 """
1172 spaces = opts.get('spaces')
1172 spaces = opts.get('spaces')
1173 dots = opts.get('dots')
1173 dots = opts.get('dots')
1174 if file_:
1174 if file_:
1175 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1175 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1176 revs = set((int(r) for r in revs))
1176 revs = set((int(r) for r in revs))
1177 def events():
1177 def events():
1178 for r in rlog:
1178 for r in rlog:
1179 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1179 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1180 if r in revs:
1180 if r in revs:
1181 yield 'l', (r, "r%i" % r)
1181 yield 'l', (r, "r%i" % r)
1182 elif repo:
1182 elif repo:
1183 cl = repo.changelog
1183 cl = repo.changelog
1184 tags = opts.get('tags')
1184 tags = opts.get('tags')
1185 branches = opts.get('branches')
1185 branches = opts.get('branches')
1186 if tags:
1186 if tags:
1187 labels = {}
1187 labels = {}
1188 for l, n in repo.tags().items():
1188 for l, n in repo.tags().items():
1189 labels.setdefault(cl.rev(n), []).append(l)
1189 labels.setdefault(cl.rev(n), []).append(l)
1190 def events():
1190 def events():
1191 b = "default"
1191 b = "default"
1192 for r in cl:
1192 for r in cl:
1193 if branches:
1193 if branches:
1194 newb = cl.read(cl.node(r))[5]['branch']
1194 newb = cl.read(cl.node(r))[5]['branch']
1195 if newb != b:
1195 if newb != b:
1196 yield 'a', newb
1196 yield 'a', newb
1197 b = newb
1197 b = newb
1198 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1198 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1199 if tags:
1199 if tags:
1200 ls = labels.get(r)
1200 ls = labels.get(r)
1201 if ls:
1201 if ls:
1202 for l in ls:
1202 for l in ls:
1203 yield 'l', (r, l)
1203 yield 'l', (r, l)
1204 else:
1204 else:
1205 raise util.Abort(_('need repo for changelog dag'))
1205 raise util.Abort(_('need repo for changelog dag'))
1206
1206
1207 for line in dagparser.dagtextlines(events(),
1207 for line in dagparser.dagtextlines(events(),
1208 addspaces=spaces,
1208 addspaces=spaces,
1209 wraplabels=True,
1209 wraplabels=True,
1210 wrapannotations=True,
1210 wrapannotations=True,
1211 wrapnonlinear=dots,
1211 wrapnonlinear=dots,
1212 usedots=dots,
1212 usedots=dots,
1213 maxlinewidth=70):
1213 maxlinewidth=70):
1214 ui.write(line)
1214 ui.write(line)
1215 ui.write("\n")
1215 ui.write("\n")
1216
1216
1217 def debugdata(ui, repo, file_, rev):
1217 def debugdata(ui, repo, file_, rev):
1218 """dump the contents of a data file revision"""
1218 """dump the contents of a data file revision"""
1219 r = None
1219 r = None
1220 if repo:
1220 if repo:
1221 filelog = repo.file(file_)
1221 filelog = repo.file(file_)
1222 if len(filelog):
1222 if len(filelog):
1223 r = filelog
1223 r = filelog
1224 if not r:
1224 if not r:
1225 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1225 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1226 try:
1226 try:
1227 ui.write(r.revision(r.lookup(rev)))
1227 ui.write(r.revision(r.lookup(rev)))
1228 except KeyError:
1228 except KeyError:
1229 raise util.Abort(_('invalid revision identifier %s') % rev)
1229 raise util.Abort(_('invalid revision identifier %s') % rev)
1230
1230
1231 def debugdate(ui, date, range=None, **opts):
1231 def debugdate(ui, date, range=None, **opts):
1232 """parse and display a date"""
1232 """parse and display a date"""
1233 if opts["extended"]:
1233 if opts["extended"]:
1234 d = util.parsedate(date, util.extendeddateformats)
1234 d = util.parsedate(date, util.extendeddateformats)
1235 else:
1235 else:
1236 d = util.parsedate(date)
1236 d = util.parsedate(date)
1237 ui.write("internal: %s %s\n" % d)
1237 ui.write("internal: %s %s\n" % d)
1238 ui.write("standard: %s\n" % util.datestr(d))
1238 ui.write("standard: %s\n" % util.datestr(d))
1239 if range:
1239 if range:
1240 m = util.matchdate(range)
1240 m = util.matchdate(range)
1241 ui.write("match: %s\n" % m(d[0]))
1241 ui.write("match: %s\n" % m(d[0]))
1242
1242
1243 def debugindex(ui, repo, file_):
1243 def debugindex(ui, repo, file_):
1244 """dump the contents of an index file"""
1244 """dump the contents of an index file"""
1245 r = None
1245 r = None
1246 if repo:
1246 if repo:
1247 filelog = repo.file(file_)
1247 filelog = repo.file(file_)
1248 if len(filelog):
1248 if len(filelog):
1249 r = filelog
1249 r = filelog
1250 if not r:
1250 if not r:
1251 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1251 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1252 ui.write(" rev offset length base linkrev"
1252 ui.write(" rev offset length base linkrev"
1253 " nodeid p1 p2\n")
1253 " nodeid p1 p2\n")
1254 for i in r:
1254 for i in r:
1255 node = r.node(i)
1255 node = r.node(i)
1256 try:
1256 try:
1257 pp = r.parents(node)
1257 pp = r.parents(node)
1258 except:
1258 except:
1259 pp = [nullid, nullid]
1259 pp = [nullid, nullid]
1260 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1260 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1261 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1261 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1262 short(node), short(pp[0]), short(pp[1])))
1262 short(node), short(pp[0]), short(pp[1])))
1263
1263
1264 def debugindexdot(ui, repo, file_):
1264 def debugindexdot(ui, repo, file_):
1265 """dump an index DAG as a graphviz dot file"""
1265 """dump an index DAG as a graphviz dot file"""
1266 r = None
1266 r = None
1267 if repo:
1267 if repo:
1268 filelog = repo.file(file_)
1268 filelog = repo.file(file_)
1269 if len(filelog):
1269 if len(filelog):
1270 r = filelog
1270 r = filelog
1271 if not r:
1271 if not r:
1272 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1272 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1273 ui.write("digraph G {\n")
1273 ui.write("digraph G {\n")
1274 for i in r:
1274 for i in r:
1275 node = r.node(i)
1275 node = r.node(i)
1276 pp = r.parents(node)
1276 pp = r.parents(node)
1277 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1277 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1278 if pp[1] != nullid:
1278 if pp[1] != nullid:
1279 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1279 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1280 ui.write("}\n")
1280 ui.write("}\n")
1281
1281
1282 def debuginstall(ui):
1282 def debuginstall(ui):
1283 '''test Mercurial installation
1283 '''test Mercurial installation
1284
1284
1285 Returns 0 on success.
1285 Returns 0 on success.
1286 '''
1286 '''
1287
1287
1288 def writetemp(contents):
1288 def writetemp(contents):
1289 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1289 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1290 f = os.fdopen(fd, "wb")
1290 f = os.fdopen(fd, "wb")
1291 f.write(contents)
1291 f.write(contents)
1292 f.close()
1292 f.close()
1293 return name
1293 return name
1294
1294
1295 problems = 0
1295 problems = 0
1296
1296
1297 # encoding
1297 # encoding
1298 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1298 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1299 try:
1299 try:
1300 encoding.fromlocal("test")
1300 encoding.fromlocal("test")
1301 except util.Abort, inst:
1301 except util.Abort, inst:
1302 ui.write(" %s\n" % inst)
1302 ui.write(" %s\n" % inst)
1303 ui.write(_(" (check that your locale is properly set)\n"))
1303 ui.write(_(" (check that your locale is properly set)\n"))
1304 problems += 1
1304 problems += 1
1305
1305
1306 # compiled modules
1306 # compiled modules
1307 ui.status(_("Checking installed modules (%s)...\n")
1307 ui.status(_("Checking installed modules (%s)...\n")
1308 % os.path.dirname(__file__))
1308 % os.path.dirname(__file__))
1309 try:
1309 try:
1310 import bdiff, mpatch, base85, osutil
1310 import bdiff, mpatch, base85, osutil
1311 except Exception, inst:
1311 except Exception, inst:
1312 ui.write(" %s\n" % inst)
1312 ui.write(" %s\n" % inst)
1313 ui.write(_(" One or more extensions could not be found"))
1313 ui.write(_(" One or more extensions could not be found"))
1314 ui.write(_(" (check that you compiled the extensions)\n"))
1314 ui.write(_(" (check that you compiled the extensions)\n"))
1315 problems += 1
1315 problems += 1
1316
1316
1317 # templates
1317 # templates
1318 ui.status(_("Checking templates...\n"))
1318 ui.status(_("Checking templates...\n"))
1319 try:
1319 try:
1320 import templater
1320 import templater
1321 templater.templater(templater.templatepath("map-cmdline.default"))
1321 templater.templater(templater.templatepath("map-cmdline.default"))
1322 except Exception, inst:
1322 except Exception, inst:
1323 ui.write(" %s\n" % inst)
1323 ui.write(" %s\n" % inst)
1324 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1324 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1325 problems += 1
1325 problems += 1
1326
1326
1327 # patch
1327 # patch
1328 ui.status(_("Checking patch...\n"))
1328 ui.status(_("Checking patch...\n"))
1329 patchproblems = 0
1329 patchproblems = 0
1330 a = "1\n2\n3\n4\n"
1330 a = "1\n2\n3\n4\n"
1331 b = "1\n2\n3\ninsert\n4\n"
1331 b = "1\n2\n3\ninsert\n4\n"
1332 fa = writetemp(a)
1332 fa = writetemp(a)
1333 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1333 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1334 os.path.basename(fa))
1334 os.path.basename(fa))
1335 fd = writetemp(d)
1335 fd = writetemp(d)
1336
1336
1337 files = {}
1337 files = {}
1338 try:
1338 try:
1339 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1339 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1340 except util.Abort, e:
1340 except util.Abort, e:
1341 ui.write(_(" patch call failed:\n"))
1341 ui.write(_(" patch call failed:\n"))
1342 ui.write(" " + str(e) + "\n")
1342 ui.write(" " + str(e) + "\n")
1343 patchproblems += 1
1343 patchproblems += 1
1344 else:
1344 else:
1345 if list(files) != [os.path.basename(fa)]:
1345 if list(files) != [os.path.basename(fa)]:
1346 ui.write(_(" unexpected patch output!\n"))
1346 ui.write(_(" unexpected patch output!\n"))
1347 patchproblems += 1
1347 patchproblems += 1
1348 a = open(fa).read()
1348 a = open(fa).read()
1349 if a != b:
1349 if a != b:
1350 ui.write(_(" patch test failed!\n"))
1350 ui.write(_(" patch test failed!\n"))
1351 patchproblems += 1
1351 patchproblems += 1
1352
1352
1353 if patchproblems:
1353 if patchproblems:
1354 if ui.config('ui', 'patch'):
1354 if ui.config('ui', 'patch'):
1355 ui.write(_(" (Current patch tool may be incompatible with patch,"
1355 ui.write(_(" (Current patch tool may be incompatible with patch,"
1356 " or misconfigured. Please check your configuration"
1356 " or misconfigured. Please check your configuration"
1357 " file)\n"))
1357 " file)\n"))
1358 else:
1358 else:
1359 ui.write(_(" Internal patcher failure, please report this error"
1359 ui.write(_(" Internal patcher failure, please report this error"
1360 " to http://mercurial.selenic.com/bts/\n"))
1360 " to http://mercurial.selenic.com/bts/\n"))
1361 problems += patchproblems
1361 problems += patchproblems
1362
1362
1363 os.unlink(fa)
1363 os.unlink(fa)
1364 os.unlink(fd)
1364 os.unlink(fd)
1365
1365
1366 # editor
1366 # editor
1367 ui.status(_("Checking commit editor...\n"))
1367 ui.status(_("Checking commit editor...\n"))
1368 editor = ui.geteditor()
1368 editor = ui.geteditor()
1369 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1369 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1370 if not cmdpath:
1370 if not cmdpath:
1371 if editor == 'vi':
1371 if editor == 'vi':
1372 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1372 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1373 ui.write(_(" (specify a commit editor in your configuration"
1373 ui.write(_(" (specify a commit editor in your configuration"
1374 " file)\n"))
1374 " file)\n"))
1375 else:
1375 else:
1376 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1376 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1377 ui.write(_(" (specify a commit editor in your configuration"
1377 ui.write(_(" (specify a commit editor in your configuration"
1378 " file)\n"))
1378 " file)\n"))
1379 problems += 1
1379 problems += 1
1380
1380
1381 # check username
1381 # check username
1382 ui.status(_("Checking username...\n"))
1382 ui.status(_("Checking username...\n"))
1383 try:
1383 try:
1384 ui.username()
1384 ui.username()
1385 except util.Abort, e:
1385 except util.Abort, e:
1386 ui.write(" %s\n" % e)
1386 ui.write(" %s\n" % e)
1387 ui.write(_(" (specify a username in your configuration file)\n"))
1387 ui.write(_(" (specify a username in your configuration file)\n"))
1388 problems += 1
1388 problems += 1
1389
1389
1390 if not problems:
1390 if not problems:
1391 ui.status(_("No problems detected\n"))
1391 ui.status(_("No problems detected\n"))
1392 else:
1392 else:
1393 ui.write(_("%s problems detected,"
1393 ui.write(_("%s problems detected,"
1394 " please check your install!\n") % problems)
1394 " please check your install!\n") % problems)
1395
1395
1396 return problems
1396 return problems
1397
1397
1398 def debugrename(ui, repo, file1, *pats, **opts):
1398 def debugrename(ui, repo, file1, *pats, **opts):
1399 """dump rename information"""
1399 """dump rename information"""
1400
1400
1401 ctx = repo[opts.get('rev')]
1401 ctx = repo[opts.get('rev')]
1402 m = cmdutil.match(repo, (file1,) + pats, opts)
1402 m = cmdutil.match(repo, (file1,) + pats, opts)
1403 for abs in ctx.walk(m):
1403 for abs in ctx.walk(m):
1404 fctx = ctx[abs]
1404 fctx = ctx[abs]
1405 o = fctx.filelog().renamed(fctx.filenode())
1405 o = fctx.filelog().renamed(fctx.filenode())
1406 rel = m.rel(abs)
1406 rel = m.rel(abs)
1407 if o:
1407 if o:
1408 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1408 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1409 else:
1409 else:
1410 ui.write(_("%s not renamed\n") % rel)
1410 ui.write(_("%s not renamed\n") % rel)
1411
1411
1412 def debugwalk(ui, repo, *pats, **opts):
1412 def debugwalk(ui, repo, *pats, **opts):
1413 """show how files match on given patterns"""
1413 """show how files match on given patterns"""
1414 m = cmdutil.match(repo, pats, opts)
1414 m = cmdutil.match(repo, pats, opts)
1415 items = list(repo.walk(m))
1415 items = list(repo.walk(m))
1416 if not items:
1416 if not items:
1417 return
1417 return
1418 fmt = 'f %%-%ds %%-%ds %%s' % (
1418 fmt = 'f %%-%ds %%-%ds %%s' % (
1419 max([len(abs) for abs in items]),
1419 max([len(abs) for abs in items]),
1420 max([len(m.rel(abs)) for abs in items]))
1420 max([len(m.rel(abs)) for abs in items]))
1421 for abs in items:
1421 for abs in items:
1422 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1422 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1423 ui.write("%s\n" % line.rstrip())
1423 ui.write("%s\n" % line.rstrip())
1424
1424
1425 def diff(ui, repo, *pats, **opts):
1425 def diff(ui, repo, *pats, **opts):
1426 """diff repository (or selected files)
1426 """diff repository (or selected files)
1427
1427
1428 Show differences between revisions for the specified files.
1428 Show differences between revisions for the specified files.
1429
1429
1430 Differences between files are shown using the unified diff format.
1430 Differences between files are shown using the unified diff format.
1431
1431
1432 NOTE: diff may generate unexpected results for merges, as it will
1432 NOTE: diff may generate unexpected results for merges, as it will
1433 default to comparing against the working directory's first parent
1433 default to comparing against the working directory's first parent
1434 changeset if no revisions are specified.
1434 changeset if no revisions are specified.
1435
1435
1436 When two revision arguments are given, then changes are shown
1436 When two revision arguments are given, then changes are shown
1437 between those revisions. If only one revision is specified then
1437 between those revisions. If only one revision is specified then
1438 that revision is compared to the working directory, and, when no
1438 that revision is compared to the working directory, and, when no
1439 revisions are specified, the working directory files are compared
1439 revisions are specified, the working directory files are compared
1440 to its parent.
1440 to its parent.
1441
1441
1442 Alternatively you can specify -c/--change with a revision to see
1442 Alternatively you can specify -c/--change with a revision to see
1443 the changes in that changeset relative to its first parent.
1443 the changes in that changeset relative to its first parent.
1444
1444
1445 Without the -a/--text option, diff will avoid generating diffs of
1445 Without the -a/--text option, diff will avoid generating diffs of
1446 files it detects as binary. With -a, diff will generate a diff
1446 files it detects as binary. With -a, diff will generate a diff
1447 anyway, probably with undesirable results.
1447 anyway, probably with undesirable results.
1448
1448
1449 Use the -g/--git option to generate diffs in the git extended diff
1449 Use the -g/--git option to generate diffs in the git extended diff
1450 format. For more information, read :hg:`help diffs`.
1450 format. For more information, read :hg:`help diffs`.
1451
1451
1452 Returns 0 on success.
1452 Returns 0 on success.
1453 """
1453 """
1454
1454
1455 revs = opts.get('rev')
1455 revs = opts.get('rev')
1456 change = opts.get('change')
1456 change = opts.get('change')
1457 stat = opts.get('stat')
1457 stat = opts.get('stat')
1458 reverse = opts.get('reverse')
1458 reverse = opts.get('reverse')
1459
1459
1460 if revs and change:
1460 if revs and change:
1461 msg = _('cannot specify --rev and --change at the same time')
1461 msg = _('cannot specify --rev and --change at the same time')
1462 raise util.Abort(msg)
1462 raise util.Abort(msg)
1463 elif change:
1463 elif change:
1464 node2 = repo.lookup(change)
1464 node2 = repo.lookup(change)
1465 node1 = repo[node2].parents()[0].node()
1465 node1 = repo[node2].parents()[0].node()
1466 else:
1466 else:
1467 node1, node2 = cmdutil.revpair(repo, revs)
1467 node1, node2 = cmdutil.revpair(repo, revs)
1468
1468
1469 if reverse:
1469 if reverse:
1470 node1, node2 = node2, node1
1470 node1, node2 = node2, node1
1471
1471
1472 diffopts = patch.diffopts(ui, opts)
1472 diffopts = patch.diffopts(ui, opts)
1473 m = cmdutil.match(repo, pats, opts)
1473 m = cmdutil.match(repo, pats, opts)
1474 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1474 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1475 listsubrepos=opts.get('subrepos'))
1475
1476
1476 def export(ui, repo, *changesets, **opts):
1477 def export(ui, repo, *changesets, **opts):
1477 """dump the header and diffs for one or more changesets
1478 """dump the header and diffs for one or more changesets
1478
1479
1479 Print the changeset header and diffs for one or more revisions.
1480 Print the changeset header and diffs for one or more revisions.
1480
1481
1481 The information shown in the changeset header is: author, date,
1482 The information shown in the changeset header is: author, date,
1482 branch name (if non-default), changeset hash, parent(s) and commit
1483 branch name (if non-default), changeset hash, parent(s) and commit
1483 comment.
1484 comment.
1484
1485
1485 NOTE: export may generate unexpected diff output for merge
1486 NOTE: export may generate unexpected diff output for merge
1486 changesets, as it will compare the merge changeset against its
1487 changesets, as it will compare the merge changeset against its
1487 first parent only.
1488 first parent only.
1488
1489
1489 Output may be to a file, in which case the name of the file is
1490 Output may be to a file, in which case the name of the file is
1490 given using a format string. The formatting rules are as follows:
1491 given using a format string. The formatting rules are as follows:
1491
1492
1492 :``%%``: literal "%" character
1493 :``%%``: literal "%" character
1493 :``%H``: changeset hash (40 hexadecimal digits)
1494 :``%H``: changeset hash (40 hexadecimal digits)
1494 :``%N``: number of patches being generated
1495 :``%N``: number of patches being generated
1495 :``%R``: changeset revision number
1496 :``%R``: changeset revision number
1496 :``%b``: basename of the exporting repository
1497 :``%b``: basename of the exporting repository
1497 :``%h``: short-form changeset hash (12 hexadecimal digits)
1498 :``%h``: short-form changeset hash (12 hexadecimal digits)
1498 :``%n``: zero-padded sequence number, starting at 1
1499 :``%n``: zero-padded sequence number, starting at 1
1499 :``%r``: zero-padded changeset revision number
1500 :``%r``: zero-padded changeset revision number
1500
1501
1501 Without the -a/--text option, export will avoid generating diffs
1502 Without the -a/--text option, export will avoid generating diffs
1502 of files it detects as binary. With -a, export will generate a
1503 of files it detects as binary. With -a, export will generate a
1503 diff anyway, probably with undesirable results.
1504 diff anyway, probably with undesirable results.
1504
1505
1505 Use the -g/--git option to generate diffs in the git extended diff
1506 Use the -g/--git option to generate diffs in the git extended diff
1506 format. See :hg:`help diffs` for more information.
1507 format. See :hg:`help diffs` for more information.
1507
1508
1508 With the --switch-parent option, the diff will be against the
1509 With the --switch-parent option, the diff will be against the
1509 second parent. It can be useful to review a merge.
1510 second parent. It can be useful to review a merge.
1510
1511
1511 Returns 0 on success.
1512 Returns 0 on success.
1512 """
1513 """
1513 changesets += tuple(opts.get('rev', []))
1514 changesets += tuple(opts.get('rev', []))
1514 if not changesets:
1515 if not changesets:
1515 raise util.Abort(_("export requires at least one changeset"))
1516 raise util.Abort(_("export requires at least one changeset"))
1516 revs = cmdutil.revrange(repo, changesets)
1517 revs = cmdutil.revrange(repo, changesets)
1517 if len(revs) > 1:
1518 if len(revs) > 1:
1518 ui.note(_('exporting patches:\n'))
1519 ui.note(_('exporting patches:\n'))
1519 else:
1520 else:
1520 ui.note(_('exporting patch:\n'))
1521 ui.note(_('exporting patch:\n'))
1521 cmdutil.export(repo, revs, template=opts.get('output'),
1522 cmdutil.export(repo, revs, template=opts.get('output'),
1522 switch_parent=opts.get('switch_parent'),
1523 switch_parent=opts.get('switch_parent'),
1523 opts=patch.diffopts(ui, opts))
1524 opts=patch.diffopts(ui, opts))
1524
1525
1525 def forget(ui, repo, *pats, **opts):
1526 def forget(ui, repo, *pats, **opts):
1526 """forget the specified files on the next commit
1527 """forget the specified files on the next commit
1527
1528
1528 Mark the specified files so they will no longer be tracked
1529 Mark the specified files so they will no longer be tracked
1529 after the next commit.
1530 after the next commit.
1530
1531
1531 This only removes files from the current branch, not from the
1532 This only removes files from the current branch, not from the
1532 entire project history, and it does not delete them from the
1533 entire project history, and it does not delete them from the
1533 working directory.
1534 working directory.
1534
1535
1535 To undo a forget before the next commit, see :hg:`add`.
1536 To undo a forget before the next commit, see :hg:`add`.
1536
1537
1537 Returns 0 on success.
1538 Returns 0 on success.
1538 """
1539 """
1539
1540
1540 if not pats:
1541 if not pats:
1541 raise util.Abort(_('no files specified'))
1542 raise util.Abort(_('no files specified'))
1542
1543
1543 m = cmdutil.match(repo, pats, opts)
1544 m = cmdutil.match(repo, pats, opts)
1544 s = repo.status(match=m, clean=True)
1545 s = repo.status(match=m, clean=True)
1545 forget = sorted(s[0] + s[1] + s[3] + s[6])
1546 forget = sorted(s[0] + s[1] + s[3] + s[6])
1546 errs = 0
1547 errs = 0
1547
1548
1548 for f in m.files():
1549 for f in m.files():
1549 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1550 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1550 ui.warn(_('not removing %s: file is already untracked\n')
1551 ui.warn(_('not removing %s: file is already untracked\n')
1551 % m.rel(f))
1552 % m.rel(f))
1552 errs = 1
1553 errs = 1
1553
1554
1554 for f in forget:
1555 for f in forget:
1555 if ui.verbose or not m.exact(f):
1556 if ui.verbose or not m.exact(f):
1556 ui.status(_('removing %s\n') % m.rel(f))
1557 ui.status(_('removing %s\n') % m.rel(f))
1557
1558
1558 repo[None].remove(forget, unlink=False)
1559 repo[None].remove(forget, unlink=False)
1559 return errs
1560 return errs
1560
1561
1561 def grep(ui, repo, pattern, *pats, **opts):
1562 def grep(ui, repo, pattern, *pats, **opts):
1562 """search for a pattern in specified files and revisions
1563 """search for a pattern in specified files and revisions
1563
1564
1564 Search revisions of files for a regular expression.
1565 Search revisions of files for a regular expression.
1565
1566
1566 This command behaves differently than Unix grep. It only accepts
1567 This command behaves differently than Unix grep. It only accepts
1567 Python/Perl regexps. It searches repository history, not the
1568 Python/Perl regexps. It searches repository history, not the
1568 working directory. It always prints the revision number in which a
1569 working directory. It always prints the revision number in which a
1569 match appears.
1570 match appears.
1570
1571
1571 By default, grep only prints output for the first revision of a
1572 By default, grep only prints output for the first revision of a
1572 file in which it finds a match. To get it to print every revision
1573 file in which it finds a match. To get it to print every revision
1573 that contains a change in match status ("-" for a match that
1574 that contains a change in match status ("-" for a match that
1574 becomes a non-match, or "+" for a non-match that becomes a match),
1575 becomes a non-match, or "+" for a non-match that becomes a match),
1575 use the --all flag.
1576 use the --all flag.
1576
1577
1577 Returns 0 if a match is found, 1 otherwise.
1578 Returns 0 if a match is found, 1 otherwise.
1578 """
1579 """
1579 reflags = 0
1580 reflags = 0
1580 if opts.get('ignore_case'):
1581 if opts.get('ignore_case'):
1581 reflags |= re.I
1582 reflags |= re.I
1582 try:
1583 try:
1583 regexp = re.compile(pattern, reflags)
1584 regexp = re.compile(pattern, reflags)
1584 except Exception, inst:
1585 except Exception, inst:
1585 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1586 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1586 return 1
1587 return 1
1587 sep, eol = ':', '\n'
1588 sep, eol = ':', '\n'
1588 if opts.get('print0'):
1589 if opts.get('print0'):
1589 sep = eol = '\0'
1590 sep = eol = '\0'
1590
1591
1591 getfile = util.lrucachefunc(repo.file)
1592 getfile = util.lrucachefunc(repo.file)
1592
1593
1593 def matchlines(body):
1594 def matchlines(body):
1594 begin = 0
1595 begin = 0
1595 linenum = 0
1596 linenum = 0
1596 while True:
1597 while True:
1597 match = regexp.search(body, begin)
1598 match = regexp.search(body, begin)
1598 if not match:
1599 if not match:
1599 break
1600 break
1600 mstart, mend = match.span()
1601 mstart, mend = match.span()
1601 linenum += body.count('\n', begin, mstart) + 1
1602 linenum += body.count('\n', begin, mstart) + 1
1602 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1603 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1603 begin = body.find('\n', mend) + 1 or len(body)
1604 begin = body.find('\n', mend) + 1 or len(body)
1604 lend = begin - 1
1605 lend = begin - 1
1605 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1606 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1606
1607
1607 class linestate(object):
1608 class linestate(object):
1608 def __init__(self, line, linenum, colstart, colend):
1609 def __init__(self, line, linenum, colstart, colend):
1609 self.line = line
1610 self.line = line
1610 self.linenum = linenum
1611 self.linenum = linenum
1611 self.colstart = colstart
1612 self.colstart = colstart
1612 self.colend = colend
1613 self.colend = colend
1613
1614
1614 def __hash__(self):
1615 def __hash__(self):
1615 return hash((self.linenum, self.line))
1616 return hash((self.linenum, self.line))
1616
1617
1617 def __eq__(self, other):
1618 def __eq__(self, other):
1618 return self.line == other.line
1619 return self.line == other.line
1619
1620
1620 matches = {}
1621 matches = {}
1621 copies = {}
1622 copies = {}
1622 def grepbody(fn, rev, body):
1623 def grepbody(fn, rev, body):
1623 matches[rev].setdefault(fn, [])
1624 matches[rev].setdefault(fn, [])
1624 m = matches[rev][fn]
1625 m = matches[rev][fn]
1625 for lnum, cstart, cend, line in matchlines(body):
1626 for lnum, cstart, cend, line in matchlines(body):
1626 s = linestate(line, lnum, cstart, cend)
1627 s = linestate(line, lnum, cstart, cend)
1627 m.append(s)
1628 m.append(s)
1628
1629
1629 def difflinestates(a, b):
1630 def difflinestates(a, b):
1630 sm = difflib.SequenceMatcher(None, a, b)
1631 sm = difflib.SequenceMatcher(None, a, b)
1631 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1632 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1632 if tag == 'insert':
1633 if tag == 'insert':
1633 for i in xrange(blo, bhi):
1634 for i in xrange(blo, bhi):
1634 yield ('+', b[i])
1635 yield ('+', b[i])
1635 elif tag == 'delete':
1636 elif tag == 'delete':
1636 for i in xrange(alo, ahi):
1637 for i in xrange(alo, ahi):
1637 yield ('-', a[i])
1638 yield ('-', a[i])
1638 elif tag == 'replace':
1639 elif tag == 'replace':
1639 for i in xrange(alo, ahi):
1640 for i in xrange(alo, ahi):
1640 yield ('-', a[i])
1641 yield ('-', a[i])
1641 for i in xrange(blo, bhi):
1642 for i in xrange(blo, bhi):
1642 yield ('+', b[i])
1643 yield ('+', b[i])
1643
1644
1644 def display(fn, ctx, pstates, states):
1645 def display(fn, ctx, pstates, states):
1645 rev = ctx.rev()
1646 rev = ctx.rev()
1646 datefunc = ui.quiet and util.shortdate or util.datestr
1647 datefunc = ui.quiet and util.shortdate or util.datestr
1647 found = False
1648 found = False
1648 filerevmatches = {}
1649 filerevmatches = {}
1649 if opts.get('all'):
1650 if opts.get('all'):
1650 iter = difflinestates(pstates, states)
1651 iter = difflinestates(pstates, states)
1651 else:
1652 else:
1652 iter = [('', l) for l in states]
1653 iter = [('', l) for l in states]
1653 for change, l in iter:
1654 for change, l in iter:
1654 cols = [fn, str(rev)]
1655 cols = [fn, str(rev)]
1655 before, match, after = None, None, None
1656 before, match, after = None, None, None
1656 if opts.get('line_number'):
1657 if opts.get('line_number'):
1657 cols.append(str(l.linenum))
1658 cols.append(str(l.linenum))
1658 if opts.get('all'):
1659 if opts.get('all'):
1659 cols.append(change)
1660 cols.append(change)
1660 if opts.get('user'):
1661 if opts.get('user'):
1661 cols.append(ui.shortuser(ctx.user()))
1662 cols.append(ui.shortuser(ctx.user()))
1662 if opts.get('date'):
1663 if opts.get('date'):
1663 cols.append(datefunc(ctx.date()))
1664 cols.append(datefunc(ctx.date()))
1664 if opts.get('files_with_matches'):
1665 if opts.get('files_with_matches'):
1665 c = (fn, rev)
1666 c = (fn, rev)
1666 if c in filerevmatches:
1667 if c in filerevmatches:
1667 continue
1668 continue
1668 filerevmatches[c] = 1
1669 filerevmatches[c] = 1
1669 else:
1670 else:
1670 before = l.line[:l.colstart]
1671 before = l.line[:l.colstart]
1671 match = l.line[l.colstart:l.colend]
1672 match = l.line[l.colstart:l.colend]
1672 after = l.line[l.colend:]
1673 after = l.line[l.colend:]
1673 ui.write(sep.join(cols))
1674 ui.write(sep.join(cols))
1674 if before is not None:
1675 if before is not None:
1675 ui.write(sep + before)
1676 ui.write(sep + before)
1676 ui.write(match, label='grep.match')
1677 ui.write(match, label='grep.match')
1677 ui.write(after)
1678 ui.write(after)
1678 ui.write(eol)
1679 ui.write(eol)
1679 found = True
1680 found = True
1680 return found
1681 return found
1681
1682
1682 skip = {}
1683 skip = {}
1683 revfiles = {}
1684 revfiles = {}
1684 matchfn = cmdutil.match(repo, pats, opts)
1685 matchfn = cmdutil.match(repo, pats, opts)
1685 found = False
1686 found = False
1686 follow = opts.get('follow')
1687 follow = opts.get('follow')
1687
1688
1688 def prep(ctx, fns):
1689 def prep(ctx, fns):
1689 rev = ctx.rev()
1690 rev = ctx.rev()
1690 pctx = ctx.parents()[0]
1691 pctx = ctx.parents()[0]
1691 parent = pctx.rev()
1692 parent = pctx.rev()
1692 matches.setdefault(rev, {})
1693 matches.setdefault(rev, {})
1693 matches.setdefault(parent, {})
1694 matches.setdefault(parent, {})
1694 files = revfiles.setdefault(rev, [])
1695 files = revfiles.setdefault(rev, [])
1695 for fn in fns:
1696 for fn in fns:
1696 flog = getfile(fn)
1697 flog = getfile(fn)
1697 try:
1698 try:
1698 fnode = ctx.filenode(fn)
1699 fnode = ctx.filenode(fn)
1699 except error.LookupError:
1700 except error.LookupError:
1700 continue
1701 continue
1701
1702
1702 copied = flog.renamed(fnode)
1703 copied = flog.renamed(fnode)
1703 copy = follow and copied and copied[0]
1704 copy = follow and copied and copied[0]
1704 if copy:
1705 if copy:
1705 copies.setdefault(rev, {})[fn] = copy
1706 copies.setdefault(rev, {})[fn] = copy
1706 if fn in skip:
1707 if fn in skip:
1707 if copy:
1708 if copy:
1708 skip[copy] = True
1709 skip[copy] = True
1709 continue
1710 continue
1710 files.append(fn)
1711 files.append(fn)
1711
1712
1712 if fn not in matches[rev]:
1713 if fn not in matches[rev]:
1713 grepbody(fn, rev, flog.read(fnode))
1714 grepbody(fn, rev, flog.read(fnode))
1714
1715
1715 pfn = copy or fn
1716 pfn = copy or fn
1716 if pfn not in matches[parent]:
1717 if pfn not in matches[parent]:
1717 try:
1718 try:
1718 fnode = pctx.filenode(pfn)
1719 fnode = pctx.filenode(pfn)
1719 grepbody(pfn, parent, flog.read(fnode))
1720 grepbody(pfn, parent, flog.read(fnode))
1720 except error.LookupError:
1721 except error.LookupError:
1721 pass
1722 pass
1722
1723
1723 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1724 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1724 rev = ctx.rev()
1725 rev = ctx.rev()
1725 parent = ctx.parents()[0].rev()
1726 parent = ctx.parents()[0].rev()
1726 for fn in sorted(revfiles.get(rev, [])):
1727 for fn in sorted(revfiles.get(rev, [])):
1727 states = matches[rev][fn]
1728 states = matches[rev][fn]
1728 copy = copies.get(rev, {}).get(fn)
1729 copy = copies.get(rev, {}).get(fn)
1729 if fn in skip:
1730 if fn in skip:
1730 if copy:
1731 if copy:
1731 skip[copy] = True
1732 skip[copy] = True
1732 continue
1733 continue
1733 pstates = matches.get(parent, {}).get(copy or fn, [])
1734 pstates = matches.get(parent, {}).get(copy or fn, [])
1734 if pstates or states:
1735 if pstates or states:
1735 r = display(fn, ctx, pstates, states)
1736 r = display(fn, ctx, pstates, states)
1736 found = found or r
1737 found = found or r
1737 if r and not opts.get('all'):
1738 if r and not opts.get('all'):
1738 skip[fn] = True
1739 skip[fn] = True
1739 if copy:
1740 if copy:
1740 skip[copy] = True
1741 skip[copy] = True
1741 del matches[rev]
1742 del matches[rev]
1742 del revfiles[rev]
1743 del revfiles[rev]
1743
1744
1744 return not found
1745 return not found
1745
1746
1746 def heads(ui, repo, *branchrevs, **opts):
1747 def heads(ui, repo, *branchrevs, **opts):
1747 """show current repository heads or show branch heads
1748 """show current repository heads or show branch heads
1748
1749
1749 With no arguments, show all repository branch heads.
1750 With no arguments, show all repository branch heads.
1750
1751
1751 Repository "heads" are changesets with no child changesets. They are
1752 Repository "heads" are changesets with no child changesets. They are
1752 where development generally takes place and are the usual targets
1753 where development generally takes place and are the usual targets
1753 for update and merge operations. Branch heads are changesets that have
1754 for update and merge operations. Branch heads are changesets that have
1754 no child changeset on the same branch.
1755 no child changeset on the same branch.
1755
1756
1756 If one or more REVs are given, only branch heads on the branches
1757 If one or more REVs are given, only branch heads on the branches
1757 associated with the specified changesets are shown.
1758 associated with the specified changesets are shown.
1758
1759
1759 If -c/--closed is specified, also show branch heads marked closed
1760 If -c/--closed is specified, also show branch heads marked closed
1760 (see :hg:`commit --close-branch`).
1761 (see :hg:`commit --close-branch`).
1761
1762
1762 If STARTREV is specified, only those heads that are descendants of
1763 If STARTREV is specified, only those heads that are descendants of
1763 STARTREV will be displayed.
1764 STARTREV will be displayed.
1764
1765
1765 If -t/--topo is specified, named branch mechanics will be ignored and only
1766 If -t/--topo is specified, named branch mechanics will be ignored and only
1766 changesets without children will be shown.
1767 changesets without children will be shown.
1767
1768
1768 Returns 0 if matching heads are found, 1 if not.
1769 Returns 0 if matching heads are found, 1 if not.
1769 """
1770 """
1770
1771
1771 if opts.get('rev'):
1772 if opts.get('rev'):
1772 start = repo.lookup(opts['rev'])
1773 start = repo.lookup(opts['rev'])
1773 else:
1774 else:
1774 start = None
1775 start = None
1775
1776
1776 if opts.get('topo'):
1777 if opts.get('topo'):
1777 heads = [repo[h] for h in repo.heads(start)]
1778 heads = [repo[h] for h in repo.heads(start)]
1778 else:
1779 else:
1779 heads = []
1780 heads = []
1780 for b, ls in repo.branchmap().iteritems():
1781 for b, ls in repo.branchmap().iteritems():
1781 if start is None:
1782 if start is None:
1782 heads += [repo[h] for h in ls]
1783 heads += [repo[h] for h in ls]
1783 continue
1784 continue
1784 startrev = repo.changelog.rev(start)
1785 startrev = repo.changelog.rev(start)
1785 descendants = set(repo.changelog.descendants(startrev))
1786 descendants = set(repo.changelog.descendants(startrev))
1786 descendants.add(startrev)
1787 descendants.add(startrev)
1787 rev = repo.changelog.rev
1788 rev = repo.changelog.rev
1788 heads += [repo[h] for h in ls if rev(h) in descendants]
1789 heads += [repo[h] for h in ls if rev(h) in descendants]
1789
1790
1790 if branchrevs:
1791 if branchrevs:
1791 decode, encode = encoding.fromlocal, encoding.tolocal
1792 decode, encode = encoding.fromlocal, encoding.tolocal
1792 branches = set(repo[decode(br)].branch() for br in branchrevs)
1793 branches = set(repo[decode(br)].branch() for br in branchrevs)
1793 heads = [h for h in heads if h.branch() in branches]
1794 heads = [h for h in heads if h.branch() in branches]
1794
1795
1795 if not opts.get('closed'):
1796 if not opts.get('closed'):
1796 heads = [h for h in heads if not h.extra().get('close')]
1797 heads = [h for h in heads if not h.extra().get('close')]
1797
1798
1798 if opts.get('active') and branchrevs:
1799 if opts.get('active') and branchrevs:
1799 dagheads = repo.heads(start)
1800 dagheads = repo.heads(start)
1800 heads = [h for h in heads if h.node() in dagheads]
1801 heads = [h for h in heads if h.node() in dagheads]
1801
1802
1802 if branchrevs:
1803 if branchrevs:
1803 haveheads = set(h.branch() for h in heads)
1804 haveheads = set(h.branch() for h in heads)
1804 if branches - haveheads:
1805 if branches - haveheads:
1805 headless = ', '.join(encode(b) for b in branches - haveheads)
1806 headless = ', '.join(encode(b) for b in branches - haveheads)
1806 msg = _('no open branch heads found on branches %s')
1807 msg = _('no open branch heads found on branches %s')
1807 if opts.get('rev'):
1808 if opts.get('rev'):
1808 msg += _(' (started at %s)' % opts['rev'])
1809 msg += _(' (started at %s)' % opts['rev'])
1809 ui.warn((msg + '\n') % headless)
1810 ui.warn((msg + '\n') % headless)
1810
1811
1811 if not heads:
1812 if not heads:
1812 return 1
1813 return 1
1813
1814
1814 heads = sorted(heads, key=lambda x: -x.rev())
1815 heads = sorted(heads, key=lambda x: -x.rev())
1815 displayer = cmdutil.show_changeset(ui, repo, opts)
1816 displayer = cmdutil.show_changeset(ui, repo, opts)
1816 for ctx in heads:
1817 for ctx in heads:
1817 displayer.show(ctx)
1818 displayer.show(ctx)
1818 displayer.close()
1819 displayer.close()
1819
1820
1820 def help_(ui, name=None, with_version=False, unknowncmd=False):
1821 def help_(ui, name=None, with_version=False, unknowncmd=False):
1821 """show help for a given topic or a help overview
1822 """show help for a given topic or a help overview
1822
1823
1823 With no arguments, print a list of commands with short help messages.
1824 With no arguments, print a list of commands with short help messages.
1824
1825
1825 Given a topic, extension, or command name, print help for that
1826 Given a topic, extension, or command name, print help for that
1826 topic.
1827 topic.
1827
1828
1828 Returns 0 if successful.
1829 Returns 0 if successful.
1829 """
1830 """
1830 option_lists = []
1831 option_lists = []
1831 textwidth = util.termwidth() - 2
1832 textwidth = util.termwidth() - 2
1832
1833
1833 def addglobalopts(aliases):
1834 def addglobalopts(aliases):
1834 if ui.verbose:
1835 if ui.verbose:
1835 option_lists.append((_("global options:"), globalopts))
1836 option_lists.append((_("global options:"), globalopts))
1836 if name == 'shortlist':
1837 if name == 'shortlist':
1837 option_lists.append((_('use "hg help" for the full list '
1838 option_lists.append((_('use "hg help" for the full list '
1838 'of commands'), ()))
1839 'of commands'), ()))
1839 else:
1840 else:
1840 if name == 'shortlist':
1841 if name == 'shortlist':
1841 msg = _('use "hg help" for the full list of commands '
1842 msg = _('use "hg help" for the full list of commands '
1842 'or "hg -v" for details')
1843 'or "hg -v" for details')
1843 elif aliases:
1844 elif aliases:
1844 msg = _('use "hg -v help%s" to show aliases and '
1845 msg = _('use "hg -v help%s" to show aliases and '
1845 'global options') % (name and " " + name or "")
1846 'global options') % (name and " " + name or "")
1846 else:
1847 else:
1847 msg = _('use "hg -v help %s" to show global options') % name
1848 msg = _('use "hg -v help %s" to show global options') % name
1848 option_lists.append((msg, ()))
1849 option_lists.append((msg, ()))
1849
1850
1850 def helpcmd(name):
1851 def helpcmd(name):
1851 if with_version:
1852 if with_version:
1852 version_(ui)
1853 version_(ui)
1853 ui.write('\n')
1854 ui.write('\n')
1854
1855
1855 try:
1856 try:
1856 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1857 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1857 except error.AmbiguousCommand, inst:
1858 except error.AmbiguousCommand, inst:
1858 # py3k fix: except vars can't be used outside the scope of the
1859 # py3k fix: except vars can't be used outside the scope of the
1859 # except block, nor can be used inside a lambda. python issue4617
1860 # except block, nor can be used inside a lambda. python issue4617
1860 prefix = inst.args[0]
1861 prefix = inst.args[0]
1861 select = lambda c: c.lstrip('^').startswith(prefix)
1862 select = lambda c: c.lstrip('^').startswith(prefix)
1862 helplist(_('list of commands:\n\n'), select)
1863 helplist(_('list of commands:\n\n'), select)
1863 return
1864 return
1864
1865
1865 # check if it's an invalid alias and display its error if it is
1866 # check if it's an invalid alias and display its error if it is
1866 if getattr(entry[0], 'badalias', False):
1867 if getattr(entry[0], 'badalias', False):
1867 if not unknowncmd:
1868 if not unknowncmd:
1868 entry[0](ui)
1869 entry[0](ui)
1869 return
1870 return
1870
1871
1871 # synopsis
1872 # synopsis
1872 if len(entry) > 2:
1873 if len(entry) > 2:
1873 if entry[2].startswith('hg'):
1874 if entry[2].startswith('hg'):
1874 ui.write("%s\n" % entry[2])
1875 ui.write("%s\n" % entry[2])
1875 else:
1876 else:
1876 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1877 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1877 else:
1878 else:
1878 ui.write('hg %s\n' % aliases[0])
1879 ui.write('hg %s\n' % aliases[0])
1879
1880
1880 # aliases
1881 # aliases
1881 if not ui.quiet and len(aliases) > 1:
1882 if not ui.quiet and len(aliases) > 1:
1882 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1883 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1883
1884
1884 # description
1885 # description
1885 doc = gettext(entry[0].__doc__)
1886 doc = gettext(entry[0].__doc__)
1886 if not doc:
1887 if not doc:
1887 doc = _("(no help text available)")
1888 doc = _("(no help text available)")
1888 if hasattr(entry[0], 'definition'): # aliased command
1889 if hasattr(entry[0], 'definition'): # aliased command
1889 if entry[0].definition.startswith('!'): # shell alias
1890 if entry[0].definition.startswith('!'): # shell alias
1890 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1891 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1891 else:
1892 else:
1892 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1893 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1893 if ui.quiet:
1894 if ui.quiet:
1894 doc = doc.splitlines()[0]
1895 doc = doc.splitlines()[0]
1895 keep = ui.verbose and ['verbose'] or []
1896 keep = ui.verbose and ['verbose'] or []
1896 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1897 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1897 ui.write("\n%s\n" % formatted)
1898 ui.write("\n%s\n" % formatted)
1898 if pruned:
1899 if pruned:
1899 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1900 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1900
1901
1901 if not ui.quiet:
1902 if not ui.quiet:
1902 # options
1903 # options
1903 if entry[1]:
1904 if entry[1]:
1904 option_lists.append((_("options:\n"), entry[1]))
1905 option_lists.append((_("options:\n"), entry[1]))
1905
1906
1906 addglobalopts(False)
1907 addglobalopts(False)
1907
1908
1908 def helplist(header, select=None):
1909 def helplist(header, select=None):
1909 h = {}
1910 h = {}
1910 cmds = {}
1911 cmds = {}
1911 for c, e in table.iteritems():
1912 for c, e in table.iteritems():
1912 f = c.split("|", 1)[0]
1913 f = c.split("|", 1)[0]
1913 if select and not select(f):
1914 if select and not select(f):
1914 continue
1915 continue
1915 if (not select and name != 'shortlist' and
1916 if (not select and name != 'shortlist' and
1916 e[0].__module__ != __name__):
1917 e[0].__module__ != __name__):
1917 continue
1918 continue
1918 if name == "shortlist" and not f.startswith("^"):
1919 if name == "shortlist" and not f.startswith("^"):
1919 continue
1920 continue
1920 f = f.lstrip("^")
1921 f = f.lstrip("^")
1921 if not ui.debugflag and f.startswith("debug"):
1922 if not ui.debugflag and f.startswith("debug"):
1922 continue
1923 continue
1923 doc = e[0].__doc__
1924 doc = e[0].__doc__
1924 if doc and 'DEPRECATED' in doc and not ui.verbose:
1925 if doc and 'DEPRECATED' in doc and not ui.verbose:
1925 continue
1926 continue
1926 doc = gettext(doc)
1927 doc = gettext(doc)
1927 if not doc:
1928 if not doc:
1928 doc = _("(no help text available)")
1929 doc = _("(no help text available)")
1929 h[f] = doc.splitlines()[0].rstrip()
1930 h[f] = doc.splitlines()[0].rstrip()
1930 cmds[f] = c.lstrip("^")
1931 cmds[f] = c.lstrip("^")
1931
1932
1932 if not h:
1933 if not h:
1933 ui.status(_('no commands defined\n'))
1934 ui.status(_('no commands defined\n'))
1934 return
1935 return
1935
1936
1936 ui.status(header)
1937 ui.status(header)
1937 fns = sorted(h)
1938 fns = sorted(h)
1938 m = max(map(len, fns))
1939 m = max(map(len, fns))
1939 for f in fns:
1940 for f in fns:
1940 if ui.verbose:
1941 if ui.verbose:
1941 commands = cmds[f].replace("|",", ")
1942 commands = cmds[f].replace("|",", ")
1942 ui.write(" %s:\n %s\n"%(commands, h[f]))
1943 ui.write(" %s:\n %s\n"%(commands, h[f]))
1943 else:
1944 else:
1944 ui.write('%s\n' % (util.wrap(h[f],
1945 ui.write('%s\n' % (util.wrap(h[f],
1945 initindent=' %-*s ' % (m, f),
1946 initindent=' %-*s ' % (m, f),
1946 hangindent=' ' * (m + 4))))
1947 hangindent=' ' * (m + 4))))
1947
1948
1948 if not ui.quiet:
1949 if not ui.quiet:
1949 addglobalopts(True)
1950 addglobalopts(True)
1950
1951
1951 def helptopic(name):
1952 def helptopic(name):
1952 for names, header, doc in help.helptable:
1953 for names, header, doc in help.helptable:
1953 if name in names:
1954 if name in names:
1954 break
1955 break
1955 else:
1956 else:
1956 raise error.UnknownCommand(name)
1957 raise error.UnknownCommand(name)
1957
1958
1958 # description
1959 # description
1959 if not doc:
1960 if not doc:
1960 doc = _("(no help text available)")
1961 doc = _("(no help text available)")
1961 if hasattr(doc, '__call__'):
1962 if hasattr(doc, '__call__'):
1962 doc = doc()
1963 doc = doc()
1963
1964
1964 ui.write("%s\n\n" % header)
1965 ui.write("%s\n\n" % header)
1965 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1966 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1966
1967
1967 def helpext(name):
1968 def helpext(name):
1968 try:
1969 try:
1969 mod = extensions.find(name)
1970 mod = extensions.find(name)
1970 doc = gettext(mod.__doc__) or _('no help text available')
1971 doc = gettext(mod.__doc__) or _('no help text available')
1971 except KeyError:
1972 except KeyError:
1972 mod = None
1973 mod = None
1973 doc = extensions.disabledext(name)
1974 doc = extensions.disabledext(name)
1974 if not doc:
1975 if not doc:
1975 raise error.UnknownCommand(name)
1976 raise error.UnknownCommand(name)
1976
1977
1977 if '\n' not in doc:
1978 if '\n' not in doc:
1978 head, tail = doc, ""
1979 head, tail = doc, ""
1979 else:
1980 else:
1980 head, tail = doc.split('\n', 1)
1981 head, tail = doc.split('\n', 1)
1981 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1982 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1982 if tail:
1983 if tail:
1983 ui.write(minirst.format(tail, textwidth))
1984 ui.write(minirst.format(tail, textwidth))
1984 ui.status('\n\n')
1985 ui.status('\n\n')
1985
1986
1986 if mod:
1987 if mod:
1987 try:
1988 try:
1988 ct = mod.cmdtable
1989 ct = mod.cmdtable
1989 except AttributeError:
1990 except AttributeError:
1990 ct = {}
1991 ct = {}
1991 modcmds = set([c.split('|', 1)[0] for c in ct])
1992 modcmds = set([c.split('|', 1)[0] for c in ct])
1992 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1993 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1993 else:
1994 else:
1994 ui.write(_('use "hg help extensions" for information on enabling '
1995 ui.write(_('use "hg help extensions" for information on enabling '
1995 'extensions\n'))
1996 'extensions\n'))
1996
1997
1997 def helpextcmd(name):
1998 def helpextcmd(name):
1998 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1999 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1999 doc = gettext(mod.__doc__).splitlines()[0]
2000 doc = gettext(mod.__doc__).splitlines()[0]
2000
2001
2001 msg = help.listexts(_("'%s' is provided by the following "
2002 msg = help.listexts(_("'%s' is provided by the following "
2002 "extension:") % cmd, {ext: doc}, len(ext),
2003 "extension:") % cmd, {ext: doc}, len(ext),
2003 indent=4)
2004 indent=4)
2004 ui.write(minirst.format(msg, textwidth))
2005 ui.write(minirst.format(msg, textwidth))
2005 ui.write('\n\n')
2006 ui.write('\n\n')
2006 ui.write(_('use "hg help extensions" for information on enabling '
2007 ui.write(_('use "hg help extensions" for information on enabling '
2007 'extensions\n'))
2008 'extensions\n'))
2008
2009
2009 if name and name != 'shortlist':
2010 if name and name != 'shortlist':
2010 i = None
2011 i = None
2011 if unknowncmd:
2012 if unknowncmd:
2012 queries = (helpextcmd,)
2013 queries = (helpextcmd,)
2013 else:
2014 else:
2014 queries = (helptopic, helpcmd, helpext, helpextcmd)
2015 queries = (helptopic, helpcmd, helpext, helpextcmd)
2015 for f in queries:
2016 for f in queries:
2016 try:
2017 try:
2017 f(name)
2018 f(name)
2018 i = None
2019 i = None
2019 break
2020 break
2020 except error.UnknownCommand, inst:
2021 except error.UnknownCommand, inst:
2021 i = inst
2022 i = inst
2022 if i:
2023 if i:
2023 raise i
2024 raise i
2024
2025
2025 else:
2026 else:
2026 # program name
2027 # program name
2027 if ui.verbose or with_version:
2028 if ui.verbose or with_version:
2028 version_(ui)
2029 version_(ui)
2029 else:
2030 else:
2030 ui.status(_("Mercurial Distributed SCM\n"))
2031 ui.status(_("Mercurial Distributed SCM\n"))
2031 ui.status('\n')
2032 ui.status('\n')
2032
2033
2033 # list of commands
2034 # list of commands
2034 if name == "shortlist":
2035 if name == "shortlist":
2035 header = _('basic commands:\n\n')
2036 header = _('basic commands:\n\n')
2036 else:
2037 else:
2037 header = _('list of commands:\n\n')
2038 header = _('list of commands:\n\n')
2038
2039
2039 helplist(header)
2040 helplist(header)
2040 if name != 'shortlist':
2041 if name != 'shortlist':
2041 exts, maxlength = extensions.enabled()
2042 exts, maxlength = extensions.enabled()
2042 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2043 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2043 if text:
2044 if text:
2044 ui.write("\n%s\n" % minirst.format(text, textwidth))
2045 ui.write("\n%s\n" % minirst.format(text, textwidth))
2045
2046
2046 # list all option lists
2047 # list all option lists
2047 opt_output = []
2048 opt_output = []
2048 multioccur = False
2049 multioccur = False
2049 for title, options in option_lists:
2050 for title, options in option_lists:
2050 opt_output.append(("\n%s" % title, None))
2051 opt_output.append(("\n%s" % title, None))
2051 for option in options:
2052 for option in options:
2052 if len(option) == 5:
2053 if len(option) == 5:
2053 shortopt, longopt, default, desc, optlabel = option
2054 shortopt, longopt, default, desc, optlabel = option
2054 else:
2055 else:
2055 shortopt, longopt, default, desc = option
2056 shortopt, longopt, default, desc = option
2056 optlabel = _("VALUE") # default label
2057 optlabel = _("VALUE") # default label
2057
2058
2058 if _("DEPRECATED") in desc and not ui.verbose:
2059 if _("DEPRECATED") in desc and not ui.verbose:
2059 continue
2060 continue
2060 if isinstance(default, list):
2061 if isinstance(default, list):
2061 numqualifier = " %s [+]" % optlabel
2062 numqualifier = " %s [+]" % optlabel
2062 multioccur = True
2063 multioccur = True
2063 elif (default is not None) and not isinstance(default, bool):
2064 elif (default is not None) and not isinstance(default, bool):
2064 numqualifier = " %s" % optlabel
2065 numqualifier = " %s" % optlabel
2065 else:
2066 else:
2066 numqualifier = ""
2067 numqualifier = ""
2067 opt_output.append(("%2s%s" %
2068 opt_output.append(("%2s%s" %
2068 (shortopt and "-%s" % shortopt,
2069 (shortopt and "-%s" % shortopt,
2069 longopt and " --%s%s" %
2070 longopt and " --%s%s" %
2070 (longopt, numqualifier)),
2071 (longopt, numqualifier)),
2071 "%s%s" % (desc,
2072 "%s%s" % (desc,
2072 default
2073 default
2073 and _(" (default: %s)") % default
2074 and _(" (default: %s)") % default
2074 or "")))
2075 or "")))
2075 if multioccur:
2076 if multioccur:
2076 msg = _("\n[+] marked option can be specified multiple times")
2077 msg = _("\n[+] marked option can be specified multiple times")
2077 if ui.verbose and name != 'shortlist':
2078 if ui.verbose and name != 'shortlist':
2078 opt_output.append((msg, None))
2079 opt_output.append((msg, None))
2079 else:
2080 else:
2080 opt_output.insert(-1, (msg, None))
2081 opt_output.insert(-1, (msg, None))
2081
2082
2082 if not name:
2083 if not name:
2083 ui.write(_("\nadditional help topics:\n\n"))
2084 ui.write(_("\nadditional help topics:\n\n"))
2084 topics = []
2085 topics = []
2085 for names, header, doc in help.helptable:
2086 for names, header, doc in help.helptable:
2086 topics.append((sorted(names, key=len, reverse=True)[0], header))
2087 topics.append((sorted(names, key=len, reverse=True)[0], header))
2087 topics_len = max([len(s[0]) for s in topics])
2088 topics_len = max([len(s[0]) for s in topics])
2088 for t, desc in topics:
2089 for t, desc in topics:
2089 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2090 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2090
2091
2091 if opt_output:
2092 if opt_output:
2092 colwidth = encoding.colwidth
2093 colwidth = encoding.colwidth
2093 # normalize: (opt or message, desc or None, width of opt)
2094 # normalize: (opt or message, desc or None, width of opt)
2094 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2095 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2095 for opt, desc in opt_output]
2096 for opt, desc in opt_output]
2096 hanging = max([e[2] for e in entries])
2097 hanging = max([e[2] for e in entries])
2097 for opt, desc, width in entries:
2098 for opt, desc, width in entries:
2098 if desc:
2099 if desc:
2099 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2100 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2100 hangindent = ' ' * (hanging + 3)
2101 hangindent = ' ' * (hanging + 3)
2101 ui.write('%s\n' % (util.wrap(desc,
2102 ui.write('%s\n' % (util.wrap(desc,
2102 initindent=initindent,
2103 initindent=initindent,
2103 hangindent=hangindent)))
2104 hangindent=hangindent)))
2104 else:
2105 else:
2105 ui.write("%s\n" % opt)
2106 ui.write("%s\n" % opt)
2106
2107
2107 def identify(ui, repo, source=None,
2108 def identify(ui, repo, source=None,
2108 rev=None, num=None, id=None, branch=None, tags=None):
2109 rev=None, num=None, id=None, branch=None, tags=None):
2109 """identify the working copy or specified revision
2110 """identify the working copy or specified revision
2110
2111
2111 With no revision, print a summary of the current state of the
2112 With no revision, print a summary of the current state of the
2112 repository.
2113 repository.
2113
2114
2114 Specifying a path to a repository root or Mercurial bundle will
2115 Specifying a path to a repository root or Mercurial bundle will
2115 cause lookup to operate on that repository/bundle.
2116 cause lookup to operate on that repository/bundle.
2116
2117
2117 This summary identifies the repository state using one or two
2118 This summary identifies the repository state using one or two
2118 parent hash identifiers, followed by a "+" if there are
2119 parent hash identifiers, followed by a "+" if there are
2119 uncommitted changes in the working directory, a list of tags for
2120 uncommitted changes in the working directory, a list of tags for
2120 this revision and a branch name for non-default branches.
2121 this revision and a branch name for non-default branches.
2121
2122
2122 Returns 0 if successful.
2123 Returns 0 if successful.
2123 """
2124 """
2124
2125
2125 if not repo and not source:
2126 if not repo and not source:
2126 raise util.Abort(_("there is no Mercurial repository here "
2127 raise util.Abort(_("there is no Mercurial repository here "
2127 "(.hg not found)"))
2128 "(.hg not found)"))
2128
2129
2129 hexfunc = ui.debugflag and hex or short
2130 hexfunc = ui.debugflag and hex or short
2130 default = not (num or id or branch or tags)
2131 default = not (num or id or branch or tags)
2131 output = []
2132 output = []
2132
2133
2133 revs = []
2134 revs = []
2134 if source:
2135 if source:
2135 source, branches = hg.parseurl(ui.expandpath(source))
2136 source, branches = hg.parseurl(ui.expandpath(source))
2136 repo = hg.repository(ui, source)
2137 repo = hg.repository(ui, source)
2137 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2138 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2138
2139
2139 if not repo.local():
2140 if not repo.local():
2140 if not rev and revs:
2141 if not rev and revs:
2141 rev = revs[0]
2142 rev = revs[0]
2142 if not rev:
2143 if not rev:
2143 rev = "tip"
2144 rev = "tip"
2144 if num or branch or tags:
2145 if num or branch or tags:
2145 raise util.Abort(
2146 raise util.Abort(
2146 "can't query remote revision number, branch, or tags")
2147 "can't query remote revision number, branch, or tags")
2147 output = [hexfunc(repo.lookup(rev))]
2148 output = [hexfunc(repo.lookup(rev))]
2148 elif not rev:
2149 elif not rev:
2149 ctx = repo[None]
2150 ctx = repo[None]
2150 parents = ctx.parents()
2151 parents = ctx.parents()
2151 changed = False
2152 changed = False
2152 if default or id or num:
2153 if default or id or num:
2153 changed = util.any(repo.status())
2154 changed = util.any(repo.status())
2154 if default or id:
2155 if default or id:
2155 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2156 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2156 (changed) and "+" or "")]
2157 (changed) and "+" or "")]
2157 if num:
2158 if num:
2158 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2159 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2159 (changed) and "+" or ""))
2160 (changed) and "+" or ""))
2160 else:
2161 else:
2161 ctx = repo[rev]
2162 ctx = repo[rev]
2162 if default or id:
2163 if default or id:
2163 output = [hexfunc(ctx.node())]
2164 output = [hexfunc(ctx.node())]
2164 if num:
2165 if num:
2165 output.append(str(ctx.rev()))
2166 output.append(str(ctx.rev()))
2166
2167
2167 if repo.local() and default and not ui.quiet:
2168 if repo.local() and default and not ui.quiet:
2168 b = encoding.tolocal(ctx.branch())
2169 b = encoding.tolocal(ctx.branch())
2169 if b != 'default':
2170 if b != 'default':
2170 output.append("(%s)" % b)
2171 output.append("(%s)" % b)
2171
2172
2172 # multiple tags for a single parent separated by '/'
2173 # multiple tags for a single parent separated by '/'
2173 t = "/".join(ctx.tags())
2174 t = "/".join(ctx.tags())
2174 if t:
2175 if t:
2175 output.append(t)
2176 output.append(t)
2176
2177
2177 if branch:
2178 if branch:
2178 output.append(encoding.tolocal(ctx.branch()))
2179 output.append(encoding.tolocal(ctx.branch()))
2179
2180
2180 if tags:
2181 if tags:
2181 output.extend(ctx.tags())
2182 output.extend(ctx.tags())
2182
2183
2183 ui.write("%s\n" % ' '.join(output))
2184 ui.write("%s\n" % ' '.join(output))
2184
2185
2185 def import_(ui, repo, patch1, *patches, **opts):
2186 def import_(ui, repo, patch1, *patches, **opts):
2186 """import an ordered set of patches
2187 """import an ordered set of patches
2187
2188
2188 Import a list of patches and commit them individually (unless
2189 Import a list of patches and commit them individually (unless
2189 --no-commit is specified).
2190 --no-commit is specified).
2190
2191
2191 If there are outstanding changes in the working directory, import
2192 If there are outstanding changes in the working directory, import
2192 will abort unless given the -f/--force flag.
2193 will abort unless given the -f/--force flag.
2193
2194
2194 You can import a patch straight from a mail message. Even patches
2195 You can import a patch straight from a mail message. Even patches
2195 as attachments work (to use the body part, it must have type
2196 as attachments work (to use the body part, it must have type
2196 text/plain or text/x-patch). From and Subject headers of email
2197 text/plain or text/x-patch). From and Subject headers of email
2197 message are used as default committer and commit message. All
2198 message are used as default committer and commit message. All
2198 text/plain body parts before first diff are added to commit
2199 text/plain body parts before first diff are added to commit
2199 message.
2200 message.
2200
2201
2201 If the imported patch was generated by :hg:`export`, user and
2202 If the imported patch was generated by :hg:`export`, user and
2202 description from patch override values from message headers and
2203 description from patch override values from message headers and
2203 body. Values given on command line with -m/--message and -u/--user
2204 body. Values given on command line with -m/--message and -u/--user
2204 override these.
2205 override these.
2205
2206
2206 If --exact is specified, import will set the working directory to
2207 If --exact is specified, import will set the working directory to
2207 the parent of each patch before applying it, and will abort if the
2208 the parent of each patch before applying it, and will abort if the
2208 resulting changeset has a different ID than the one recorded in
2209 resulting changeset has a different ID than the one recorded in
2209 the patch. This may happen due to character set problems or other
2210 the patch. This may happen due to character set problems or other
2210 deficiencies in the text patch format.
2211 deficiencies in the text patch format.
2211
2212
2212 With -s/--similarity, hg will attempt to discover renames and
2213 With -s/--similarity, hg will attempt to discover renames and
2213 copies in the patch in the same way as 'addremove'.
2214 copies in the patch in the same way as 'addremove'.
2214
2215
2215 To read a patch from standard input, use "-" as the patch name. If
2216 To read a patch from standard input, use "-" as the patch name. If
2216 a URL is specified, the patch will be downloaded from it.
2217 a URL is specified, the patch will be downloaded from it.
2217 See :hg:`help dates` for a list of formats valid for -d/--date.
2218 See :hg:`help dates` for a list of formats valid for -d/--date.
2218
2219
2219 Returns 0 on success.
2220 Returns 0 on success.
2220 """
2221 """
2221 patches = (patch1,) + patches
2222 patches = (patch1,) + patches
2222
2223
2223 date = opts.get('date')
2224 date = opts.get('date')
2224 if date:
2225 if date:
2225 opts['date'] = util.parsedate(date)
2226 opts['date'] = util.parsedate(date)
2226
2227
2227 try:
2228 try:
2228 sim = float(opts.get('similarity') or 0)
2229 sim = float(opts.get('similarity') or 0)
2229 except ValueError:
2230 except ValueError:
2230 raise util.Abort(_('similarity must be a number'))
2231 raise util.Abort(_('similarity must be a number'))
2231 if sim < 0 or sim > 100:
2232 if sim < 0 or sim > 100:
2232 raise util.Abort(_('similarity must be between 0 and 100'))
2233 raise util.Abort(_('similarity must be between 0 and 100'))
2233
2234
2234 if opts.get('exact') or not opts.get('force'):
2235 if opts.get('exact') or not opts.get('force'):
2235 cmdutil.bail_if_changed(repo)
2236 cmdutil.bail_if_changed(repo)
2236
2237
2237 d = opts["base"]
2238 d = opts["base"]
2238 strip = opts["strip"]
2239 strip = opts["strip"]
2239 wlock = lock = None
2240 wlock = lock = None
2240
2241
2241 def tryone(ui, hunk):
2242 def tryone(ui, hunk):
2242 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2243 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2243 patch.extract(ui, hunk)
2244 patch.extract(ui, hunk)
2244
2245
2245 if not tmpname:
2246 if not tmpname:
2246 return None
2247 return None
2247 commitid = _('to working directory')
2248 commitid = _('to working directory')
2248
2249
2249 try:
2250 try:
2250 cmdline_message = cmdutil.logmessage(opts)
2251 cmdline_message = cmdutil.logmessage(opts)
2251 if cmdline_message:
2252 if cmdline_message:
2252 # pickup the cmdline msg
2253 # pickup the cmdline msg
2253 message = cmdline_message
2254 message = cmdline_message
2254 elif message:
2255 elif message:
2255 # pickup the patch msg
2256 # pickup the patch msg
2256 message = message.strip()
2257 message = message.strip()
2257 else:
2258 else:
2258 # launch the editor
2259 # launch the editor
2259 message = None
2260 message = None
2260 ui.debug('message:\n%s\n' % message)
2261 ui.debug('message:\n%s\n' % message)
2261
2262
2262 wp = repo.parents()
2263 wp = repo.parents()
2263 if opts.get('exact'):
2264 if opts.get('exact'):
2264 if not nodeid or not p1:
2265 if not nodeid or not p1:
2265 raise util.Abort(_('not a Mercurial patch'))
2266 raise util.Abort(_('not a Mercurial patch'))
2266 p1 = repo.lookup(p1)
2267 p1 = repo.lookup(p1)
2267 p2 = repo.lookup(p2 or hex(nullid))
2268 p2 = repo.lookup(p2 or hex(nullid))
2268
2269
2269 if p1 != wp[0].node():
2270 if p1 != wp[0].node():
2270 hg.clean(repo, p1)
2271 hg.clean(repo, p1)
2271 repo.dirstate.setparents(p1, p2)
2272 repo.dirstate.setparents(p1, p2)
2272 elif p2:
2273 elif p2:
2273 try:
2274 try:
2274 p1 = repo.lookup(p1)
2275 p1 = repo.lookup(p1)
2275 p2 = repo.lookup(p2)
2276 p2 = repo.lookup(p2)
2276 if p1 == wp[0].node():
2277 if p1 == wp[0].node():
2277 repo.dirstate.setparents(p1, p2)
2278 repo.dirstate.setparents(p1, p2)
2278 except error.RepoError:
2279 except error.RepoError:
2279 pass
2280 pass
2280 if opts.get('exact') or opts.get('import_branch'):
2281 if opts.get('exact') or opts.get('import_branch'):
2281 repo.dirstate.setbranch(branch or 'default')
2282 repo.dirstate.setbranch(branch or 'default')
2282
2283
2283 files = {}
2284 files = {}
2284 try:
2285 try:
2285 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2286 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2286 files=files, eolmode=None)
2287 files=files, eolmode=None)
2287 finally:
2288 finally:
2288 files = patch.updatedir(ui, repo, files,
2289 files = patch.updatedir(ui, repo, files,
2289 similarity=sim / 100.0)
2290 similarity=sim / 100.0)
2290 if not opts.get('no_commit'):
2291 if not opts.get('no_commit'):
2291 if opts.get('exact'):
2292 if opts.get('exact'):
2292 m = None
2293 m = None
2293 else:
2294 else:
2294 m = cmdutil.matchfiles(repo, files or [])
2295 m = cmdutil.matchfiles(repo, files or [])
2295 n = repo.commit(message, opts.get('user') or user,
2296 n = repo.commit(message, opts.get('user') or user,
2296 opts.get('date') or date, match=m,
2297 opts.get('date') or date, match=m,
2297 editor=cmdutil.commiteditor)
2298 editor=cmdutil.commiteditor)
2298 if opts.get('exact'):
2299 if opts.get('exact'):
2299 if hex(n) != nodeid:
2300 if hex(n) != nodeid:
2300 repo.rollback()
2301 repo.rollback()
2301 raise util.Abort(_('patch is damaged'
2302 raise util.Abort(_('patch is damaged'
2302 ' or loses information'))
2303 ' or loses information'))
2303 # Force a dirstate write so that the next transaction
2304 # Force a dirstate write so that the next transaction
2304 # backups an up-do-date file.
2305 # backups an up-do-date file.
2305 repo.dirstate.write()
2306 repo.dirstate.write()
2306 if n:
2307 if n:
2307 commitid = short(n)
2308 commitid = short(n)
2308
2309
2309 return commitid
2310 return commitid
2310 finally:
2311 finally:
2311 os.unlink(tmpname)
2312 os.unlink(tmpname)
2312
2313
2313 try:
2314 try:
2314 wlock = repo.wlock()
2315 wlock = repo.wlock()
2315 lock = repo.lock()
2316 lock = repo.lock()
2316 lastcommit = None
2317 lastcommit = None
2317 for p in patches:
2318 for p in patches:
2318 pf = os.path.join(d, p)
2319 pf = os.path.join(d, p)
2319
2320
2320 if pf == '-':
2321 if pf == '-':
2321 ui.status(_("applying patch from stdin\n"))
2322 ui.status(_("applying patch from stdin\n"))
2322 pf = sys.stdin
2323 pf = sys.stdin
2323 else:
2324 else:
2324 ui.status(_("applying %s\n") % p)
2325 ui.status(_("applying %s\n") % p)
2325 pf = url.open(ui, pf)
2326 pf = url.open(ui, pf)
2326
2327
2327 haspatch = False
2328 haspatch = False
2328 for hunk in patch.split(pf):
2329 for hunk in patch.split(pf):
2329 commitid = tryone(ui, hunk)
2330 commitid = tryone(ui, hunk)
2330 if commitid:
2331 if commitid:
2331 haspatch = True
2332 haspatch = True
2332 if lastcommit:
2333 if lastcommit:
2333 ui.status(_('applied %s\n') % lastcommit)
2334 ui.status(_('applied %s\n') % lastcommit)
2334 lastcommit = commitid
2335 lastcommit = commitid
2335
2336
2336 if not haspatch:
2337 if not haspatch:
2337 raise util.Abort(_('no diffs found'))
2338 raise util.Abort(_('no diffs found'))
2338
2339
2339 finally:
2340 finally:
2340 release(lock, wlock)
2341 release(lock, wlock)
2341
2342
2342 def incoming(ui, repo, source="default", **opts):
2343 def incoming(ui, repo, source="default", **opts):
2343 """show new changesets found in source
2344 """show new changesets found in source
2344
2345
2345 Show new changesets found in the specified path/URL or the default
2346 Show new changesets found in the specified path/URL or the default
2346 pull location. These are the changesets that would have been pulled
2347 pull location. These are the changesets that would have been pulled
2347 if a pull at the time you issued this command.
2348 if a pull at the time you issued this command.
2348
2349
2349 For remote repository, using --bundle avoids downloading the
2350 For remote repository, using --bundle avoids downloading the
2350 changesets twice if the incoming is followed by a pull.
2351 changesets twice if the incoming is followed by a pull.
2351
2352
2352 See pull for valid source format details.
2353 See pull for valid source format details.
2353
2354
2354 Returns 0 if there are incoming changes, 1 otherwise.
2355 Returns 0 if there are incoming changes, 1 otherwise.
2355 """
2356 """
2356 limit = cmdutil.loglimit(opts)
2357 limit = cmdutil.loglimit(opts)
2357 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2358 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2358 other = hg.repository(hg.remoteui(repo, opts), source)
2359 other = hg.repository(hg.remoteui(repo, opts), source)
2359 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2360 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2360 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2361 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2361 if revs:
2362 if revs:
2362 revs = [other.lookup(rev) for rev in revs]
2363 revs = [other.lookup(rev) for rev in revs]
2363
2364
2364 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2365 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2365 force=opts.get('force'))
2366 force=opts.get('force'))
2366 common, incoming, rheads = tmp
2367 common, incoming, rheads = tmp
2367 if not incoming:
2368 if not incoming:
2368 try:
2369 try:
2369 os.unlink(opts["bundle"])
2370 os.unlink(opts["bundle"])
2370 except:
2371 except:
2371 pass
2372 pass
2372 ui.status(_("no changes found\n"))
2373 ui.status(_("no changes found\n"))
2373 return 1
2374 return 1
2374
2375
2375 cleanup = None
2376 cleanup = None
2376 try:
2377 try:
2377 fname = opts["bundle"]
2378 fname = opts["bundle"]
2378 if fname or not other.local():
2379 if fname or not other.local():
2379 # create a bundle (uncompressed if other repo is not local)
2380 # create a bundle (uncompressed if other repo is not local)
2380
2381
2381 if revs is None and other.capable('changegroupsubset'):
2382 if revs is None and other.capable('changegroupsubset'):
2382 revs = rheads
2383 revs = rheads
2383
2384
2384 if revs is None:
2385 if revs is None:
2385 cg = other.changegroup(incoming, "incoming")
2386 cg = other.changegroup(incoming, "incoming")
2386 else:
2387 else:
2387 cg = other.changegroupsubset(incoming, revs, 'incoming')
2388 cg = other.changegroupsubset(incoming, revs, 'incoming')
2388 bundletype = other.local() and "HG10BZ" or "HG10UN"
2389 bundletype = other.local() and "HG10BZ" or "HG10UN"
2389 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2390 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2390 # keep written bundle?
2391 # keep written bundle?
2391 if opts["bundle"]:
2392 if opts["bundle"]:
2392 cleanup = None
2393 cleanup = None
2393 if not other.local():
2394 if not other.local():
2394 # use the created uncompressed bundlerepo
2395 # use the created uncompressed bundlerepo
2395 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2396 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2396
2397
2397 o = other.changelog.nodesbetween(incoming, revs)[0]
2398 o = other.changelog.nodesbetween(incoming, revs)[0]
2398 if opts.get('newest_first'):
2399 if opts.get('newest_first'):
2399 o.reverse()
2400 o.reverse()
2400 displayer = cmdutil.show_changeset(ui, other, opts)
2401 displayer = cmdutil.show_changeset(ui, other, opts)
2401 count = 0
2402 count = 0
2402 for n in o:
2403 for n in o:
2403 if limit is not None and count >= limit:
2404 if limit is not None and count >= limit:
2404 break
2405 break
2405 parents = [p for p in other.changelog.parents(n) if p != nullid]
2406 parents = [p for p in other.changelog.parents(n) if p != nullid]
2406 if opts.get('no_merges') and len(parents) == 2:
2407 if opts.get('no_merges') and len(parents) == 2:
2407 continue
2408 continue
2408 count += 1
2409 count += 1
2409 displayer.show(other[n])
2410 displayer.show(other[n])
2410 displayer.close()
2411 displayer.close()
2411 finally:
2412 finally:
2412 if hasattr(other, 'close'):
2413 if hasattr(other, 'close'):
2413 other.close()
2414 other.close()
2414 if cleanup:
2415 if cleanup:
2415 os.unlink(cleanup)
2416 os.unlink(cleanup)
2416
2417
2417 def init(ui, dest=".", **opts):
2418 def init(ui, dest=".", **opts):
2418 """create a new repository in the given directory
2419 """create a new repository in the given directory
2419
2420
2420 Initialize a new repository in the given directory. If the given
2421 Initialize a new repository in the given directory. If the given
2421 directory does not exist, it will be created.
2422 directory does not exist, it will be created.
2422
2423
2423 If no directory is given, the current directory is used.
2424 If no directory is given, the current directory is used.
2424
2425
2425 It is possible to specify an ``ssh://`` URL as the destination.
2426 It is possible to specify an ``ssh://`` URL as the destination.
2426 See :hg:`help urls` for more information.
2427 See :hg:`help urls` for more information.
2427
2428
2428 Returns 0 on success.
2429 Returns 0 on success.
2429 """
2430 """
2430 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2431 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2431
2432
2432 def locate(ui, repo, *pats, **opts):
2433 def locate(ui, repo, *pats, **opts):
2433 """locate files matching specific patterns
2434 """locate files matching specific patterns
2434
2435
2435 Print files under Mercurial control in the working directory whose
2436 Print files under Mercurial control in the working directory whose
2436 names match the given patterns.
2437 names match the given patterns.
2437
2438
2438 By default, this command searches all directories in the working
2439 By default, this command searches all directories in the working
2439 directory. To search just the current directory and its
2440 directory. To search just the current directory and its
2440 subdirectories, use "--include .".
2441 subdirectories, use "--include .".
2441
2442
2442 If no patterns are given to match, this command prints the names
2443 If no patterns are given to match, this command prints the names
2443 of all files under Mercurial control in the working directory.
2444 of all files under Mercurial control in the working directory.
2444
2445
2445 If you want to feed the output of this command into the "xargs"
2446 If you want to feed the output of this command into the "xargs"
2446 command, use the -0 option to both this command and "xargs". This
2447 command, use the -0 option to both this command and "xargs". This
2447 will avoid the problem of "xargs" treating single filenames that
2448 will avoid the problem of "xargs" treating single filenames that
2448 contain whitespace as multiple filenames.
2449 contain whitespace as multiple filenames.
2449
2450
2450 Returns 0 if a match is found, 1 otherwise.
2451 Returns 0 if a match is found, 1 otherwise.
2451 """
2452 """
2452 end = opts.get('print0') and '\0' or '\n'
2453 end = opts.get('print0') and '\0' or '\n'
2453 rev = opts.get('rev') or None
2454 rev = opts.get('rev') or None
2454
2455
2455 ret = 1
2456 ret = 1
2456 m = cmdutil.match(repo, pats, opts, default='relglob')
2457 m = cmdutil.match(repo, pats, opts, default='relglob')
2457 m.bad = lambda x, y: False
2458 m.bad = lambda x, y: False
2458 for abs in repo[rev].walk(m):
2459 for abs in repo[rev].walk(m):
2459 if not rev and abs not in repo.dirstate:
2460 if not rev and abs not in repo.dirstate:
2460 continue
2461 continue
2461 if opts.get('fullpath'):
2462 if opts.get('fullpath'):
2462 ui.write(repo.wjoin(abs), end)
2463 ui.write(repo.wjoin(abs), end)
2463 else:
2464 else:
2464 ui.write(((pats and m.rel(abs)) or abs), end)
2465 ui.write(((pats and m.rel(abs)) or abs), end)
2465 ret = 0
2466 ret = 0
2466
2467
2467 return ret
2468 return ret
2468
2469
2469 def log(ui, repo, *pats, **opts):
2470 def log(ui, repo, *pats, **opts):
2470 """show revision history of entire repository or files
2471 """show revision history of entire repository or files
2471
2472
2472 Print the revision history of the specified files or the entire
2473 Print the revision history of the specified files or the entire
2473 project.
2474 project.
2474
2475
2475 File history is shown without following rename or copy history of
2476 File history is shown without following rename or copy history of
2476 files. Use -f/--follow with a filename to follow history across
2477 files. Use -f/--follow with a filename to follow history across
2477 renames and copies. --follow without a filename will only show
2478 renames and copies. --follow without a filename will only show
2478 ancestors or descendants of the starting revision. --follow-first
2479 ancestors or descendants of the starting revision. --follow-first
2479 only follows the first parent of merge revisions.
2480 only follows the first parent of merge revisions.
2480
2481
2481 If no revision range is specified, the default is tip:0 unless
2482 If no revision range is specified, the default is tip:0 unless
2482 --follow is set, in which case the working directory parent is
2483 --follow is set, in which case the working directory parent is
2483 used as the starting revision. You can specify a revision set for
2484 used as the starting revision. You can specify a revision set for
2484 log, see :hg:`help revsets` for more information.
2485 log, see :hg:`help revsets` for more information.
2485
2486
2486 See :hg:`help dates` for a list of formats valid for -d/--date.
2487 See :hg:`help dates` for a list of formats valid for -d/--date.
2487
2488
2488 By default this command prints revision number and changeset id,
2489 By default this command prints revision number and changeset id,
2489 tags, non-trivial parents, user, date and time, and a summary for
2490 tags, non-trivial parents, user, date and time, and a summary for
2490 each commit. When the -v/--verbose switch is used, the list of
2491 each commit. When the -v/--verbose switch is used, the list of
2491 changed files and full commit message are shown.
2492 changed files and full commit message are shown.
2492
2493
2493 NOTE: log -p/--patch may generate unexpected diff output for merge
2494 NOTE: log -p/--patch may generate unexpected diff output for merge
2494 changesets, as it will only compare the merge changeset against
2495 changesets, as it will only compare the merge changeset against
2495 its first parent. Also, only files different from BOTH parents
2496 its first parent. Also, only files different from BOTH parents
2496 will appear in files:.
2497 will appear in files:.
2497
2498
2498 Returns 0 on success.
2499 Returns 0 on success.
2499 """
2500 """
2500
2501
2501 matchfn = cmdutil.match(repo, pats, opts)
2502 matchfn = cmdutil.match(repo, pats, opts)
2502 limit = cmdutil.loglimit(opts)
2503 limit = cmdutil.loglimit(opts)
2503 count = 0
2504 count = 0
2504
2505
2505 endrev = None
2506 endrev = None
2506 if opts.get('copies') and opts.get('rev'):
2507 if opts.get('copies') and opts.get('rev'):
2507 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2508 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2508
2509
2509 df = False
2510 df = False
2510 if opts["date"]:
2511 if opts["date"]:
2511 df = util.matchdate(opts["date"])
2512 df = util.matchdate(opts["date"])
2512
2513
2513 branches = opts.get('branch', []) + opts.get('only_branch', [])
2514 branches = opts.get('branch', []) + opts.get('only_branch', [])
2514 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2515 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2515
2516
2516 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2517 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2517 def prep(ctx, fns):
2518 def prep(ctx, fns):
2518 rev = ctx.rev()
2519 rev = ctx.rev()
2519 parents = [p for p in repo.changelog.parentrevs(rev)
2520 parents = [p for p in repo.changelog.parentrevs(rev)
2520 if p != nullrev]
2521 if p != nullrev]
2521 if opts.get('no_merges') and len(parents) == 2:
2522 if opts.get('no_merges') and len(parents) == 2:
2522 return
2523 return
2523 if opts.get('only_merges') and len(parents) != 2:
2524 if opts.get('only_merges') and len(parents) != 2:
2524 return
2525 return
2525 if opts.get('branch') and ctx.branch() not in opts['branch']:
2526 if opts.get('branch') and ctx.branch() not in opts['branch']:
2526 return
2527 return
2527 if df and not df(ctx.date()[0]):
2528 if df and not df(ctx.date()[0]):
2528 return
2529 return
2529 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2530 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2530 return
2531 return
2531 if opts.get('keyword'):
2532 if opts.get('keyword'):
2532 for k in [kw.lower() for kw in opts['keyword']]:
2533 for k in [kw.lower() for kw in opts['keyword']]:
2533 if (k in ctx.user().lower() or
2534 if (k in ctx.user().lower() or
2534 k in ctx.description().lower() or
2535 k in ctx.description().lower() or
2535 k in " ".join(ctx.files()).lower()):
2536 k in " ".join(ctx.files()).lower()):
2536 break
2537 break
2537 else:
2538 else:
2538 return
2539 return
2539
2540
2540 copies = None
2541 copies = None
2541 if opts.get('copies') and rev:
2542 if opts.get('copies') and rev:
2542 copies = []
2543 copies = []
2543 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2544 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2544 for fn in ctx.files():
2545 for fn in ctx.files():
2545 rename = getrenamed(fn, rev)
2546 rename = getrenamed(fn, rev)
2546 if rename:
2547 if rename:
2547 copies.append((fn, rename[0]))
2548 copies.append((fn, rename[0]))
2548
2549
2549 revmatchfn = None
2550 revmatchfn = None
2550 if opts.get('patch') or opts.get('stat'):
2551 if opts.get('patch') or opts.get('stat'):
2551 revmatchfn = cmdutil.match(repo, fns, default='path')
2552 revmatchfn = cmdutil.match(repo, fns, default='path')
2552
2553
2553 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2554 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2554
2555
2555 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2556 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2556 if count == limit:
2557 if count == limit:
2557 break
2558 break
2558 if displayer.flush(ctx.rev()):
2559 if displayer.flush(ctx.rev()):
2559 count += 1
2560 count += 1
2560 displayer.close()
2561 displayer.close()
2561
2562
2562 def manifest(ui, repo, node=None, rev=None):
2563 def manifest(ui, repo, node=None, rev=None):
2563 """output the current or given revision of the project manifest
2564 """output the current or given revision of the project manifest
2564
2565
2565 Print a list of version controlled files for the given revision.
2566 Print a list of version controlled files for the given revision.
2566 If no revision is given, the first parent of the working directory
2567 If no revision is given, the first parent of the working directory
2567 is used, or the null revision if no revision is checked out.
2568 is used, or the null revision if no revision is checked out.
2568
2569
2569 With -v, print file permissions, symlink and executable bits.
2570 With -v, print file permissions, symlink and executable bits.
2570 With --debug, print file revision hashes.
2571 With --debug, print file revision hashes.
2571
2572
2572 Returns 0 on success.
2573 Returns 0 on success.
2573 """
2574 """
2574
2575
2575 if rev and node:
2576 if rev and node:
2576 raise util.Abort(_("please specify just one revision"))
2577 raise util.Abort(_("please specify just one revision"))
2577
2578
2578 if not node:
2579 if not node:
2579 node = rev
2580 node = rev
2580
2581
2581 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2582 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2582 ctx = repo[node]
2583 ctx = repo[node]
2583 for f in ctx:
2584 for f in ctx:
2584 if ui.debugflag:
2585 if ui.debugflag:
2585 ui.write("%40s " % hex(ctx.manifest()[f]))
2586 ui.write("%40s " % hex(ctx.manifest()[f]))
2586 if ui.verbose:
2587 if ui.verbose:
2587 ui.write(decor[ctx.flags(f)])
2588 ui.write(decor[ctx.flags(f)])
2588 ui.write("%s\n" % f)
2589 ui.write("%s\n" % f)
2589
2590
2590 def merge(ui, repo, node=None, **opts):
2591 def merge(ui, repo, node=None, **opts):
2591 """merge working directory with another revision
2592 """merge working directory with another revision
2592
2593
2593 The current working directory is updated with all changes made in
2594 The current working directory is updated with all changes made in
2594 the requested revision since the last common predecessor revision.
2595 the requested revision since the last common predecessor revision.
2595
2596
2596 Files that changed between either parent are marked as changed for
2597 Files that changed between either parent are marked as changed for
2597 the next commit and a commit must be performed before any further
2598 the next commit and a commit must be performed before any further
2598 updates to the repository are allowed. The next commit will have
2599 updates to the repository are allowed. The next commit will have
2599 two parents.
2600 two parents.
2600
2601
2601 If no revision is specified, the working directory's parent is a
2602 If no revision is specified, the working directory's parent is a
2602 head revision, and the current branch contains exactly one other
2603 head revision, and the current branch contains exactly one other
2603 head, the other head is merged with by default. Otherwise, an
2604 head, the other head is merged with by default. Otherwise, an
2604 explicit revision with which to merge with must be provided.
2605 explicit revision with which to merge with must be provided.
2605
2606
2606 To undo an uncommitted merge, use :hg:`update --clean .` which
2607 To undo an uncommitted merge, use :hg:`update --clean .` which
2607 will check out a clean copy of the original merge parent, losing
2608 will check out a clean copy of the original merge parent, losing
2608 all changes.
2609 all changes.
2609
2610
2610 Returns 0 on success, 1 if there are unresolved files.
2611 Returns 0 on success, 1 if there are unresolved files.
2611 """
2612 """
2612
2613
2613 if opts.get('rev') and node:
2614 if opts.get('rev') and node:
2614 raise util.Abort(_("please specify just one revision"))
2615 raise util.Abort(_("please specify just one revision"))
2615 if not node:
2616 if not node:
2616 node = opts.get('rev')
2617 node = opts.get('rev')
2617
2618
2618 if not node:
2619 if not node:
2619 branch = repo.changectx(None).branch()
2620 branch = repo.changectx(None).branch()
2620 bheads = repo.branchheads(branch)
2621 bheads = repo.branchheads(branch)
2621 if len(bheads) > 2:
2622 if len(bheads) > 2:
2622 raise util.Abort(_(
2623 raise util.Abort(_(
2623 'branch \'%s\' has %d heads - '
2624 'branch \'%s\' has %d heads - '
2624 'please merge with an explicit rev\n'
2625 'please merge with an explicit rev\n'
2625 '(run \'hg heads .\' to see heads)')
2626 '(run \'hg heads .\' to see heads)')
2626 % (branch, len(bheads)))
2627 % (branch, len(bheads)))
2627
2628
2628 parent = repo.dirstate.parents()[0]
2629 parent = repo.dirstate.parents()[0]
2629 if len(bheads) == 1:
2630 if len(bheads) == 1:
2630 if len(repo.heads()) > 1:
2631 if len(repo.heads()) > 1:
2631 raise util.Abort(_(
2632 raise util.Abort(_(
2632 'branch \'%s\' has one head - '
2633 'branch \'%s\' has one head - '
2633 'please merge with an explicit rev\n'
2634 'please merge with an explicit rev\n'
2634 '(run \'hg heads\' to see all heads)')
2635 '(run \'hg heads\' to see all heads)')
2635 % branch)
2636 % branch)
2636 msg = _('there is nothing to merge')
2637 msg = _('there is nothing to merge')
2637 if parent != repo.lookup(repo[None].branch()):
2638 if parent != repo.lookup(repo[None].branch()):
2638 msg = _('%s - use "hg update" instead') % msg
2639 msg = _('%s - use "hg update" instead') % msg
2639 raise util.Abort(msg)
2640 raise util.Abort(msg)
2640
2641
2641 if parent not in bheads:
2642 if parent not in bheads:
2642 raise util.Abort(_('working dir not at a head rev - '
2643 raise util.Abort(_('working dir not at a head rev - '
2643 'use "hg update" or merge with an explicit rev'))
2644 'use "hg update" or merge with an explicit rev'))
2644 node = parent == bheads[0] and bheads[-1] or bheads[0]
2645 node = parent == bheads[0] and bheads[-1] or bheads[0]
2645
2646
2646 if opts.get('preview'):
2647 if opts.get('preview'):
2647 # find nodes that are ancestors of p2 but not of p1
2648 # find nodes that are ancestors of p2 but not of p1
2648 p1 = repo.lookup('.')
2649 p1 = repo.lookup('.')
2649 p2 = repo.lookup(node)
2650 p2 = repo.lookup(node)
2650 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2651 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2651
2652
2652 displayer = cmdutil.show_changeset(ui, repo, opts)
2653 displayer = cmdutil.show_changeset(ui, repo, opts)
2653 for node in nodes:
2654 for node in nodes:
2654 displayer.show(repo[node])
2655 displayer.show(repo[node])
2655 displayer.close()
2656 displayer.close()
2656 return 0
2657 return 0
2657
2658
2658 return hg.merge(repo, node, force=opts.get('force'))
2659 return hg.merge(repo, node, force=opts.get('force'))
2659
2660
2660 def outgoing(ui, repo, dest=None, **opts):
2661 def outgoing(ui, repo, dest=None, **opts):
2661 """show changesets not found in the destination
2662 """show changesets not found in the destination
2662
2663
2663 Show changesets not found in the specified destination repository
2664 Show changesets not found in the specified destination repository
2664 or the default push location. These are the changesets that would
2665 or the default push location. These are the changesets that would
2665 be pushed if a push was requested.
2666 be pushed if a push was requested.
2666
2667
2667 See pull for details of valid destination formats.
2668 See pull for details of valid destination formats.
2668
2669
2669 Returns 0 if there are outgoing changes, 1 otherwise.
2670 Returns 0 if there are outgoing changes, 1 otherwise.
2670 """
2671 """
2671 limit = cmdutil.loglimit(opts)
2672 limit = cmdutil.loglimit(opts)
2672 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2673 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2673 dest, branches = hg.parseurl(dest, opts.get('branch'))
2674 dest, branches = hg.parseurl(dest, opts.get('branch'))
2674 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2675 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2675 if revs:
2676 if revs:
2676 revs = [repo.lookup(rev) for rev in revs]
2677 revs = [repo.lookup(rev) for rev in revs]
2677
2678
2678 other = hg.repository(hg.remoteui(repo, opts), dest)
2679 other = hg.repository(hg.remoteui(repo, opts), dest)
2679 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2680 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2680 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2681 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2681 if not o:
2682 if not o:
2682 ui.status(_("no changes found\n"))
2683 ui.status(_("no changes found\n"))
2683 return 1
2684 return 1
2684 o = repo.changelog.nodesbetween(o, revs)[0]
2685 o = repo.changelog.nodesbetween(o, revs)[0]
2685 if opts.get('newest_first'):
2686 if opts.get('newest_first'):
2686 o.reverse()
2687 o.reverse()
2687 displayer = cmdutil.show_changeset(ui, repo, opts)
2688 displayer = cmdutil.show_changeset(ui, repo, opts)
2688 count = 0
2689 count = 0
2689 for n in o:
2690 for n in o:
2690 if limit is not None and count >= limit:
2691 if limit is not None and count >= limit:
2691 break
2692 break
2692 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2693 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2693 if opts.get('no_merges') and len(parents) == 2:
2694 if opts.get('no_merges') and len(parents) == 2:
2694 continue
2695 continue
2695 count += 1
2696 count += 1
2696 displayer.show(repo[n])
2697 displayer.show(repo[n])
2697 displayer.close()
2698 displayer.close()
2698
2699
2699 def parents(ui, repo, file_=None, **opts):
2700 def parents(ui, repo, file_=None, **opts):
2700 """show the parents of the working directory or revision
2701 """show the parents of the working directory or revision
2701
2702
2702 Print the working directory's parent revisions. If a revision is
2703 Print the working directory's parent revisions. If a revision is
2703 given via -r/--rev, the parent of that revision will be printed.
2704 given via -r/--rev, the parent of that revision will be printed.
2704 If a file argument is given, the revision in which the file was
2705 If a file argument is given, the revision in which the file was
2705 last changed (before the working directory revision or the
2706 last changed (before the working directory revision or the
2706 argument to --rev if given) is printed.
2707 argument to --rev if given) is printed.
2707
2708
2708 Returns 0 on success.
2709 Returns 0 on success.
2709 """
2710 """
2710 rev = opts.get('rev')
2711 rev = opts.get('rev')
2711 if rev:
2712 if rev:
2712 ctx = repo[rev]
2713 ctx = repo[rev]
2713 else:
2714 else:
2714 ctx = repo[None]
2715 ctx = repo[None]
2715
2716
2716 if file_:
2717 if file_:
2717 m = cmdutil.match(repo, (file_,), opts)
2718 m = cmdutil.match(repo, (file_,), opts)
2718 if m.anypats() or len(m.files()) != 1:
2719 if m.anypats() or len(m.files()) != 1:
2719 raise util.Abort(_('can only specify an explicit filename'))
2720 raise util.Abort(_('can only specify an explicit filename'))
2720 file_ = m.files()[0]
2721 file_ = m.files()[0]
2721 filenodes = []
2722 filenodes = []
2722 for cp in ctx.parents():
2723 for cp in ctx.parents():
2723 if not cp:
2724 if not cp:
2724 continue
2725 continue
2725 try:
2726 try:
2726 filenodes.append(cp.filenode(file_))
2727 filenodes.append(cp.filenode(file_))
2727 except error.LookupError:
2728 except error.LookupError:
2728 pass
2729 pass
2729 if not filenodes:
2730 if not filenodes:
2730 raise util.Abort(_("'%s' not found in manifest!") % file_)
2731 raise util.Abort(_("'%s' not found in manifest!") % file_)
2731 fl = repo.file(file_)
2732 fl = repo.file(file_)
2732 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2733 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2733 else:
2734 else:
2734 p = [cp.node() for cp in ctx.parents()]
2735 p = [cp.node() for cp in ctx.parents()]
2735
2736
2736 displayer = cmdutil.show_changeset(ui, repo, opts)
2737 displayer = cmdutil.show_changeset(ui, repo, opts)
2737 for n in p:
2738 for n in p:
2738 if n != nullid:
2739 if n != nullid:
2739 displayer.show(repo[n])
2740 displayer.show(repo[n])
2740 displayer.close()
2741 displayer.close()
2741
2742
2742 def paths(ui, repo, search=None):
2743 def paths(ui, repo, search=None):
2743 """show aliases for remote repositories
2744 """show aliases for remote repositories
2744
2745
2745 Show definition of symbolic path name NAME. If no name is given,
2746 Show definition of symbolic path name NAME. If no name is given,
2746 show definition of all available names.
2747 show definition of all available names.
2747
2748
2748 Path names are defined in the [paths] section of your
2749 Path names are defined in the [paths] section of your
2749 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2750 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2750 repository, ``.hg/hgrc`` is used, too.
2751 repository, ``.hg/hgrc`` is used, too.
2751
2752
2752 The path names ``default`` and ``default-push`` have a special
2753 The path names ``default`` and ``default-push`` have a special
2753 meaning. When performing a push or pull operation, they are used
2754 meaning. When performing a push or pull operation, they are used
2754 as fallbacks if no location is specified on the command-line.
2755 as fallbacks if no location is specified on the command-line.
2755 When ``default-push`` is set, it will be used for push and
2756 When ``default-push`` is set, it will be used for push and
2756 ``default`` will be used for pull; otherwise ``default`` is used
2757 ``default`` will be used for pull; otherwise ``default`` is used
2757 as the fallback for both. When cloning a repository, the clone
2758 as the fallback for both. When cloning a repository, the clone
2758 source is written as ``default`` in ``.hg/hgrc``. Note that
2759 source is written as ``default`` in ``.hg/hgrc``. Note that
2759 ``default`` and ``default-push`` apply to all inbound (e.g.
2760 ``default`` and ``default-push`` apply to all inbound (e.g.
2760 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2761 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2761 :hg:`bundle`) operations.
2762 :hg:`bundle`) operations.
2762
2763
2763 See :hg:`help urls` for more information.
2764 See :hg:`help urls` for more information.
2764
2765
2765 Returns 0 on success.
2766 Returns 0 on success.
2766 """
2767 """
2767 if search:
2768 if search:
2768 for name, path in ui.configitems("paths"):
2769 for name, path in ui.configitems("paths"):
2769 if name == search:
2770 if name == search:
2770 ui.write("%s\n" % url.hidepassword(path))
2771 ui.write("%s\n" % url.hidepassword(path))
2771 return
2772 return
2772 ui.warn(_("not found!\n"))
2773 ui.warn(_("not found!\n"))
2773 return 1
2774 return 1
2774 else:
2775 else:
2775 for name, path in ui.configitems("paths"):
2776 for name, path in ui.configitems("paths"):
2776 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2777 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2777
2778
2778 def postincoming(ui, repo, modheads, optupdate, checkout):
2779 def postincoming(ui, repo, modheads, optupdate, checkout):
2779 if modheads == 0:
2780 if modheads == 0:
2780 return
2781 return
2781 if optupdate:
2782 if optupdate:
2782 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2783 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2783 return hg.update(repo, checkout)
2784 return hg.update(repo, checkout)
2784 else:
2785 else:
2785 ui.status(_("not updating, since new heads added\n"))
2786 ui.status(_("not updating, since new heads added\n"))
2786 if modheads > 1:
2787 if modheads > 1:
2787 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2788 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2788 else:
2789 else:
2789 ui.status(_("(run 'hg update' to get a working copy)\n"))
2790 ui.status(_("(run 'hg update' to get a working copy)\n"))
2790
2791
2791 def pull(ui, repo, source="default", **opts):
2792 def pull(ui, repo, source="default", **opts):
2792 """pull changes from the specified source
2793 """pull changes from the specified source
2793
2794
2794 Pull changes from a remote repository to a local one.
2795 Pull changes from a remote repository to a local one.
2795
2796
2796 This finds all changes from the repository at the specified path
2797 This finds all changes from the repository at the specified path
2797 or URL and adds them to a local repository (the current one unless
2798 or URL and adds them to a local repository (the current one unless
2798 -R is specified). By default, this does not update the copy of the
2799 -R is specified). By default, this does not update the copy of the
2799 project in the working directory.
2800 project in the working directory.
2800
2801
2801 Use :hg:`incoming` if you want to see what would have been added
2802 Use :hg:`incoming` if you want to see what would have been added
2802 by a pull at the time you issued this command. If you then decide
2803 by a pull at the time you issued this command. If you then decide
2803 to add those changes to the repository, you should use :hg:`pull
2804 to add those changes to the repository, you should use :hg:`pull
2804 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2805 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2805
2806
2806 If SOURCE is omitted, the 'default' path will be used.
2807 If SOURCE is omitted, the 'default' path will be used.
2807 See :hg:`help urls` for more information.
2808 See :hg:`help urls` for more information.
2808
2809
2809 Returns 0 on success, 1 if an update had unresolved files.
2810 Returns 0 on success, 1 if an update had unresolved files.
2810 """
2811 """
2811 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2812 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2812 other = hg.repository(hg.remoteui(repo, opts), source)
2813 other = hg.repository(hg.remoteui(repo, opts), source)
2813 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2814 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2814 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2815 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2815 if revs:
2816 if revs:
2816 try:
2817 try:
2817 revs = [other.lookup(rev) for rev in revs]
2818 revs = [other.lookup(rev) for rev in revs]
2818 except error.CapabilityError:
2819 except error.CapabilityError:
2819 err = _("other repository doesn't support revision lookup, "
2820 err = _("other repository doesn't support revision lookup, "
2820 "so a rev cannot be specified.")
2821 "so a rev cannot be specified.")
2821 raise util.Abort(err)
2822 raise util.Abort(err)
2822
2823
2823 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2824 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2824 if checkout:
2825 if checkout:
2825 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2826 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2826 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2827 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2827
2828
2828 def push(ui, repo, dest=None, **opts):
2829 def push(ui, repo, dest=None, **opts):
2829 """push changes to the specified destination
2830 """push changes to the specified destination
2830
2831
2831 Push changesets from the local repository to the specified
2832 Push changesets from the local repository to the specified
2832 destination.
2833 destination.
2833
2834
2834 This operation is symmetrical to pull: it is identical to a pull
2835 This operation is symmetrical to pull: it is identical to a pull
2835 in the destination repository from the current one.
2836 in the destination repository from the current one.
2836
2837
2837 By default, push will not allow creation of new heads at the
2838 By default, push will not allow creation of new heads at the
2838 destination, since multiple heads would make it unclear which head
2839 destination, since multiple heads would make it unclear which head
2839 to use. In this situation, it is recommended to pull and merge
2840 to use. In this situation, it is recommended to pull and merge
2840 before pushing.
2841 before pushing.
2841
2842
2842 Use --new-branch if you want to allow push to create a new named
2843 Use --new-branch if you want to allow push to create a new named
2843 branch that is not present at the destination. This allows you to
2844 branch that is not present at the destination. This allows you to
2844 only create a new branch without forcing other changes.
2845 only create a new branch without forcing other changes.
2845
2846
2846 Use -f/--force to override the default behavior and push all
2847 Use -f/--force to override the default behavior and push all
2847 changesets on all branches.
2848 changesets on all branches.
2848
2849
2849 If -r/--rev is used, the specified revision and all its ancestors
2850 If -r/--rev is used, the specified revision and all its ancestors
2850 will be pushed to the remote repository.
2851 will be pushed to the remote repository.
2851
2852
2852 Please see :hg:`help urls` for important details about ``ssh://``
2853 Please see :hg:`help urls` for important details about ``ssh://``
2853 URLs. If DESTINATION is omitted, a default path will be used.
2854 URLs. If DESTINATION is omitted, a default path will be used.
2854
2855
2855 Returns 0 if push was successful, 1 if nothing to push.
2856 Returns 0 if push was successful, 1 if nothing to push.
2856 """
2857 """
2857 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2858 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2858 dest, branches = hg.parseurl(dest, opts.get('branch'))
2859 dest, branches = hg.parseurl(dest, opts.get('branch'))
2859 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2860 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2860 other = hg.repository(hg.remoteui(repo, opts), dest)
2861 other = hg.repository(hg.remoteui(repo, opts), dest)
2861 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2862 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2862 if revs:
2863 if revs:
2863 revs = [repo.lookup(rev) for rev in revs]
2864 revs = [repo.lookup(rev) for rev in revs]
2864
2865
2865 # push subrepos depth-first for coherent ordering
2866 # push subrepos depth-first for coherent ordering
2866 c = repo['']
2867 c = repo['']
2867 subs = c.substate # only repos that are committed
2868 subs = c.substate # only repos that are committed
2868 for s in sorted(subs):
2869 for s in sorted(subs):
2869 if not c.sub(s).push(opts.get('force')):
2870 if not c.sub(s).push(opts.get('force')):
2870 return False
2871 return False
2871
2872
2872 r = repo.push(other, opts.get('force'), revs=revs,
2873 r = repo.push(other, opts.get('force'), revs=revs,
2873 newbranch=opts.get('new_branch'))
2874 newbranch=opts.get('new_branch'))
2874 return r == 0
2875 return r == 0
2875
2876
2876 def recover(ui, repo):
2877 def recover(ui, repo):
2877 """roll back an interrupted transaction
2878 """roll back an interrupted transaction
2878
2879
2879 Recover from an interrupted commit or pull.
2880 Recover from an interrupted commit or pull.
2880
2881
2881 This command tries to fix the repository status after an
2882 This command tries to fix the repository status after an
2882 interrupted operation. It should only be necessary when Mercurial
2883 interrupted operation. It should only be necessary when Mercurial
2883 suggests it.
2884 suggests it.
2884
2885
2885 Returns 0 if successful, 1 if nothing to recover or verify fails.
2886 Returns 0 if successful, 1 if nothing to recover or verify fails.
2886 """
2887 """
2887 if repo.recover():
2888 if repo.recover():
2888 return hg.verify(repo)
2889 return hg.verify(repo)
2889 return 1
2890 return 1
2890
2891
2891 def remove(ui, repo, *pats, **opts):
2892 def remove(ui, repo, *pats, **opts):
2892 """remove the specified files on the next commit
2893 """remove the specified files on the next commit
2893
2894
2894 Schedule the indicated files for removal from the repository.
2895 Schedule the indicated files for removal from the repository.
2895
2896
2896 This only removes files from the current branch, not from the
2897 This only removes files from the current branch, not from the
2897 entire project history. -A/--after can be used to remove only
2898 entire project history. -A/--after can be used to remove only
2898 files that have already been deleted, -f/--force can be used to
2899 files that have already been deleted, -f/--force can be used to
2899 force deletion, and -Af can be used to remove files from the next
2900 force deletion, and -Af can be used to remove files from the next
2900 revision without deleting them from the working directory.
2901 revision without deleting them from the working directory.
2901
2902
2902 The following table details the behavior of remove for different
2903 The following table details the behavior of remove for different
2903 file states (columns) and option combinations (rows). The file
2904 file states (columns) and option combinations (rows). The file
2904 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2905 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2905 reported by :hg:`status`). The actions are Warn, Remove (from
2906 reported by :hg:`status`). The actions are Warn, Remove (from
2906 branch) and Delete (from disk)::
2907 branch) and Delete (from disk)::
2907
2908
2908 A C M !
2909 A C M !
2909 none W RD W R
2910 none W RD W R
2910 -f R RD RD R
2911 -f R RD RD R
2911 -A W W W R
2912 -A W W W R
2912 -Af R R R R
2913 -Af R R R R
2913
2914
2914 This command schedules the files to be removed at the next commit.
2915 This command schedules the files to be removed at the next commit.
2915 To undo a remove before that, see :hg:`revert`.
2916 To undo a remove before that, see :hg:`revert`.
2916
2917
2917 Returns 0 on success, 1 if any warnings encountered.
2918 Returns 0 on success, 1 if any warnings encountered.
2918 """
2919 """
2919
2920
2920 ret = 0
2921 ret = 0
2921 after, force = opts.get('after'), opts.get('force')
2922 after, force = opts.get('after'), opts.get('force')
2922 if not pats and not after:
2923 if not pats and not after:
2923 raise util.Abort(_('no files specified'))
2924 raise util.Abort(_('no files specified'))
2924
2925
2925 m = cmdutil.match(repo, pats, opts)
2926 m = cmdutil.match(repo, pats, opts)
2926 s = repo.status(match=m, clean=True)
2927 s = repo.status(match=m, clean=True)
2927 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2928 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2928
2929
2929 for f in m.files():
2930 for f in m.files():
2930 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2931 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2931 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2932 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2932 ret = 1
2933 ret = 1
2933
2934
2934 if force:
2935 if force:
2935 remove, forget = modified + deleted + clean, added
2936 remove, forget = modified + deleted + clean, added
2936 elif after:
2937 elif after:
2937 remove, forget = deleted, []
2938 remove, forget = deleted, []
2938 for f in modified + added + clean:
2939 for f in modified + added + clean:
2939 ui.warn(_('not removing %s: file still exists (use -f'
2940 ui.warn(_('not removing %s: file still exists (use -f'
2940 ' to force removal)\n') % m.rel(f))
2941 ' to force removal)\n') % m.rel(f))
2941 ret = 1
2942 ret = 1
2942 else:
2943 else:
2943 remove, forget = deleted + clean, []
2944 remove, forget = deleted + clean, []
2944 for f in modified:
2945 for f in modified:
2945 ui.warn(_('not removing %s: file is modified (use -f'
2946 ui.warn(_('not removing %s: file is modified (use -f'
2946 ' to force removal)\n') % m.rel(f))
2947 ' to force removal)\n') % m.rel(f))
2947 ret = 1
2948 ret = 1
2948 for f in added:
2949 for f in added:
2949 ui.warn(_('not removing %s: file has been marked for add (use -f'
2950 ui.warn(_('not removing %s: file has been marked for add (use -f'
2950 ' to force removal)\n') % m.rel(f))
2951 ' to force removal)\n') % m.rel(f))
2951 ret = 1
2952 ret = 1
2952
2953
2953 for f in sorted(remove + forget):
2954 for f in sorted(remove + forget):
2954 if ui.verbose or not m.exact(f):
2955 if ui.verbose or not m.exact(f):
2955 ui.status(_('removing %s\n') % m.rel(f))
2956 ui.status(_('removing %s\n') % m.rel(f))
2956
2957
2957 repo[None].forget(forget)
2958 repo[None].forget(forget)
2958 repo[None].remove(remove, unlink=not after)
2959 repo[None].remove(remove, unlink=not after)
2959 return ret
2960 return ret
2960
2961
2961 def rename(ui, repo, *pats, **opts):
2962 def rename(ui, repo, *pats, **opts):
2962 """rename files; equivalent of copy + remove
2963 """rename files; equivalent of copy + remove
2963
2964
2964 Mark dest as copies of sources; mark sources for deletion. If dest
2965 Mark dest as copies of sources; mark sources for deletion. If dest
2965 is a directory, copies are put in that directory. If dest is a
2966 is a directory, copies are put in that directory. If dest is a
2966 file, there can only be one source.
2967 file, there can only be one source.
2967
2968
2968 By default, this command copies the contents of files as they
2969 By default, this command copies the contents of files as they
2969 exist in the working directory. If invoked with -A/--after, the
2970 exist in the working directory. If invoked with -A/--after, the
2970 operation is recorded, but no copying is performed.
2971 operation is recorded, but no copying is performed.
2971
2972
2972 This command takes effect at the next commit. To undo a rename
2973 This command takes effect at the next commit. To undo a rename
2973 before that, see :hg:`revert`.
2974 before that, see :hg:`revert`.
2974
2975
2975 Returns 0 on success, 1 if errors are encountered.
2976 Returns 0 on success, 1 if errors are encountered.
2976 """
2977 """
2977 wlock = repo.wlock(False)
2978 wlock = repo.wlock(False)
2978 try:
2979 try:
2979 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2980 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2980 finally:
2981 finally:
2981 wlock.release()
2982 wlock.release()
2982
2983
2983 def resolve(ui, repo, *pats, **opts):
2984 def resolve(ui, repo, *pats, **opts):
2984 """redo merges or set/view the merge status of files
2985 """redo merges or set/view the merge status of files
2985
2986
2986 Merges with unresolved conflicts are often the result of
2987 Merges with unresolved conflicts are often the result of
2987 non-interactive merging using the ``internal:merge`` configuration
2988 non-interactive merging using the ``internal:merge`` configuration
2988 setting, or a command-line merge tool like ``diff3``. The resolve
2989 setting, or a command-line merge tool like ``diff3``. The resolve
2989 command is used to manage the files involved in a merge, after
2990 command is used to manage the files involved in a merge, after
2990 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2991 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2991 working directory must have two parents).
2992 working directory must have two parents).
2992
2993
2993 The resolve command can be used in the following ways:
2994 The resolve command can be used in the following ways:
2994
2995
2995 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2996 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2996 discarding any previous merge attempts. Re-merging is not
2997 discarding any previous merge attempts. Re-merging is not
2997 performed for files already marked as resolved. Use ``--all/-a``
2998 performed for files already marked as resolved. Use ``--all/-a``
2998 to selects all unresolved files.
2999 to selects all unresolved files.
2999
3000
3000 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3001 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3001 (e.g. after having manually fixed-up the files). The default is
3002 (e.g. after having manually fixed-up the files). The default is
3002 to mark all unresolved files.
3003 to mark all unresolved files.
3003
3004
3004 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3005 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3005 default is to mark all resolved files.
3006 default is to mark all resolved files.
3006
3007
3007 - :hg:`resolve -l`: list files which had or still have conflicts.
3008 - :hg:`resolve -l`: list files which had or still have conflicts.
3008 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3009 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3009
3010
3010 Note that Mercurial will not let you commit files with unresolved
3011 Note that Mercurial will not let you commit files with unresolved
3011 merge conflicts. You must use :hg:`resolve -m ...` before you can
3012 merge conflicts. You must use :hg:`resolve -m ...` before you can
3012 commit after a conflicting merge.
3013 commit after a conflicting merge.
3013
3014
3014 Returns 0 on success, 1 if any files fail a resolve attempt.
3015 Returns 0 on success, 1 if any files fail a resolve attempt.
3015 """
3016 """
3016
3017
3017 all, mark, unmark, show, nostatus = \
3018 all, mark, unmark, show, nostatus = \
3018 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3019 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3019
3020
3020 if (show and (mark or unmark)) or (mark and unmark):
3021 if (show and (mark or unmark)) or (mark and unmark):
3021 raise util.Abort(_("too many options specified"))
3022 raise util.Abort(_("too many options specified"))
3022 if pats and all:
3023 if pats and all:
3023 raise util.Abort(_("can't specify --all and patterns"))
3024 raise util.Abort(_("can't specify --all and patterns"))
3024 if not (all or pats or show or mark or unmark):
3025 if not (all or pats or show or mark or unmark):
3025 raise util.Abort(_('no files or directories specified; '
3026 raise util.Abort(_('no files or directories specified; '
3026 'use --all to remerge all files'))
3027 'use --all to remerge all files'))
3027
3028
3028 ms = mergemod.mergestate(repo)
3029 ms = mergemod.mergestate(repo)
3029 m = cmdutil.match(repo, pats, opts)
3030 m = cmdutil.match(repo, pats, opts)
3030 ret = 0
3031 ret = 0
3031
3032
3032 for f in ms:
3033 for f in ms:
3033 if m(f):
3034 if m(f):
3034 if show:
3035 if show:
3035 if nostatus:
3036 if nostatus:
3036 ui.write("%s\n" % f)
3037 ui.write("%s\n" % f)
3037 else:
3038 else:
3038 ui.write("%s %s\n" % (ms[f].upper(), f),
3039 ui.write("%s %s\n" % (ms[f].upper(), f),
3039 label='resolve.' +
3040 label='resolve.' +
3040 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3041 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3041 elif mark:
3042 elif mark:
3042 ms.mark(f, "r")
3043 ms.mark(f, "r")
3043 elif unmark:
3044 elif unmark:
3044 ms.mark(f, "u")
3045 ms.mark(f, "u")
3045 else:
3046 else:
3046 wctx = repo[None]
3047 wctx = repo[None]
3047 mctx = wctx.parents()[-1]
3048 mctx = wctx.parents()[-1]
3048
3049
3049 # backup pre-resolve (merge uses .orig for its own purposes)
3050 # backup pre-resolve (merge uses .orig for its own purposes)
3050 a = repo.wjoin(f)
3051 a = repo.wjoin(f)
3051 util.copyfile(a, a + ".resolve")
3052 util.copyfile(a, a + ".resolve")
3052
3053
3053 # resolve file
3054 # resolve file
3054 if ms.resolve(f, wctx, mctx):
3055 if ms.resolve(f, wctx, mctx):
3055 ret = 1
3056 ret = 1
3056
3057
3057 # replace filemerge's .orig file with our resolve file
3058 # replace filemerge's .orig file with our resolve file
3058 util.rename(a + ".resolve", a + ".orig")
3059 util.rename(a + ".resolve", a + ".orig")
3059 return ret
3060 return ret
3060
3061
3061 def revert(ui, repo, *pats, **opts):
3062 def revert(ui, repo, *pats, **opts):
3062 """restore individual files or directories to an earlier state
3063 """restore individual files or directories to an earlier state
3063
3064
3064 NOTE: This command is most likely not what you are looking for. revert
3065 NOTE: This command is most likely not what you are looking for. revert
3065 will partially overwrite content in the working directory without changing
3066 will partially overwrite content in the working directory without changing
3066 the working directory parents. Use :hg:`update -r rev` to check out earlier
3067 the working directory parents. Use :hg:`update -r rev` to check out earlier
3067 revisions, or :hg:`update --clean .` to undo a merge which has added
3068 revisions, or :hg:`update --clean .` to undo a merge which has added
3068 another parent.
3069 another parent.
3069
3070
3070 With no revision specified, revert the named files or directories
3071 With no revision specified, revert the named files or directories
3071 to the contents they had in the parent of the working directory.
3072 to the contents they had in the parent of the working directory.
3072 This restores the contents of the affected files to an unmodified
3073 This restores the contents of the affected files to an unmodified
3073 state and unschedules adds, removes, copies, and renames. If the
3074 state and unschedules adds, removes, copies, and renames. If the
3074 working directory has two parents, you must explicitly specify a
3075 working directory has two parents, you must explicitly specify a
3075 revision.
3076 revision.
3076
3077
3077 Using the -r/--rev option, revert the given files or directories
3078 Using the -r/--rev option, revert the given files or directories
3078 to their contents as of a specific revision. This can be helpful
3079 to their contents as of a specific revision. This can be helpful
3079 to "roll back" some or all of an earlier change. See :hg:`help
3080 to "roll back" some or all of an earlier change. See :hg:`help
3080 dates` for a list of formats valid for -d/--date.
3081 dates` for a list of formats valid for -d/--date.
3081
3082
3082 Revert modifies the working directory. It does not commit any
3083 Revert modifies the working directory. It does not commit any
3083 changes, or change the parent of the working directory. If you
3084 changes, or change the parent of the working directory. If you
3084 revert to a revision other than the parent of the working
3085 revert to a revision other than the parent of the working
3085 directory, the reverted files will thus appear modified
3086 directory, the reverted files will thus appear modified
3086 afterwards.
3087 afterwards.
3087
3088
3088 If a file has been deleted, it is restored. If the executable mode
3089 If a file has been deleted, it is restored. If the executable mode
3089 of a file was changed, it is reset.
3090 of a file was changed, it is reset.
3090
3091
3091 If names are given, all files matching the names are reverted.
3092 If names are given, all files matching the names are reverted.
3092 If no arguments are given, no files are reverted.
3093 If no arguments are given, no files are reverted.
3093
3094
3094 Modified files are saved with a .orig suffix before reverting.
3095 Modified files are saved with a .orig suffix before reverting.
3095 To disable these backups, use --no-backup.
3096 To disable these backups, use --no-backup.
3096
3097
3097 Returns 0 on success.
3098 Returns 0 on success.
3098 """
3099 """
3099
3100
3100 if opts.get("date"):
3101 if opts.get("date"):
3101 if opts.get("rev"):
3102 if opts.get("rev"):
3102 raise util.Abort(_("you can't specify a revision and a date"))
3103 raise util.Abort(_("you can't specify a revision and a date"))
3103 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3104 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3104
3105
3105 if not pats and not opts.get('all'):
3106 if not pats and not opts.get('all'):
3106 raise util.Abort(_('no files or directories specified; '
3107 raise util.Abort(_('no files or directories specified; '
3107 'use --all to revert the whole repo'))
3108 'use --all to revert the whole repo'))
3108
3109
3109 parent, p2 = repo.dirstate.parents()
3110 parent, p2 = repo.dirstate.parents()
3110 if not opts.get('rev') and p2 != nullid:
3111 if not opts.get('rev') and p2 != nullid:
3111 raise util.Abort(_('uncommitted merge - please provide a '
3112 raise util.Abort(_('uncommitted merge - please provide a '
3112 'specific revision'))
3113 'specific revision'))
3113 ctx = repo[opts.get('rev')]
3114 ctx = repo[opts.get('rev')]
3114 node = ctx.node()
3115 node = ctx.node()
3115 mf = ctx.manifest()
3116 mf = ctx.manifest()
3116 if node == parent:
3117 if node == parent:
3117 pmf = mf
3118 pmf = mf
3118 else:
3119 else:
3119 pmf = None
3120 pmf = None
3120
3121
3121 # need all matching names in dirstate and manifest of target rev,
3122 # need all matching names in dirstate and manifest of target rev,
3122 # so have to walk both. do not print errors if files exist in one
3123 # so have to walk both. do not print errors if files exist in one
3123 # but not other.
3124 # but not other.
3124
3125
3125 names = {}
3126 names = {}
3126
3127
3127 wlock = repo.wlock()
3128 wlock = repo.wlock()
3128 try:
3129 try:
3129 # walk dirstate.
3130 # walk dirstate.
3130
3131
3131 m = cmdutil.match(repo, pats, opts)
3132 m = cmdutil.match(repo, pats, opts)
3132 m.bad = lambda x, y: False
3133 m.bad = lambda x, y: False
3133 for abs in repo.walk(m):
3134 for abs in repo.walk(m):
3134 names[abs] = m.rel(abs), m.exact(abs)
3135 names[abs] = m.rel(abs), m.exact(abs)
3135
3136
3136 # walk target manifest.
3137 # walk target manifest.
3137
3138
3138 def badfn(path, msg):
3139 def badfn(path, msg):
3139 if path in names:
3140 if path in names:
3140 return
3141 return
3141 path_ = path + '/'
3142 path_ = path + '/'
3142 for f in names:
3143 for f in names:
3143 if f.startswith(path_):
3144 if f.startswith(path_):
3144 return
3145 return
3145 ui.warn("%s: %s\n" % (m.rel(path), msg))
3146 ui.warn("%s: %s\n" % (m.rel(path), msg))
3146
3147
3147 m = cmdutil.match(repo, pats, opts)
3148 m = cmdutil.match(repo, pats, opts)
3148 m.bad = badfn
3149 m.bad = badfn
3149 for abs in repo[node].walk(m):
3150 for abs in repo[node].walk(m):
3150 if abs not in names:
3151 if abs not in names:
3151 names[abs] = m.rel(abs), m.exact(abs)
3152 names[abs] = m.rel(abs), m.exact(abs)
3152
3153
3153 m = cmdutil.matchfiles(repo, names)
3154 m = cmdutil.matchfiles(repo, names)
3154 changes = repo.status(match=m)[:4]
3155 changes = repo.status(match=m)[:4]
3155 modified, added, removed, deleted = map(set, changes)
3156 modified, added, removed, deleted = map(set, changes)
3156
3157
3157 # if f is a rename, also revert the source
3158 # if f is a rename, also revert the source
3158 cwd = repo.getcwd()
3159 cwd = repo.getcwd()
3159 for f in added:
3160 for f in added:
3160 src = repo.dirstate.copied(f)
3161 src = repo.dirstate.copied(f)
3161 if src and src not in names and repo.dirstate[src] == 'r':
3162 if src and src not in names and repo.dirstate[src] == 'r':
3162 removed.add(src)
3163 removed.add(src)
3163 names[src] = (repo.pathto(src, cwd), True)
3164 names[src] = (repo.pathto(src, cwd), True)
3164
3165
3165 def removeforget(abs):
3166 def removeforget(abs):
3166 if repo.dirstate[abs] == 'a':
3167 if repo.dirstate[abs] == 'a':
3167 return _('forgetting %s\n')
3168 return _('forgetting %s\n')
3168 return _('removing %s\n')
3169 return _('removing %s\n')
3169
3170
3170 revert = ([], _('reverting %s\n'))
3171 revert = ([], _('reverting %s\n'))
3171 add = ([], _('adding %s\n'))
3172 add = ([], _('adding %s\n'))
3172 remove = ([], removeforget)
3173 remove = ([], removeforget)
3173 undelete = ([], _('undeleting %s\n'))
3174 undelete = ([], _('undeleting %s\n'))
3174
3175
3175 disptable = (
3176 disptable = (
3176 # dispatch table:
3177 # dispatch table:
3177 # file state
3178 # file state
3178 # action if in target manifest
3179 # action if in target manifest
3179 # action if not in target manifest
3180 # action if not in target manifest
3180 # make backup if in target manifest
3181 # make backup if in target manifest
3181 # make backup if not in target manifest
3182 # make backup if not in target manifest
3182 (modified, revert, remove, True, True),
3183 (modified, revert, remove, True, True),
3183 (added, revert, remove, True, False),
3184 (added, revert, remove, True, False),
3184 (removed, undelete, None, False, False),
3185 (removed, undelete, None, False, False),
3185 (deleted, revert, remove, False, False),
3186 (deleted, revert, remove, False, False),
3186 )
3187 )
3187
3188
3188 for abs, (rel, exact) in sorted(names.items()):
3189 for abs, (rel, exact) in sorted(names.items()):
3189 mfentry = mf.get(abs)
3190 mfentry = mf.get(abs)
3190 target = repo.wjoin(abs)
3191 target = repo.wjoin(abs)
3191 def handle(xlist, dobackup):
3192 def handle(xlist, dobackup):
3192 xlist[0].append(abs)
3193 xlist[0].append(abs)
3193 if (dobackup and not opts.get('no_backup') and
3194 if (dobackup and not opts.get('no_backup') and
3194 os.path.lexists(target)):
3195 os.path.lexists(target)):
3195 bakname = "%s.orig" % rel
3196 bakname = "%s.orig" % rel
3196 ui.note(_('saving current version of %s as %s\n') %
3197 ui.note(_('saving current version of %s as %s\n') %
3197 (rel, bakname))
3198 (rel, bakname))
3198 if not opts.get('dry_run'):
3199 if not opts.get('dry_run'):
3199 util.rename(target, bakname)
3200 util.rename(target, bakname)
3200 if ui.verbose or not exact:
3201 if ui.verbose or not exact:
3201 msg = xlist[1]
3202 msg = xlist[1]
3202 if not isinstance(msg, basestring):
3203 if not isinstance(msg, basestring):
3203 msg = msg(abs)
3204 msg = msg(abs)
3204 ui.status(msg % rel)
3205 ui.status(msg % rel)
3205 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3206 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3206 if abs not in table:
3207 if abs not in table:
3207 continue
3208 continue
3208 # file has changed in dirstate
3209 # file has changed in dirstate
3209 if mfentry:
3210 if mfentry:
3210 handle(hitlist, backuphit)
3211 handle(hitlist, backuphit)
3211 elif misslist is not None:
3212 elif misslist is not None:
3212 handle(misslist, backupmiss)
3213 handle(misslist, backupmiss)
3213 break
3214 break
3214 else:
3215 else:
3215 if abs not in repo.dirstate:
3216 if abs not in repo.dirstate:
3216 if mfentry:
3217 if mfentry:
3217 handle(add, True)
3218 handle(add, True)
3218 elif exact:
3219 elif exact:
3219 ui.warn(_('file not managed: %s\n') % rel)
3220 ui.warn(_('file not managed: %s\n') % rel)
3220 continue
3221 continue
3221 # file has not changed in dirstate
3222 # file has not changed in dirstate
3222 if node == parent:
3223 if node == parent:
3223 if exact:
3224 if exact:
3224 ui.warn(_('no changes needed to %s\n') % rel)
3225 ui.warn(_('no changes needed to %s\n') % rel)
3225 continue
3226 continue
3226 if pmf is None:
3227 if pmf is None:
3227 # only need parent manifest in this unlikely case,
3228 # only need parent manifest in this unlikely case,
3228 # so do not read by default
3229 # so do not read by default
3229 pmf = repo[parent].manifest()
3230 pmf = repo[parent].manifest()
3230 if abs in pmf:
3231 if abs in pmf:
3231 if mfentry:
3232 if mfentry:
3232 # if version of file is same in parent and target
3233 # if version of file is same in parent and target
3233 # manifests, do nothing
3234 # manifests, do nothing
3234 if (pmf[abs] != mfentry or
3235 if (pmf[abs] != mfentry or
3235 pmf.flags(abs) != mf.flags(abs)):
3236 pmf.flags(abs) != mf.flags(abs)):
3236 handle(revert, False)
3237 handle(revert, False)
3237 else:
3238 else:
3238 handle(remove, False)
3239 handle(remove, False)
3239
3240
3240 if not opts.get('dry_run'):
3241 if not opts.get('dry_run'):
3241 def checkout(f):
3242 def checkout(f):
3242 fc = ctx[f]
3243 fc = ctx[f]
3243 repo.wwrite(f, fc.data(), fc.flags())
3244 repo.wwrite(f, fc.data(), fc.flags())
3244
3245
3245 audit_path = util.path_auditor(repo.root)
3246 audit_path = util.path_auditor(repo.root)
3246 for f in remove[0]:
3247 for f in remove[0]:
3247 if repo.dirstate[f] == 'a':
3248 if repo.dirstate[f] == 'a':
3248 repo.dirstate.forget(f)
3249 repo.dirstate.forget(f)
3249 continue
3250 continue
3250 audit_path(f)
3251 audit_path(f)
3251 try:
3252 try:
3252 util.unlink(repo.wjoin(f))
3253 util.unlink(repo.wjoin(f))
3253 except OSError:
3254 except OSError:
3254 pass
3255 pass
3255 repo.dirstate.remove(f)
3256 repo.dirstate.remove(f)
3256
3257
3257 normal = None
3258 normal = None
3258 if node == parent:
3259 if node == parent:
3259 # We're reverting to our parent. If possible, we'd like status
3260 # We're reverting to our parent. If possible, we'd like status
3260 # to report the file as clean. We have to use normallookup for
3261 # to report the file as clean. We have to use normallookup for
3261 # merges to avoid losing information about merged/dirty files.
3262 # merges to avoid losing information about merged/dirty files.
3262 if p2 != nullid:
3263 if p2 != nullid:
3263 normal = repo.dirstate.normallookup
3264 normal = repo.dirstate.normallookup
3264 else:
3265 else:
3265 normal = repo.dirstate.normal
3266 normal = repo.dirstate.normal
3266 for f in revert[0]:
3267 for f in revert[0]:
3267 checkout(f)
3268 checkout(f)
3268 if normal:
3269 if normal:
3269 normal(f)
3270 normal(f)
3270
3271
3271 for f in add[0]:
3272 for f in add[0]:
3272 checkout(f)
3273 checkout(f)
3273 repo.dirstate.add(f)
3274 repo.dirstate.add(f)
3274
3275
3275 normal = repo.dirstate.normallookup
3276 normal = repo.dirstate.normallookup
3276 if node == parent and p2 == nullid:
3277 if node == parent and p2 == nullid:
3277 normal = repo.dirstate.normal
3278 normal = repo.dirstate.normal
3278 for f in undelete[0]:
3279 for f in undelete[0]:
3279 checkout(f)
3280 checkout(f)
3280 normal(f)
3281 normal(f)
3281
3282
3282 finally:
3283 finally:
3283 wlock.release()
3284 wlock.release()
3284
3285
3285 def rollback(ui, repo, **opts):
3286 def rollback(ui, repo, **opts):
3286 """roll back the last transaction (dangerous)
3287 """roll back the last transaction (dangerous)
3287
3288
3288 This command should be used with care. There is only one level of
3289 This command should be used with care. There is only one level of
3289 rollback, and there is no way to undo a rollback. It will also
3290 rollback, and there is no way to undo a rollback. It will also
3290 restore the dirstate at the time of the last transaction, losing
3291 restore the dirstate at the time of the last transaction, losing
3291 any dirstate changes since that time. This command does not alter
3292 any dirstate changes since that time. This command does not alter
3292 the working directory.
3293 the working directory.
3293
3294
3294 Transactions are used to encapsulate the effects of all commands
3295 Transactions are used to encapsulate the effects of all commands
3295 that create new changesets or propagate existing changesets into a
3296 that create new changesets or propagate existing changesets into a
3296 repository. For example, the following commands are transactional,
3297 repository. For example, the following commands are transactional,
3297 and their effects can be rolled back:
3298 and their effects can be rolled back:
3298
3299
3299 - commit
3300 - commit
3300 - import
3301 - import
3301 - pull
3302 - pull
3302 - push (with this repository as the destination)
3303 - push (with this repository as the destination)
3303 - unbundle
3304 - unbundle
3304
3305
3305 This command is not intended for use on public repositories. Once
3306 This command is not intended for use on public repositories. Once
3306 changes are visible for pull by other users, rolling a transaction
3307 changes are visible for pull by other users, rolling a transaction
3307 back locally is ineffective (someone else may already have pulled
3308 back locally is ineffective (someone else may already have pulled
3308 the changes). Furthermore, a race is possible with readers of the
3309 the changes). Furthermore, a race is possible with readers of the
3309 repository; for example an in-progress pull from the repository
3310 repository; for example an in-progress pull from the repository
3310 may fail if a rollback is performed.
3311 may fail if a rollback is performed.
3311
3312
3312 Returns 0 on success, 1 if no rollback data is available.
3313 Returns 0 on success, 1 if no rollback data is available.
3313 """
3314 """
3314 return repo.rollback(opts.get('dry_run'))
3315 return repo.rollback(opts.get('dry_run'))
3315
3316
3316 def root(ui, repo):
3317 def root(ui, repo):
3317 """print the root (top) of the current working directory
3318 """print the root (top) of the current working directory
3318
3319
3319 Print the root directory of the current repository.
3320 Print the root directory of the current repository.
3320
3321
3321 Returns 0 on success.
3322 Returns 0 on success.
3322 """
3323 """
3323 ui.write(repo.root + "\n")
3324 ui.write(repo.root + "\n")
3324
3325
3325 def serve(ui, repo, **opts):
3326 def serve(ui, repo, **opts):
3326 """start stand-alone webserver
3327 """start stand-alone webserver
3327
3328
3328 Start a local HTTP repository browser and pull server. You can use
3329 Start a local HTTP repository browser and pull server. You can use
3329 this for ad-hoc sharing and browing of repositories. It is
3330 this for ad-hoc sharing and browing of repositories. It is
3330 recommended to use a real web server to serve a repository for
3331 recommended to use a real web server to serve a repository for
3331 longer periods of time.
3332 longer periods of time.
3332
3333
3333 Please note that the server does not implement access control.
3334 Please note that the server does not implement access control.
3334 This means that, by default, anybody can read from the server and
3335 This means that, by default, anybody can read from the server and
3335 nobody can write to it by default. Set the ``web.allow_push``
3336 nobody can write to it by default. Set the ``web.allow_push``
3336 option to ``*`` to allow everybody to push to the server. You
3337 option to ``*`` to allow everybody to push to the server. You
3337 should use a real web server if you need to authenticate users.
3338 should use a real web server if you need to authenticate users.
3338
3339
3339 By default, the server logs accesses to stdout and errors to
3340 By default, the server logs accesses to stdout and errors to
3340 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3341 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3341 files.
3342 files.
3342
3343
3343 To have the server choose a free port number to listen on, specify
3344 To have the server choose a free port number to listen on, specify
3344 a port number of 0; in this case, the server will print the port
3345 a port number of 0; in this case, the server will print the port
3345 number it uses.
3346 number it uses.
3346
3347
3347 Returns 0 on success.
3348 Returns 0 on success.
3348 """
3349 """
3349
3350
3350 if opts["stdio"]:
3351 if opts["stdio"]:
3351 if repo is None:
3352 if repo is None:
3352 raise error.RepoError(_("There is no Mercurial repository here"
3353 raise error.RepoError(_("There is no Mercurial repository here"
3353 " (.hg not found)"))
3354 " (.hg not found)"))
3354 s = sshserver.sshserver(ui, repo)
3355 s = sshserver.sshserver(ui, repo)
3355 s.serve_forever()
3356 s.serve_forever()
3356
3357
3357 # this way we can check if something was given in the command-line
3358 # this way we can check if something was given in the command-line
3358 if opts.get('port'):
3359 if opts.get('port'):
3359 opts['port'] = util.getport(opts.get('port'))
3360 opts['port'] = util.getport(opts.get('port'))
3360
3361
3361 baseui = repo and repo.baseui or ui
3362 baseui = repo and repo.baseui or ui
3362 optlist = ("name templates style address port prefix ipv6"
3363 optlist = ("name templates style address port prefix ipv6"
3363 " accesslog errorlog certificate encoding")
3364 " accesslog errorlog certificate encoding")
3364 for o in optlist.split():
3365 for o in optlist.split():
3365 val = opts.get(o, '')
3366 val = opts.get(o, '')
3366 if val in (None, ''): # should check against default options instead
3367 if val in (None, ''): # should check against default options instead
3367 continue
3368 continue
3368 baseui.setconfig("web", o, val)
3369 baseui.setconfig("web", o, val)
3369 if repo and repo.ui != baseui:
3370 if repo and repo.ui != baseui:
3370 repo.ui.setconfig("web", o, val)
3371 repo.ui.setconfig("web", o, val)
3371
3372
3372 o = opts.get('web_conf') or opts.get('webdir_conf')
3373 o = opts.get('web_conf') or opts.get('webdir_conf')
3373 if not o:
3374 if not o:
3374 if not repo:
3375 if not repo:
3375 raise error.RepoError(_("There is no Mercurial repository"
3376 raise error.RepoError(_("There is no Mercurial repository"
3376 " here (.hg not found)"))
3377 " here (.hg not found)"))
3377 o = repo.root
3378 o = repo.root
3378
3379
3379 app = hgweb.hgweb(o, baseui=ui)
3380 app = hgweb.hgweb(o, baseui=ui)
3380
3381
3381 class service(object):
3382 class service(object):
3382 def init(self):
3383 def init(self):
3383 util.set_signal_handler()
3384 util.set_signal_handler()
3384 self.httpd = hgweb.server.create_server(ui, app)
3385 self.httpd = hgweb.server.create_server(ui, app)
3385
3386
3386 if opts['port'] and not ui.verbose:
3387 if opts['port'] and not ui.verbose:
3387 return
3388 return
3388
3389
3389 if self.httpd.prefix:
3390 if self.httpd.prefix:
3390 prefix = self.httpd.prefix.strip('/') + '/'
3391 prefix = self.httpd.prefix.strip('/') + '/'
3391 else:
3392 else:
3392 prefix = ''
3393 prefix = ''
3393
3394
3394 port = ':%d' % self.httpd.port
3395 port = ':%d' % self.httpd.port
3395 if port == ':80':
3396 if port == ':80':
3396 port = ''
3397 port = ''
3397
3398
3398 bindaddr = self.httpd.addr
3399 bindaddr = self.httpd.addr
3399 if bindaddr == '0.0.0.0':
3400 if bindaddr == '0.0.0.0':
3400 bindaddr = '*'
3401 bindaddr = '*'
3401 elif ':' in bindaddr: # IPv6
3402 elif ':' in bindaddr: # IPv6
3402 bindaddr = '[%s]' % bindaddr
3403 bindaddr = '[%s]' % bindaddr
3403
3404
3404 fqaddr = self.httpd.fqaddr
3405 fqaddr = self.httpd.fqaddr
3405 if ':' in fqaddr:
3406 if ':' in fqaddr:
3406 fqaddr = '[%s]' % fqaddr
3407 fqaddr = '[%s]' % fqaddr
3407 if opts['port']:
3408 if opts['port']:
3408 write = ui.status
3409 write = ui.status
3409 else:
3410 else:
3410 write = ui.write
3411 write = ui.write
3411 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3412 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3412 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3413 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3413
3414
3414 def run(self):
3415 def run(self):
3415 self.httpd.serve_forever()
3416 self.httpd.serve_forever()
3416
3417
3417 service = service()
3418 service = service()
3418
3419
3419 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3420 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3420
3421
3421 def status(ui, repo, *pats, **opts):
3422 def status(ui, repo, *pats, **opts):
3422 """show changed files in the working directory
3423 """show changed files in the working directory
3423
3424
3424 Show status of files in the repository. If names are given, only
3425 Show status of files in the repository. If names are given, only
3425 files that match are shown. Files that are clean or ignored or
3426 files that match are shown. Files that are clean or ignored or
3426 the source of a copy/move operation, are not listed unless
3427 the source of a copy/move operation, are not listed unless
3427 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3428 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3428 Unless options described with "show only ..." are given, the
3429 Unless options described with "show only ..." are given, the
3429 options -mardu are used.
3430 options -mardu are used.
3430
3431
3431 Option -q/--quiet hides untracked (unknown and ignored) files
3432 Option -q/--quiet hides untracked (unknown and ignored) files
3432 unless explicitly requested with -u/--unknown or -i/--ignored.
3433 unless explicitly requested with -u/--unknown or -i/--ignored.
3433
3434
3434 NOTE: status may appear to disagree with diff if permissions have
3435 NOTE: status may appear to disagree with diff if permissions have
3435 changed or a merge has occurred. The standard diff format does not
3436 changed or a merge has occurred. The standard diff format does not
3436 report permission changes and diff only reports changes relative
3437 report permission changes and diff only reports changes relative
3437 to one merge parent.
3438 to one merge parent.
3438
3439
3439 If one revision is given, it is used as the base revision.
3440 If one revision is given, it is used as the base revision.
3440 If two revisions are given, the differences between them are
3441 If two revisions are given, the differences between them are
3441 shown. The --change option can also be used as a shortcut to list
3442 shown. The --change option can also be used as a shortcut to list
3442 the changed files of a revision from its first parent.
3443 the changed files of a revision from its first parent.
3443
3444
3444 The codes used to show the status of files are::
3445 The codes used to show the status of files are::
3445
3446
3446 M = modified
3447 M = modified
3447 A = added
3448 A = added
3448 R = removed
3449 R = removed
3449 C = clean
3450 C = clean
3450 ! = missing (deleted by non-hg command, but still tracked)
3451 ! = missing (deleted by non-hg command, but still tracked)
3451 ? = not tracked
3452 ? = not tracked
3452 I = ignored
3453 I = ignored
3453 = origin of the previous file listed as A (added)
3454 = origin of the previous file listed as A (added)
3454
3455
3455 Returns 0 on success.
3456 Returns 0 on success.
3456 """
3457 """
3457
3458
3458 revs = opts.get('rev')
3459 revs = opts.get('rev')
3459 change = opts.get('change')
3460 change = opts.get('change')
3460
3461
3461 if revs and change:
3462 if revs and change:
3462 msg = _('cannot specify --rev and --change at the same time')
3463 msg = _('cannot specify --rev and --change at the same time')
3463 raise util.Abort(msg)
3464 raise util.Abort(msg)
3464 elif change:
3465 elif change:
3465 node2 = repo.lookup(change)
3466 node2 = repo.lookup(change)
3466 node1 = repo[node2].parents()[0].node()
3467 node1 = repo[node2].parents()[0].node()
3467 else:
3468 else:
3468 node1, node2 = cmdutil.revpair(repo, revs)
3469 node1, node2 = cmdutil.revpair(repo, revs)
3469
3470
3470 cwd = (pats and repo.getcwd()) or ''
3471 cwd = (pats and repo.getcwd()) or ''
3471 end = opts.get('print0') and '\0' or '\n'
3472 end = opts.get('print0') and '\0' or '\n'
3472 copy = {}
3473 copy = {}
3473 states = 'modified added removed deleted unknown ignored clean'.split()
3474 states = 'modified added removed deleted unknown ignored clean'.split()
3474 show = [k for k in states if opts.get(k)]
3475 show = [k for k in states if opts.get(k)]
3475 if opts.get('all'):
3476 if opts.get('all'):
3476 show += ui.quiet and (states[:4] + ['clean']) or states
3477 show += ui.quiet and (states[:4] + ['clean']) or states
3477 if not show:
3478 if not show:
3478 show = ui.quiet and states[:4] or states[:5]
3479 show = ui.quiet and states[:4] or states[:5]
3479
3480
3480 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3481 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3481 'ignored' in show, 'clean' in show, 'unknown' in show,
3482 'ignored' in show, 'clean' in show, 'unknown' in show,
3482 opts.get('subrepos'))
3483 opts.get('subrepos'))
3483 changestates = zip(states, 'MAR!?IC', stat)
3484 changestates = zip(states, 'MAR!?IC', stat)
3484
3485
3485 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3486 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3486 ctxn = repo[nullid]
3487 ctxn = repo[nullid]
3487 ctx1 = repo[node1]
3488 ctx1 = repo[node1]
3488 ctx2 = repo[node2]
3489 ctx2 = repo[node2]
3489 added = stat[1]
3490 added = stat[1]
3490 if node2 is None:
3491 if node2 is None:
3491 added = stat[0] + stat[1] # merged?
3492 added = stat[0] + stat[1] # merged?
3492
3493
3493 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3494 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3494 if k in added:
3495 if k in added:
3495 copy[k] = v
3496 copy[k] = v
3496 elif v in added:
3497 elif v in added:
3497 copy[v] = k
3498 copy[v] = k
3498
3499
3499 for state, char, files in changestates:
3500 for state, char, files in changestates:
3500 if state in show:
3501 if state in show:
3501 format = "%s %%s%s" % (char, end)
3502 format = "%s %%s%s" % (char, end)
3502 if opts.get('no_status'):
3503 if opts.get('no_status'):
3503 format = "%%s%s" % end
3504 format = "%%s%s" % end
3504
3505
3505 for f in files:
3506 for f in files:
3506 ui.write(format % repo.pathto(f, cwd),
3507 ui.write(format % repo.pathto(f, cwd),
3507 label='status.' + state)
3508 label='status.' + state)
3508 if f in copy:
3509 if f in copy:
3509 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3510 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3510 label='status.copied')
3511 label='status.copied')
3511
3512
3512 def summary(ui, repo, **opts):
3513 def summary(ui, repo, **opts):
3513 """summarize working directory state
3514 """summarize working directory state
3514
3515
3515 This generates a brief summary of the working directory state,
3516 This generates a brief summary of the working directory state,
3516 including parents, branch, commit status, and available updates.
3517 including parents, branch, commit status, and available updates.
3517
3518
3518 With the --remote option, this will check the default paths for
3519 With the --remote option, this will check the default paths for
3519 incoming and outgoing changes. This can be time-consuming.
3520 incoming and outgoing changes. This can be time-consuming.
3520
3521
3521 Returns 0 on success.
3522 Returns 0 on success.
3522 """
3523 """
3523
3524
3524 ctx = repo[None]
3525 ctx = repo[None]
3525 parents = ctx.parents()
3526 parents = ctx.parents()
3526 pnode = parents[0].node()
3527 pnode = parents[0].node()
3527
3528
3528 for p in parents:
3529 for p in parents:
3529 # label with log.changeset (instead of log.parent) since this
3530 # label with log.changeset (instead of log.parent) since this
3530 # shows a working directory parent *changeset*:
3531 # shows a working directory parent *changeset*:
3531 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3532 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3532 label='log.changeset')
3533 label='log.changeset')
3533 ui.write(' '.join(p.tags()), label='log.tag')
3534 ui.write(' '.join(p.tags()), label='log.tag')
3534 if p.rev() == -1:
3535 if p.rev() == -1:
3535 if not len(repo):
3536 if not len(repo):
3536 ui.write(_(' (empty repository)'))
3537 ui.write(_(' (empty repository)'))
3537 else:
3538 else:
3538 ui.write(_(' (no revision checked out)'))
3539 ui.write(_(' (no revision checked out)'))
3539 ui.write('\n')
3540 ui.write('\n')
3540 if p.description():
3541 if p.description():
3541 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3542 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3542 label='log.summary')
3543 label='log.summary')
3543
3544
3544 branch = ctx.branch()
3545 branch = ctx.branch()
3545 bheads = repo.branchheads(branch)
3546 bheads = repo.branchheads(branch)
3546 m = _('branch: %s\n') % branch
3547 m = _('branch: %s\n') % branch
3547 if branch != 'default':
3548 if branch != 'default':
3548 ui.write(m, label='log.branch')
3549 ui.write(m, label='log.branch')
3549 else:
3550 else:
3550 ui.status(m, label='log.branch')
3551 ui.status(m, label='log.branch')
3551
3552
3552 st = list(repo.status(unknown=True))[:6]
3553 st = list(repo.status(unknown=True))[:6]
3553
3554
3554 c = repo.dirstate.copies()
3555 c = repo.dirstate.copies()
3555 copied, renamed = [], []
3556 copied, renamed = [], []
3556 for d, s in c.iteritems():
3557 for d, s in c.iteritems():
3557 if s in st[2]:
3558 if s in st[2]:
3558 st[2].remove(s)
3559 st[2].remove(s)
3559 renamed.append(d)
3560 renamed.append(d)
3560 else:
3561 else:
3561 copied.append(d)
3562 copied.append(d)
3562 if d in st[1]:
3563 if d in st[1]:
3563 st[1].remove(d)
3564 st[1].remove(d)
3564 st.insert(3, renamed)
3565 st.insert(3, renamed)
3565 st.insert(4, copied)
3566 st.insert(4, copied)
3566
3567
3567 ms = mergemod.mergestate(repo)
3568 ms = mergemod.mergestate(repo)
3568 st.append([f for f in ms if ms[f] == 'u'])
3569 st.append([f for f in ms if ms[f] == 'u'])
3569
3570
3570 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3571 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3571 st.append(subs)
3572 st.append(subs)
3572
3573
3573 labels = [ui.label(_('%d modified'), 'status.modified'),
3574 labels = [ui.label(_('%d modified'), 'status.modified'),
3574 ui.label(_('%d added'), 'status.added'),
3575 ui.label(_('%d added'), 'status.added'),
3575 ui.label(_('%d removed'), 'status.removed'),
3576 ui.label(_('%d removed'), 'status.removed'),
3576 ui.label(_('%d renamed'), 'status.copied'),
3577 ui.label(_('%d renamed'), 'status.copied'),
3577 ui.label(_('%d copied'), 'status.copied'),
3578 ui.label(_('%d copied'), 'status.copied'),
3578 ui.label(_('%d deleted'), 'status.deleted'),
3579 ui.label(_('%d deleted'), 'status.deleted'),
3579 ui.label(_('%d unknown'), 'status.unknown'),
3580 ui.label(_('%d unknown'), 'status.unknown'),
3580 ui.label(_('%d ignored'), 'status.ignored'),
3581 ui.label(_('%d ignored'), 'status.ignored'),
3581 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3582 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3582 ui.label(_('%d subrepos'), 'status.modified')]
3583 ui.label(_('%d subrepos'), 'status.modified')]
3583 t = []
3584 t = []
3584 for s, l in zip(st, labels):
3585 for s, l in zip(st, labels):
3585 if s:
3586 if s:
3586 t.append(l % len(s))
3587 t.append(l % len(s))
3587
3588
3588 t = ', '.join(t)
3589 t = ', '.join(t)
3589 cleanworkdir = False
3590 cleanworkdir = False
3590
3591
3591 if len(parents) > 1:
3592 if len(parents) > 1:
3592 t += _(' (merge)')
3593 t += _(' (merge)')
3593 elif branch != parents[0].branch():
3594 elif branch != parents[0].branch():
3594 t += _(' (new branch)')
3595 t += _(' (new branch)')
3595 elif (parents[0].extra().get('close') and
3596 elif (parents[0].extra().get('close') and
3596 pnode in repo.branchheads(branch, closed=True)):
3597 pnode in repo.branchheads(branch, closed=True)):
3597 t += _(' (head closed)')
3598 t += _(' (head closed)')
3598 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3599 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3599 t += _(' (clean)')
3600 t += _(' (clean)')
3600 cleanworkdir = True
3601 cleanworkdir = True
3601 elif pnode not in bheads:
3602 elif pnode not in bheads:
3602 t += _(' (new branch head)')
3603 t += _(' (new branch head)')
3603
3604
3604 if cleanworkdir:
3605 if cleanworkdir:
3605 ui.status(_('commit: %s\n') % t.strip())
3606 ui.status(_('commit: %s\n') % t.strip())
3606 else:
3607 else:
3607 ui.write(_('commit: %s\n') % t.strip())
3608 ui.write(_('commit: %s\n') % t.strip())
3608
3609
3609 # all ancestors of branch heads - all ancestors of parent = new csets
3610 # all ancestors of branch heads - all ancestors of parent = new csets
3610 new = [0] * len(repo)
3611 new = [0] * len(repo)
3611 cl = repo.changelog
3612 cl = repo.changelog
3612 for a in [cl.rev(n) for n in bheads]:
3613 for a in [cl.rev(n) for n in bheads]:
3613 new[a] = 1
3614 new[a] = 1
3614 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3615 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3615 new[a] = 1
3616 new[a] = 1
3616 for a in [p.rev() for p in parents]:
3617 for a in [p.rev() for p in parents]:
3617 if a >= 0:
3618 if a >= 0:
3618 new[a] = 0
3619 new[a] = 0
3619 for a in cl.ancestors(*[p.rev() for p in parents]):
3620 for a in cl.ancestors(*[p.rev() for p in parents]):
3620 new[a] = 0
3621 new[a] = 0
3621 new = sum(new)
3622 new = sum(new)
3622
3623
3623 if new == 0:
3624 if new == 0:
3624 ui.status(_('update: (current)\n'))
3625 ui.status(_('update: (current)\n'))
3625 elif pnode not in bheads:
3626 elif pnode not in bheads:
3626 ui.write(_('update: %d new changesets (update)\n') % new)
3627 ui.write(_('update: %d new changesets (update)\n') % new)
3627 else:
3628 else:
3628 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3629 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3629 (new, len(bheads)))
3630 (new, len(bheads)))
3630
3631
3631 if opts.get('remote'):
3632 if opts.get('remote'):
3632 t = []
3633 t = []
3633 source, branches = hg.parseurl(ui.expandpath('default'))
3634 source, branches = hg.parseurl(ui.expandpath('default'))
3634 other = hg.repository(hg.remoteui(repo, {}), source)
3635 other = hg.repository(hg.remoteui(repo, {}), source)
3635 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3636 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3636 ui.debug('comparing with %s\n' % url.hidepassword(source))
3637 ui.debug('comparing with %s\n' % url.hidepassword(source))
3637 repo.ui.pushbuffer()
3638 repo.ui.pushbuffer()
3638 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3639 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3639 repo.ui.popbuffer()
3640 repo.ui.popbuffer()
3640 if incoming:
3641 if incoming:
3641 t.append(_('1 or more incoming'))
3642 t.append(_('1 or more incoming'))
3642
3643
3643 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3644 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3644 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3645 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3645 other = hg.repository(hg.remoteui(repo, {}), dest)
3646 other = hg.repository(hg.remoteui(repo, {}), dest)
3646 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3647 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3647 repo.ui.pushbuffer()
3648 repo.ui.pushbuffer()
3648 o = discovery.findoutgoing(repo, other)
3649 o = discovery.findoutgoing(repo, other)
3649 repo.ui.popbuffer()
3650 repo.ui.popbuffer()
3650 o = repo.changelog.nodesbetween(o, None)[0]
3651 o = repo.changelog.nodesbetween(o, None)[0]
3651 if o:
3652 if o:
3652 t.append(_('%d outgoing') % len(o))
3653 t.append(_('%d outgoing') % len(o))
3653
3654
3654 if t:
3655 if t:
3655 ui.write(_('remote: %s\n') % (', '.join(t)))
3656 ui.write(_('remote: %s\n') % (', '.join(t)))
3656 else:
3657 else:
3657 ui.status(_('remote: (synced)\n'))
3658 ui.status(_('remote: (synced)\n'))
3658
3659
3659 def tag(ui, repo, name1, *names, **opts):
3660 def tag(ui, repo, name1, *names, **opts):
3660 """add one or more tags for the current or given revision
3661 """add one or more tags for the current or given revision
3661
3662
3662 Name a particular revision using <name>.
3663 Name a particular revision using <name>.
3663
3664
3664 Tags are used to name particular revisions of the repository and are
3665 Tags are used to name particular revisions of the repository and are
3665 very useful to compare different revisions, to go back to significant
3666 very useful to compare different revisions, to go back to significant
3666 earlier versions or to mark branch points as releases, etc.
3667 earlier versions or to mark branch points as releases, etc.
3667
3668
3668 If no revision is given, the parent of the working directory is
3669 If no revision is given, the parent of the working directory is
3669 used, or tip if no revision is checked out.
3670 used, or tip if no revision is checked out.
3670
3671
3671 To facilitate version control, distribution, and merging of tags,
3672 To facilitate version control, distribution, and merging of tags,
3672 they are stored as a file named ".hgtags" which is managed
3673 they are stored as a file named ".hgtags" which is managed
3673 similarly to other project files and can be hand-edited if
3674 similarly to other project files and can be hand-edited if
3674 necessary. The file '.hg/localtags' is used for local tags (not
3675 necessary. The file '.hg/localtags' is used for local tags (not
3675 shared among repositories).
3676 shared among repositories).
3676
3677
3677 See :hg:`help dates` for a list of formats valid for -d/--date.
3678 See :hg:`help dates` for a list of formats valid for -d/--date.
3678
3679
3679 Since tag names have priority over branch names during revision
3680 Since tag names have priority over branch names during revision
3680 lookup, using an existing branch name as a tag name is discouraged.
3681 lookup, using an existing branch name as a tag name is discouraged.
3681
3682
3682 Returns 0 on success.
3683 Returns 0 on success.
3683 """
3684 """
3684
3685
3685 rev_ = "."
3686 rev_ = "."
3686 names = [t.strip() for t in (name1,) + names]
3687 names = [t.strip() for t in (name1,) + names]
3687 if len(names) != len(set(names)):
3688 if len(names) != len(set(names)):
3688 raise util.Abort(_('tag names must be unique'))
3689 raise util.Abort(_('tag names must be unique'))
3689 for n in names:
3690 for n in names:
3690 if n in ['tip', '.', 'null']:
3691 if n in ['tip', '.', 'null']:
3691 raise util.Abort(_('the name \'%s\' is reserved') % n)
3692 raise util.Abort(_('the name \'%s\' is reserved') % n)
3692 if not n:
3693 if not n:
3693 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3694 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3694 if opts.get('rev') and opts.get('remove'):
3695 if opts.get('rev') and opts.get('remove'):
3695 raise util.Abort(_("--rev and --remove are incompatible"))
3696 raise util.Abort(_("--rev and --remove are incompatible"))
3696 if opts.get('rev'):
3697 if opts.get('rev'):
3697 rev_ = opts['rev']
3698 rev_ = opts['rev']
3698 message = opts.get('message')
3699 message = opts.get('message')
3699 if opts.get('remove'):
3700 if opts.get('remove'):
3700 expectedtype = opts.get('local') and 'local' or 'global'
3701 expectedtype = opts.get('local') and 'local' or 'global'
3701 for n in names:
3702 for n in names:
3702 if not repo.tagtype(n):
3703 if not repo.tagtype(n):
3703 raise util.Abort(_('tag \'%s\' does not exist') % n)
3704 raise util.Abort(_('tag \'%s\' does not exist') % n)
3704 if repo.tagtype(n) != expectedtype:
3705 if repo.tagtype(n) != expectedtype:
3705 if expectedtype == 'global':
3706 if expectedtype == 'global':
3706 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3707 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3707 else:
3708 else:
3708 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3709 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3709 rev_ = nullid
3710 rev_ = nullid
3710 if not message:
3711 if not message:
3711 # we don't translate commit messages
3712 # we don't translate commit messages
3712 message = 'Removed tag %s' % ', '.join(names)
3713 message = 'Removed tag %s' % ', '.join(names)
3713 elif not opts.get('force'):
3714 elif not opts.get('force'):
3714 for n in names:
3715 for n in names:
3715 if n in repo.tags():
3716 if n in repo.tags():
3716 raise util.Abort(_('tag \'%s\' already exists '
3717 raise util.Abort(_('tag \'%s\' already exists '
3717 '(use -f to force)') % n)
3718 '(use -f to force)') % n)
3718 if not rev_ and repo.dirstate.parents()[1] != nullid:
3719 if not rev_ and repo.dirstate.parents()[1] != nullid:
3719 raise util.Abort(_('uncommitted merge - please provide a '
3720 raise util.Abort(_('uncommitted merge - please provide a '
3720 'specific revision'))
3721 'specific revision'))
3721 r = repo[rev_].node()
3722 r = repo[rev_].node()
3722
3723
3723 if not message:
3724 if not message:
3724 # we don't translate commit messages
3725 # we don't translate commit messages
3725 message = ('Added tag %s for changeset %s' %
3726 message = ('Added tag %s for changeset %s' %
3726 (', '.join(names), short(r)))
3727 (', '.join(names), short(r)))
3727
3728
3728 date = opts.get('date')
3729 date = opts.get('date')
3729 if date:
3730 if date:
3730 date = util.parsedate(date)
3731 date = util.parsedate(date)
3731
3732
3732 if opts.get('edit'):
3733 if opts.get('edit'):
3733 message = ui.edit(message, ui.username())
3734 message = ui.edit(message, ui.username())
3734
3735
3735 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3736 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3736
3737
3737 def tags(ui, repo):
3738 def tags(ui, repo):
3738 """list repository tags
3739 """list repository tags
3739
3740
3740 This lists both regular and local tags. When the -v/--verbose
3741 This lists both regular and local tags. When the -v/--verbose
3741 switch is used, a third column "local" is printed for local tags.
3742 switch is used, a third column "local" is printed for local tags.
3742
3743
3743 Returns 0 on success.
3744 Returns 0 on success.
3744 """
3745 """
3745
3746
3746 hexfunc = ui.debugflag and hex or short
3747 hexfunc = ui.debugflag and hex or short
3747 tagtype = ""
3748 tagtype = ""
3748
3749
3749 for t, n in reversed(repo.tagslist()):
3750 for t, n in reversed(repo.tagslist()):
3750 if ui.quiet:
3751 if ui.quiet:
3751 ui.write("%s\n" % t)
3752 ui.write("%s\n" % t)
3752 continue
3753 continue
3753
3754
3754 try:
3755 try:
3755 hn = hexfunc(n)
3756 hn = hexfunc(n)
3756 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3757 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3757 except error.LookupError:
3758 except error.LookupError:
3758 r = " ?:%s" % hn
3759 r = " ?:%s" % hn
3759 else:
3760 else:
3760 spaces = " " * (30 - encoding.colwidth(t))
3761 spaces = " " * (30 - encoding.colwidth(t))
3761 if ui.verbose:
3762 if ui.verbose:
3762 if repo.tagtype(t) == 'local':
3763 if repo.tagtype(t) == 'local':
3763 tagtype = " local"
3764 tagtype = " local"
3764 else:
3765 else:
3765 tagtype = ""
3766 tagtype = ""
3766 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3767 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3767
3768
3768 def tip(ui, repo, **opts):
3769 def tip(ui, repo, **opts):
3769 """show the tip revision
3770 """show the tip revision
3770
3771
3771 The tip revision (usually just called the tip) is the changeset
3772 The tip revision (usually just called the tip) is the changeset
3772 most recently added to the repository (and therefore the most
3773 most recently added to the repository (and therefore the most
3773 recently changed head).
3774 recently changed head).
3774
3775
3775 If you have just made a commit, that commit will be the tip. If
3776 If you have just made a commit, that commit will be the tip. If
3776 you have just pulled changes from another repository, the tip of
3777 you have just pulled changes from another repository, the tip of
3777 that repository becomes the current tip. The "tip" tag is special
3778 that repository becomes the current tip. The "tip" tag is special
3778 and cannot be renamed or assigned to a different changeset.
3779 and cannot be renamed or assigned to a different changeset.
3779
3780
3780 Returns 0 on success.
3781 Returns 0 on success.
3781 """
3782 """
3782 displayer = cmdutil.show_changeset(ui, repo, opts)
3783 displayer = cmdutil.show_changeset(ui, repo, opts)
3783 displayer.show(repo[len(repo) - 1])
3784 displayer.show(repo[len(repo) - 1])
3784 displayer.close()
3785 displayer.close()
3785
3786
3786 def unbundle(ui, repo, fname1, *fnames, **opts):
3787 def unbundle(ui, repo, fname1, *fnames, **opts):
3787 """apply one or more changegroup files
3788 """apply one or more changegroup files
3788
3789
3789 Apply one or more compressed changegroup files generated by the
3790 Apply one or more compressed changegroup files generated by the
3790 bundle command.
3791 bundle command.
3791
3792
3792 Returns 0 on success, 1 if an update has unresolved files.
3793 Returns 0 on success, 1 if an update has unresolved files.
3793 """
3794 """
3794 fnames = (fname1,) + fnames
3795 fnames = (fname1,) + fnames
3795
3796
3796 lock = repo.lock()
3797 lock = repo.lock()
3797 try:
3798 try:
3798 for fname in fnames:
3799 for fname in fnames:
3799 f = url.open(ui, fname)
3800 f = url.open(ui, fname)
3800 gen = changegroup.readbundle(f, fname)
3801 gen = changegroup.readbundle(f, fname)
3801 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3802 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3802 lock=lock)
3803 lock=lock)
3803 finally:
3804 finally:
3804 lock.release()
3805 lock.release()
3805
3806
3806 return postincoming(ui, repo, modheads, opts.get('update'), None)
3807 return postincoming(ui, repo, modheads, opts.get('update'), None)
3807
3808
3808 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3809 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3809 """update working directory (or switch revisions)
3810 """update working directory (or switch revisions)
3810
3811
3811 Update the repository's working directory to the specified
3812 Update the repository's working directory to the specified
3812 changeset.
3813 changeset.
3813
3814
3814 If no changeset is specified, attempt to update to the tip of the
3815 If no changeset is specified, attempt to update to the tip of the
3815 current branch. If this changeset is a descendant of the working
3816 current branch. If this changeset is a descendant of the working
3816 directory's parent, update to it, otherwise abort.
3817 directory's parent, update to it, otherwise abort.
3817
3818
3818 The following rules apply when the working directory contains
3819 The following rules apply when the working directory contains
3819 uncommitted changes:
3820 uncommitted changes:
3820
3821
3821 1. If neither -c/--check nor -C/--clean is specified, and if
3822 1. If neither -c/--check nor -C/--clean is specified, and if
3822 the requested changeset is an ancestor or descendant of
3823 the requested changeset is an ancestor or descendant of
3823 the working directory's parent, the uncommitted changes
3824 the working directory's parent, the uncommitted changes
3824 are merged into the requested changeset and the merged
3825 are merged into the requested changeset and the merged
3825 result is left uncommitted. If the requested changeset is
3826 result is left uncommitted. If the requested changeset is
3826 not an ancestor or descendant (that is, it is on another
3827 not an ancestor or descendant (that is, it is on another
3827 branch), the update is aborted and the uncommitted changes
3828 branch), the update is aborted and the uncommitted changes
3828 are preserved.
3829 are preserved.
3829
3830
3830 2. With the -c/--check option, the update is aborted and the
3831 2. With the -c/--check option, the update is aborted and the
3831 uncommitted changes are preserved.
3832 uncommitted changes are preserved.
3832
3833
3833 3. With the -C/--clean option, uncommitted changes are discarded and
3834 3. With the -C/--clean option, uncommitted changes are discarded and
3834 the working directory is updated to the requested changeset.
3835 the working directory is updated to the requested changeset.
3835
3836
3836 Use null as the changeset to remove the working directory (like
3837 Use null as the changeset to remove the working directory (like
3837 :hg:`clone -U`).
3838 :hg:`clone -U`).
3838
3839
3839 If you want to update just one file to an older changeset, use :hg:`revert`.
3840 If you want to update just one file to an older changeset, use :hg:`revert`.
3840
3841
3841 See :hg:`help dates` for a list of formats valid for -d/--date.
3842 See :hg:`help dates` for a list of formats valid for -d/--date.
3842
3843
3843 Returns 0 on success, 1 if there are unresolved files.
3844 Returns 0 on success, 1 if there are unresolved files.
3844 """
3845 """
3845 if rev and node:
3846 if rev and node:
3846 raise util.Abort(_("please specify just one revision"))
3847 raise util.Abort(_("please specify just one revision"))
3847
3848
3848 if not rev:
3849 if not rev:
3849 rev = node
3850 rev = node
3850
3851
3851 if check and clean:
3852 if check and clean:
3852 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3853 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3853
3854
3854 if check:
3855 if check:
3855 # we could use dirty() but we can ignore merge and branch trivia
3856 # we could use dirty() but we can ignore merge and branch trivia
3856 c = repo[None]
3857 c = repo[None]
3857 if c.modified() or c.added() or c.removed():
3858 if c.modified() or c.added() or c.removed():
3858 raise util.Abort(_("uncommitted local changes"))
3859 raise util.Abort(_("uncommitted local changes"))
3859
3860
3860 if date:
3861 if date:
3861 if rev:
3862 if rev:
3862 raise util.Abort(_("you can't specify a revision and a date"))
3863 raise util.Abort(_("you can't specify a revision and a date"))
3863 rev = cmdutil.finddate(ui, repo, date)
3864 rev = cmdutil.finddate(ui, repo, date)
3864
3865
3865 if clean or check:
3866 if clean or check:
3866 return hg.clean(repo, rev)
3867 return hg.clean(repo, rev)
3867 else:
3868 else:
3868 return hg.update(repo, rev)
3869 return hg.update(repo, rev)
3869
3870
3870 def verify(ui, repo):
3871 def verify(ui, repo):
3871 """verify the integrity of the repository
3872 """verify the integrity of the repository
3872
3873
3873 Verify the integrity of the current repository.
3874 Verify the integrity of the current repository.
3874
3875
3875 This will perform an extensive check of the repository's
3876 This will perform an extensive check of the repository's
3876 integrity, validating the hashes and checksums of each entry in
3877 integrity, validating the hashes and checksums of each entry in
3877 the changelog, manifest, and tracked files, as well as the
3878 the changelog, manifest, and tracked files, as well as the
3878 integrity of their crosslinks and indices.
3879 integrity of their crosslinks and indices.
3879
3880
3880 Returns 0 on success, 1 if errors are encountered.
3881 Returns 0 on success, 1 if errors are encountered.
3881 """
3882 """
3882 return hg.verify(repo)
3883 return hg.verify(repo)
3883
3884
3884 def version_(ui):
3885 def version_(ui):
3885 """output version and copyright information"""
3886 """output version and copyright information"""
3886 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3887 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3887 % util.version())
3888 % util.version())
3888 ui.status(_(
3889 ui.status(_(
3889 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3890 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3890 "This is free software; see the source for copying conditions. "
3891 "This is free software; see the source for copying conditions. "
3891 "There is NO\nwarranty; "
3892 "There is NO\nwarranty; "
3892 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3893 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3893 ))
3894 ))
3894
3895
3895 # Command options and aliases are listed here, alphabetically
3896 # Command options and aliases are listed here, alphabetically
3896
3897
3897 globalopts = [
3898 globalopts = [
3898 ('R', 'repository', '',
3899 ('R', 'repository', '',
3899 _('repository root directory or name of overlay bundle file'),
3900 _('repository root directory or name of overlay bundle file'),
3900 _('REPO')),
3901 _('REPO')),
3901 ('', 'cwd', '',
3902 ('', 'cwd', '',
3902 _('change working directory'), _('DIR')),
3903 _('change working directory'), _('DIR')),
3903 ('y', 'noninteractive', None,
3904 ('y', 'noninteractive', None,
3904 _('do not prompt, assume \'yes\' for any required answers')),
3905 _('do not prompt, assume \'yes\' for any required answers')),
3905 ('q', 'quiet', None, _('suppress output')),
3906 ('q', 'quiet', None, _('suppress output')),
3906 ('v', 'verbose', None, _('enable additional output')),
3907 ('v', 'verbose', None, _('enable additional output')),
3907 ('', 'config', [],
3908 ('', 'config', [],
3908 _('set/override config option (use \'section.name=value\')'),
3909 _('set/override config option (use \'section.name=value\')'),
3909 _('CONFIG')),
3910 _('CONFIG')),
3910 ('', 'debug', None, _('enable debugging output')),
3911 ('', 'debug', None, _('enable debugging output')),
3911 ('', 'debugger', None, _('start debugger')),
3912 ('', 'debugger', None, _('start debugger')),
3912 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3913 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3913 _('ENCODE')),
3914 _('ENCODE')),
3914 ('', 'encodingmode', encoding.encodingmode,
3915 ('', 'encodingmode', encoding.encodingmode,
3915 _('set the charset encoding mode'), _('MODE')),
3916 _('set the charset encoding mode'), _('MODE')),
3916 ('', 'traceback', None, _('always print a traceback on exception')),
3917 ('', 'traceback', None, _('always print a traceback on exception')),
3917 ('', 'time', None, _('time how long the command takes')),
3918 ('', 'time', None, _('time how long the command takes')),
3918 ('', 'profile', None, _('print command execution profile')),
3919 ('', 'profile', None, _('print command execution profile')),
3919 ('', 'version', None, _('output version information and exit')),
3920 ('', 'version', None, _('output version information and exit')),
3920 ('h', 'help', None, _('display help and exit')),
3921 ('h', 'help', None, _('display help and exit')),
3921 ]
3922 ]
3922
3923
3923 dryrunopts = [('n', 'dry-run', None,
3924 dryrunopts = [('n', 'dry-run', None,
3924 _('do not perform actions, just print output'))]
3925 _('do not perform actions, just print output'))]
3925
3926
3926 remoteopts = [
3927 remoteopts = [
3927 ('e', 'ssh', '',
3928 ('e', 'ssh', '',
3928 _('specify ssh command to use'), _('CMD')),
3929 _('specify ssh command to use'), _('CMD')),
3929 ('', 'remotecmd', '',
3930 ('', 'remotecmd', '',
3930 _('specify hg command to run on the remote side'), _('CMD')),
3931 _('specify hg command to run on the remote side'), _('CMD')),
3931 ]
3932 ]
3932
3933
3933 walkopts = [
3934 walkopts = [
3934 ('I', 'include', [],
3935 ('I', 'include', [],
3935 _('include names matching the given patterns'), _('PATTERN')),
3936 _('include names matching the given patterns'), _('PATTERN')),
3936 ('X', 'exclude', [],
3937 ('X', 'exclude', [],
3937 _('exclude names matching the given patterns'), _('PATTERN')),
3938 _('exclude names matching the given patterns'), _('PATTERN')),
3938 ]
3939 ]
3939
3940
3940 commitopts = [
3941 commitopts = [
3941 ('m', 'message', '',
3942 ('m', 'message', '',
3942 _('use text as commit message'), _('TEXT')),
3943 _('use text as commit message'), _('TEXT')),
3943 ('l', 'logfile', '',
3944 ('l', 'logfile', '',
3944 _('read commit message from file'), _('FILE')),
3945 _('read commit message from file'), _('FILE')),
3945 ]
3946 ]
3946
3947
3947 commitopts2 = [
3948 commitopts2 = [
3948 ('d', 'date', '',
3949 ('d', 'date', '',
3949 _('record datecode as commit date'), _('DATE')),
3950 _('record datecode as commit date'), _('DATE')),
3950 ('u', 'user', '',
3951 ('u', 'user', '',
3951 _('record the specified user as committer'), _('USER')),
3952 _('record the specified user as committer'), _('USER')),
3952 ]
3953 ]
3953
3954
3954 templateopts = [
3955 templateopts = [
3955 ('', 'style', '',
3956 ('', 'style', '',
3956 _('display using template map file'), _('STYLE')),
3957 _('display using template map file'), _('STYLE')),
3957 ('', 'template', '',
3958 ('', 'template', '',
3958 _('display with template'), _('TEMPLATE')),
3959 _('display with template'), _('TEMPLATE')),
3959 ]
3960 ]
3960
3961
3961 logopts = [
3962 logopts = [
3962 ('p', 'patch', None, _('show patch')),
3963 ('p', 'patch', None, _('show patch')),
3963 ('g', 'git', None, _('use git extended diff format')),
3964 ('g', 'git', None, _('use git extended diff format')),
3964 ('l', 'limit', '',
3965 ('l', 'limit', '',
3965 _('limit number of changes displayed'), _('NUM')),
3966 _('limit number of changes displayed'), _('NUM')),
3966 ('M', 'no-merges', None, _('do not show merges')),
3967 ('M', 'no-merges', None, _('do not show merges')),
3967 ('', 'stat', None, _('output diffstat-style summary of changes')),
3968 ('', 'stat', None, _('output diffstat-style summary of changes')),
3968 ] + templateopts
3969 ] + templateopts
3969
3970
3970 diffopts = [
3971 diffopts = [
3971 ('a', 'text', None, _('treat all files as text')),
3972 ('a', 'text', None, _('treat all files as text')),
3972 ('g', 'git', None, _('use git extended diff format')),
3973 ('g', 'git', None, _('use git extended diff format')),
3973 ('', 'nodates', None, _('omit dates from diff headers'))
3974 ('', 'nodates', None, _('omit dates from diff headers'))
3974 ]
3975 ]
3975
3976
3976 diffopts2 = [
3977 diffopts2 = [
3977 ('p', 'show-function', None, _('show which function each change is in')),
3978 ('p', 'show-function', None, _('show which function each change is in')),
3978 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3979 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3979 ('w', 'ignore-all-space', None,
3980 ('w', 'ignore-all-space', None,
3980 _('ignore white space when comparing lines')),
3981 _('ignore white space when comparing lines')),
3981 ('b', 'ignore-space-change', None,
3982 ('b', 'ignore-space-change', None,
3982 _('ignore changes in the amount of white space')),
3983 _('ignore changes in the amount of white space')),
3983 ('B', 'ignore-blank-lines', None,
3984 ('B', 'ignore-blank-lines', None,
3984 _('ignore changes whose lines are all blank')),
3985 _('ignore changes whose lines are all blank')),
3985 ('U', 'unified', '',
3986 ('U', 'unified', '',
3986 _('number of lines of context to show'), _('NUM')),
3987 _('number of lines of context to show'), _('NUM')),
3987 ('', 'stat', None, _('output diffstat-style summary of changes')),
3988 ('', 'stat', None, _('output diffstat-style summary of changes')),
3988 ]
3989 ]
3989
3990
3990 similarityopts = [
3991 similarityopts = [
3991 ('s', 'similarity', '',
3992 ('s', 'similarity', '',
3992 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3993 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3993 ]
3994 ]
3994
3995
3995 subrepoopts = [
3996 subrepoopts = [
3996 ('S', 'subrepos', None,
3997 ('S', 'subrepos', None,
3997 _('recurse into subrepositories'))
3998 _('recurse into subrepositories'))
3998 ]
3999 ]
3999
4000
4000 table = {
4001 table = {
4001 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
4002 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
4002 "addremove":
4003 "addremove":
4003 (addremove, similarityopts + walkopts + dryrunopts,
4004 (addremove, similarityopts + walkopts + dryrunopts,
4004 _('[OPTION]... [FILE]...')),
4005 _('[OPTION]... [FILE]...')),
4005 "^annotate|blame":
4006 "^annotate|blame":
4006 (annotate,
4007 (annotate,
4007 [('r', 'rev', '',
4008 [('r', 'rev', '',
4008 _('annotate the specified revision'), _('REV')),
4009 _('annotate the specified revision'), _('REV')),
4009 ('', 'follow', None,
4010 ('', 'follow', None,
4010 _('follow copies/renames and list the filename (DEPRECATED)')),
4011 _('follow copies/renames and list the filename (DEPRECATED)')),
4011 ('', 'no-follow', None, _("don't follow copies and renames")),
4012 ('', 'no-follow', None, _("don't follow copies and renames")),
4012 ('a', 'text', None, _('treat all files as text')),
4013 ('a', 'text', None, _('treat all files as text')),
4013 ('u', 'user', None, _('list the author (long with -v)')),
4014 ('u', 'user', None, _('list the author (long with -v)')),
4014 ('f', 'file', None, _('list the filename')),
4015 ('f', 'file', None, _('list the filename')),
4015 ('d', 'date', None, _('list the date (short with -q)')),
4016 ('d', 'date', None, _('list the date (short with -q)')),
4016 ('n', 'number', None, _('list the revision number (default)')),
4017 ('n', 'number', None, _('list the revision number (default)')),
4017 ('c', 'changeset', None, _('list the changeset')),
4018 ('c', 'changeset', None, _('list the changeset')),
4018 ('l', 'line-number', None,
4019 ('l', 'line-number', None,
4019 _('show line number at the first appearance'))
4020 _('show line number at the first appearance'))
4020 ] + walkopts,
4021 ] + walkopts,
4021 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4022 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4022 "archive":
4023 "archive":
4023 (archive,
4024 (archive,
4024 [('', 'no-decode', None, _('do not pass files through decoders')),
4025 [('', 'no-decode', None, _('do not pass files through decoders')),
4025 ('p', 'prefix', '',
4026 ('p', 'prefix', '',
4026 _('directory prefix for files in archive'), _('PREFIX')),
4027 _('directory prefix for files in archive'), _('PREFIX')),
4027 ('r', 'rev', '',
4028 ('r', 'rev', '',
4028 _('revision to distribute'), _('REV')),
4029 _('revision to distribute'), _('REV')),
4029 ('t', 'type', '',
4030 ('t', 'type', '',
4030 _('type of distribution to create'), _('TYPE')),
4031 _('type of distribution to create'), _('TYPE')),
4031 ] + walkopts,
4032 ] + walkopts,
4032 _('[OPTION]... DEST')),
4033 _('[OPTION]... DEST')),
4033 "backout":
4034 "backout":
4034 (backout,
4035 (backout,
4035 [('', 'merge', None,
4036 [('', 'merge', None,
4036 _('merge with old dirstate parent after backout')),
4037 _('merge with old dirstate parent after backout')),
4037 ('', 'parent', '',
4038 ('', 'parent', '',
4038 _('parent to choose when backing out merge'), _('REV')),
4039 _('parent to choose when backing out merge'), _('REV')),
4039 ('r', 'rev', '',
4040 ('r', 'rev', '',
4040 _('revision to backout'), _('REV')),
4041 _('revision to backout'), _('REV')),
4041 ] + walkopts + commitopts + commitopts2,
4042 ] + walkopts + commitopts + commitopts2,
4042 _('[OPTION]... [-r] REV')),
4043 _('[OPTION]... [-r] REV')),
4043 "bisect":
4044 "bisect":
4044 (bisect,
4045 (bisect,
4045 [('r', 'reset', False, _('reset bisect state')),
4046 [('r', 'reset', False, _('reset bisect state')),
4046 ('g', 'good', False, _('mark changeset good')),
4047 ('g', 'good', False, _('mark changeset good')),
4047 ('b', 'bad', False, _('mark changeset bad')),
4048 ('b', 'bad', False, _('mark changeset bad')),
4048 ('s', 'skip', False, _('skip testing changeset')),
4049 ('s', 'skip', False, _('skip testing changeset')),
4049 ('c', 'command', '',
4050 ('c', 'command', '',
4050 _('use command to check changeset state'), _('CMD')),
4051 _('use command to check changeset state'), _('CMD')),
4051 ('U', 'noupdate', False, _('do not update to target'))],
4052 ('U', 'noupdate', False, _('do not update to target'))],
4052 _("[-gbsr] [-U] [-c CMD] [REV]")),
4053 _("[-gbsr] [-U] [-c CMD] [REV]")),
4053 "branch":
4054 "branch":
4054 (branch,
4055 (branch,
4055 [('f', 'force', None,
4056 [('f', 'force', None,
4056 _('set branch name even if it shadows an existing branch')),
4057 _('set branch name even if it shadows an existing branch')),
4057 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4058 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4058 _('[-fC] [NAME]')),
4059 _('[-fC] [NAME]')),
4059 "branches":
4060 "branches":
4060 (branches,
4061 (branches,
4061 [('a', 'active', False,
4062 [('a', 'active', False,
4062 _('show only branches that have unmerged heads')),
4063 _('show only branches that have unmerged heads')),
4063 ('c', 'closed', False,
4064 ('c', 'closed', False,
4064 _('show normal and closed branches'))],
4065 _('show normal and closed branches'))],
4065 _('[-ac]')),
4066 _('[-ac]')),
4066 "bundle":
4067 "bundle":
4067 (bundle,
4068 (bundle,
4068 [('f', 'force', None,
4069 [('f', 'force', None,
4069 _('run even when the destination is unrelated')),
4070 _('run even when the destination is unrelated')),
4070 ('r', 'rev', [],
4071 ('r', 'rev', [],
4071 _('a changeset intended to be added to the destination'),
4072 _('a changeset intended to be added to the destination'),
4072 _('REV')),
4073 _('REV')),
4073 ('b', 'branch', [],
4074 ('b', 'branch', [],
4074 _('a specific branch you would like to bundle'),
4075 _('a specific branch you would like to bundle'),
4075 _('BRANCH')),
4076 _('BRANCH')),
4076 ('', 'base', [],
4077 ('', 'base', [],
4077 _('a base changeset assumed to be available at the destination'),
4078 _('a base changeset assumed to be available at the destination'),
4078 _('REV')),
4079 _('REV')),
4079 ('a', 'all', None, _('bundle all changesets in the repository')),
4080 ('a', 'all', None, _('bundle all changesets in the repository')),
4080 ('t', 'type', 'bzip2',
4081 ('t', 'type', 'bzip2',
4081 _('bundle compression type to use'), _('TYPE')),
4082 _('bundle compression type to use'), _('TYPE')),
4082 ] + remoteopts,
4083 ] + remoteopts,
4083 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4084 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4084 "cat":
4085 "cat":
4085 (cat,
4086 (cat,
4086 [('o', 'output', '',
4087 [('o', 'output', '',
4087 _('print output to file with formatted name'), _('FORMAT')),
4088 _('print output to file with formatted name'), _('FORMAT')),
4088 ('r', 'rev', '',
4089 ('r', 'rev', '',
4089 _('print the given revision'), _('REV')),
4090 _('print the given revision'), _('REV')),
4090 ('', 'decode', None, _('apply any matching decode filter')),
4091 ('', 'decode', None, _('apply any matching decode filter')),
4091 ] + walkopts,
4092 ] + walkopts,
4092 _('[OPTION]... FILE...')),
4093 _('[OPTION]... FILE...')),
4093 "^clone":
4094 "^clone":
4094 (clone,
4095 (clone,
4095 [('U', 'noupdate', None,
4096 [('U', 'noupdate', None,
4096 _('the clone will include an empty working copy (only a repository)')),
4097 _('the clone will include an empty working copy (only a repository)')),
4097 ('u', 'updaterev', '',
4098 ('u', 'updaterev', '',
4098 _('revision, tag or branch to check out'), _('REV')),
4099 _('revision, tag or branch to check out'), _('REV')),
4099 ('r', 'rev', [],
4100 ('r', 'rev', [],
4100 _('include the specified changeset'), _('REV')),
4101 _('include the specified changeset'), _('REV')),
4101 ('b', 'branch', [],
4102 ('b', 'branch', [],
4102 _('clone only the specified branch'), _('BRANCH')),
4103 _('clone only the specified branch'), _('BRANCH')),
4103 ('', 'pull', None, _('use pull protocol to copy metadata')),
4104 ('', 'pull', None, _('use pull protocol to copy metadata')),
4104 ('', 'uncompressed', None,
4105 ('', 'uncompressed', None,
4105 _('use uncompressed transfer (fast over LAN)')),
4106 _('use uncompressed transfer (fast over LAN)')),
4106 ] + remoteopts,
4107 ] + remoteopts,
4107 _('[OPTION]... SOURCE [DEST]')),
4108 _('[OPTION]... SOURCE [DEST]')),
4108 "^commit|ci":
4109 "^commit|ci":
4109 (commit,
4110 (commit,
4110 [('A', 'addremove', None,
4111 [('A', 'addremove', None,
4111 _('mark new/missing files as added/removed before committing')),
4112 _('mark new/missing files as added/removed before committing')),
4112 ('', 'close-branch', None,
4113 ('', 'close-branch', None,
4113 _('mark a branch as closed, hiding it from the branch list')),
4114 _('mark a branch as closed, hiding it from the branch list')),
4114 ] + walkopts + commitopts + commitopts2,
4115 ] + walkopts + commitopts + commitopts2,
4115 _('[OPTION]... [FILE]...')),
4116 _('[OPTION]... [FILE]...')),
4116 "copy|cp":
4117 "copy|cp":
4117 (copy,
4118 (copy,
4118 [('A', 'after', None, _('record a copy that has already occurred')),
4119 [('A', 'after', None, _('record a copy that has already occurred')),
4119 ('f', 'force', None,
4120 ('f', 'force', None,
4120 _('forcibly copy over an existing managed file')),
4121 _('forcibly copy over an existing managed file')),
4121 ] + walkopts + dryrunopts,
4122 ] + walkopts + dryrunopts,
4122 _('[OPTION]... [SOURCE]... DEST')),
4123 _('[OPTION]... [SOURCE]... DEST')),
4123 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4124 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4124 "debugbuilddag":
4125 "debugbuilddag":
4125 (debugbuilddag,
4126 (debugbuilddag,
4126 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4127 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4127 ('a', 'appended-file', None, _('add single file all revs append to')),
4128 ('a', 'appended-file', None, _('add single file all revs append to')),
4128 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4129 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4129 ('n', 'new-file', None, _('add new file at each rev')),
4130 ('n', 'new-file', None, _('add new file at each rev')),
4130 ],
4131 ],
4131 _('[OPTION]... TEXT')),
4132 _('[OPTION]... TEXT')),
4132 "debugcheckstate": (debugcheckstate, [], ''),
4133 "debugcheckstate": (debugcheckstate, [], ''),
4133 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4134 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4134 "debugcomplete":
4135 "debugcomplete":
4135 (debugcomplete,
4136 (debugcomplete,
4136 [('o', 'options', None, _('show the command options'))],
4137 [('o', 'options', None, _('show the command options'))],
4137 _('[-o] CMD')),
4138 _('[-o] CMD')),
4138 "debugdag":
4139 "debugdag":
4139 (debugdag,
4140 (debugdag,
4140 [('t', 'tags', None, _('use tags as labels')),
4141 [('t', 'tags', None, _('use tags as labels')),
4141 ('b', 'branches', None, _('annotate with branch names')),
4142 ('b', 'branches', None, _('annotate with branch names')),
4142 ('', 'dots', None, _('use dots for runs')),
4143 ('', 'dots', None, _('use dots for runs')),
4143 ('s', 'spaces', None, _('separate elements by spaces')),
4144 ('s', 'spaces', None, _('separate elements by spaces')),
4144 ],
4145 ],
4145 _('[OPTION]... [FILE [REV]...]')),
4146 _('[OPTION]... [FILE [REV]...]')),
4146 "debugdate":
4147 "debugdate":
4147 (debugdate,
4148 (debugdate,
4148 [('e', 'extended', None, _('try extended date formats'))],
4149 [('e', 'extended', None, _('try extended date formats'))],
4149 _('[-e] DATE [RANGE]')),
4150 _('[-e] DATE [RANGE]')),
4150 "debugdata": (debugdata, [], _('FILE REV')),
4151 "debugdata": (debugdata, [], _('FILE REV')),
4151 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4152 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4152 "debugindex": (debugindex, [], _('FILE')),
4153 "debugindex": (debugindex, [], _('FILE')),
4153 "debugindexdot": (debugindexdot, [], _('FILE')),
4154 "debugindexdot": (debugindexdot, [], _('FILE')),
4154 "debuginstall": (debuginstall, [], ''),
4155 "debuginstall": (debuginstall, [], ''),
4155 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4156 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4156 "debugrebuildstate":
4157 "debugrebuildstate":
4157 (debugrebuildstate,
4158 (debugrebuildstate,
4158 [('r', 'rev', '',
4159 [('r', 'rev', '',
4159 _('revision to rebuild to'), _('REV'))],
4160 _('revision to rebuild to'), _('REV'))],
4160 _('[-r REV] [REV]')),
4161 _('[-r REV] [REV]')),
4161 "debugrename":
4162 "debugrename":
4162 (debugrename,
4163 (debugrename,
4163 [('r', 'rev', '',
4164 [('r', 'rev', '',
4164 _('revision to debug'), _('REV'))],
4165 _('revision to debug'), _('REV'))],
4165 _('[-r REV] FILE')),
4166 _('[-r REV] FILE')),
4166 "debugrevspec":
4167 "debugrevspec":
4167 (debugrevspec, [], ('REVSPEC')),
4168 (debugrevspec, [], ('REVSPEC')),
4168 "debugsetparents":
4169 "debugsetparents":
4169 (debugsetparents, [], _('REV1 [REV2]')),
4170 (debugsetparents, [], _('REV1 [REV2]')),
4170 "debugstate":
4171 "debugstate":
4171 (debugstate,
4172 (debugstate,
4172 [('', 'nodates', None, _('do not display the saved mtime'))],
4173 [('', 'nodates', None, _('do not display the saved mtime'))],
4173 _('[OPTION]...')),
4174 _('[OPTION]...')),
4174 "debugsub":
4175 "debugsub":
4175 (debugsub,
4176 (debugsub,
4176 [('r', 'rev', '',
4177 [('r', 'rev', '',
4177 _('revision to check'), _('REV'))],
4178 _('revision to check'), _('REV'))],
4178 _('[-r REV] [REV]')),
4179 _('[-r REV] [REV]')),
4179 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4180 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4180 "^diff":
4181 "^diff":
4181 (diff,
4182 (diff,
4182 [('r', 'rev', [],
4183 [('r', 'rev', [],
4183 _('revision'), _('REV')),
4184 _('revision'), _('REV')),
4184 ('c', 'change', '',
4185 ('c', 'change', '',
4185 _('change made by revision'), _('REV'))
4186 _('change made by revision'), _('REV'))
4186 ] + diffopts + diffopts2 + walkopts,
4187 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4187 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4188 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4188 "^export":
4189 "^export":
4189 (export,
4190 (export,
4190 [('o', 'output', '',
4191 [('o', 'output', '',
4191 _('print output to file with formatted name'), _('FORMAT')),
4192 _('print output to file with formatted name'), _('FORMAT')),
4192 ('', 'switch-parent', None, _('diff against the second parent')),
4193 ('', 'switch-parent', None, _('diff against the second parent')),
4193 ('r', 'rev', [],
4194 ('r', 'rev', [],
4194 _('revisions to export'), _('REV')),
4195 _('revisions to export'), _('REV')),
4195 ] + diffopts,
4196 ] + diffopts,
4196 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4197 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4197 "^forget":
4198 "^forget":
4198 (forget,
4199 (forget,
4199 [] + walkopts,
4200 [] + walkopts,
4200 _('[OPTION]... FILE...')),
4201 _('[OPTION]... FILE...')),
4201 "grep":
4202 "grep":
4202 (grep,
4203 (grep,
4203 [('0', 'print0', None, _('end fields with NUL')),
4204 [('0', 'print0', None, _('end fields with NUL')),
4204 ('', 'all', None, _('print all revisions that match')),
4205 ('', 'all', None, _('print all revisions that match')),
4205 ('f', 'follow', None,
4206 ('f', 'follow', None,
4206 _('follow changeset history,'
4207 _('follow changeset history,'
4207 ' or file history across copies and renames')),
4208 ' or file history across copies and renames')),
4208 ('i', 'ignore-case', None, _('ignore case when matching')),
4209 ('i', 'ignore-case', None, _('ignore case when matching')),
4209 ('l', 'files-with-matches', None,
4210 ('l', 'files-with-matches', None,
4210 _('print only filenames and revisions that match')),
4211 _('print only filenames and revisions that match')),
4211 ('n', 'line-number', None, _('print matching line numbers')),
4212 ('n', 'line-number', None, _('print matching line numbers')),
4212 ('r', 'rev', [],
4213 ('r', 'rev', [],
4213 _('only search files changed within revision range'), _('REV')),
4214 _('only search files changed within revision range'), _('REV')),
4214 ('u', 'user', None, _('list the author (long with -v)')),
4215 ('u', 'user', None, _('list the author (long with -v)')),
4215 ('d', 'date', None, _('list the date (short with -q)')),
4216 ('d', 'date', None, _('list the date (short with -q)')),
4216 ] + walkopts,
4217 ] + walkopts,
4217 _('[OPTION]... PATTERN [FILE]...')),
4218 _('[OPTION]... PATTERN [FILE]...')),
4218 "heads":
4219 "heads":
4219 (heads,
4220 (heads,
4220 [('r', 'rev', '',
4221 [('r', 'rev', '',
4221 _('show only heads which are descendants of REV'), _('REV')),
4222 _('show only heads which are descendants of REV'), _('REV')),
4222 ('t', 'topo', False, _('show topological heads only')),
4223 ('t', 'topo', False, _('show topological heads only')),
4223 ('a', 'active', False,
4224 ('a', 'active', False,
4224 _('show active branchheads only (DEPRECATED)')),
4225 _('show active branchheads only (DEPRECATED)')),
4225 ('c', 'closed', False,
4226 ('c', 'closed', False,
4226 _('show normal and closed branch heads')),
4227 _('show normal and closed branch heads')),
4227 ] + templateopts,
4228 ] + templateopts,
4228 _('[-ac] [-r REV] [REV]...')),
4229 _('[-ac] [-r REV] [REV]...')),
4229 "help": (help_, [], _('[TOPIC]')),
4230 "help": (help_, [], _('[TOPIC]')),
4230 "identify|id":
4231 "identify|id":
4231 (identify,
4232 (identify,
4232 [('r', 'rev', '',
4233 [('r', 'rev', '',
4233 _('identify the specified revision'), _('REV')),
4234 _('identify the specified revision'), _('REV')),
4234 ('n', 'num', None, _('show local revision number')),
4235 ('n', 'num', None, _('show local revision number')),
4235 ('i', 'id', None, _('show global revision id')),
4236 ('i', 'id', None, _('show global revision id')),
4236 ('b', 'branch', None, _('show branch')),
4237 ('b', 'branch', None, _('show branch')),
4237 ('t', 'tags', None, _('show tags'))],
4238 ('t', 'tags', None, _('show tags'))],
4238 _('[-nibt] [-r REV] [SOURCE]')),
4239 _('[-nibt] [-r REV] [SOURCE]')),
4239 "import|patch":
4240 "import|patch":
4240 (import_,
4241 (import_,
4241 [('p', 'strip', 1,
4242 [('p', 'strip', 1,
4242 _('directory strip option for patch. This has the same '
4243 _('directory strip option for patch. This has the same '
4243 'meaning as the corresponding patch option'),
4244 'meaning as the corresponding patch option'),
4244 _('NUM')),
4245 _('NUM')),
4245 ('b', 'base', '',
4246 ('b', 'base', '',
4246 _('base path'), _('PATH')),
4247 _('base path'), _('PATH')),
4247 ('f', 'force', None,
4248 ('f', 'force', None,
4248 _('skip check for outstanding uncommitted changes')),
4249 _('skip check for outstanding uncommitted changes')),
4249 ('', 'no-commit', None,
4250 ('', 'no-commit', None,
4250 _("don't commit, just update the working directory")),
4251 _("don't commit, just update the working directory")),
4251 ('', 'exact', None,
4252 ('', 'exact', None,
4252 _('apply patch to the nodes from which it was generated')),
4253 _('apply patch to the nodes from which it was generated')),
4253 ('', 'import-branch', None,
4254 ('', 'import-branch', None,
4254 _('use any branch information in patch (implied by --exact)'))] +
4255 _('use any branch information in patch (implied by --exact)'))] +
4255 commitopts + commitopts2 + similarityopts,
4256 commitopts + commitopts2 + similarityopts,
4256 _('[OPTION]... PATCH...')),
4257 _('[OPTION]... PATCH...')),
4257 "incoming|in":
4258 "incoming|in":
4258 (incoming,
4259 (incoming,
4259 [('f', 'force', None,
4260 [('f', 'force', None,
4260 _('run even if remote repository is unrelated')),
4261 _('run even if remote repository is unrelated')),
4261 ('n', 'newest-first', None, _('show newest record first')),
4262 ('n', 'newest-first', None, _('show newest record first')),
4262 ('', 'bundle', '',
4263 ('', 'bundle', '',
4263 _('file to store the bundles into'), _('FILE')),
4264 _('file to store the bundles into'), _('FILE')),
4264 ('r', 'rev', [],
4265 ('r', 'rev', [],
4265 _('a remote changeset intended to be added'), _('REV')),
4266 _('a remote changeset intended to be added'), _('REV')),
4266 ('b', 'branch', [],
4267 ('b', 'branch', [],
4267 _('a specific branch you would like to pull'), _('BRANCH')),
4268 _('a specific branch you would like to pull'), _('BRANCH')),
4268 ] + logopts + remoteopts,
4269 ] + logopts + remoteopts,
4269 _('[-p] [-n] [-M] [-f] [-r REV]...'
4270 _('[-p] [-n] [-M] [-f] [-r REV]...'
4270 ' [--bundle FILENAME] [SOURCE]')),
4271 ' [--bundle FILENAME] [SOURCE]')),
4271 "^init":
4272 "^init":
4272 (init,
4273 (init,
4273 remoteopts,
4274 remoteopts,
4274 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4275 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4275 "locate":
4276 "locate":
4276 (locate,
4277 (locate,
4277 [('r', 'rev', '',
4278 [('r', 'rev', '',
4278 _('search the repository as it is in REV'), _('REV')),
4279 _('search the repository as it is in REV'), _('REV')),
4279 ('0', 'print0', None,
4280 ('0', 'print0', None,
4280 _('end filenames with NUL, for use with xargs')),
4281 _('end filenames with NUL, for use with xargs')),
4281 ('f', 'fullpath', None,
4282 ('f', 'fullpath', None,
4282 _('print complete paths from the filesystem root')),
4283 _('print complete paths from the filesystem root')),
4283 ] + walkopts,
4284 ] + walkopts,
4284 _('[OPTION]... [PATTERN]...')),
4285 _('[OPTION]... [PATTERN]...')),
4285 "^log|history":
4286 "^log|history":
4286 (log,
4287 (log,
4287 [('f', 'follow', None,
4288 [('f', 'follow', None,
4288 _('follow changeset history,'
4289 _('follow changeset history,'
4289 ' or file history across copies and renames')),
4290 ' or file history across copies and renames')),
4290 ('', 'follow-first', None,
4291 ('', 'follow-first', None,
4291 _('only follow the first parent of merge changesets')),
4292 _('only follow the first parent of merge changesets')),
4292 ('d', 'date', '',
4293 ('d', 'date', '',
4293 _('show revisions matching date spec'), _('DATE')),
4294 _('show revisions matching date spec'), _('DATE')),
4294 ('C', 'copies', None, _('show copied files')),
4295 ('C', 'copies', None, _('show copied files')),
4295 ('k', 'keyword', [],
4296 ('k', 'keyword', [],
4296 _('do case-insensitive search for a given text'), _('TEXT')),
4297 _('do case-insensitive search for a given text'), _('TEXT')),
4297 ('r', 'rev', [],
4298 ('r', 'rev', [],
4298 _('show the specified revision or range'), _('REV')),
4299 _('show the specified revision or range'), _('REV')),
4299 ('', 'removed', None, _('include revisions where files were removed')),
4300 ('', 'removed', None, _('include revisions where files were removed')),
4300 ('m', 'only-merges', None, _('show only merges')),
4301 ('m', 'only-merges', None, _('show only merges')),
4301 ('u', 'user', [],
4302 ('u', 'user', [],
4302 _('revisions committed by user'), _('USER')),
4303 _('revisions committed by user'), _('USER')),
4303 ('', 'only-branch', [],
4304 ('', 'only-branch', [],
4304 _('show only changesets within the given named branch (DEPRECATED)'),
4305 _('show only changesets within the given named branch (DEPRECATED)'),
4305 _('BRANCH')),
4306 _('BRANCH')),
4306 ('b', 'branch', [],
4307 ('b', 'branch', [],
4307 _('show changesets within the given named branch'), _('BRANCH')),
4308 _('show changesets within the given named branch'), _('BRANCH')),
4308 ('P', 'prune', [],
4309 ('P', 'prune', [],
4309 _('do not display revision or any of its ancestors'), _('REV')),
4310 _('do not display revision or any of its ancestors'), _('REV')),
4310 ] + logopts + walkopts,
4311 ] + logopts + walkopts,
4311 _('[OPTION]... [FILE]')),
4312 _('[OPTION]... [FILE]')),
4312 "manifest":
4313 "manifest":
4313 (manifest,
4314 (manifest,
4314 [('r', 'rev', '',
4315 [('r', 'rev', '',
4315 _('revision to display'), _('REV'))],
4316 _('revision to display'), _('REV'))],
4316 _('[-r REV]')),
4317 _('[-r REV]')),
4317 "^merge":
4318 "^merge":
4318 (merge,
4319 (merge,
4319 [('f', 'force', None, _('force a merge with outstanding changes')),
4320 [('f', 'force', None, _('force a merge with outstanding changes')),
4320 ('r', 'rev', '',
4321 ('r', 'rev', '',
4321 _('revision to merge'), _('REV')),
4322 _('revision to merge'), _('REV')),
4322 ('P', 'preview', None,
4323 ('P', 'preview', None,
4323 _('review revisions to merge (no merge is performed)'))],
4324 _('review revisions to merge (no merge is performed)'))],
4324 _('[-P] [-f] [[-r] REV]')),
4325 _('[-P] [-f] [[-r] REV]')),
4325 "outgoing|out":
4326 "outgoing|out":
4326 (outgoing,
4327 (outgoing,
4327 [('f', 'force', None,
4328 [('f', 'force', None,
4328 _('run even when the destination is unrelated')),
4329 _('run even when the destination is unrelated')),
4329 ('r', 'rev', [],
4330 ('r', 'rev', [],
4330 _('a changeset intended to be included in the destination'),
4331 _('a changeset intended to be included in the destination'),
4331 _('REV')),
4332 _('REV')),
4332 ('n', 'newest-first', None, _('show newest record first')),
4333 ('n', 'newest-first', None, _('show newest record first')),
4333 ('b', 'branch', [],
4334 ('b', 'branch', [],
4334 _('a specific branch you would like to push'), _('BRANCH')),
4335 _('a specific branch you would like to push'), _('BRANCH')),
4335 ] + logopts + remoteopts,
4336 ] + logopts + remoteopts,
4336 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4337 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4337 "parents":
4338 "parents":
4338 (parents,
4339 (parents,
4339 [('r', 'rev', '',
4340 [('r', 'rev', '',
4340 _('show parents of the specified revision'), _('REV')),
4341 _('show parents of the specified revision'), _('REV')),
4341 ] + templateopts,
4342 ] + templateopts,
4342 _('[-r REV] [FILE]')),
4343 _('[-r REV] [FILE]')),
4343 "paths": (paths, [], _('[NAME]')),
4344 "paths": (paths, [], _('[NAME]')),
4344 "^pull":
4345 "^pull":
4345 (pull,
4346 (pull,
4346 [('u', 'update', None,
4347 [('u', 'update', None,
4347 _('update to new branch head if changesets were pulled')),
4348 _('update to new branch head if changesets were pulled')),
4348 ('f', 'force', None,
4349 ('f', 'force', None,
4349 _('run even when remote repository is unrelated')),
4350 _('run even when remote repository is unrelated')),
4350 ('r', 'rev', [],
4351 ('r', 'rev', [],
4351 _('a remote changeset intended to be added'), _('REV')),
4352 _('a remote changeset intended to be added'), _('REV')),
4352 ('b', 'branch', [],
4353 ('b', 'branch', [],
4353 _('a specific branch you would like to pull'), _('BRANCH')),
4354 _('a specific branch you would like to pull'), _('BRANCH')),
4354 ] + remoteopts,
4355 ] + remoteopts,
4355 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4356 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4356 "^push":
4357 "^push":
4357 (push,
4358 (push,
4358 [('f', 'force', None, _('force push')),
4359 [('f', 'force', None, _('force push')),
4359 ('r', 'rev', [],
4360 ('r', 'rev', [],
4360 _('a changeset intended to be included in the destination'),
4361 _('a changeset intended to be included in the destination'),
4361 _('REV')),
4362 _('REV')),
4362 ('b', 'branch', [],
4363 ('b', 'branch', [],
4363 _('a specific branch you would like to push'), _('BRANCH')),
4364 _('a specific branch you would like to push'), _('BRANCH')),
4364 ('', 'new-branch', False, _('allow pushing a new branch')),
4365 ('', 'new-branch', False, _('allow pushing a new branch')),
4365 ] + remoteopts,
4366 ] + remoteopts,
4366 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4367 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4367 "recover": (recover, []),
4368 "recover": (recover, []),
4368 "^remove|rm":
4369 "^remove|rm":
4369 (remove,
4370 (remove,
4370 [('A', 'after', None, _('record delete for missing files')),
4371 [('A', 'after', None, _('record delete for missing files')),
4371 ('f', 'force', None,
4372 ('f', 'force', None,
4372 _('remove (and delete) file even if added or modified')),
4373 _('remove (and delete) file even if added or modified')),
4373 ] + walkopts,
4374 ] + walkopts,
4374 _('[OPTION]... FILE...')),
4375 _('[OPTION]... FILE...')),
4375 "rename|mv":
4376 "rename|mv":
4376 (rename,
4377 (rename,
4377 [('A', 'after', None, _('record a rename that has already occurred')),
4378 [('A', 'after', None, _('record a rename that has already occurred')),
4378 ('f', 'force', None,
4379 ('f', 'force', None,
4379 _('forcibly copy over an existing managed file')),
4380 _('forcibly copy over an existing managed file')),
4380 ] + walkopts + dryrunopts,
4381 ] + walkopts + dryrunopts,
4381 _('[OPTION]... SOURCE... DEST')),
4382 _('[OPTION]... SOURCE... DEST')),
4382 "resolve":
4383 "resolve":
4383 (resolve,
4384 (resolve,
4384 [('a', 'all', None, _('select all unresolved files')),
4385 [('a', 'all', None, _('select all unresolved files')),
4385 ('l', 'list', None, _('list state of files needing merge')),
4386 ('l', 'list', None, _('list state of files needing merge')),
4386 ('m', 'mark', None, _('mark files as resolved')),
4387 ('m', 'mark', None, _('mark files as resolved')),
4387 ('u', 'unmark', None, _('mark files as unresolved')),
4388 ('u', 'unmark', None, _('mark files as unresolved')),
4388 ('n', 'no-status', None, _('hide status prefix'))]
4389 ('n', 'no-status', None, _('hide status prefix'))]
4389 + walkopts,
4390 + walkopts,
4390 _('[OPTION]... [FILE]...')),
4391 _('[OPTION]... [FILE]...')),
4391 "revert":
4392 "revert":
4392 (revert,
4393 (revert,
4393 [('a', 'all', None, _('revert all changes when no arguments given')),
4394 [('a', 'all', None, _('revert all changes when no arguments given')),
4394 ('d', 'date', '',
4395 ('d', 'date', '',
4395 _('tipmost revision matching date'), _('DATE')),
4396 _('tipmost revision matching date'), _('DATE')),
4396 ('r', 'rev', '',
4397 ('r', 'rev', '',
4397 _('revert to the specified revision'), _('REV')),
4398 _('revert to the specified revision'), _('REV')),
4398 ('', 'no-backup', None, _('do not save backup copies of files')),
4399 ('', 'no-backup', None, _('do not save backup copies of files')),
4399 ] + walkopts + dryrunopts,
4400 ] + walkopts + dryrunopts,
4400 _('[OPTION]... [-r REV] [NAME]...')),
4401 _('[OPTION]... [-r REV] [NAME]...')),
4401 "rollback": (rollback, dryrunopts),
4402 "rollback": (rollback, dryrunopts),
4402 "root": (root, []),
4403 "root": (root, []),
4403 "^serve":
4404 "^serve":
4404 (serve,
4405 (serve,
4405 [('A', 'accesslog', '',
4406 [('A', 'accesslog', '',
4406 _('name of access log file to write to'), _('FILE')),
4407 _('name of access log file to write to'), _('FILE')),
4407 ('d', 'daemon', None, _('run server in background')),
4408 ('d', 'daemon', None, _('run server in background')),
4408 ('', 'daemon-pipefds', '',
4409 ('', 'daemon-pipefds', '',
4409 _('used internally by daemon mode'), _('NUM')),
4410 _('used internally by daemon mode'), _('NUM')),
4410 ('E', 'errorlog', '',
4411 ('E', 'errorlog', '',
4411 _('name of error log file to write to'), _('FILE')),
4412 _('name of error log file to write to'), _('FILE')),
4412 # use string type, then we can check if something was passed
4413 # use string type, then we can check if something was passed
4413 ('p', 'port', '',
4414 ('p', 'port', '',
4414 _('port to listen on (default: 8000)'), _('PORT')),
4415 _('port to listen on (default: 8000)'), _('PORT')),
4415 ('a', 'address', '',
4416 ('a', 'address', '',
4416 _('address to listen on (default: all interfaces)'), _('ADDR')),
4417 _('address to listen on (default: all interfaces)'), _('ADDR')),
4417 ('', 'prefix', '',
4418 ('', 'prefix', '',
4418 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4419 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4419 ('n', 'name', '',
4420 ('n', 'name', '',
4420 _('name to show in web pages (default: working directory)'),
4421 _('name to show in web pages (default: working directory)'),
4421 _('NAME')),
4422 _('NAME')),
4422 ('', 'web-conf', '',
4423 ('', 'web-conf', '',
4423 _('name of the hgweb config file (serve more than one repository)'),
4424 _('name of the hgweb config file (serve more than one repository)'),
4424 _('FILE')),
4425 _('FILE')),
4425 ('', 'webdir-conf', '',
4426 ('', 'webdir-conf', '',
4426 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4427 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4427 ('', 'pid-file', '',
4428 ('', 'pid-file', '',
4428 _('name of file to write process ID to'), _('FILE')),
4429 _('name of file to write process ID to'), _('FILE')),
4429 ('', 'stdio', None, _('for remote clients')),
4430 ('', 'stdio', None, _('for remote clients')),
4430 ('t', 'templates', '',
4431 ('t', 'templates', '',
4431 _('web templates to use'), _('TEMPLATE')),
4432 _('web templates to use'), _('TEMPLATE')),
4432 ('', 'style', '',
4433 ('', 'style', '',
4433 _('template style to use'), _('STYLE')),
4434 _('template style to use'), _('STYLE')),
4434 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4435 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4435 ('', 'certificate', '',
4436 ('', 'certificate', '',
4436 _('SSL certificate file'), _('FILE'))],
4437 _('SSL certificate file'), _('FILE'))],
4437 _('[OPTION]...')),
4438 _('[OPTION]...')),
4438 "showconfig|debugconfig":
4439 "showconfig|debugconfig":
4439 (showconfig,
4440 (showconfig,
4440 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4441 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4441 _('[-u] [NAME]...')),
4442 _('[-u] [NAME]...')),
4442 "^summary|sum":
4443 "^summary|sum":
4443 (summary,
4444 (summary,
4444 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4445 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4445 "^status|st":
4446 "^status|st":
4446 (status,
4447 (status,
4447 [('A', 'all', None, _('show status of all files')),
4448 [('A', 'all', None, _('show status of all files')),
4448 ('m', 'modified', None, _('show only modified files')),
4449 ('m', 'modified', None, _('show only modified files')),
4449 ('a', 'added', None, _('show only added files')),
4450 ('a', 'added', None, _('show only added files')),
4450 ('r', 'removed', None, _('show only removed files')),
4451 ('r', 'removed', None, _('show only removed files')),
4451 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4452 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4452 ('c', 'clean', None, _('show only files without changes')),
4453 ('c', 'clean', None, _('show only files without changes')),
4453 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4454 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4454 ('i', 'ignored', None, _('show only ignored files')),
4455 ('i', 'ignored', None, _('show only ignored files')),
4455 ('n', 'no-status', None, _('hide status prefix')),
4456 ('n', 'no-status', None, _('hide status prefix')),
4456 ('C', 'copies', None, _('show source of copied files')),
4457 ('C', 'copies', None, _('show source of copied files')),
4457 ('0', 'print0', None,
4458 ('0', 'print0', None,
4458 _('end filenames with NUL, for use with xargs')),
4459 _('end filenames with NUL, for use with xargs')),
4459 ('', 'rev', [],
4460 ('', 'rev', [],
4460 _('show difference from revision'), _('REV')),
4461 _('show difference from revision'), _('REV')),
4461 ('', 'change', '',
4462 ('', 'change', '',
4462 _('list the changed files of a revision'), _('REV')),
4463 _('list the changed files of a revision'), _('REV')),
4463 ] + walkopts + subrepoopts,
4464 ] + walkopts + subrepoopts,
4464 _('[OPTION]... [FILE]...')),
4465 _('[OPTION]... [FILE]...')),
4465 "tag":
4466 "tag":
4466 (tag,
4467 (tag,
4467 [('f', 'force', None, _('replace existing tag')),
4468 [('f', 'force', None, _('replace existing tag')),
4468 ('l', 'local', None, _('make the tag local')),
4469 ('l', 'local', None, _('make the tag local')),
4469 ('r', 'rev', '',
4470 ('r', 'rev', '',
4470 _('revision to tag'), _('REV')),
4471 _('revision to tag'), _('REV')),
4471 ('', 'remove', None, _('remove a tag')),
4472 ('', 'remove', None, _('remove a tag')),
4472 # -l/--local is already there, commitopts cannot be used
4473 # -l/--local is already there, commitopts cannot be used
4473 ('e', 'edit', None, _('edit commit message')),
4474 ('e', 'edit', None, _('edit commit message')),
4474 ('m', 'message', '',
4475 ('m', 'message', '',
4475 _('use <text> as commit message'), _('TEXT')),
4476 _('use <text> as commit message'), _('TEXT')),
4476 ] + commitopts2,
4477 ] + commitopts2,
4477 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4478 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4478 "tags": (tags, [], ''),
4479 "tags": (tags, [], ''),
4479 "tip":
4480 "tip":
4480 (tip,
4481 (tip,
4481 [('p', 'patch', None, _('show patch')),
4482 [('p', 'patch', None, _('show patch')),
4482 ('g', 'git', None, _('use git extended diff format')),
4483 ('g', 'git', None, _('use git extended diff format')),
4483 ] + templateopts,
4484 ] + templateopts,
4484 _('[-p] [-g]')),
4485 _('[-p] [-g]')),
4485 "unbundle":
4486 "unbundle":
4486 (unbundle,
4487 (unbundle,
4487 [('u', 'update', None,
4488 [('u', 'update', None,
4488 _('update to new branch head if changesets were unbundled'))],
4489 _('update to new branch head if changesets were unbundled'))],
4489 _('[-u] FILE...')),
4490 _('[-u] FILE...')),
4490 "^update|up|checkout|co":
4491 "^update|up|checkout|co":
4491 (update,
4492 (update,
4492 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4493 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4493 ('c', 'check', None, _('check for uncommitted changes')),
4494 ('c', 'check', None, _('check for uncommitted changes')),
4494 ('d', 'date', '',
4495 ('d', 'date', '',
4495 _('tipmost revision matching date'), _('DATE')),
4496 _('tipmost revision matching date'), _('DATE')),
4496 ('r', 'rev', '',
4497 ('r', 'rev', '',
4497 _('revision'), _('REV'))],
4498 _('revision'), _('REV'))],
4498 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4499 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4499 "verify": (verify, []),
4500 "verify": (verify, []),
4500 "version": (version_, []),
4501 "version": (version_, []),
4501 }
4502 }
4502
4503
4503 norepo = ("clone init version help debugcommands debugcomplete"
4504 norepo = ("clone init version help debugcommands debugcomplete"
4504 " debugdate debuginstall debugfsinfo debugpushkey")
4505 " debugdate debuginstall debugfsinfo debugpushkey")
4505 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4506 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4506 " debugdata debugindex debugindexdot")
4507 " debugdata debugindex debugindexdot")
@@ -1,1709 +1,1715 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
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 import cStringIO, email.Parser, os, re
9 import cStringIO, email.Parser, os, re
10 import tempfile, zlib
10 import tempfile, zlib
11
11
12 from i18n import _
12 from i18n import _
13 from node import hex, nullid, short
13 from node import hex, nullid, short
14 import base85, cmdutil, mdiff, util, diffhelpers, copies, encoding
14 import base85, cmdutil, mdiff, util, diffhelpers, copies, encoding
15
15
16 gitre = re.compile('diff --git a/(.*) b/(.*)')
16 gitre = re.compile('diff --git a/(.*) b/(.*)')
17
17
18 class PatchError(Exception):
18 class PatchError(Exception):
19 pass
19 pass
20
20
21 class NoHunks(PatchError):
21 class NoHunks(PatchError):
22 pass
22 pass
23
23
24 # helper functions
24 # helper functions
25
25
26 def copyfile(src, dst, basedir):
26 def copyfile(src, dst, basedir):
27 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
28 if os.path.exists(absdst):
28 if os.path.exists(absdst):
29 raise util.Abort(_("cannot create %s: destination already exists") %
29 raise util.Abort(_("cannot create %s: destination already exists") %
30 dst)
30 dst)
31
31
32 dstdir = os.path.dirname(absdst)
32 dstdir = os.path.dirname(absdst)
33 if dstdir and not os.path.isdir(dstdir):
33 if dstdir and not os.path.isdir(dstdir):
34 try:
34 try:
35 os.makedirs(dstdir)
35 os.makedirs(dstdir)
36 except IOError:
36 except IOError:
37 raise util.Abort(
37 raise util.Abort(
38 _("cannot create %s: unable to create destination directory")
38 _("cannot create %s: unable to create destination directory")
39 % dst)
39 % dst)
40
40
41 util.copyfile(abssrc, absdst)
41 util.copyfile(abssrc, absdst)
42
42
43 # public functions
43 # public functions
44
44
45 def split(stream):
45 def split(stream):
46 '''return an iterator of individual patches from a stream'''
46 '''return an iterator of individual patches from a stream'''
47 def isheader(line, inheader):
47 def isheader(line, inheader):
48 if inheader and line[0] in (' ', '\t'):
48 if inheader and line[0] in (' ', '\t'):
49 # continuation
49 # continuation
50 return True
50 return True
51 if line[0] in (' ', '-', '+'):
51 if line[0] in (' ', '-', '+'):
52 # diff line - don't check for header pattern in there
52 # diff line - don't check for header pattern in there
53 return False
53 return False
54 l = line.split(': ', 1)
54 l = line.split(': ', 1)
55 return len(l) == 2 and ' ' not in l[0]
55 return len(l) == 2 and ' ' not in l[0]
56
56
57 def chunk(lines):
57 def chunk(lines):
58 return cStringIO.StringIO(''.join(lines))
58 return cStringIO.StringIO(''.join(lines))
59
59
60 def hgsplit(stream, cur):
60 def hgsplit(stream, cur):
61 inheader = True
61 inheader = True
62
62
63 for line in stream:
63 for line in stream:
64 if not line.strip():
64 if not line.strip():
65 inheader = False
65 inheader = False
66 if not inheader and line.startswith('# HG changeset patch'):
66 if not inheader and line.startswith('# HG changeset patch'):
67 yield chunk(cur)
67 yield chunk(cur)
68 cur = []
68 cur = []
69 inheader = True
69 inheader = True
70
70
71 cur.append(line)
71 cur.append(line)
72
72
73 if cur:
73 if cur:
74 yield chunk(cur)
74 yield chunk(cur)
75
75
76 def mboxsplit(stream, cur):
76 def mboxsplit(stream, cur):
77 for line in stream:
77 for line in stream:
78 if line.startswith('From '):
78 if line.startswith('From '):
79 for c in split(chunk(cur[1:])):
79 for c in split(chunk(cur[1:])):
80 yield c
80 yield c
81 cur = []
81 cur = []
82
82
83 cur.append(line)
83 cur.append(line)
84
84
85 if cur:
85 if cur:
86 for c in split(chunk(cur[1:])):
86 for c in split(chunk(cur[1:])):
87 yield c
87 yield c
88
88
89 def mimesplit(stream, cur):
89 def mimesplit(stream, cur):
90 def msgfp(m):
90 def msgfp(m):
91 fp = cStringIO.StringIO()
91 fp = cStringIO.StringIO()
92 g = email.Generator.Generator(fp, mangle_from_=False)
92 g = email.Generator.Generator(fp, mangle_from_=False)
93 g.flatten(m)
93 g.flatten(m)
94 fp.seek(0)
94 fp.seek(0)
95 return fp
95 return fp
96
96
97 for line in stream:
97 for line in stream:
98 cur.append(line)
98 cur.append(line)
99 c = chunk(cur)
99 c = chunk(cur)
100
100
101 m = email.Parser.Parser().parse(c)
101 m = email.Parser.Parser().parse(c)
102 if not m.is_multipart():
102 if not m.is_multipart():
103 yield msgfp(m)
103 yield msgfp(m)
104 else:
104 else:
105 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
105 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
106 for part in m.walk():
106 for part in m.walk():
107 ct = part.get_content_type()
107 ct = part.get_content_type()
108 if ct not in ok_types:
108 if ct not in ok_types:
109 continue
109 continue
110 yield msgfp(part)
110 yield msgfp(part)
111
111
112 def headersplit(stream, cur):
112 def headersplit(stream, cur):
113 inheader = False
113 inheader = False
114
114
115 for line in stream:
115 for line in stream:
116 if not inheader and isheader(line, inheader):
116 if not inheader and isheader(line, inheader):
117 yield chunk(cur)
117 yield chunk(cur)
118 cur = []
118 cur = []
119 inheader = True
119 inheader = True
120 if inheader and not isheader(line, inheader):
120 if inheader and not isheader(line, inheader):
121 inheader = False
121 inheader = False
122
122
123 cur.append(line)
123 cur.append(line)
124
124
125 if cur:
125 if cur:
126 yield chunk(cur)
126 yield chunk(cur)
127
127
128 def remainder(cur):
128 def remainder(cur):
129 yield chunk(cur)
129 yield chunk(cur)
130
130
131 class fiter(object):
131 class fiter(object):
132 def __init__(self, fp):
132 def __init__(self, fp):
133 self.fp = fp
133 self.fp = fp
134
134
135 def __iter__(self):
135 def __iter__(self):
136 return self
136 return self
137
137
138 def next(self):
138 def next(self):
139 l = self.fp.readline()
139 l = self.fp.readline()
140 if not l:
140 if not l:
141 raise StopIteration
141 raise StopIteration
142 return l
142 return l
143
143
144 inheader = False
144 inheader = False
145 cur = []
145 cur = []
146
146
147 mimeheaders = ['content-type']
147 mimeheaders = ['content-type']
148
148
149 if not hasattr(stream, 'next'):
149 if not hasattr(stream, 'next'):
150 # http responses, for example, have readline but not next
150 # http responses, for example, have readline but not next
151 stream = fiter(stream)
151 stream = fiter(stream)
152
152
153 for line in stream:
153 for line in stream:
154 cur.append(line)
154 cur.append(line)
155 if line.startswith('# HG changeset patch'):
155 if line.startswith('# HG changeset patch'):
156 return hgsplit(stream, cur)
156 return hgsplit(stream, cur)
157 elif line.startswith('From '):
157 elif line.startswith('From '):
158 return mboxsplit(stream, cur)
158 return mboxsplit(stream, cur)
159 elif isheader(line, inheader):
159 elif isheader(line, inheader):
160 inheader = True
160 inheader = True
161 if line.split(':', 1)[0].lower() in mimeheaders:
161 if line.split(':', 1)[0].lower() in mimeheaders:
162 # let email parser handle this
162 # let email parser handle this
163 return mimesplit(stream, cur)
163 return mimesplit(stream, cur)
164 elif line.startswith('--- ') and inheader:
164 elif line.startswith('--- ') and inheader:
165 # No evil headers seen by diff start, split by hand
165 # No evil headers seen by diff start, split by hand
166 return headersplit(stream, cur)
166 return headersplit(stream, cur)
167 # Not enough info, keep reading
167 # Not enough info, keep reading
168
168
169 # if we are here, we have a very plain patch
169 # if we are here, we have a very plain patch
170 return remainder(cur)
170 return remainder(cur)
171
171
172 def extract(ui, fileobj):
172 def extract(ui, fileobj):
173 '''extract patch from data read from fileobj.
173 '''extract patch from data read from fileobj.
174
174
175 patch can be a normal patch or contained in an email message.
175 patch can be a normal patch or contained in an email message.
176
176
177 return tuple (filename, message, user, date, branch, node, p1, p2).
177 return tuple (filename, message, user, date, branch, node, p1, p2).
178 Any item in the returned tuple can be None. If filename is None,
178 Any item in the returned tuple can be None. If filename is None,
179 fileobj did not contain a patch. Caller must unlink filename when done.'''
179 fileobj did not contain a patch. Caller must unlink filename when done.'''
180
180
181 # attempt to detect the start of a patch
181 # attempt to detect the start of a patch
182 # (this heuristic is borrowed from quilt)
182 # (this heuristic is borrowed from quilt)
183 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
183 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
184 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
184 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
185 r'---[ \t].*?^\+\+\+[ \t]|'
185 r'---[ \t].*?^\+\+\+[ \t]|'
186 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
186 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
187
187
188 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
188 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
189 tmpfp = os.fdopen(fd, 'w')
189 tmpfp = os.fdopen(fd, 'w')
190 try:
190 try:
191 msg = email.Parser.Parser().parse(fileobj)
191 msg = email.Parser.Parser().parse(fileobj)
192
192
193 subject = msg['Subject']
193 subject = msg['Subject']
194 user = msg['From']
194 user = msg['From']
195 if not subject and not user:
195 if not subject and not user:
196 # Not an email, restore parsed headers if any
196 # Not an email, restore parsed headers if any
197 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
197 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
198
198
199 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
199 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
200 # should try to parse msg['Date']
200 # should try to parse msg['Date']
201 date = None
201 date = None
202 nodeid = None
202 nodeid = None
203 branch = None
203 branch = None
204 parents = []
204 parents = []
205
205
206 if subject:
206 if subject:
207 if subject.startswith('[PATCH'):
207 if subject.startswith('[PATCH'):
208 pend = subject.find(']')
208 pend = subject.find(']')
209 if pend >= 0:
209 if pend >= 0:
210 subject = subject[pend + 1:].lstrip()
210 subject = subject[pend + 1:].lstrip()
211 subject = subject.replace('\n\t', ' ')
211 subject = subject.replace('\n\t', ' ')
212 ui.debug('Subject: %s\n' % subject)
212 ui.debug('Subject: %s\n' % subject)
213 if user:
213 if user:
214 ui.debug('From: %s\n' % user)
214 ui.debug('From: %s\n' % user)
215 diffs_seen = 0
215 diffs_seen = 0
216 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
216 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
217 message = ''
217 message = ''
218 for part in msg.walk():
218 for part in msg.walk():
219 content_type = part.get_content_type()
219 content_type = part.get_content_type()
220 ui.debug('Content-Type: %s\n' % content_type)
220 ui.debug('Content-Type: %s\n' % content_type)
221 if content_type not in ok_types:
221 if content_type not in ok_types:
222 continue
222 continue
223 payload = part.get_payload(decode=True)
223 payload = part.get_payload(decode=True)
224 m = diffre.search(payload)
224 m = diffre.search(payload)
225 if m:
225 if m:
226 hgpatch = False
226 hgpatch = False
227 ignoretext = False
227 ignoretext = False
228
228
229 ui.debug('found patch at byte %d\n' % m.start(0))
229 ui.debug('found patch at byte %d\n' % m.start(0))
230 diffs_seen += 1
230 diffs_seen += 1
231 cfp = cStringIO.StringIO()
231 cfp = cStringIO.StringIO()
232 for line in payload[:m.start(0)].splitlines():
232 for line in payload[:m.start(0)].splitlines():
233 if line.startswith('# HG changeset patch'):
233 if line.startswith('# HG changeset patch'):
234 ui.debug('patch generated by hg export\n')
234 ui.debug('patch generated by hg export\n')
235 hgpatch = True
235 hgpatch = True
236 # drop earlier commit message content
236 # drop earlier commit message content
237 cfp.seek(0)
237 cfp.seek(0)
238 cfp.truncate()
238 cfp.truncate()
239 subject = None
239 subject = None
240 elif hgpatch:
240 elif hgpatch:
241 if line.startswith('# User '):
241 if line.startswith('# User '):
242 user = line[7:]
242 user = line[7:]
243 ui.debug('From: %s\n' % user)
243 ui.debug('From: %s\n' % user)
244 elif line.startswith("# Date "):
244 elif line.startswith("# Date "):
245 date = line[7:]
245 date = line[7:]
246 elif line.startswith("# Branch "):
246 elif line.startswith("# Branch "):
247 branch = line[9:]
247 branch = line[9:]
248 elif line.startswith("# Node ID "):
248 elif line.startswith("# Node ID "):
249 nodeid = line[10:]
249 nodeid = line[10:]
250 elif line.startswith("# Parent "):
250 elif line.startswith("# Parent "):
251 parents.append(line[10:])
251 parents.append(line[10:])
252 elif line == '---' and gitsendmail:
252 elif line == '---' and gitsendmail:
253 ignoretext = True
253 ignoretext = True
254 if not line.startswith('# ') and not ignoretext:
254 if not line.startswith('# ') and not ignoretext:
255 cfp.write(line)
255 cfp.write(line)
256 cfp.write('\n')
256 cfp.write('\n')
257 message = cfp.getvalue()
257 message = cfp.getvalue()
258 if tmpfp:
258 if tmpfp:
259 tmpfp.write(payload)
259 tmpfp.write(payload)
260 if not payload.endswith('\n'):
260 if not payload.endswith('\n'):
261 tmpfp.write('\n')
261 tmpfp.write('\n')
262 elif not diffs_seen and message and content_type == 'text/plain':
262 elif not diffs_seen and message and content_type == 'text/plain':
263 message += '\n' + payload
263 message += '\n' + payload
264 except:
264 except:
265 tmpfp.close()
265 tmpfp.close()
266 os.unlink(tmpname)
266 os.unlink(tmpname)
267 raise
267 raise
268
268
269 if subject and not message.startswith(subject):
269 if subject and not message.startswith(subject):
270 message = '%s\n%s' % (subject, message)
270 message = '%s\n%s' % (subject, message)
271 tmpfp.close()
271 tmpfp.close()
272 if not diffs_seen:
272 if not diffs_seen:
273 os.unlink(tmpname)
273 os.unlink(tmpname)
274 return None, message, user, date, branch, None, None, None
274 return None, message, user, date, branch, None, None, None
275 p1 = parents and parents.pop(0) or None
275 p1 = parents and parents.pop(0) or None
276 p2 = parents and parents.pop(0) or None
276 p2 = parents and parents.pop(0) or None
277 return tmpname, message, user, date, branch, nodeid, p1, p2
277 return tmpname, message, user, date, branch, nodeid, p1, p2
278
278
279 GP_PATCH = 1 << 0 # we have to run patch
279 GP_PATCH = 1 << 0 # we have to run patch
280 GP_FILTER = 1 << 1 # there's some copy/rename operation
280 GP_FILTER = 1 << 1 # there's some copy/rename operation
281 GP_BINARY = 1 << 2 # there's a binary patch
281 GP_BINARY = 1 << 2 # there's a binary patch
282
282
283 class patchmeta(object):
283 class patchmeta(object):
284 """Patched file metadata
284 """Patched file metadata
285
285
286 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
286 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
287 or COPY. 'path' is patched file path. 'oldpath' is set to the
287 or COPY. 'path' is patched file path. 'oldpath' is set to the
288 origin file when 'op' is either COPY or RENAME, None otherwise. If
288 origin file when 'op' is either COPY or RENAME, None otherwise. If
289 file mode is changed, 'mode' is a tuple (islink, isexec) where
289 file mode is changed, 'mode' is a tuple (islink, isexec) where
290 'islink' is True if the file is a symlink and 'isexec' is True if
290 'islink' is True if the file is a symlink and 'isexec' is True if
291 the file is executable. Otherwise, 'mode' is None.
291 the file is executable. Otherwise, 'mode' is None.
292 """
292 """
293 def __init__(self, path):
293 def __init__(self, path):
294 self.path = path
294 self.path = path
295 self.oldpath = None
295 self.oldpath = None
296 self.mode = None
296 self.mode = None
297 self.op = 'MODIFY'
297 self.op = 'MODIFY'
298 self.lineno = 0
298 self.lineno = 0
299 self.binary = False
299 self.binary = False
300
300
301 def setmode(self, mode):
301 def setmode(self, mode):
302 islink = mode & 020000
302 islink = mode & 020000
303 isexec = mode & 0100
303 isexec = mode & 0100
304 self.mode = (islink, isexec)
304 self.mode = (islink, isexec)
305
305
306 def __repr__(self):
306 def __repr__(self):
307 return "<patchmeta %s %r>" % (self.op, self.path)
307 return "<patchmeta %s %r>" % (self.op, self.path)
308
308
309 def readgitpatch(lr):
309 def readgitpatch(lr):
310 """extract git-style metadata about patches from <patchname>"""
310 """extract git-style metadata about patches from <patchname>"""
311
311
312 # Filter patch for git information
312 # Filter patch for git information
313 gp = None
313 gp = None
314 gitpatches = []
314 gitpatches = []
315 # Can have a git patch with only metadata, causing patch to complain
315 # Can have a git patch with only metadata, causing patch to complain
316 dopatch = 0
316 dopatch = 0
317
317
318 lineno = 0
318 lineno = 0
319 for line in lr:
319 for line in lr:
320 lineno += 1
320 lineno += 1
321 line = line.rstrip(' \r\n')
321 line = line.rstrip(' \r\n')
322 if line.startswith('diff --git'):
322 if line.startswith('diff --git'):
323 m = gitre.match(line)
323 m = gitre.match(line)
324 if m:
324 if m:
325 if gp:
325 if gp:
326 gitpatches.append(gp)
326 gitpatches.append(gp)
327 dst = m.group(2)
327 dst = m.group(2)
328 gp = patchmeta(dst)
328 gp = patchmeta(dst)
329 gp.lineno = lineno
329 gp.lineno = lineno
330 elif gp:
330 elif gp:
331 if line.startswith('--- '):
331 if line.startswith('--- '):
332 if gp.op in ('COPY', 'RENAME'):
332 if gp.op in ('COPY', 'RENAME'):
333 dopatch |= GP_FILTER
333 dopatch |= GP_FILTER
334 gitpatches.append(gp)
334 gitpatches.append(gp)
335 gp = None
335 gp = None
336 dopatch |= GP_PATCH
336 dopatch |= GP_PATCH
337 continue
337 continue
338 if line.startswith('rename from '):
338 if line.startswith('rename from '):
339 gp.op = 'RENAME'
339 gp.op = 'RENAME'
340 gp.oldpath = line[12:]
340 gp.oldpath = line[12:]
341 elif line.startswith('rename to '):
341 elif line.startswith('rename to '):
342 gp.path = line[10:]
342 gp.path = line[10:]
343 elif line.startswith('copy from '):
343 elif line.startswith('copy from '):
344 gp.op = 'COPY'
344 gp.op = 'COPY'
345 gp.oldpath = line[10:]
345 gp.oldpath = line[10:]
346 elif line.startswith('copy to '):
346 elif line.startswith('copy to '):
347 gp.path = line[8:]
347 gp.path = line[8:]
348 elif line.startswith('deleted file'):
348 elif line.startswith('deleted file'):
349 gp.op = 'DELETE'
349 gp.op = 'DELETE'
350 elif line.startswith('new file mode '):
350 elif line.startswith('new file mode '):
351 gp.op = 'ADD'
351 gp.op = 'ADD'
352 gp.setmode(int(line[-6:], 8))
352 gp.setmode(int(line[-6:], 8))
353 elif line.startswith('new mode '):
353 elif line.startswith('new mode '):
354 gp.setmode(int(line[-6:], 8))
354 gp.setmode(int(line[-6:], 8))
355 elif line.startswith('GIT binary patch'):
355 elif line.startswith('GIT binary patch'):
356 dopatch |= GP_BINARY
356 dopatch |= GP_BINARY
357 gp.binary = True
357 gp.binary = True
358 if gp:
358 if gp:
359 gitpatches.append(gp)
359 gitpatches.append(gp)
360
360
361 if not gitpatches:
361 if not gitpatches:
362 dopatch = GP_PATCH
362 dopatch = GP_PATCH
363
363
364 return (dopatch, gitpatches)
364 return (dopatch, gitpatches)
365
365
366 class linereader(object):
366 class linereader(object):
367 # simple class to allow pushing lines back into the input stream
367 # simple class to allow pushing lines back into the input stream
368 def __init__(self, fp, textmode=False):
368 def __init__(self, fp, textmode=False):
369 self.fp = fp
369 self.fp = fp
370 self.buf = []
370 self.buf = []
371 self.textmode = textmode
371 self.textmode = textmode
372 self.eol = None
372 self.eol = None
373
373
374 def push(self, line):
374 def push(self, line):
375 if line is not None:
375 if line is not None:
376 self.buf.append(line)
376 self.buf.append(line)
377
377
378 def readline(self):
378 def readline(self):
379 if self.buf:
379 if self.buf:
380 l = self.buf[0]
380 l = self.buf[0]
381 del self.buf[0]
381 del self.buf[0]
382 return l
382 return l
383 l = self.fp.readline()
383 l = self.fp.readline()
384 if not self.eol:
384 if not self.eol:
385 if l.endswith('\r\n'):
385 if l.endswith('\r\n'):
386 self.eol = '\r\n'
386 self.eol = '\r\n'
387 elif l.endswith('\n'):
387 elif l.endswith('\n'):
388 self.eol = '\n'
388 self.eol = '\n'
389 if self.textmode and l.endswith('\r\n'):
389 if self.textmode and l.endswith('\r\n'):
390 l = l[:-2] + '\n'
390 l = l[:-2] + '\n'
391 return l
391 return l
392
392
393 def __iter__(self):
393 def __iter__(self):
394 while 1:
394 while 1:
395 l = self.readline()
395 l = self.readline()
396 if not l:
396 if not l:
397 break
397 break
398 yield l
398 yield l
399
399
400 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
400 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
401 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
401 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
402 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
402 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
403 eolmodes = ['strict', 'crlf', 'lf', 'auto']
403 eolmodes = ['strict', 'crlf', 'lf', 'auto']
404
404
405 class patchfile(object):
405 class patchfile(object):
406 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
406 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
407 self.fname = fname
407 self.fname = fname
408 self.eolmode = eolmode
408 self.eolmode = eolmode
409 self.eol = None
409 self.eol = None
410 self.opener = opener
410 self.opener = opener
411 self.ui = ui
411 self.ui = ui
412 self.lines = []
412 self.lines = []
413 self.exists = False
413 self.exists = False
414 self.missing = missing
414 self.missing = missing
415 if not missing:
415 if not missing:
416 try:
416 try:
417 self.lines = self.readlines(fname)
417 self.lines = self.readlines(fname)
418 self.exists = True
418 self.exists = True
419 except IOError:
419 except IOError:
420 pass
420 pass
421 else:
421 else:
422 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
422 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
423
423
424 self.hash = {}
424 self.hash = {}
425 self.dirty = 0
425 self.dirty = 0
426 self.offset = 0
426 self.offset = 0
427 self.skew = 0
427 self.skew = 0
428 self.rej = []
428 self.rej = []
429 self.fileprinted = False
429 self.fileprinted = False
430 self.printfile(False)
430 self.printfile(False)
431 self.hunks = 0
431 self.hunks = 0
432
432
433 def readlines(self, fname):
433 def readlines(self, fname):
434 if os.path.islink(fname):
434 if os.path.islink(fname):
435 return [os.readlink(fname)]
435 return [os.readlink(fname)]
436 fp = self.opener(fname, 'r')
436 fp = self.opener(fname, 'r')
437 try:
437 try:
438 lr = linereader(fp, self.eolmode != 'strict')
438 lr = linereader(fp, self.eolmode != 'strict')
439 lines = list(lr)
439 lines = list(lr)
440 self.eol = lr.eol
440 self.eol = lr.eol
441 return lines
441 return lines
442 finally:
442 finally:
443 fp.close()
443 fp.close()
444
444
445 def writelines(self, fname, lines):
445 def writelines(self, fname, lines):
446 # Ensure supplied data ends in fname, being a regular file or
446 # Ensure supplied data ends in fname, being a regular file or
447 # a symlink. updatedir() will -too magically- take care of
447 # a symlink. updatedir() will -too magically- take care of
448 # setting it to the proper type afterwards.
448 # setting it to the proper type afterwards.
449 islink = os.path.islink(fname)
449 islink = os.path.islink(fname)
450 if islink:
450 if islink:
451 fp = cStringIO.StringIO()
451 fp = cStringIO.StringIO()
452 else:
452 else:
453 fp = self.opener(fname, 'w')
453 fp = self.opener(fname, 'w')
454 try:
454 try:
455 if self.eolmode == 'auto':
455 if self.eolmode == 'auto':
456 eol = self.eol
456 eol = self.eol
457 elif self.eolmode == 'crlf':
457 elif self.eolmode == 'crlf':
458 eol = '\r\n'
458 eol = '\r\n'
459 else:
459 else:
460 eol = '\n'
460 eol = '\n'
461
461
462 if self.eolmode != 'strict' and eol and eol != '\n':
462 if self.eolmode != 'strict' and eol and eol != '\n':
463 for l in lines:
463 for l in lines:
464 if l and l[-1] == '\n':
464 if l and l[-1] == '\n':
465 l = l[:-1] + eol
465 l = l[:-1] + eol
466 fp.write(l)
466 fp.write(l)
467 else:
467 else:
468 fp.writelines(lines)
468 fp.writelines(lines)
469 if islink:
469 if islink:
470 self.opener.symlink(fp.getvalue(), fname)
470 self.opener.symlink(fp.getvalue(), fname)
471 finally:
471 finally:
472 fp.close()
472 fp.close()
473
473
474 def unlink(self, fname):
474 def unlink(self, fname):
475 os.unlink(fname)
475 os.unlink(fname)
476
476
477 def printfile(self, warn):
477 def printfile(self, warn):
478 if self.fileprinted:
478 if self.fileprinted:
479 return
479 return
480 if warn or self.ui.verbose:
480 if warn or self.ui.verbose:
481 self.fileprinted = True
481 self.fileprinted = True
482 s = _("patching file %s\n") % self.fname
482 s = _("patching file %s\n") % self.fname
483 if warn:
483 if warn:
484 self.ui.warn(s)
484 self.ui.warn(s)
485 else:
485 else:
486 self.ui.note(s)
486 self.ui.note(s)
487
487
488
488
489 def findlines(self, l, linenum):
489 def findlines(self, l, linenum):
490 # looks through the hash and finds candidate lines. The
490 # looks through the hash and finds candidate lines. The
491 # result is a list of line numbers sorted based on distance
491 # result is a list of line numbers sorted based on distance
492 # from linenum
492 # from linenum
493
493
494 cand = self.hash.get(l, [])
494 cand = self.hash.get(l, [])
495 if len(cand) > 1:
495 if len(cand) > 1:
496 # resort our list of potentials forward then back.
496 # resort our list of potentials forward then back.
497 cand.sort(key=lambda x: abs(x - linenum))
497 cand.sort(key=lambda x: abs(x - linenum))
498 return cand
498 return cand
499
499
500 def hashlines(self):
500 def hashlines(self):
501 self.hash = {}
501 self.hash = {}
502 for x, s in enumerate(self.lines):
502 for x, s in enumerate(self.lines):
503 self.hash.setdefault(s, []).append(x)
503 self.hash.setdefault(s, []).append(x)
504
504
505 def write_rej(self):
505 def write_rej(self):
506 # our rejects are a little different from patch(1). This always
506 # our rejects are a little different from patch(1). This always
507 # creates rejects in the same form as the original patch. A file
507 # creates rejects in the same form as the original patch. A file
508 # header is inserted so that you can run the reject through patch again
508 # header is inserted so that you can run the reject through patch again
509 # without having to type the filename.
509 # without having to type the filename.
510
510
511 if not self.rej:
511 if not self.rej:
512 return
512 return
513
513
514 fname = self.fname + ".rej"
514 fname = self.fname + ".rej"
515 self.ui.warn(
515 self.ui.warn(
516 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
516 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
517 (len(self.rej), self.hunks, fname))
517 (len(self.rej), self.hunks, fname))
518
518
519 def rejlines():
519 def rejlines():
520 base = os.path.basename(self.fname)
520 base = os.path.basename(self.fname)
521 yield "--- %s\n+++ %s\n" % (base, base)
521 yield "--- %s\n+++ %s\n" % (base, base)
522 for x in self.rej:
522 for x in self.rej:
523 for l in x.hunk:
523 for l in x.hunk:
524 yield l
524 yield l
525 if l[-1] != '\n':
525 if l[-1] != '\n':
526 yield "\n\ No newline at end of file\n"
526 yield "\n\ No newline at end of file\n"
527
527
528 self.writelines(fname, rejlines())
528 self.writelines(fname, rejlines())
529
529
530 def apply(self, h):
530 def apply(self, h):
531 if not h.complete():
531 if not h.complete():
532 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
532 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
533 (h.number, h.desc, len(h.a), h.lena, len(h.b),
533 (h.number, h.desc, len(h.a), h.lena, len(h.b),
534 h.lenb))
534 h.lenb))
535
535
536 self.hunks += 1
536 self.hunks += 1
537
537
538 if self.missing:
538 if self.missing:
539 self.rej.append(h)
539 self.rej.append(h)
540 return -1
540 return -1
541
541
542 if self.exists and h.createfile():
542 if self.exists and h.createfile():
543 self.ui.warn(_("file %s already exists\n") % self.fname)
543 self.ui.warn(_("file %s already exists\n") % self.fname)
544 self.rej.append(h)
544 self.rej.append(h)
545 return -1
545 return -1
546
546
547 if isinstance(h, binhunk):
547 if isinstance(h, binhunk):
548 if h.rmfile():
548 if h.rmfile():
549 self.unlink(self.fname)
549 self.unlink(self.fname)
550 else:
550 else:
551 self.lines[:] = h.new()
551 self.lines[:] = h.new()
552 self.offset += len(h.new())
552 self.offset += len(h.new())
553 self.dirty = 1
553 self.dirty = 1
554 return 0
554 return 0
555
555
556 horig = h
556 horig = h
557 if (self.eolmode in ('crlf', 'lf')
557 if (self.eolmode in ('crlf', 'lf')
558 or self.eolmode == 'auto' and self.eol):
558 or self.eolmode == 'auto' and self.eol):
559 # If new eols are going to be normalized, then normalize
559 # If new eols are going to be normalized, then normalize
560 # hunk data before patching. Otherwise, preserve input
560 # hunk data before patching. Otherwise, preserve input
561 # line-endings.
561 # line-endings.
562 h = h.getnormalized()
562 h = h.getnormalized()
563
563
564 # fast case first, no offsets, no fuzz
564 # fast case first, no offsets, no fuzz
565 old = h.old()
565 old = h.old()
566 # patch starts counting at 1 unless we are adding the file
566 # patch starts counting at 1 unless we are adding the file
567 if h.starta == 0:
567 if h.starta == 0:
568 start = 0
568 start = 0
569 else:
569 else:
570 start = h.starta + self.offset - 1
570 start = h.starta + self.offset - 1
571 orig_start = start
571 orig_start = start
572 # if there's skew we want to emit the "(offset %d lines)" even
572 # if there's skew we want to emit the "(offset %d lines)" even
573 # when the hunk cleanly applies at start + skew, so skip the
573 # when the hunk cleanly applies at start + skew, so skip the
574 # fast case code
574 # fast case code
575 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
575 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
576 if h.rmfile():
576 if h.rmfile():
577 self.unlink(self.fname)
577 self.unlink(self.fname)
578 else:
578 else:
579 self.lines[start : start + h.lena] = h.new()
579 self.lines[start : start + h.lena] = h.new()
580 self.offset += h.lenb - h.lena
580 self.offset += h.lenb - h.lena
581 self.dirty = 1
581 self.dirty = 1
582 return 0
582 return 0
583
583
584 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
584 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
585 self.hashlines()
585 self.hashlines()
586 if h.hunk[-1][0] != ' ':
586 if h.hunk[-1][0] != ' ':
587 # if the hunk tried to put something at the bottom of the file
587 # if the hunk tried to put something at the bottom of the file
588 # override the start line and use eof here
588 # override the start line and use eof here
589 search_start = len(self.lines)
589 search_start = len(self.lines)
590 else:
590 else:
591 search_start = orig_start + self.skew
591 search_start = orig_start + self.skew
592
592
593 for fuzzlen in xrange(3):
593 for fuzzlen in xrange(3):
594 for toponly in [True, False]:
594 for toponly in [True, False]:
595 old = h.old(fuzzlen, toponly)
595 old = h.old(fuzzlen, toponly)
596
596
597 cand = self.findlines(old[0][1:], search_start)
597 cand = self.findlines(old[0][1:], search_start)
598 for l in cand:
598 for l in cand:
599 if diffhelpers.testhunk(old, self.lines, l) == 0:
599 if diffhelpers.testhunk(old, self.lines, l) == 0:
600 newlines = h.new(fuzzlen, toponly)
600 newlines = h.new(fuzzlen, toponly)
601 self.lines[l : l + len(old)] = newlines
601 self.lines[l : l + len(old)] = newlines
602 self.offset += len(newlines) - len(old)
602 self.offset += len(newlines) - len(old)
603 self.skew = l - orig_start
603 self.skew = l - orig_start
604 self.dirty = 1
604 self.dirty = 1
605 offset = l - orig_start - fuzzlen
605 offset = l - orig_start - fuzzlen
606 if fuzzlen:
606 if fuzzlen:
607 msg = _("Hunk #%d succeeded at %d "
607 msg = _("Hunk #%d succeeded at %d "
608 "with fuzz %d "
608 "with fuzz %d "
609 "(offset %d lines).\n")
609 "(offset %d lines).\n")
610 self.printfile(True)
610 self.printfile(True)
611 self.ui.warn(msg %
611 self.ui.warn(msg %
612 (h.number, l + 1, fuzzlen, offset))
612 (h.number, l + 1, fuzzlen, offset))
613 else:
613 else:
614 msg = _("Hunk #%d succeeded at %d "
614 msg = _("Hunk #%d succeeded at %d "
615 "(offset %d lines).\n")
615 "(offset %d lines).\n")
616 self.ui.note(msg % (h.number, l + 1, offset))
616 self.ui.note(msg % (h.number, l + 1, offset))
617 return fuzzlen
617 return fuzzlen
618 self.printfile(True)
618 self.printfile(True)
619 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
619 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
620 self.rej.append(horig)
620 self.rej.append(horig)
621 return -1
621 return -1
622
622
623 class hunk(object):
623 class hunk(object):
624 def __init__(self, desc, num, lr, context, create=False, remove=False):
624 def __init__(self, desc, num, lr, context, create=False, remove=False):
625 self.number = num
625 self.number = num
626 self.desc = desc
626 self.desc = desc
627 self.hunk = [desc]
627 self.hunk = [desc]
628 self.a = []
628 self.a = []
629 self.b = []
629 self.b = []
630 self.starta = self.lena = None
630 self.starta = self.lena = None
631 self.startb = self.lenb = None
631 self.startb = self.lenb = None
632 if lr is not None:
632 if lr is not None:
633 if context:
633 if context:
634 self.read_context_hunk(lr)
634 self.read_context_hunk(lr)
635 else:
635 else:
636 self.read_unified_hunk(lr)
636 self.read_unified_hunk(lr)
637 self.create = create
637 self.create = create
638 self.remove = remove and not create
638 self.remove = remove and not create
639
639
640 def getnormalized(self):
640 def getnormalized(self):
641 """Return a copy with line endings normalized to LF."""
641 """Return a copy with line endings normalized to LF."""
642
642
643 def normalize(lines):
643 def normalize(lines):
644 nlines = []
644 nlines = []
645 for line in lines:
645 for line in lines:
646 if line.endswith('\r\n'):
646 if line.endswith('\r\n'):
647 line = line[:-2] + '\n'
647 line = line[:-2] + '\n'
648 nlines.append(line)
648 nlines.append(line)
649 return nlines
649 return nlines
650
650
651 # Dummy object, it is rebuilt manually
651 # Dummy object, it is rebuilt manually
652 nh = hunk(self.desc, self.number, None, None, False, False)
652 nh = hunk(self.desc, self.number, None, None, False, False)
653 nh.number = self.number
653 nh.number = self.number
654 nh.desc = self.desc
654 nh.desc = self.desc
655 nh.hunk = self.hunk
655 nh.hunk = self.hunk
656 nh.a = normalize(self.a)
656 nh.a = normalize(self.a)
657 nh.b = normalize(self.b)
657 nh.b = normalize(self.b)
658 nh.starta = self.starta
658 nh.starta = self.starta
659 nh.startb = self.startb
659 nh.startb = self.startb
660 nh.lena = self.lena
660 nh.lena = self.lena
661 nh.lenb = self.lenb
661 nh.lenb = self.lenb
662 nh.create = self.create
662 nh.create = self.create
663 nh.remove = self.remove
663 nh.remove = self.remove
664 return nh
664 return nh
665
665
666 def read_unified_hunk(self, lr):
666 def read_unified_hunk(self, lr):
667 m = unidesc.match(self.desc)
667 m = unidesc.match(self.desc)
668 if not m:
668 if not m:
669 raise PatchError(_("bad hunk #%d") % self.number)
669 raise PatchError(_("bad hunk #%d") % self.number)
670 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
670 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
671 if self.lena is None:
671 if self.lena is None:
672 self.lena = 1
672 self.lena = 1
673 else:
673 else:
674 self.lena = int(self.lena)
674 self.lena = int(self.lena)
675 if self.lenb is None:
675 if self.lenb is None:
676 self.lenb = 1
676 self.lenb = 1
677 else:
677 else:
678 self.lenb = int(self.lenb)
678 self.lenb = int(self.lenb)
679 self.starta = int(self.starta)
679 self.starta = int(self.starta)
680 self.startb = int(self.startb)
680 self.startb = int(self.startb)
681 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
681 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
682 # if we hit eof before finishing out the hunk, the last line will
682 # if we hit eof before finishing out the hunk, the last line will
683 # be zero length. Lets try to fix it up.
683 # be zero length. Lets try to fix it up.
684 while len(self.hunk[-1]) == 0:
684 while len(self.hunk[-1]) == 0:
685 del self.hunk[-1]
685 del self.hunk[-1]
686 del self.a[-1]
686 del self.a[-1]
687 del self.b[-1]
687 del self.b[-1]
688 self.lena -= 1
688 self.lena -= 1
689 self.lenb -= 1
689 self.lenb -= 1
690
690
691 def read_context_hunk(self, lr):
691 def read_context_hunk(self, lr):
692 self.desc = lr.readline()
692 self.desc = lr.readline()
693 m = contextdesc.match(self.desc)
693 m = contextdesc.match(self.desc)
694 if not m:
694 if not m:
695 raise PatchError(_("bad hunk #%d") % self.number)
695 raise PatchError(_("bad hunk #%d") % self.number)
696 foo, self.starta, foo2, aend, foo3 = m.groups()
696 foo, self.starta, foo2, aend, foo3 = m.groups()
697 self.starta = int(self.starta)
697 self.starta = int(self.starta)
698 if aend is None:
698 if aend is None:
699 aend = self.starta
699 aend = self.starta
700 self.lena = int(aend) - self.starta
700 self.lena = int(aend) - self.starta
701 if self.starta:
701 if self.starta:
702 self.lena += 1
702 self.lena += 1
703 for x in xrange(self.lena):
703 for x in xrange(self.lena):
704 l = lr.readline()
704 l = lr.readline()
705 if l.startswith('---'):
705 if l.startswith('---'):
706 lr.push(l)
706 lr.push(l)
707 break
707 break
708 s = l[2:]
708 s = l[2:]
709 if l.startswith('- ') or l.startswith('! '):
709 if l.startswith('- ') or l.startswith('! '):
710 u = '-' + s
710 u = '-' + s
711 elif l.startswith(' '):
711 elif l.startswith(' '):
712 u = ' ' + s
712 u = ' ' + s
713 else:
713 else:
714 raise PatchError(_("bad hunk #%d old text line %d") %
714 raise PatchError(_("bad hunk #%d old text line %d") %
715 (self.number, x))
715 (self.number, x))
716 self.a.append(u)
716 self.a.append(u)
717 self.hunk.append(u)
717 self.hunk.append(u)
718
718
719 l = lr.readline()
719 l = lr.readline()
720 if l.startswith('\ '):
720 if l.startswith('\ '):
721 s = self.a[-1][:-1]
721 s = self.a[-1][:-1]
722 self.a[-1] = s
722 self.a[-1] = s
723 self.hunk[-1] = s
723 self.hunk[-1] = s
724 l = lr.readline()
724 l = lr.readline()
725 m = contextdesc.match(l)
725 m = contextdesc.match(l)
726 if not m:
726 if not m:
727 raise PatchError(_("bad hunk #%d") % self.number)
727 raise PatchError(_("bad hunk #%d") % self.number)
728 foo, self.startb, foo2, bend, foo3 = m.groups()
728 foo, self.startb, foo2, bend, foo3 = m.groups()
729 self.startb = int(self.startb)
729 self.startb = int(self.startb)
730 if bend is None:
730 if bend is None:
731 bend = self.startb
731 bend = self.startb
732 self.lenb = int(bend) - self.startb
732 self.lenb = int(bend) - self.startb
733 if self.startb:
733 if self.startb:
734 self.lenb += 1
734 self.lenb += 1
735 hunki = 1
735 hunki = 1
736 for x in xrange(self.lenb):
736 for x in xrange(self.lenb):
737 l = lr.readline()
737 l = lr.readline()
738 if l.startswith('\ '):
738 if l.startswith('\ '):
739 s = self.b[-1][:-1]
739 s = self.b[-1][:-1]
740 self.b[-1] = s
740 self.b[-1] = s
741 self.hunk[hunki - 1] = s
741 self.hunk[hunki - 1] = s
742 continue
742 continue
743 if not l:
743 if not l:
744 lr.push(l)
744 lr.push(l)
745 break
745 break
746 s = l[2:]
746 s = l[2:]
747 if l.startswith('+ ') or l.startswith('! '):
747 if l.startswith('+ ') or l.startswith('! '):
748 u = '+' + s
748 u = '+' + s
749 elif l.startswith(' '):
749 elif l.startswith(' '):
750 u = ' ' + s
750 u = ' ' + s
751 elif len(self.b) == 0:
751 elif len(self.b) == 0:
752 # this can happen when the hunk does not add any lines
752 # this can happen when the hunk does not add any lines
753 lr.push(l)
753 lr.push(l)
754 break
754 break
755 else:
755 else:
756 raise PatchError(_("bad hunk #%d old text line %d") %
756 raise PatchError(_("bad hunk #%d old text line %d") %
757 (self.number, x))
757 (self.number, x))
758 self.b.append(s)
758 self.b.append(s)
759 while True:
759 while True:
760 if hunki >= len(self.hunk):
760 if hunki >= len(self.hunk):
761 h = ""
761 h = ""
762 else:
762 else:
763 h = self.hunk[hunki]
763 h = self.hunk[hunki]
764 hunki += 1
764 hunki += 1
765 if h == u:
765 if h == u:
766 break
766 break
767 elif h.startswith('-'):
767 elif h.startswith('-'):
768 continue
768 continue
769 else:
769 else:
770 self.hunk.insert(hunki - 1, u)
770 self.hunk.insert(hunki - 1, u)
771 break
771 break
772
772
773 if not self.a:
773 if not self.a:
774 # this happens when lines were only added to the hunk
774 # this happens when lines were only added to the hunk
775 for x in self.hunk:
775 for x in self.hunk:
776 if x.startswith('-') or x.startswith(' '):
776 if x.startswith('-') or x.startswith(' '):
777 self.a.append(x)
777 self.a.append(x)
778 if not self.b:
778 if not self.b:
779 # this happens when lines were only deleted from the hunk
779 # this happens when lines were only deleted from the hunk
780 for x in self.hunk:
780 for x in self.hunk:
781 if x.startswith('+') or x.startswith(' '):
781 if x.startswith('+') or x.startswith(' '):
782 self.b.append(x[1:])
782 self.b.append(x[1:])
783 # @@ -start,len +start,len @@
783 # @@ -start,len +start,len @@
784 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
784 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
785 self.startb, self.lenb)
785 self.startb, self.lenb)
786 self.hunk[0] = self.desc
786 self.hunk[0] = self.desc
787
787
788 def fix_newline(self):
788 def fix_newline(self):
789 diffhelpers.fix_newline(self.hunk, self.a, self.b)
789 diffhelpers.fix_newline(self.hunk, self.a, self.b)
790
790
791 def complete(self):
791 def complete(self):
792 return len(self.a) == self.lena and len(self.b) == self.lenb
792 return len(self.a) == self.lena and len(self.b) == self.lenb
793
793
794 def createfile(self):
794 def createfile(self):
795 return self.starta == 0 and self.lena == 0 and self.create
795 return self.starta == 0 and self.lena == 0 and self.create
796
796
797 def rmfile(self):
797 def rmfile(self):
798 return self.startb == 0 and self.lenb == 0 and self.remove
798 return self.startb == 0 and self.lenb == 0 and self.remove
799
799
800 def fuzzit(self, l, fuzz, toponly):
800 def fuzzit(self, l, fuzz, toponly):
801 # this removes context lines from the top and bottom of list 'l'. It
801 # this removes context lines from the top and bottom of list 'l'. It
802 # checks the hunk to make sure only context lines are removed, and then
802 # checks the hunk to make sure only context lines are removed, and then
803 # returns a new shortened list of lines.
803 # returns a new shortened list of lines.
804 fuzz = min(fuzz, len(l)-1)
804 fuzz = min(fuzz, len(l)-1)
805 if fuzz:
805 if fuzz:
806 top = 0
806 top = 0
807 bot = 0
807 bot = 0
808 hlen = len(self.hunk)
808 hlen = len(self.hunk)
809 for x in xrange(hlen - 1):
809 for x in xrange(hlen - 1):
810 # the hunk starts with the @@ line, so use x+1
810 # the hunk starts with the @@ line, so use x+1
811 if self.hunk[x + 1][0] == ' ':
811 if self.hunk[x + 1][0] == ' ':
812 top += 1
812 top += 1
813 else:
813 else:
814 break
814 break
815 if not toponly:
815 if not toponly:
816 for x in xrange(hlen - 1):
816 for x in xrange(hlen - 1):
817 if self.hunk[hlen - bot - 1][0] == ' ':
817 if self.hunk[hlen - bot - 1][0] == ' ':
818 bot += 1
818 bot += 1
819 else:
819 else:
820 break
820 break
821
821
822 # top and bot now count context in the hunk
822 # top and bot now count context in the hunk
823 # adjust them if either one is short
823 # adjust them if either one is short
824 context = max(top, bot, 3)
824 context = max(top, bot, 3)
825 if bot < context:
825 if bot < context:
826 bot = max(0, fuzz - (context - bot))
826 bot = max(0, fuzz - (context - bot))
827 else:
827 else:
828 bot = min(fuzz, bot)
828 bot = min(fuzz, bot)
829 if top < context:
829 if top < context:
830 top = max(0, fuzz - (context - top))
830 top = max(0, fuzz - (context - top))
831 else:
831 else:
832 top = min(fuzz, top)
832 top = min(fuzz, top)
833
833
834 return l[top:len(l)-bot]
834 return l[top:len(l)-bot]
835 return l
835 return l
836
836
837 def old(self, fuzz=0, toponly=False):
837 def old(self, fuzz=0, toponly=False):
838 return self.fuzzit(self.a, fuzz, toponly)
838 return self.fuzzit(self.a, fuzz, toponly)
839
839
840 def new(self, fuzz=0, toponly=False):
840 def new(self, fuzz=0, toponly=False):
841 return self.fuzzit(self.b, fuzz, toponly)
841 return self.fuzzit(self.b, fuzz, toponly)
842
842
843 class binhunk:
843 class binhunk:
844 'A binary patch file. Only understands literals so far.'
844 'A binary patch file. Only understands literals so far.'
845 def __init__(self, gitpatch):
845 def __init__(self, gitpatch):
846 self.gitpatch = gitpatch
846 self.gitpatch = gitpatch
847 self.text = None
847 self.text = None
848 self.hunk = ['GIT binary patch\n']
848 self.hunk = ['GIT binary patch\n']
849
849
850 def createfile(self):
850 def createfile(self):
851 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
851 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
852
852
853 def rmfile(self):
853 def rmfile(self):
854 return self.gitpatch.op == 'DELETE'
854 return self.gitpatch.op == 'DELETE'
855
855
856 def complete(self):
856 def complete(self):
857 return self.text is not None
857 return self.text is not None
858
858
859 def new(self):
859 def new(self):
860 return [self.text]
860 return [self.text]
861
861
862 def extract(self, lr):
862 def extract(self, lr):
863 line = lr.readline()
863 line = lr.readline()
864 self.hunk.append(line)
864 self.hunk.append(line)
865 while line and not line.startswith('literal '):
865 while line and not line.startswith('literal '):
866 line = lr.readline()
866 line = lr.readline()
867 self.hunk.append(line)
867 self.hunk.append(line)
868 if not line:
868 if not line:
869 raise PatchError(_('could not extract binary patch'))
869 raise PatchError(_('could not extract binary patch'))
870 size = int(line[8:].rstrip())
870 size = int(line[8:].rstrip())
871 dec = []
871 dec = []
872 line = lr.readline()
872 line = lr.readline()
873 self.hunk.append(line)
873 self.hunk.append(line)
874 while len(line) > 1:
874 while len(line) > 1:
875 l = line[0]
875 l = line[0]
876 if l <= 'Z' and l >= 'A':
876 if l <= 'Z' and l >= 'A':
877 l = ord(l) - ord('A') + 1
877 l = ord(l) - ord('A') + 1
878 else:
878 else:
879 l = ord(l) - ord('a') + 27
879 l = ord(l) - ord('a') + 27
880 dec.append(base85.b85decode(line[1:-1])[:l])
880 dec.append(base85.b85decode(line[1:-1])[:l])
881 line = lr.readline()
881 line = lr.readline()
882 self.hunk.append(line)
882 self.hunk.append(line)
883 text = zlib.decompress(''.join(dec))
883 text = zlib.decompress(''.join(dec))
884 if len(text) != size:
884 if len(text) != size:
885 raise PatchError(_('binary patch is %d bytes, not %d') %
885 raise PatchError(_('binary patch is %d bytes, not %d') %
886 len(text), size)
886 len(text), size)
887 self.text = text
887 self.text = text
888
888
889 def parsefilename(str):
889 def parsefilename(str):
890 # --- filename \t|space stuff
890 # --- filename \t|space stuff
891 s = str[4:].rstrip('\r\n')
891 s = str[4:].rstrip('\r\n')
892 i = s.find('\t')
892 i = s.find('\t')
893 if i < 0:
893 if i < 0:
894 i = s.find(' ')
894 i = s.find(' ')
895 if i < 0:
895 if i < 0:
896 return s
896 return s
897 return s[:i]
897 return s[:i]
898
898
899 def pathstrip(path, strip):
899 def pathstrip(path, strip):
900 pathlen = len(path)
900 pathlen = len(path)
901 i = 0
901 i = 0
902 if strip == 0:
902 if strip == 0:
903 return '', path.rstrip()
903 return '', path.rstrip()
904 count = strip
904 count = strip
905 while count > 0:
905 while count > 0:
906 i = path.find('/', i)
906 i = path.find('/', i)
907 if i == -1:
907 if i == -1:
908 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
908 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
909 (count, strip, path))
909 (count, strip, path))
910 i += 1
910 i += 1
911 # consume '//' in the path
911 # consume '//' in the path
912 while i < pathlen - 1 and path[i] == '/':
912 while i < pathlen - 1 and path[i] == '/':
913 i += 1
913 i += 1
914 count -= 1
914 count -= 1
915 return path[:i].lstrip(), path[i:].rstrip()
915 return path[:i].lstrip(), path[i:].rstrip()
916
916
917 def selectfile(afile_orig, bfile_orig, hunk, strip):
917 def selectfile(afile_orig, bfile_orig, hunk, strip):
918 nulla = afile_orig == "/dev/null"
918 nulla = afile_orig == "/dev/null"
919 nullb = bfile_orig == "/dev/null"
919 nullb = bfile_orig == "/dev/null"
920 abase, afile = pathstrip(afile_orig, strip)
920 abase, afile = pathstrip(afile_orig, strip)
921 gooda = not nulla and os.path.lexists(afile)
921 gooda = not nulla and os.path.lexists(afile)
922 bbase, bfile = pathstrip(bfile_orig, strip)
922 bbase, bfile = pathstrip(bfile_orig, strip)
923 if afile == bfile:
923 if afile == bfile:
924 goodb = gooda
924 goodb = gooda
925 else:
925 else:
926 goodb = not nullb and os.path.exists(bfile)
926 goodb = not nullb and os.path.exists(bfile)
927 createfunc = hunk.createfile
927 createfunc = hunk.createfile
928 missing = not goodb and not gooda and not createfunc()
928 missing = not goodb and not gooda and not createfunc()
929
929
930 # some diff programs apparently produce patches where the afile is
930 # some diff programs apparently produce patches where the afile is
931 # not /dev/null, but afile starts with bfile
931 # not /dev/null, but afile starts with bfile
932 abasedir = afile[:afile.rfind('/') + 1]
932 abasedir = afile[:afile.rfind('/') + 1]
933 bbasedir = bfile[:bfile.rfind('/') + 1]
933 bbasedir = bfile[:bfile.rfind('/') + 1]
934 if missing and abasedir == bbasedir and afile.startswith(bfile):
934 if missing and abasedir == bbasedir and afile.startswith(bfile):
935 # this isn't very pretty
935 # this isn't very pretty
936 hunk.create = True
936 hunk.create = True
937 if createfunc():
937 if createfunc():
938 missing = False
938 missing = False
939 else:
939 else:
940 hunk.create = False
940 hunk.create = False
941
941
942 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
942 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
943 # diff is between a file and its backup. In this case, the original
943 # diff is between a file and its backup. In this case, the original
944 # file should be patched (see original mpatch code).
944 # file should be patched (see original mpatch code).
945 isbackup = (abase == bbase and bfile.startswith(afile))
945 isbackup = (abase == bbase and bfile.startswith(afile))
946 fname = None
946 fname = None
947 if not missing:
947 if not missing:
948 if gooda and goodb:
948 if gooda and goodb:
949 fname = isbackup and afile or bfile
949 fname = isbackup and afile or bfile
950 elif gooda:
950 elif gooda:
951 fname = afile
951 fname = afile
952
952
953 if not fname:
953 if not fname:
954 if not nullb:
954 if not nullb:
955 fname = isbackup and afile or bfile
955 fname = isbackup and afile or bfile
956 elif not nulla:
956 elif not nulla:
957 fname = afile
957 fname = afile
958 else:
958 else:
959 raise PatchError(_("undefined source and destination files"))
959 raise PatchError(_("undefined source and destination files"))
960
960
961 return fname, missing
961 return fname, missing
962
962
963 def scangitpatch(lr, firstline):
963 def scangitpatch(lr, firstline):
964 """
964 """
965 Git patches can emit:
965 Git patches can emit:
966 - rename a to b
966 - rename a to b
967 - change b
967 - change b
968 - copy a to c
968 - copy a to c
969 - change c
969 - change c
970
970
971 We cannot apply this sequence as-is, the renamed 'a' could not be
971 We cannot apply this sequence as-is, the renamed 'a' could not be
972 found for it would have been renamed already. And we cannot copy
972 found for it would have been renamed already. And we cannot copy
973 from 'b' instead because 'b' would have been changed already. So
973 from 'b' instead because 'b' would have been changed already. So
974 we scan the git patch for copy and rename commands so we can
974 we scan the git patch for copy and rename commands so we can
975 perform the copies ahead of time.
975 perform the copies ahead of time.
976 """
976 """
977 pos = 0
977 pos = 0
978 try:
978 try:
979 pos = lr.fp.tell()
979 pos = lr.fp.tell()
980 fp = lr.fp
980 fp = lr.fp
981 except IOError:
981 except IOError:
982 fp = cStringIO.StringIO(lr.fp.read())
982 fp = cStringIO.StringIO(lr.fp.read())
983 gitlr = linereader(fp, lr.textmode)
983 gitlr = linereader(fp, lr.textmode)
984 gitlr.push(firstline)
984 gitlr.push(firstline)
985 (dopatch, gitpatches) = readgitpatch(gitlr)
985 (dopatch, gitpatches) = readgitpatch(gitlr)
986 fp.seek(pos)
986 fp.seek(pos)
987 return dopatch, gitpatches
987 return dopatch, gitpatches
988
988
989 def iterhunks(ui, fp, sourcefile=None):
989 def iterhunks(ui, fp, sourcefile=None):
990 """Read a patch and yield the following events:
990 """Read a patch and yield the following events:
991 - ("file", afile, bfile, firsthunk): select a new target file.
991 - ("file", afile, bfile, firsthunk): select a new target file.
992 - ("hunk", hunk): a new hunk is ready to be applied, follows a
992 - ("hunk", hunk): a new hunk is ready to be applied, follows a
993 "file" event.
993 "file" event.
994 - ("git", gitchanges): current diff is in git format, gitchanges
994 - ("git", gitchanges): current diff is in git format, gitchanges
995 maps filenames to gitpatch records. Unique event.
995 maps filenames to gitpatch records. Unique event.
996 """
996 """
997 changed = {}
997 changed = {}
998 current_hunk = None
998 current_hunk = None
999 afile = ""
999 afile = ""
1000 bfile = ""
1000 bfile = ""
1001 state = None
1001 state = None
1002 hunknum = 0
1002 hunknum = 0
1003 emitfile = False
1003 emitfile = False
1004 git = False
1004 git = False
1005
1005
1006 # our states
1006 # our states
1007 BFILE = 1
1007 BFILE = 1
1008 context = None
1008 context = None
1009 lr = linereader(fp)
1009 lr = linereader(fp)
1010 # gitworkdone is True if a git operation (copy, rename, ...) was
1010 # gitworkdone is True if a git operation (copy, rename, ...) was
1011 # performed already for the current file. Useful when the file
1011 # performed already for the current file. Useful when the file
1012 # section may have no hunk.
1012 # section may have no hunk.
1013 gitworkdone = False
1013 gitworkdone = False
1014 empty = None
1014 empty = None
1015
1015
1016 while True:
1016 while True:
1017 newfile = newgitfile = False
1017 newfile = newgitfile = False
1018 x = lr.readline()
1018 x = lr.readline()
1019 if not x:
1019 if not x:
1020 break
1020 break
1021 if current_hunk:
1021 if current_hunk:
1022 if x.startswith('\ '):
1022 if x.startswith('\ '):
1023 current_hunk.fix_newline()
1023 current_hunk.fix_newline()
1024 yield 'hunk', current_hunk
1024 yield 'hunk', current_hunk
1025 current_hunk = None
1025 current_hunk = None
1026 empty = False
1026 empty = False
1027 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
1027 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
1028 ((context is not False) and x.startswith('***************')))):
1028 ((context is not False) and x.startswith('***************')))):
1029 try:
1029 try:
1030 if context is None and x.startswith('***************'):
1030 if context is None and x.startswith('***************'):
1031 context = True
1031 context = True
1032 gpatch = changed.get(bfile)
1032 gpatch = changed.get(bfile)
1033 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
1033 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
1034 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
1034 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
1035 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
1035 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
1036 except PatchError, err:
1036 except PatchError, err:
1037 ui.debug(err)
1037 ui.debug(err)
1038 current_hunk = None
1038 current_hunk = None
1039 continue
1039 continue
1040 hunknum += 1
1040 hunknum += 1
1041 if emitfile:
1041 if emitfile:
1042 emitfile = False
1042 emitfile = False
1043 yield 'file', (afile, bfile, current_hunk)
1043 yield 'file', (afile, bfile, current_hunk)
1044 empty = False
1044 empty = False
1045 elif state == BFILE and x.startswith('GIT binary patch'):
1045 elif state == BFILE and x.startswith('GIT binary patch'):
1046 current_hunk = binhunk(changed[bfile])
1046 current_hunk = binhunk(changed[bfile])
1047 hunknum += 1
1047 hunknum += 1
1048 if emitfile:
1048 if emitfile:
1049 emitfile = False
1049 emitfile = False
1050 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
1050 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
1051 empty = False
1051 empty = False
1052 current_hunk.extract(lr)
1052 current_hunk.extract(lr)
1053 elif x.startswith('diff --git'):
1053 elif x.startswith('diff --git'):
1054 # check for git diff, scanning the whole patch file if needed
1054 # check for git diff, scanning the whole patch file if needed
1055 m = gitre.match(x)
1055 m = gitre.match(x)
1056 gitworkdone = False
1056 gitworkdone = False
1057 if m:
1057 if m:
1058 afile, bfile = m.group(1, 2)
1058 afile, bfile = m.group(1, 2)
1059 if not git:
1059 if not git:
1060 git = True
1060 git = True
1061 gitpatches = scangitpatch(lr, x)[1]
1061 gitpatches = scangitpatch(lr, x)[1]
1062 yield 'git', gitpatches
1062 yield 'git', gitpatches
1063 for gp in gitpatches:
1063 for gp in gitpatches:
1064 changed[gp.path] = gp
1064 changed[gp.path] = gp
1065 # else error?
1065 # else error?
1066 # copy/rename + modify should modify target, not source
1066 # copy/rename + modify should modify target, not source
1067 gp = changed.get(bfile)
1067 gp = changed.get(bfile)
1068 if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
1068 if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
1069 or gp.mode):
1069 or gp.mode):
1070 afile = bfile
1070 afile = bfile
1071 gitworkdone = True
1071 gitworkdone = True
1072 newgitfile = True
1072 newgitfile = True
1073 elif x.startswith('---'):
1073 elif x.startswith('---'):
1074 # check for a unified diff
1074 # check for a unified diff
1075 l2 = lr.readline()
1075 l2 = lr.readline()
1076 if not l2.startswith('+++'):
1076 if not l2.startswith('+++'):
1077 lr.push(l2)
1077 lr.push(l2)
1078 continue
1078 continue
1079 newfile = True
1079 newfile = True
1080 context = False
1080 context = False
1081 afile = parsefilename(x)
1081 afile = parsefilename(x)
1082 bfile = parsefilename(l2)
1082 bfile = parsefilename(l2)
1083 elif x.startswith('***'):
1083 elif x.startswith('***'):
1084 # check for a context diff
1084 # check for a context diff
1085 l2 = lr.readline()
1085 l2 = lr.readline()
1086 if not l2.startswith('---'):
1086 if not l2.startswith('---'):
1087 lr.push(l2)
1087 lr.push(l2)
1088 continue
1088 continue
1089 l3 = lr.readline()
1089 l3 = lr.readline()
1090 lr.push(l3)
1090 lr.push(l3)
1091 if not l3.startswith("***************"):
1091 if not l3.startswith("***************"):
1092 lr.push(l2)
1092 lr.push(l2)
1093 continue
1093 continue
1094 newfile = True
1094 newfile = True
1095 context = True
1095 context = True
1096 afile = parsefilename(x)
1096 afile = parsefilename(x)
1097 bfile = parsefilename(l2)
1097 bfile = parsefilename(l2)
1098
1098
1099 if newfile:
1099 if newfile:
1100 if empty:
1100 if empty:
1101 raise NoHunks
1101 raise NoHunks
1102 empty = not gitworkdone
1102 empty = not gitworkdone
1103 gitworkdone = False
1103 gitworkdone = False
1104
1104
1105 if newgitfile or newfile:
1105 if newgitfile or newfile:
1106 emitfile = True
1106 emitfile = True
1107 state = BFILE
1107 state = BFILE
1108 hunknum = 0
1108 hunknum = 0
1109 if current_hunk:
1109 if current_hunk:
1110 if current_hunk.complete():
1110 if current_hunk.complete():
1111 yield 'hunk', current_hunk
1111 yield 'hunk', current_hunk
1112 empty = False
1112 empty = False
1113 else:
1113 else:
1114 raise PatchError(_("malformed patch %s %s") % (afile,
1114 raise PatchError(_("malformed patch %s %s") % (afile,
1115 current_hunk.desc))
1115 current_hunk.desc))
1116
1116
1117 if (empty is None and not gitworkdone) or empty:
1117 if (empty is None and not gitworkdone) or empty:
1118 raise NoHunks
1118 raise NoHunks
1119
1119
1120
1120
1121 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
1121 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
1122 """Reads a patch from fp and tries to apply it.
1122 """Reads a patch from fp and tries to apply it.
1123
1123
1124 The dict 'changed' is filled in with all of the filenames changed
1124 The dict 'changed' is filled in with all of the filenames changed
1125 by the patch. Returns 0 for a clean patch, -1 if any rejects were
1125 by the patch. Returns 0 for a clean patch, -1 if any rejects were
1126 found and 1 if there was any fuzz.
1126 found and 1 if there was any fuzz.
1127
1127
1128 If 'eolmode' is 'strict', the patch content and patched file are
1128 If 'eolmode' is 'strict', the patch content and patched file are
1129 read in binary mode. Otherwise, line endings are ignored when
1129 read in binary mode. Otherwise, line endings are ignored when
1130 patching then normalized according to 'eolmode'.
1130 patching then normalized according to 'eolmode'.
1131
1131
1132 Callers probably want to call 'updatedir' after this to apply
1132 Callers probably want to call 'updatedir' after this to apply
1133 certain categories of changes not done by this function.
1133 certain categories of changes not done by this function.
1134 """
1134 """
1135 return _applydiff(
1135 return _applydiff(
1136 ui, fp, patchfile, copyfile,
1136 ui, fp, patchfile, copyfile,
1137 changed, strip=strip, sourcefile=sourcefile, eolmode=eolmode)
1137 changed, strip=strip, sourcefile=sourcefile, eolmode=eolmode)
1138
1138
1139
1139
1140 def _applydiff(ui, fp, patcher, copyfn, changed, strip=1,
1140 def _applydiff(ui, fp, patcher, copyfn, changed, strip=1,
1141 sourcefile=None, eolmode='strict'):
1141 sourcefile=None, eolmode='strict'):
1142 rejects = 0
1142 rejects = 0
1143 err = 0
1143 err = 0
1144 current_file = None
1144 current_file = None
1145 cwd = os.getcwd()
1145 cwd = os.getcwd()
1146 opener = util.opener(cwd)
1146 opener = util.opener(cwd)
1147
1147
1148 def closefile():
1148 def closefile():
1149 if not current_file:
1149 if not current_file:
1150 return 0
1150 return 0
1151 if current_file.dirty:
1151 if current_file.dirty:
1152 current_file.writelines(current_file.fname, current_file.lines)
1152 current_file.writelines(current_file.fname, current_file.lines)
1153 current_file.write_rej()
1153 current_file.write_rej()
1154 return len(current_file.rej)
1154 return len(current_file.rej)
1155
1155
1156 for state, values in iterhunks(ui, fp, sourcefile):
1156 for state, values in iterhunks(ui, fp, sourcefile):
1157 if state == 'hunk':
1157 if state == 'hunk':
1158 if not current_file:
1158 if not current_file:
1159 continue
1159 continue
1160 ret = current_file.apply(values)
1160 ret = current_file.apply(values)
1161 if ret >= 0:
1161 if ret >= 0:
1162 changed.setdefault(current_file.fname, None)
1162 changed.setdefault(current_file.fname, None)
1163 if ret > 0:
1163 if ret > 0:
1164 err = 1
1164 err = 1
1165 elif state == 'file':
1165 elif state == 'file':
1166 rejects += closefile()
1166 rejects += closefile()
1167 afile, bfile, first_hunk = values
1167 afile, bfile, first_hunk = values
1168 try:
1168 try:
1169 if sourcefile:
1169 if sourcefile:
1170 current_file = patcher(ui, sourcefile, opener,
1170 current_file = patcher(ui, sourcefile, opener,
1171 eolmode=eolmode)
1171 eolmode=eolmode)
1172 else:
1172 else:
1173 current_file, missing = selectfile(afile, bfile,
1173 current_file, missing = selectfile(afile, bfile,
1174 first_hunk, strip)
1174 first_hunk, strip)
1175 current_file = patcher(ui, current_file, opener,
1175 current_file = patcher(ui, current_file, opener,
1176 missing=missing, eolmode=eolmode)
1176 missing=missing, eolmode=eolmode)
1177 except PatchError, err:
1177 except PatchError, err:
1178 ui.warn(str(err) + '\n')
1178 ui.warn(str(err) + '\n')
1179 current_file = None
1179 current_file = None
1180 rejects += 1
1180 rejects += 1
1181 continue
1181 continue
1182 elif state == 'git':
1182 elif state == 'git':
1183 for gp in values:
1183 for gp in values:
1184 gp.path = pathstrip(gp.path, strip - 1)[1]
1184 gp.path = pathstrip(gp.path, strip - 1)[1]
1185 if gp.oldpath:
1185 if gp.oldpath:
1186 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1186 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1187 if gp.op in ('COPY', 'RENAME'):
1187 if gp.op in ('COPY', 'RENAME'):
1188 copyfn(gp.oldpath, gp.path, cwd)
1188 copyfn(gp.oldpath, gp.path, cwd)
1189 changed[gp.path] = gp
1189 changed[gp.path] = gp
1190 else:
1190 else:
1191 raise util.Abort(_('unsupported parser state: %s') % state)
1191 raise util.Abort(_('unsupported parser state: %s') % state)
1192
1192
1193 rejects += closefile()
1193 rejects += closefile()
1194
1194
1195 if rejects:
1195 if rejects:
1196 return -1
1196 return -1
1197 return err
1197 return err
1198
1198
1199 def updatedir(ui, repo, patches, similarity=0):
1199 def updatedir(ui, repo, patches, similarity=0):
1200 '''Update dirstate after patch application according to metadata'''
1200 '''Update dirstate after patch application according to metadata'''
1201 if not patches:
1201 if not patches:
1202 return
1202 return
1203 copies = []
1203 copies = []
1204 removes = set()
1204 removes = set()
1205 cfiles = patches.keys()
1205 cfiles = patches.keys()
1206 cwd = repo.getcwd()
1206 cwd = repo.getcwd()
1207 if cwd:
1207 if cwd:
1208 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1208 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1209 for f in patches:
1209 for f in patches:
1210 gp = patches[f]
1210 gp = patches[f]
1211 if not gp:
1211 if not gp:
1212 continue
1212 continue
1213 if gp.op == 'RENAME':
1213 if gp.op == 'RENAME':
1214 copies.append((gp.oldpath, gp.path))
1214 copies.append((gp.oldpath, gp.path))
1215 removes.add(gp.oldpath)
1215 removes.add(gp.oldpath)
1216 elif gp.op == 'COPY':
1216 elif gp.op == 'COPY':
1217 copies.append((gp.oldpath, gp.path))
1217 copies.append((gp.oldpath, gp.path))
1218 elif gp.op == 'DELETE':
1218 elif gp.op == 'DELETE':
1219 removes.add(gp.path)
1219 removes.add(gp.path)
1220
1220
1221 wctx = repo[None]
1221 wctx = repo[None]
1222 for src, dst in copies:
1222 for src, dst in copies:
1223 wctx.copy(src, dst)
1223 wctx.copy(src, dst)
1224 if (not similarity) and removes:
1224 if (not similarity) and removes:
1225 wctx.remove(sorted(removes), True)
1225 wctx.remove(sorted(removes), True)
1226
1226
1227 for f in patches:
1227 for f in patches:
1228 gp = patches[f]
1228 gp = patches[f]
1229 if gp and gp.mode:
1229 if gp and gp.mode:
1230 islink, isexec = gp.mode
1230 islink, isexec = gp.mode
1231 dst = repo.wjoin(gp.path)
1231 dst = repo.wjoin(gp.path)
1232 # patch won't create empty files
1232 # patch won't create empty files
1233 if gp.op == 'ADD' and not os.path.exists(dst):
1233 if gp.op == 'ADD' and not os.path.exists(dst):
1234 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1234 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1235 repo.wwrite(gp.path, '', flags)
1235 repo.wwrite(gp.path, '', flags)
1236 util.set_flags(dst, islink, isexec)
1236 util.set_flags(dst, islink, isexec)
1237 cmdutil.addremove(repo, cfiles, similarity=similarity)
1237 cmdutil.addremove(repo, cfiles, similarity=similarity)
1238 files = patches.keys()
1238 files = patches.keys()
1239 files.extend([r for r in removes if r not in files])
1239 files.extend([r for r in removes if r not in files])
1240 return sorted(files)
1240 return sorted(files)
1241
1241
1242 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1242 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1243 """use <patcher> to apply <patchname> to the working directory.
1243 """use <patcher> to apply <patchname> to the working directory.
1244 returns whether patch was applied with fuzz factor."""
1244 returns whether patch was applied with fuzz factor."""
1245
1245
1246 fuzz = False
1246 fuzz = False
1247 if cwd:
1247 if cwd:
1248 args.append('-d %s' % util.shellquote(cwd))
1248 args.append('-d %s' % util.shellquote(cwd))
1249 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1249 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1250 util.shellquote(patchname)))
1250 util.shellquote(patchname)))
1251
1251
1252 for line in fp:
1252 for line in fp:
1253 line = line.rstrip()
1253 line = line.rstrip()
1254 ui.note(line + '\n')
1254 ui.note(line + '\n')
1255 if line.startswith('patching file '):
1255 if line.startswith('patching file '):
1256 pf = util.parse_patch_output(line)
1256 pf = util.parse_patch_output(line)
1257 printed_file = False
1257 printed_file = False
1258 files.setdefault(pf, None)
1258 files.setdefault(pf, None)
1259 elif line.find('with fuzz') >= 0:
1259 elif line.find('with fuzz') >= 0:
1260 fuzz = True
1260 fuzz = True
1261 if not printed_file:
1261 if not printed_file:
1262 ui.warn(pf + '\n')
1262 ui.warn(pf + '\n')
1263 printed_file = True
1263 printed_file = True
1264 ui.warn(line + '\n')
1264 ui.warn(line + '\n')
1265 elif line.find('saving rejects to file') >= 0:
1265 elif line.find('saving rejects to file') >= 0:
1266 ui.warn(line + '\n')
1266 ui.warn(line + '\n')
1267 elif line.find('FAILED') >= 0:
1267 elif line.find('FAILED') >= 0:
1268 if not printed_file:
1268 if not printed_file:
1269 ui.warn(pf + '\n')
1269 ui.warn(pf + '\n')
1270 printed_file = True
1270 printed_file = True
1271 ui.warn(line + '\n')
1271 ui.warn(line + '\n')
1272 code = fp.close()
1272 code = fp.close()
1273 if code:
1273 if code:
1274 raise PatchError(_("patch command failed: %s") %
1274 raise PatchError(_("patch command failed: %s") %
1275 util.explain_exit(code)[0])
1275 util.explain_exit(code)[0])
1276 return fuzz
1276 return fuzz
1277
1277
1278 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1278 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1279 """use builtin patch to apply <patchobj> to the working directory.
1279 """use builtin patch to apply <patchobj> to the working directory.
1280 returns whether patch was applied with fuzz factor."""
1280 returns whether patch was applied with fuzz factor."""
1281
1281
1282 if files is None:
1282 if files is None:
1283 files = {}
1283 files = {}
1284 if eolmode is None:
1284 if eolmode is None:
1285 eolmode = ui.config('patch', 'eol', 'strict')
1285 eolmode = ui.config('patch', 'eol', 'strict')
1286 if eolmode.lower() not in eolmodes:
1286 if eolmode.lower() not in eolmodes:
1287 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1287 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1288 eolmode = eolmode.lower()
1288 eolmode = eolmode.lower()
1289
1289
1290 try:
1290 try:
1291 fp = open(patchobj, 'rb')
1291 fp = open(patchobj, 'rb')
1292 except TypeError:
1292 except TypeError:
1293 fp = patchobj
1293 fp = patchobj
1294 if cwd:
1294 if cwd:
1295 curdir = os.getcwd()
1295 curdir = os.getcwd()
1296 os.chdir(cwd)
1296 os.chdir(cwd)
1297 try:
1297 try:
1298 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1298 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1299 finally:
1299 finally:
1300 if cwd:
1300 if cwd:
1301 os.chdir(curdir)
1301 os.chdir(curdir)
1302 if fp != patchobj:
1302 if fp != patchobj:
1303 fp.close()
1303 fp.close()
1304 if ret < 0:
1304 if ret < 0:
1305 raise PatchError
1305 raise PatchError
1306 return ret > 0
1306 return ret > 0
1307
1307
1308 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1308 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1309 """Apply <patchname> to the working directory.
1309 """Apply <patchname> to the working directory.
1310
1310
1311 'eolmode' specifies how end of lines should be handled. It can be:
1311 'eolmode' specifies how end of lines should be handled. It can be:
1312 - 'strict': inputs are read in binary mode, EOLs are preserved
1312 - 'strict': inputs are read in binary mode, EOLs are preserved
1313 - 'crlf': EOLs are ignored when patching and reset to CRLF
1313 - 'crlf': EOLs are ignored when patching and reset to CRLF
1314 - 'lf': EOLs are ignored when patching and reset to LF
1314 - 'lf': EOLs are ignored when patching and reset to LF
1315 - None: get it from user settings, default to 'strict'
1315 - None: get it from user settings, default to 'strict'
1316 'eolmode' is ignored when using an external patcher program.
1316 'eolmode' is ignored when using an external patcher program.
1317
1317
1318 Returns whether patch was applied with fuzz factor.
1318 Returns whether patch was applied with fuzz factor.
1319 """
1319 """
1320 patcher = ui.config('ui', 'patch')
1320 patcher = ui.config('ui', 'patch')
1321 args = []
1321 args = []
1322 if files is None:
1322 if files is None:
1323 files = {}
1323 files = {}
1324 try:
1324 try:
1325 if patcher:
1325 if patcher:
1326 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1326 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1327 files)
1327 files)
1328 else:
1328 else:
1329 try:
1329 try:
1330 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1330 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1331 except NoHunks:
1331 except NoHunks:
1332 ui.warn(_('internal patcher failed\n'
1332 ui.warn(_('internal patcher failed\n'
1333 'please report details to '
1333 'please report details to '
1334 'http://mercurial.selenic.com/bts/\n'
1334 'http://mercurial.selenic.com/bts/\n'
1335 'or mercurial@selenic.com\n'))
1335 'or mercurial@selenic.com\n'))
1336 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1336 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1337 or 'patch')
1337 or 'patch')
1338 ui.debug('no valid hunks found; trying with %r instead\n' %
1338 ui.debug('no valid hunks found; trying with %r instead\n' %
1339 patcher)
1339 patcher)
1340 if util.needbinarypatch():
1340 if util.needbinarypatch():
1341 args.append('--binary')
1341 args.append('--binary')
1342 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1342 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1343 files)
1343 files)
1344 except PatchError, err:
1344 except PatchError, err:
1345 s = str(err)
1345 s = str(err)
1346 if s:
1346 if s:
1347 raise util.Abort(s)
1347 raise util.Abort(s)
1348 else:
1348 else:
1349 raise util.Abort(_('patch failed to apply'))
1349 raise util.Abort(_('patch failed to apply'))
1350
1350
1351 def b85diff(to, tn):
1351 def b85diff(to, tn):
1352 '''print base85-encoded binary diff'''
1352 '''print base85-encoded binary diff'''
1353 def gitindex(text):
1353 def gitindex(text):
1354 if not text:
1354 if not text:
1355 return hex(nullid)
1355 return hex(nullid)
1356 l = len(text)
1356 l = len(text)
1357 s = util.sha1('blob %d\0' % l)
1357 s = util.sha1('blob %d\0' % l)
1358 s.update(text)
1358 s.update(text)
1359 return s.hexdigest()
1359 return s.hexdigest()
1360
1360
1361 def fmtline(line):
1361 def fmtline(line):
1362 l = len(line)
1362 l = len(line)
1363 if l <= 26:
1363 if l <= 26:
1364 l = chr(ord('A') + l - 1)
1364 l = chr(ord('A') + l - 1)
1365 else:
1365 else:
1366 l = chr(l - 26 + ord('a') - 1)
1366 l = chr(l - 26 + ord('a') - 1)
1367 return '%c%s\n' % (l, base85.b85encode(line, True))
1367 return '%c%s\n' % (l, base85.b85encode(line, True))
1368
1368
1369 def chunk(text, csize=52):
1369 def chunk(text, csize=52):
1370 l = len(text)
1370 l = len(text)
1371 i = 0
1371 i = 0
1372 while i < l:
1372 while i < l:
1373 yield text[i:i + csize]
1373 yield text[i:i + csize]
1374 i += csize
1374 i += csize
1375
1375
1376 tohash = gitindex(to)
1376 tohash = gitindex(to)
1377 tnhash = gitindex(tn)
1377 tnhash = gitindex(tn)
1378 if tohash == tnhash:
1378 if tohash == tnhash:
1379 return ""
1379 return ""
1380
1380
1381 # TODO: deltas
1381 # TODO: deltas
1382 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1382 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1383 (tohash, tnhash, len(tn))]
1383 (tohash, tnhash, len(tn))]
1384 for l in chunk(zlib.compress(tn)):
1384 for l in chunk(zlib.compress(tn)):
1385 ret.append(fmtline(l))
1385 ret.append(fmtline(l))
1386 ret.append('\n')
1386 ret.append('\n')
1387 return ''.join(ret)
1387 return ''.join(ret)
1388
1388
1389 class GitDiffRequired(Exception):
1389 class GitDiffRequired(Exception):
1390 pass
1390 pass
1391
1391
1392 def diffopts(ui, opts=None, untrusted=False):
1392 def diffopts(ui, opts=None, untrusted=False):
1393 def get(key, name=None, getter=ui.configbool):
1393 def get(key, name=None, getter=ui.configbool):
1394 return ((opts and opts.get(key)) or
1394 return ((opts and opts.get(key)) or
1395 getter('diff', name or key, None, untrusted=untrusted))
1395 getter('diff', name or key, None, untrusted=untrusted))
1396 return mdiff.diffopts(
1396 return mdiff.diffopts(
1397 text=opts and opts.get('text'),
1397 text=opts and opts.get('text'),
1398 git=get('git'),
1398 git=get('git'),
1399 nodates=get('nodates'),
1399 nodates=get('nodates'),
1400 showfunc=get('show_function', 'showfunc'),
1400 showfunc=get('show_function', 'showfunc'),
1401 ignorews=get('ignore_all_space', 'ignorews'),
1401 ignorews=get('ignore_all_space', 'ignorews'),
1402 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1402 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1403 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1403 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1404 context=get('unified', getter=ui.config))
1404 context=get('unified', getter=ui.config))
1405
1405
1406 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1406 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1407 losedatafn=None):
1407 losedatafn=None, prefix=''):
1408 '''yields diff of changes to files between two nodes, or node and
1408 '''yields diff of changes to files between two nodes, or node and
1409 working directory.
1409 working directory.
1410
1410
1411 if node1 is None, use first dirstate parent instead.
1411 if node1 is None, use first dirstate parent instead.
1412 if node2 is None, compare node1 with working directory.
1412 if node2 is None, compare node1 with working directory.
1413
1413
1414 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1414 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1415 every time some change cannot be represented with the current
1415 every time some change cannot be represented with the current
1416 patch format. Return False to upgrade to git patch format, True to
1416 patch format. Return False to upgrade to git patch format, True to
1417 accept the loss or raise an exception to abort the diff. It is
1417 accept the loss or raise an exception to abort the diff. It is
1418 called with the name of current file being diffed as 'fn'. If set
1418 called with the name of current file being diffed as 'fn'. If set
1419 to None, patches will always be upgraded to git format when
1419 to None, patches will always be upgraded to git format when
1420 necessary.
1420 necessary.
1421
1422 prefix is a filename prefix that is prepended to all filenames on
1423 display (used for subrepos).
1421 '''
1424 '''
1422
1425
1423 if opts is None:
1426 if opts is None:
1424 opts = mdiff.defaultopts
1427 opts = mdiff.defaultopts
1425
1428
1426 if not node1 and not node2:
1429 if not node1 and not node2:
1427 node1 = repo.dirstate.parents()[0]
1430 node1 = repo.dirstate.parents()[0]
1428
1431
1429 def lrugetfilectx():
1432 def lrugetfilectx():
1430 cache = {}
1433 cache = {}
1431 order = []
1434 order = []
1432 def getfilectx(f, ctx):
1435 def getfilectx(f, ctx):
1433 fctx = ctx.filectx(f, filelog=cache.get(f))
1436 fctx = ctx.filectx(f, filelog=cache.get(f))
1434 if f not in cache:
1437 if f not in cache:
1435 if len(cache) > 20:
1438 if len(cache) > 20:
1436 del cache[order.pop(0)]
1439 del cache[order.pop(0)]
1437 cache[f] = fctx.filelog()
1440 cache[f] = fctx.filelog()
1438 else:
1441 else:
1439 order.remove(f)
1442 order.remove(f)
1440 order.append(f)
1443 order.append(f)
1441 return fctx
1444 return fctx
1442 return getfilectx
1445 return getfilectx
1443 getfilectx = lrugetfilectx()
1446 getfilectx = lrugetfilectx()
1444
1447
1445 ctx1 = repo[node1]
1448 ctx1 = repo[node1]
1446 ctx2 = repo[node2]
1449 ctx2 = repo[node2]
1447
1450
1448 if not changes:
1451 if not changes:
1449 changes = repo.status(ctx1, ctx2, match=match)
1452 changes = repo.status(ctx1, ctx2, match=match)
1450 modified, added, removed = changes[:3]
1453 modified, added, removed = changes[:3]
1451
1454
1452 if not modified and not added and not removed:
1455 if not modified and not added and not removed:
1453 return []
1456 return []
1454
1457
1455 revs = None
1458 revs = None
1456 if not repo.ui.quiet:
1459 if not repo.ui.quiet:
1457 hexfunc = repo.ui.debugflag and hex or short
1460 hexfunc = repo.ui.debugflag and hex or short
1458 revs = [hexfunc(node) for node in [node1, node2] if node]
1461 revs = [hexfunc(node) for node in [node1, node2] if node]
1459
1462
1460 copy = {}
1463 copy = {}
1461 if opts.git or opts.upgrade:
1464 if opts.git or opts.upgrade:
1462 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1465 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1463
1466
1464 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1467 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1465 modified, added, removed, copy, getfilectx, opts, losedata)
1468 modified, added, removed, copy, getfilectx, opts, losedata, prefix)
1466 if opts.upgrade and not opts.git:
1469 if opts.upgrade and not opts.git:
1467 try:
1470 try:
1468 def losedata(fn):
1471 def losedata(fn):
1469 if not losedatafn or not losedatafn(fn=fn):
1472 if not losedatafn or not losedatafn(fn=fn):
1470 raise GitDiffRequired()
1473 raise GitDiffRequired()
1471 # Buffer the whole output until we are sure it can be generated
1474 # Buffer the whole output until we are sure it can be generated
1472 return list(difffn(opts.copy(git=False), losedata))
1475 return list(difffn(opts.copy(git=False), losedata))
1473 except GitDiffRequired:
1476 except GitDiffRequired:
1474 return difffn(opts.copy(git=True), None)
1477 return difffn(opts.copy(git=True), None)
1475 else:
1478 else:
1476 return difffn(opts, None)
1479 return difffn(opts, None)
1477
1480
1478 def difflabel(func, *args, **kw):
1481 def difflabel(func, *args, **kw):
1479 '''yields 2-tuples of (output, label) based on the output of func()'''
1482 '''yields 2-tuples of (output, label) based on the output of func()'''
1480 prefixes = [('diff', 'diff.diffline'),
1483 prefixes = [('diff', 'diff.diffline'),
1481 ('copy', 'diff.extended'),
1484 ('copy', 'diff.extended'),
1482 ('rename', 'diff.extended'),
1485 ('rename', 'diff.extended'),
1483 ('old', 'diff.extended'),
1486 ('old', 'diff.extended'),
1484 ('new', 'diff.extended'),
1487 ('new', 'diff.extended'),
1485 ('deleted', 'diff.extended'),
1488 ('deleted', 'diff.extended'),
1486 ('---', 'diff.file_a'),
1489 ('---', 'diff.file_a'),
1487 ('+++', 'diff.file_b'),
1490 ('+++', 'diff.file_b'),
1488 ('@@', 'diff.hunk'),
1491 ('@@', 'diff.hunk'),
1489 ('-', 'diff.deleted'),
1492 ('-', 'diff.deleted'),
1490 ('+', 'diff.inserted')]
1493 ('+', 'diff.inserted')]
1491
1494
1492 for chunk in func(*args, **kw):
1495 for chunk in func(*args, **kw):
1493 lines = chunk.split('\n')
1496 lines = chunk.split('\n')
1494 for i, line in enumerate(lines):
1497 for i, line in enumerate(lines):
1495 if i != 0:
1498 if i != 0:
1496 yield ('\n', '')
1499 yield ('\n', '')
1497 stripline = line
1500 stripline = line
1498 if line and line[0] in '+-':
1501 if line and line[0] in '+-':
1499 # highlight trailing whitespace, but only in changed lines
1502 # highlight trailing whitespace, but only in changed lines
1500 stripline = line.rstrip()
1503 stripline = line.rstrip()
1501 for prefix, label in prefixes:
1504 for prefix, label in prefixes:
1502 if stripline.startswith(prefix):
1505 if stripline.startswith(prefix):
1503 yield (stripline, label)
1506 yield (stripline, label)
1504 break
1507 break
1505 else:
1508 else:
1506 yield (line, '')
1509 yield (line, '')
1507 if line != stripline:
1510 if line != stripline:
1508 yield (line[len(stripline):], 'diff.trailingwhitespace')
1511 yield (line[len(stripline):], 'diff.trailingwhitespace')
1509
1512
1510 def diffui(*args, **kw):
1513 def diffui(*args, **kw):
1511 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1514 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1512 return difflabel(diff, *args, **kw)
1515 return difflabel(diff, *args, **kw)
1513
1516
1514
1517
1515 def _addmodehdr(header, omode, nmode):
1518 def _addmodehdr(header, omode, nmode):
1516 if omode != nmode:
1519 if omode != nmode:
1517 header.append('old mode %s\n' % omode)
1520 header.append('old mode %s\n' % omode)
1518 header.append('new mode %s\n' % nmode)
1521 header.append('new mode %s\n' % nmode)
1519
1522
1520 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1523 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1521 copy, getfilectx, opts, losedatafn):
1524 copy, getfilectx, opts, losedatafn, prefix):
1525
1526 def join(f):
1527 return os.path.join(prefix, f)
1522
1528
1523 date1 = util.datestr(ctx1.date())
1529 date1 = util.datestr(ctx1.date())
1524 man1 = ctx1.manifest()
1530 man1 = ctx1.manifest()
1525
1531
1526 gone = set()
1532 gone = set()
1527 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1533 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1528
1534
1529 copyto = dict([(v, k) for k, v in copy.items()])
1535 copyto = dict([(v, k) for k, v in copy.items()])
1530
1536
1531 if opts.git:
1537 if opts.git:
1532 revs = None
1538 revs = None
1533
1539
1534 for f in sorted(modified + added + removed):
1540 for f in sorted(modified + added + removed):
1535 to = None
1541 to = None
1536 tn = None
1542 tn = None
1537 dodiff = True
1543 dodiff = True
1538 header = []
1544 header = []
1539 if f in man1:
1545 if f in man1:
1540 to = getfilectx(f, ctx1).data()
1546 to = getfilectx(f, ctx1).data()
1541 if f not in removed:
1547 if f not in removed:
1542 tn = getfilectx(f, ctx2).data()
1548 tn = getfilectx(f, ctx2).data()
1543 a, b = f, f
1549 a, b = f, f
1544 if opts.git or losedatafn:
1550 if opts.git or losedatafn:
1545 if f in added:
1551 if f in added:
1546 mode = gitmode[ctx2.flags(f)]
1552 mode = gitmode[ctx2.flags(f)]
1547 if f in copy or f in copyto:
1553 if f in copy or f in copyto:
1548 if opts.git:
1554 if opts.git:
1549 if f in copy:
1555 if f in copy:
1550 a = copy[f]
1556 a = copy[f]
1551 else:
1557 else:
1552 a = copyto[f]
1558 a = copyto[f]
1553 omode = gitmode[man1.flags(a)]
1559 omode = gitmode[man1.flags(a)]
1554 _addmodehdr(header, omode, mode)
1560 _addmodehdr(header, omode, mode)
1555 if a in removed and a not in gone:
1561 if a in removed and a not in gone:
1556 op = 'rename'
1562 op = 'rename'
1557 gone.add(a)
1563 gone.add(a)
1558 else:
1564 else:
1559 op = 'copy'
1565 op = 'copy'
1560 header.append('%s from %s\n' % (op, a))
1566 header.append('%s from %s\n' % (op, join(a)))
1561 header.append('%s to %s\n' % (op, f))
1567 header.append('%s to %s\n' % (op, join(f)))
1562 to = getfilectx(a, ctx1).data()
1568 to = getfilectx(a, ctx1).data()
1563 else:
1569 else:
1564 losedatafn(f)
1570 losedatafn(f)
1565 else:
1571 else:
1566 if opts.git:
1572 if opts.git:
1567 header.append('new file mode %s\n' % mode)
1573 header.append('new file mode %s\n' % mode)
1568 elif ctx2.flags(f):
1574 elif ctx2.flags(f):
1569 losedatafn(f)
1575 losedatafn(f)
1570 if util.binary(tn):
1576 if util.binary(tn):
1571 if opts.git:
1577 if opts.git:
1572 dodiff = 'binary'
1578 dodiff = 'binary'
1573 else:
1579 else:
1574 losedatafn(f)
1580 losedatafn(f)
1575 if not opts.git and not tn:
1581 if not opts.git and not tn:
1576 # regular diffs cannot represent new empty file
1582 # regular diffs cannot represent new empty file
1577 losedatafn(f)
1583 losedatafn(f)
1578 elif f in removed:
1584 elif f in removed:
1579 if opts.git:
1585 if opts.git:
1580 # have we already reported a copy above?
1586 # have we already reported a copy above?
1581 if ((f in copy and copy[f] in added
1587 if ((f in copy and copy[f] in added
1582 and copyto[copy[f]] == f) or
1588 and copyto[copy[f]] == f) or
1583 (f in copyto and copyto[f] in added
1589 (f in copyto and copyto[f] in added
1584 and copy[copyto[f]] == f)):
1590 and copy[copyto[f]] == f)):
1585 dodiff = False
1591 dodiff = False
1586 else:
1592 else:
1587 header.append('deleted file mode %s\n' %
1593 header.append('deleted file mode %s\n' %
1588 gitmode[man1.flags(f)])
1594 gitmode[man1.flags(f)])
1589 elif not to:
1595 elif not to:
1590 # regular diffs cannot represent empty file deletion
1596 # regular diffs cannot represent empty file deletion
1591 losedatafn(f)
1597 losedatafn(f)
1592 else:
1598 else:
1593 oflag = man1.flags(f)
1599 oflag = man1.flags(f)
1594 nflag = ctx2.flags(f)
1600 nflag = ctx2.flags(f)
1595 binary = util.binary(to) or util.binary(tn)
1601 binary = util.binary(to) or util.binary(tn)
1596 if opts.git:
1602 if opts.git:
1597 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1603 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1598 if binary:
1604 if binary:
1599 dodiff = 'binary'
1605 dodiff = 'binary'
1600 elif binary or nflag != oflag:
1606 elif binary or nflag != oflag:
1601 losedatafn(f)
1607 losedatafn(f)
1602 if opts.git:
1608 if opts.git:
1603 header.insert(0, mdiff.diffline(revs, a, b, opts))
1609 header.insert(0, mdiff.diffline(revs, join(a), join(b), opts))
1604
1610
1605 if dodiff:
1611 if dodiff:
1606 if dodiff == 'binary':
1612 if dodiff == 'binary':
1607 text = b85diff(to, tn)
1613 text = b85diff(to, tn)
1608 else:
1614 else:
1609 text = mdiff.unidiff(to, date1,
1615 text = mdiff.unidiff(to, date1,
1610 # ctx2 date may be dynamic
1616 # ctx2 date may be dynamic
1611 tn, util.datestr(ctx2.date()),
1617 tn, util.datestr(ctx2.date()),
1612 a, b, revs, opts=opts)
1618 join(a), join(b), revs, opts=opts)
1613 if header and (text or len(header) > 1):
1619 if header and (text or len(header) > 1):
1614 yield ''.join(header)
1620 yield ''.join(header)
1615 if text:
1621 if text:
1616 yield text
1622 yield text
1617
1623
1618 def diffstatdata(lines):
1624 def diffstatdata(lines):
1619 filename, adds, removes = None, 0, 0
1625 filename, adds, removes = None, 0, 0
1620 for line in lines:
1626 for line in lines:
1621 if line.startswith('diff'):
1627 if line.startswith('diff'):
1622 if filename:
1628 if filename:
1623 isbinary = adds == 0 and removes == 0
1629 isbinary = adds == 0 and removes == 0
1624 yield (filename, adds, removes, isbinary)
1630 yield (filename, adds, removes, isbinary)
1625 # set numbers to 0 anyway when starting new file
1631 # set numbers to 0 anyway when starting new file
1626 adds, removes = 0, 0
1632 adds, removes = 0, 0
1627 if line.startswith('diff --git'):
1633 if line.startswith('diff --git'):
1628 filename = gitre.search(line).group(1)
1634 filename = gitre.search(line).group(1)
1629 else:
1635 else:
1630 # format: "diff -r ... -r ... filename"
1636 # format: "diff -r ... -r ... filename"
1631 filename = line.split(None, 5)[-1]
1637 filename = line.split(None, 5)[-1]
1632 elif line.startswith('+') and not line.startswith('+++'):
1638 elif line.startswith('+') and not line.startswith('+++'):
1633 adds += 1
1639 adds += 1
1634 elif line.startswith('-') and not line.startswith('---'):
1640 elif line.startswith('-') and not line.startswith('---'):
1635 removes += 1
1641 removes += 1
1636 if filename:
1642 if filename:
1637 isbinary = adds == 0 and removes == 0
1643 isbinary = adds == 0 and removes == 0
1638 yield (filename, adds, removes, isbinary)
1644 yield (filename, adds, removes, isbinary)
1639
1645
1640 def diffstat(lines, width=80, git=False):
1646 def diffstat(lines, width=80, git=False):
1641 output = []
1647 output = []
1642 stats = list(diffstatdata(lines))
1648 stats = list(diffstatdata(lines))
1643
1649
1644 maxtotal, maxname = 0, 0
1650 maxtotal, maxname = 0, 0
1645 totaladds, totalremoves = 0, 0
1651 totaladds, totalremoves = 0, 0
1646 hasbinary = False
1652 hasbinary = False
1647
1653
1648 sized = [(filename, adds, removes, isbinary, encoding.colwidth(filename))
1654 sized = [(filename, adds, removes, isbinary, encoding.colwidth(filename))
1649 for filename, adds, removes, isbinary in stats]
1655 for filename, adds, removes, isbinary in stats]
1650
1656
1651 for filename, adds, removes, isbinary, namewidth in sized:
1657 for filename, adds, removes, isbinary, namewidth in sized:
1652 totaladds += adds
1658 totaladds += adds
1653 totalremoves += removes
1659 totalremoves += removes
1654 maxname = max(maxname, namewidth)
1660 maxname = max(maxname, namewidth)
1655 maxtotal = max(maxtotal, adds + removes)
1661 maxtotal = max(maxtotal, adds + removes)
1656 if isbinary:
1662 if isbinary:
1657 hasbinary = True
1663 hasbinary = True
1658
1664
1659 countwidth = len(str(maxtotal))
1665 countwidth = len(str(maxtotal))
1660 if hasbinary and countwidth < 3:
1666 if hasbinary and countwidth < 3:
1661 countwidth = 3
1667 countwidth = 3
1662 graphwidth = width - countwidth - maxname - 6
1668 graphwidth = width - countwidth - maxname - 6
1663 if graphwidth < 10:
1669 if graphwidth < 10:
1664 graphwidth = 10
1670 graphwidth = 10
1665
1671
1666 def scale(i):
1672 def scale(i):
1667 if maxtotal <= graphwidth:
1673 if maxtotal <= graphwidth:
1668 return i
1674 return i
1669 # If diffstat runs out of room it doesn't print anything,
1675 # If diffstat runs out of room it doesn't print anything,
1670 # which isn't very useful, so always print at least one + or -
1676 # which isn't very useful, so always print at least one + or -
1671 # if there were at least some changes.
1677 # if there were at least some changes.
1672 return max(i * graphwidth // maxtotal, int(bool(i)))
1678 return max(i * graphwidth // maxtotal, int(bool(i)))
1673
1679
1674 for filename, adds, removes, isbinary, namewidth in sized:
1680 for filename, adds, removes, isbinary, namewidth in sized:
1675 if git and isbinary:
1681 if git and isbinary:
1676 count = 'Bin'
1682 count = 'Bin'
1677 else:
1683 else:
1678 count = adds + removes
1684 count = adds + removes
1679 pluses = '+' * scale(adds)
1685 pluses = '+' * scale(adds)
1680 minuses = '-' * scale(removes)
1686 minuses = '-' * scale(removes)
1681 output.append(' %s%s | %*s %s%s\n' %
1687 output.append(' %s%s | %*s %s%s\n' %
1682 (filename, ' ' * (maxname - namewidth),
1688 (filename, ' ' * (maxname - namewidth),
1683 countwidth, count,
1689 countwidth, count,
1684 pluses, minuses))
1690 pluses, minuses))
1685
1691
1686 if stats:
1692 if stats:
1687 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1693 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1688 % (len(stats), totaladds, totalremoves))
1694 % (len(stats), totaladds, totalremoves))
1689
1695
1690 return ''.join(output)
1696 return ''.join(output)
1691
1697
1692 def diffstatui(*args, **kw):
1698 def diffstatui(*args, **kw):
1693 '''like diffstat(), but yields 2-tuples of (output, label) for
1699 '''like diffstat(), but yields 2-tuples of (output, label) for
1694 ui.write()
1700 ui.write()
1695 '''
1701 '''
1696
1702
1697 for line in diffstat(*args, **kw).splitlines():
1703 for line in diffstat(*args, **kw).splitlines():
1698 if line and line[-1] in '+-':
1704 if line and line[-1] in '+-':
1699 name, graph = line.rsplit(' ', 1)
1705 name, graph = line.rsplit(' ', 1)
1700 yield (name + ' ', '')
1706 yield (name + ' ', '')
1701 m = re.search(r'\++', graph)
1707 m = re.search(r'\++', graph)
1702 if m:
1708 if m:
1703 yield (m.group(0), 'diffstat.inserted')
1709 yield (m.group(0), 'diffstat.inserted')
1704 m = re.search(r'-+', graph)
1710 m = re.search(r'-+', graph)
1705 if m:
1711 if m:
1706 yield (m.group(0), 'diffstat.deleted')
1712 yield (m.group(0), 'diffstat.deleted')
1707 else:
1713 else:
1708 yield (line, '')
1714 yield (line, '')
1709 yield ('\n', '')
1715 yield ('\n', '')
@@ -1,468 +1,482 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 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 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
8 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
9 from i18n import _
9 from i18n import _
10 import config, util, node, error
10 import config, util, node, error, cmdutil
11 hg = None
11 hg = None
12
12
13 nullstate = ('', '', 'empty')
13 nullstate = ('', '', 'empty')
14
14
15 def state(ctx, ui):
15 def state(ctx, ui):
16 """return a state dict, mapping subrepo paths configured in .hgsub
16 """return a state dict, mapping subrepo paths configured in .hgsub
17 to tuple: (source from .hgsub, revision from .hgsubstate, kind
17 to tuple: (source from .hgsub, revision from .hgsubstate, kind
18 (key in types dict))
18 (key in types dict))
19 """
19 """
20 p = config.config()
20 p = config.config()
21 def read(f, sections=None, remap=None):
21 def read(f, sections=None, remap=None):
22 if f in ctx:
22 if f in ctx:
23 p.parse(f, ctx[f].data(), sections, remap, read)
23 p.parse(f, ctx[f].data(), sections, remap, read)
24 else:
24 else:
25 raise util.Abort(_("subrepo spec file %s not found") % f)
25 raise util.Abort(_("subrepo spec file %s not found") % f)
26
26
27 if '.hgsub' in ctx:
27 if '.hgsub' in ctx:
28 read('.hgsub')
28 read('.hgsub')
29
29
30 for path, src in ui.configitems('subpaths'):
30 for path, src in ui.configitems('subpaths'):
31 p.set('subpaths', path, src, ui.configsource('subpaths', path))
31 p.set('subpaths', path, src, ui.configsource('subpaths', path))
32
32
33 rev = {}
33 rev = {}
34 if '.hgsubstate' in ctx:
34 if '.hgsubstate' in ctx:
35 try:
35 try:
36 for l in ctx['.hgsubstate'].data().splitlines():
36 for l in ctx['.hgsubstate'].data().splitlines():
37 revision, path = l.split(" ", 1)
37 revision, path = l.split(" ", 1)
38 rev[path] = revision
38 rev[path] = revision
39 except IOError, err:
39 except IOError, err:
40 if err.errno != errno.ENOENT:
40 if err.errno != errno.ENOENT:
41 raise
41 raise
42
42
43 state = {}
43 state = {}
44 for path, src in p[''].items():
44 for path, src in p[''].items():
45 kind = 'hg'
45 kind = 'hg'
46 if src.startswith('['):
46 if src.startswith('['):
47 if ']' not in src:
47 if ']' not in src:
48 raise util.Abort(_('missing ] in subrepo source'))
48 raise util.Abort(_('missing ] in subrepo source'))
49 kind, src = src.split(']', 1)
49 kind, src = src.split(']', 1)
50 kind = kind[1:]
50 kind = kind[1:]
51
51
52 for pattern, repl in p.items('subpaths'):
52 for pattern, repl in p.items('subpaths'):
53 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
53 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
54 # does a string decode.
54 # does a string decode.
55 repl = repl.encode('string-escape')
55 repl = repl.encode('string-escape')
56 # However, we still want to allow back references to go
56 # However, we still want to allow back references to go
57 # through unharmed, so we turn r'\\1' into r'\1'. Again,
57 # through unharmed, so we turn r'\\1' into r'\1'. Again,
58 # extra escapes are needed because re.sub string decodes.
58 # extra escapes are needed because re.sub string decodes.
59 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
59 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
60 try:
60 try:
61 src = re.sub(pattern, repl, src, 1)
61 src = re.sub(pattern, repl, src, 1)
62 except re.error, e:
62 except re.error, e:
63 raise util.Abort(_("bad subrepository pattern in %s: %s")
63 raise util.Abort(_("bad subrepository pattern in %s: %s")
64 % (p.source('subpaths', pattern), e))
64 % (p.source('subpaths', pattern), e))
65
65
66 state[path] = (src.strip(), rev.get(path, ''), kind)
66 state[path] = (src.strip(), rev.get(path, ''), kind)
67
67
68 return state
68 return state
69
69
70 def writestate(repo, state):
70 def writestate(repo, state):
71 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
71 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
72 repo.wwrite('.hgsubstate',
72 repo.wwrite('.hgsubstate',
73 ''.join(['%s %s\n' % (state[s][1], s)
73 ''.join(['%s %s\n' % (state[s][1], s)
74 for s in sorted(state)]), '')
74 for s in sorted(state)]), '')
75
75
76 def submerge(repo, wctx, mctx, actx):
76 def submerge(repo, wctx, mctx, actx):
77 """delegated from merge.applyupdates: merging of .hgsubstate file
77 """delegated from merge.applyupdates: merging of .hgsubstate file
78 in working context, merging context and ancestor context"""
78 in working context, merging context and ancestor context"""
79 if mctx == actx: # backwards?
79 if mctx == actx: # backwards?
80 actx = wctx.p1()
80 actx = wctx.p1()
81 s1 = wctx.substate
81 s1 = wctx.substate
82 s2 = mctx.substate
82 s2 = mctx.substate
83 sa = actx.substate
83 sa = actx.substate
84 sm = {}
84 sm = {}
85
85
86 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
86 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
87
87
88 def debug(s, msg, r=""):
88 def debug(s, msg, r=""):
89 if r:
89 if r:
90 r = "%s:%s:%s" % r
90 r = "%s:%s:%s" % r
91 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
91 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
92
92
93 for s, l in s1.items():
93 for s, l in s1.items():
94 a = sa.get(s, nullstate)
94 a = sa.get(s, nullstate)
95 ld = l # local state with possible dirty flag for compares
95 ld = l # local state with possible dirty flag for compares
96 if wctx.sub(s).dirty():
96 if wctx.sub(s).dirty():
97 ld = (l[0], l[1] + "+")
97 ld = (l[0], l[1] + "+")
98 if wctx == actx: # overwrite
98 if wctx == actx: # overwrite
99 a = ld
99 a = ld
100
100
101 if s in s2:
101 if s in s2:
102 r = s2[s]
102 r = s2[s]
103 if ld == r or r == a: # no change or local is newer
103 if ld == r or r == a: # no change or local is newer
104 sm[s] = l
104 sm[s] = l
105 continue
105 continue
106 elif ld == a: # other side changed
106 elif ld == a: # other side changed
107 debug(s, "other changed, get", r)
107 debug(s, "other changed, get", r)
108 wctx.sub(s).get(r)
108 wctx.sub(s).get(r)
109 sm[s] = r
109 sm[s] = r
110 elif ld[0] != r[0]: # sources differ
110 elif ld[0] != r[0]: # sources differ
111 if repo.ui.promptchoice(
111 if repo.ui.promptchoice(
112 _(' subrepository sources for %s differ\n'
112 _(' subrepository sources for %s differ\n'
113 'use (l)ocal source (%s) or (r)emote source (%s)?')
113 'use (l)ocal source (%s) or (r)emote source (%s)?')
114 % (s, l[0], r[0]),
114 % (s, l[0], r[0]),
115 (_('&Local'), _('&Remote')), 0):
115 (_('&Local'), _('&Remote')), 0):
116 debug(s, "prompt changed, get", r)
116 debug(s, "prompt changed, get", r)
117 wctx.sub(s).get(r)
117 wctx.sub(s).get(r)
118 sm[s] = r
118 sm[s] = r
119 elif ld[1] == a[1]: # local side is unchanged
119 elif ld[1] == a[1]: # local side is unchanged
120 debug(s, "other side changed, get", r)
120 debug(s, "other side changed, get", r)
121 wctx.sub(s).get(r)
121 wctx.sub(s).get(r)
122 sm[s] = r
122 sm[s] = r
123 else:
123 else:
124 debug(s, "both sides changed, merge with", r)
124 debug(s, "both sides changed, merge with", r)
125 wctx.sub(s).merge(r)
125 wctx.sub(s).merge(r)
126 sm[s] = l
126 sm[s] = l
127 elif ld == a: # remote removed, local unchanged
127 elif ld == a: # remote removed, local unchanged
128 debug(s, "remote removed, remove")
128 debug(s, "remote removed, remove")
129 wctx.sub(s).remove()
129 wctx.sub(s).remove()
130 else:
130 else:
131 if repo.ui.promptchoice(
131 if repo.ui.promptchoice(
132 _(' local changed subrepository %s which remote removed\n'
132 _(' local changed subrepository %s which remote removed\n'
133 'use (c)hanged version or (d)elete?') % s,
133 'use (c)hanged version or (d)elete?') % s,
134 (_('&Changed'), _('&Delete')), 0):
134 (_('&Changed'), _('&Delete')), 0):
135 debug(s, "prompt remove")
135 debug(s, "prompt remove")
136 wctx.sub(s).remove()
136 wctx.sub(s).remove()
137
137
138 for s, r in s2.items():
138 for s, r in s2.items():
139 if s in s1:
139 if s in s1:
140 continue
140 continue
141 elif s not in sa:
141 elif s not in sa:
142 debug(s, "remote added, get", r)
142 debug(s, "remote added, get", r)
143 mctx.sub(s).get(r)
143 mctx.sub(s).get(r)
144 sm[s] = r
144 sm[s] = r
145 elif r != sa[s]:
145 elif r != sa[s]:
146 if repo.ui.promptchoice(
146 if repo.ui.promptchoice(
147 _(' remote changed subrepository %s which local removed\n'
147 _(' remote changed subrepository %s which local removed\n'
148 'use (c)hanged version or (d)elete?') % s,
148 'use (c)hanged version or (d)elete?') % s,
149 (_('&Changed'), _('&Delete')), 0) == 0:
149 (_('&Changed'), _('&Delete')), 0) == 0:
150 debug(s, "prompt recreate", r)
150 debug(s, "prompt recreate", r)
151 wctx.sub(s).get(r)
151 wctx.sub(s).get(r)
152 sm[s] = r
152 sm[s] = r
153
153
154 # record merged .hgsubstate
154 # record merged .hgsubstate
155 writestate(repo, sm)
155 writestate(repo, sm)
156
156
157 def relpath(sub):
157 def relpath(sub):
158 """return path to this subrepo as seen from outermost repo"""
158 """return path to this subrepo as seen from outermost repo"""
159 if not hasattr(sub, '_repo'):
159 if not hasattr(sub, '_repo'):
160 return sub._path
160 return sub._path
161 parent = sub._repo
161 parent = sub._repo
162 while hasattr(parent, '_subparent'):
162 while hasattr(parent, '_subparent'):
163 parent = parent._subparent
163 parent = parent._subparent
164 return sub._repo.root[len(parent.root)+1:]
164 return sub._repo.root[len(parent.root)+1:]
165
165
166 def _abssource(repo, push=False):
166 def _abssource(repo, push=False):
167 """return pull/push path of repo - either based on parent repo
167 """return pull/push path of repo - either based on parent repo
168 .hgsub info or on the subrepos own config"""
168 .hgsub info or on the subrepos own config"""
169 if hasattr(repo, '_subparent'):
169 if hasattr(repo, '_subparent'):
170 source = repo._subsource
170 source = repo._subsource
171 if source.startswith('/') or '://' in source:
171 if source.startswith('/') or '://' in source:
172 return source
172 return source
173 parent = _abssource(repo._subparent, push)
173 parent = _abssource(repo._subparent, push)
174 if '://' in parent:
174 if '://' in parent:
175 if parent[-1] == '/':
175 if parent[-1] == '/':
176 parent = parent[:-1]
176 parent = parent[:-1]
177 r = urlparse.urlparse(parent + '/' + source)
177 r = urlparse.urlparse(parent + '/' + source)
178 r = urlparse.urlunparse((r[0], r[1],
178 r = urlparse.urlunparse((r[0], r[1],
179 posixpath.normpath(r[2]),
179 posixpath.normpath(r[2]),
180 r[3], r[4], r[5]))
180 r[3], r[4], r[5]))
181 return r
181 return r
182 return posixpath.normpath(os.path.join(parent, repo._subsource))
182 return posixpath.normpath(os.path.join(parent, repo._subsource))
183 if push and repo.ui.config('paths', 'default-push'):
183 if push and repo.ui.config('paths', 'default-push'):
184 return repo.ui.config('paths', 'default-push', repo.root)
184 return repo.ui.config('paths', 'default-push', repo.root)
185 return repo.ui.config('paths', 'default', repo.root)
185 return repo.ui.config('paths', 'default', repo.root)
186
186
187 def subrepo(ctx, path):
187 def subrepo(ctx, path):
188 """return instance of the right subrepo class for subrepo in path"""
188 """return instance of the right subrepo class for subrepo in path"""
189 # subrepo inherently violates our import layering rules
189 # subrepo inherently violates our import layering rules
190 # because it wants to make repo objects from deep inside the stack
190 # because it wants to make repo objects from deep inside the stack
191 # so we manually delay the circular imports to not break
191 # so we manually delay the circular imports to not break
192 # scripts that don't use our demand-loading
192 # scripts that don't use our demand-loading
193 global hg
193 global hg
194 import hg as h
194 import hg as h
195 hg = h
195 hg = h
196
196
197 util.path_auditor(ctx._repo.root)(path)
197 util.path_auditor(ctx._repo.root)(path)
198 state = ctx.substate.get(path, nullstate)
198 state = ctx.substate.get(path, nullstate)
199 if state[2] not in types:
199 if state[2] not in types:
200 raise util.Abort(_('unknown subrepo type %s') % state[2])
200 raise util.Abort(_('unknown subrepo type %s') % state[2])
201 return types[state[2]](ctx, path, state[:2])
201 return types[state[2]](ctx, path, state[:2])
202
202
203 # subrepo classes need to implement the following abstract class:
203 # subrepo classes need to implement the following abstract class:
204
204
205 class abstractsubrepo(object):
205 class abstractsubrepo(object):
206
206
207 def dirty(self):
207 def dirty(self):
208 """returns true if the dirstate of the subrepo does not match
208 """returns true if the dirstate of the subrepo does not match
209 current stored state
209 current stored state
210 """
210 """
211 raise NotImplementedError
211 raise NotImplementedError
212
212
213 def checknested(path):
213 def checknested(path):
214 """check if path is a subrepository within this repository"""
214 """check if path is a subrepository within this repository"""
215 return False
215 return False
216
216
217 def commit(self, text, user, date):
217 def commit(self, text, user, date):
218 """commit the current changes to the subrepo with the given
218 """commit the current changes to the subrepo with the given
219 log message. Use given user and date if possible. Return the
219 log message. Use given user and date if possible. Return the
220 new state of the subrepo.
220 new state of the subrepo.
221 """
221 """
222 raise NotImplementedError
222 raise NotImplementedError
223
223
224 def remove(self):
224 def remove(self):
225 """remove the subrepo
225 """remove the subrepo
226
226
227 (should verify the dirstate is not dirty first)
227 (should verify the dirstate is not dirty first)
228 """
228 """
229 raise NotImplementedError
229 raise NotImplementedError
230
230
231 def get(self, state):
231 def get(self, state):
232 """run whatever commands are needed to put the subrepo into
232 """run whatever commands are needed to put the subrepo into
233 this state
233 this state
234 """
234 """
235 raise NotImplementedError
235 raise NotImplementedError
236
236
237 def merge(self, state):
237 def merge(self, state):
238 """merge currently-saved state with the new state."""
238 """merge currently-saved state with the new state."""
239 raise NotImplementedError
239 raise NotImplementedError
240
240
241 def push(self, force):
241 def push(self, force):
242 """perform whatever action is analogous to 'hg push'
242 """perform whatever action is analogous to 'hg push'
243
243
244 This may be a no-op on some systems.
244 This may be a no-op on some systems.
245 """
245 """
246 raise NotImplementedError
246 raise NotImplementedError
247
247
248
248
249 def status(self, rev2, **opts):
249 def status(self, rev2, **opts):
250 return [], [], [], [], [], [], []
250 return [], [], [], [], [], [], []
251
251
252 def diff(self, diffopts, node2, match, prefix, **opts):
253 pass
254
252 class hgsubrepo(abstractsubrepo):
255 class hgsubrepo(abstractsubrepo):
253 def __init__(self, ctx, path, state):
256 def __init__(self, ctx, path, state):
254 self._path = path
257 self._path = path
255 self._state = state
258 self._state = state
256 r = ctx._repo
259 r = ctx._repo
257 root = r.wjoin(path)
260 root = r.wjoin(path)
258 create = False
261 create = False
259 if not os.path.exists(os.path.join(root, '.hg')):
262 if not os.path.exists(os.path.join(root, '.hg')):
260 create = True
263 create = True
261 util.makedirs(root)
264 util.makedirs(root)
262 self._repo = hg.repository(r.ui, root, create=create)
265 self._repo = hg.repository(r.ui, root, create=create)
263 self._repo._subparent = r
266 self._repo._subparent = r
264 self._repo._subsource = state[0]
267 self._repo._subsource = state[0]
265
268
266 if create:
269 if create:
267 fp = self._repo.opener("hgrc", "w", text=True)
270 fp = self._repo.opener("hgrc", "w", text=True)
268 fp.write('[paths]\n')
271 fp.write('[paths]\n')
269
272
270 def addpathconfig(key, value):
273 def addpathconfig(key, value):
271 fp.write('%s = %s\n' % (key, value))
274 fp.write('%s = %s\n' % (key, value))
272 self._repo.ui.setconfig('paths', key, value)
275 self._repo.ui.setconfig('paths', key, value)
273
276
274 defpath = _abssource(self._repo)
277 defpath = _abssource(self._repo)
275 defpushpath = _abssource(self._repo, True)
278 defpushpath = _abssource(self._repo, True)
276 addpathconfig('default', defpath)
279 addpathconfig('default', defpath)
277 if defpath != defpushpath:
280 if defpath != defpushpath:
278 addpathconfig('default-push', defpushpath)
281 addpathconfig('default-push', defpushpath)
279 fp.close()
282 fp.close()
280
283
281 def status(self, rev2, **opts):
284 def status(self, rev2, **opts):
282 try:
285 try:
283 rev1 = self._state[1]
286 rev1 = self._state[1]
284 ctx1 = self._repo[rev1]
287 ctx1 = self._repo[rev1]
285 ctx2 = self._repo[rev2]
288 ctx2 = self._repo[rev2]
286 return self._repo.status(ctx1, ctx2, **opts)
289 return self._repo.status(ctx1, ctx2, **opts)
287 except error.RepoLookupError, inst:
290 except error.RepoLookupError, inst:
288 self._repo.ui.warn(_("warning: %s in %s\n")
291 self._repo.ui.warn(_("warning: %s in %s\n")
289 % (inst, relpath(self)))
292 % (inst, relpath(self)))
290 return [], [], [], [], [], [], []
293 return [], [], [], [], [], [], []
291
294
295 def diff(self, diffopts, node2, match, prefix, **opts):
296 try:
297 node1 = node.bin(self._state[1])
298 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
299 node1, node2, match,
300 prefix=os.path.join(prefix, self._path),
301 listsubrepos=True, **opts)
302 except error.RepoLookupError, inst:
303 self._repo.ui.warn(_("warning: %s in %s\n")
304 % (inst, relpath(self)))
305
292 def dirty(self):
306 def dirty(self):
293 r = self._state[1]
307 r = self._state[1]
294 if r == '':
308 if r == '':
295 return True
309 return True
296 w = self._repo[None]
310 w = self._repo[None]
297 if w.p1() != self._repo[r]: # version checked out change
311 if w.p1() != self._repo[r]: # version checked out change
298 return True
312 return True
299 return w.dirty() # working directory changed
313 return w.dirty() # working directory changed
300
314
301 def checknested(self, path):
315 def checknested(self, path):
302 return self._repo._checknested(self._repo.wjoin(path))
316 return self._repo._checknested(self._repo.wjoin(path))
303
317
304 def commit(self, text, user, date):
318 def commit(self, text, user, date):
305 self._repo.ui.debug("committing subrepo %s\n" % relpath(self))
319 self._repo.ui.debug("committing subrepo %s\n" % relpath(self))
306 n = self._repo.commit(text, user, date)
320 n = self._repo.commit(text, user, date)
307 if not n:
321 if not n:
308 return self._repo['.'].hex() # different version checked out
322 return self._repo['.'].hex() # different version checked out
309 return node.hex(n)
323 return node.hex(n)
310
324
311 def remove(self):
325 def remove(self):
312 # we can't fully delete the repository as it may contain
326 # we can't fully delete the repository as it may contain
313 # local-only history
327 # local-only history
314 self._repo.ui.note(_('removing subrepo %s\n') % relpath(self))
328 self._repo.ui.note(_('removing subrepo %s\n') % relpath(self))
315 hg.clean(self._repo, node.nullid, False)
329 hg.clean(self._repo, node.nullid, False)
316
330
317 def _get(self, state):
331 def _get(self, state):
318 source, revision, kind = state
332 source, revision, kind = state
319 try:
333 try:
320 self._repo.lookup(revision)
334 self._repo.lookup(revision)
321 except error.RepoError:
335 except error.RepoError:
322 self._repo._subsource = source
336 self._repo._subsource = source
323 srcurl = _abssource(self._repo)
337 srcurl = _abssource(self._repo)
324 self._repo.ui.status(_('pulling subrepo %s from %s\n')
338 self._repo.ui.status(_('pulling subrepo %s from %s\n')
325 % (relpath(self), srcurl))
339 % (relpath(self), srcurl))
326 other = hg.repository(self._repo.ui, srcurl)
340 other = hg.repository(self._repo.ui, srcurl)
327 self._repo.pull(other)
341 self._repo.pull(other)
328
342
329 def get(self, state):
343 def get(self, state):
330 self._get(state)
344 self._get(state)
331 source, revision, kind = state
345 source, revision, kind = state
332 self._repo.ui.debug("getting subrepo %s\n" % self._path)
346 self._repo.ui.debug("getting subrepo %s\n" % self._path)
333 hg.clean(self._repo, revision, False)
347 hg.clean(self._repo, revision, False)
334
348
335 def merge(self, state):
349 def merge(self, state):
336 self._get(state)
350 self._get(state)
337 cur = self._repo['.']
351 cur = self._repo['.']
338 dst = self._repo[state[1]]
352 dst = self._repo[state[1]]
339 anc = dst.ancestor(cur)
353 anc = dst.ancestor(cur)
340 if anc == cur:
354 if anc == cur:
341 self._repo.ui.debug("updating subrepo %s\n" % relpath(self))
355 self._repo.ui.debug("updating subrepo %s\n" % relpath(self))
342 hg.update(self._repo, state[1])
356 hg.update(self._repo, state[1])
343 elif anc == dst:
357 elif anc == dst:
344 self._repo.ui.debug("skipping subrepo %s\n" % relpath(self))
358 self._repo.ui.debug("skipping subrepo %s\n" % relpath(self))
345 else:
359 else:
346 self._repo.ui.debug("merging subrepo %s\n" % relpath(self))
360 self._repo.ui.debug("merging subrepo %s\n" % relpath(self))
347 hg.merge(self._repo, state[1], remind=False)
361 hg.merge(self._repo, state[1], remind=False)
348
362
349 def push(self, force):
363 def push(self, force):
350 # push subrepos depth-first for coherent ordering
364 # push subrepos depth-first for coherent ordering
351 c = self._repo['']
365 c = self._repo['']
352 subs = c.substate # only repos that are committed
366 subs = c.substate # only repos that are committed
353 for s in sorted(subs):
367 for s in sorted(subs):
354 if not c.sub(s).push(force):
368 if not c.sub(s).push(force):
355 return False
369 return False
356
370
357 dsturl = _abssource(self._repo, True)
371 dsturl = _abssource(self._repo, True)
358 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
372 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
359 (relpath(self), dsturl))
373 (relpath(self), dsturl))
360 other = hg.repository(self._repo.ui, dsturl)
374 other = hg.repository(self._repo.ui, dsturl)
361 return self._repo.push(other, force)
375 return self._repo.push(other, force)
362
376
363 class svnsubrepo(abstractsubrepo):
377 class svnsubrepo(abstractsubrepo):
364 def __init__(self, ctx, path, state):
378 def __init__(self, ctx, path, state):
365 self._path = path
379 self._path = path
366 self._state = state
380 self._state = state
367 self._ctx = ctx
381 self._ctx = ctx
368 self._ui = ctx._repo.ui
382 self._ui = ctx._repo.ui
369
383
370 def _svncommand(self, commands, filename=''):
384 def _svncommand(self, commands, filename=''):
371 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
385 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
372 cmd = ['svn'] + commands + [path]
386 cmd = ['svn'] + commands + [path]
373 cmd = [util.shellquote(arg) for arg in cmd]
387 cmd = [util.shellquote(arg) for arg in cmd]
374 cmd = util.quotecommand(' '.join(cmd))
388 cmd = util.quotecommand(' '.join(cmd))
375 env = dict(os.environ)
389 env = dict(os.environ)
376 # Avoid localized output, preserve current locale for everything else.
390 # Avoid localized output, preserve current locale for everything else.
377 env['LC_MESSAGES'] = 'C'
391 env['LC_MESSAGES'] = 'C'
378 write, read, err = util.popen3(cmd, env=env, newlines=True)
392 write, read, err = util.popen3(cmd, env=env, newlines=True)
379 retdata = read.read()
393 retdata = read.read()
380 err = err.read().strip()
394 err = err.read().strip()
381 if err:
395 if err:
382 raise util.Abort(err)
396 raise util.Abort(err)
383 return retdata
397 return retdata
384
398
385 def _wcrev(self):
399 def _wcrev(self):
386 output = self._svncommand(['info', '--xml'])
400 output = self._svncommand(['info', '--xml'])
387 doc = xml.dom.minidom.parseString(output)
401 doc = xml.dom.minidom.parseString(output)
388 entries = doc.getElementsByTagName('entry')
402 entries = doc.getElementsByTagName('entry')
389 if not entries:
403 if not entries:
390 return 0
404 return 0
391 return int(entries[0].getAttribute('revision') or 0)
405 return int(entries[0].getAttribute('revision') or 0)
392
406
393 def _wcchanged(self):
407 def _wcchanged(self):
394 """Return (changes, extchanges) where changes is True
408 """Return (changes, extchanges) where changes is True
395 if the working directory was changed, and extchanges is
409 if the working directory was changed, and extchanges is
396 True if any of these changes concern an external entry.
410 True if any of these changes concern an external entry.
397 """
411 """
398 output = self._svncommand(['status', '--xml'])
412 output = self._svncommand(['status', '--xml'])
399 externals, changes = [], []
413 externals, changes = [], []
400 doc = xml.dom.minidom.parseString(output)
414 doc = xml.dom.minidom.parseString(output)
401 for e in doc.getElementsByTagName('entry'):
415 for e in doc.getElementsByTagName('entry'):
402 s = e.getElementsByTagName('wc-status')
416 s = e.getElementsByTagName('wc-status')
403 if not s:
417 if not s:
404 continue
418 continue
405 item = s[0].getAttribute('item')
419 item = s[0].getAttribute('item')
406 props = s[0].getAttribute('props')
420 props = s[0].getAttribute('props')
407 path = e.getAttribute('path')
421 path = e.getAttribute('path')
408 if item == 'external':
422 if item == 'external':
409 externals.append(path)
423 externals.append(path)
410 if (item not in ('', 'normal', 'unversioned', 'external')
424 if (item not in ('', 'normal', 'unversioned', 'external')
411 or props not in ('', 'none')):
425 or props not in ('', 'none')):
412 changes.append(path)
426 changes.append(path)
413 for path in changes:
427 for path in changes:
414 for ext in externals:
428 for ext in externals:
415 if path == ext or path.startswith(ext + os.sep):
429 if path == ext or path.startswith(ext + os.sep):
416 return True, True
430 return True, True
417 return bool(changes), False
431 return bool(changes), False
418
432
419 def dirty(self):
433 def dirty(self):
420 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
434 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
421 return False
435 return False
422 return True
436 return True
423
437
424 def commit(self, text, user, date):
438 def commit(self, text, user, date):
425 # user and date are out of our hands since svn is centralized
439 # user and date are out of our hands since svn is centralized
426 changed, extchanged = self._wcchanged()
440 changed, extchanged = self._wcchanged()
427 if not changed:
441 if not changed:
428 return self._wcrev()
442 return self._wcrev()
429 if extchanged:
443 if extchanged:
430 # Do not try to commit externals
444 # Do not try to commit externals
431 raise util.Abort(_('cannot commit svn externals'))
445 raise util.Abort(_('cannot commit svn externals'))
432 commitinfo = self._svncommand(['commit', '-m', text])
446 commitinfo = self._svncommand(['commit', '-m', text])
433 self._ui.status(commitinfo)
447 self._ui.status(commitinfo)
434 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
448 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
435 if not newrev:
449 if not newrev:
436 raise util.Abort(commitinfo.splitlines()[-1])
450 raise util.Abort(commitinfo.splitlines()[-1])
437 newrev = newrev.groups()[0]
451 newrev = newrev.groups()[0]
438 self._ui.status(self._svncommand(['update', '-r', newrev]))
452 self._ui.status(self._svncommand(['update', '-r', newrev]))
439 return newrev
453 return newrev
440
454
441 def remove(self):
455 def remove(self):
442 if self.dirty():
456 if self.dirty():
443 self._ui.warn(_('not removing repo %s because '
457 self._ui.warn(_('not removing repo %s because '
444 'it has changes.\n' % self._path))
458 'it has changes.\n' % self._path))
445 return
459 return
446 self._ui.note(_('removing subrepo %s\n') % self._path)
460 self._ui.note(_('removing subrepo %s\n') % self._path)
447 shutil.rmtree(self._ctx.repo.join(self._path))
461 shutil.rmtree(self._ctx.repo.join(self._path))
448
462
449 def get(self, state):
463 def get(self, state):
450 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
464 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
451 if not re.search('Checked out revision [0-9]+.', status):
465 if not re.search('Checked out revision [0-9]+.', status):
452 raise util.Abort(status.splitlines()[-1])
466 raise util.Abort(status.splitlines()[-1])
453 self._ui.status(status)
467 self._ui.status(status)
454
468
455 def merge(self, state):
469 def merge(self, state):
456 old = int(self._state[1])
470 old = int(self._state[1])
457 new = int(state[1])
471 new = int(state[1])
458 if new > old:
472 if new > old:
459 self.get(state)
473 self.get(state)
460
474
461 def push(self, force):
475 def push(self, force):
462 # push is a no-op for SVN
476 # push is a no-op for SVN
463 return True
477 return True
464
478
465 types = {
479 types = {
466 'hg': hgsubrepo,
480 'hg': hgsubrepo,
467 'svn': svnsubrepo,
481 'svn': svnsubrepo,
468 }
482 }
@@ -1,250 +1,250 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 branch
9 branch
10 branches
10 branches
11 bundle
11 bundle
12 cat
12 cat
13 clone
13 clone
14 commit
14 commit
15 copy
15 copy
16 diff
16 diff
17 export
17 export
18 forget
18 forget
19 grep
19 grep
20 heads
20 heads
21 help
21 help
22 identify
22 identify
23 import
23 import
24 incoming
24 incoming
25 init
25 init
26 locate
26 locate
27 log
27 log
28 manifest
28 manifest
29 merge
29 merge
30 outgoing
30 outgoing
31 parents
31 parents
32 paths
32 paths
33 pull
33 pull
34 push
34 push
35 recover
35 recover
36 remove
36 remove
37 rename
37 rename
38 resolve
38 resolve
39 revert
39 revert
40 rollback
40 rollback
41 root
41 root
42 serve
42 serve
43 showconfig
43 showconfig
44 status
44 status
45 summary
45 summary
46 tag
46 tag
47 tags
47 tags
48 tip
48 tip
49 unbundle
49 unbundle
50 update
50 update
51 verify
51 verify
52 version
52 version
53
53
54 Show all commands that start with "a"
54 Show all commands that start with "a"
55 $ hg debugcomplete a
55 $ hg debugcomplete a
56 add
56 add
57 addremove
57 addremove
58 annotate
58 annotate
59 archive
59 archive
60
60
61 Do not show debug commands if there are other candidates
61 Do not show debug commands if there are other candidates
62 $ hg debugcomplete d
62 $ hg debugcomplete d
63 diff
63 diff
64
64
65 Show debug commands if there are no other candidates
65 Show debug commands if there are no other candidates
66 $ hg debugcomplete debug
66 $ hg debugcomplete debug
67 debugancestor
67 debugancestor
68 debugbuilddag
68 debugbuilddag
69 debugcheckstate
69 debugcheckstate
70 debugcommands
70 debugcommands
71 debugcomplete
71 debugcomplete
72 debugconfig
72 debugconfig
73 debugdag
73 debugdag
74 debugdata
74 debugdata
75 debugdate
75 debugdate
76 debugfsinfo
76 debugfsinfo
77 debugindex
77 debugindex
78 debugindexdot
78 debugindexdot
79 debuginstall
79 debuginstall
80 debugpushkey
80 debugpushkey
81 debugrebuildstate
81 debugrebuildstate
82 debugrename
82 debugrename
83 debugrevspec
83 debugrevspec
84 debugsetparents
84 debugsetparents
85 debugstate
85 debugstate
86 debugsub
86 debugsub
87 debugwalk
87 debugwalk
88
88
89 Do not show the alias of a debug command if there are other candidates
89 Do not show the alias of a debug command if there are other candidates
90 (this should hide rawcommit)
90 (this should hide rawcommit)
91 $ hg debugcomplete r
91 $ hg debugcomplete r
92 recover
92 recover
93 remove
93 remove
94 rename
94 rename
95 resolve
95 resolve
96 revert
96 revert
97 rollback
97 rollback
98 root
98 root
99 Show the alias of a debug command if there are no other candidates
99 Show the alias of a debug command if there are no other candidates
100 $ hg debugcomplete rawc
100 $ hg debugcomplete rawc
101
101
102
102
103 Show the global options
103 Show the global options
104 $ hg debugcomplete --options | sort
104 $ hg debugcomplete --options | sort
105 --config
105 --config
106 --cwd
106 --cwd
107 --debug
107 --debug
108 --debugger
108 --debugger
109 --encoding
109 --encoding
110 --encodingmode
110 --encodingmode
111 --help
111 --help
112 --noninteractive
112 --noninteractive
113 --profile
113 --profile
114 --quiet
114 --quiet
115 --repository
115 --repository
116 --time
116 --time
117 --traceback
117 --traceback
118 --verbose
118 --verbose
119 --version
119 --version
120 -R
120 -R
121 -h
121 -h
122 -q
122 -q
123 -v
123 -v
124 -y
124 -y
125
125
126 Show the options for the "serve" command
126 Show the options for the "serve" command
127 $ hg debugcomplete --options serve | sort
127 $ hg debugcomplete --options serve | sort
128 --accesslog
128 --accesslog
129 --address
129 --address
130 --certificate
130 --certificate
131 --config
131 --config
132 --cwd
132 --cwd
133 --daemon
133 --daemon
134 --daemon-pipefds
134 --daemon-pipefds
135 --debug
135 --debug
136 --debugger
136 --debugger
137 --encoding
137 --encoding
138 --encodingmode
138 --encodingmode
139 --errorlog
139 --errorlog
140 --help
140 --help
141 --ipv6
141 --ipv6
142 --name
142 --name
143 --noninteractive
143 --noninteractive
144 --pid-file
144 --pid-file
145 --port
145 --port
146 --prefix
146 --prefix
147 --profile
147 --profile
148 --quiet
148 --quiet
149 --repository
149 --repository
150 --stdio
150 --stdio
151 --style
151 --style
152 --templates
152 --templates
153 --time
153 --time
154 --traceback
154 --traceback
155 --verbose
155 --verbose
156 --version
156 --version
157 --web-conf
157 --web-conf
158 -6
158 -6
159 -A
159 -A
160 -E
160 -E
161 -R
161 -R
162 -a
162 -a
163 -d
163 -d
164 -h
164 -h
165 -n
165 -n
166 -p
166 -p
167 -q
167 -q
168 -t
168 -t
169 -v
169 -v
170 -y
170 -y
171
171
172 Show an error if we use --options with an ambiguous abbreviation
172 Show an error if we use --options with an ambiguous abbreviation
173 $ hg debugcomplete --options s
173 $ hg debugcomplete --options s
174 hg: command 's' is ambiguous:
174 hg: command 's' is ambiguous:
175 serve showconfig status summary
175 serve showconfig status summary
176
176
177 Show all commands + options
177 Show all commands + options
178 $ hg debugcommands
178 $ hg debugcommands
179 add: include, exclude, dry-run
179 add: include, exclude, dry-run
180 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
180 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
181 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd
181 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd
182 commit: addremove, close-branch, include, exclude, message, logfile, date, user
182 commit: addremove, close-branch, include, exclude, message, logfile, date, user
183 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude
183 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
184 export: output, switch-parent, rev, text, git, nodates
184 export: output, switch-parent, rev, text, git, nodates
185 forget: include, exclude
185 forget: include, exclude
186 init: ssh, remotecmd
186 init: ssh, remotecmd
187 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
187 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
188 merge: force, rev, preview
188 merge: force, rev, preview
189 pull: update, force, rev, branch, ssh, remotecmd
189 pull: update, force, rev, branch, ssh, remotecmd
190 push: force, rev, branch, new-branch, ssh, remotecmd
190 push: force, rev, branch, new-branch, ssh, remotecmd
191 remove: after, force, include, exclude
191 remove: after, force, include, exclude
192 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
192 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
193 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
193 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
194 summary: remote
194 summary: remote
195 update: clean, check, date, rev
195 update: clean, check, date, rev
196 addremove: similarity, include, exclude, dry-run
196 addremove: similarity, include, exclude, dry-run
197 archive: no-decode, prefix, rev, type, include, exclude
197 archive: no-decode, prefix, rev, type, include, exclude
198 backout: merge, parent, rev, include, exclude, message, logfile, date, user
198 backout: merge, parent, rev, include, exclude, message, logfile, date, user
199 bisect: reset, good, bad, skip, command, noupdate
199 bisect: reset, good, bad, skip, command, noupdate
200 branch: force, clean
200 branch: force, clean
201 branches: active, closed
201 branches: active, closed
202 bundle: force, rev, branch, base, all, type, ssh, remotecmd
202 bundle: force, rev, branch, base, all, type, ssh, remotecmd
203 cat: output, rev, decode, include, exclude
203 cat: output, rev, decode, include, exclude
204 copy: after, force, include, exclude, dry-run
204 copy: after, force, include, exclude, dry-run
205 debugancestor:
205 debugancestor:
206 debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
206 debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
207 debugcheckstate:
207 debugcheckstate:
208 debugcommands:
208 debugcommands:
209 debugcomplete: options
209 debugcomplete: options
210 debugdag: tags, branches, dots, spaces
210 debugdag: tags, branches, dots, spaces
211 debugdata:
211 debugdata:
212 debugdate: extended
212 debugdate: extended
213 debugfsinfo:
213 debugfsinfo:
214 debugindex:
214 debugindex:
215 debugindexdot:
215 debugindexdot:
216 debuginstall:
216 debuginstall:
217 debugpushkey:
217 debugpushkey:
218 debugrebuildstate: rev
218 debugrebuildstate: rev
219 debugrename: rev
219 debugrename: rev
220 debugrevspec:
220 debugrevspec:
221 debugsetparents:
221 debugsetparents:
222 debugstate: nodates
222 debugstate: nodates
223 debugsub: rev
223 debugsub: rev
224 debugwalk: include, exclude
224 debugwalk: include, exclude
225 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
225 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
226 heads: rev, topo, active, closed, style, template
226 heads: rev, topo, active, closed, style, template
227 help:
227 help:
228 identify: rev, num, id, branch, tags
228 identify: rev, num, id, branch, tags
229 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
229 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
230 incoming: force, newest-first, bundle, rev, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd
230 incoming: force, newest-first, bundle, rev, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd
231 locate: rev, print0, fullpath, include, exclude
231 locate: rev, print0, fullpath, include, exclude
232 manifest: rev
232 manifest: rev
233 outgoing: force, rev, newest-first, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd
233 outgoing: force, rev, newest-first, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd
234 parents: rev, style, template
234 parents: rev, style, template
235 paths:
235 paths:
236 recover:
236 recover:
237 rename: after, force, include, exclude, dry-run
237 rename: after, force, include, exclude, dry-run
238 resolve: all, list, mark, unmark, no-status, include, exclude
238 resolve: all, list, mark, unmark, no-status, include, exclude
239 revert: all, date, rev, no-backup, include, exclude, dry-run
239 revert: all, date, rev, no-backup, include, exclude, dry-run
240 rollback: dry-run
240 rollback: dry-run
241 root:
241 root:
242 showconfig: untrusted
242 showconfig: untrusted
243 tag: force, local, rev, remove, edit, message, date, user
243 tag: force, local, rev, remove, edit, message, date, user
244 tags:
244 tags:
245 tip: patch, git, style, template
245 tip: patch, git, style, template
246 unbundle: update
246 unbundle: update
247 verify:
247 verify:
248 version:
248 version:
249
249
250 $ exit 0
250 $ exit 0
@@ -1,745 +1,746 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 $ hg
3 $ hg
4 Mercurial Distributed SCM
4 Mercurial Distributed SCM
5
5
6 basic commands:
6 basic commands:
7
7
8 add add the specified files on the next commit
8 add add the specified files on the next commit
9 annotate show changeset information by line for each file
9 annotate show changeset information by line for each file
10 clone make a copy of an existing repository
10 clone make a copy of an existing repository
11 commit commit the specified files or all outstanding changes
11 commit commit the specified files or all outstanding changes
12 diff diff repository (or selected files)
12 diff diff repository (or selected files)
13 export dump the header and diffs for one or more changesets
13 export dump the header and diffs for one or more changesets
14 forget forget the specified files on the next commit
14 forget forget the specified files on the next commit
15 init create a new repository in the given directory
15 init create a new repository in the given directory
16 log show revision history of entire repository or files
16 log show revision history of entire repository or files
17 merge merge working directory with another revision
17 merge merge working directory with another revision
18 pull pull changes from the specified source
18 pull pull changes from the specified source
19 push push changes to the specified destination
19 push push changes to the specified destination
20 remove remove the specified files on the next commit
20 remove remove the specified files on the next commit
21 serve start stand-alone webserver
21 serve start stand-alone webserver
22 status show changed files in the working directory
22 status show changed files in the working directory
23 summary summarize working directory state
23 summary summarize working directory state
24 update update working directory (or switch revisions)
24 update update working directory (or switch revisions)
25
25
26 use "hg help" for the full list of commands or "hg -v" for details
26 use "hg help" for the full list of commands or "hg -v" for details
27
27
28 $ hg -q
28 $ hg -q
29 add add the specified files on the next commit
29 add add the specified files on the next commit
30 annotate show changeset information by line for each file
30 annotate show changeset information by line for each file
31 clone make a copy of an existing repository
31 clone make a copy of an existing repository
32 commit commit the specified files or all outstanding changes
32 commit commit the specified files or all outstanding changes
33 diff diff repository (or selected files)
33 diff diff repository (or selected files)
34 export dump the header and diffs for one or more changesets
34 export dump the header and diffs for one or more changesets
35 forget forget the specified files on the next commit
35 forget forget the specified files on the next commit
36 init create a new repository in the given directory
36 init create a new repository in the given directory
37 log show revision history of entire repository or files
37 log show revision history of entire repository or files
38 merge merge working directory with another revision
38 merge merge working directory with another revision
39 pull pull changes from the specified source
39 pull pull changes from the specified source
40 push push changes to the specified destination
40 push push changes to the specified destination
41 remove remove the specified files on the next commit
41 remove remove the specified files on the next commit
42 serve start stand-alone webserver
42 serve start stand-alone webserver
43 status show changed files in the working directory
43 status show changed files in the working directory
44 summary summarize working directory state
44 summary summarize working directory state
45 update update working directory (or switch revisions)
45 update update working directory (or switch revisions)
46
46
47 $ hg help
47 $ hg help
48 Mercurial Distributed SCM
48 Mercurial Distributed SCM
49
49
50 list of commands:
50 list of commands:
51
51
52 add add the specified files on the next commit
52 add add the specified files on the next commit
53 addremove add all new files, delete all missing files
53 addremove add all new files, delete all missing files
54 annotate show changeset information by line for each file
54 annotate show changeset information by line for each file
55 archive create an unversioned archive of a repository revision
55 archive create an unversioned archive of a repository revision
56 backout reverse effect of earlier changeset
56 backout reverse effect of earlier changeset
57 bisect subdivision search of changesets
57 bisect subdivision search of changesets
58 branch set or show the current branch name
58 branch set or show the current branch name
59 branches list repository named branches
59 branches list repository named branches
60 bundle create a changegroup file
60 bundle create a changegroup file
61 cat output the current or given revision of files
61 cat output the current or given revision of files
62 clone make a copy of an existing repository
62 clone make a copy of an existing repository
63 commit commit the specified files or all outstanding changes
63 commit commit the specified files or all outstanding changes
64 copy mark files as copied for the next commit
64 copy mark files as copied for the next commit
65 diff diff repository (or selected files)
65 diff diff repository (or selected files)
66 export dump the header and diffs for one or more changesets
66 export dump the header and diffs for one or more changesets
67 forget forget the specified files on the next commit
67 forget forget the specified files on the next commit
68 grep search for a pattern in specified files and revisions
68 grep search for a pattern in specified files and revisions
69 heads show current repository heads or show branch heads
69 heads show current repository heads or show branch heads
70 help show help for a given topic or a help overview
70 help show help for a given topic or a help overview
71 identify identify the working copy or specified revision
71 identify identify the working copy or specified revision
72 import import an ordered set of patches
72 import import an ordered set of patches
73 incoming show new changesets found in source
73 incoming show new changesets found in source
74 init create a new repository in the given directory
74 init create a new repository in the given directory
75 locate locate files matching specific patterns
75 locate locate files matching specific patterns
76 log show revision history of entire repository or files
76 log show revision history of entire repository or files
77 manifest output the current or given revision of the project manifest
77 manifest output the current or given revision of the project manifest
78 merge merge working directory with another revision
78 merge merge working directory with another revision
79 outgoing show changesets not found in the destination
79 outgoing show changesets not found in the destination
80 parents show the parents of the working directory or revision
80 parents show the parents of the working directory or revision
81 paths show aliases for remote repositories
81 paths show aliases for remote repositories
82 pull pull changes from the specified source
82 pull pull changes from the specified source
83 push push changes to the specified destination
83 push push changes to the specified destination
84 recover roll back an interrupted transaction
84 recover roll back an interrupted transaction
85 remove remove the specified files on the next commit
85 remove remove the specified files on the next commit
86 rename rename files; equivalent of copy + remove
86 rename rename files; equivalent of copy + remove
87 resolve redo merges or set/view the merge status of files
87 resolve redo merges or set/view the merge status of files
88 revert restore individual files or directories to an earlier state
88 revert restore individual files or directories to an earlier state
89 rollback roll back the last transaction (dangerous)
89 rollback roll back the last transaction (dangerous)
90 root print the root (top) of the current working directory
90 root print the root (top) of the current working directory
91 serve start stand-alone webserver
91 serve start stand-alone webserver
92 showconfig show combined config settings from all hgrc files
92 showconfig show combined config settings from all hgrc files
93 status show changed files in the working directory
93 status show changed files in the working directory
94 summary summarize working directory state
94 summary summarize working directory state
95 tag add one or more tags for the current or given revision
95 tag add one or more tags for the current or given revision
96 tags list repository tags
96 tags list repository tags
97 tip show the tip revision
97 tip show the tip revision
98 unbundle apply one or more changegroup files
98 unbundle apply one or more changegroup files
99 update update working directory (or switch revisions)
99 update update working directory (or switch revisions)
100 verify verify the integrity of the repository
100 verify verify the integrity of the repository
101 version output version and copyright information
101 version output version and copyright information
102
102
103 additional help topics:
103 additional help topics:
104
104
105 config Configuration Files
105 config Configuration Files
106 dates Date Formats
106 dates Date Formats
107 patterns File Name Patterns
107 patterns File Name Patterns
108 environment Environment Variables
108 environment Environment Variables
109 revisions Specifying Single Revisions
109 revisions Specifying Single Revisions
110 multirevs Specifying Multiple Revisions
110 multirevs Specifying Multiple Revisions
111 revsets Specifying Revision Sets
111 revsets Specifying Revision Sets
112 diffs Diff Formats
112 diffs Diff Formats
113 templating Template Usage
113 templating Template Usage
114 urls URL Paths
114 urls URL Paths
115 extensions Using additional features
115 extensions Using additional features
116 hgweb Configuring hgweb
116 hgweb Configuring hgweb
117 glossary Glossary
117 glossary Glossary
118
118
119 use "hg -v help" to show aliases and global options
119 use "hg -v help" to show aliases and global options
120
120
121 $ hg -q help
121 $ hg -q help
122 add add the specified files on the next commit
122 add add the specified files on the next commit
123 addremove add all new files, delete all missing files
123 addremove add all new files, delete all missing files
124 annotate show changeset information by line for each file
124 annotate show changeset information by line for each file
125 archive create an unversioned archive of a repository revision
125 archive create an unversioned archive of a repository revision
126 backout reverse effect of earlier changeset
126 backout reverse effect of earlier changeset
127 bisect subdivision search of changesets
127 bisect subdivision search of changesets
128 branch set or show the current branch name
128 branch set or show the current branch name
129 branches list repository named branches
129 branches list repository named branches
130 bundle create a changegroup file
130 bundle create a changegroup file
131 cat output the current or given revision of files
131 cat output the current or given revision of files
132 clone make a copy of an existing repository
132 clone make a copy of an existing repository
133 commit commit the specified files or all outstanding changes
133 commit commit the specified files or all outstanding changes
134 copy mark files as copied for the next commit
134 copy mark files as copied for the next commit
135 diff diff repository (or selected files)
135 diff diff repository (or selected files)
136 export dump the header and diffs for one or more changesets
136 export dump the header and diffs for one or more changesets
137 forget forget the specified files on the next commit
137 forget forget the specified files on the next commit
138 grep search for a pattern in specified files and revisions
138 grep search for a pattern in specified files and revisions
139 heads show current repository heads or show branch heads
139 heads show current repository heads or show branch heads
140 help show help for a given topic or a help overview
140 help show help for a given topic or a help overview
141 identify identify the working copy or specified revision
141 identify identify the working copy or specified revision
142 import import an ordered set of patches
142 import import an ordered set of patches
143 incoming show new changesets found in source
143 incoming show new changesets found in source
144 init create a new repository in the given directory
144 init create a new repository in the given directory
145 locate locate files matching specific patterns
145 locate locate files matching specific patterns
146 log show revision history of entire repository or files
146 log show revision history of entire repository or files
147 manifest output the current or given revision of the project manifest
147 manifest output the current or given revision of the project manifest
148 merge merge working directory with another revision
148 merge merge working directory with another revision
149 outgoing show changesets not found in the destination
149 outgoing show changesets not found in the destination
150 parents show the parents of the working directory or revision
150 parents show the parents of the working directory or revision
151 paths show aliases for remote repositories
151 paths show aliases for remote repositories
152 pull pull changes from the specified source
152 pull pull changes from the specified source
153 push push changes to the specified destination
153 push push changes to the specified destination
154 recover roll back an interrupted transaction
154 recover roll back an interrupted transaction
155 remove remove the specified files on the next commit
155 remove remove the specified files on the next commit
156 rename rename files; equivalent of copy + remove
156 rename rename files; equivalent of copy + remove
157 resolve redo merges or set/view the merge status of files
157 resolve redo merges or set/view the merge status of files
158 revert restore individual files or directories to an earlier state
158 revert restore individual files or directories to an earlier state
159 rollback roll back the last transaction (dangerous)
159 rollback roll back the last transaction (dangerous)
160 root print the root (top) of the current working directory
160 root print the root (top) of the current working directory
161 serve start stand-alone webserver
161 serve start stand-alone webserver
162 showconfig show combined config settings from all hgrc files
162 showconfig show combined config settings from all hgrc files
163 status show changed files in the working directory
163 status show changed files in the working directory
164 summary summarize working directory state
164 summary summarize working directory state
165 tag add one or more tags for the current or given revision
165 tag add one or more tags for the current or given revision
166 tags list repository tags
166 tags list repository tags
167 tip show the tip revision
167 tip show the tip revision
168 unbundle apply one or more changegroup files
168 unbundle apply one or more changegroup files
169 update update working directory (or switch revisions)
169 update update working directory (or switch revisions)
170 verify verify the integrity of the repository
170 verify verify the integrity of the repository
171 version output version and copyright information
171 version output version and copyright information
172
172
173 additional help topics:
173 additional help topics:
174
174
175 config Configuration Files
175 config Configuration Files
176 dates Date Formats
176 dates Date Formats
177 patterns File Name Patterns
177 patterns File Name Patterns
178 environment Environment Variables
178 environment Environment Variables
179 revisions Specifying Single Revisions
179 revisions Specifying Single Revisions
180 multirevs Specifying Multiple Revisions
180 multirevs Specifying Multiple Revisions
181 revsets Specifying Revision Sets
181 revsets Specifying Revision Sets
182 diffs Diff Formats
182 diffs Diff Formats
183 templating Template Usage
183 templating Template Usage
184 urls URL Paths
184 urls URL Paths
185 extensions Using additional features
185 extensions Using additional features
186 hgweb Configuring hgweb
186 hgweb Configuring hgweb
187 glossary Glossary
187 glossary Glossary
188
188
189 Test short command list with verbose option
189 Test short command list with verbose option
190
190
191 $ hg -v help shortlist
191 $ hg -v help shortlist
192 Mercurial Distributed SCM \(version .*?\)
192 Mercurial Distributed SCM \(version .*?\)
193
193
194 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
194 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
195 This is free software; see the source for copying conditions. There is NO
195 This is free software; see the source for copying conditions. There is NO
196 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
196 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
197
197
198 basic commands:
198 basic commands:
199
199
200 add:
200 add:
201 add the specified files on the next commit
201 add the specified files on the next commit
202 annotate, blame:
202 annotate, blame:
203 show changeset information by line for each file
203 show changeset information by line for each file
204 clone:
204 clone:
205 make a copy of an existing repository
205 make a copy of an existing repository
206 commit, ci:
206 commit, ci:
207 commit the specified files or all outstanding changes
207 commit the specified files or all outstanding changes
208 diff:
208 diff:
209 diff repository (or selected files)
209 diff repository (or selected files)
210 export:
210 export:
211 dump the header and diffs for one or more changesets
211 dump the header and diffs for one or more changesets
212 forget:
212 forget:
213 forget the specified files on the next commit
213 forget the specified files on the next commit
214 init:
214 init:
215 create a new repository in the given directory
215 create a new repository in the given directory
216 log, history:
216 log, history:
217 show revision history of entire repository or files
217 show revision history of entire repository or files
218 merge:
218 merge:
219 merge working directory with another revision
219 merge working directory with another revision
220 pull:
220 pull:
221 pull changes from the specified source
221 pull changes from the specified source
222 push:
222 push:
223 push changes to the specified destination
223 push changes to the specified destination
224 remove, rm:
224 remove, rm:
225 remove the specified files on the next commit
225 remove the specified files on the next commit
226 serve:
226 serve:
227 start stand-alone webserver
227 start stand-alone webserver
228 status, st:
228 status, st:
229 show changed files in the working directory
229 show changed files in the working directory
230 summary, sum:
230 summary, sum:
231 summarize working directory state
231 summarize working directory state
232 update, up, checkout, co:
232 update, up, checkout, co:
233 update working directory (or switch revisions)
233 update working directory (or switch revisions)
234
234
235 global options:
235 global options:
236 -R --repository REPO repository root directory or name of overlay bundle
236 -R --repository REPO repository root directory or name of overlay bundle
237 file
237 file
238 --cwd DIR change working directory
238 --cwd DIR change working directory
239 -y --noninteractive do not prompt, assume 'yes' for any required answers
239 -y --noninteractive do not prompt, assume 'yes' for any required answers
240 -q --quiet suppress output
240 -q --quiet suppress output
241 -v --verbose enable additional output
241 -v --verbose enable additional output
242 --config CONFIG [+] set/override config option (use 'section.name=value')
242 --config CONFIG [+] set/override config option (use 'section.name=value')
243 --debug enable debugging output
243 --debug enable debugging output
244 --debugger start debugger
244 --debugger start debugger
245 --encoding ENCODE set the charset encoding (default: ascii)
245 --encoding ENCODE set the charset encoding (default: ascii)
246 --encodingmode MODE set the charset encoding mode (default: strict)
246 --encodingmode MODE set the charset encoding mode (default: strict)
247 --traceback always print a traceback on exception
247 --traceback always print a traceback on exception
248 --time time how long the command takes
248 --time time how long the command takes
249 --profile print command execution profile
249 --profile print command execution profile
250 --version output version information and exit
250 --version output version information and exit
251 -h --help display help and exit
251 -h --help display help and exit
252
252
253 [+] marked option can be specified multiple times
253 [+] marked option can be specified multiple times
254
254
255 use "hg help" for the full list of commands
255 use "hg help" for the full list of commands
256
256
257 $ hg add -h
257 $ hg add -h
258 hg add [OPTION]... [FILE]...
258 hg add [OPTION]... [FILE]...
259
259
260 add the specified files on the next commit
260 add the specified files on the next commit
261
261
262 Schedule files to be version controlled and added to the repository.
262 Schedule files to be version controlled and added to the repository.
263
263
264 The files will be added to the repository at the next commit. To undo an
264 The files will be added to the repository at the next commit. To undo an
265 add before that, see "hg forget".
265 add before that, see "hg forget".
266
266
267 If no names are given, add all files to the repository.
267 If no names are given, add all files to the repository.
268
268
269 Returns 0 if all files are successfully added.
269 Returns 0 if all files are successfully added.
270
270
271 use "hg -v help add" to show verbose help
271 use "hg -v help add" to show verbose help
272
272
273 options:
273 options:
274
274
275 -I --include PATTERN [+] include names matching the given patterns
275 -I --include PATTERN [+] include names matching the given patterns
276 -X --exclude PATTERN [+] exclude names matching the given patterns
276 -X --exclude PATTERN [+] exclude names matching the given patterns
277 -n --dry-run do not perform actions, just print output
277 -n --dry-run do not perform actions, just print output
278
278
279 [+] marked option can be specified multiple times
279 [+] marked option can be specified multiple times
280
280
281 use "hg -v help add" to show global options
281 use "hg -v help add" to show global options
282
282
283 Verbose help for add
283 Verbose help for add
284
284
285 $ hg add -hv
285 $ hg add -hv
286 hg add [OPTION]... [FILE]...
286 hg add [OPTION]... [FILE]...
287
287
288 add the specified files on the next commit
288 add the specified files on the next commit
289
289
290 Schedule files to be version controlled and added to the repository.
290 Schedule files to be version controlled and added to the repository.
291
291
292 The files will be added to the repository at the next commit. To undo an
292 The files will be added to the repository at the next commit. To undo an
293 add before that, see "hg forget".
293 add before that, see "hg forget".
294
294
295 If no names are given, add all files to the repository.
295 If no names are given, add all files to the repository.
296
296
297 An example showing how new (unknown) files are added automatically by "hg
297 An example showing how new (unknown) files are added automatically by "hg
298 add":
298 add":
299
299
300 $ ls
300 $ ls
301 foo.c
301 foo.c
302 $ hg status
302 $ hg status
303 ? foo.c
303 ? foo.c
304 $ hg add
304 $ hg add
305 adding foo.c
305 adding foo.c
306 $ hg status
306 $ hg status
307 A foo.c
307 A foo.c
308
308
309 Returns 0 if all files are successfully added.
309 Returns 0 if all files are successfully added.
310
310
311 options:
311 options:
312
312
313 -I --include PATTERN [+] include names matching the given patterns
313 -I --include PATTERN [+] include names matching the given patterns
314 -X --exclude PATTERN [+] exclude names matching the given patterns
314 -X --exclude PATTERN [+] exclude names matching the given patterns
315 -n --dry-run do not perform actions, just print output
315 -n --dry-run do not perform actions, just print output
316
316
317 global options:
317 global options:
318 -R --repository REPO repository root directory or name of overlay bundle
318 -R --repository REPO repository root directory or name of overlay bundle
319 file
319 file
320 --cwd DIR change working directory
320 --cwd DIR change working directory
321 -y --noninteractive do not prompt, assume 'yes' for any required
321 -y --noninteractive do not prompt, assume 'yes' for any required
322 answers
322 answers
323 -q --quiet suppress output
323 -q --quiet suppress output
324 -v --verbose enable additional output
324 -v --verbose enable additional output
325 --config CONFIG [+] set/override config option (use
325 --config CONFIG [+] set/override config option (use
326 'section.name=value')
326 'section.name=value')
327 --debug enable debugging output
327 --debug enable debugging output
328 --debugger start debugger
328 --debugger start debugger
329 --encoding ENCODE set the charset encoding (default: ascii)
329 --encoding ENCODE set the charset encoding (default: ascii)
330 --encodingmode MODE set the charset encoding mode (default: strict)
330 --encodingmode MODE set the charset encoding mode (default: strict)
331 --traceback always print a traceback on exception
331 --traceback always print a traceback on exception
332 --time time how long the command takes
332 --time time how long the command takes
333 --profile print command execution profile
333 --profile print command execution profile
334 --version output version information and exit
334 --version output version information and exit
335 -h --help display help and exit
335 -h --help display help and exit
336
336
337 [+] marked option can be specified multiple times
337 [+] marked option can be specified multiple times
338
338
339 Test help option with version option
339 Test help option with version option
340
340
341 $ hg add -h --version
341 $ hg add -h --version
342 Mercurial Distributed SCM \(version .+?\)
342 Mercurial Distributed SCM \(version .+?\)
343
343
344 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
344 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
345 This is free software; see the source for copying conditions. There is NO
345 This is free software; see the source for copying conditions. There is NO
346 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
346 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
347
347
348 hg add [OPTION]... [FILE]...
348 hg add [OPTION]... [FILE]...
349
349
350 add the specified files on the next commit
350 add the specified files on the next commit
351
351
352 Schedule files to be version controlled and added to the repository.
352 Schedule files to be version controlled and added to the repository.
353
353
354 The files will be added to the repository at the next commit. To undo an
354 The files will be added to the repository at the next commit. To undo an
355 add before that, see "hg forget".
355 add before that, see "hg forget".
356
356
357 If no names are given, add all files to the repository.
357 If no names are given, add all files to the repository.
358
358
359 Returns 0 if all files are successfully added.
359 Returns 0 if all files are successfully added.
360
360
361 use "hg -v help add" to show verbose help
361 use "hg -v help add" to show verbose help
362
362
363 options:
363 options:
364
364
365 -I --include PATTERN [+] include names matching the given patterns
365 -I --include PATTERN [+] include names matching the given patterns
366 -X --exclude PATTERN [+] exclude names matching the given patterns
366 -X --exclude PATTERN [+] exclude names matching the given patterns
367 -n --dry-run do not perform actions, just print output
367 -n --dry-run do not perform actions, just print output
368
368
369 [+] marked option can be specified multiple times
369 [+] marked option can be specified multiple times
370
370
371 use "hg -v help add" to show global options
371 use "hg -v help add" to show global options
372
372
373 $ hg add --skjdfks
373 $ hg add --skjdfks
374 hg add: option --skjdfks not recognized
374 hg add: option --skjdfks not recognized
375 hg add [OPTION]... [FILE]...
375 hg add [OPTION]... [FILE]...
376
376
377 add the specified files on the next commit
377 add the specified files on the next commit
378
378
379 Schedule files to be version controlled and added to the repository.
379 Schedule files to be version controlled and added to the repository.
380
380
381 The files will be added to the repository at the next commit. To undo an
381 The files will be added to the repository at the next commit. To undo an
382 add before that, see "hg forget".
382 add before that, see "hg forget".
383
383
384 If no names are given, add all files to the repository.
384 If no names are given, add all files to the repository.
385
385
386 Returns 0 if all files are successfully added.
386 Returns 0 if all files are successfully added.
387
387
388 use "hg -v help add" to show verbose help
388 use "hg -v help add" to show verbose help
389
389
390 options:
390 options:
391
391
392 -I --include PATTERN [+] include names matching the given patterns
392 -I --include PATTERN [+] include names matching the given patterns
393 -X --exclude PATTERN [+] exclude names matching the given patterns
393 -X --exclude PATTERN [+] exclude names matching the given patterns
394 -n --dry-run do not perform actions, just print output
394 -n --dry-run do not perform actions, just print output
395
395
396 [+] marked option can be specified multiple times
396 [+] marked option can be specified multiple times
397
397
398 use "hg -v help add" to show global options
398 use "hg -v help add" to show global options
399
399
400 Test ambiguous command help
400 Test ambiguous command help
401
401
402 $ hg help ad
402 $ hg help ad
403 list of commands:
403 list of commands:
404
404
405 add add the specified files on the next commit
405 add add the specified files on the next commit
406 addremove add all new files, delete all missing files
406 addremove add all new files, delete all missing files
407
407
408 use "hg -v help ad" to show aliases and global options
408 use "hg -v help ad" to show aliases and global options
409
409
410 Test command without options
410 Test command without options
411
411
412 $ hg help verify
412 $ hg help verify
413 hg verify
413 hg verify
414
414
415 verify the integrity of the repository
415 verify the integrity of the repository
416
416
417 Verify the integrity of the current repository.
417 Verify the integrity of the current repository.
418
418
419 This will perform an extensive check of the repository's integrity,
419 This will perform an extensive check of the repository's integrity,
420 validating the hashes and checksums of each entry in the changelog,
420 validating the hashes and checksums of each entry in the changelog,
421 manifest, and tracked files, as well as the integrity of their crosslinks
421 manifest, and tracked files, as well as the integrity of their crosslinks
422 and indices.
422 and indices.
423
423
424 Returns 0 on success, 1 if errors are encountered.
424 Returns 0 on success, 1 if errors are encountered.
425
425
426 use "hg -v help verify" to show global options
426 use "hg -v help verify" to show global options
427
427
428 $ hg help diff
428 $ hg help diff
429 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
429 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
430
430
431 diff repository (or selected files)
431 diff repository (or selected files)
432
432
433 Show differences between revisions for the specified files.
433 Show differences between revisions for the specified files.
434
434
435 Differences between files are shown using the unified diff format.
435 Differences between files are shown using the unified diff format.
436
436
437 NOTE: diff may generate unexpected results for merges, as it will default
437 NOTE: diff may generate unexpected results for merges, as it will default
438 to comparing against the working directory's first parent changeset if no
438 to comparing against the working directory's first parent changeset if no
439 revisions are specified.
439 revisions are specified.
440
440
441 When two revision arguments are given, then changes are shown between
441 When two revision arguments are given, then changes are shown between
442 those revisions. If only one revision is specified then that revision is
442 those revisions. If only one revision is specified then that revision is
443 compared to the working directory, and, when no revisions are specified,
443 compared to the working directory, and, when no revisions are specified,
444 the working directory files are compared to its parent.
444 the working directory files are compared to its parent.
445
445
446 Alternatively you can specify -c/--change with a revision to see the
446 Alternatively you can specify -c/--change with a revision to see the
447 changes in that changeset relative to its first parent.
447 changes in that changeset relative to its first parent.
448
448
449 Without the -a/--text option, diff will avoid generating diffs of files it
449 Without the -a/--text option, diff will avoid generating diffs of files it
450 detects as binary. With -a, diff will generate a diff anyway, probably
450 detects as binary. With -a, diff will generate a diff anyway, probably
451 with undesirable results.
451 with undesirable results.
452
452
453 Use the -g/--git option to generate diffs in the git extended diff format.
453 Use the -g/--git option to generate diffs in the git extended diff format.
454 For more information, read "hg help diffs".
454 For more information, read "hg help diffs".
455
455
456 Returns 0 on success.
456 Returns 0 on success.
457
457
458 options:
458 options:
459
459
460 -r --rev REV [+] revision
460 -r --rev REV [+] revision
461 -c --change REV change made by revision
461 -c --change REV change made by revision
462 -a --text treat all files as text
462 -a --text treat all files as text
463 -g --git use git extended diff format
463 -g --git use git extended diff format
464 --nodates omit dates from diff headers
464 --nodates omit dates from diff headers
465 -p --show-function show which function each change is in
465 -p --show-function show which function each change is in
466 --reverse produce a diff that undoes the changes
466 --reverse produce a diff that undoes the changes
467 -w --ignore-all-space ignore white space when comparing lines
467 -w --ignore-all-space ignore white space when comparing lines
468 -b --ignore-space-change ignore changes in the amount of white space
468 -b --ignore-space-change ignore changes in the amount of white space
469 -B --ignore-blank-lines ignore changes whose lines are all blank
469 -B --ignore-blank-lines ignore changes whose lines are all blank
470 -U --unified NUM number of lines of context to show
470 -U --unified NUM number of lines of context to show
471 --stat output diffstat-style summary of changes
471 --stat output diffstat-style summary of changes
472 -I --include PATTERN [+] include names matching the given patterns
472 -I --include PATTERN [+] include names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
474 -S --subrepos recurse into subrepositories
474
475
475 [+] marked option can be specified multiple times
476 [+] marked option can be specified multiple times
476
477
477 use "hg -v help diff" to show global options
478 use "hg -v help diff" to show global options
478
479
479 $ hg help status
480 $ hg help status
480 hg status [OPTION]... [FILE]...
481 hg status [OPTION]... [FILE]...
481
482
482 aliases: st
483 aliases: st
483
484
484 show changed files in the working directory
485 show changed files in the working directory
485
486
486 Show status of files in the repository. If names are given, only files
487 Show status of files in the repository. If names are given, only files
487 that match are shown. Files that are clean or ignored or the source of a
488 that match are shown. Files that are clean or ignored or the source of a
488 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
489 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
489 -C/--copies or -A/--all are given. Unless options described with "show
490 -C/--copies or -A/--all are given. Unless options described with "show
490 only ..." are given, the options -mardu are used.
491 only ..." are given, the options -mardu are used.
491
492
492 Option -q/--quiet hides untracked (unknown and ignored) files unless
493 Option -q/--quiet hides untracked (unknown and ignored) files unless
493 explicitly requested with -u/--unknown or -i/--ignored.
494 explicitly requested with -u/--unknown or -i/--ignored.
494
495
495 NOTE: status may appear to disagree with diff if permissions have changed
496 NOTE: status may appear to disagree with diff if permissions have changed
496 or a merge has occurred. The standard diff format does not report
497 or a merge has occurred. The standard diff format does not report
497 permission changes and diff only reports changes relative to one merge
498 permission changes and diff only reports changes relative to one merge
498 parent.
499 parent.
499
500
500 If one revision is given, it is used as the base revision. If two
501 If one revision is given, it is used as the base revision. If two
501 revisions are given, the differences between them are shown. The --change
502 revisions are given, the differences between them are shown. The --change
502 option can also be used as a shortcut to list the changed files of a
503 option can also be used as a shortcut to list the changed files of a
503 revision from its first parent.
504 revision from its first parent.
504
505
505 The codes used to show the status of files are:
506 The codes used to show the status of files are:
506
507
507 M = modified
508 M = modified
508 A = added
509 A = added
509 R = removed
510 R = removed
510 C = clean
511 C = clean
511 ! = missing (deleted by non-hg command, but still tracked)
512 ! = missing (deleted by non-hg command, but still tracked)
512 ? = not tracked
513 ? = not tracked
513 I = ignored
514 I = ignored
514 = origin of the previous file listed as A (added)
515 = origin of the previous file listed as A (added)
515
516
516 Returns 0 on success.
517 Returns 0 on success.
517
518
518 options:
519 options:
519
520
520 -A --all show status of all files
521 -A --all show status of all files
521 -m --modified show only modified files
522 -m --modified show only modified files
522 -a --added show only added files
523 -a --added show only added files
523 -r --removed show only removed files
524 -r --removed show only removed files
524 -d --deleted show only deleted (but tracked) files
525 -d --deleted show only deleted (but tracked) files
525 -c --clean show only files without changes
526 -c --clean show only files without changes
526 -u --unknown show only unknown (not tracked) files
527 -u --unknown show only unknown (not tracked) files
527 -i --ignored show only ignored files
528 -i --ignored show only ignored files
528 -n --no-status hide status prefix
529 -n --no-status hide status prefix
529 -C --copies show source of copied files
530 -C --copies show source of copied files
530 -0 --print0 end filenames with NUL, for use with xargs
531 -0 --print0 end filenames with NUL, for use with xargs
531 --rev REV [+] show difference from revision
532 --rev REV [+] show difference from revision
532 --change REV list the changed files of a revision
533 --change REV list the changed files of a revision
533 -I --include PATTERN [+] include names matching the given patterns
534 -I --include PATTERN [+] include names matching the given patterns
534 -X --exclude PATTERN [+] exclude names matching the given patterns
535 -X --exclude PATTERN [+] exclude names matching the given patterns
535 -S --subrepos recurse into subrepositories
536 -S --subrepos recurse into subrepositories
536
537
537 [+] marked option can be specified multiple times
538 [+] marked option can be specified multiple times
538
539
539 use "hg -v help status" to show global options
540 use "hg -v help status" to show global options
540
541
541 $ hg -q help status
542 $ hg -q help status
542 hg status [OPTION]... [FILE]...
543 hg status [OPTION]... [FILE]...
543
544
544 show changed files in the working directory
545 show changed files in the working directory
545
546
546 $ hg help foo
547 $ hg help foo
547 hg: unknown command 'foo'
548 hg: unknown command 'foo'
548 Mercurial Distributed SCM
549 Mercurial Distributed SCM
549
550
550 basic commands:
551 basic commands:
551
552
552 add add the specified files on the next commit
553 add add the specified files on the next commit
553 annotate show changeset information by line for each file
554 annotate show changeset information by line for each file
554 clone make a copy of an existing repository
555 clone make a copy of an existing repository
555 commit commit the specified files or all outstanding changes
556 commit commit the specified files or all outstanding changes
556 diff diff repository (or selected files)
557 diff diff repository (or selected files)
557 export dump the header and diffs for one or more changesets
558 export dump the header and diffs for one or more changesets
558 forget forget the specified files on the next commit
559 forget forget the specified files on the next commit
559 init create a new repository in the given directory
560 init create a new repository in the given directory
560 log show revision history of entire repository or files
561 log show revision history of entire repository or files
561 merge merge working directory with another revision
562 merge merge working directory with another revision
562 pull pull changes from the specified source
563 pull pull changes from the specified source
563 push push changes to the specified destination
564 push push changes to the specified destination
564 remove remove the specified files on the next commit
565 remove remove the specified files on the next commit
565 serve start stand-alone webserver
566 serve start stand-alone webserver
566 status show changed files in the working directory
567 status show changed files in the working directory
567 summary summarize working directory state
568 summary summarize working directory state
568 update update working directory (or switch revisions)
569 update update working directory (or switch revisions)
569
570
570 use "hg help" for the full list of commands or "hg -v" for details
571 use "hg help" for the full list of commands or "hg -v" for details
571
572
572 $ hg skjdfks
573 $ hg skjdfks
573 hg: unknown command 'skjdfks'
574 hg: unknown command 'skjdfks'
574 Mercurial Distributed SCM
575 Mercurial Distributed SCM
575
576
576 basic commands:
577 basic commands:
577
578
578 add add the specified files on the next commit
579 add add the specified files on the next commit
579 annotate show changeset information by line for each file
580 annotate show changeset information by line for each file
580 clone make a copy of an existing repository
581 clone make a copy of an existing repository
581 commit commit the specified files or all outstanding changes
582 commit commit the specified files or all outstanding changes
582 diff diff repository (or selected files)
583 diff diff repository (or selected files)
583 export dump the header and diffs for one or more changesets
584 export dump the header and diffs for one or more changesets
584 forget forget the specified files on the next commit
585 forget forget the specified files on the next commit
585 init create a new repository in the given directory
586 init create a new repository in the given directory
586 log show revision history of entire repository or files
587 log show revision history of entire repository or files
587 merge merge working directory with another revision
588 merge merge working directory with another revision
588 pull pull changes from the specified source
589 pull pull changes from the specified source
589 push push changes to the specified destination
590 push push changes to the specified destination
590 remove remove the specified files on the next commit
591 remove remove the specified files on the next commit
591 serve start stand-alone webserver
592 serve start stand-alone webserver
592 status show changed files in the working directory
593 status show changed files in the working directory
593 summary summarize working directory state
594 summary summarize working directory state
594 update update working directory (or switch revisions)
595 update update working directory (or switch revisions)
595
596
596 use "hg help" for the full list of commands or "hg -v" for details
597 use "hg help" for the full list of commands or "hg -v" for details
597
598
598 $ cat > helpext.py <<EOF
599 $ cat > helpext.py <<EOF
599 > import os
600 > import os
600 > from mercurial import commands
601 > from mercurial import commands
601 >
602 >
602 > def nohelp(ui, *args, **kwargs):
603 > def nohelp(ui, *args, **kwargs):
603 > pass
604 > pass
604 >
605 >
605 > cmdtable = {
606 > cmdtable = {
606 > "nohelp": (nohelp, [], "hg nohelp"),
607 > "nohelp": (nohelp, [], "hg nohelp"),
607 > }
608 > }
608 >
609 >
609 > commands.norepo += ' nohelp'
610 > commands.norepo += ' nohelp'
610 > EOF
611 > EOF
611 $ echo '[extensions]' >> $HGRCPATH
612 $ echo '[extensions]' >> $HGRCPATH
612 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
613 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
613
614
614 Test command with no help text
615 Test command with no help text
615
616
616 $ hg help nohelp
617 $ hg help nohelp
617 hg nohelp
618 hg nohelp
618
619
619 (no help text available)
620 (no help text available)
620
621
621 use "hg -v help nohelp" to show global options
622 use "hg -v help nohelp" to show global options
622
623
623 Test that default list of commands omits extension commands
624 Test that default list of commands omits extension commands
624
625
625 $ hg help
626 $ hg help
626 Mercurial Distributed SCM
627 Mercurial Distributed SCM
627
628
628 list of commands:
629 list of commands:
629
630
630 add add the specified files on the next commit
631 add add the specified files on the next commit
631 addremove add all new files, delete all missing files
632 addremove add all new files, delete all missing files
632 annotate show changeset information by line for each file
633 annotate show changeset information by line for each file
633 archive create an unversioned archive of a repository revision
634 archive create an unversioned archive of a repository revision
634 backout reverse effect of earlier changeset
635 backout reverse effect of earlier changeset
635 bisect subdivision search of changesets
636 bisect subdivision search of changesets
636 branch set or show the current branch name
637 branch set or show the current branch name
637 branches list repository named branches
638 branches list repository named branches
638 bundle create a changegroup file
639 bundle create a changegroup file
639 cat output the current or given revision of files
640 cat output the current or given revision of files
640 clone make a copy of an existing repository
641 clone make a copy of an existing repository
641 commit commit the specified files or all outstanding changes
642 commit commit the specified files or all outstanding changes
642 copy mark files as copied for the next commit
643 copy mark files as copied for the next commit
643 diff diff repository (or selected files)
644 diff diff repository (or selected files)
644 export dump the header and diffs for one or more changesets
645 export dump the header and diffs for one or more changesets
645 forget forget the specified files on the next commit
646 forget forget the specified files on the next commit
646 grep search for a pattern in specified files and revisions
647 grep search for a pattern in specified files and revisions
647 heads show current repository heads or show branch heads
648 heads show current repository heads or show branch heads
648 help show help for a given topic or a help overview
649 help show help for a given topic or a help overview
649 identify identify the working copy or specified revision
650 identify identify the working copy or specified revision
650 import import an ordered set of patches
651 import import an ordered set of patches
651 incoming show new changesets found in source
652 incoming show new changesets found in source
652 init create a new repository in the given directory
653 init create a new repository in the given directory
653 locate locate files matching specific patterns
654 locate locate files matching specific patterns
654 log show revision history of entire repository or files
655 log show revision history of entire repository or files
655 manifest output the current or given revision of the project manifest
656 manifest output the current or given revision of the project manifest
656 merge merge working directory with another revision
657 merge merge working directory with another revision
657 outgoing show changesets not found in the destination
658 outgoing show changesets not found in the destination
658 parents show the parents of the working directory or revision
659 parents show the parents of the working directory or revision
659 paths show aliases for remote repositories
660 paths show aliases for remote repositories
660 pull pull changes from the specified source
661 pull pull changes from the specified source
661 push push changes to the specified destination
662 push push changes to the specified destination
662 recover roll back an interrupted transaction
663 recover roll back an interrupted transaction
663 remove remove the specified files on the next commit
664 remove remove the specified files on the next commit
664 rename rename files; equivalent of copy + remove
665 rename rename files; equivalent of copy + remove
665 resolve redo merges or set/view the merge status of files
666 resolve redo merges or set/view the merge status of files
666 revert restore individual files or directories to an earlier state
667 revert restore individual files or directories to an earlier state
667 rollback roll back the last transaction (dangerous)
668 rollback roll back the last transaction (dangerous)
668 root print the root (top) of the current working directory
669 root print the root (top) of the current working directory
669 serve start stand-alone webserver
670 serve start stand-alone webserver
670 showconfig show combined config settings from all hgrc files
671 showconfig show combined config settings from all hgrc files
671 status show changed files in the working directory
672 status show changed files in the working directory
672 summary summarize working directory state
673 summary summarize working directory state
673 tag add one or more tags for the current or given revision
674 tag add one or more tags for the current or given revision
674 tags list repository tags
675 tags list repository tags
675 tip show the tip revision
676 tip show the tip revision
676 unbundle apply one or more changegroup files
677 unbundle apply one or more changegroup files
677 update update working directory (or switch revisions)
678 update update working directory (or switch revisions)
678 verify verify the integrity of the repository
679 verify verify the integrity of the repository
679 version output version and copyright information
680 version output version and copyright information
680
681
681 enabled extensions:
682 enabled extensions:
682
683
683 helpext (no help text available)
684 helpext (no help text available)
684
685
685 additional help topics:
686 additional help topics:
686
687
687 config Configuration Files
688 config Configuration Files
688 dates Date Formats
689 dates Date Formats
689 patterns File Name Patterns
690 patterns File Name Patterns
690 environment Environment Variables
691 environment Environment Variables
691 revisions Specifying Single Revisions
692 revisions Specifying Single Revisions
692 multirevs Specifying Multiple Revisions
693 multirevs Specifying Multiple Revisions
693 revsets Specifying Revision Sets
694 revsets Specifying Revision Sets
694 diffs Diff Formats
695 diffs Diff Formats
695 templating Template Usage
696 templating Template Usage
696 urls URL Paths
697 urls URL Paths
697 extensions Using additional features
698 extensions Using additional features
698 hgweb Configuring hgweb
699 hgweb Configuring hgweb
699 glossary Glossary
700 glossary Glossary
700
701
701 use "hg -v help" to show aliases and global options
702 use "hg -v help" to show aliases and global options
702
703
703 Test list of commands with command with no help text
704 Test list of commands with command with no help text
704
705
705 $ hg help helpext
706 $ hg help helpext
706 helpext extension - no help text available
707 helpext extension - no help text available
707
708
708 list of commands:
709 list of commands:
709
710
710 nohelp (no help text available)
711 nohelp (no help text available)
711
712
712 use "hg -v help helpext" to show aliases and global options
713 use "hg -v help helpext" to show aliases and global options
713
714
714 Test a help topic
715 Test a help topic
715
716
716 $ hg help revs
717 $ hg help revs
717 Specifying Single Revisions
718 Specifying Single Revisions
718
719
719 Mercurial supports several ways to specify individual revisions.
720 Mercurial supports several ways to specify individual revisions.
720
721
721 A plain integer is treated as a revision number. Negative integers are
722 A plain integer is treated as a revision number. Negative integers are
722 treated as sequential offsets from the tip, with -1 denoting the tip, -2
723 treated as sequential offsets from the tip, with -1 denoting the tip, -2
723 denoting the revision prior to the tip, and so forth.
724 denoting the revision prior to the tip, and so forth.
724
725
725 A 40-digit hexadecimal string is treated as a unique revision identifier.
726 A 40-digit hexadecimal string is treated as a unique revision identifier.
726
727
727 A hexadecimal string less than 40 characters long is treated as a unique
728 A hexadecimal string less than 40 characters long is treated as a unique
728 revision identifier and is referred to as a short-form identifier. A
729 revision identifier and is referred to as a short-form identifier. A
729 short-form identifier is only valid if it is the prefix of exactly one
730 short-form identifier is only valid if it is the prefix of exactly one
730 full-length identifier.
731 full-length identifier.
731
732
732 Any other string is treated as a tag or branch name. A tag name is a
733 Any other string is treated as a tag or branch name. A tag name is a
733 symbolic name associated with a revision identifier. A branch name denotes
734 symbolic name associated with a revision identifier. A branch name denotes
734 the tipmost revision of that branch. Tag and branch names must not contain
735 the tipmost revision of that branch. Tag and branch names must not contain
735 the ":" character.
736 the ":" character.
736
737
737 The reserved name "tip" is a special tag that always identifies the most
738 The reserved name "tip" is a special tag that always identifies the most
738 recent revision.
739 recent revision.
739
740
740 The reserved name "null" indicates the null revision. This is the revision
741 The reserved name "null" indicates the null revision. This is the revision
741 of an empty repository, and the parent of revision 0.
742 of an empty repository, and the parent of revision 0.
742
743
743 The reserved name "." indicates the working directory parent. If no
744 The reserved name "." indicates the working directory parent. If no
744 working directory is checked out, it is equivalent to null. If an
745 working directory is checked out, it is equivalent to null. If an
745 uncommitted merge is in progress, "." is the revision of the first parent.
746 uncommitted merge is in progress, "." is the revision of the first parent.
@@ -1,97 +1,192 b''
1 Make status look into subrepositories by default:
1 Make status look into subrepositories by default:
2
2
3 $ echo '[defaults]' >> $HGRCPATH
3 $ echo '[defaults]' >> $HGRCPATH
4 $ echo 'status = -S' >> $HGRCPATH
4 $ echo 'status = -S' >> $HGRCPATH
5 $ echo 'diff = --nodates -S' >> $HGRCPATH
5
6
6 Create test repository:
7 Create test repository:
7
8
8 $ hg init
9 $ hg init
9 $ echo x1 > x.txt
10 $ echo x1 > x.txt
10 $ hg add x.txt
11 $ hg add x.txt
11
12
12 $ hg init foo
13 $ hg init foo
13 $ cd foo
14 $ cd foo
14 $ echo y1 > y.txt
15 $ echo y1 > y.txt
15 $ hg add y.txt
16 $ hg add y.txt
16
17
17 $ hg init bar
18 $ hg init bar
18 $ cd bar
19 $ cd bar
19 $ echo z1 > z.txt
20 $ echo z1 > z.txt
20 $ hg add z.txt
21 $ hg add z.txt
21
22
22 $ cd ..
23 $ cd ..
23 $ echo 'bar = bar' > .hgsub
24 $ echo 'bar = bar' > .hgsub
24 $ hg add .hgsub
25 $ hg add .hgsub
25
26
26 $ cd ..
27 $ cd ..
27 $ echo 'foo = foo' > .hgsub
28 $ echo 'foo = foo' > .hgsub
28 $ hg add .hgsub
29 $ hg add .hgsub
29
30
30 $ hg commit -m 0-0-0
31 $ hg commit -m 0-0-0
31 committing subrepository foo
32 committing subrepository foo
32 committing subrepository foo/bar
33 committing subrepository foo/bar
33
34
34 $ cd foo
35 $ cd foo
35 $ echo y2 >> y.txt
36 $ echo y2 >> y.txt
36 $ hg commit -m 0-1-0
37 $ hg commit -m 0-1-0
37
38
38 $ cd bar
39 $ cd bar
39 $ echo z2 >> z.txt
40 $ echo z2 >> z.txt
40 $ hg commit -m 0-1-1
41 $ hg commit -m 0-1-1
41
42
42 $ cd ..
43 $ cd ..
43 $ hg commit -m 0-2-1
44 $ hg commit -m 0-2-1
44 committing subrepository bar
45 committing subrepository bar
45
46
46 $ cd ..
47 $ cd ..
47 $ hg commit -m 1-2-1
48 $ hg commit -m 1-2-1
48 committing subrepository foo
49 committing subrepository foo
49
50
50 Change working directory:
51 Change working directory:
51
52
52 $ echo y3 >> foo/y.txt
53 $ echo y3 >> foo/y.txt
53 $ echo z3 >> foo/bar/z.txt
54 $ echo z3 >> foo/bar/z.txt
54 $ hg status
55 $ hg status
55 M foo/bar/z.txt
56 M foo/bar/z.txt
56 M foo/y.txt
57 M foo/y.txt
58 $ hg diff
59 diff -r d254738c5f5e foo/y.txt
60 --- a/foo/y.txt
61 +++ b/foo/y.txt
62 @@ -1,2 +1,3 @@
63 y1
64 y2
65 +y3
66 diff -r 9647f22de499 foo/bar/z.txt
67 --- a/foo/bar/z.txt
68 +++ b/foo/bar/z.txt
69 @@ -1,2 +1,3 @@
70 z1
71 z2
72 +z3
57
73
58 Status call crossing repository boundaries:
74 Status call crossing repository boundaries:
59
75
60 $ hg status foo/bar/z.txt
76 $ hg status foo/bar/z.txt
61 M foo/bar/z.txt
77 M foo/bar/z.txt
62 $ hg status -I 'foo/?.txt'
78 $ hg status -I 'foo/?.txt'
63 M foo/y.txt
79 M foo/y.txt
64 $ hg status -I '**/?.txt'
80 $ hg status -I '**/?.txt'
65 M foo/bar/z.txt
81 M foo/bar/z.txt
66 M foo/y.txt
82 M foo/y.txt
83 $ hg diff -I '**/?.txt'
84 diff -r d254738c5f5e foo/y.txt
85 --- a/foo/y.txt
86 +++ b/foo/y.txt
87 @@ -1,2 +1,3 @@
88 y1
89 y2
90 +y3
91 diff -r 9647f22de499 foo/bar/z.txt
92 --- a/foo/bar/z.txt
93 +++ b/foo/bar/z.txt
94 @@ -1,2 +1,3 @@
95 z1
96 z2
97 +z3
67
98
68 Status from within a subdirectory:
99 Status from within a subdirectory:
69
100
70 $ mkdir dir
101 $ mkdir dir
71 $ cd dir
102 $ cd dir
72 $ echo a1 > a.txt
103 $ echo a1 > a.txt
73 $ hg status
104 $ hg status
74 M foo/bar/z.txt
105 M foo/bar/z.txt
75 M foo/y.txt
106 M foo/y.txt
76 ? dir/a.txt
107 ? dir/a.txt
108 $ hg diff
109 diff -r d254738c5f5e foo/y.txt
110 --- a/foo/y.txt
111 +++ b/foo/y.txt
112 @@ -1,2 +1,3 @@
113 y1
114 y2
115 +y3
116 diff -r 9647f22de499 foo/bar/z.txt
117 --- a/foo/bar/z.txt
118 +++ b/foo/bar/z.txt
119 @@ -1,2 +1,3 @@
120 z1
121 z2
122 +z3
77
123
78 Status with relative path:
124 Status with relative path:
79
125
80 $ hg status ..
126 $ hg status ..
81 M ../foo/bar/z.txt
127 M ../foo/bar/z.txt
82 M ../foo/y.txt
128 M ../foo/y.txt
83 ? a.txt
129 ? a.txt
130 $ hg diff ..
131 diff -r d254738c5f5e foo/y.txt
132 --- a/foo/y.txt
133 +++ b/foo/y.txt
134 @@ -1,2 +1,3 @@
135 y1
136 y2
137 +y3
138 diff -r 9647f22de499 foo/bar/z.txt
139 --- a/foo/bar/z.txt
140 +++ b/foo/bar/z.txt
141 @@ -1,2 +1,3 @@
142 z1
143 z2
144 +z3
84 $ cd ..
145 $ cd ..
85
146
147 Cleanup and final commit:
148
149 $ rm -r dir
150 $ hg commit -m 2-3-2
151 committing subrepository foo
152 committing subrepository foo/bar
153
154 Log with the relationships between repo and its subrepo:
155
156 $ hg log --template '{rev}:{node|short} {desc}\n'
157 2:1326fa26d0c0 2-3-2
158 1:4b3c9ff4f66b 1-2-1
159 0:23376cbba0d8 0-0-0
160
161 $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
162 3:65903cebad86 2-3-2
163 2:d254738c5f5e 0-2-1
164 1:8629ce7dcc39 0-1-0
165 0:af048e97ade2 0-0-0
166
167 $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
168 2:31ecbdafd357 2-3-2
169 1:9647f22de499 0-1-1
170 0:4904098473f9 0-0-0
171
86 Status between revisions:
172 Status between revisions:
87
173
88 $ rm -r dir
89 $ hg commit -m 2-2-1
90 committing subrepository foo
91 committing subrepository foo/bar
92 $ hg status
174 $ hg status
93 $ hg status --rev 0:1
175 $ hg status --rev 0:1
94 M .hgsubstate
176 M .hgsubstate
95 M foo/.hgsubstate
177 M foo/.hgsubstate
96 M foo/bar/z.txt
178 M foo/bar/z.txt
97 M foo/y.txt
179 M foo/y.txt
180 $ hg diff -I '**/?.txt' --rev 0:1
181 diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
182 --- a/foo/y.txt
183 +++ b/foo/y.txt
184 @@ -1,1 +1,2 @@
185 y1
186 +y2
187 diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
188 --- a/foo/bar/z.txt
189 +++ b/foo/bar/z.txt
190 @@ -1,1 +1,2 @@
191 z1
192 +z2
General Comments 0
You need to be logged in to leave comments. Login now