##// END OF EJS Templates
merge with stable
Matt Mackall -
r16232:877aea86 merge default
parent child Browse files
Show More
@@ -1,197 +1,197 b''
1 # churn.py - create a graph of revisions count grouped by template
1 # churn.py - create a graph of revisions count grouped by template
2 #
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
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 '''command to display statistics about repository history'''
9 '''command to display statistics about repository history'''
10
10
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial import patch, cmdutil, scmutil, util, templater, commands
12 from mercurial import patch, cmdutil, scmutil, util, templater, commands
13 import os
13 import os
14 import time, datetime
14 import time, datetime
15
15
16 def maketemplater(ui, repo, tmpl):
16 def maketemplater(ui, repo, tmpl):
17 tmpl = templater.parsestring(tmpl, quoted=False)
17 tmpl = templater.parsestring(tmpl, quoted=False)
18 try:
18 try:
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
19 t = cmdutil.changeset_templater(ui, repo, False, None, None, False)
20 except SyntaxError, inst:
20 except SyntaxError, inst:
21 raise util.Abort(inst.args[0])
21 raise util.Abort(inst.args[0])
22 t.use_template(tmpl)
22 t.use_template(tmpl)
23 return t
23 return t
24
24
25 def changedlines(ui, repo, ctx1, ctx2, fns):
25 def changedlines(ui, repo, ctx1, ctx2, fns):
26 added, removed = 0, 0
26 added, removed = 0, 0
27 fmatch = scmutil.matchfiles(repo, fns)
27 fmatch = scmutil.matchfiles(repo, fns)
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
28 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
29 for l in diff.split('\n'):
29 for l in diff.split('\n'):
30 if l.startswith("+") and not l.startswith("+++ "):
30 if l.startswith("+") and not l.startswith("+++ "):
31 added += 1
31 added += 1
32 elif l.startswith("-") and not l.startswith("--- "):
32 elif l.startswith("-") and not l.startswith("--- "):
33 removed += 1
33 removed += 1
34 return (added, removed)
34 return (added, removed)
35
35
36 def countrate(ui, repo, amap, *pats, **opts):
36 def countrate(ui, repo, amap, *pats, **opts):
37 """Calculate stats"""
37 """Calculate stats"""
38 if opts.get('dateformat'):
38 if opts.get('dateformat'):
39 def getkey(ctx):
39 def getkey(ctx):
40 t, tz = ctx.date()
40 t, tz = ctx.date()
41 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
41 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
42 return date.strftime(opts['dateformat'])
42 return date.strftime(opts['dateformat'])
43 else:
43 else:
44 tmpl = opts.get('template', '{author|email}')
44 tmpl = opts.get('template', '{author|email}')
45 tmpl = maketemplater(ui, repo, tmpl)
45 tmpl = maketemplater(ui, repo, tmpl)
46 def getkey(ctx):
46 def getkey(ctx):
47 ui.pushbuffer()
47 ui.pushbuffer()
48 tmpl.show(ctx)
48 tmpl.show(ctx)
49 return ui.popbuffer()
49 return ui.popbuffer()
50
50
51 state = {'count': 0}
51 state = {'count': 0}
52 rate = {}
52 rate = {}
53 df = False
53 df = False
54 if opts.get('date'):
54 if opts.get('date'):
55 df = util.matchdate(opts['date'])
55 df = util.matchdate(opts['date'])
56
56
57 m = scmutil.match(repo[None], pats, opts)
57 m = scmutil.match(repo[None], pats, opts)
58 def prep(ctx, fns):
58 def prep(ctx, fns):
59 rev = ctx.rev()
59 rev = ctx.rev()
60 if df and not df(ctx.date()[0]): # doesn't match date format
60 if df and not df(ctx.date()[0]): # doesn't match date format
61 return
61 return
62
62
63 key = getkey(ctx).strip()
63 key = getkey(ctx).strip()
64 key = amap.get(key, key) # alias remap
64 key = amap.get(key, key) # alias remap
65 if opts.get('changesets'):
65 if opts.get('changesets'):
66 rate[key] = (rate.get(key, (0,))[0] + 1, 0)
66 rate[key] = (rate.get(key, (0,))[0] + 1, 0)
67 else:
67 else:
68 parents = ctx.parents()
68 parents = ctx.parents()
69 if len(parents) > 1:
69 if len(parents) > 1:
70 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
70 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
71 return
71 return
72
72
73 ctx1 = parents[0]
73 ctx1 = parents[0]
74 lines = changedlines(ui, repo, ctx1, ctx, fns)
74 lines = changedlines(ui, repo, ctx1, ctx, fns)
75 rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
75 rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
76
76
77 state['count'] += 1
77 state['count'] += 1
78 ui.progress(_('analyzing'), state['count'], total=len(repo))
78 ui.progress(_('analyzing'), state['count'], total=len(repo))
79
79
80 for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
80 for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
81 continue
81 continue
82
82
83 ui.progress(_('analyzing'), None)
83 ui.progress(_('analyzing'), None)
84
84
85 return rate
85 return rate
86
86
87
87
88 def churn(ui, repo, *pats, **opts):
88 def churn(ui, repo, *pats, **opts):
89 '''histogram of changes to the repository
89 '''histogram of changes to the repository
90
90
91 This command will display a histogram representing the number
91 This command will display a histogram representing the number
92 of changed lines or revisions, grouped according to the given
92 of changed lines or revisions, grouped according to the given
93 template. The default template will group changes by author.
93 template. The default template will group changes by author.
94 The --dateformat option may be used to group the results by
94 The --dateformat option may be used to group the results by
95 date instead.
95 date instead.
96
96
97 Statistics are based on the number of changed lines, or
97 Statistics are based on the number of changed lines, or
98 alternatively the number of matching revisions if the
98 alternatively the number of matching revisions if the
99 --changesets option is specified.
99 --changesets option is specified.
100
100
101 Examples::
101 Examples::
102
102
103 # display count of changed lines for every committer
103 # display count of changed lines for every committer
104 hg churn -t '{author|email}'
104 hg churn -t '{author|email}'
105
105
106 # display daily activity graph
106 # display daily activity graph
107 hg churn -f '%H' -s -c
107 hg churn -f '%H' -s -c
108
108
109 # display activity of developers by month
109 # display activity of developers by month
110 hg churn -f '%Y-%m' -s -c
110 hg churn -f '%Y-%m' -s -c
111
111
112 # display count of lines changed in every year
112 # display count of lines changed in every year
113 hg churn -f '%Y' -s
113 hg churn -f '%Y' -s
114
114
115 It is possible to map alternate email addresses to a main address
115 It is possible to map alternate email addresses to a main address
116 by providing a file using the following format::
116 by providing a file using the following format::
117
117
118 <alias email> = <actual email>
118 <alias email> = <actual email>
119
119
120 Such a file may be specified with the --aliases option, otherwise
120 Such a file may be specified with the --aliases option, otherwise
121 a .hgchurn file will be looked for in the working directory root.
121 a .hgchurn file will be looked for in the working directory root.
122 '''
122 '''
123 def pad(s, l):
123 def pad(s, l):
124 return (s + " " * l)[:l]
124 return (s + " " * l)[:l]
125
125
126 amap = {}
126 amap = {}
127 aliases = opts.get('aliases')
127 aliases = opts.get('aliases')
128 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
128 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
129 aliases = repo.wjoin('.hgchurn')
129 aliases = repo.wjoin('.hgchurn')
130 if aliases:
130 if aliases:
131 for l in open(aliases, "r"):
131 for l in open(aliases, "r"):
132 try:
132 try:
133 alias, actual = l.split('=' in l and '=' or None, 1)
133 alias, actual = l.split('=' in l and '=' or None, 1)
134 amap[alias.strip()] = actual.strip()
134 amap[alias.strip()] = actual.strip()
135 except ValueError:
135 except ValueError:
136 l = l.strip()
136 l = l.strip()
137 if l:
137 if l:
138 ui.warn(_("skipping malformed alias: %s\n" % l))
138 ui.warn(_("skipping malformed alias: %s\n") % l)
139 continue
139 continue
140
140
141 rate = countrate(ui, repo, amap, *pats, **opts).items()
141 rate = countrate(ui, repo, amap, *pats, **opts).items()
142 if not rate:
142 if not rate:
143 return
143 return
144
144
145 sortkey = ((not opts.get('sort')) and (lambda x: -sum(x[1])) or None)
145 sortkey = ((not opts.get('sort')) and (lambda x: -sum(x[1])) or None)
146 rate.sort(key=sortkey)
146 rate.sort(key=sortkey)
147
147
148 # Be careful not to have a zero maxcount (issue833)
148 # Be careful not to have a zero maxcount (issue833)
149 maxcount = float(max(sum(v) for k, v in rate)) or 1.0
149 maxcount = float(max(sum(v) for k, v in rate)) or 1.0
150 maxname = max(len(k) for k, v in rate)
150 maxname = max(len(k) for k, v in rate)
151
151
152 ttywidth = ui.termwidth()
152 ttywidth = ui.termwidth()
153 ui.debug("assuming %i character terminal\n" % ttywidth)
153 ui.debug("assuming %i character terminal\n" % ttywidth)
154 width = ttywidth - maxname - 2 - 2 - 2
154 width = ttywidth - maxname - 2 - 2 - 2
155
155
156 if opts.get('diffstat'):
156 if opts.get('diffstat'):
157 width -= 15
157 width -= 15
158 def format(name, diffstat):
158 def format(name, diffstat):
159 added, removed = diffstat
159 added, removed = diffstat
160 return "%s %15s %s%s\n" % (pad(name, maxname),
160 return "%s %15s %s%s\n" % (pad(name, maxname),
161 '+%d/-%d' % (added, removed),
161 '+%d/-%d' % (added, removed),
162 ui.label('+' * charnum(added),
162 ui.label('+' * charnum(added),
163 'diffstat.inserted'),
163 'diffstat.inserted'),
164 ui.label('-' * charnum(removed),
164 ui.label('-' * charnum(removed),
165 'diffstat.deleted'))
165 'diffstat.deleted'))
166 else:
166 else:
167 width -= 6
167 width -= 6
168 def format(name, count):
168 def format(name, count):
169 return "%s %6d %s\n" % (pad(name, maxname), sum(count),
169 return "%s %6d %s\n" % (pad(name, maxname), sum(count),
170 '*' * charnum(sum(count)))
170 '*' * charnum(sum(count)))
171
171
172 def charnum(count):
172 def charnum(count):
173 return int(round(count * width / maxcount))
173 return int(round(count * width / maxcount))
174
174
175 for name, count in rate:
175 for name, count in rate:
176 ui.write(format(name, count))
176 ui.write(format(name, count))
177
177
178
178
179 cmdtable = {
179 cmdtable = {
180 "churn":
180 "churn":
181 (churn,
181 (churn,
182 [('r', 'rev', [],
182 [('r', 'rev', [],
183 _('count rate for the specified revision or range'), _('REV')),
183 _('count rate for the specified revision or range'), _('REV')),
184 ('d', 'date', '',
184 ('d', 'date', '',
185 _('count rate for revisions matching date spec'), _('DATE')),
185 _('count rate for revisions matching date spec'), _('DATE')),
186 ('t', 'template', '{author|email}',
186 ('t', 'template', '{author|email}',
187 _('template to group changesets'), _('TEMPLATE')),
187 _('template to group changesets'), _('TEMPLATE')),
188 ('f', 'dateformat', '',
188 ('f', 'dateformat', '',
189 _('strftime-compatible format for grouping by date'), _('FORMAT')),
189 _('strftime-compatible format for grouping by date'), _('FORMAT')),
190 ('c', 'changesets', False, _('count rate by number of changesets')),
190 ('c', 'changesets', False, _('count rate by number of changesets')),
191 ('s', 'sort', False, _('sort by key (default: sort by count)')),
191 ('s', 'sort', False, _('sort by key (default: sort by count)')),
192 ('', 'diffstat', False, _('display added/removed lines separately')),
192 ('', 'diffstat', False, _('display added/removed lines separately')),
193 ('', 'aliases', '',
193 ('', 'aliases', '',
194 _('file with email aliases'), _('FILE')),
194 _('file with email aliases'), _('FILE')),
195 ] + commands.walkopts,
195 ] + commands.walkopts,
196 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]")),
196 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]")),
197 }
197 }
@@ -1,360 +1,360 b''
1 # monotone.py - monotone support for the convert extension
1 # monotone.py - monotone support for the convert extension
2 #
2 #
3 # Copyright 2008, 2009 Mikkel Fahnoe Jorgensen <mikkel@dvide.com> and
3 # Copyright 2008, 2009 Mikkel Fahnoe Jorgensen <mikkel@dvide.com> and
4 # others
4 # others
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 os, re
9 import os, re
10 from mercurial import util
10 from mercurial import util
11 from common import NoRepo, commit, converter_source, checktool
11 from common import NoRepo, commit, converter_source, checktool
12 from common import commandline
12 from common import commandline
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14
14
15 class monotone_source(converter_source, commandline):
15 class monotone_source(converter_source, commandline):
16 def __init__(self, ui, path=None, rev=None):
16 def __init__(self, ui, path=None, rev=None):
17 converter_source.__init__(self, ui, path, rev)
17 converter_source.__init__(self, ui, path, rev)
18 commandline.__init__(self, ui, 'mtn')
18 commandline.__init__(self, ui, 'mtn')
19
19
20 self.ui = ui
20 self.ui = ui
21 self.path = path
21 self.path = path
22 self.automatestdio = False
22 self.automatestdio = False
23 self.rev = rev
23 self.rev = rev
24
24
25 norepo = NoRepo(_("%s does not look like a monotone repository")
25 norepo = NoRepo(_("%s does not look like a monotone repository")
26 % path)
26 % path)
27 if not os.path.exists(os.path.join(path, '_MTN')):
27 if not os.path.exists(os.path.join(path, '_MTN')):
28 # Could be a monotone repository (SQLite db file)
28 # Could be a monotone repository (SQLite db file)
29 try:
29 try:
30 f = file(path, 'rb')
30 f = file(path, 'rb')
31 header = f.read(16)
31 header = f.read(16)
32 f.close()
32 f.close()
33 except:
33 except:
34 header = ''
34 header = ''
35 if header != 'SQLite format 3\x00':
35 if header != 'SQLite format 3\x00':
36 raise norepo
36 raise norepo
37
37
38 # regular expressions for parsing monotone output
38 # regular expressions for parsing monotone output
39 space = r'\s*'
39 space = r'\s*'
40 name = r'\s+"((?:\\"|[^"])*)"\s*'
40 name = r'\s+"((?:\\"|[^"])*)"\s*'
41 value = name
41 value = name
42 revision = r'\s+\[(\w+)\]\s*'
42 revision = r'\s+\[(\w+)\]\s*'
43 lines = r'(?:.|\n)+'
43 lines = r'(?:.|\n)+'
44
44
45 self.dir_re = re.compile(space + "dir" + name)
45 self.dir_re = re.compile(space + "dir" + name)
46 self.file_re = re.compile(space + "file" + name +
46 self.file_re = re.compile(space + "file" + name +
47 "content" + revision)
47 "content" + revision)
48 self.add_file_re = re.compile(space + "add_file" + name +
48 self.add_file_re = re.compile(space + "add_file" + name +
49 "content" + revision)
49 "content" + revision)
50 self.patch_re = re.compile(space + "patch" + name +
50 self.patch_re = re.compile(space + "patch" + name +
51 "from" + revision + "to" + revision)
51 "from" + revision + "to" + revision)
52 self.rename_re = re.compile(space + "rename" + name + "to" + name)
52 self.rename_re = re.compile(space + "rename" + name + "to" + name)
53 self.delete_re = re.compile(space + "delete" + name)
53 self.delete_re = re.compile(space + "delete" + name)
54 self.tag_re = re.compile(space + "tag" + name + "revision" +
54 self.tag_re = re.compile(space + "tag" + name + "revision" +
55 revision)
55 revision)
56 self.cert_re = re.compile(lines + space + "name" + name +
56 self.cert_re = re.compile(lines + space + "name" + name +
57 "value" + value)
57 "value" + value)
58
58
59 attr = space + "file" + lines + space + "attr" + space
59 attr = space + "file" + lines + space + "attr" + space
60 self.attr_execute_re = re.compile(attr + '"mtn:execute"' +
60 self.attr_execute_re = re.compile(attr + '"mtn:execute"' +
61 space + '"true"')
61 space + '"true"')
62
62
63 # cached data
63 # cached data
64 self.manifest_rev = None
64 self.manifest_rev = None
65 self.manifest = None
65 self.manifest = None
66 self.files = None
66 self.files = None
67 self.dirs = None
67 self.dirs = None
68
68
69 checktool('mtn', abort=False)
69 checktool('mtn', abort=False)
70
70
71 def mtnrun(self, *args, **kwargs):
71 def mtnrun(self, *args, **kwargs):
72 if self.automatestdio:
72 if self.automatestdio:
73 return self.mtnrunstdio(*args, **kwargs)
73 return self.mtnrunstdio(*args, **kwargs)
74 else:
74 else:
75 return self.mtnrunsingle(*args, **kwargs)
75 return self.mtnrunsingle(*args, **kwargs)
76
76
77 def mtnrunsingle(self, *args, **kwargs):
77 def mtnrunsingle(self, *args, **kwargs):
78 kwargs['d'] = self.path
78 kwargs['d'] = self.path
79 return self.run0('automate', *args, **kwargs)
79 return self.run0('automate', *args, **kwargs)
80
80
81 def mtnrunstdio(self, *args, **kwargs):
81 def mtnrunstdio(self, *args, **kwargs):
82 # Prepare the command in automate stdio format
82 # Prepare the command in automate stdio format
83 command = []
83 command = []
84 for k, v in kwargs.iteritems():
84 for k, v in kwargs.iteritems():
85 command.append("%s:%s" % (len(k), k))
85 command.append("%s:%s" % (len(k), k))
86 if v:
86 if v:
87 command.append("%s:%s" % (len(v), v))
87 command.append("%s:%s" % (len(v), v))
88 if command:
88 if command:
89 command.insert(0, 'o')
89 command.insert(0, 'o')
90 command.append('e')
90 command.append('e')
91
91
92 command.append('l')
92 command.append('l')
93 for arg in args:
93 for arg in args:
94 command += "%s:%s" % (len(arg), arg)
94 command += "%s:%s" % (len(arg), arg)
95 command.append('e')
95 command.append('e')
96 command = ''.join(command)
96 command = ''.join(command)
97
97
98 self.ui.debug("mtn: sending '%s'\n" % command)
98 self.ui.debug("mtn: sending '%s'\n" % command)
99 self.mtnwritefp.write(command)
99 self.mtnwritefp.write(command)
100 self.mtnwritefp.flush()
100 self.mtnwritefp.flush()
101
101
102 return self.mtnstdioreadcommandoutput(command)
102 return self.mtnstdioreadcommandoutput(command)
103
103
104 def mtnstdioreadpacket(self):
104 def mtnstdioreadpacket(self):
105 read = None
105 read = None
106 commandnbr = ''
106 commandnbr = ''
107 while read != ':':
107 while read != ':':
108 read = self.mtnreadfp.read(1)
108 read = self.mtnreadfp.read(1)
109 if not read:
109 if not read:
110 raise util.Abort(_('bad mtn packet - no end of commandnbr'))
110 raise util.Abort(_('bad mtn packet - no end of commandnbr'))
111 commandnbr += read
111 commandnbr += read
112 commandnbr = commandnbr[:-1]
112 commandnbr = commandnbr[:-1]
113
113
114 stream = self.mtnreadfp.read(1)
114 stream = self.mtnreadfp.read(1)
115 if stream not in 'mewptl':
115 if stream not in 'mewptl':
116 raise util.Abort(_('bad mtn packet - bad stream type %s' % stream))
116 raise util.Abort(_('bad mtn packet - bad stream type %s') % stream)
117
117
118 read = self.mtnreadfp.read(1)
118 read = self.mtnreadfp.read(1)
119 if read != ':':
119 if read != ':':
120 raise util.Abort(_('bad mtn packet - no divider before size'))
120 raise util.Abort(_('bad mtn packet - no divider before size'))
121
121
122 read = None
122 read = None
123 lengthstr = ''
123 lengthstr = ''
124 while read != ':':
124 while read != ':':
125 read = self.mtnreadfp.read(1)
125 read = self.mtnreadfp.read(1)
126 if not read:
126 if not read:
127 raise util.Abort(_('bad mtn packet - no end of packet size'))
127 raise util.Abort(_('bad mtn packet - no end of packet size'))
128 lengthstr += read
128 lengthstr += read
129 try:
129 try:
130 length = long(lengthstr[:-1])
130 length = long(lengthstr[:-1])
131 except TypeError:
131 except TypeError:
132 raise util.Abort(_('bad mtn packet - bad packet size %s')
132 raise util.Abort(_('bad mtn packet - bad packet size %s')
133 % lengthstr)
133 % lengthstr)
134
134
135 read = self.mtnreadfp.read(length)
135 read = self.mtnreadfp.read(length)
136 if len(read) != length:
136 if len(read) != length:
137 raise util.Abort(_("bad mtn packet - unable to read full packet "
137 raise util.Abort(_("bad mtn packet - unable to read full packet "
138 "read %s of %s") % (len(read), length))
138 "read %s of %s") % (len(read), length))
139
139
140 return (commandnbr, stream, length, read)
140 return (commandnbr, stream, length, read)
141
141
142 def mtnstdioreadcommandoutput(self, command):
142 def mtnstdioreadcommandoutput(self, command):
143 retval = []
143 retval = []
144 while True:
144 while True:
145 commandnbr, stream, length, output = self.mtnstdioreadpacket()
145 commandnbr, stream, length, output = self.mtnstdioreadpacket()
146 self.ui.debug('mtn: read packet %s:%s:%s\n' %
146 self.ui.debug('mtn: read packet %s:%s:%s\n' %
147 (commandnbr, stream, length))
147 (commandnbr, stream, length))
148
148
149 if stream == 'l':
149 if stream == 'l':
150 # End of command
150 # End of command
151 if output != '0':
151 if output != '0':
152 raise util.Abort(_("mtn command '%s' returned %s") %
152 raise util.Abort(_("mtn command '%s' returned %s") %
153 (command, output))
153 (command, output))
154 break
154 break
155 elif stream in 'ew':
155 elif stream in 'ew':
156 # Error, warning output
156 # Error, warning output
157 self.ui.warn(_('%s error:\n') % self.command)
157 self.ui.warn(_('%s error:\n') % self.command)
158 self.ui.warn(output)
158 self.ui.warn(output)
159 elif stream == 'p':
159 elif stream == 'p':
160 # Progress messages
160 # Progress messages
161 self.ui.debug('mtn: ' + output)
161 self.ui.debug('mtn: ' + output)
162 elif stream == 'm':
162 elif stream == 'm':
163 # Main stream - command output
163 # Main stream - command output
164 retval.append(output)
164 retval.append(output)
165
165
166 return ''.join(retval)
166 return ''.join(retval)
167
167
168 def mtnloadmanifest(self, rev):
168 def mtnloadmanifest(self, rev):
169 if self.manifest_rev == rev:
169 if self.manifest_rev == rev:
170 return
170 return
171 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
171 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
172 self.manifest_rev = rev
172 self.manifest_rev = rev
173 self.files = {}
173 self.files = {}
174 self.dirs = {}
174 self.dirs = {}
175
175
176 for e in self.manifest:
176 for e in self.manifest:
177 m = self.file_re.match(e)
177 m = self.file_re.match(e)
178 if m:
178 if m:
179 attr = ""
179 attr = ""
180 name = m.group(1)
180 name = m.group(1)
181 node = m.group(2)
181 node = m.group(2)
182 if self.attr_execute_re.match(e):
182 if self.attr_execute_re.match(e):
183 attr += "x"
183 attr += "x"
184 self.files[name] = (node, attr)
184 self.files[name] = (node, attr)
185 m = self.dir_re.match(e)
185 m = self.dir_re.match(e)
186 if m:
186 if m:
187 self.dirs[m.group(1)] = True
187 self.dirs[m.group(1)] = True
188
188
189 def mtnisfile(self, name, rev):
189 def mtnisfile(self, name, rev):
190 # a non-file could be a directory or a deleted or renamed file
190 # a non-file could be a directory or a deleted or renamed file
191 self.mtnloadmanifest(rev)
191 self.mtnloadmanifest(rev)
192 return name in self.files
192 return name in self.files
193
193
194 def mtnisdir(self, name, rev):
194 def mtnisdir(self, name, rev):
195 self.mtnloadmanifest(rev)
195 self.mtnloadmanifest(rev)
196 return name in self.dirs
196 return name in self.dirs
197
197
198 def mtngetcerts(self, rev):
198 def mtngetcerts(self, rev):
199 certs = {"author":"<missing>", "date":"<missing>",
199 certs = {"author":"<missing>", "date":"<missing>",
200 "changelog":"<missing>", "branch":"<missing>"}
200 "changelog":"<missing>", "branch":"<missing>"}
201 certlist = self.mtnrun("certs", rev)
201 certlist = self.mtnrun("certs", rev)
202 # mtn < 0.45:
202 # mtn < 0.45:
203 # key "test@selenic.com"
203 # key "test@selenic.com"
204 # mtn >= 0.45:
204 # mtn >= 0.45:
205 # key [ff58a7ffb771907c4ff68995eada1c4da068d328]
205 # key [ff58a7ffb771907c4ff68995eada1c4da068d328]
206 certlist = re.split('\n\n key ["\[]', certlist)
206 certlist = re.split('\n\n key ["\[]', certlist)
207 for e in certlist:
207 for e in certlist:
208 m = self.cert_re.match(e)
208 m = self.cert_re.match(e)
209 if m:
209 if m:
210 name, value = m.groups()
210 name, value = m.groups()
211 value = value.replace(r'\"', '"')
211 value = value.replace(r'\"', '"')
212 value = value.replace(r'\\', '\\')
212 value = value.replace(r'\\', '\\')
213 certs[name] = value
213 certs[name] = value
214 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
214 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
215 # and all times are stored in UTC
215 # and all times are stored in UTC
216 certs["date"] = certs["date"].split('.')[0] + " UTC"
216 certs["date"] = certs["date"].split('.')[0] + " UTC"
217 return certs
217 return certs
218
218
219 # implement the converter_source interface:
219 # implement the converter_source interface:
220
220
221 def getheads(self):
221 def getheads(self):
222 if not self.rev:
222 if not self.rev:
223 return self.mtnrun("leaves").splitlines()
223 return self.mtnrun("leaves").splitlines()
224 else:
224 else:
225 return [self.rev]
225 return [self.rev]
226
226
227 def getchanges(self, rev):
227 def getchanges(self, rev):
228 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
228 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
229 revision = self.mtnrun("get_revision", rev).split("\n\n")
229 revision = self.mtnrun("get_revision", rev).split("\n\n")
230 files = {}
230 files = {}
231 ignoremove = {}
231 ignoremove = {}
232 renameddirs = []
232 renameddirs = []
233 copies = {}
233 copies = {}
234 for e in revision:
234 for e in revision:
235 m = self.add_file_re.match(e)
235 m = self.add_file_re.match(e)
236 if m:
236 if m:
237 files[m.group(1)] = rev
237 files[m.group(1)] = rev
238 ignoremove[m.group(1)] = rev
238 ignoremove[m.group(1)] = rev
239 m = self.patch_re.match(e)
239 m = self.patch_re.match(e)
240 if m:
240 if m:
241 files[m.group(1)] = rev
241 files[m.group(1)] = rev
242 # Delete/rename is handled later when the convert engine
242 # Delete/rename is handled later when the convert engine
243 # discovers an IOError exception from getfile,
243 # discovers an IOError exception from getfile,
244 # but only if we add the "from" file to the list of changes.
244 # but only if we add the "from" file to the list of changes.
245 m = self.delete_re.match(e)
245 m = self.delete_re.match(e)
246 if m:
246 if m:
247 files[m.group(1)] = rev
247 files[m.group(1)] = rev
248 m = self.rename_re.match(e)
248 m = self.rename_re.match(e)
249 if m:
249 if m:
250 toname = m.group(2)
250 toname = m.group(2)
251 fromname = m.group(1)
251 fromname = m.group(1)
252 if self.mtnisfile(toname, rev):
252 if self.mtnisfile(toname, rev):
253 ignoremove[toname] = 1
253 ignoremove[toname] = 1
254 copies[toname] = fromname
254 copies[toname] = fromname
255 files[toname] = rev
255 files[toname] = rev
256 files[fromname] = rev
256 files[fromname] = rev
257 elif self.mtnisdir(toname, rev):
257 elif self.mtnisdir(toname, rev):
258 renameddirs.append((fromname, toname))
258 renameddirs.append((fromname, toname))
259
259
260 # Directory renames can be handled only once we have recorded
260 # Directory renames can be handled only once we have recorded
261 # all new files
261 # all new files
262 for fromdir, todir in renameddirs:
262 for fromdir, todir in renameddirs:
263 renamed = {}
263 renamed = {}
264 for tofile in self.files:
264 for tofile in self.files:
265 if tofile in ignoremove:
265 if tofile in ignoremove:
266 continue
266 continue
267 if tofile.startswith(todir + '/'):
267 if tofile.startswith(todir + '/'):
268 renamed[tofile] = fromdir + tofile[len(todir):]
268 renamed[tofile] = fromdir + tofile[len(todir):]
269 # Avoid chained moves like:
269 # Avoid chained moves like:
270 # d1(/a) => d3/d1(/a)
270 # d1(/a) => d3/d1(/a)
271 # d2 => d3
271 # d2 => d3
272 ignoremove[tofile] = 1
272 ignoremove[tofile] = 1
273 for tofile, fromfile in renamed.items():
273 for tofile, fromfile in renamed.items():
274 self.ui.debug (_("copying file in renamed directory "
274 self.ui.debug (_("copying file in renamed directory "
275 "from '%s' to '%s'")
275 "from '%s' to '%s'")
276 % (fromfile, tofile), '\n')
276 % (fromfile, tofile), '\n')
277 files[tofile] = rev
277 files[tofile] = rev
278 copies[tofile] = fromfile
278 copies[tofile] = fromfile
279 for fromfile in renamed.values():
279 for fromfile in renamed.values():
280 files[fromfile] = rev
280 files[fromfile] = rev
281
281
282 return (files.items(), copies)
282 return (files.items(), copies)
283
283
284 def getfile(self, name, rev):
284 def getfile(self, name, rev):
285 if not self.mtnisfile(name, rev):
285 if not self.mtnisfile(name, rev):
286 raise IOError() # file was deleted or renamed
286 raise IOError() # file was deleted or renamed
287 try:
287 try:
288 data = self.mtnrun("get_file_of", name, r=rev)
288 data = self.mtnrun("get_file_of", name, r=rev)
289 except:
289 except:
290 raise IOError() # file was deleted or renamed
290 raise IOError() # file was deleted or renamed
291 self.mtnloadmanifest(rev)
291 self.mtnloadmanifest(rev)
292 node, attr = self.files.get(name, (None, ""))
292 node, attr = self.files.get(name, (None, ""))
293 return data, attr
293 return data, attr
294
294
295 def getcommit(self, rev):
295 def getcommit(self, rev):
296 extra = {}
296 extra = {}
297 certs = self.mtngetcerts(rev)
297 certs = self.mtngetcerts(rev)
298 if certs.get('suspend') == certs["branch"]:
298 if certs.get('suspend') == certs["branch"]:
299 extra['close'] = '1'
299 extra['close'] = '1'
300 return commit(
300 return commit(
301 author=certs["author"],
301 author=certs["author"],
302 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
302 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
303 desc=certs["changelog"],
303 desc=certs["changelog"],
304 rev=rev,
304 rev=rev,
305 parents=self.mtnrun("parents", rev).splitlines(),
305 parents=self.mtnrun("parents", rev).splitlines(),
306 branch=certs["branch"],
306 branch=certs["branch"],
307 extra=extra)
307 extra=extra)
308
308
309 def gettags(self):
309 def gettags(self):
310 tags = {}
310 tags = {}
311 for e in self.mtnrun("tags").split("\n\n"):
311 for e in self.mtnrun("tags").split("\n\n"):
312 m = self.tag_re.match(e)
312 m = self.tag_re.match(e)
313 if m:
313 if m:
314 tags[m.group(1)] = m.group(2)
314 tags[m.group(1)] = m.group(2)
315 return tags
315 return tags
316
316
317 def getchangedfiles(self, rev, i):
317 def getchangedfiles(self, rev, i):
318 # This function is only needed to support --filemap
318 # This function is only needed to support --filemap
319 # ... and we don't support that
319 # ... and we don't support that
320 raise NotImplementedError()
320 raise NotImplementedError()
321
321
322 def before(self):
322 def before(self):
323 # Check if we have a new enough version to use automate stdio
323 # Check if we have a new enough version to use automate stdio
324 version = 0.0
324 version = 0.0
325 try:
325 try:
326 versionstr = self.mtnrunsingle("interface_version")
326 versionstr = self.mtnrunsingle("interface_version")
327 version = float(versionstr)
327 version = float(versionstr)
328 except Exception:
328 except Exception:
329 raise util.Abort(_("unable to determine mtn automate interface "
329 raise util.Abort(_("unable to determine mtn automate interface "
330 "version"))
330 "version"))
331
331
332 if version >= 12.0:
332 if version >= 12.0:
333 self.automatestdio = True
333 self.automatestdio = True
334 self.ui.debug("mtn automate version %s - using automate stdio\n" %
334 self.ui.debug("mtn automate version %s - using automate stdio\n" %
335 version)
335 version)
336
336
337 # launch the long-running automate stdio process
337 # launch the long-running automate stdio process
338 self.mtnwritefp, self.mtnreadfp = self._run2('automate', 'stdio',
338 self.mtnwritefp, self.mtnreadfp = self._run2('automate', 'stdio',
339 '-d', self.path)
339 '-d', self.path)
340 # read the headers
340 # read the headers
341 read = self.mtnreadfp.readline()
341 read = self.mtnreadfp.readline()
342 if read != 'format-version: 2\n':
342 if read != 'format-version: 2\n':
343 raise util.Abort(_('mtn automate stdio header unexpected: %s')
343 raise util.Abort(_('mtn automate stdio header unexpected: %s')
344 % read)
344 % read)
345 while read != '\n':
345 while read != '\n':
346 read = self.mtnreadfp.readline()
346 read = self.mtnreadfp.readline()
347 if not read:
347 if not read:
348 raise util.Abort(_("failed to reach end of mtn automate "
348 raise util.Abort(_("failed to reach end of mtn automate "
349 "stdio headers"))
349 "stdio headers"))
350 else:
350 else:
351 self.ui.debug("mtn automate version %s - not using automate stdio "
351 self.ui.debug("mtn automate version %s - not using automate stdio "
352 "(automate >= 12.0 - mtn >= 0.46 is needed)\n" % version)
352 "(automate >= 12.0 - mtn >= 0.46 is needed)\n" % version)
353
353
354 def after(self):
354 def after(self):
355 if self.automatestdio:
355 if self.automatestdio:
356 self.mtnwritefp.close()
356 self.mtnwritefp.close()
357 self.mtnwritefp = None
357 self.mtnwritefp = None
358 self.mtnreadfp.close()
358 self.mtnreadfp.close()
359 self.mtnreadfp = None
359 self.mtnreadfp = None
360
360
@@ -1,500 +1,500 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''High-level command function for lfconvert, plus the cmdtable.'''
9 '''High-level command function for lfconvert, plus the cmdtable.'''
10
10
11 import os
11 import os
12 import shutil
12 import shutil
13
13
14 from mercurial import util, match as match_, hg, node, context, error
14 from mercurial import util, match as match_, hg, node, context, error
15 from mercurial.i18n import _
15 from mercurial.i18n import _
16
16
17 import lfutil
17 import lfutil
18 import basestore
18 import basestore
19
19
20 # -- Commands ----------------------------------------------------------
20 # -- Commands ----------------------------------------------------------
21
21
22 def lfconvert(ui, src, dest, *pats, **opts):
22 def lfconvert(ui, src, dest, *pats, **opts):
23 '''convert a normal repository to a largefiles repository
23 '''convert a normal repository to a largefiles repository
24
24
25 Convert repository SOURCE to a new repository DEST, identical to
25 Convert repository SOURCE to a new repository DEST, identical to
26 SOURCE except that certain files will be converted as largefiles:
26 SOURCE except that certain files will be converted as largefiles:
27 specifically, any file that matches any PATTERN *or* whose size is
27 specifically, any file that matches any PATTERN *or* whose size is
28 above the minimum size threshold is converted as a largefile. The
28 above the minimum size threshold is converted as a largefile. The
29 size used to determine whether or not to track a file as a
29 size used to determine whether or not to track a file as a
30 largefile is the size of the first version of the file. The
30 largefile is the size of the first version of the file. The
31 minimum size can be specified either with --size or in
31 minimum size can be specified either with --size or in
32 configuration as ``largefiles.size``.
32 configuration as ``largefiles.size``.
33
33
34 After running this command you will need to make sure that
34 After running this command you will need to make sure that
35 largefiles is enabled anywhere you intend to push the new
35 largefiles is enabled anywhere you intend to push the new
36 repository.
36 repository.
37
37
38 Use --to-normal to convert largefiles back to normal files; after
38 Use --to-normal to convert largefiles back to normal files; after
39 this, the DEST repository can be used without largefiles at all.'''
39 this, the DEST repository can be used without largefiles at all.'''
40
40
41 if opts['to_normal']:
41 if opts['to_normal']:
42 tolfile = False
42 tolfile = False
43 else:
43 else:
44 tolfile = True
44 tolfile = True
45 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
45 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
46
46
47 if not hg.islocal(src):
47 if not hg.islocal(src):
48 raise util.Abort(_('%s is not a local Mercurial repo') % src)
48 raise util.Abort(_('%s is not a local Mercurial repo') % src)
49 if not hg.islocal(dest):
49 if not hg.islocal(dest):
50 raise util.Abort(_('%s is not a local Mercurial repo') % dest)
50 raise util.Abort(_('%s is not a local Mercurial repo') % dest)
51
51
52 rsrc = hg.repository(ui, src)
52 rsrc = hg.repository(ui, src)
53 ui.status(_('initializing destination %s\n') % dest)
53 ui.status(_('initializing destination %s\n') % dest)
54 rdst = hg.repository(ui, dest, create=True)
54 rdst = hg.repository(ui, dest, create=True)
55
55
56 success = False
56 success = False
57 try:
57 try:
58 # Lock destination to prevent modification while it is converted to.
58 # Lock destination to prevent modification while it is converted to.
59 # Don't need to lock src because we are just reading from its history
59 # Don't need to lock src because we are just reading from its history
60 # which can't change.
60 # which can't change.
61 dst_lock = rdst.lock()
61 dst_lock = rdst.lock()
62
62
63 # Get a list of all changesets in the source. The easy way to do this
63 # Get a list of all changesets in the source. The easy way to do this
64 # is to simply walk the changelog, using changelog.nodesbewteen().
64 # is to simply walk the changelog, using changelog.nodesbewteen().
65 # Take a look at mercurial/revlog.py:639 for more details.
65 # Take a look at mercurial/revlog.py:639 for more details.
66 # Use a generator instead of a list to decrease memory usage
66 # Use a generator instead of a list to decrease memory usage
67 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
67 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
68 rsrc.heads())[0])
68 rsrc.heads())[0])
69 revmap = {node.nullid: node.nullid}
69 revmap = {node.nullid: node.nullid}
70 if tolfile:
70 if tolfile:
71 lfiles = set()
71 lfiles = set()
72 normalfiles = set()
72 normalfiles = set()
73 if not pats:
73 if not pats:
74 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
74 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
75 if pats:
75 if pats:
76 matcher = match_.match(rsrc.root, '', list(pats))
76 matcher = match_.match(rsrc.root, '', list(pats))
77 else:
77 else:
78 matcher = None
78 matcher = None
79
79
80 lfiletohash = {}
80 lfiletohash = {}
81 for ctx in ctxs:
81 for ctx in ctxs:
82 ui.progress(_('converting revisions'), ctx.rev(),
82 ui.progress(_('converting revisions'), ctx.rev(),
83 unit=_('revision'), total=rsrc['tip'].rev())
83 unit=_('revision'), total=rsrc['tip'].rev())
84 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
84 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
85 lfiles, normalfiles, matcher, size, lfiletohash)
85 lfiles, normalfiles, matcher, size, lfiletohash)
86 ui.progress(_('converting revisions'), None)
86 ui.progress(_('converting revisions'), None)
87
87
88 if os.path.exists(rdst.wjoin(lfutil.shortname)):
88 if os.path.exists(rdst.wjoin(lfutil.shortname)):
89 shutil.rmtree(rdst.wjoin(lfutil.shortname))
89 shutil.rmtree(rdst.wjoin(lfutil.shortname))
90
90
91 for f in lfiletohash.keys():
91 for f in lfiletohash.keys():
92 if os.path.isfile(rdst.wjoin(f)):
92 if os.path.isfile(rdst.wjoin(f)):
93 os.unlink(rdst.wjoin(f))
93 os.unlink(rdst.wjoin(f))
94 try:
94 try:
95 os.removedirs(os.path.dirname(rdst.wjoin(f)))
95 os.removedirs(os.path.dirname(rdst.wjoin(f)))
96 except OSError:
96 except OSError:
97 pass
97 pass
98
98
99 # If there were any files converted to largefiles, add largefiles
99 # If there were any files converted to largefiles, add largefiles
100 # to the destination repository's requirements.
100 # to the destination repository's requirements.
101 if lfiles:
101 if lfiles:
102 rdst.requirements.add('largefiles')
102 rdst.requirements.add('largefiles')
103 rdst._writerequirements()
103 rdst._writerequirements()
104 else:
104 else:
105 for ctx in ctxs:
105 for ctx in ctxs:
106 ui.progress(_('converting revisions'), ctx.rev(),
106 ui.progress(_('converting revisions'), ctx.rev(),
107 unit=_('revision'), total=rsrc['tip'].rev())
107 unit=_('revision'), total=rsrc['tip'].rev())
108 _addchangeset(ui, rsrc, rdst, ctx, revmap)
108 _addchangeset(ui, rsrc, rdst, ctx, revmap)
109
109
110 ui.progress(_('converting revisions'), None)
110 ui.progress(_('converting revisions'), None)
111 success = True
111 success = True
112 finally:
112 finally:
113 if not success:
113 if not success:
114 # we failed, remove the new directory
114 # we failed, remove the new directory
115 shutil.rmtree(rdst.root)
115 shutil.rmtree(rdst.root)
116 dst_lock.release()
116 dst_lock.release()
117
117
118 def _addchangeset(ui, rsrc, rdst, ctx, revmap):
118 def _addchangeset(ui, rsrc, rdst, ctx, revmap):
119 # Convert src parents to dst parents
119 # Convert src parents to dst parents
120 parents = _convertparents(ctx, revmap)
120 parents = _convertparents(ctx, revmap)
121
121
122 # Generate list of changed files
122 # Generate list of changed files
123 files = _getchangedfiles(ctx, parents)
123 files = _getchangedfiles(ctx, parents)
124
124
125 def getfilectx(repo, memctx, f):
125 def getfilectx(repo, memctx, f):
126 if lfutil.standin(f) in files:
126 if lfutil.standin(f) in files:
127 # if the file isn't in the manifest then it was removed
127 # if the file isn't in the manifest then it was removed
128 # or renamed, raise IOError to indicate this
128 # or renamed, raise IOError to indicate this
129 try:
129 try:
130 fctx = ctx.filectx(lfutil.standin(f))
130 fctx = ctx.filectx(lfutil.standin(f))
131 except error.LookupError:
131 except error.LookupError:
132 raise IOError()
132 raise IOError()
133 renamed = fctx.renamed()
133 renamed = fctx.renamed()
134 if renamed:
134 if renamed:
135 renamed = lfutil.splitstandin(renamed[0])
135 renamed = lfutil.splitstandin(renamed[0])
136
136
137 hash = fctx.data().strip()
137 hash = fctx.data().strip()
138 path = lfutil.findfile(rsrc, hash)
138 path = lfutil.findfile(rsrc, hash)
139 ### TODO: What if the file is not cached?
139 ### TODO: What if the file is not cached?
140 data = ''
140 data = ''
141 fd = None
141 fd = None
142 try:
142 try:
143 fd = open(path, 'rb')
143 fd = open(path, 'rb')
144 data = fd.read()
144 data = fd.read()
145 finally:
145 finally:
146 if fd:
146 if fd:
147 fd.close()
147 fd.close()
148 return context.memfilectx(f, data, 'l' in fctx.flags(),
148 return context.memfilectx(f, data, 'l' in fctx.flags(),
149 'x' in fctx.flags(), renamed)
149 'x' in fctx.flags(), renamed)
150 else:
150 else:
151 return _getnormalcontext(repo.ui, ctx, f, revmap)
151 return _getnormalcontext(repo.ui, ctx, f, revmap)
152
152
153 dstfiles = []
153 dstfiles = []
154 for file in files:
154 for file in files:
155 if lfutil.isstandin(file):
155 if lfutil.isstandin(file):
156 dstfiles.append(lfutil.splitstandin(file))
156 dstfiles.append(lfutil.splitstandin(file))
157 else:
157 else:
158 dstfiles.append(file)
158 dstfiles.append(file)
159 # Commit
159 # Commit
160 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
160 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
161
161
162 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
162 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
163 matcher, size, lfiletohash):
163 matcher, size, lfiletohash):
164 # Convert src parents to dst parents
164 # Convert src parents to dst parents
165 parents = _convertparents(ctx, revmap)
165 parents = _convertparents(ctx, revmap)
166
166
167 # Generate list of changed files
167 # Generate list of changed files
168 files = _getchangedfiles(ctx, parents)
168 files = _getchangedfiles(ctx, parents)
169
169
170 dstfiles = []
170 dstfiles = []
171 for f in files:
171 for f in files:
172 if f not in lfiles and f not in normalfiles:
172 if f not in lfiles and f not in normalfiles:
173 islfile = _islfile(f, ctx, matcher, size)
173 islfile = _islfile(f, ctx, matcher, size)
174 # If this file was renamed or copied then copy
174 # If this file was renamed or copied then copy
175 # the lfileness of its predecessor
175 # the lfileness of its predecessor
176 if f in ctx.manifest():
176 if f in ctx.manifest():
177 fctx = ctx.filectx(f)
177 fctx = ctx.filectx(f)
178 renamed = fctx.renamed()
178 renamed = fctx.renamed()
179 renamedlfile = renamed and renamed[0] in lfiles
179 renamedlfile = renamed and renamed[0] in lfiles
180 islfile |= renamedlfile
180 islfile |= renamedlfile
181 if 'l' in fctx.flags():
181 if 'l' in fctx.flags():
182 if renamedlfile:
182 if renamedlfile:
183 raise util.Abort(
183 raise util.Abort(
184 _('renamed/copied largefile %s becomes symlink')
184 _('renamed/copied largefile %s becomes symlink')
185 % f)
185 % f)
186 islfile = False
186 islfile = False
187 if islfile:
187 if islfile:
188 lfiles.add(f)
188 lfiles.add(f)
189 else:
189 else:
190 normalfiles.add(f)
190 normalfiles.add(f)
191
191
192 if f in lfiles:
192 if f in lfiles:
193 dstfiles.append(lfutil.standin(f))
193 dstfiles.append(lfutil.standin(f))
194 # largefile in manifest if it has not been removed/renamed
194 # largefile in manifest if it has not been removed/renamed
195 if f in ctx.manifest():
195 if f in ctx.manifest():
196 fctx = ctx.filectx(f)
196 fctx = ctx.filectx(f)
197 if 'l' in fctx.flags():
197 if 'l' in fctx.flags():
198 renamed = fctx.renamed()
198 renamed = fctx.renamed()
199 if renamed and renamed[0] in lfiles:
199 if renamed and renamed[0] in lfiles:
200 raise util.Abort(_('largefile %s becomes symlink') % f)
200 raise util.Abort(_('largefile %s becomes symlink') % f)
201
201
202 # largefile was modified, update standins
202 # largefile was modified, update standins
203 fullpath = rdst.wjoin(f)
203 fullpath = rdst.wjoin(f)
204 util.makedirs(os.path.dirname(fullpath))
204 util.makedirs(os.path.dirname(fullpath))
205 m = util.sha1('')
205 m = util.sha1('')
206 m.update(ctx[f].data())
206 m.update(ctx[f].data())
207 hash = m.hexdigest()
207 hash = m.hexdigest()
208 if f not in lfiletohash or lfiletohash[f] != hash:
208 if f not in lfiletohash or lfiletohash[f] != hash:
209 try:
209 try:
210 fd = open(fullpath, 'wb')
210 fd = open(fullpath, 'wb')
211 fd.write(ctx[f].data())
211 fd.write(ctx[f].data())
212 finally:
212 finally:
213 if fd:
213 if fd:
214 fd.close()
214 fd.close()
215 executable = 'x' in ctx[f].flags()
215 executable = 'x' in ctx[f].flags()
216 os.chmod(fullpath, lfutil.getmode(executable))
216 os.chmod(fullpath, lfutil.getmode(executable))
217 lfutil.writestandin(rdst, lfutil.standin(f), hash,
217 lfutil.writestandin(rdst, lfutil.standin(f), hash,
218 executable)
218 executable)
219 lfiletohash[f] = hash
219 lfiletohash[f] = hash
220 else:
220 else:
221 # normal file
221 # normal file
222 dstfiles.append(f)
222 dstfiles.append(f)
223
223
224 def getfilectx(repo, memctx, f):
224 def getfilectx(repo, memctx, f):
225 if lfutil.isstandin(f):
225 if lfutil.isstandin(f):
226 # if the file isn't in the manifest then it was removed
226 # if the file isn't in the manifest then it was removed
227 # or renamed, raise IOError to indicate this
227 # or renamed, raise IOError to indicate this
228 srcfname = lfutil.splitstandin(f)
228 srcfname = lfutil.splitstandin(f)
229 try:
229 try:
230 fctx = ctx.filectx(srcfname)
230 fctx = ctx.filectx(srcfname)
231 except error.LookupError:
231 except error.LookupError:
232 raise IOError()
232 raise IOError()
233 renamed = fctx.renamed()
233 renamed = fctx.renamed()
234 if renamed:
234 if renamed:
235 # standin is always a largefile because largefile-ness
235 # standin is always a largefile because largefile-ness
236 # doesn't change after rename or copy
236 # doesn't change after rename or copy
237 renamed = lfutil.standin(renamed[0])
237 renamed = lfutil.standin(renamed[0])
238
238
239 return context.memfilectx(f, lfiletohash[srcfname] + '\n', 'l' in
239 return context.memfilectx(f, lfiletohash[srcfname] + '\n', 'l' in
240 fctx.flags(), 'x' in fctx.flags(), renamed)
240 fctx.flags(), 'x' in fctx.flags(), renamed)
241 else:
241 else:
242 return _getnormalcontext(repo.ui, ctx, f, revmap)
242 return _getnormalcontext(repo.ui, ctx, f, revmap)
243
243
244 # Commit
244 # Commit
245 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
245 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
246
246
247 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
247 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
248 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
248 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
249 getfilectx, ctx.user(), ctx.date(), ctx.extra())
249 getfilectx, ctx.user(), ctx.date(), ctx.extra())
250 ret = rdst.commitctx(mctx)
250 ret = rdst.commitctx(mctx)
251 rdst.dirstate.setparents(ret)
251 rdst.dirstate.setparents(ret)
252 revmap[ctx.node()] = rdst.changelog.tip()
252 revmap[ctx.node()] = rdst.changelog.tip()
253
253
254 # Generate list of changed files
254 # Generate list of changed files
255 def _getchangedfiles(ctx, parents):
255 def _getchangedfiles(ctx, parents):
256 files = set(ctx.files())
256 files = set(ctx.files())
257 if node.nullid not in parents:
257 if node.nullid not in parents:
258 mc = ctx.manifest()
258 mc = ctx.manifest()
259 mp1 = ctx.parents()[0].manifest()
259 mp1 = ctx.parents()[0].manifest()
260 mp2 = ctx.parents()[1].manifest()
260 mp2 = ctx.parents()[1].manifest()
261 files |= (set(mp1) | set(mp2)) - set(mc)
261 files |= (set(mp1) | set(mp2)) - set(mc)
262 for f in mc:
262 for f in mc:
263 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
263 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
264 files.add(f)
264 files.add(f)
265 return files
265 return files
266
266
267 # Convert src parents to dst parents
267 # Convert src parents to dst parents
268 def _convertparents(ctx, revmap):
268 def _convertparents(ctx, revmap):
269 parents = []
269 parents = []
270 for p in ctx.parents():
270 for p in ctx.parents():
271 parents.append(revmap[p.node()])
271 parents.append(revmap[p.node()])
272 while len(parents) < 2:
272 while len(parents) < 2:
273 parents.append(node.nullid)
273 parents.append(node.nullid)
274 return parents
274 return parents
275
275
276 # Get memfilectx for a normal file
276 # Get memfilectx for a normal file
277 def _getnormalcontext(ui, ctx, f, revmap):
277 def _getnormalcontext(ui, ctx, f, revmap):
278 try:
278 try:
279 fctx = ctx.filectx(f)
279 fctx = ctx.filectx(f)
280 except error.LookupError:
280 except error.LookupError:
281 raise IOError()
281 raise IOError()
282 renamed = fctx.renamed()
282 renamed = fctx.renamed()
283 if renamed:
283 if renamed:
284 renamed = renamed[0]
284 renamed = renamed[0]
285
285
286 data = fctx.data()
286 data = fctx.data()
287 if f == '.hgtags':
287 if f == '.hgtags':
288 data = _converttags (ui, revmap, data)
288 data = _converttags (ui, revmap, data)
289 return context.memfilectx(f, data, 'l' in fctx.flags(),
289 return context.memfilectx(f, data, 'l' in fctx.flags(),
290 'x' in fctx.flags(), renamed)
290 'x' in fctx.flags(), renamed)
291
291
292 # Remap tag data using a revision map
292 # Remap tag data using a revision map
293 def _converttags(ui, revmap, data):
293 def _converttags(ui, revmap, data):
294 newdata = []
294 newdata = []
295 for line in data.splitlines():
295 for line in data.splitlines():
296 try:
296 try:
297 id, name = line.split(' ', 1)
297 id, name = line.split(' ', 1)
298 except ValueError:
298 except ValueError:
299 ui.warn(_('skipping incorrectly formatted tag %s\n'
299 ui.warn(_('skipping incorrectly formatted tag %s\n'
300 % line))
300 % line))
301 continue
301 continue
302 try:
302 try:
303 newid = node.bin(id)
303 newid = node.bin(id)
304 except TypeError:
304 except TypeError:
305 ui.warn(_('skipping incorrectly formatted id %s\n'
305 ui.warn(_('skipping incorrectly formatted id %s\n'
306 % id))
306 % id))
307 continue
307 continue
308 try:
308 try:
309 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
309 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
310 name))
310 name))
311 except KeyError:
311 except KeyError:
312 ui.warn(_('no mapping for id %s\n' % id))
312 ui.warn(_('no mapping for id %s\n') % id)
313 continue
313 continue
314 return ''.join(newdata)
314 return ''.join(newdata)
315
315
316 def _islfile(file, ctx, matcher, size):
316 def _islfile(file, ctx, matcher, size):
317 '''Return true if file should be considered a largefile, i.e.
317 '''Return true if file should be considered a largefile, i.e.
318 matcher matches it or it is larger than size.'''
318 matcher matches it or it is larger than size.'''
319 # never store special .hg* files as largefiles
319 # never store special .hg* files as largefiles
320 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
320 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
321 return False
321 return False
322 if matcher and matcher(file):
322 if matcher and matcher(file):
323 return True
323 return True
324 try:
324 try:
325 return ctx.filectx(file).size() >= size * 1024 * 1024
325 return ctx.filectx(file).size() >= size * 1024 * 1024
326 except error.LookupError:
326 except error.LookupError:
327 return False
327 return False
328
328
329 def uploadlfiles(ui, rsrc, rdst, files):
329 def uploadlfiles(ui, rsrc, rdst, files):
330 '''upload largefiles to the central store'''
330 '''upload largefiles to the central store'''
331
331
332 if not files:
332 if not files:
333 return
333 return
334
334
335 store = basestore._openstore(rsrc, rdst, put=True)
335 store = basestore._openstore(rsrc, rdst, put=True)
336
336
337 at = 0
337 at = 0
338 files = filter(lambda h: not store.exists(h), files)
338 files = filter(lambda h: not store.exists(h), files)
339 for hash in files:
339 for hash in files:
340 ui.progress(_('uploading largefiles'), at, unit='largefile',
340 ui.progress(_('uploading largefiles'), at, unit='largefile',
341 total=len(files))
341 total=len(files))
342 source = lfutil.findfile(rsrc, hash)
342 source = lfutil.findfile(rsrc, hash)
343 if not source:
343 if not source:
344 raise util.Abort(_('largefile %s missing from store'
344 raise util.Abort(_('largefile %s missing from store'
345 ' (needs to be uploaded)') % hash)
345 ' (needs to be uploaded)') % hash)
346 # XXX check for errors here
346 # XXX check for errors here
347 store.put(source, hash)
347 store.put(source, hash)
348 at += 1
348 at += 1
349 ui.progress(_('uploading largefiles'), None)
349 ui.progress(_('uploading largefiles'), None)
350
350
351 def verifylfiles(ui, repo, all=False, contents=False):
351 def verifylfiles(ui, repo, all=False, contents=False):
352 '''Verify that every big file revision in the current changeset
352 '''Verify that every big file revision in the current changeset
353 exists in the central store. With --contents, also verify that
353 exists in the central store. With --contents, also verify that
354 the contents of each big file revision are correct (SHA-1 hash
354 the contents of each big file revision are correct (SHA-1 hash
355 matches the revision ID). With --all, check every changeset in
355 matches the revision ID). With --all, check every changeset in
356 this repository.'''
356 this repository.'''
357 if all:
357 if all:
358 # Pass a list to the function rather than an iterator because we know a
358 # Pass a list to the function rather than an iterator because we know a
359 # list will work.
359 # list will work.
360 revs = range(len(repo))
360 revs = range(len(repo))
361 else:
361 else:
362 revs = ['.']
362 revs = ['.']
363
363
364 store = basestore._openstore(repo)
364 store = basestore._openstore(repo)
365 return store.verify(revs, contents=contents)
365 return store.verify(revs, contents=contents)
366
366
367 def cachelfiles(ui, repo, node):
367 def cachelfiles(ui, repo, node):
368 '''cachelfiles ensures that all largefiles needed by the specified revision
368 '''cachelfiles ensures that all largefiles needed by the specified revision
369 are present in the repository's largefile cache.
369 are present in the repository's largefile cache.
370
370
371 returns a tuple (cached, missing). cached is the list of files downloaded
371 returns a tuple (cached, missing). cached is the list of files downloaded
372 by this operation; missing is the list of files that were needed but could
372 by this operation; missing is the list of files that were needed but could
373 not be found.'''
373 not be found.'''
374 lfiles = lfutil.listlfiles(repo, node)
374 lfiles = lfutil.listlfiles(repo, node)
375 toget = []
375 toget = []
376
376
377 for lfile in lfiles:
377 for lfile in lfiles:
378 # If we are mid-merge, then we have to trust the standin that is in the
378 # If we are mid-merge, then we have to trust the standin that is in the
379 # working copy to have the correct hashvalue. This is because the
379 # working copy to have the correct hashvalue. This is because the
380 # original hg.merge() already updated the standin as part of the normal
380 # original hg.merge() already updated the standin as part of the normal
381 # merge process -- we just have to udpate the largefile to match.
381 # merge process -- we just have to udpate the largefile to match.
382 if (getattr(repo, "_ismerging", False) and
382 if (getattr(repo, "_ismerging", False) and
383 os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
383 os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
384 expectedhash = lfutil.readstandin(repo, lfile)
384 expectedhash = lfutil.readstandin(repo, lfile)
385 else:
385 else:
386 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
386 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
387
387
388 # if it exists and its hash matches, it might have been locally
388 # if it exists and its hash matches, it might have been locally
389 # modified before updating and the user chose 'local'. in this case,
389 # modified before updating and the user chose 'local'. in this case,
390 # it will not be in any store, so don't look for it.
390 # it will not be in any store, so don't look for it.
391 if ((not os.path.exists(repo.wjoin(lfile)) or
391 if ((not os.path.exists(repo.wjoin(lfile)) or
392 expectedhash != lfutil.hashfile(repo.wjoin(lfile))) and
392 expectedhash != lfutil.hashfile(repo.wjoin(lfile))) and
393 not lfutil.findfile(repo, expectedhash)):
393 not lfutil.findfile(repo, expectedhash)):
394 toget.append((lfile, expectedhash))
394 toget.append((lfile, expectedhash))
395
395
396 if toget:
396 if toget:
397 store = basestore._openstore(repo)
397 store = basestore._openstore(repo)
398 ret = store.get(toget)
398 ret = store.get(toget)
399 return ret
399 return ret
400
400
401 return ([], [])
401 return ([], [])
402
402
403 def updatelfiles(ui, repo, filelist=None, printmessage=True):
403 def updatelfiles(ui, repo, filelist=None, printmessage=True):
404 wlock = repo.wlock()
404 wlock = repo.wlock()
405 try:
405 try:
406 lfdirstate = lfutil.openlfdirstate(ui, repo)
406 lfdirstate = lfutil.openlfdirstate(ui, repo)
407 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
407 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
408
408
409 if filelist is not None:
409 if filelist is not None:
410 lfiles = [f for f in lfiles if f in filelist]
410 lfiles = [f for f in lfiles if f in filelist]
411
411
412 printed = False
412 printed = False
413 if printmessage and lfiles:
413 if printmessage and lfiles:
414 ui.status(_('getting changed largefiles\n'))
414 ui.status(_('getting changed largefiles\n'))
415 printed = True
415 printed = True
416 cachelfiles(ui, repo, '.')
416 cachelfiles(ui, repo, '.')
417
417
418 updated, removed = 0, 0
418 updated, removed = 0, 0
419 for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles):
419 for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles):
420 # increment the appropriate counter according to _updatelfile's
420 # increment the appropriate counter according to _updatelfile's
421 # return value
421 # return value
422 updated += i > 0 and i or 0
422 updated += i > 0 and i or 0
423 removed -= i < 0 and i or 0
423 removed -= i < 0 and i or 0
424 if printmessage and (removed or updated) and not printed:
424 if printmessage and (removed or updated) and not printed:
425 ui.status(_('getting changed largefiles\n'))
425 ui.status(_('getting changed largefiles\n'))
426 printed = True
426 printed = True
427
427
428 lfdirstate.write()
428 lfdirstate.write()
429 if printed and printmessage:
429 if printed and printmessage:
430 ui.status(_('%d largefiles updated, %d removed\n') % (updated,
430 ui.status(_('%d largefiles updated, %d removed\n') % (updated,
431 removed))
431 removed))
432 finally:
432 finally:
433 wlock.release()
433 wlock.release()
434
434
435 def _updatelfile(repo, lfdirstate, lfile):
435 def _updatelfile(repo, lfdirstate, lfile):
436 '''updates a single largefile and copies the state of its standin from
436 '''updates a single largefile and copies the state of its standin from
437 the repository's dirstate to its state in the lfdirstate.
437 the repository's dirstate to its state in the lfdirstate.
438
438
439 returns 1 if the file was modified, -1 if the file was removed, 0 if the
439 returns 1 if the file was modified, -1 if the file was removed, 0 if the
440 file was unchanged, and None if the needed largefile was missing from the
440 file was unchanged, and None if the needed largefile was missing from the
441 cache.'''
441 cache.'''
442 ret = 0
442 ret = 0
443 abslfile = repo.wjoin(lfile)
443 abslfile = repo.wjoin(lfile)
444 absstandin = repo.wjoin(lfutil.standin(lfile))
444 absstandin = repo.wjoin(lfutil.standin(lfile))
445 if os.path.exists(absstandin):
445 if os.path.exists(absstandin):
446 if os.path.exists(absstandin+'.orig'):
446 if os.path.exists(absstandin+'.orig'):
447 shutil.copyfile(abslfile, abslfile+'.orig')
447 shutil.copyfile(abslfile, abslfile+'.orig')
448 expecthash = lfutil.readstandin(repo, lfile)
448 expecthash = lfutil.readstandin(repo, lfile)
449 if (expecthash != '' and
449 if (expecthash != '' and
450 (not os.path.exists(abslfile) or
450 (not os.path.exists(abslfile) or
451 expecthash != lfutil.hashfile(abslfile))):
451 expecthash != lfutil.hashfile(abslfile))):
452 if not lfutil.copyfromcache(repo, expecthash, lfile):
452 if not lfutil.copyfromcache(repo, expecthash, lfile):
453 # use normallookup() to allocate entry in largefiles dirstate,
453 # use normallookup() to allocate entry in largefiles dirstate,
454 # because lack of it misleads lfiles_repo.status() into
454 # because lack of it misleads lfiles_repo.status() into
455 # recognition that such cache missing files are REMOVED.
455 # recognition that such cache missing files are REMOVED.
456 lfdirstate.normallookup(lfile)
456 lfdirstate.normallookup(lfile)
457 return None # don't try to set the mode
457 return None # don't try to set the mode
458 ret = 1
458 ret = 1
459 mode = os.stat(absstandin).st_mode
459 mode = os.stat(absstandin).st_mode
460 if mode != os.stat(abslfile).st_mode:
460 if mode != os.stat(abslfile).st_mode:
461 os.chmod(abslfile, mode)
461 os.chmod(abslfile, mode)
462 ret = 1
462 ret = 1
463 else:
463 else:
464 # Remove lfiles for which the standin is deleted, unless the
464 # Remove lfiles for which the standin is deleted, unless the
465 # lfile is added to the repository again. This happens when a
465 # lfile is added to the repository again. This happens when a
466 # largefile is converted back to a normal file: the standin
466 # largefile is converted back to a normal file: the standin
467 # disappears, but a new (normal) file appears as the lfile.
467 # disappears, but a new (normal) file appears as the lfile.
468 if os.path.exists(abslfile) and lfile not in repo[None]:
468 if os.path.exists(abslfile) and lfile not in repo[None]:
469 util.unlinkpath(abslfile)
469 util.unlinkpath(abslfile)
470 ret = -1
470 ret = -1
471 state = repo.dirstate[lfutil.standin(lfile)]
471 state = repo.dirstate[lfutil.standin(lfile)]
472 if state == 'n':
472 if state == 'n':
473 # When rebasing, we need to synchronize the standin and the largefile,
473 # When rebasing, we need to synchronize the standin and the largefile,
474 # because otherwise the largefile will get reverted. But for commit's
474 # because otherwise the largefile will get reverted. But for commit's
475 # sake, we have to mark the file as unclean.
475 # sake, we have to mark the file as unclean.
476 if getattr(repo, "_isrebasing", False):
476 if getattr(repo, "_isrebasing", False):
477 lfdirstate.normallookup(lfile)
477 lfdirstate.normallookup(lfile)
478 else:
478 else:
479 lfdirstate.normal(lfile)
479 lfdirstate.normal(lfile)
480 elif state == 'r':
480 elif state == 'r':
481 lfdirstate.remove(lfile)
481 lfdirstate.remove(lfile)
482 elif state == 'a':
482 elif state == 'a':
483 lfdirstate.add(lfile)
483 lfdirstate.add(lfile)
484 elif state == '?':
484 elif state == '?':
485 lfdirstate.drop(lfile)
485 lfdirstate.drop(lfile)
486 return ret
486 return ret
487
487
488 # -- hg commands declarations ------------------------------------------------
488 # -- hg commands declarations ------------------------------------------------
489
489
490 cmdtable = {
490 cmdtable = {
491 'lfconvert': (lfconvert,
491 'lfconvert': (lfconvert,
492 [('s', 'size', '',
492 [('s', 'size', '',
493 _('minimum size (MB) for files to be converted '
493 _('minimum size (MB) for files to be converted '
494 'as largefiles'),
494 'as largefiles'),
495 'SIZE'),
495 'SIZE'),
496 ('', 'to-normal', False,
496 ('', 'to-normal', False,
497 _('convert from a largefiles repo to a normal repo')),
497 _('convert from a largefiles repo to a normal repo')),
498 ],
498 ],
499 _('hg lfconvert SOURCE DEST [FILE ...]')),
499 _('hg lfconvert SOURCE DEST [FILE ...]')),
500 }
500 }
@@ -1,973 +1,973 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 node, archival, error, merge
15 node, archival, error, merge
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def installnormalfilesmatchfn(manifest):
25 def installnormalfilesmatchfn(manifest):
26 '''overrides scmutil.match so that the matcher it returns will ignore all
26 '''overrides scmutil.match so that the matcher it returns will ignore all
27 largefiles'''
27 largefiles'''
28 oldmatch = None # for the closure
28 oldmatch = None # for the closure
29 def override_match(ctx, pats=[], opts={}, globbed=False,
29 def override_match(ctx, pats=[], opts={}, globbed=False,
30 default='relpath'):
30 default='relpath'):
31 match = oldmatch(ctx, pats, opts, globbed, default)
31 match = oldmatch(ctx, pats, opts, globbed, default)
32 m = copy.copy(match)
32 m = copy.copy(match)
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 manifest)
34 manifest)
35 m._files = filter(notlfile, m._files)
35 m._files = filter(notlfile, m._files)
36 m._fmap = set(m._files)
36 m._fmap = set(m._files)
37 orig_matchfn = m.matchfn
37 orig_matchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(override_match)
40 oldmatch = installmatchfn(override_match)
41
41
42 def installmatchfn(f):
42 def installmatchfn(f):
43 oldmatch = scmutil.match
43 oldmatch = scmutil.match
44 setattr(f, 'oldmatch', oldmatch)
44 setattr(f, 'oldmatch', oldmatch)
45 scmutil.match = f
45 scmutil.match = f
46 return oldmatch
46 return oldmatch
47
47
48 def restorematchfn():
48 def restorematchfn():
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
50 was called. no-op if scmutil.match is its original function.
50 was called. no-op if scmutil.match is its original function.
51
51
52 Note that n calls to installnormalfilesmatchfn will require n calls to
52 Note that n calls to installnormalfilesmatchfn will require n calls to
53 restore matchfn to reverse'''
53 restore matchfn to reverse'''
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
55
55
56 def add_largefiles(ui, repo, *pats, **opts):
56 def add_largefiles(ui, repo, *pats, **opts):
57 large = opts.pop('large', None)
57 large = opts.pop('large', None)
58 lfsize = lfutil.getminsize(
58 lfsize = lfutil.getminsize(
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
60
60
61 lfmatcher = None
61 lfmatcher = None
62 if lfutil.islfilesrepo(repo):
62 if lfutil.islfilesrepo(repo):
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
64 if lfpats:
64 if lfpats:
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
66
66
67 lfnames = []
67 lfnames = []
68 m = scmutil.match(repo[None], pats, opts)
68 m = scmutil.match(repo[None], pats, opts)
69 m.bad = lambda x, y: None
69 m.bad = lambda x, y: None
70 wctx = repo[None]
70 wctx = repo[None]
71 for f in repo.walk(m):
71 for f in repo.walk(m):
72 exact = m.exact(f)
72 exact = m.exact(f)
73 lfile = lfutil.standin(f) in wctx
73 lfile = lfutil.standin(f) in wctx
74 nfile = f in wctx
74 nfile = f in wctx
75 exists = lfile or nfile
75 exists = lfile or nfile
76
76
77 # Don't warn the user when they attempt to add a normal tracked file.
77 # Don't warn the user when they attempt to add a normal tracked file.
78 # The normal add code will do that for us.
78 # The normal add code will do that for us.
79 if exact and exists:
79 if exact and exists:
80 if lfile:
80 if lfile:
81 ui.warn(_('%s already a largefile\n') % f)
81 ui.warn(_('%s already a largefile\n') % f)
82 continue
82 continue
83
83
84 if exact or not exists:
84 if exact or not exists:
85 abovemin = (lfsize and
85 abovemin = (lfsize and
86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
87 if large or abovemin or (lfmatcher and lfmatcher(f)):
87 if large or abovemin or (lfmatcher and lfmatcher(f)):
88 lfnames.append(f)
88 lfnames.append(f)
89 if ui.verbose or not exact:
89 if ui.verbose or not exact:
90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
91
91
92 bad = []
92 bad = []
93 standins = []
93 standins = []
94
94
95 # Need to lock, otherwise there could be a race condition between
95 # Need to lock, otherwise there could be a race condition between
96 # when standins are created and added to the repo.
96 # when standins are created and added to the repo.
97 wlock = repo.wlock()
97 wlock = repo.wlock()
98 try:
98 try:
99 if not opts.get('dry_run'):
99 if not opts.get('dry_run'):
100 lfdirstate = lfutil.openlfdirstate(ui, repo)
100 lfdirstate = lfutil.openlfdirstate(ui, repo)
101 for f in lfnames:
101 for f in lfnames:
102 standinname = lfutil.standin(f)
102 standinname = lfutil.standin(f)
103 lfutil.writestandin(repo, standinname, hash='',
103 lfutil.writestandin(repo, standinname, hash='',
104 executable=lfutil.getexecutable(repo.wjoin(f)))
104 executable=lfutil.getexecutable(repo.wjoin(f)))
105 standins.append(standinname)
105 standins.append(standinname)
106 if lfdirstate[f] == 'r':
106 if lfdirstate[f] == 'r':
107 lfdirstate.normallookup(f)
107 lfdirstate.normallookup(f)
108 else:
108 else:
109 lfdirstate.add(f)
109 lfdirstate.add(f)
110 lfdirstate.write()
110 lfdirstate.write()
111 bad += [lfutil.splitstandin(f)
111 bad += [lfutil.splitstandin(f)
112 for f in lfutil.repo_add(repo, standins)
112 for f in lfutil.repo_add(repo, standins)
113 if f in m.files()]
113 if f in m.files()]
114 finally:
114 finally:
115 wlock.release()
115 wlock.release()
116 return bad
116 return bad
117
117
118 def remove_largefiles(ui, repo, *pats, **opts):
118 def remove_largefiles(ui, repo, *pats, **opts):
119 after = opts.get('after')
119 after = opts.get('after')
120 if not pats and not after:
120 if not pats and not after:
121 raise util.Abort(_('no files specified'))
121 raise util.Abort(_('no files specified'))
122 m = scmutil.match(repo[None], pats, opts)
122 m = scmutil.match(repo[None], pats, opts)
123 try:
123 try:
124 repo.lfstatus = True
124 repo.lfstatus = True
125 s = repo.status(match=m, clean=True)
125 s = repo.status(match=m, clean=True)
126 finally:
126 finally:
127 repo.lfstatus = False
127 repo.lfstatus = False
128 manifest = repo[None].manifest()
128 manifest = repo[None].manifest()
129 modified, added, deleted, clean = [[f for f in list
129 modified, added, deleted, clean = [[f for f in list
130 if lfutil.standin(f) in manifest]
130 if lfutil.standin(f) in manifest]
131 for list in [s[0], s[1], s[3], s[6]]]
131 for list in [s[0], s[1], s[3], s[6]]]
132
132
133 def warn(files, reason):
133 def warn(files, reason):
134 for f in files:
134 for f in files:
135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
136 % (m.rel(f), reason))
136 % (m.rel(f), reason))
137
137
138 if after:
138 if after:
139 remove, forget = deleted, []
139 remove, forget = deleted, []
140 warn(modified + added + clean, _('file still exists'))
140 warn(modified + added + clean, _('file still exists'))
141 else:
141 else:
142 remove, forget = deleted + clean, []
142 remove, forget = deleted + clean, []
143 warn(modified, _('file is modified'))
143 warn(modified, _('file is modified'))
144 warn(added, _('file has been marked for add'))
144 warn(added, _('file has been marked for add'))
145
145
146 for f in sorted(remove + forget):
146 for f in sorted(remove + forget):
147 if ui.verbose or not m.exact(f):
147 if ui.verbose or not m.exact(f):
148 ui.status(_('removing %s\n') % m.rel(f))
148 ui.status(_('removing %s\n') % m.rel(f))
149
149
150 # Need to lock because standin files are deleted then removed from the
150 # Need to lock because standin files are deleted then removed from the
151 # repository and we could race inbetween.
151 # repository and we could race inbetween.
152 wlock = repo.wlock()
152 wlock = repo.wlock()
153 try:
153 try:
154 lfdirstate = lfutil.openlfdirstate(ui, repo)
154 lfdirstate = lfutil.openlfdirstate(ui, repo)
155 for f in remove:
155 for f in remove:
156 if not after:
156 if not after:
157 # If this is being called by addremove, notify the user that we
157 # If this is being called by addremove, notify the user that we
158 # are removing the file.
158 # are removing the file.
159 if getattr(repo, "_isaddremove", False):
159 if getattr(repo, "_isaddremove", False):
160 ui.status(_('removing %s\n' % f))
160 ui.status(_('removing %s\n') % f)
161 if os.path.exists(repo.wjoin(f)):
161 if os.path.exists(repo.wjoin(f)):
162 util.unlinkpath(repo.wjoin(f))
162 util.unlinkpath(repo.wjoin(f))
163 lfdirstate.remove(f)
163 lfdirstate.remove(f)
164 lfdirstate.write()
164 lfdirstate.write()
165 forget = [lfutil.standin(f) for f in forget]
165 forget = [lfutil.standin(f) for f in forget]
166 remove = [lfutil.standin(f) for f in remove]
166 remove = [lfutil.standin(f) for f in remove]
167 lfutil.repo_forget(repo, forget)
167 lfutil.repo_forget(repo, forget)
168 # If this is being called by addremove, let the original addremove
168 # If this is being called by addremove, let the original addremove
169 # function handle this.
169 # function handle this.
170 if not getattr(repo, "_isaddremove", False):
170 if not getattr(repo, "_isaddremove", False):
171 lfutil.repo_remove(repo, remove, unlink=True)
171 lfutil.repo_remove(repo, remove, unlink=True)
172 finally:
172 finally:
173 wlock.release()
173 wlock.release()
174
174
175 # -- Wrappers: modify existing commands --------------------------------
175 # -- Wrappers: modify existing commands --------------------------------
176
176
177 # Add works by going through the files that the user wanted to add and
177 # Add works by going through the files that the user wanted to add and
178 # checking if they should be added as largefiles. Then it makes a new
178 # checking if they should be added as largefiles. Then it makes a new
179 # matcher which matches only the normal files and runs the original
179 # matcher which matches only the normal files and runs the original
180 # version of add.
180 # version of add.
181 def override_add(orig, ui, repo, *pats, **opts):
181 def override_add(orig, ui, repo, *pats, **opts):
182 normal = opts.pop('normal')
182 normal = opts.pop('normal')
183 if normal:
183 if normal:
184 if opts.get('large'):
184 if opts.get('large'):
185 raise util.Abort(_('--normal cannot be used with --large'))
185 raise util.Abort(_('--normal cannot be used with --large'))
186 return orig(ui, repo, *pats, **opts)
186 return orig(ui, repo, *pats, **opts)
187 bad = add_largefiles(ui, repo, *pats, **opts)
187 bad = add_largefiles(ui, repo, *pats, **opts)
188 installnormalfilesmatchfn(repo[None].manifest())
188 installnormalfilesmatchfn(repo[None].manifest())
189 result = orig(ui, repo, *pats, **opts)
189 result = orig(ui, repo, *pats, **opts)
190 restorematchfn()
190 restorematchfn()
191
191
192 return (result == 1 or bad) and 1 or 0
192 return (result == 1 or bad) and 1 or 0
193
193
194 def override_remove(orig, ui, repo, *pats, **opts):
194 def override_remove(orig, ui, repo, *pats, **opts):
195 installnormalfilesmatchfn(repo[None].manifest())
195 installnormalfilesmatchfn(repo[None].manifest())
196 orig(ui, repo, *pats, **opts)
196 orig(ui, repo, *pats, **opts)
197 restorematchfn()
197 restorematchfn()
198 remove_largefiles(ui, repo, *pats, **opts)
198 remove_largefiles(ui, repo, *pats, **opts)
199
199
200 def override_status(orig, ui, repo, *pats, **opts):
200 def override_status(orig, ui, repo, *pats, **opts):
201 try:
201 try:
202 repo.lfstatus = True
202 repo.lfstatus = True
203 return orig(ui, repo, *pats, **opts)
203 return orig(ui, repo, *pats, **opts)
204 finally:
204 finally:
205 repo.lfstatus = False
205 repo.lfstatus = False
206
206
207 def override_log(orig, ui, repo, *pats, **opts):
207 def override_log(orig, ui, repo, *pats, **opts):
208 try:
208 try:
209 repo.lfstatus = True
209 repo.lfstatus = True
210 orig(ui, repo, *pats, **opts)
210 orig(ui, repo, *pats, **opts)
211 finally:
211 finally:
212 repo.lfstatus = False
212 repo.lfstatus = False
213
213
214 def override_verify(orig, ui, repo, *pats, **opts):
214 def override_verify(orig, ui, repo, *pats, **opts):
215 large = opts.pop('large', False)
215 large = opts.pop('large', False)
216 all = opts.pop('lfa', False)
216 all = opts.pop('lfa', False)
217 contents = opts.pop('lfc', False)
217 contents = opts.pop('lfc', False)
218
218
219 result = orig(ui, repo, *pats, **opts)
219 result = orig(ui, repo, *pats, **opts)
220 if large:
220 if large:
221 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
221 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
222 return result
222 return result
223
223
224 # Override needs to refresh standins so that update's normal merge
224 # Override needs to refresh standins so that update's normal merge
225 # will go through properly. Then the other update hook (overriding repo.update)
225 # will go through properly. Then the other update hook (overriding repo.update)
226 # will get the new files. Filemerge is also overriden so that the merge
226 # will get the new files. Filemerge is also overriden so that the merge
227 # will merge standins correctly.
227 # will merge standins correctly.
228 def override_update(orig, ui, repo, *pats, **opts):
228 def override_update(orig, ui, repo, *pats, **opts):
229 lfdirstate = lfutil.openlfdirstate(ui, repo)
229 lfdirstate = lfutil.openlfdirstate(ui, repo)
230 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
230 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
231 False, False)
231 False, False)
232 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
232 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
233
233
234 # Need to lock between the standins getting updated and their
234 # Need to lock between the standins getting updated and their
235 # largefiles getting updated
235 # largefiles getting updated
236 wlock = repo.wlock()
236 wlock = repo.wlock()
237 try:
237 try:
238 if opts['check']:
238 if opts['check']:
239 mod = len(modified) > 0
239 mod = len(modified) > 0
240 for lfile in unsure:
240 for lfile in unsure:
241 standin = lfutil.standin(lfile)
241 standin = lfutil.standin(lfile)
242 if repo['.'][standin].data().strip() != \
242 if repo['.'][standin].data().strip() != \
243 lfutil.hashfile(repo.wjoin(lfile)):
243 lfutil.hashfile(repo.wjoin(lfile)):
244 mod = True
244 mod = True
245 else:
245 else:
246 lfdirstate.normal(lfile)
246 lfdirstate.normal(lfile)
247 lfdirstate.write()
247 lfdirstate.write()
248 if mod:
248 if mod:
249 raise util.Abort(_('uncommitted local changes'))
249 raise util.Abort(_('uncommitted local changes'))
250 # XXX handle removed differently
250 # XXX handle removed differently
251 if not opts['clean']:
251 if not opts['clean']:
252 for lfile in unsure + modified + added:
252 for lfile in unsure + modified + added:
253 lfutil.updatestandin(repo, lfutil.standin(lfile))
253 lfutil.updatestandin(repo, lfutil.standin(lfile))
254 finally:
254 finally:
255 wlock.release()
255 wlock.release()
256 return orig(ui, repo, *pats, **opts)
256 return orig(ui, repo, *pats, **opts)
257
257
258 # Before starting the manifest merge, merge.updates will call
258 # Before starting the manifest merge, merge.updates will call
259 # _checkunknown to check if there are any files in the merged-in
259 # _checkunknown to check if there are any files in the merged-in
260 # changeset that collide with unknown files in the working copy.
260 # changeset that collide with unknown files in the working copy.
261 #
261 #
262 # The largefiles are seen as unknown, so this prevents us from merging
262 # The largefiles are seen as unknown, so this prevents us from merging
263 # in a file 'foo' if we already have a largefile with the same name.
263 # in a file 'foo' if we already have a largefile with the same name.
264 #
264 #
265 # The overridden function filters the unknown files by removing any
265 # The overridden function filters the unknown files by removing any
266 # largefiles. This makes the merge proceed and we can then handle this
266 # largefiles. This makes the merge proceed and we can then handle this
267 # case further in the overridden manifestmerge function below.
267 # case further in the overridden manifestmerge function below.
268 def override_checkunknownfile(origfn, repo, wctx, mctx, f):
268 def override_checkunknownfile(origfn, repo, wctx, mctx, f):
269 if lfutil.standin(f) in wctx:
269 if lfutil.standin(f) in wctx:
270 return False
270 return False
271 return origfn(repo, wctx, mctx, f)
271 return origfn(repo, wctx, mctx, f)
272
272
273 # The manifest merge handles conflicts on the manifest level. We want
273 # The manifest merge handles conflicts on the manifest level. We want
274 # to handle changes in largefile-ness of files at this level too.
274 # to handle changes in largefile-ness of files at this level too.
275 #
275 #
276 # The strategy is to run the original manifestmerge and then process
276 # The strategy is to run the original manifestmerge and then process
277 # the action list it outputs. There are two cases we need to deal with:
277 # the action list it outputs. There are two cases we need to deal with:
278 #
278 #
279 # 1. Normal file in p1, largefile in p2. Here the largefile is
279 # 1. Normal file in p1, largefile in p2. Here the largefile is
280 # detected via its standin file, which will enter the working copy
280 # detected via its standin file, which will enter the working copy
281 # with a "get" action. It is not "merge" since the standin is all
281 # with a "get" action. It is not "merge" since the standin is all
282 # Mercurial is concerned with at this level -- the link to the
282 # Mercurial is concerned with at this level -- the link to the
283 # existing normal file is not relevant here.
283 # existing normal file is not relevant here.
284 #
284 #
285 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
285 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
286 # since the largefile will be present in the working copy and
286 # since the largefile will be present in the working copy and
287 # different from the normal file in p2. Mercurial therefore
287 # different from the normal file in p2. Mercurial therefore
288 # triggers a merge action.
288 # triggers a merge action.
289 #
289 #
290 # In both cases, we prompt the user and emit new actions to either
290 # In both cases, we prompt the user and emit new actions to either
291 # remove the standin (if the normal file was kept) or to remove the
291 # remove the standin (if the normal file was kept) or to remove the
292 # normal file and get the standin (if the largefile was kept). The
292 # normal file and get the standin (if the largefile was kept). The
293 # default prompt answer is to use the largefile version since it was
293 # default prompt answer is to use the largefile version since it was
294 # presumably changed on purpose.
294 # presumably changed on purpose.
295 #
295 #
296 # Finally, the merge.applyupdates function will then take care of
296 # Finally, the merge.applyupdates function will then take care of
297 # writing the files into the working copy and lfcommands.updatelfiles
297 # writing the files into the working copy and lfcommands.updatelfiles
298 # will update the largefiles.
298 # will update the largefiles.
299 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
299 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
300 actions = origfn(repo, p1, p2, pa, overwrite, partial)
300 actions = origfn(repo, p1, p2, pa, overwrite, partial)
301 processed = []
301 processed = []
302
302
303 for action in actions:
303 for action in actions:
304 if overwrite:
304 if overwrite:
305 processed.append(action)
305 processed.append(action)
306 continue
306 continue
307 f, m = action[:2]
307 f, m = action[:2]
308
308
309 choices = (_('&Largefile'), _('&Normal file'))
309 choices = (_('&Largefile'), _('&Normal file'))
310 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
310 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
311 # Case 1: normal file in the working copy, largefile in
311 # Case 1: normal file in the working copy, largefile in
312 # the second parent
312 # the second parent
313 lfile = lfutil.splitstandin(f)
313 lfile = lfutil.splitstandin(f)
314 standin = f
314 standin = f
315 msg = _('%s has been turned into a largefile\n'
315 msg = _('%s has been turned into a largefile\n'
316 'use (l)argefile or keep as (n)ormal file?') % lfile
316 'use (l)argefile or keep as (n)ormal file?') % lfile
317 if repo.ui.promptchoice(msg, choices, 0) == 0:
317 if repo.ui.promptchoice(msg, choices, 0) == 0:
318 processed.append((lfile, "r"))
318 processed.append((lfile, "r"))
319 processed.append((standin, "g", p2.flags(standin)))
319 processed.append((standin, "g", p2.flags(standin)))
320 else:
320 else:
321 processed.append((standin, "r"))
321 processed.append((standin, "r"))
322 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
322 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
323 # Case 2: largefile in the working copy, normal file in
323 # Case 2: largefile in the working copy, normal file in
324 # the second parent
324 # the second parent
325 standin = lfutil.standin(f)
325 standin = lfutil.standin(f)
326 lfile = f
326 lfile = f
327 msg = _('%s has been turned into a normal file\n'
327 msg = _('%s has been turned into a normal file\n'
328 'keep as (l)argefile or use (n)ormal file?') % lfile
328 'keep as (l)argefile or use (n)ormal file?') % lfile
329 if repo.ui.promptchoice(msg, choices, 0) == 0:
329 if repo.ui.promptchoice(msg, choices, 0) == 0:
330 processed.append((lfile, "r"))
330 processed.append((lfile, "r"))
331 else:
331 else:
332 processed.append((standin, "r"))
332 processed.append((standin, "r"))
333 processed.append((lfile, "g", p2.flags(lfile)))
333 processed.append((lfile, "g", p2.flags(lfile)))
334 else:
334 else:
335 processed.append(action)
335 processed.append(action)
336
336
337 return processed
337 return processed
338
338
339 # Override filemerge to prompt the user about how they wish to merge
339 # Override filemerge to prompt the user about how they wish to merge
340 # largefiles. This will handle identical edits, and copy/rename +
340 # largefiles. This will handle identical edits, and copy/rename +
341 # edit without prompting the user.
341 # edit without prompting the user.
342 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
342 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
343 # Use better variable names here. Because this is a wrapper we cannot
343 # Use better variable names here. Because this is a wrapper we cannot
344 # change the variable names in the function declaration.
344 # change the variable names in the function declaration.
345 fcdest, fcother, fcancestor = fcd, fco, fca
345 fcdest, fcother, fcancestor = fcd, fco, fca
346 if not lfutil.isstandin(orig):
346 if not lfutil.isstandin(orig):
347 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
347 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
348 else:
348 else:
349 if not fcother.cmp(fcdest): # files identical?
349 if not fcother.cmp(fcdest): # files identical?
350 return None
350 return None
351
351
352 # backwards, use working dir parent as ancestor
352 # backwards, use working dir parent as ancestor
353 if fcancestor == fcother:
353 if fcancestor == fcother:
354 fcancestor = fcdest.parents()[0]
354 fcancestor = fcdest.parents()[0]
355
355
356 if orig != fcother.path():
356 if orig != fcother.path():
357 repo.ui.status(_('merging %s and %s to %s\n')
357 repo.ui.status(_('merging %s and %s to %s\n')
358 % (lfutil.splitstandin(orig),
358 % (lfutil.splitstandin(orig),
359 lfutil.splitstandin(fcother.path()),
359 lfutil.splitstandin(fcother.path()),
360 lfutil.splitstandin(fcdest.path())))
360 lfutil.splitstandin(fcdest.path())))
361 else:
361 else:
362 repo.ui.status(_('merging %s\n')
362 repo.ui.status(_('merging %s\n')
363 % lfutil.splitstandin(fcdest.path()))
363 % lfutil.splitstandin(fcdest.path()))
364
364
365 if fcancestor.path() != fcother.path() and fcother.data() == \
365 if fcancestor.path() != fcother.path() and fcother.data() == \
366 fcancestor.data():
366 fcancestor.data():
367 return 0
367 return 0
368 if fcancestor.path() != fcdest.path() and fcdest.data() == \
368 if fcancestor.path() != fcdest.path() and fcdest.data() == \
369 fcancestor.data():
369 fcancestor.data():
370 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
370 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
371 return 0
371 return 0
372
372
373 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
373 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
374 'keep (l)ocal or take (o)ther?') %
374 'keep (l)ocal or take (o)ther?') %
375 lfutil.splitstandin(orig),
375 lfutil.splitstandin(orig),
376 (_('&Local'), _('&Other')), 0) == 0:
376 (_('&Local'), _('&Other')), 0) == 0:
377 return 0
377 return 0
378 else:
378 else:
379 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
379 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
380 return 0
380 return 0
381
381
382 # Copy first changes the matchers to match standins instead of
382 # Copy first changes the matchers to match standins instead of
383 # largefiles. Then it overrides util.copyfile in that function it
383 # largefiles. Then it overrides util.copyfile in that function it
384 # checks if the destination largefile already exists. It also keeps a
384 # checks if the destination largefile already exists. It also keeps a
385 # list of copied files so that the largefiles can be copied and the
385 # list of copied files so that the largefiles can be copied and the
386 # dirstate updated.
386 # dirstate updated.
387 def override_copy(orig, ui, repo, pats, opts, rename=False):
387 def override_copy(orig, ui, repo, pats, opts, rename=False):
388 # doesn't remove largefile on rename
388 # doesn't remove largefile on rename
389 if len(pats) < 2:
389 if len(pats) < 2:
390 # this isn't legal, let the original function deal with it
390 # this isn't legal, let the original function deal with it
391 return orig(ui, repo, pats, opts, rename)
391 return orig(ui, repo, pats, opts, rename)
392
392
393 def makestandin(relpath):
393 def makestandin(relpath):
394 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
394 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
395 return os.path.join(repo.wjoin(lfutil.standin(path)))
395 return os.path.join(repo.wjoin(lfutil.standin(path)))
396
396
397 fullpats = scmutil.expandpats(pats)
397 fullpats = scmutil.expandpats(pats)
398 dest = fullpats[-1]
398 dest = fullpats[-1]
399
399
400 if os.path.isdir(dest):
400 if os.path.isdir(dest):
401 if not os.path.isdir(makestandin(dest)):
401 if not os.path.isdir(makestandin(dest)):
402 os.makedirs(makestandin(dest))
402 os.makedirs(makestandin(dest))
403 # This could copy both lfiles and normal files in one command,
403 # This could copy both lfiles and normal files in one command,
404 # but we don't want to do that. First replace their matcher to
404 # but we don't want to do that. First replace their matcher to
405 # only match normal files and run it, then replace it to just
405 # only match normal files and run it, then replace it to just
406 # match largefiles and run it again.
406 # match largefiles and run it again.
407 nonormalfiles = False
407 nonormalfiles = False
408 nolfiles = False
408 nolfiles = False
409 try:
409 try:
410 try:
410 try:
411 installnormalfilesmatchfn(repo[None].manifest())
411 installnormalfilesmatchfn(repo[None].manifest())
412 result = orig(ui, repo, pats, opts, rename)
412 result = orig(ui, repo, pats, opts, rename)
413 except util.Abort, e:
413 except util.Abort, e:
414 if str(e) != 'no files to copy':
414 if str(e) != 'no files to copy':
415 raise e
415 raise e
416 else:
416 else:
417 nonormalfiles = True
417 nonormalfiles = True
418 result = 0
418 result = 0
419 finally:
419 finally:
420 restorematchfn()
420 restorematchfn()
421
421
422 # The first rename can cause our current working directory to be removed.
422 # The first rename can cause our current working directory to be removed.
423 # In that case there is nothing left to copy/rename so just quit.
423 # In that case there is nothing left to copy/rename so just quit.
424 try:
424 try:
425 repo.getcwd()
425 repo.getcwd()
426 except OSError:
426 except OSError:
427 return result
427 return result
428
428
429 try:
429 try:
430 try:
430 try:
431 # When we call orig below it creates the standins but we don't add them
431 # When we call orig below it creates the standins but we don't add them
432 # to the dir state until later so lock during that time.
432 # to the dir state until later so lock during that time.
433 wlock = repo.wlock()
433 wlock = repo.wlock()
434
434
435 manifest = repo[None].manifest()
435 manifest = repo[None].manifest()
436 oldmatch = None # for the closure
436 oldmatch = None # for the closure
437 def override_match(ctx, pats=[], opts={}, globbed=False,
437 def override_match(ctx, pats=[], opts={}, globbed=False,
438 default='relpath'):
438 default='relpath'):
439 newpats = []
439 newpats = []
440 # The patterns were previously mangled to add the standin
440 # The patterns were previously mangled to add the standin
441 # directory; we need to remove that now
441 # directory; we need to remove that now
442 for pat in pats:
442 for pat in pats:
443 if match_.patkind(pat) is None and lfutil.shortname in pat:
443 if match_.patkind(pat) is None and lfutil.shortname in pat:
444 newpats.append(pat.replace(lfutil.shortname, ''))
444 newpats.append(pat.replace(lfutil.shortname, ''))
445 else:
445 else:
446 newpats.append(pat)
446 newpats.append(pat)
447 match = oldmatch(ctx, newpats, opts, globbed, default)
447 match = oldmatch(ctx, newpats, opts, globbed, default)
448 m = copy.copy(match)
448 m = copy.copy(match)
449 lfile = lambda f: lfutil.standin(f) in manifest
449 lfile = lambda f: lfutil.standin(f) in manifest
450 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
450 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
451 m._fmap = set(m._files)
451 m._fmap = set(m._files)
452 orig_matchfn = m.matchfn
452 orig_matchfn = m.matchfn
453 m.matchfn = lambda f: (lfutil.isstandin(f) and
453 m.matchfn = lambda f: (lfutil.isstandin(f) and
454 (f in manifest) and
454 (f in manifest) and
455 orig_matchfn(lfutil.splitstandin(f)) or
455 orig_matchfn(lfutil.splitstandin(f)) or
456 None)
456 None)
457 return m
457 return m
458 oldmatch = installmatchfn(override_match)
458 oldmatch = installmatchfn(override_match)
459 listpats = []
459 listpats = []
460 for pat in pats:
460 for pat in pats:
461 if match_.patkind(pat) is not None:
461 if match_.patkind(pat) is not None:
462 listpats.append(pat)
462 listpats.append(pat)
463 else:
463 else:
464 listpats.append(makestandin(pat))
464 listpats.append(makestandin(pat))
465
465
466 try:
466 try:
467 origcopyfile = util.copyfile
467 origcopyfile = util.copyfile
468 copiedfiles = []
468 copiedfiles = []
469 def override_copyfile(src, dest):
469 def override_copyfile(src, dest):
470 if (lfutil.shortname in src and
470 if (lfutil.shortname in src and
471 dest.startswith(repo.wjoin(lfutil.shortname))):
471 dest.startswith(repo.wjoin(lfutil.shortname))):
472 destlfile = dest.replace(lfutil.shortname, '')
472 destlfile = dest.replace(lfutil.shortname, '')
473 if not opts['force'] and os.path.exists(destlfile):
473 if not opts['force'] and os.path.exists(destlfile):
474 raise IOError('',
474 raise IOError('',
475 _('destination largefile already exists'))
475 _('destination largefile already exists'))
476 copiedfiles.append((src, dest))
476 copiedfiles.append((src, dest))
477 origcopyfile(src, dest)
477 origcopyfile(src, dest)
478
478
479 util.copyfile = override_copyfile
479 util.copyfile = override_copyfile
480 result += orig(ui, repo, listpats, opts, rename)
480 result += orig(ui, repo, listpats, opts, rename)
481 finally:
481 finally:
482 util.copyfile = origcopyfile
482 util.copyfile = origcopyfile
483
483
484 lfdirstate = lfutil.openlfdirstate(ui, repo)
484 lfdirstate = lfutil.openlfdirstate(ui, repo)
485 for (src, dest) in copiedfiles:
485 for (src, dest) in copiedfiles:
486 if (lfutil.shortname in src and
486 if (lfutil.shortname in src and
487 dest.startswith(repo.wjoin(lfutil.shortname))):
487 dest.startswith(repo.wjoin(lfutil.shortname))):
488 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
488 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
489 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
489 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
490 destlfiledir = os.path.dirname(destlfile) or '.'
490 destlfiledir = os.path.dirname(destlfile) or '.'
491 if not os.path.isdir(destlfiledir):
491 if not os.path.isdir(destlfiledir):
492 os.makedirs(destlfiledir)
492 os.makedirs(destlfiledir)
493 if rename:
493 if rename:
494 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
494 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
495 lfdirstate.remove(srclfile)
495 lfdirstate.remove(srclfile)
496 else:
496 else:
497 util.copyfile(srclfile, destlfile)
497 util.copyfile(srclfile, destlfile)
498 lfdirstate.add(destlfile)
498 lfdirstate.add(destlfile)
499 lfdirstate.write()
499 lfdirstate.write()
500 except util.Abort, e:
500 except util.Abort, e:
501 if str(e) != 'no files to copy':
501 if str(e) != 'no files to copy':
502 raise e
502 raise e
503 else:
503 else:
504 nolfiles = True
504 nolfiles = True
505 finally:
505 finally:
506 restorematchfn()
506 restorematchfn()
507 wlock.release()
507 wlock.release()
508
508
509 if nolfiles and nonormalfiles:
509 if nolfiles and nonormalfiles:
510 raise util.Abort(_('no files to copy'))
510 raise util.Abort(_('no files to copy'))
511
511
512 return result
512 return result
513
513
514 # When the user calls revert, we have to be careful to not revert any
514 # When the user calls revert, we have to be careful to not revert any
515 # changes to other largefiles accidentally. This means we have to keep
515 # changes to other largefiles accidentally. This means we have to keep
516 # track of the largefiles that are being reverted so we only pull down
516 # track of the largefiles that are being reverted so we only pull down
517 # the necessary largefiles.
517 # the necessary largefiles.
518 #
518 #
519 # Standins are only updated (to match the hash of largefiles) before
519 # Standins are only updated (to match the hash of largefiles) before
520 # commits. Update the standins then run the original revert, changing
520 # commits. Update the standins then run the original revert, changing
521 # the matcher to hit standins instead of largefiles. Based on the
521 # the matcher to hit standins instead of largefiles. Based on the
522 # resulting standins update the largefiles. Then return the standins
522 # resulting standins update the largefiles. Then return the standins
523 # to their proper state
523 # to their proper state
524 def override_revert(orig, ui, repo, *pats, **opts):
524 def override_revert(orig, ui, repo, *pats, **opts):
525 # Because we put the standins in a bad state (by updating them)
525 # Because we put the standins in a bad state (by updating them)
526 # and then return them to a correct state we need to lock to
526 # and then return them to a correct state we need to lock to
527 # prevent others from changing them in their incorrect state.
527 # prevent others from changing them in their incorrect state.
528 wlock = repo.wlock()
528 wlock = repo.wlock()
529 try:
529 try:
530 lfdirstate = lfutil.openlfdirstate(ui, repo)
530 lfdirstate = lfutil.openlfdirstate(ui, repo)
531 (modified, added, removed, missing, unknown, ignored, clean) = \
531 (modified, added, removed, missing, unknown, ignored, clean) = \
532 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
532 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
533 for lfile in modified:
533 for lfile in modified:
534 lfutil.updatestandin(repo, lfutil.standin(lfile))
534 lfutil.updatestandin(repo, lfutil.standin(lfile))
535 for lfile in missing:
535 for lfile in missing:
536 os.unlink(repo.wjoin(lfutil.standin(lfile)))
536 os.unlink(repo.wjoin(lfutil.standin(lfile)))
537
537
538 try:
538 try:
539 ctx = repo[opts.get('rev')]
539 ctx = repo[opts.get('rev')]
540 oldmatch = None # for the closure
540 oldmatch = None # for the closure
541 def override_match(ctx, pats=[], opts={}, globbed=False,
541 def override_match(ctx, pats=[], opts={}, globbed=False,
542 default='relpath'):
542 default='relpath'):
543 match = oldmatch(ctx, pats, opts, globbed, default)
543 match = oldmatch(ctx, pats, opts, globbed, default)
544 m = copy.copy(match)
544 m = copy.copy(match)
545 def tostandin(f):
545 def tostandin(f):
546 if lfutil.standin(f) in ctx:
546 if lfutil.standin(f) in ctx:
547 return lfutil.standin(f)
547 return lfutil.standin(f)
548 elif lfutil.standin(f) in repo[None]:
548 elif lfutil.standin(f) in repo[None]:
549 return None
549 return None
550 return f
550 return f
551 m._files = [tostandin(f) for f in m._files]
551 m._files = [tostandin(f) for f in m._files]
552 m._files = [f for f in m._files if f is not None]
552 m._files = [f for f in m._files if f is not None]
553 m._fmap = set(m._files)
553 m._fmap = set(m._files)
554 orig_matchfn = m.matchfn
554 orig_matchfn = m.matchfn
555 def matchfn(f):
555 def matchfn(f):
556 if lfutil.isstandin(f):
556 if lfutil.isstandin(f):
557 # We need to keep track of what largefiles are being
557 # We need to keep track of what largefiles are being
558 # matched so we know which ones to update later --
558 # matched so we know which ones to update later --
559 # otherwise we accidentally revert changes to other
559 # otherwise we accidentally revert changes to other
560 # largefiles. This is repo-specific, so duckpunch the
560 # largefiles. This is repo-specific, so duckpunch the
561 # repo object to keep the list of largefiles for us
561 # repo object to keep the list of largefiles for us
562 # later.
562 # later.
563 if orig_matchfn(lfutil.splitstandin(f)) and \
563 if orig_matchfn(lfutil.splitstandin(f)) and \
564 (f in repo[None] or f in ctx):
564 (f in repo[None] or f in ctx):
565 lfileslist = getattr(repo, '_lfilestoupdate', [])
565 lfileslist = getattr(repo, '_lfilestoupdate', [])
566 lfileslist.append(lfutil.splitstandin(f))
566 lfileslist.append(lfutil.splitstandin(f))
567 repo._lfilestoupdate = lfileslist
567 repo._lfilestoupdate = lfileslist
568 return True
568 return True
569 else:
569 else:
570 return False
570 return False
571 return orig_matchfn(f)
571 return orig_matchfn(f)
572 m.matchfn = matchfn
572 m.matchfn = matchfn
573 return m
573 return m
574 oldmatch = installmatchfn(override_match)
574 oldmatch = installmatchfn(override_match)
575 scmutil.match
575 scmutil.match
576 matches = override_match(repo[None], pats, opts)
576 matches = override_match(repo[None], pats, opts)
577 orig(ui, repo, *pats, **opts)
577 orig(ui, repo, *pats, **opts)
578 finally:
578 finally:
579 restorematchfn()
579 restorematchfn()
580 lfileslist = getattr(repo, '_lfilestoupdate', [])
580 lfileslist = getattr(repo, '_lfilestoupdate', [])
581 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
581 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
582 printmessage=False)
582 printmessage=False)
583
583
584 # empty out the largefiles list so we start fresh next time
584 # empty out the largefiles list so we start fresh next time
585 repo._lfilestoupdate = []
585 repo._lfilestoupdate = []
586 for lfile in modified:
586 for lfile in modified:
587 if lfile in lfileslist:
587 if lfile in lfileslist:
588 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
588 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
589 in repo['.']:
589 in repo['.']:
590 lfutil.writestandin(repo, lfutil.standin(lfile),
590 lfutil.writestandin(repo, lfutil.standin(lfile),
591 repo['.'][lfile].data().strip(),
591 repo['.'][lfile].data().strip(),
592 'x' in repo['.'][lfile].flags())
592 'x' in repo['.'][lfile].flags())
593 lfdirstate = lfutil.openlfdirstate(ui, repo)
593 lfdirstate = lfutil.openlfdirstate(ui, repo)
594 for lfile in added:
594 for lfile in added:
595 standin = lfutil.standin(lfile)
595 standin = lfutil.standin(lfile)
596 if standin not in ctx and (standin in matches or opts.get('all')):
596 if standin not in ctx and (standin in matches or opts.get('all')):
597 if lfile in lfdirstate:
597 if lfile in lfdirstate:
598 lfdirstate.drop(lfile)
598 lfdirstate.drop(lfile)
599 util.unlinkpath(repo.wjoin(standin))
599 util.unlinkpath(repo.wjoin(standin))
600 lfdirstate.write()
600 lfdirstate.write()
601 finally:
601 finally:
602 wlock.release()
602 wlock.release()
603
603
604 def hg_update(orig, repo, node):
604 def hg_update(orig, repo, node):
605 # In order to not waste a lot of extra time during the update largefiles
605 # In order to not waste a lot of extra time during the update largefiles
606 # step, we keep track of the state of the standins before and after we
606 # step, we keep track of the state of the standins before and after we
607 # call the original update function, and only update the standins that
607 # call the original update function, and only update the standins that
608 # have changed in the hg.update() call
608 # have changed in the hg.update() call
609 oldstandins = lfutil.getstandinsstate(repo)
609 oldstandins = lfutil.getstandinsstate(repo)
610 result = orig(repo, node)
610 result = orig(repo, node)
611 newstandins = lfutil.getstandinsstate(repo)
611 newstandins = lfutil.getstandinsstate(repo)
612 tobeupdated = set(oldstandins).symmetric_difference(set(newstandins))
612 tobeupdated = set(oldstandins).symmetric_difference(set(newstandins))
613 filelist = []
613 filelist = []
614 for f in tobeupdated:
614 for f in tobeupdated:
615 if f[0] not in filelist:
615 if f[0] not in filelist:
616 filelist.append(f[0])
616 filelist.append(f[0])
617
617
618 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
618 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
619 return result
619 return result
620
620
621 def hg_clean(orig, repo, node, show_stats=True):
621 def hg_clean(orig, repo, node, show_stats=True):
622 result = orig(repo, node, show_stats)
622 result = orig(repo, node, show_stats)
623 lfcommands.updatelfiles(repo.ui, repo)
623 lfcommands.updatelfiles(repo.ui, repo)
624 return result
624 return result
625
625
626 def hg_merge(orig, repo, node, force=None, remind=True):
626 def hg_merge(orig, repo, node, force=None, remind=True):
627 # Mark the repo as being in the middle of a merge, so that
627 # Mark the repo as being in the middle of a merge, so that
628 # updatelfiles() will know that it needs to trust the standins in
628 # updatelfiles() will know that it needs to trust the standins in
629 # the working copy, not in the standins in the current node
629 # the working copy, not in the standins in the current node
630 repo._ismerging = True
630 repo._ismerging = True
631 try:
631 try:
632 result = orig(repo, node, force, remind)
632 result = orig(repo, node, force, remind)
633 lfcommands.updatelfiles(repo.ui, repo)
633 lfcommands.updatelfiles(repo.ui, repo)
634 finally:
634 finally:
635 repo._ismerging = False
635 repo._ismerging = False
636 return result
636 return result
637
637
638 # When we rebase a repository with remotely changed largefiles, we need to
638 # When we rebase a repository with remotely changed largefiles, we need to
639 # take some extra care so that the largefiles are correctly updated in the
639 # take some extra care so that the largefiles are correctly updated in the
640 # working copy
640 # working copy
641 def override_pull(orig, ui, repo, source=None, **opts):
641 def override_pull(orig, ui, repo, source=None, **opts):
642 if opts.get('rebase', False):
642 if opts.get('rebase', False):
643 repo._isrebasing = True
643 repo._isrebasing = True
644 try:
644 try:
645 if opts.get('update'):
645 if opts.get('update'):
646 del opts['update']
646 del opts['update']
647 ui.debug('--update and --rebase are not compatible, ignoring '
647 ui.debug('--update and --rebase are not compatible, ignoring '
648 'the update flag\n')
648 'the update flag\n')
649 del opts['rebase']
649 del opts['rebase']
650 cmdutil.bailifchanged(repo)
650 cmdutil.bailifchanged(repo)
651 revsprepull = len(repo)
651 revsprepull = len(repo)
652 origpostincoming = commands.postincoming
652 origpostincoming = commands.postincoming
653 def _dummy(*args, **kwargs):
653 def _dummy(*args, **kwargs):
654 pass
654 pass
655 commands.postincoming = _dummy
655 commands.postincoming = _dummy
656 repo.lfpullsource = source
656 repo.lfpullsource = source
657 if not source:
657 if not source:
658 source = 'default'
658 source = 'default'
659 try:
659 try:
660 result = commands.pull(ui, repo, source, **opts)
660 result = commands.pull(ui, repo, source, **opts)
661 finally:
661 finally:
662 commands.postincoming = origpostincoming
662 commands.postincoming = origpostincoming
663 revspostpull = len(repo)
663 revspostpull = len(repo)
664 if revspostpull > revsprepull:
664 if revspostpull > revsprepull:
665 result = result or rebase.rebase(ui, repo)
665 result = result or rebase.rebase(ui, repo)
666 finally:
666 finally:
667 repo._isrebasing = False
667 repo._isrebasing = False
668 else:
668 else:
669 repo.lfpullsource = source
669 repo.lfpullsource = source
670 if not source:
670 if not source:
671 source = 'default'
671 source = 'default'
672 oldheads = lfutil.getcurrentheads(repo)
672 oldheads = lfutil.getcurrentheads(repo)
673 result = orig(ui, repo, source, **opts)
673 result = orig(ui, repo, source, **opts)
674 # If we do not have the new largefiles for any new heads we pulled, we
674 # If we do not have the new largefiles for any new heads we pulled, we
675 # will run into a problem later if we try to merge or rebase with one of
675 # will run into a problem later if we try to merge or rebase with one of
676 # these heads, so cache the largefiles now direclty into the system
676 # these heads, so cache the largefiles now direclty into the system
677 # cache.
677 # cache.
678 ui.status(_("caching new largefiles\n"))
678 ui.status(_("caching new largefiles\n"))
679 numcached = 0
679 numcached = 0
680 heads = lfutil.getcurrentheads(repo)
680 heads = lfutil.getcurrentheads(repo)
681 newheads = set(heads).difference(set(oldheads))
681 newheads = set(heads).difference(set(oldheads))
682 for head in newheads:
682 for head in newheads:
683 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
683 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
684 numcached += len(cached)
684 numcached += len(cached)
685 ui.status(_("%d largefiles cached\n" % numcached))
685 ui.status(_("%d largefiles cached\n") % numcached)
686 return result
686 return result
687
687
688 def override_rebase(orig, ui, repo, **opts):
688 def override_rebase(orig, ui, repo, **opts):
689 repo._isrebasing = True
689 repo._isrebasing = True
690 try:
690 try:
691 orig(ui, repo, **opts)
691 orig(ui, repo, **opts)
692 finally:
692 finally:
693 repo._isrebasing = False
693 repo._isrebasing = False
694
694
695 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
695 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
696 prefix=None, mtime=None, subrepos=None):
696 prefix=None, mtime=None, subrepos=None):
697 # No need to lock because we are only reading history and
697 # No need to lock because we are only reading history and
698 # largefile caches, neither of which are modified.
698 # largefile caches, neither of which are modified.
699 lfcommands.cachelfiles(repo.ui, repo, node)
699 lfcommands.cachelfiles(repo.ui, repo, node)
700
700
701 if kind not in archival.archivers:
701 if kind not in archival.archivers:
702 raise util.Abort(_("unknown archive type '%s'") % kind)
702 raise util.Abort(_("unknown archive type '%s'") % kind)
703
703
704 ctx = repo[node]
704 ctx = repo[node]
705
705
706 if kind == 'files':
706 if kind == 'files':
707 if prefix:
707 if prefix:
708 raise util.Abort(
708 raise util.Abort(
709 _('cannot give prefix when archiving to files'))
709 _('cannot give prefix when archiving to files'))
710 else:
710 else:
711 prefix = archival.tidyprefix(dest, kind, prefix)
711 prefix = archival.tidyprefix(dest, kind, prefix)
712
712
713 def write(name, mode, islink, getdata):
713 def write(name, mode, islink, getdata):
714 if matchfn and not matchfn(name):
714 if matchfn and not matchfn(name):
715 return
715 return
716 data = getdata()
716 data = getdata()
717 if decode:
717 if decode:
718 data = repo.wwritedata(name, data)
718 data = repo.wwritedata(name, data)
719 archiver.addfile(prefix + name, mode, islink, data)
719 archiver.addfile(prefix + name, mode, islink, data)
720
720
721 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
721 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
722
722
723 if repo.ui.configbool("ui", "archivemeta", True):
723 if repo.ui.configbool("ui", "archivemeta", True):
724 def metadata():
724 def metadata():
725 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
725 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
726 hex(repo.changelog.node(0)), hex(node), ctx.branch())
726 hex(repo.changelog.node(0)), hex(node), ctx.branch())
727
727
728 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
728 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
729 if repo.tagtype(t) == 'global')
729 if repo.tagtype(t) == 'global')
730 if not tags:
730 if not tags:
731 repo.ui.pushbuffer()
731 repo.ui.pushbuffer()
732 opts = {'template': '{latesttag}\n{latesttagdistance}',
732 opts = {'template': '{latesttag}\n{latesttagdistance}',
733 'style': '', 'patch': None, 'git': None}
733 'style': '', 'patch': None, 'git': None}
734 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
734 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
735 ltags, dist = repo.ui.popbuffer().split('\n')
735 ltags, dist = repo.ui.popbuffer().split('\n')
736 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
736 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
737 tags += 'latesttagdistance: %s\n' % dist
737 tags += 'latesttagdistance: %s\n' % dist
738
738
739 return base + tags
739 return base + tags
740
740
741 write('.hg_archival.txt', 0644, False, metadata)
741 write('.hg_archival.txt', 0644, False, metadata)
742
742
743 for f in ctx:
743 for f in ctx:
744 ff = ctx.flags(f)
744 ff = ctx.flags(f)
745 getdata = ctx[f].data
745 getdata = ctx[f].data
746 if lfutil.isstandin(f):
746 if lfutil.isstandin(f):
747 path = lfutil.findfile(repo, getdata().strip())
747 path = lfutil.findfile(repo, getdata().strip())
748 if path is None:
748 if path is None:
749 raise util.Abort(
749 raise util.Abort(
750 _('largefile %s not found in repo store or system cache')
750 _('largefile %s not found in repo store or system cache')
751 % lfutil.splitstandin(f))
751 % lfutil.splitstandin(f))
752 f = lfutil.splitstandin(f)
752 f = lfutil.splitstandin(f)
753
753
754 def getdatafn():
754 def getdatafn():
755 fd = None
755 fd = None
756 try:
756 try:
757 fd = open(path, 'rb')
757 fd = open(path, 'rb')
758 return fd.read()
758 return fd.read()
759 finally:
759 finally:
760 if fd:
760 if fd:
761 fd.close()
761 fd.close()
762
762
763 getdata = getdatafn
763 getdata = getdatafn
764 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
764 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
765
765
766 if subrepos:
766 if subrepos:
767 for subpath in ctx.substate:
767 for subpath in ctx.substate:
768 sub = ctx.sub(subpath)
768 sub = ctx.sub(subpath)
769 sub.archive(repo.ui, archiver, prefix)
769 sub.archive(repo.ui, archiver, prefix)
770
770
771 archiver.done()
771 archiver.done()
772
772
773 # If a largefile is modified, the change is not reflected in its
773 # If a largefile is modified, the change is not reflected in its
774 # standin until a commit. cmdutil.bailifchanged() raises an exception
774 # standin until a commit. cmdutil.bailifchanged() raises an exception
775 # if the repo has uncommitted changes. Wrap it to also check if
775 # if the repo has uncommitted changes. Wrap it to also check if
776 # largefiles were changed. This is used by bisect and backout.
776 # largefiles were changed. This is used by bisect and backout.
777 def override_bailifchanged(orig, repo):
777 def override_bailifchanged(orig, repo):
778 orig(repo)
778 orig(repo)
779 repo.lfstatus = True
779 repo.lfstatus = True
780 modified, added, removed, deleted = repo.status()[:4]
780 modified, added, removed, deleted = repo.status()[:4]
781 repo.lfstatus = False
781 repo.lfstatus = False
782 if modified or added or removed or deleted:
782 if modified or added or removed or deleted:
783 raise util.Abort(_('outstanding uncommitted changes'))
783 raise util.Abort(_('outstanding uncommitted changes'))
784
784
785 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
785 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
786 def override_fetch(orig, ui, repo, *pats, **opts):
786 def override_fetch(orig, ui, repo, *pats, **opts):
787 repo.lfstatus = True
787 repo.lfstatus = True
788 modified, added, removed, deleted = repo.status()[:4]
788 modified, added, removed, deleted = repo.status()[:4]
789 repo.lfstatus = False
789 repo.lfstatus = False
790 if modified or added or removed or deleted:
790 if modified or added or removed or deleted:
791 raise util.Abort(_('outstanding uncommitted changes'))
791 raise util.Abort(_('outstanding uncommitted changes'))
792 return orig(ui, repo, *pats, **opts)
792 return orig(ui, repo, *pats, **opts)
793
793
794 def override_forget(orig, ui, repo, *pats, **opts):
794 def override_forget(orig, ui, repo, *pats, **opts):
795 installnormalfilesmatchfn(repo[None].manifest())
795 installnormalfilesmatchfn(repo[None].manifest())
796 orig(ui, repo, *pats, **opts)
796 orig(ui, repo, *pats, **opts)
797 restorematchfn()
797 restorematchfn()
798 m = scmutil.match(repo[None], pats, opts)
798 m = scmutil.match(repo[None], pats, opts)
799
799
800 try:
800 try:
801 repo.lfstatus = True
801 repo.lfstatus = True
802 s = repo.status(match=m, clean=True)
802 s = repo.status(match=m, clean=True)
803 finally:
803 finally:
804 repo.lfstatus = False
804 repo.lfstatus = False
805 forget = sorted(s[0] + s[1] + s[3] + s[6])
805 forget = sorted(s[0] + s[1] + s[3] + s[6])
806 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
806 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
807
807
808 for f in forget:
808 for f in forget:
809 if lfutil.standin(f) not in repo.dirstate and not \
809 if lfutil.standin(f) not in repo.dirstate and not \
810 os.path.isdir(m.rel(lfutil.standin(f))):
810 os.path.isdir(m.rel(lfutil.standin(f))):
811 ui.warn(_('not removing %s: file is already untracked\n')
811 ui.warn(_('not removing %s: file is already untracked\n')
812 % m.rel(f))
812 % m.rel(f))
813
813
814 for f in forget:
814 for f in forget:
815 if ui.verbose or not m.exact(f):
815 if ui.verbose or not m.exact(f):
816 ui.status(_('removing %s\n') % m.rel(f))
816 ui.status(_('removing %s\n') % m.rel(f))
817
817
818 # Need to lock because standin files are deleted then removed from the
818 # Need to lock because standin files are deleted then removed from the
819 # repository and we could race inbetween.
819 # repository and we could race inbetween.
820 wlock = repo.wlock()
820 wlock = repo.wlock()
821 try:
821 try:
822 lfdirstate = lfutil.openlfdirstate(ui, repo)
822 lfdirstate = lfutil.openlfdirstate(ui, repo)
823 for f in forget:
823 for f in forget:
824 if lfdirstate[f] == 'a':
824 if lfdirstate[f] == 'a':
825 lfdirstate.drop(f)
825 lfdirstate.drop(f)
826 else:
826 else:
827 lfdirstate.remove(f)
827 lfdirstate.remove(f)
828 lfdirstate.write()
828 lfdirstate.write()
829 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
829 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
830 unlink=True)
830 unlink=True)
831 finally:
831 finally:
832 wlock.release()
832 wlock.release()
833
833
834 def getoutgoinglfiles(ui, repo, dest=None, **opts):
834 def getoutgoinglfiles(ui, repo, dest=None, **opts):
835 dest = ui.expandpath(dest or 'default-push', dest or 'default')
835 dest = ui.expandpath(dest or 'default-push', dest or 'default')
836 dest, branches = hg.parseurl(dest, opts.get('branch'))
836 dest, branches = hg.parseurl(dest, opts.get('branch'))
837 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
837 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
838 if revs:
838 if revs:
839 revs = [repo.lookup(rev) for rev in revs]
839 revs = [repo.lookup(rev) for rev in revs]
840
840
841 remoteui = hg.remoteui
841 remoteui = hg.remoteui
842
842
843 try:
843 try:
844 remote = hg.repository(remoteui(repo, opts), dest)
844 remote = hg.repository(remoteui(repo, opts), dest)
845 except error.RepoError:
845 except error.RepoError:
846 return None
846 return None
847 o = lfutil.findoutgoing(repo, remote, False)
847 o = lfutil.findoutgoing(repo, remote, False)
848 if not o:
848 if not o:
849 return None
849 return None
850 o = repo.changelog.nodesbetween(o, revs)[0]
850 o = repo.changelog.nodesbetween(o, revs)[0]
851 if opts.get('newest_first'):
851 if opts.get('newest_first'):
852 o.reverse()
852 o.reverse()
853
853
854 toupload = set()
854 toupload = set()
855 for n in o:
855 for n in o:
856 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
856 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
857 ctx = repo[n]
857 ctx = repo[n]
858 files = set(ctx.files())
858 files = set(ctx.files())
859 if len(parents) == 2:
859 if len(parents) == 2:
860 mc = ctx.manifest()
860 mc = ctx.manifest()
861 mp1 = ctx.parents()[0].manifest()
861 mp1 = ctx.parents()[0].manifest()
862 mp2 = ctx.parents()[1].manifest()
862 mp2 = ctx.parents()[1].manifest()
863 for f in mp1:
863 for f in mp1:
864 if f not in mc:
864 if f not in mc:
865 files.add(f)
865 files.add(f)
866 for f in mp2:
866 for f in mp2:
867 if f not in mc:
867 if f not in mc:
868 files.add(f)
868 files.add(f)
869 for f in mc:
869 for f in mc:
870 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
870 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
871 files.add(f)
871 files.add(f)
872 toupload = toupload.union(
872 toupload = toupload.union(
873 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
873 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
874 return toupload
874 return toupload
875
875
876 def override_outgoing(orig, ui, repo, dest=None, **opts):
876 def override_outgoing(orig, ui, repo, dest=None, **opts):
877 orig(ui, repo, dest, **opts)
877 orig(ui, repo, dest, **opts)
878
878
879 if opts.pop('large', None):
879 if opts.pop('large', None):
880 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
880 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
881 if toupload is None:
881 if toupload is None:
882 ui.status(_('largefiles: No remote repo\n'))
882 ui.status(_('largefiles: No remote repo\n'))
883 else:
883 else:
884 ui.status(_('largefiles to upload:\n'))
884 ui.status(_('largefiles to upload:\n'))
885 for file in toupload:
885 for file in toupload:
886 ui.status(lfutil.splitstandin(file) + '\n')
886 ui.status(lfutil.splitstandin(file) + '\n')
887 ui.status('\n')
887 ui.status('\n')
888
888
889 def override_summary(orig, ui, repo, *pats, **opts):
889 def override_summary(orig, ui, repo, *pats, **opts):
890 try:
890 try:
891 repo.lfstatus = True
891 repo.lfstatus = True
892 orig(ui, repo, *pats, **opts)
892 orig(ui, repo, *pats, **opts)
893 finally:
893 finally:
894 repo.lfstatus = False
894 repo.lfstatus = False
895
895
896 if opts.pop('large', None):
896 if opts.pop('large', None):
897 toupload = getoutgoinglfiles(ui, repo, None, **opts)
897 toupload = getoutgoinglfiles(ui, repo, None, **opts)
898 if toupload is None:
898 if toupload is None:
899 ui.status(_('largefiles: No remote repo\n'))
899 ui.status(_('largefiles: No remote repo\n'))
900 else:
900 else:
901 ui.status(_('largefiles: %d to upload\n') % len(toupload))
901 ui.status(_('largefiles: %d to upload\n') % len(toupload))
902
902
903 def override_addremove(orig, ui, repo, *pats, **opts):
903 def override_addremove(orig, ui, repo, *pats, **opts):
904 # Get the list of missing largefiles so we can remove them
904 # Get the list of missing largefiles so we can remove them
905 lfdirstate = lfutil.openlfdirstate(ui, repo)
905 lfdirstate = lfutil.openlfdirstate(ui, repo)
906 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
906 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
907 False, False)
907 False, False)
908 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
908 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
909
909
910 # Call into the normal remove code, but the removing of the standin, we want
910 # Call into the normal remove code, but the removing of the standin, we want
911 # to have handled by original addremove. Monkey patching here makes sure
911 # to have handled by original addremove. Monkey patching here makes sure
912 # we don't remove the standin in the largefiles code, preventing a very
912 # we don't remove the standin in the largefiles code, preventing a very
913 # confused state later.
913 # confused state later.
914 if missing:
914 if missing:
915 repo._isaddremove = True
915 repo._isaddremove = True
916 remove_largefiles(ui, repo, *missing, **opts)
916 remove_largefiles(ui, repo, *missing, **opts)
917 repo._isaddremove = False
917 repo._isaddremove = False
918 # Call into the normal add code, and any files that *should* be added as
918 # Call into the normal add code, and any files that *should* be added as
919 # largefiles will be
919 # largefiles will be
920 add_largefiles(ui, repo, *pats, **opts)
920 add_largefiles(ui, repo, *pats, **opts)
921 # Now that we've handled largefiles, hand off to the original addremove
921 # Now that we've handled largefiles, hand off to the original addremove
922 # function to take care of the rest. Make sure it doesn't do anything with
922 # function to take care of the rest. Make sure it doesn't do anything with
923 # largefiles by installing a matcher that will ignore them.
923 # largefiles by installing a matcher that will ignore them.
924 installnormalfilesmatchfn(repo[None].manifest())
924 installnormalfilesmatchfn(repo[None].manifest())
925 result = orig(ui, repo, *pats, **opts)
925 result = orig(ui, repo, *pats, **opts)
926 restorematchfn()
926 restorematchfn()
927 return result
927 return result
928
928
929 # Calling purge with --all will cause the largefiles to be deleted.
929 # Calling purge with --all will cause the largefiles to be deleted.
930 # Override repo.status to prevent this from happening.
930 # Override repo.status to prevent this from happening.
931 def override_purge(orig, ui, repo, *dirs, **opts):
931 def override_purge(orig, ui, repo, *dirs, **opts):
932 oldstatus = repo.status
932 oldstatus = repo.status
933 def override_status(node1='.', node2=None, match=None, ignored=False,
933 def override_status(node1='.', node2=None, match=None, ignored=False,
934 clean=False, unknown=False, listsubrepos=False):
934 clean=False, unknown=False, listsubrepos=False):
935 r = oldstatus(node1, node2, match, ignored, clean, unknown,
935 r = oldstatus(node1, node2, match, ignored, clean, unknown,
936 listsubrepos)
936 listsubrepos)
937 lfdirstate = lfutil.openlfdirstate(ui, repo)
937 lfdirstate = lfutil.openlfdirstate(ui, repo)
938 modified, added, removed, deleted, unknown, ignored, clean = r
938 modified, added, removed, deleted, unknown, ignored, clean = r
939 unknown = [f for f in unknown if lfdirstate[f] == '?']
939 unknown = [f for f in unknown if lfdirstate[f] == '?']
940 ignored = [f for f in ignored if lfdirstate[f] == '?']
940 ignored = [f for f in ignored if lfdirstate[f] == '?']
941 return modified, added, removed, deleted, unknown, ignored, clean
941 return modified, added, removed, deleted, unknown, ignored, clean
942 repo.status = override_status
942 repo.status = override_status
943 orig(ui, repo, *dirs, **opts)
943 orig(ui, repo, *dirs, **opts)
944 repo.status = oldstatus
944 repo.status = oldstatus
945
945
946 def override_rollback(orig, ui, repo, **opts):
946 def override_rollback(orig, ui, repo, **opts):
947 result = orig(ui, repo, **opts)
947 result = orig(ui, repo, **opts)
948 merge.update(repo, node=None, branchmerge=False, force=True,
948 merge.update(repo, node=None, branchmerge=False, force=True,
949 partial=lfutil.isstandin)
949 partial=lfutil.isstandin)
950 wlock = repo.wlock()
950 wlock = repo.wlock()
951 try:
951 try:
952 lfdirstate = lfutil.openlfdirstate(ui, repo)
952 lfdirstate = lfutil.openlfdirstate(ui, repo)
953 lfiles = lfutil.listlfiles(repo)
953 lfiles = lfutil.listlfiles(repo)
954 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
954 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
955 for file in lfiles:
955 for file in lfiles:
956 if file in oldlfiles:
956 if file in oldlfiles:
957 lfdirstate.normallookup(file)
957 lfdirstate.normallookup(file)
958 else:
958 else:
959 lfdirstate.add(file)
959 lfdirstate.add(file)
960 lfdirstate.write()
960 lfdirstate.write()
961 finally:
961 finally:
962 wlock.release()
962 wlock.release()
963 return result
963 return result
964
964
965 def override_transplant(orig, ui, repo, *revs, **opts):
965 def override_transplant(orig, ui, repo, *revs, **opts):
966 try:
966 try:
967 repo._istransplanting = True
967 repo._istransplanting = True
968 result = orig(ui, repo, *revs, **opts)
968 result = orig(ui, repo, *revs, **opts)
969 lfcommands.updatelfiles(ui, repo, filelist=None,
969 lfcommands.updatelfiles(ui, repo, filelist=None,
970 printmessage=False)
970 printmessage=False)
971 finally:
971 finally:
972 repo._istransplanting = False
972 repo._istransplanting = False
973 return result
973 return result
@@ -1,3407 +1,3408 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.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 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49 '''
49 '''
50
50
51 from mercurial.i18n import _
51 from mercurial.i18n import _
52 from mercurial.node import bin, hex, short, nullid, nullrev
52 from mercurial.node import bin, hex, short, nullid, nullrev
53 from mercurial.lock import release
53 from mercurial.lock import release
54 from mercurial import commands, cmdutil, hg, scmutil, util, revset
54 from mercurial import commands, cmdutil, hg, scmutil, util, revset
55 from mercurial import repair, extensions, url, error, phases
55 from mercurial import repair, extensions, url, error, phases
56 from mercurial import patch as patchmod
56 from mercurial import patch as patchmod
57 import os, re, errno, shutil
57 import os, re, errno, shutil
58
58
59 commands.norepo += " qclone"
59 commands.norepo += " qclone"
60
60
61 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
61 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
62
62
63 cmdtable = {}
63 cmdtable = {}
64 command = cmdutil.command(cmdtable)
64 command = cmdutil.command(cmdtable)
65
65
66 # Patch names looks like unix-file names.
66 # Patch names looks like unix-file names.
67 # They must be joinable with queue directory and result in the patch path.
67 # They must be joinable with queue directory and result in the patch path.
68 normname = util.normpath
68 normname = util.normpath
69
69
70 class statusentry(object):
70 class statusentry(object):
71 def __init__(self, node, name):
71 def __init__(self, node, name):
72 self.node, self.name = node, name
72 self.node, self.name = node, name
73 def __repr__(self):
73 def __repr__(self):
74 return hex(self.node) + ':' + self.name
74 return hex(self.node) + ':' + self.name
75
75
76 class patchheader(object):
76 class patchheader(object):
77 def __init__(self, pf, plainmode=False):
77 def __init__(self, pf, plainmode=False):
78 def eatdiff(lines):
78 def eatdiff(lines):
79 while lines:
79 while lines:
80 l = lines[-1]
80 l = lines[-1]
81 if (l.startswith("diff -") or
81 if (l.startswith("diff -") or
82 l.startswith("Index:") or
82 l.startswith("Index:") or
83 l.startswith("===========")):
83 l.startswith("===========")):
84 del lines[-1]
84 del lines[-1]
85 else:
85 else:
86 break
86 break
87 def eatempty(lines):
87 def eatempty(lines):
88 while lines:
88 while lines:
89 if not lines[-1].strip():
89 if not lines[-1].strip():
90 del lines[-1]
90 del lines[-1]
91 else:
91 else:
92 break
92 break
93
93
94 message = []
94 message = []
95 comments = []
95 comments = []
96 user = None
96 user = None
97 date = None
97 date = None
98 parent = None
98 parent = None
99 format = None
99 format = None
100 subject = None
100 subject = None
101 branch = None
101 branch = None
102 nodeid = None
102 nodeid = None
103 diffstart = 0
103 diffstart = 0
104
104
105 for line in file(pf):
105 for line in file(pf):
106 line = line.rstrip()
106 line = line.rstrip()
107 if (line.startswith('diff --git')
107 if (line.startswith('diff --git')
108 or (diffstart and line.startswith('+++ '))):
108 or (diffstart and line.startswith('+++ '))):
109 diffstart = 2
109 diffstart = 2
110 break
110 break
111 diffstart = 0 # reset
111 diffstart = 0 # reset
112 if line.startswith("--- "):
112 if line.startswith("--- "):
113 diffstart = 1
113 diffstart = 1
114 continue
114 continue
115 elif format == "hgpatch":
115 elif format == "hgpatch":
116 # parse values when importing the result of an hg export
116 # parse values when importing the result of an hg export
117 if line.startswith("# User "):
117 if line.startswith("# User "):
118 user = line[7:]
118 user = line[7:]
119 elif line.startswith("# Date "):
119 elif line.startswith("# Date "):
120 date = line[7:]
120 date = line[7:]
121 elif line.startswith("# Parent "):
121 elif line.startswith("# Parent "):
122 parent = line[9:].lstrip()
122 parent = line[9:].lstrip()
123 elif line.startswith("# Branch "):
123 elif line.startswith("# Branch "):
124 branch = line[9:]
124 branch = line[9:]
125 elif line.startswith("# Node ID "):
125 elif line.startswith("# Node ID "):
126 nodeid = line[10:]
126 nodeid = line[10:]
127 elif not line.startswith("# ") and line:
127 elif not line.startswith("# ") and line:
128 message.append(line)
128 message.append(line)
129 format = None
129 format = None
130 elif line == '# HG changeset patch':
130 elif line == '# HG changeset patch':
131 message = []
131 message = []
132 format = "hgpatch"
132 format = "hgpatch"
133 elif (format != "tagdone" and (line.startswith("Subject: ") or
133 elif (format != "tagdone" and (line.startswith("Subject: ") or
134 line.startswith("subject: "))):
134 line.startswith("subject: "))):
135 subject = line[9:]
135 subject = line[9:]
136 format = "tag"
136 format = "tag"
137 elif (format != "tagdone" and (line.startswith("From: ") or
137 elif (format != "tagdone" and (line.startswith("From: ") or
138 line.startswith("from: "))):
138 line.startswith("from: "))):
139 user = line[6:]
139 user = line[6:]
140 format = "tag"
140 format = "tag"
141 elif (format != "tagdone" and (line.startswith("Date: ") or
141 elif (format != "tagdone" and (line.startswith("Date: ") or
142 line.startswith("date: "))):
142 line.startswith("date: "))):
143 date = line[6:]
143 date = line[6:]
144 format = "tag"
144 format = "tag"
145 elif format == "tag" and line == "":
145 elif format == "tag" and line == "":
146 # when looking for tags (subject: from: etc) they
146 # when looking for tags (subject: from: etc) they
147 # end once you find a blank line in the source
147 # end once you find a blank line in the source
148 format = "tagdone"
148 format = "tagdone"
149 elif message or line:
149 elif message or line:
150 message.append(line)
150 message.append(line)
151 comments.append(line)
151 comments.append(line)
152
152
153 eatdiff(message)
153 eatdiff(message)
154 eatdiff(comments)
154 eatdiff(comments)
155 # Remember the exact starting line of the patch diffs before consuming
155 # Remember the exact starting line of the patch diffs before consuming
156 # empty lines, for external use by TortoiseHg and others
156 # empty lines, for external use by TortoiseHg and others
157 self.diffstartline = len(comments)
157 self.diffstartline = len(comments)
158 eatempty(message)
158 eatempty(message)
159 eatempty(comments)
159 eatempty(comments)
160
160
161 # make sure message isn't empty
161 # make sure message isn't empty
162 if format and format.startswith("tag") and subject:
162 if format and format.startswith("tag") and subject:
163 message.insert(0, "")
163 message.insert(0, "")
164 message.insert(0, subject)
164 message.insert(0, subject)
165
165
166 self.message = message
166 self.message = message
167 self.comments = comments
167 self.comments = comments
168 self.user = user
168 self.user = user
169 self.date = date
169 self.date = date
170 self.parent = parent
170 self.parent = parent
171 # nodeid and branch are for external use by TortoiseHg and others
171 # nodeid and branch are for external use by TortoiseHg and others
172 self.nodeid = nodeid
172 self.nodeid = nodeid
173 self.branch = branch
173 self.branch = branch
174 self.haspatch = diffstart > 1
174 self.haspatch = diffstart > 1
175 self.plainmode = plainmode
175 self.plainmode = plainmode
176
176
177 def setuser(self, user):
177 def setuser(self, user):
178 if not self.updateheader(['From: ', '# User '], user):
178 if not self.updateheader(['From: ', '# User '], user):
179 try:
179 try:
180 patchheaderat = self.comments.index('# HG changeset patch')
180 patchheaderat = self.comments.index('# HG changeset patch')
181 self.comments.insert(patchheaderat + 1, '# User ' + user)
181 self.comments.insert(patchheaderat + 1, '# User ' + user)
182 except ValueError:
182 except ValueError:
183 if self.plainmode or self._hasheader(['Date: ']):
183 if self.plainmode or self._hasheader(['Date: ']):
184 self.comments = ['From: ' + user] + self.comments
184 self.comments = ['From: ' + user] + self.comments
185 else:
185 else:
186 tmp = ['# HG changeset patch', '# User ' + user, '']
186 tmp = ['# HG changeset patch', '# User ' + user, '']
187 self.comments = tmp + self.comments
187 self.comments = tmp + self.comments
188 self.user = user
188 self.user = user
189
189
190 def setdate(self, date):
190 def setdate(self, date):
191 if not self.updateheader(['Date: ', '# Date '], date):
191 if not self.updateheader(['Date: ', '# Date '], date):
192 try:
192 try:
193 patchheaderat = self.comments.index('# HG changeset patch')
193 patchheaderat = self.comments.index('# HG changeset patch')
194 self.comments.insert(patchheaderat + 1, '# Date ' + date)
194 self.comments.insert(patchheaderat + 1, '# Date ' + date)
195 except ValueError:
195 except ValueError:
196 if self.plainmode or self._hasheader(['From: ']):
196 if self.plainmode or self._hasheader(['From: ']):
197 self.comments = ['Date: ' + date] + self.comments
197 self.comments = ['Date: ' + date] + self.comments
198 else:
198 else:
199 tmp = ['# HG changeset patch', '# Date ' + date, '']
199 tmp = ['# HG changeset patch', '# Date ' + date, '']
200 self.comments = tmp + self.comments
200 self.comments = tmp + self.comments
201 self.date = date
201 self.date = date
202
202
203 def setparent(self, parent):
203 def setparent(self, parent):
204 if not self.updateheader(['# Parent '], parent):
204 if not self.updateheader(['# Parent '], parent):
205 try:
205 try:
206 patchheaderat = self.comments.index('# HG changeset patch')
206 patchheaderat = self.comments.index('# HG changeset patch')
207 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
207 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
208 except ValueError:
208 except ValueError:
209 pass
209 pass
210 self.parent = parent
210 self.parent = parent
211
211
212 def setmessage(self, message):
212 def setmessage(self, message):
213 if self.comments:
213 if self.comments:
214 self._delmsg()
214 self._delmsg()
215 self.message = [message]
215 self.message = [message]
216 self.comments += self.message
216 self.comments += self.message
217
217
218 def updateheader(self, prefixes, new):
218 def updateheader(self, prefixes, new):
219 '''Update all references to a field in the patch header.
219 '''Update all references to a field in the patch header.
220 Return whether the field is present.'''
220 Return whether the field is present.'''
221 res = False
221 res = False
222 for prefix in prefixes:
222 for prefix in prefixes:
223 for i in xrange(len(self.comments)):
223 for i in xrange(len(self.comments)):
224 if self.comments[i].startswith(prefix):
224 if self.comments[i].startswith(prefix):
225 self.comments[i] = prefix + new
225 self.comments[i] = prefix + new
226 res = True
226 res = True
227 break
227 break
228 return res
228 return res
229
229
230 def _hasheader(self, prefixes):
230 def _hasheader(self, prefixes):
231 '''Check if a header starts with any of the given prefixes.'''
231 '''Check if a header starts with any of the given prefixes.'''
232 for prefix in prefixes:
232 for prefix in prefixes:
233 for comment in self.comments:
233 for comment in self.comments:
234 if comment.startswith(prefix):
234 if comment.startswith(prefix):
235 return True
235 return True
236 return False
236 return False
237
237
238 def __str__(self):
238 def __str__(self):
239 if not self.comments:
239 if not self.comments:
240 return ''
240 return ''
241 return '\n'.join(self.comments) + '\n\n'
241 return '\n'.join(self.comments) + '\n\n'
242
242
243 def _delmsg(self):
243 def _delmsg(self):
244 '''Remove existing message, keeping the rest of the comments fields.
244 '''Remove existing message, keeping the rest of the comments fields.
245 If comments contains 'subject: ', message will prepend
245 If comments contains 'subject: ', message will prepend
246 the field and a blank line.'''
246 the field and a blank line.'''
247 if self.message:
247 if self.message:
248 subj = 'subject: ' + self.message[0].lower()
248 subj = 'subject: ' + self.message[0].lower()
249 for i in xrange(len(self.comments)):
249 for i in xrange(len(self.comments)):
250 if subj == self.comments[i].lower():
250 if subj == self.comments[i].lower():
251 del self.comments[i]
251 del self.comments[i]
252 self.message = self.message[2:]
252 self.message = self.message[2:]
253 break
253 break
254 ci = 0
254 ci = 0
255 for mi in self.message:
255 for mi in self.message:
256 while mi != self.comments[ci]:
256 while mi != self.comments[ci]:
257 ci += 1
257 ci += 1
258 del self.comments[ci]
258 del self.comments[ci]
259
259
260 def newcommit(repo, phase, *args, **kwargs):
260 def newcommit(repo, phase, *args, **kwargs):
261 """helper dedicated to ensure a commit respect mq.secret setting
261 """helper dedicated to ensure a commit respect mq.secret setting
262
262
263 It should be used instead of repo.commit inside the mq source for operation
263 It should be used instead of repo.commit inside the mq source for operation
264 creating new changeset.
264 creating new changeset.
265 """
265 """
266 if phase is None:
266 if phase is None:
267 if repo.ui.configbool('mq', 'secret', False):
267 if repo.ui.configbool('mq', 'secret', False):
268 phase = phases.secret
268 phase = phases.secret
269 if phase is not None:
269 if phase is not None:
270 backup = repo.ui.backupconfig('phases', 'new-commit')
270 backup = repo.ui.backupconfig('phases', 'new-commit')
271 # Marking the repository as committing an mq patch can be used
271 # Marking the repository as committing an mq patch can be used
272 # to optimize operations like _branchtags().
272 # to optimize operations like _branchtags().
273 repo._committingpatch = True
273 repo._committingpatch = True
274 try:
274 try:
275 if phase is not None:
275 if phase is not None:
276 repo.ui.setconfig('phases', 'new-commit', phase)
276 repo.ui.setconfig('phases', 'new-commit', phase)
277 return repo.commit(*args, **kwargs)
277 return repo.commit(*args, **kwargs)
278 finally:
278 finally:
279 repo._committingpatch = False
279 repo._committingpatch = False
280 if phase is not None:
280 if phase is not None:
281 repo.ui.restoreconfig(backup)
281 repo.ui.restoreconfig(backup)
282
282
283 class queue(object):
283 class queue(object):
284 def __init__(self, ui, path, patchdir=None):
284 def __init__(self, ui, path, patchdir=None):
285 self.basepath = path
285 self.basepath = path
286 try:
286 try:
287 fh = open(os.path.join(path, 'patches.queue'))
287 fh = open(os.path.join(path, 'patches.queue'))
288 cur = fh.read().rstrip()
288 cur = fh.read().rstrip()
289 fh.close()
289 fh.close()
290 if not cur:
290 if not cur:
291 curpath = os.path.join(path, 'patches')
291 curpath = os.path.join(path, 'patches')
292 else:
292 else:
293 curpath = os.path.join(path, 'patches-' + cur)
293 curpath = os.path.join(path, 'patches-' + cur)
294 except IOError:
294 except IOError:
295 curpath = os.path.join(path, 'patches')
295 curpath = os.path.join(path, 'patches')
296 self.path = patchdir or curpath
296 self.path = patchdir or curpath
297 self.opener = scmutil.opener(self.path)
297 self.opener = scmutil.opener(self.path)
298 self.ui = ui
298 self.ui = ui
299 self.applieddirty = False
299 self.applieddirty = False
300 self.seriesdirty = False
300 self.seriesdirty = False
301 self.added = []
301 self.added = []
302 self.seriespath = "series"
302 self.seriespath = "series"
303 self.statuspath = "status"
303 self.statuspath = "status"
304 self.guardspath = "guards"
304 self.guardspath = "guards"
305 self.activeguards = None
305 self.activeguards = None
306 self.guardsdirty = False
306 self.guardsdirty = False
307 # Handle mq.git as a bool with extended values
307 # Handle mq.git as a bool with extended values
308 try:
308 try:
309 gitmode = ui.configbool('mq', 'git', None)
309 gitmode = ui.configbool('mq', 'git', None)
310 if gitmode is None:
310 if gitmode is None:
311 raise error.ConfigError()
311 raise error.ConfigError()
312 self.gitmode = gitmode and 'yes' or 'no'
312 self.gitmode = gitmode and 'yes' or 'no'
313 except error.ConfigError:
313 except error.ConfigError:
314 self.gitmode = ui.config('mq', 'git', 'auto').lower()
314 self.gitmode = ui.config('mq', 'git', 'auto').lower()
315 self.plainmode = ui.configbool('mq', 'plain', False)
315 self.plainmode = ui.configbool('mq', 'plain', False)
316
316
317 @util.propertycache
317 @util.propertycache
318 def applied(self):
318 def applied(self):
319 def parselines(lines):
319 def parselines(lines):
320 for l in lines:
320 for l in lines:
321 entry = l.split(':', 1)
321 entry = l.split(':', 1)
322 if len(entry) > 1:
322 if len(entry) > 1:
323 n, name = entry
323 n, name = entry
324 yield statusentry(bin(n), name)
324 yield statusentry(bin(n), name)
325 elif l.strip():
325 elif l.strip():
326 self.ui.warn(_('malformated mq status line: %s\n') % entry)
326 self.ui.warn(_('malformated mq status line: %s\n') % entry)
327 # else we ignore empty lines
327 # else we ignore empty lines
328 try:
328 try:
329 lines = self.opener.read(self.statuspath).splitlines()
329 lines = self.opener.read(self.statuspath).splitlines()
330 return list(parselines(lines))
330 return list(parselines(lines))
331 except IOError, e:
331 except IOError, e:
332 if e.errno == errno.ENOENT:
332 if e.errno == errno.ENOENT:
333 return []
333 return []
334 raise
334 raise
335
335
336 @util.propertycache
336 @util.propertycache
337 def fullseries(self):
337 def fullseries(self):
338 try:
338 try:
339 return self.opener.read(self.seriespath).splitlines()
339 return self.opener.read(self.seriespath).splitlines()
340 except IOError, e:
340 except IOError, e:
341 if e.errno == errno.ENOENT:
341 if e.errno == errno.ENOENT:
342 return []
342 return []
343 raise
343 raise
344
344
345 @util.propertycache
345 @util.propertycache
346 def series(self):
346 def series(self):
347 self.parseseries()
347 self.parseseries()
348 return self.series
348 return self.series
349
349
350 @util.propertycache
350 @util.propertycache
351 def seriesguards(self):
351 def seriesguards(self):
352 self.parseseries()
352 self.parseseries()
353 return self.seriesguards
353 return self.seriesguards
354
354
355 def invalidate(self):
355 def invalidate(self):
356 for a in 'applied fullseries series seriesguards'.split():
356 for a in 'applied fullseries series seriesguards'.split():
357 if a in self.__dict__:
357 if a in self.__dict__:
358 delattr(self, a)
358 delattr(self, a)
359 self.applieddirty = False
359 self.applieddirty = False
360 self.seriesdirty = False
360 self.seriesdirty = False
361 self.guardsdirty = False
361 self.guardsdirty = False
362 self.activeguards = None
362 self.activeguards = None
363
363
364 def diffopts(self, opts={}, patchfn=None):
364 def diffopts(self, opts={}, patchfn=None):
365 diffopts = patchmod.diffopts(self.ui, opts)
365 diffopts = patchmod.diffopts(self.ui, opts)
366 if self.gitmode == 'auto':
366 if self.gitmode == 'auto':
367 diffopts.upgrade = True
367 diffopts.upgrade = True
368 elif self.gitmode == 'keep':
368 elif self.gitmode == 'keep':
369 pass
369 pass
370 elif self.gitmode in ('yes', 'no'):
370 elif self.gitmode in ('yes', 'no'):
371 diffopts.git = self.gitmode == 'yes'
371 diffopts.git = self.gitmode == 'yes'
372 else:
372 else:
373 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
373 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
374 ' got %s') % self.gitmode)
374 ' got %s') % self.gitmode)
375 if patchfn:
375 if patchfn:
376 diffopts = self.patchopts(diffopts, patchfn)
376 diffopts = self.patchopts(diffopts, patchfn)
377 return diffopts
377 return diffopts
378
378
379 def patchopts(self, diffopts, *patches):
379 def patchopts(self, diffopts, *patches):
380 """Return a copy of input diff options with git set to true if
380 """Return a copy of input diff options with git set to true if
381 referenced patch is a git patch and should be preserved as such.
381 referenced patch is a git patch and should be preserved as such.
382 """
382 """
383 diffopts = diffopts.copy()
383 diffopts = diffopts.copy()
384 if not diffopts.git and self.gitmode == 'keep':
384 if not diffopts.git and self.gitmode == 'keep':
385 for patchfn in patches:
385 for patchfn in patches:
386 patchf = self.opener(patchfn, 'r')
386 patchf = self.opener(patchfn, 'r')
387 # if the patch was a git patch, refresh it as a git patch
387 # if the patch was a git patch, refresh it as a git patch
388 for line in patchf:
388 for line in patchf:
389 if line.startswith('diff --git'):
389 if line.startswith('diff --git'):
390 diffopts.git = True
390 diffopts.git = True
391 break
391 break
392 patchf.close()
392 patchf.close()
393 return diffopts
393 return diffopts
394
394
395 def join(self, *p):
395 def join(self, *p):
396 return os.path.join(self.path, *p)
396 return os.path.join(self.path, *p)
397
397
398 def findseries(self, patch):
398 def findseries(self, patch):
399 def matchpatch(l):
399 def matchpatch(l):
400 l = l.split('#', 1)[0]
400 l = l.split('#', 1)[0]
401 return l.strip() == patch
401 return l.strip() == patch
402 for index, l in enumerate(self.fullseries):
402 for index, l in enumerate(self.fullseries):
403 if matchpatch(l):
403 if matchpatch(l):
404 return index
404 return index
405 return None
405 return None
406
406
407 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
407 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
408
408
409 def parseseries(self):
409 def parseseries(self):
410 self.series = []
410 self.series = []
411 self.seriesguards = []
411 self.seriesguards = []
412 for l in self.fullseries:
412 for l in self.fullseries:
413 h = l.find('#')
413 h = l.find('#')
414 if h == -1:
414 if h == -1:
415 patch = l
415 patch = l
416 comment = ''
416 comment = ''
417 elif h == 0:
417 elif h == 0:
418 continue
418 continue
419 else:
419 else:
420 patch = l[:h]
420 patch = l[:h]
421 comment = l[h:]
421 comment = l[h:]
422 patch = patch.strip()
422 patch = patch.strip()
423 if patch:
423 if patch:
424 if patch in self.series:
424 if patch in self.series:
425 raise util.Abort(_('%s appears more than once in %s') %
425 raise util.Abort(_('%s appears more than once in %s') %
426 (patch, self.join(self.seriespath)))
426 (patch, self.join(self.seriespath)))
427 self.series.append(patch)
427 self.series.append(patch)
428 self.seriesguards.append(self.guard_re.findall(comment))
428 self.seriesguards.append(self.guard_re.findall(comment))
429
429
430 def checkguard(self, guard):
430 def checkguard(self, guard):
431 if not guard:
431 if not guard:
432 return _('guard cannot be an empty string')
432 return _('guard cannot be an empty string')
433 bad_chars = '# \t\r\n\f'
433 bad_chars = '# \t\r\n\f'
434 first = guard[0]
434 first = guard[0]
435 if first in '-+':
435 if first in '-+':
436 return (_('guard %r starts with invalid character: %r') %
436 return (_('guard %r starts with invalid character: %r') %
437 (guard, first))
437 (guard, first))
438 for c in bad_chars:
438 for c in bad_chars:
439 if c in guard:
439 if c in guard:
440 return _('invalid character in guard %r: %r') % (guard, c)
440 return _('invalid character in guard %r: %r') % (guard, c)
441
441
442 def setactive(self, guards):
442 def setactive(self, guards):
443 for guard in guards:
443 for guard in guards:
444 bad = self.checkguard(guard)
444 bad = self.checkguard(guard)
445 if bad:
445 if bad:
446 raise util.Abort(bad)
446 raise util.Abort(bad)
447 guards = sorted(set(guards))
447 guards = sorted(set(guards))
448 self.ui.debug('active guards: %s\n' % ' '.join(guards))
448 self.ui.debug('active guards: %s\n' % ' '.join(guards))
449 self.activeguards = guards
449 self.activeguards = guards
450 self.guardsdirty = True
450 self.guardsdirty = True
451
451
452 def active(self):
452 def active(self):
453 if self.activeguards is None:
453 if self.activeguards is None:
454 self.activeguards = []
454 self.activeguards = []
455 try:
455 try:
456 guards = self.opener.read(self.guardspath).split()
456 guards = self.opener.read(self.guardspath).split()
457 except IOError, err:
457 except IOError, err:
458 if err.errno != errno.ENOENT:
458 if err.errno != errno.ENOENT:
459 raise
459 raise
460 guards = []
460 guards = []
461 for i, guard in enumerate(guards):
461 for i, guard in enumerate(guards):
462 bad = self.checkguard(guard)
462 bad = self.checkguard(guard)
463 if bad:
463 if bad:
464 self.ui.warn('%s:%d: %s\n' %
464 self.ui.warn('%s:%d: %s\n' %
465 (self.join(self.guardspath), i + 1, bad))
465 (self.join(self.guardspath), i + 1, bad))
466 else:
466 else:
467 self.activeguards.append(guard)
467 self.activeguards.append(guard)
468 return self.activeguards
468 return self.activeguards
469
469
470 def setguards(self, idx, guards):
470 def setguards(self, idx, guards):
471 for g in guards:
471 for g in guards:
472 if len(g) < 2:
472 if len(g) < 2:
473 raise util.Abort(_('guard %r too short') % g)
473 raise util.Abort(_('guard %r too short') % g)
474 if g[0] not in '-+':
474 if g[0] not in '-+':
475 raise util.Abort(_('guard %r starts with invalid char') % g)
475 raise util.Abort(_('guard %r starts with invalid char') % g)
476 bad = self.checkguard(g[1:])
476 bad = self.checkguard(g[1:])
477 if bad:
477 if bad:
478 raise util.Abort(bad)
478 raise util.Abort(bad)
479 drop = self.guard_re.sub('', self.fullseries[idx])
479 drop = self.guard_re.sub('', self.fullseries[idx])
480 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
480 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
481 self.parseseries()
481 self.parseseries()
482 self.seriesdirty = True
482 self.seriesdirty = True
483
483
484 def pushable(self, idx):
484 def pushable(self, idx):
485 if isinstance(idx, str):
485 if isinstance(idx, str):
486 idx = self.series.index(idx)
486 idx = self.series.index(idx)
487 patchguards = self.seriesguards[idx]
487 patchguards = self.seriesguards[idx]
488 if not patchguards:
488 if not patchguards:
489 return True, None
489 return True, None
490 guards = self.active()
490 guards = self.active()
491 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
491 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
492 if exactneg:
492 if exactneg:
493 return False, repr(exactneg[0])
493 return False, repr(exactneg[0])
494 pos = [g for g in patchguards if g[0] == '+']
494 pos = [g for g in patchguards if g[0] == '+']
495 exactpos = [g for g in pos if g[1:] in guards]
495 exactpos = [g for g in pos if g[1:] in guards]
496 if pos:
496 if pos:
497 if exactpos:
497 if exactpos:
498 return True, repr(exactpos[0])
498 return True, repr(exactpos[0])
499 return False, ' '.join(map(repr, pos))
499 return False, ' '.join(map(repr, pos))
500 return True, ''
500 return True, ''
501
501
502 def explainpushable(self, idx, all_patches=False):
502 def explainpushable(self, idx, all_patches=False):
503 write = all_patches and self.ui.write or self.ui.warn
503 write = all_patches and self.ui.write or self.ui.warn
504 if all_patches or self.ui.verbose:
504 if all_patches or self.ui.verbose:
505 if isinstance(idx, str):
505 if isinstance(idx, str):
506 idx = self.series.index(idx)
506 idx = self.series.index(idx)
507 pushable, why = self.pushable(idx)
507 pushable, why = self.pushable(idx)
508 if all_patches and pushable:
508 if all_patches and pushable:
509 if why is None:
509 if why is None:
510 write(_('allowing %s - no guards in effect\n') %
510 write(_('allowing %s - no guards in effect\n') %
511 self.series[idx])
511 self.series[idx])
512 else:
512 else:
513 if not why:
513 if not why:
514 write(_('allowing %s - no matching negative guards\n') %
514 write(_('allowing %s - no matching negative guards\n') %
515 self.series[idx])
515 self.series[idx])
516 else:
516 else:
517 write(_('allowing %s - guarded by %s\n') %
517 write(_('allowing %s - guarded by %s\n') %
518 (self.series[idx], why))
518 (self.series[idx], why))
519 if not pushable:
519 if not pushable:
520 if why:
520 if why:
521 write(_('skipping %s - guarded by %s\n') %
521 write(_('skipping %s - guarded by %s\n') %
522 (self.series[idx], why))
522 (self.series[idx], why))
523 else:
523 else:
524 write(_('skipping %s - no matching guards\n') %
524 write(_('skipping %s - no matching guards\n') %
525 self.series[idx])
525 self.series[idx])
526
526
527 def savedirty(self):
527 def savedirty(self):
528 def writelist(items, path):
528 def writelist(items, path):
529 fp = self.opener(path, 'w')
529 fp = self.opener(path, 'w')
530 for i in items:
530 for i in items:
531 fp.write("%s\n" % i)
531 fp.write("%s\n" % i)
532 fp.close()
532 fp.close()
533 if self.applieddirty:
533 if self.applieddirty:
534 writelist(map(str, self.applied), self.statuspath)
534 writelist(map(str, self.applied), self.statuspath)
535 self.applieddirty = False
535 self.applieddirty = False
536 if self.seriesdirty:
536 if self.seriesdirty:
537 writelist(self.fullseries, self.seriespath)
537 writelist(self.fullseries, self.seriespath)
538 self.seriesdirty = False
538 self.seriesdirty = False
539 if self.guardsdirty:
539 if self.guardsdirty:
540 writelist(self.activeguards, self.guardspath)
540 writelist(self.activeguards, self.guardspath)
541 self.guardsdirty = False
541 self.guardsdirty = False
542 if self.added:
542 if self.added:
543 qrepo = self.qrepo()
543 qrepo = self.qrepo()
544 if qrepo:
544 if qrepo:
545 qrepo[None].add(f for f in self.added if f not in qrepo[None])
545 qrepo[None].add(f for f in self.added if f not in qrepo[None])
546 self.added = []
546 self.added = []
547
547
548 def removeundo(self, repo):
548 def removeundo(self, repo):
549 undo = repo.sjoin('undo')
549 undo = repo.sjoin('undo')
550 if not os.path.exists(undo):
550 if not os.path.exists(undo):
551 return
551 return
552 try:
552 try:
553 os.unlink(undo)
553 os.unlink(undo)
554 except OSError, inst:
554 except OSError, inst:
555 self.ui.warn(_('error removing undo: %s\n') % str(inst))
555 self.ui.warn(_('error removing undo: %s\n') % str(inst))
556
556
557 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
557 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
558 fp=None, changes=None, opts={}):
558 fp=None, changes=None, opts={}):
559 stat = opts.get('stat')
559 stat = opts.get('stat')
560 m = scmutil.match(repo[node1], files, opts)
560 m = scmutil.match(repo[node1], files, opts)
561 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
561 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
562 changes, stat, fp)
562 changes, stat, fp)
563
563
564 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
564 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
565 # first try just applying the patch
565 # first try just applying the patch
566 (err, n) = self.apply(repo, [patch], update_status=False,
566 (err, n) = self.apply(repo, [patch], update_status=False,
567 strict=True, merge=rev)
567 strict=True, merge=rev)
568
568
569 if err == 0:
569 if err == 0:
570 return (err, n)
570 return (err, n)
571
571
572 if n is None:
572 if n is None:
573 raise util.Abort(_("apply failed for patch %s") % patch)
573 raise util.Abort(_("apply failed for patch %s") % patch)
574
574
575 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
575 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
576
576
577 # apply failed, strip away that rev and merge.
577 # apply failed, strip away that rev and merge.
578 hg.clean(repo, head)
578 hg.clean(repo, head)
579 self.strip(repo, [n], update=False, backup='strip')
579 self.strip(repo, [n], update=False, backup='strip')
580
580
581 ctx = repo[rev]
581 ctx = repo[rev]
582 ret = hg.merge(repo, rev)
582 ret = hg.merge(repo, rev)
583 if ret:
583 if ret:
584 raise util.Abort(_("update returned %d") % ret)
584 raise util.Abort(_("update returned %d") % ret)
585 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
585 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
586 if n is None:
586 if n is None:
587 raise util.Abort(_("repo commit failed"))
587 raise util.Abort(_("repo commit failed"))
588 try:
588 try:
589 ph = patchheader(mergeq.join(patch), self.plainmode)
589 ph = patchheader(mergeq.join(patch), self.plainmode)
590 except:
590 except:
591 raise util.Abort(_("unable to read %s") % patch)
591 raise util.Abort(_("unable to read %s") % patch)
592
592
593 diffopts = self.patchopts(diffopts, patch)
593 diffopts = self.patchopts(diffopts, patch)
594 patchf = self.opener(patch, "w")
594 patchf = self.opener(patch, "w")
595 comments = str(ph)
595 comments = str(ph)
596 if comments:
596 if comments:
597 patchf.write(comments)
597 patchf.write(comments)
598 self.printdiff(repo, diffopts, head, n, fp=patchf)
598 self.printdiff(repo, diffopts, head, n, fp=patchf)
599 patchf.close()
599 patchf.close()
600 self.removeundo(repo)
600 self.removeundo(repo)
601 return (0, n)
601 return (0, n)
602
602
603 def qparents(self, repo, rev=None):
603 def qparents(self, repo, rev=None):
604 if rev is None:
604 if rev is None:
605 (p1, p2) = repo.dirstate.parents()
605 (p1, p2) = repo.dirstate.parents()
606 if p2 == nullid:
606 if p2 == nullid:
607 return p1
607 return p1
608 if not self.applied:
608 if not self.applied:
609 return None
609 return None
610 return self.applied[-1].node
610 return self.applied[-1].node
611 p1, p2 = repo.changelog.parents(rev)
611 p1, p2 = repo.changelog.parents(rev)
612 if p2 != nullid and p2 in [x.node for x in self.applied]:
612 if p2 != nullid and p2 in [x.node for x in self.applied]:
613 return p2
613 return p2
614 return p1
614 return p1
615
615
616 def mergepatch(self, repo, mergeq, series, diffopts):
616 def mergepatch(self, repo, mergeq, series, diffopts):
617 if not self.applied:
617 if not self.applied:
618 # each of the patches merged in will have two parents. This
618 # each of the patches merged in will have two parents. This
619 # can confuse the qrefresh, qdiff, and strip code because it
619 # can confuse the qrefresh, qdiff, and strip code because it
620 # needs to know which parent is actually in the patch queue.
620 # needs to know which parent is actually in the patch queue.
621 # so, we insert a merge marker with only one parent. This way
621 # so, we insert a merge marker with only one parent. This way
622 # the first patch in the queue is never a merge patch
622 # the first patch in the queue is never a merge patch
623 #
623 #
624 pname = ".hg.patches.merge.marker"
624 pname = ".hg.patches.merge.marker"
625 n = newcommit(repo, None, '[mq]: merge marker', force=True)
625 n = newcommit(repo, None, '[mq]: merge marker', force=True)
626 self.removeundo(repo)
626 self.removeundo(repo)
627 self.applied.append(statusentry(n, pname))
627 self.applied.append(statusentry(n, pname))
628 self.applieddirty = True
628 self.applieddirty = True
629
629
630 head = self.qparents(repo)
630 head = self.qparents(repo)
631
631
632 for patch in series:
632 for patch in series:
633 patch = mergeq.lookup(patch, strict=True)
633 patch = mergeq.lookup(patch, strict=True)
634 if not patch:
634 if not patch:
635 self.ui.warn(_("patch %s does not exist\n") % patch)
635 self.ui.warn(_("patch %s does not exist\n") % patch)
636 return (1, None)
636 return (1, None)
637 pushable, reason = self.pushable(patch)
637 pushable, reason = self.pushable(patch)
638 if not pushable:
638 if not pushable:
639 self.explainpushable(patch, all_patches=True)
639 self.explainpushable(patch, all_patches=True)
640 continue
640 continue
641 info = mergeq.isapplied(patch)
641 info = mergeq.isapplied(patch)
642 if not info:
642 if not info:
643 self.ui.warn(_("patch %s is not applied\n") % patch)
643 self.ui.warn(_("patch %s is not applied\n") % patch)
644 return (1, None)
644 return (1, None)
645 rev = info[1]
645 rev = info[1]
646 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
646 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
647 if head:
647 if head:
648 self.applied.append(statusentry(head, patch))
648 self.applied.append(statusentry(head, patch))
649 self.applieddirty = True
649 self.applieddirty = True
650 if err:
650 if err:
651 return (err, head)
651 return (err, head)
652 self.savedirty()
652 self.savedirty()
653 return (0, head)
653 return (0, head)
654
654
655 def patch(self, repo, patchfile):
655 def patch(self, repo, patchfile):
656 '''Apply patchfile to the working directory.
656 '''Apply patchfile to the working directory.
657 patchfile: name of patch file'''
657 patchfile: name of patch file'''
658 files = set()
658 files = set()
659 try:
659 try:
660 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
660 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
661 files=files, eolmode=None)
661 files=files, eolmode=None)
662 return (True, list(files), fuzz)
662 return (True, list(files), fuzz)
663 except Exception, inst:
663 except Exception, inst:
664 self.ui.note(str(inst) + '\n')
664 self.ui.note(str(inst) + '\n')
665 if not self.ui.verbose:
665 if not self.ui.verbose:
666 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
666 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
667 self.ui.traceback()
667 self.ui.traceback()
668 return (False, list(files), False)
668 return (False, list(files), False)
669
669
670 def apply(self, repo, series, list=False, update_status=True,
670 def apply(self, repo, series, list=False, update_status=True,
671 strict=False, patchdir=None, merge=None, all_files=None):
671 strict=False, patchdir=None, merge=None, all_files=None):
672 wlock = lock = tr = None
672 wlock = lock = tr = None
673 try:
673 try:
674 wlock = repo.wlock()
674 wlock = repo.wlock()
675 lock = repo.lock()
675 lock = repo.lock()
676 tr = repo.transaction("qpush")
676 tr = repo.transaction("qpush")
677 try:
677 try:
678 ret = self._apply(repo, series, list, update_status,
678 ret = self._apply(repo, series, list, update_status,
679 strict, patchdir, merge, all_files=all_files)
679 strict, patchdir, merge, all_files=all_files)
680 tr.close()
680 tr.close()
681 self.savedirty()
681 self.savedirty()
682 return ret
682 return ret
683 except:
683 except:
684 try:
684 try:
685 tr.abort()
685 tr.abort()
686 finally:
686 finally:
687 repo.invalidate()
687 repo.invalidate()
688 repo.dirstate.invalidate()
688 repo.dirstate.invalidate()
689 self.invalidate()
689 self.invalidate()
690 raise
690 raise
691 finally:
691 finally:
692 release(tr, lock, wlock)
692 release(tr, lock, wlock)
693 self.removeundo(repo)
693 self.removeundo(repo)
694
694
695 def _apply(self, repo, series, list=False, update_status=True,
695 def _apply(self, repo, series, list=False, update_status=True,
696 strict=False, patchdir=None, merge=None, all_files=None):
696 strict=False, patchdir=None, merge=None, all_files=None):
697 '''returns (error, hash)
697 '''returns (error, hash)
698 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
698 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
699 # TODO unify with commands.py
699 # TODO unify with commands.py
700 if not patchdir:
700 if not patchdir:
701 patchdir = self.path
701 patchdir = self.path
702 err = 0
702 err = 0
703 n = None
703 n = None
704 for patchname in series:
704 for patchname in series:
705 pushable, reason = self.pushable(patchname)
705 pushable, reason = self.pushable(patchname)
706 if not pushable:
706 if not pushable:
707 self.explainpushable(patchname, all_patches=True)
707 self.explainpushable(patchname, all_patches=True)
708 continue
708 continue
709 self.ui.status(_("applying %s\n") % patchname)
709 self.ui.status(_("applying %s\n") % patchname)
710 pf = os.path.join(patchdir, patchname)
710 pf = os.path.join(patchdir, patchname)
711
711
712 try:
712 try:
713 ph = patchheader(self.join(patchname), self.plainmode)
713 ph = patchheader(self.join(patchname), self.plainmode)
714 except IOError:
714 except IOError:
715 self.ui.warn(_("unable to read %s\n") % patchname)
715 self.ui.warn(_("unable to read %s\n") % patchname)
716 err = 1
716 err = 1
717 break
717 break
718
718
719 message = ph.message
719 message = ph.message
720 if not message:
720 if not message:
721 # The commit message should not be translated
721 # The commit message should not be translated
722 message = "imported patch %s\n" % patchname
722 message = "imported patch %s\n" % patchname
723 else:
723 else:
724 if list:
724 if list:
725 # The commit message should not be translated
725 # The commit message should not be translated
726 message.append("\nimported patch %s" % patchname)
726 message.append("\nimported patch %s" % patchname)
727 message = '\n'.join(message)
727 message = '\n'.join(message)
728
728
729 if ph.haspatch:
729 if ph.haspatch:
730 (patcherr, files, fuzz) = self.patch(repo, pf)
730 (patcherr, files, fuzz) = self.patch(repo, pf)
731 if all_files is not None:
731 if all_files is not None:
732 all_files.update(files)
732 all_files.update(files)
733 patcherr = not patcherr
733 patcherr = not patcherr
734 else:
734 else:
735 self.ui.warn(_("patch %s is empty\n") % patchname)
735 self.ui.warn(_("patch %s is empty\n") % patchname)
736 patcherr, files, fuzz = 0, [], 0
736 patcherr, files, fuzz = 0, [], 0
737
737
738 if merge and files:
738 if merge and files:
739 # Mark as removed/merged and update dirstate parent info
739 # Mark as removed/merged and update dirstate parent info
740 removed = []
740 removed = []
741 merged = []
741 merged = []
742 for f in files:
742 for f in files:
743 if os.path.lexists(repo.wjoin(f)):
743 if os.path.lexists(repo.wjoin(f)):
744 merged.append(f)
744 merged.append(f)
745 else:
745 else:
746 removed.append(f)
746 removed.append(f)
747 for f in removed:
747 for f in removed:
748 repo.dirstate.remove(f)
748 repo.dirstate.remove(f)
749 for f in merged:
749 for f in merged:
750 repo.dirstate.merge(f)
750 repo.dirstate.merge(f)
751 p1, p2 = repo.dirstate.parents()
751 p1, p2 = repo.dirstate.parents()
752 repo.dirstate.setparents(p1, merge)
752 repo.dirstate.setparents(p1, merge)
753
753
754 match = scmutil.matchfiles(repo, files or [])
754 match = scmutil.matchfiles(repo, files or [])
755 oldtip = repo['tip']
755 oldtip = repo['tip']
756 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
756 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
757 force=True)
757 force=True)
758 if repo['tip'] == oldtip:
758 if repo['tip'] == oldtip:
759 raise util.Abort(_("qpush exactly duplicates child changeset"))
759 raise util.Abort(_("qpush exactly duplicates child changeset"))
760 if n is None:
760 if n is None:
761 raise util.Abort(_("repository commit failed"))
761 raise util.Abort(_("repository commit failed"))
762
762
763 if update_status:
763 if update_status:
764 self.applied.append(statusentry(n, patchname))
764 self.applied.append(statusentry(n, patchname))
765
765
766 if patcherr:
766 if patcherr:
767 self.ui.warn(_("patch failed, rejects left in working dir\n"))
767 self.ui.warn(_("patch failed, rejects left in working dir\n"))
768 err = 2
768 err = 2
769 break
769 break
770
770
771 if fuzz and strict:
771 if fuzz and strict:
772 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
772 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
773 err = 3
773 err = 3
774 break
774 break
775 return (err, n)
775 return (err, n)
776
776
777 def _cleanup(self, patches, numrevs, keep=False):
777 def _cleanup(self, patches, numrevs, keep=False):
778 if not keep:
778 if not keep:
779 r = self.qrepo()
779 r = self.qrepo()
780 if r:
780 if r:
781 r[None].forget(patches)
781 r[None].forget(patches)
782 for p in patches:
782 for p in patches:
783 os.unlink(self.join(p))
783 os.unlink(self.join(p))
784
784
785 qfinished = []
785 qfinished = []
786 if numrevs:
786 if numrevs:
787 qfinished = self.applied[:numrevs]
787 qfinished = self.applied[:numrevs]
788 del self.applied[:numrevs]
788 del self.applied[:numrevs]
789 self.applieddirty = True
789 self.applieddirty = True
790
790
791 unknown = []
791 unknown = []
792
792
793 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
793 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
794 reverse=True):
794 reverse=True):
795 if i is not None:
795 if i is not None:
796 del self.fullseries[i]
796 del self.fullseries[i]
797 else:
797 else:
798 unknown.append(p)
798 unknown.append(p)
799
799
800 if unknown:
800 if unknown:
801 if numrevs:
801 if numrevs:
802 rev = dict((entry.name, entry.node) for entry in qfinished)
802 rev = dict((entry.name, entry.node) for entry in qfinished)
803 for p in unknown:
803 for p in unknown:
804 msg = _('revision %s refers to unknown patches: %s\n')
804 msg = _('revision %s refers to unknown patches: %s\n')
805 self.ui.warn(msg % (short(rev[p]), p))
805 self.ui.warn(msg % (short(rev[p]), p))
806 else:
806 else:
807 msg = _('unknown patches: %s\n')
807 msg = _('unknown patches: %s\n')
808 raise util.Abort(''.join(msg % p for p in unknown))
808 raise util.Abort(''.join(msg % p for p in unknown))
809
809
810 self.parseseries()
810 self.parseseries()
811 self.seriesdirty = True
811 self.seriesdirty = True
812 return [entry.node for entry in qfinished]
812 return [entry.node for entry in qfinished]
813
813
814 def _revpatches(self, repo, revs):
814 def _revpatches(self, repo, revs):
815 firstrev = repo[self.applied[0].node].rev()
815 firstrev = repo[self.applied[0].node].rev()
816 patches = []
816 patches = []
817 for i, rev in enumerate(revs):
817 for i, rev in enumerate(revs):
818
818
819 if rev < firstrev:
819 if rev < firstrev:
820 raise util.Abort(_('revision %d is not managed') % rev)
820 raise util.Abort(_('revision %d is not managed') % rev)
821
821
822 ctx = repo[rev]
822 ctx = repo[rev]
823 base = self.applied[i].node
823 base = self.applied[i].node
824 if ctx.node() != base:
824 if ctx.node() != base:
825 msg = _('cannot delete revision %d above applied patches')
825 msg = _('cannot delete revision %d above applied patches')
826 raise util.Abort(msg % rev)
826 raise util.Abort(msg % rev)
827
827
828 patch = self.applied[i].name
828 patch = self.applied[i].name
829 for fmt in ('[mq]: %s', 'imported patch %s'):
829 for fmt in ('[mq]: %s', 'imported patch %s'):
830 if ctx.description() == fmt % patch:
830 if ctx.description() == fmt % patch:
831 msg = _('patch %s finalized without changeset message\n')
831 msg = _('patch %s finalized without changeset message\n')
832 repo.ui.status(msg % patch)
832 repo.ui.status(msg % patch)
833 break
833 break
834
834
835 patches.append(patch)
835 patches.append(patch)
836 return patches
836 return patches
837
837
838 def finish(self, repo, revs):
838 def finish(self, repo, revs):
839 # Manually trigger phase computation to ensure phasedefaults is
839 # Manually trigger phase computation to ensure phasedefaults is
840 # executed before we remove the patches.
840 # executed before we remove the patches.
841 repo._phaserev
841 repo._phaserev
842 patches = self._revpatches(repo, sorted(revs))
842 patches = self._revpatches(repo, sorted(revs))
843 qfinished = self._cleanup(patches, len(patches))
843 qfinished = self._cleanup(patches, len(patches))
844 if qfinished and repo.ui.configbool('mq', 'secret', False):
844 if qfinished and repo.ui.configbool('mq', 'secret', False):
845 # only use this logic when the secret option is added
845 # only use this logic when the secret option is added
846 oldqbase = repo[qfinished[0]]
846 oldqbase = repo[qfinished[0]]
847 if oldqbase.p1().phase() < phases.secret:
847 if oldqbase.p1().phase() < phases.secret:
848 phases.advanceboundary(repo, phases.draft, qfinished)
848 phases.advanceboundary(repo, phases.draft, qfinished)
849
849
850 def delete(self, repo, patches, opts):
850 def delete(self, repo, patches, opts):
851 if not patches and not opts.get('rev'):
851 if not patches and not opts.get('rev'):
852 raise util.Abort(_('qdelete requires at least one revision or '
852 raise util.Abort(_('qdelete requires at least one revision or '
853 'patch name'))
853 'patch name'))
854
854
855 realpatches = []
855 realpatches = []
856 for patch in patches:
856 for patch in patches:
857 patch = self.lookup(patch, strict=True)
857 patch = self.lookup(patch, strict=True)
858 info = self.isapplied(patch)
858 info = self.isapplied(patch)
859 if info:
859 if info:
860 raise util.Abort(_("cannot delete applied patch %s") % patch)
860 raise util.Abort(_("cannot delete applied patch %s") % patch)
861 if patch not in self.series:
861 if patch not in self.series:
862 raise util.Abort(_("patch %s not in series file") % patch)
862 raise util.Abort(_("patch %s not in series file") % patch)
863 if patch not in realpatches:
863 if patch not in realpatches:
864 realpatches.append(patch)
864 realpatches.append(patch)
865
865
866 numrevs = 0
866 numrevs = 0
867 if opts.get('rev'):
867 if opts.get('rev'):
868 if not self.applied:
868 if not self.applied:
869 raise util.Abort(_('no patches applied'))
869 raise util.Abort(_('no patches applied'))
870 revs = scmutil.revrange(repo, opts.get('rev'))
870 revs = scmutil.revrange(repo, opts.get('rev'))
871 if len(revs) > 1 and revs[0] > revs[1]:
871 if len(revs) > 1 and revs[0] > revs[1]:
872 revs.reverse()
872 revs.reverse()
873 revpatches = self._revpatches(repo, revs)
873 revpatches = self._revpatches(repo, revs)
874 realpatches += revpatches
874 realpatches += revpatches
875 numrevs = len(revpatches)
875 numrevs = len(revpatches)
876
876
877 self._cleanup(realpatches, numrevs, opts.get('keep'))
877 self._cleanup(realpatches, numrevs, opts.get('keep'))
878
878
879 def checktoppatch(self, repo):
879 def checktoppatch(self, repo):
880 if self.applied:
880 if self.applied:
881 top = self.applied[-1].node
881 top = self.applied[-1].node
882 patch = self.applied[-1].name
882 patch = self.applied[-1].name
883 pp = repo.dirstate.parents()
883 pp = repo.dirstate.parents()
884 if top not in pp:
884 if top not in pp:
885 raise util.Abort(_("working directory revision is not qtip"))
885 raise util.Abort(_("working directory revision is not qtip"))
886 return top, patch
886 return top, patch
887 return None, None
887 return None, None
888
888
889 def checksubstate(self, repo):
889 def checksubstate(self, repo):
890 '''return list of subrepos at a different revision than substate.
890 '''return list of subrepos at a different revision than substate.
891 Abort if any subrepos have uncommitted changes.'''
891 Abort if any subrepos have uncommitted changes.'''
892 inclsubs = []
892 inclsubs = []
893 wctx = repo[None]
893 wctx = repo[None]
894 for s in wctx.substate:
894 for s in wctx.substate:
895 if wctx.sub(s).dirty(True):
895 if wctx.sub(s).dirty(True):
896 raise util.Abort(
896 raise util.Abort(
897 _("uncommitted changes in subrepository %s") % s)
897 _("uncommitted changes in subrepository %s") % s)
898 elif wctx.sub(s).dirty():
898 elif wctx.sub(s).dirty():
899 inclsubs.append(s)
899 inclsubs.append(s)
900 return inclsubs
900 return inclsubs
901
901
902 def localchangesfound(self, refresh=True):
902 def localchangesfound(self, refresh=True):
903 if refresh:
903 if refresh:
904 raise util.Abort(_("local changes found, refresh first"))
904 raise util.Abort(_("local changes found, refresh first"))
905 else:
905 else:
906 raise util.Abort(_("local changes found"))
906 raise util.Abort(_("local changes found"))
907
907
908 def checklocalchanges(self, repo, force=False, refresh=True):
908 def checklocalchanges(self, repo, force=False, refresh=True):
909 m, a, r, d = repo.status()[:4]
909 m, a, r, d = repo.status()[:4]
910 if (m or a or r or d) and not force:
910 if (m or a or r or d) and not force:
911 self.localchangesfound(refresh)
911 self.localchangesfound(refresh)
912 return m, a, r, d
912 return m, a, r, d
913
913
914 _reserved = ('series', 'status', 'guards', '.', '..')
914 _reserved = ('series', 'status', 'guards', '.', '..')
915 def checkreservedname(self, name):
915 def checkreservedname(self, name):
916 if name in self._reserved:
916 if name in self._reserved:
917 raise util.Abort(_('"%s" cannot be used as the name of a patch')
917 raise util.Abort(_('"%s" cannot be used as the name of a patch')
918 % name)
918 % name)
919 for prefix in ('.hg', '.mq'):
919 for prefix in ('.hg', '.mq'):
920 if name.startswith(prefix):
920 if name.startswith(prefix):
921 raise util.Abort(_('patch name cannot begin with "%s"')
921 raise util.Abort(_('patch name cannot begin with "%s"')
922 % prefix)
922 % prefix)
923 for c in ('#', ':'):
923 for c in ('#', ':'):
924 if c in name:
924 if c in name:
925 raise util.Abort(_('"%s" cannot be used in the name of a patch')
925 raise util.Abort(_('"%s" cannot be used in the name of a patch')
926 % c)
926 % c)
927
927
928 def checkpatchname(self, name, force=False):
928 def checkpatchname(self, name, force=False):
929 self.checkreservedname(name)
929 self.checkreservedname(name)
930 if not force and os.path.exists(self.join(name)):
930 if not force and os.path.exists(self.join(name)):
931 if os.path.isdir(self.join(name)):
931 if os.path.isdir(self.join(name)):
932 raise util.Abort(_('"%s" already exists as a directory')
932 raise util.Abort(_('"%s" already exists as a directory')
933 % name)
933 % name)
934 else:
934 else:
935 raise util.Abort(_('patch "%s" already exists') % name)
935 raise util.Abort(_('patch "%s" already exists') % name)
936
936
937 def new(self, repo, patchfn, *pats, **opts):
937 def new(self, repo, patchfn, *pats, **opts):
938 """options:
938 """options:
939 msg: a string or a no-argument function returning a string
939 msg: a string or a no-argument function returning a string
940 """
940 """
941 msg = opts.get('msg')
941 msg = opts.get('msg')
942 user = opts.get('user')
942 user = opts.get('user')
943 date = opts.get('date')
943 date = opts.get('date')
944 if date:
944 if date:
945 date = util.parsedate(date)
945 date = util.parsedate(date)
946 diffopts = self.diffopts({'git': opts.get('git')})
946 diffopts = self.diffopts({'git': opts.get('git')})
947 if opts.get('checkname', True):
947 if opts.get('checkname', True):
948 self.checkpatchname(patchfn)
948 self.checkpatchname(patchfn)
949 inclsubs = self.checksubstate(repo)
949 inclsubs = self.checksubstate(repo)
950 if inclsubs:
950 if inclsubs:
951 inclsubs.append('.hgsubstate')
951 inclsubs.append('.hgsubstate')
952 if opts.get('include') or opts.get('exclude') or pats:
952 if opts.get('include') or opts.get('exclude') or pats:
953 if inclsubs:
953 if inclsubs:
954 pats = list(pats or []) + inclsubs
954 pats = list(pats or []) + inclsubs
955 match = scmutil.match(repo[None], pats, opts)
955 match = scmutil.match(repo[None], pats, opts)
956 # detect missing files in pats
956 # detect missing files in pats
957 def badfn(f, msg):
957 def badfn(f, msg):
958 if f != '.hgsubstate': # .hgsubstate is auto-created
958 if f != '.hgsubstate': # .hgsubstate is auto-created
959 raise util.Abort('%s: %s' % (f, msg))
959 raise util.Abort('%s: %s' % (f, msg))
960 match.bad = badfn
960 match.bad = badfn
961 m, a, r, d = repo.status(match=match)[:4]
961 m, a, r, d = repo.status(match=match)[:4]
962 else:
962 else:
963 m, a, r, d = self.checklocalchanges(repo, force=True)
963 m, a, r, d = self.checklocalchanges(repo, force=True)
964 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
964 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
965 if len(repo[None].parents()) > 1:
965 if len(repo[None].parents()) > 1:
966 raise util.Abort(_('cannot manage merge changesets'))
966 raise util.Abort(_('cannot manage merge changesets'))
967 commitfiles = m + a + r
967 commitfiles = m + a + r
968 self.checktoppatch(repo)
968 self.checktoppatch(repo)
969 insert = self.fullseriesend()
969 insert = self.fullseriesend()
970 wlock = repo.wlock()
970 wlock = repo.wlock()
971 try:
971 try:
972 try:
972 try:
973 # if patch file write fails, abort early
973 # if patch file write fails, abort early
974 p = self.opener(patchfn, "w")
974 p = self.opener(patchfn, "w")
975 except IOError, e:
975 except IOError, e:
976 raise util.Abort(_('cannot write patch "%s": %s')
976 raise util.Abort(_('cannot write patch "%s": %s')
977 % (patchfn, e.strerror))
977 % (patchfn, e.strerror))
978 try:
978 try:
979 if self.plainmode:
979 if self.plainmode:
980 if user:
980 if user:
981 p.write("From: " + user + "\n")
981 p.write("From: " + user + "\n")
982 if not date:
982 if not date:
983 p.write("\n")
983 p.write("\n")
984 if date:
984 if date:
985 p.write("Date: %d %d\n\n" % date)
985 p.write("Date: %d %d\n\n" % date)
986 else:
986 else:
987 p.write("# HG changeset patch\n")
987 p.write("# HG changeset patch\n")
988 p.write("# Parent "
988 p.write("# Parent "
989 + hex(repo[None].p1().node()) + "\n")
989 + hex(repo[None].p1().node()) + "\n")
990 if user:
990 if user:
991 p.write("# User " + user + "\n")
991 p.write("# User " + user + "\n")
992 if date:
992 if date:
993 p.write("# Date %s %s\n\n" % date)
993 p.write("# Date %s %s\n\n" % date)
994 if util.safehasattr(msg, '__call__'):
994 if util.safehasattr(msg, '__call__'):
995 msg = msg()
995 msg = msg()
996 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
996 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
997 n = newcommit(repo, None, commitmsg, user, date, match=match,
997 n = newcommit(repo, None, commitmsg, user, date, match=match,
998 force=True)
998 force=True)
999 if n is None:
999 if n is None:
1000 raise util.Abort(_("repo commit failed"))
1000 raise util.Abort(_("repo commit failed"))
1001 try:
1001 try:
1002 self.fullseries[insert:insert] = [patchfn]
1002 self.fullseries[insert:insert] = [patchfn]
1003 self.applied.append(statusentry(n, patchfn))
1003 self.applied.append(statusentry(n, patchfn))
1004 self.parseseries()
1004 self.parseseries()
1005 self.seriesdirty = True
1005 self.seriesdirty = True
1006 self.applieddirty = True
1006 self.applieddirty = True
1007 if msg:
1007 if msg:
1008 msg = msg + "\n\n"
1008 msg = msg + "\n\n"
1009 p.write(msg)
1009 p.write(msg)
1010 if commitfiles:
1010 if commitfiles:
1011 parent = self.qparents(repo, n)
1011 parent = self.qparents(repo, n)
1012 chunks = patchmod.diff(repo, node1=parent, node2=n,
1012 chunks = patchmod.diff(repo, node1=parent, node2=n,
1013 match=match, opts=diffopts)
1013 match=match, opts=diffopts)
1014 for chunk in chunks:
1014 for chunk in chunks:
1015 p.write(chunk)
1015 p.write(chunk)
1016 p.close()
1016 p.close()
1017 r = self.qrepo()
1017 r = self.qrepo()
1018 if r:
1018 if r:
1019 r[None].add([patchfn])
1019 r[None].add([patchfn])
1020 except:
1020 except:
1021 repo.rollback()
1021 repo.rollback()
1022 raise
1022 raise
1023 except Exception:
1023 except Exception:
1024 patchpath = self.join(patchfn)
1024 patchpath = self.join(patchfn)
1025 try:
1025 try:
1026 os.unlink(patchpath)
1026 os.unlink(patchpath)
1027 except:
1027 except:
1028 self.ui.warn(_('error unlinking %s\n') % patchpath)
1028 self.ui.warn(_('error unlinking %s\n') % patchpath)
1029 raise
1029 raise
1030 self.removeundo(repo)
1030 self.removeundo(repo)
1031 finally:
1031 finally:
1032 release(wlock)
1032 release(wlock)
1033
1033
1034 def strip(self, repo, revs, update=True, backup="all", force=None):
1034 def strip(self, repo, revs, update=True, backup="all", force=None):
1035 wlock = lock = None
1035 wlock = lock = None
1036 try:
1036 try:
1037 wlock = repo.wlock()
1037 wlock = repo.wlock()
1038 lock = repo.lock()
1038 lock = repo.lock()
1039
1039
1040 if update:
1040 if update:
1041 self.checklocalchanges(repo, force=force, refresh=False)
1041 self.checklocalchanges(repo, force=force, refresh=False)
1042 urev = self.qparents(repo, revs[0])
1042 urev = self.qparents(repo, revs[0])
1043 hg.clean(repo, urev)
1043 hg.clean(repo, urev)
1044 repo.dirstate.write()
1044 repo.dirstate.write()
1045
1045
1046 self.removeundo(repo)
1046 self.removeundo(repo)
1047 for rev in revs:
1047 for rev in revs:
1048 repair.strip(self.ui, repo, rev, backup)
1048 repair.strip(self.ui, repo, rev, backup)
1049 # strip may have unbundled a set of backed up revisions after
1049 # strip may have unbundled a set of backed up revisions after
1050 # the actual strip
1050 # the actual strip
1051 self.removeundo(repo)
1051 self.removeundo(repo)
1052 finally:
1052 finally:
1053 release(lock, wlock)
1053 release(lock, wlock)
1054
1054
1055 def isapplied(self, patch):
1055 def isapplied(self, patch):
1056 """returns (index, rev, patch)"""
1056 """returns (index, rev, patch)"""
1057 for i, a in enumerate(self.applied):
1057 for i, a in enumerate(self.applied):
1058 if a.name == patch:
1058 if a.name == patch:
1059 return (i, a.node, a.name)
1059 return (i, a.node, a.name)
1060 return None
1060 return None
1061
1061
1062 # if the exact patch name does not exist, we try a few
1062 # if the exact patch name does not exist, we try a few
1063 # variations. If strict is passed, we try only #1
1063 # variations. If strict is passed, we try only #1
1064 #
1064 #
1065 # 1) a number (as string) to indicate an offset in the series file
1065 # 1) a number (as string) to indicate an offset in the series file
1066 # 2) a unique substring of the patch name was given
1066 # 2) a unique substring of the patch name was given
1067 # 3) patchname[-+]num to indicate an offset in the series file
1067 # 3) patchname[-+]num to indicate an offset in the series file
1068 def lookup(self, patch, strict=False):
1068 def lookup(self, patch, strict=False):
1069 def partialname(s):
1069 def partialname(s):
1070 if s in self.series:
1070 if s in self.series:
1071 return s
1071 return s
1072 matches = [x for x in self.series if s in x]
1072 matches = [x for x in self.series if s in x]
1073 if len(matches) > 1:
1073 if len(matches) > 1:
1074 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1074 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1075 for m in matches:
1075 for m in matches:
1076 self.ui.warn(' %s\n' % m)
1076 self.ui.warn(' %s\n' % m)
1077 return None
1077 return None
1078 if matches:
1078 if matches:
1079 return matches[0]
1079 return matches[0]
1080 if self.series and self.applied:
1080 if self.series and self.applied:
1081 if s == 'qtip':
1081 if s == 'qtip':
1082 return self.series[self.seriesend(True)-1]
1082 return self.series[self.seriesend(True)-1]
1083 if s == 'qbase':
1083 if s == 'qbase':
1084 return self.series[0]
1084 return self.series[0]
1085 return None
1085 return None
1086
1086
1087 if patch in self.series:
1087 if patch in self.series:
1088 return patch
1088 return patch
1089
1089
1090 if not os.path.isfile(self.join(patch)):
1090 if not os.path.isfile(self.join(patch)):
1091 try:
1091 try:
1092 sno = int(patch)
1092 sno = int(patch)
1093 except (ValueError, OverflowError):
1093 except (ValueError, OverflowError):
1094 pass
1094 pass
1095 else:
1095 else:
1096 if -len(self.series) <= sno < len(self.series):
1096 if -len(self.series) <= sno < len(self.series):
1097 return self.series[sno]
1097 return self.series[sno]
1098
1098
1099 if not strict:
1099 if not strict:
1100 res = partialname(patch)
1100 res = partialname(patch)
1101 if res:
1101 if res:
1102 return res
1102 return res
1103 minus = patch.rfind('-')
1103 minus = patch.rfind('-')
1104 if minus >= 0:
1104 if minus >= 0:
1105 res = partialname(patch[:minus])
1105 res = partialname(patch[:minus])
1106 if res:
1106 if res:
1107 i = self.series.index(res)
1107 i = self.series.index(res)
1108 try:
1108 try:
1109 off = int(patch[minus + 1:] or 1)
1109 off = int(patch[minus + 1:] or 1)
1110 except (ValueError, OverflowError):
1110 except (ValueError, OverflowError):
1111 pass
1111 pass
1112 else:
1112 else:
1113 if i - off >= 0:
1113 if i - off >= 0:
1114 return self.series[i - off]
1114 return self.series[i - off]
1115 plus = patch.rfind('+')
1115 plus = patch.rfind('+')
1116 if plus >= 0:
1116 if plus >= 0:
1117 res = partialname(patch[:plus])
1117 res = partialname(patch[:plus])
1118 if res:
1118 if res:
1119 i = self.series.index(res)
1119 i = self.series.index(res)
1120 try:
1120 try:
1121 off = int(patch[plus + 1:] or 1)
1121 off = int(patch[plus + 1:] or 1)
1122 except (ValueError, OverflowError):
1122 except (ValueError, OverflowError):
1123 pass
1123 pass
1124 else:
1124 else:
1125 if i + off < len(self.series):
1125 if i + off < len(self.series):
1126 return self.series[i + off]
1126 return self.series[i + off]
1127 raise util.Abort(_("patch %s not in series") % patch)
1127 raise util.Abort(_("patch %s not in series") % patch)
1128
1128
1129 def push(self, repo, patch=None, force=False, list=False,
1129 def push(self, repo, patch=None, force=False, list=False,
1130 mergeq=None, all=False, move=False, exact=False):
1130 mergeq=None, all=False, move=False, exact=False):
1131 diffopts = self.diffopts()
1131 diffopts = self.diffopts()
1132 wlock = repo.wlock()
1132 wlock = repo.wlock()
1133 try:
1133 try:
1134 heads = []
1134 heads = []
1135 for b, ls in repo.branchmap().iteritems():
1135 for b, ls in repo.branchmap().iteritems():
1136 heads += ls
1136 heads += ls
1137 if not heads:
1137 if not heads:
1138 heads = [nullid]
1138 heads = [nullid]
1139 if repo.dirstate.p1() not in heads and not exact:
1139 if repo.dirstate.p1() not in heads and not exact:
1140 self.ui.status(_("(working directory not at a head)\n"))
1140 self.ui.status(_("(working directory not at a head)\n"))
1141
1141
1142 if not self.series:
1142 if not self.series:
1143 self.ui.warn(_('no patches in series\n'))
1143 self.ui.warn(_('no patches in series\n'))
1144 return 0
1144 return 0
1145
1145
1146 # Suppose our series file is: A B C and the current 'top'
1146 # Suppose our series file is: A B C and the current 'top'
1147 # patch is B. qpush C should be performed (moving forward)
1147 # patch is B. qpush C should be performed (moving forward)
1148 # qpush B is a NOP (no change) qpush A is an error (can't
1148 # qpush B is a NOP (no change) qpush A is an error (can't
1149 # go backwards with qpush)
1149 # go backwards with qpush)
1150 if patch:
1150 if patch:
1151 patch = self.lookup(patch)
1151 patch = self.lookup(patch)
1152 info = self.isapplied(patch)
1152 info = self.isapplied(patch)
1153 if info and info[0] >= len(self.applied) - 1:
1153 if info and info[0] >= len(self.applied) - 1:
1154 self.ui.warn(
1154 self.ui.warn(
1155 _('qpush: %s is already at the top\n') % patch)
1155 _('qpush: %s is already at the top\n') % patch)
1156 return 0
1156 return 0
1157
1157
1158 pushable, reason = self.pushable(patch)
1158 pushable, reason = self.pushable(patch)
1159 if pushable:
1159 if pushable:
1160 if self.series.index(patch) < self.seriesend():
1160 if self.series.index(patch) < self.seriesend():
1161 raise util.Abort(
1161 raise util.Abort(
1162 _("cannot push to a previous patch: %s") % patch)
1162 _("cannot push to a previous patch: %s") % patch)
1163 else:
1163 else:
1164 if reason:
1164 if reason:
1165 reason = _('guarded by %s') % reason
1165 reason = _('guarded by %s') % reason
1166 else:
1166 else:
1167 reason = _('no matching guards')
1167 reason = _('no matching guards')
1168 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1168 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1169 return 1
1169 return 1
1170 elif all:
1170 elif all:
1171 patch = self.series[-1]
1171 patch = self.series[-1]
1172 if self.isapplied(patch):
1172 if self.isapplied(patch):
1173 self.ui.warn(_('all patches are currently applied\n'))
1173 self.ui.warn(_('all patches are currently applied\n'))
1174 return 0
1174 return 0
1175
1175
1176 # Following the above example, starting at 'top' of B:
1176 # Following the above example, starting at 'top' of B:
1177 # qpush should be performed (pushes C), but a subsequent
1177 # qpush should be performed (pushes C), but a subsequent
1178 # qpush without an argument is an error (nothing to
1178 # qpush without an argument is an error (nothing to
1179 # apply). This allows a loop of "...while hg qpush..." to
1179 # apply). This allows a loop of "...while hg qpush..." to
1180 # work as it detects an error when done
1180 # work as it detects an error when done
1181 start = self.seriesend()
1181 start = self.seriesend()
1182 if start == len(self.series):
1182 if start == len(self.series):
1183 self.ui.warn(_('patch series already fully applied\n'))
1183 self.ui.warn(_('patch series already fully applied\n'))
1184 return 1
1184 return 1
1185 if not force:
1185 if not force:
1186 self.checklocalchanges(repo, refresh=self.applied)
1186 self.checklocalchanges(repo, refresh=self.applied)
1187
1187
1188 if exact:
1188 if exact:
1189 if move:
1189 if move:
1190 raise util.Abort(_("cannot use --exact and --move together"))
1190 raise util.Abort(_("cannot use --exact and --move together"))
1191 if self.applied:
1191 if self.applied:
1192 raise util.Abort(_("cannot push --exact with applied patches"))
1192 raise util.Abort(_("cannot push --exact with applied patches"))
1193 root = self.series[start]
1193 root = self.series[start]
1194 target = patchheader(self.join(root), self.plainmode).parent
1194 target = patchheader(self.join(root), self.plainmode).parent
1195 if not target:
1195 if not target:
1196 raise util.Abort(_("%s does not have a parent recorded" % root))
1196 raise util.Abort(
1197 _("%s does not have a parent recorded") % root)
1197 if not repo[target] == repo['.']:
1198 if not repo[target] == repo['.']:
1198 hg.update(repo, target)
1199 hg.update(repo, target)
1199
1200
1200 if move:
1201 if move:
1201 if not patch:
1202 if not patch:
1202 raise util.Abort(_("please specify the patch to move"))
1203 raise util.Abort(_("please specify the patch to move"))
1203 for i, rpn in enumerate(self.fullseries[start:]):
1204 for i, rpn in enumerate(self.fullseries[start:]):
1204 # strip markers for patch guards
1205 # strip markers for patch guards
1205 if self.guard_re.split(rpn, 1)[0] == patch:
1206 if self.guard_re.split(rpn, 1)[0] == patch:
1206 break
1207 break
1207 index = start + i
1208 index = start + i
1208 assert index < len(self.fullseries)
1209 assert index < len(self.fullseries)
1209 fullpatch = self.fullseries[index]
1210 fullpatch = self.fullseries[index]
1210 del self.fullseries[index]
1211 del self.fullseries[index]
1211 self.fullseries.insert(start, fullpatch)
1212 self.fullseries.insert(start, fullpatch)
1212 self.parseseries()
1213 self.parseseries()
1213 self.seriesdirty = True
1214 self.seriesdirty = True
1214
1215
1215 self.applieddirty = True
1216 self.applieddirty = True
1216 if start > 0:
1217 if start > 0:
1217 self.checktoppatch(repo)
1218 self.checktoppatch(repo)
1218 if not patch:
1219 if not patch:
1219 patch = self.series[start]
1220 patch = self.series[start]
1220 end = start + 1
1221 end = start + 1
1221 else:
1222 else:
1222 end = self.series.index(patch, start) + 1
1223 end = self.series.index(patch, start) + 1
1223
1224
1224 s = self.series[start:end]
1225 s = self.series[start:end]
1225 all_files = set()
1226 all_files = set()
1226 try:
1227 try:
1227 if mergeq:
1228 if mergeq:
1228 ret = self.mergepatch(repo, mergeq, s, diffopts)
1229 ret = self.mergepatch(repo, mergeq, s, diffopts)
1229 else:
1230 else:
1230 ret = self.apply(repo, s, list, all_files=all_files)
1231 ret = self.apply(repo, s, list, all_files=all_files)
1231 except:
1232 except:
1232 self.ui.warn(_('cleaning up working directory...'))
1233 self.ui.warn(_('cleaning up working directory...'))
1233 node = repo.dirstate.p1()
1234 node = repo.dirstate.p1()
1234 hg.revert(repo, node, None)
1235 hg.revert(repo, node, None)
1235 # only remove unknown files that we know we touched or
1236 # only remove unknown files that we know we touched or
1236 # created while patching
1237 # created while patching
1237 for f in all_files:
1238 for f in all_files:
1238 if f not in repo.dirstate:
1239 if f not in repo.dirstate:
1239 try:
1240 try:
1240 util.unlinkpath(repo.wjoin(f))
1241 util.unlinkpath(repo.wjoin(f))
1241 except OSError, inst:
1242 except OSError, inst:
1242 if inst.errno != errno.ENOENT:
1243 if inst.errno != errno.ENOENT:
1243 raise
1244 raise
1244 self.ui.warn(_('done\n'))
1245 self.ui.warn(_('done\n'))
1245 raise
1246 raise
1246
1247
1247 if not self.applied:
1248 if not self.applied:
1248 return ret[0]
1249 return ret[0]
1249 top = self.applied[-1].name
1250 top = self.applied[-1].name
1250 if ret[0] and ret[0] > 1:
1251 if ret[0] and ret[0] > 1:
1251 msg = _("errors during apply, please fix and refresh %s\n")
1252 msg = _("errors during apply, please fix and refresh %s\n")
1252 self.ui.write(msg % top)
1253 self.ui.write(msg % top)
1253 else:
1254 else:
1254 self.ui.write(_("now at: %s\n") % top)
1255 self.ui.write(_("now at: %s\n") % top)
1255 return ret[0]
1256 return ret[0]
1256
1257
1257 finally:
1258 finally:
1258 wlock.release()
1259 wlock.release()
1259
1260
1260 def pop(self, repo, patch=None, force=False, update=True, all=False):
1261 def pop(self, repo, patch=None, force=False, update=True, all=False):
1261 wlock = repo.wlock()
1262 wlock = repo.wlock()
1262 try:
1263 try:
1263 if patch:
1264 if patch:
1264 # index, rev, patch
1265 # index, rev, patch
1265 info = self.isapplied(patch)
1266 info = self.isapplied(patch)
1266 if not info:
1267 if not info:
1267 patch = self.lookup(patch)
1268 patch = self.lookup(patch)
1268 info = self.isapplied(patch)
1269 info = self.isapplied(patch)
1269 if not info:
1270 if not info:
1270 raise util.Abort(_("patch %s is not applied") % patch)
1271 raise util.Abort(_("patch %s is not applied") % patch)
1271
1272
1272 if not self.applied:
1273 if not self.applied:
1273 # Allow qpop -a to work repeatedly,
1274 # Allow qpop -a to work repeatedly,
1274 # but not qpop without an argument
1275 # but not qpop without an argument
1275 self.ui.warn(_("no patches applied\n"))
1276 self.ui.warn(_("no patches applied\n"))
1276 return not all
1277 return not all
1277
1278
1278 if all:
1279 if all:
1279 start = 0
1280 start = 0
1280 elif patch:
1281 elif patch:
1281 start = info[0] + 1
1282 start = info[0] + 1
1282 else:
1283 else:
1283 start = len(self.applied) - 1
1284 start = len(self.applied) - 1
1284
1285
1285 if start >= len(self.applied):
1286 if start >= len(self.applied):
1286 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1287 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1287 return
1288 return
1288
1289
1289 if not update:
1290 if not update:
1290 parents = repo.dirstate.parents()
1291 parents = repo.dirstate.parents()
1291 rr = [x.node for x in self.applied]
1292 rr = [x.node for x in self.applied]
1292 for p in parents:
1293 for p in parents:
1293 if p in rr:
1294 if p in rr:
1294 self.ui.warn(_("qpop: forcing dirstate update\n"))
1295 self.ui.warn(_("qpop: forcing dirstate update\n"))
1295 update = True
1296 update = True
1296 else:
1297 else:
1297 parents = [p.node() for p in repo[None].parents()]
1298 parents = [p.node() for p in repo[None].parents()]
1298 needupdate = False
1299 needupdate = False
1299 for entry in self.applied[start:]:
1300 for entry in self.applied[start:]:
1300 if entry.node in parents:
1301 if entry.node in parents:
1301 needupdate = True
1302 needupdate = True
1302 break
1303 break
1303 update = needupdate
1304 update = needupdate
1304
1305
1305 if not force and update:
1306 if not force and update:
1306 self.checklocalchanges(repo)
1307 self.checklocalchanges(repo)
1307
1308
1308 self.applieddirty = True
1309 self.applieddirty = True
1309 end = len(self.applied)
1310 end = len(self.applied)
1310 rev = self.applied[start].node
1311 rev = self.applied[start].node
1311 if update:
1312 if update:
1312 top = self.checktoppatch(repo)[0]
1313 top = self.checktoppatch(repo)[0]
1313
1314
1314 try:
1315 try:
1315 heads = repo.changelog.heads(rev)
1316 heads = repo.changelog.heads(rev)
1316 except error.LookupError:
1317 except error.LookupError:
1317 node = short(rev)
1318 node = short(rev)
1318 raise util.Abort(_('trying to pop unknown node %s') % node)
1319 raise util.Abort(_('trying to pop unknown node %s') % node)
1319
1320
1320 if heads != [self.applied[-1].node]:
1321 if heads != [self.applied[-1].node]:
1321 raise util.Abort(_("popping would remove a revision not "
1322 raise util.Abort(_("popping would remove a revision not "
1322 "managed by this patch queue"))
1323 "managed by this patch queue"))
1323 if not repo[self.applied[-1].node].mutable():
1324 if not repo[self.applied[-1].node].mutable():
1324 raise util.Abort(
1325 raise util.Abort(
1325 _("popping would remove an immutable revision"),
1326 _("popping would remove an immutable revision"),
1326 hint=_('see "hg help phases" for details'))
1327 hint=_('see "hg help phases" for details'))
1327
1328
1328 # we know there are no local changes, so we can make a simplified
1329 # we know there are no local changes, so we can make a simplified
1329 # form of hg.update.
1330 # form of hg.update.
1330 if update:
1331 if update:
1331 qp = self.qparents(repo, rev)
1332 qp = self.qparents(repo, rev)
1332 ctx = repo[qp]
1333 ctx = repo[qp]
1333 m, a, r, d = repo.status(qp, top)[:4]
1334 m, a, r, d = repo.status(qp, top)[:4]
1334 if d:
1335 if d:
1335 raise util.Abort(_("deletions found between repo revs"))
1336 raise util.Abort(_("deletions found between repo revs"))
1336 for f in a:
1337 for f in a:
1337 try:
1338 try:
1338 util.unlinkpath(repo.wjoin(f))
1339 util.unlinkpath(repo.wjoin(f))
1339 except OSError, e:
1340 except OSError, e:
1340 if e.errno != errno.ENOENT:
1341 if e.errno != errno.ENOENT:
1341 raise
1342 raise
1342 repo.dirstate.drop(f)
1343 repo.dirstate.drop(f)
1343 for f in m + r:
1344 for f in m + r:
1344 fctx = ctx[f]
1345 fctx = ctx[f]
1345 repo.wwrite(f, fctx.data(), fctx.flags())
1346 repo.wwrite(f, fctx.data(), fctx.flags())
1346 repo.dirstate.normal(f)
1347 repo.dirstate.normal(f)
1347 repo.dirstate.setparents(qp, nullid)
1348 repo.dirstate.setparents(qp, nullid)
1348 for patch in reversed(self.applied[start:end]):
1349 for patch in reversed(self.applied[start:end]):
1349 self.ui.status(_("popping %s\n") % patch.name)
1350 self.ui.status(_("popping %s\n") % patch.name)
1350 del self.applied[start:end]
1351 del self.applied[start:end]
1351 self.strip(repo, [rev], update=False, backup='strip')
1352 self.strip(repo, [rev], update=False, backup='strip')
1352 if self.applied:
1353 if self.applied:
1353 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1354 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1354 else:
1355 else:
1355 self.ui.write(_("patch queue now empty\n"))
1356 self.ui.write(_("patch queue now empty\n"))
1356 finally:
1357 finally:
1357 wlock.release()
1358 wlock.release()
1358
1359
1359 def diff(self, repo, pats, opts):
1360 def diff(self, repo, pats, opts):
1360 top, patch = self.checktoppatch(repo)
1361 top, patch = self.checktoppatch(repo)
1361 if not top:
1362 if not top:
1362 self.ui.write(_("no patches applied\n"))
1363 self.ui.write(_("no patches applied\n"))
1363 return
1364 return
1364 qp = self.qparents(repo, top)
1365 qp = self.qparents(repo, top)
1365 if opts.get('reverse'):
1366 if opts.get('reverse'):
1366 node1, node2 = None, qp
1367 node1, node2 = None, qp
1367 else:
1368 else:
1368 node1, node2 = qp, None
1369 node1, node2 = qp, None
1369 diffopts = self.diffopts(opts, patch)
1370 diffopts = self.diffopts(opts, patch)
1370 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1371 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1371
1372
1372 def refresh(self, repo, pats=None, **opts):
1373 def refresh(self, repo, pats=None, **opts):
1373 if not self.applied:
1374 if not self.applied:
1374 self.ui.write(_("no patches applied\n"))
1375 self.ui.write(_("no patches applied\n"))
1375 return 1
1376 return 1
1376 msg = opts.get('msg', '').rstrip()
1377 msg = opts.get('msg', '').rstrip()
1377 newuser = opts.get('user')
1378 newuser = opts.get('user')
1378 newdate = opts.get('date')
1379 newdate = opts.get('date')
1379 if newdate:
1380 if newdate:
1380 newdate = '%d %d' % util.parsedate(newdate)
1381 newdate = '%d %d' % util.parsedate(newdate)
1381 wlock = repo.wlock()
1382 wlock = repo.wlock()
1382
1383
1383 try:
1384 try:
1384 self.checktoppatch(repo)
1385 self.checktoppatch(repo)
1385 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1386 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1386 if repo.changelog.heads(top) != [top]:
1387 if repo.changelog.heads(top) != [top]:
1387 raise util.Abort(_("cannot refresh a revision with children"))
1388 raise util.Abort(_("cannot refresh a revision with children"))
1388 if not repo[top].mutable():
1389 if not repo[top].mutable():
1389 raise util.Abort(_("cannot refresh immutable revision"),
1390 raise util.Abort(_("cannot refresh immutable revision"),
1390 hint=_('see "hg help phases" for details'))
1391 hint=_('see "hg help phases" for details'))
1391
1392
1392 inclsubs = self.checksubstate(repo)
1393 inclsubs = self.checksubstate(repo)
1393
1394
1394 cparents = repo.changelog.parents(top)
1395 cparents = repo.changelog.parents(top)
1395 patchparent = self.qparents(repo, top)
1396 patchparent = self.qparents(repo, top)
1396 ph = patchheader(self.join(patchfn), self.plainmode)
1397 ph = patchheader(self.join(patchfn), self.plainmode)
1397 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1398 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1398 if msg:
1399 if msg:
1399 ph.setmessage(msg)
1400 ph.setmessage(msg)
1400 if newuser:
1401 if newuser:
1401 ph.setuser(newuser)
1402 ph.setuser(newuser)
1402 if newdate:
1403 if newdate:
1403 ph.setdate(newdate)
1404 ph.setdate(newdate)
1404 ph.setparent(hex(patchparent))
1405 ph.setparent(hex(patchparent))
1405
1406
1406 # only commit new patch when write is complete
1407 # only commit new patch when write is complete
1407 patchf = self.opener(patchfn, 'w', atomictemp=True)
1408 patchf = self.opener(patchfn, 'w', atomictemp=True)
1408
1409
1409 comments = str(ph)
1410 comments = str(ph)
1410 if comments:
1411 if comments:
1411 patchf.write(comments)
1412 patchf.write(comments)
1412
1413
1413 # update the dirstate in place, strip off the qtip commit
1414 # update the dirstate in place, strip off the qtip commit
1414 # and then commit.
1415 # and then commit.
1415 #
1416 #
1416 # this should really read:
1417 # this should really read:
1417 # mm, dd, aa = repo.status(top, patchparent)[:3]
1418 # mm, dd, aa = repo.status(top, patchparent)[:3]
1418 # but we do it backwards to take advantage of manifest/chlog
1419 # but we do it backwards to take advantage of manifest/chlog
1419 # caching against the next repo.status call
1420 # caching against the next repo.status call
1420 mm, aa, dd = repo.status(patchparent, top)[:3]
1421 mm, aa, dd = repo.status(patchparent, top)[:3]
1421 changes = repo.changelog.read(top)
1422 changes = repo.changelog.read(top)
1422 man = repo.manifest.read(changes[0])
1423 man = repo.manifest.read(changes[0])
1423 aaa = aa[:]
1424 aaa = aa[:]
1424 matchfn = scmutil.match(repo[None], pats, opts)
1425 matchfn = scmutil.match(repo[None], pats, opts)
1425 # in short mode, we only diff the files included in the
1426 # in short mode, we only diff the files included in the
1426 # patch already plus specified files
1427 # patch already plus specified files
1427 if opts.get('short'):
1428 if opts.get('short'):
1428 # if amending a patch, we start with existing
1429 # if amending a patch, we start with existing
1429 # files plus specified files - unfiltered
1430 # files plus specified files - unfiltered
1430 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1431 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1431 # filter with inc/exl options
1432 # filter with inc/exl options
1432 matchfn = scmutil.match(repo[None], opts=opts)
1433 matchfn = scmutil.match(repo[None], opts=opts)
1433 else:
1434 else:
1434 match = scmutil.matchall(repo)
1435 match = scmutil.matchall(repo)
1435 m, a, r, d = repo.status(match=match)[:4]
1436 m, a, r, d = repo.status(match=match)[:4]
1436 mm = set(mm)
1437 mm = set(mm)
1437 aa = set(aa)
1438 aa = set(aa)
1438 dd = set(dd)
1439 dd = set(dd)
1439
1440
1440 # we might end up with files that were added between
1441 # we might end up with files that were added between
1441 # qtip and the dirstate parent, but then changed in the
1442 # qtip and the dirstate parent, but then changed in the
1442 # local dirstate. in this case, we want them to only
1443 # local dirstate. in this case, we want them to only
1443 # show up in the added section
1444 # show up in the added section
1444 for x in m:
1445 for x in m:
1445 if x not in aa:
1446 if x not in aa:
1446 mm.add(x)
1447 mm.add(x)
1447 # we might end up with files added by the local dirstate that
1448 # we might end up with files added by the local dirstate that
1448 # were deleted by the patch. In this case, they should only
1449 # were deleted by the patch. In this case, they should only
1449 # show up in the changed section.
1450 # show up in the changed section.
1450 for x in a:
1451 for x in a:
1451 if x in dd:
1452 if x in dd:
1452 dd.remove(x)
1453 dd.remove(x)
1453 mm.add(x)
1454 mm.add(x)
1454 else:
1455 else:
1455 aa.add(x)
1456 aa.add(x)
1456 # make sure any files deleted in the local dirstate
1457 # make sure any files deleted in the local dirstate
1457 # are not in the add or change column of the patch
1458 # are not in the add or change column of the patch
1458 forget = []
1459 forget = []
1459 for x in d + r:
1460 for x in d + r:
1460 if x in aa:
1461 if x in aa:
1461 aa.remove(x)
1462 aa.remove(x)
1462 forget.append(x)
1463 forget.append(x)
1463 continue
1464 continue
1464 else:
1465 else:
1465 mm.discard(x)
1466 mm.discard(x)
1466 dd.add(x)
1467 dd.add(x)
1467
1468
1468 m = list(mm)
1469 m = list(mm)
1469 r = list(dd)
1470 r = list(dd)
1470 a = list(aa)
1471 a = list(aa)
1471 c = [filter(matchfn, l) for l in (m, a, r)]
1472 c = [filter(matchfn, l) for l in (m, a, r)]
1472 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1473 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1473 chunks = patchmod.diff(repo, patchparent, match=match,
1474 chunks = patchmod.diff(repo, patchparent, match=match,
1474 changes=c, opts=diffopts)
1475 changes=c, opts=diffopts)
1475 for chunk in chunks:
1476 for chunk in chunks:
1476 patchf.write(chunk)
1477 patchf.write(chunk)
1477
1478
1478 try:
1479 try:
1479 if diffopts.git or diffopts.upgrade:
1480 if diffopts.git or diffopts.upgrade:
1480 copies = {}
1481 copies = {}
1481 for dst in a:
1482 for dst in a:
1482 src = repo.dirstate.copied(dst)
1483 src = repo.dirstate.copied(dst)
1483 # during qfold, the source file for copies may
1484 # during qfold, the source file for copies may
1484 # be removed. Treat this as a simple add.
1485 # be removed. Treat this as a simple add.
1485 if src is not None and src in repo.dirstate:
1486 if src is not None and src in repo.dirstate:
1486 copies.setdefault(src, []).append(dst)
1487 copies.setdefault(src, []).append(dst)
1487 repo.dirstate.add(dst)
1488 repo.dirstate.add(dst)
1488 # remember the copies between patchparent and qtip
1489 # remember the copies between patchparent and qtip
1489 for dst in aaa:
1490 for dst in aaa:
1490 f = repo.file(dst)
1491 f = repo.file(dst)
1491 src = f.renamed(man[dst])
1492 src = f.renamed(man[dst])
1492 if src:
1493 if src:
1493 copies.setdefault(src[0], []).extend(
1494 copies.setdefault(src[0], []).extend(
1494 copies.get(dst, []))
1495 copies.get(dst, []))
1495 if dst in a:
1496 if dst in a:
1496 copies[src[0]].append(dst)
1497 copies[src[0]].append(dst)
1497 # we can't copy a file created by the patch itself
1498 # we can't copy a file created by the patch itself
1498 if dst in copies:
1499 if dst in copies:
1499 del copies[dst]
1500 del copies[dst]
1500 for src, dsts in copies.iteritems():
1501 for src, dsts in copies.iteritems():
1501 for dst in dsts:
1502 for dst in dsts:
1502 repo.dirstate.copy(src, dst)
1503 repo.dirstate.copy(src, dst)
1503 else:
1504 else:
1504 for dst in a:
1505 for dst in a:
1505 repo.dirstate.add(dst)
1506 repo.dirstate.add(dst)
1506 # Drop useless copy information
1507 # Drop useless copy information
1507 for f in list(repo.dirstate.copies()):
1508 for f in list(repo.dirstate.copies()):
1508 repo.dirstate.copy(None, f)
1509 repo.dirstate.copy(None, f)
1509 for f in r:
1510 for f in r:
1510 repo.dirstate.remove(f)
1511 repo.dirstate.remove(f)
1511 # if the patch excludes a modified file, mark that
1512 # if the patch excludes a modified file, mark that
1512 # file with mtime=0 so status can see it.
1513 # file with mtime=0 so status can see it.
1513 mm = []
1514 mm = []
1514 for i in xrange(len(m)-1, -1, -1):
1515 for i in xrange(len(m)-1, -1, -1):
1515 if not matchfn(m[i]):
1516 if not matchfn(m[i]):
1516 mm.append(m[i])
1517 mm.append(m[i])
1517 del m[i]
1518 del m[i]
1518 for f in m:
1519 for f in m:
1519 repo.dirstate.normal(f)
1520 repo.dirstate.normal(f)
1520 for f in mm:
1521 for f in mm:
1521 repo.dirstate.normallookup(f)
1522 repo.dirstate.normallookup(f)
1522 for f in forget:
1523 for f in forget:
1523 repo.dirstate.drop(f)
1524 repo.dirstate.drop(f)
1524
1525
1525 if not msg:
1526 if not msg:
1526 if not ph.message:
1527 if not ph.message:
1527 message = "[mq]: %s\n" % patchfn
1528 message = "[mq]: %s\n" % patchfn
1528 else:
1529 else:
1529 message = "\n".join(ph.message)
1530 message = "\n".join(ph.message)
1530 else:
1531 else:
1531 message = msg
1532 message = msg
1532
1533
1533 user = ph.user or changes[1]
1534 user = ph.user or changes[1]
1534
1535
1535 oldphase = repo[top].phase()
1536 oldphase = repo[top].phase()
1536
1537
1537 # assumes strip can roll itself back if interrupted
1538 # assumes strip can roll itself back if interrupted
1538 repo.dirstate.setparents(*cparents)
1539 repo.dirstate.setparents(*cparents)
1539 self.applied.pop()
1540 self.applied.pop()
1540 self.applieddirty = True
1541 self.applieddirty = True
1541 self.strip(repo, [top], update=False,
1542 self.strip(repo, [top], update=False,
1542 backup='strip')
1543 backup='strip')
1543 except:
1544 except:
1544 repo.dirstate.invalidate()
1545 repo.dirstate.invalidate()
1545 raise
1546 raise
1546
1547
1547 try:
1548 try:
1548 # might be nice to attempt to roll back strip after this
1549 # might be nice to attempt to roll back strip after this
1549
1550
1550 # Ensure we create a new changeset in the same phase than
1551 # Ensure we create a new changeset in the same phase than
1551 # the old one.
1552 # the old one.
1552 n = newcommit(repo, oldphase, message, user, ph.date,
1553 n = newcommit(repo, oldphase, message, user, ph.date,
1553 match=match, force=True)
1554 match=match, force=True)
1554 # only write patch after a successful commit
1555 # only write patch after a successful commit
1555 patchf.close()
1556 patchf.close()
1556 self.applied.append(statusentry(n, patchfn))
1557 self.applied.append(statusentry(n, patchfn))
1557 except:
1558 except:
1558 ctx = repo[cparents[0]]
1559 ctx = repo[cparents[0]]
1559 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1560 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1560 self.savedirty()
1561 self.savedirty()
1561 self.ui.warn(_('refresh interrupted while patch was popped! '
1562 self.ui.warn(_('refresh interrupted while patch was popped! '
1562 '(revert --all, qpush to recover)\n'))
1563 '(revert --all, qpush to recover)\n'))
1563 raise
1564 raise
1564 finally:
1565 finally:
1565 wlock.release()
1566 wlock.release()
1566 self.removeundo(repo)
1567 self.removeundo(repo)
1567
1568
1568 def init(self, repo, create=False):
1569 def init(self, repo, create=False):
1569 if not create and os.path.isdir(self.path):
1570 if not create and os.path.isdir(self.path):
1570 raise util.Abort(_("patch queue directory already exists"))
1571 raise util.Abort(_("patch queue directory already exists"))
1571 try:
1572 try:
1572 os.mkdir(self.path)
1573 os.mkdir(self.path)
1573 except OSError, inst:
1574 except OSError, inst:
1574 if inst.errno != errno.EEXIST or not create:
1575 if inst.errno != errno.EEXIST or not create:
1575 raise
1576 raise
1576 if create:
1577 if create:
1577 return self.qrepo(create=True)
1578 return self.qrepo(create=True)
1578
1579
1579 def unapplied(self, repo, patch=None):
1580 def unapplied(self, repo, patch=None):
1580 if patch and patch not in self.series:
1581 if patch and patch not in self.series:
1581 raise util.Abort(_("patch %s is not in series file") % patch)
1582 raise util.Abort(_("patch %s is not in series file") % patch)
1582 if not patch:
1583 if not patch:
1583 start = self.seriesend()
1584 start = self.seriesend()
1584 else:
1585 else:
1585 start = self.series.index(patch) + 1
1586 start = self.series.index(patch) + 1
1586 unapplied = []
1587 unapplied = []
1587 for i in xrange(start, len(self.series)):
1588 for i in xrange(start, len(self.series)):
1588 pushable, reason = self.pushable(i)
1589 pushable, reason = self.pushable(i)
1589 if pushable:
1590 if pushable:
1590 unapplied.append((i, self.series[i]))
1591 unapplied.append((i, self.series[i]))
1591 self.explainpushable(i)
1592 self.explainpushable(i)
1592 return unapplied
1593 return unapplied
1593
1594
1594 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1595 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1595 summary=False):
1596 summary=False):
1596 def displayname(pfx, patchname, state):
1597 def displayname(pfx, patchname, state):
1597 if pfx:
1598 if pfx:
1598 self.ui.write(pfx)
1599 self.ui.write(pfx)
1599 if summary:
1600 if summary:
1600 ph = patchheader(self.join(patchname), self.plainmode)
1601 ph = patchheader(self.join(patchname), self.plainmode)
1601 msg = ph.message and ph.message[0] or ''
1602 msg = ph.message and ph.message[0] or ''
1602 if self.ui.formatted():
1603 if self.ui.formatted():
1603 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1604 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1604 if width > 0:
1605 if width > 0:
1605 msg = util.ellipsis(msg, width)
1606 msg = util.ellipsis(msg, width)
1606 else:
1607 else:
1607 msg = ''
1608 msg = ''
1608 self.ui.write(patchname, label='qseries.' + state)
1609 self.ui.write(patchname, label='qseries.' + state)
1609 self.ui.write(': ')
1610 self.ui.write(': ')
1610 self.ui.write(msg, label='qseries.message.' + state)
1611 self.ui.write(msg, label='qseries.message.' + state)
1611 else:
1612 else:
1612 self.ui.write(patchname, label='qseries.' + state)
1613 self.ui.write(patchname, label='qseries.' + state)
1613 self.ui.write('\n')
1614 self.ui.write('\n')
1614
1615
1615 applied = set([p.name for p in self.applied])
1616 applied = set([p.name for p in self.applied])
1616 if length is None:
1617 if length is None:
1617 length = len(self.series) - start
1618 length = len(self.series) - start
1618 if not missing:
1619 if not missing:
1619 if self.ui.verbose:
1620 if self.ui.verbose:
1620 idxwidth = len(str(start + length - 1))
1621 idxwidth = len(str(start + length - 1))
1621 for i in xrange(start, start + length):
1622 for i in xrange(start, start + length):
1622 patch = self.series[i]
1623 patch = self.series[i]
1623 if patch in applied:
1624 if patch in applied:
1624 char, state = 'A', 'applied'
1625 char, state = 'A', 'applied'
1625 elif self.pushable(i)[0]:
1626 elif self.pushable(i)[0]:
1626 char, state = 'U', 'unapplied'
1627 char, state = 'U', 'unapplied'
1627 else:
1628 else:
1628 char, state = 'G', 'guarded'
1629 char, state = 'G', 'guarded'
1629 pfx = ''
1630 pfx = ''
1630 if self.ui.verbose:
1631 if self.ui.verbose:
1631 pfx = '%*d %s ' % (idxwidth, i, char)
1632 pfx = '%*d %s ' % (idxwidth, i, char)
1632 elif status and status != char:
1633 elif status and status != char:
1633 continue
1634 continue
1634 displayname(pfx, patch, state)
1635 displayname(pfx, patch, state)
1635 else:
1636 else:
1636 msng_list = []
1637 msng_list = []
1637 for root, dirs, files in os.walk(self.path):
1638 for root, dirs, files in os.walk(self.path):
1638 d = root[len(self.path) + 1:]
1639 d = root[len(self.path) + 1:]
1639 for f in files:
1640 for f in files:
1640 fl = os.path.join(d, f)
1641 fl = os.path.join(d, f)
1641 if (fl not in self.series and
1642 if (fl not in self.series and
1642 fl not in (self.statuspath, self.seriespath,
1643 fl not in (self.statuspath, self.seriespath,
1643 self.guardspath)
1644 self.guardspath)
1644 and not fl.startswith('.')):
1645 and not fl.startswith('.')):
1645 msng_list.append(fl)
1646 msng_list.append(fl)
1646 for x in sorted(msng_list):
1647 for x in sorted(msng_list):
1647 pfx = self.ui.verbose and ('D ') or ''
1648 pfx = self.ui.verbose and ('D ') or ''
1648 displayname(pfx, x, 'missing')
1649 displayname(pfx, x, 'missing')
1649
1650
1650 def issaveline(self, l):
1651 def issaveline(self, l):
1651 if l.name == '.hg.patches.save.line':
1652 if l.name == '.hg.patches.save.line':
1652 return True
1653 return True
1653
1654
1654 def qrepo(self, create=False):
1655 def qrepo(self, create=False):
1655 ui = self.ui.copy()
1656 ui = self.ui.copy()
1656 ui.setconfig('paths', 'default', '', overlay=False)
1657 ui.setconfig('paths', 'default', '', overlay=False)
1657 ui.setconfig('paths', 'default-push', '', overlay=False)
1658 ui.setconfig('paths', 'default-push', '', overlay=False)
1658 if create or os.path.isdir(self.join(".hg")):
1659 if create or os.path.isdir(self.join(".hg")):
1659 return hg.repository(ui, path=self.path, create=create)
1660 return hg.repository(ui, path=self.path, create=create)
1660
1661
1661 def restore(self, repo, rev, delete=None, qupdate=None):
1662 def restore(self, repo, rev, delete=None, qupdate=None):
1662 desc = repo[rev].description().strip()
1663 desc = repo[rev].description().strip()
1663 lines = desc.splitlines()
1664 lines = desc.splitlines()
1664 i = 0
1665 i = 0
1665 datastart = None
1666 datastart = None
1666 series = []
1667 series = []
1667 applied = []
1668 applied = []
1668 qpp = None
1669 qpp = None
1669 for i, line in enumerate(lines):
1670 for i, line in enumerate(lines):
1670 if line == 'Patch Data:':
1671 if line == 'Patch Data:':
1671 datastart = i + 1
1672 datastart = i + 1
1672 elif line.startswith('Dirstate:'):
1673 elif line.startswith('Dirstate:'):
1673 l = line.rstrip()
1674 l = line.rstrip()
1674 l = l[10:].split(' ')
1675 l = l[10:].split(' ')
1675 qpp = [bin(x) for x in l]
1676 qpp = [bin(x) for x in l]
1676 elif datastart is not None:
1677 elif datastart is not None:
1677 l = line.rstrip()
1678 l = line.rstrip()
1678 n, name = l.split(':', 1)
1679 n, name = l.split(':', 1)
1679 if n:
1680 if n:
1680 applied.append(statusentry(bin(n), name))
1681 applied.append(statusentry(bin(n), name))
1681 else:
1682 else:
1682 series.append(l)
1683 series.append(l)
1683 if datastart is None:
1684 if datastart is None:
1684 self.ui.warn(_("No saved patch data found\n"))
1685 self.ui.warn(_("No saved patch data found\n"))
1685 return 1
1686 return 1
1686 self.ui.warn(_("restoring status: %s\n") % lines[0])
1687 self.ui.warn(_("restoring status: %s\n") % lines[0])
1687 self.fullseries = series
1688 self.fullseries = series
1688 self.applied = applied
1689 self.applied = applied
1689 self.parseseries()
1690 self.parseseries()
1690 self.seriesdirty = True
1691 self.seriesdirty = True
1691 self.applieddirty = True
1692 self.applieddirty = True
1692 heads = repo.changelog.heads()
1693 heads = repo.changelog.heads()
1693 if delete:
1694 if delete:
1694 if rev not in heads:
1695 if rev not in heads:
1695 self.ui.warn(_("save entry has children, leaving it alone\n"))
1696 self.ui.warn(_("save entry has children, leaving it alone\n"))
1696 else:
1697 else:
1697 self.ui.warn(_("removing save entry %s\n") % short(rev))
1698 self.ui.warn(_("removing save entry %s\n") % short(rev))
1698 pp = repo.dirstate.parents()
1699 pp = repo.dirstate.parents()
1699 if rev in pp:
1700 if rev in pp:
1700 update = True
1701 update = True
1701 else:
1702 else:
1702 update = False
1703 update = False
1703 self.strip(repo, [rev], update=update, backup='strip')
1704 self.strip(repo, [rev], update=update, backup='strip')
1704 if qpp:
1705 if qpp:
1705 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1706 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1706 (short(qpp[0]), short(qpp[1])))
1707 (short(qpp[0]), short(qpp[1])))
1707 if qupdate:
1708 if qupdate:
1708 self.ui.status(_("updating queue directory\n"))
1709 self.ui.status(_("updating queue directory\n"))
1709 r = self.qrepo()
1710 r = self.qrepo()
1710 if not r:
1711 if not r:
1711 self.ui.warn(_("Unable to load queue repository\n"))
1712 self.ui.warn(_("Unable to load queue repository\n"))
1712 return 1
1713 return 1
1713 hg.clean(r, qpp[0])
1714 hg.clean(r, qpp[0])
1714
1715
1715 def save(self, repo, msg=None):
1716 def save(self, repo, msg=None):
1716 if not self.applied:
1717 if not self.applied:
1717 self.ui.warn(_("save: no patches applied, exiting\n"))
1718 self.ui.warn(_("save: no patches applied, exiting\n"))
1718 return 1
1719 return 1
1719 if self.issaveline(self.applied[-1]):
1720 if self.issaveline(self.applied[-1]):
1720 self.ui.warn(_("status is already saved\n"))
1721 self.ui.warn(_("status is already saved\n"))
1721 return 1
1722 return 1
1722
1723
1723 if not msg:
1724 if not msg:
1724 msg = _("hg patches saved state")
1725 msg = _("hg patches saved state")
1725 else:
1726 else:
1726 msg = "hg patches: " + msg.rstrip('\r\n')
1727 msg = "hg patches: " + msg.rstrip('\r\n')
1727 r = self.qrepo()
1728 r = self.qrepo()
1728 if r:
1729 if r:
1729 pp = r.dirstate.parents()
1730 pp = r.dirstate.parents()
1730 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1731 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1731 msg += "\n\nPatch Data:\n"
1732 msg += "\n\nPatch Data:\n"
1732 msg += ''.join('%s\n' % x for x in self.applied)
1733 msg += ''.join('%s\n' % x for x in self.applied)
1733 msg += ''.join(':%s\n' % x for x in self.fullseries)
1734 msg += ''.join(':%s\n' % x for x in self.fullseries)
1734 n = repo.commit(msg, force=True)
1735 n = repo.commit(msg, force=True)
1735 if not n:
1736 if not n:
1736 self.ui.warn(_("repo commit failed\n"))
1737 self.ui.warn(_("repo commit failed\n"))
1737 return 1
1738 return 1
1738 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1739 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1739 self.applieddirty = True
1740 self.applieddirty = True
1740 self.removeundo(repo)
1741 self.removeundo(repo)
1741
1742
1742 def fullseriesend(self):
1743 def fullseriesend(self):
1743 if self.applied:
1744 if self.applied:
1744 p = self.applied[-1].name
1745 p = self.applied[-1].name
1745 end = self.findseries(p)
1746 end = self.findseries(p)
1746 if end is None:
1747 if end is None:
1747 return len(self.fullseries)
1748 return len(self.fullseries)
1748 return end + 1
1749 return end + 1
1749 return 0
1750 return 0
1750
1751
1751 def seriesend(self, all_patches=False):
1752 def seriesend(self, all_patches=False):
1752 """If all_patches is False, return the index of the next pushable patch
1753 """If all_patches is False, return the index of the next pushable patch
1753 in the series, or the series length. If all_patches is True, return the
1754 in the series, or the series length. If all_patches is True, return the
1754 index of the first patch past the last applied one.
1755 index of the first patch past the last applied one.
1755 """
1756 """
1756 end = 0
1757 end = 0
1757 def next(start):
1758 def next(start):
1758 if all_patches or start >= len(self.series):
1759 if all_patches or start >= len(self.series):
1759 return start
1760 return start
1760 for i in xrange(start, len(self.series)):
1761 for i in xrange(start, len(self.series)):
1761 p, reason = self.pushable(i)
1762 p, reason = self.pushable(i)
1762 if p:
1763 if p:
1763 return i
1764 return i
1764 self.explainpushable(i)
1765 self.explainpushable(i)
1765 return len(self.series)
1766 return len(self.series)
1766 if self.applied:
1767 if self.applied:
1767 p = self.applied[-1].name
1768 p = self.applied[-1].name
1768 try:
1769 try:
1769 end = self.series.index(p)
1770 end = self.series.index(p)
1770 except ValueError:
1771 except ValueError:
1771 return 0
1772 return 0
1772 return next(end + 1)
1773 return next(end + 1)
1773 return next(end)
1774 return next(end)
1774
1775
1775 def appliedname(self, index):
1776 def appliedname(self, index):
1776 pname = self.applied[index].name
1777 pname = self.applied[index].name
1777 if not self.ui.verbose:
1778 if not self.ui.verbose:
1778 p = pname
1779 p = pname
1779 else:
1780 else:
1780 p = str(self.series.index(pname)) + " " + pname
1781 p = str(self.series.index(pname)) + " " + pname
1781 return p
1782 return p
1782
1783
1783 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1784 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1784 force=None, git=False):
1785 force=None, git=False):
1785 def checkseries(patchname):
1786 def checkseries(patchname):
1786 if patchname in self.series:
1787 if patchname in self.series:
1787 raise util.Abort(_('patch %s is already in the series file')
1788 raise util.Abort(_('patch %s is already in the series file')
1788 % patchname)
1789 % patchname)
1789
1790
1790 if rev:
1791 if rev:
1791 if files:
1792 if files:
1792 raise util.Abort(_('option "-r" not valid when importing '
1793 raise util.Abort(_('option "-r" not valid when importing '
1793 'files'))
1794 'files'))
1794 rev = scmutil.revrange(repo, rev)
1795 rev = scmutil.revrange(repo, rev)
1795 rev.sort(reverse=True)
1796 rev.sort(reverse=True)
1796 if (len(files) > 1 or len(rev) > 1) and patchname:
1797 if (len(files) > 1 or len(rev) > 1) and patchname:
1797 raise util.Abort(_('option "-n" not valid when importing multiple '
1798 raise util.Abort(_('option "-n" not valid when importing multiple '
1798 'patches'))
1799 'patches'))
1799 imported = []
1800 imported = []
1800 if rev:
1801 if rev:
1801 # If mq patches are applied, we can only import revisions
1802 # If mq patches are applied, we can only import revisions
1802 # that form a linear path to qbase.
1803 # that form a linear path to qbase.
1803 # Otherwise, they should form a linear path to a head.
1804 # Otherwise, they should form a linear path to a head.
1804 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1805 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1805 if len(heads) > 1:
1806 if len(heads) > 1:
1806 raise util.Abort(_('revision %d is the root of more than one '
1807 raise util.Abort(_('revision %d is the root of more than one '
1807 'branch') % rev[-1])
1808 'branch') % rev[-1])
1808 if self.applied:
1809 if self.applied:
1809 base = repo.changelog.node(rev[0])
1810 base = repo.changelog.node(rev[0])
1810 if base in [n.node for n in self.applied]:
1811 if base in [n.node for n in self.applied]:
1811 raise util.Abort(_('revision %d is already managed')
1812 raise util.Abort(_('revision %d is already managed')
1812 % rev[0])
1813 % rev[0])
1813 if heads != [self.applied[-1].node]:
1814 if heads != [self.applied[-1].node]:
1814 raise util.Abort(_('revision %d is not the parent of '
1815 raise util.Abort(_('revision %d is not the parent of '
1815 'the queue') % rev[0])
1816 'the queue') % rev[0])
1816 base = repo.changelog.rev(self.applied[0].node)
1817 base = repo.changelog.rev(self.applied[0].node)
1817 lastparent = repo.changelog.parentrevs(base)[0]
1818 lastparent = repo.changelog.parentrevs(base)[0]
1818 else:
1819 else:
1819 if heads != [repo.changelog.node(rev[0])]:
1820 if heads != [repo.changelog.node(rev[0])]:
1820 raise util.Abort(_('revision %d has unmanaged children')
1821 raise util.Abort(_('revision %d has unmanaged children')
1821 % rev[0])
1822 % rev[0])
1822 lastparent = None
1823 lastparent = None
1823
1824
1824 diffopts = self.diffopts({'git': git})
1825 diffopts = self.diffopts({'git': git})
1825 for r in rev:
1826 for r in rev:
1826 if not repo[r].mutable():
1827 if not repo[r].mutable():
1827 raise util.Abort(_('revision %d is not mutable') % r,
1828 raise util.Abort(_('revision %d is not mutable') % r,
1828 hint=_('see "hg help phases" for details'))
1829 hint=_('see "hg help phases" for details'))
1829 p1, p2 = repo.changelog.parentrevs(r)
1830 p1, p2 = repo.changelog.parentrevs(r)
1830 n = repo.changelog.node(r)
1831 n = repo.changelog.node(r)
1831 if p2 != nullrev:
1832 if p2 != nullrev:
1832 raise util.Abort(_('cannot import merge revision %d') % r)
1833 raise util.Abort(_('cannot import merge revision %d') % r)
1833 if lastparent and lastparent != r:
1834 if lastparent and lastparent != r:
1834 raise util.Abort(_('revision %d is not the parent of %d')
1835 raise util.Abort(_('revision %d is not the parent of %d')
1835 % (r, lastparent))
1836 % (r, lastparent))
1836 lastparent = p1
1837 lastparent = p1
1837
1838
1838 if not patchname:
1839 if not patchname:
1839 patchname = normname('%d.diff' % r)
1840 patchname = normname('%d.diff' % r)
1840 checkseries(patchname)
1841 checkseries(patchname)
1841 self.checkpatchname(patchname, force)
1842 self.checkpatchname(patchname, force)
1842 self.fullseries.insert(0, patchname)
1843 self.fullseries.insert(0, patchname)
1843
1844
1844 patchf = self.opener(patchname, "w")
1845 patchf = self.opener(patchname, "w")
1845 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1846 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1846 patchf.close()
1847 patchf.close()
1847
1848
1848 se = statusentry(n, patchname)
1849 se = statusentry(n, patchname)
1849 self.applied.insert(0, se)
1850 self.applied.insert(0, se)
1850
1851
1851 self.added.append(patchname)
1852 self.added.append(patchname)
1852 imported.append(patchname)
1853 imported.append(patchname)
1853 patchname = None
1854 patchname = None
1854 if rev and repo.ui.configbool('mq', 'secret', False):
1855 if rev and repo.ui.configbool('mq', 'secret', False):
1855 # if we added anything with --rev, we must move the secret root
1856 # if we added anything with --rev, we must move the secret root
1856 phases.retractboundary(repo, phases.secret, [n])
1857 phases.retractboundary(repo, phases.secret, [n])
1857 self.parseseries()
1858 self.parseseries()
1858 self.applieddirty = True
1859 self.applieddirty = True
1859 self.seriesdirty = True
1860 self.seriesdirty = True
1860
1861
1861 for i, filename in enumerate(files):
1862 for i, filename in enumerate(files):
1862 if existing:
1863 if existing:
1863 if filename == '-':
1864 if filename == '-':
1864 raise util.Abort(_('-e is incompatible with import from -'))
1865 raise util.Abort(_('-e is incompatible with import from -'))
1865 filename = normname(filename)
1866 filename = normname(filename)
1866 self.checkreservedname(filename)
1867 self.checkreservedname(filename)
1867 originpath = self.join(filename)
1868 originpath = self.join(filename)
1868 if not os.path.isfile(originpath):
1869 if not os.path.isfile(originpath):
1869 raise util.Abort(_("patch %s does not exist") % filename)
1870 raise util.Abort(_("patch %s does not exist") % filename)
1870
1871
1871 if patchname:
1872 if patchname:
1872 self.checkpatchname(patchname, force)
1873 self.checkpatchname(patchname, force)
1873
1874
1874 self.ui.write(_('renaming %s to %s\n')
1875 self.ui.write(_('renaming %s to %s\n')
1875 % (filename, patchname))
1876 % (filename, patchname))
1876 util.rename(originpath, self.join(patchname))
1877 util.rename(originpath, self.join(patchname))
1877 else:
1878 else:
1878 patchname = filename
1879 patchname = filename
1879
1880
1880 else:
1881 else:
1881 if filename == '-' and not patchname:
1882 if filename == '-' and not patchname:
1882 raise util.Abort(_('need --name to import a patch from -'))
1883 raise util.Abort(_('need --name to import a patch from -'))
1883 elif not patchname:
1884 elif not patchname:
1884 patchname = normname(os.path.basename(filename.rstrip('/')))
1885 patchname = normname(os.path.basename(filename.rstrip('/')))
1885 self.checkpatchname(patchname, force)
1886 self.checkpatchname(patchname, force)
1886 try:
1887 try:
1887 if filename == '-':
1888 if filename == '-':
1888 text = self.ui.fin.read()
1889 text = self.ui.fin.read()
1889 else:
1890 else:
1890 fp = url.open(self.ui, filename)
1891 fp = url.open(self.ui, filename)
1891 text = fp.read()
1892 text = fp.read()
1892 fp.close()
1893 fp.close()
1893 except (OSError, IOError):
1894 except (OSError, IOError):
1894 raise util.Abort(_("unable to read file %s") % filename)
1895 raise util.Abort(_("unable to read file %s") % filename)
1895 patchf = self.opener(patchname, "w")
1896 patchf = self.opener(patchname, "w")
1896 patchf.write(text)
1897 patchf.write(text)
1897 patchf.close()
1898 patchf.close()
1898 if not force:
1899 if not force:
1899 checkseries(patchname)
1900 checkseries(patchname)
1900 if patchname not in self.series:
1901 if patchname not in self.series:
1901 index = self.fullseriesend() + i
1902 index = self.fullseriesend() + i
1902 self.fullseries[index:index] = [patchname]
1903 self.fullseries[index:index] = [patchname]
1903 self.parseseries()
1904 self.parseseries()
1904 self.seriesdirty = True
1905 self.seriesdirty = True
1905 self.ui.warn(_("adding %s to series file\n") % patchname)
1906 self.ui.warn(_("adding %s to series file\n") % patchname)
1906 self.added.append(patchname)
1907 self.added.append(patchname)
1907 imported.append(patchname)
1908 imported.append(patchname)
1908 patchname = None
1909 patchname = None
1909
1910
1910 self.removeundo(repo)
1911 self.removeundo(repo)
1911 return imported
1912 return imported
1912
1913
1913 @command("qdelete|qremove|qrm",
1914 @command("qdelete|qremove|qrm",
1914 [('k', 'keep', None, _('keep patch file')),
1915 [('k', 'keep', None, _('keep patch file')),
1915 ('r', 'rev', [],
1916 ('r', 'rev', [],
1916 _('stop managing a revision (DEPRECATED)'), _('REV'))],
1917 _('stop managing a revision (DEPRECATED)'), _('REV'))],
1917 _('hg qdelete [-k] [PATCH]...'))
1918 _('hg qdelete [-k] [PATCH]...'))
1918 def delete(ui, repo, *patches, **opts):
1919 def delete(ui, repo, *patches, **opts):
1919 """remove patches from queue
1920 """remove patches from queue
1920
1921
1921 The patches must not be applied, and at least one patch is required. Exact
1922 The patches must not be applied, and at least one patch is required. Exact
1922 patch identifiers must be given. With -k/--keep, the patch files are
1923 patch identifiers must be given. With -k/--keep, the patch files are
1923 preserved in the patch directory.
1924 preserved in the patch directory.
1924
1925
1925 To stop managing a patch and move it into permanent history,
1926 To stop managing a patch and move it into permanent history,
1926 use the :hg:`qfinish` command."""
1927 use the :hg:`qfinish` command."""
1927 q = repo.mq
1928 q = repo.mq
1928 q.delete(repo, patches, opts)
1929 q.delete(repo, patches, opts)
1929 q.savedirty()
1930 q.savedirty()
1930 return 0
1931 return 0
1931
1932
1932 @command("qapplied",
1933 @command("qapplied",
1933 [('1', 'last', None, _('show only the preceding applied patch'))
1934 [('1', 'last', None, _('show only the preceding applied patch'))
1934 ] + seriesopts,
1935 ] + seriesopts,
1935 _('hg qapplied [-1] [-s] [PATCH]'))
1936 _('hg qapplied [-1] [-s] [PATCH]'))
1936 def applied(ui, repo, patch=None, **opts):
1937 def applied(ui, repo, patch=None, **opts):
1937 """print the patches already applied
1938 """print the patches already applied
1938
1939
1939 Returns 0 on success."""
1940 Returns 0 on success."""
1940
1941
1941 q = repo.mq
1942 q = repo.mq
1942
1943
1943 if patch:
1944 if patch:
1944 if patch not in q.series:
1945 if patch not in q.series:
1945 raise util.Abort(_("patch %s is not in series file") % patch)
1946 raise util.Abort(_("patch %s is not in series file") % patch)
1946 end = q.series.index(patch) + 1
1947 end = q.series.index(patch) + 1
1947 else:
1948 else:
1948 end = q.seriesend(True)
1949 end = q.seriesend(True)
1949
1950
1950 if opts.get('last') and not end:
1951 if opts.get('last') and not end:
1951 ui.write(_("no patches applied\n"))
1952 ui.write(_("no patches applied\n"))
1952 return 1
1953 return 1
1953 elif opts.get('last') and end == 1:
1954 elif opts.get('last') and end == 1:
1954 ui.write(_("only one patch applied\n"))
1955 ui.write(_("only one patch applied\n"))
1955 return 1
1956 return 1
1956 elif opts.get('last'):
1957 elif opts.get('last'):
1957 start = end - 2
1958 start = end - 2
1958 end = 1
1959 end = 1
1959 else:
1960 else:
1960 start = 0
1961 start = 0
1961
1962
1962 q.qseries(repo, length=end, start=start, status='A',
1963 q.qseries(repo, length=end, start=start, status='A',
1963 summary=opts.get('summary'))
1964 summary=opts.get('summary'))
1964
1965
1965
1966
1966 @command("qunapplied",
1967 @command("qunapplied",
1967 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
1968 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
1968 _('hg qunapplied [-1] [-s] [PATCH]'))
1969 _('hg qunapplied [-1] [-s] [PATCH]'))
1969 def unapplied(ui, repo, patch=None, **opts):
1970 def unapplied(ui, repo, patch=None, **opts):
1970 """print the patches not yet applied
1971 """print the patches not yet applied
1971
1972
1972 Returns 0 on success."""
1973 Returns 0 on success."""
1973
1974
1974 q = repo.mq
1975 q = repo.mq
1975 if patch:
1976 if patch:
1976 if patch not in q.series:
1977 if patch not in q.series:
1977 raise util.Abort(_("patch %s is not in series file") % patch)
1978 raise util.Abort(_("patch %s is not in series file") % patch)
1978 start = q.series.index(patch) + 1
1979 start = q.series.index(patch) + 1
1979 else:
1980 else:
1980 start = q.seriesend(True)
1981 start = q.seriesend(True)
1981
1982
1982 if start == len(q.series) and opts.get('first'):
1983 if start == len(q.series) and opts.get('first'):
1983 ui.write(_("all patches applied\n"))
1984 ui.write(_("all patches applied\n"))
1984 return 1
1985 return 1
1985
1986
1986 length = opts.get('first') and 1 or None
1987 length = opts.get('first') and 1 or None
1987 q.qseries(repo, start=start, length=length, status='U',
1988 q.qseries(repo, start=start, length=length, status='U',
1988 summary=opts.get('summary'))
1989 summary=opts.get('summary'))
1989
1990
1990 @command("qimport",
1991 @command("qimport",
1991 [('e', 'existing', None, _('import file in patch directory')),
1992 [('e', 'existing', None, _('import file in patch directory')),
1992 ('n', 'name', '',
1993 ('n', 'name', '',
1993 _('name of patch file'), _('NAME')),
1994 _('name of patch file'), _('NAME')),
1994 ('f', 'force', None, _('overwrite existing files')),
1995 ('f', 'force', None, _('overwrite existing files')),
1995 ('r', 'rev', [],
1996 ('r', 'rev', [],
1996 _('place existing revisions under mq control'), _('REV')),
1997 _('place existing revisions under mq control'), _('REV')),
1997 ('g', 'git', None, _('use git extended diff format')),
1998 ('g', 'git', None, _('use git extended diff format')),
1998 ('P', 'push', None, _('qpush after importing'))],
1999 ('P', 'push', None, _('qpush after importing'))],
1999 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
2000 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...'))
2000 def qimport(ui, repo, *filename, **opts):
2001 def qimport(ui, repo, *filename, **opts):
2001 """import a patch or existing changeset
2002 """import a patch or existing changeset
2002
2003
2003 The patch is inserted into the series after the last applied
2004 The patch is inserted into the series after the last applied
2004 patch. If no patches have been applied, qimport prepends the patch
2005 patch. If no patches have been applied, qimport prepends the patch
2005 to the series.
2006 to the series.
2006
2007
2007 The patch will have the same name as its source file unless you
2008 The patch will have the same name as its source file unless you
2008 give it a new one with -n/--name.
2009 give it a new one with -n/--name.
2009
2010
2010 You can register an existing patch inside the patch directory with
2011 You can register an existing patch inside the patch directory with
2011 the -e/--existing flag.
2012 the -e/--existing flag.
2012
2013
2013 With -f/--force, an existing patch of the same name will be
2014 With -f/--force, an existing patch of the same name will be
2014 overwritten.
2015 overwritten.
2015
2016
2016 An existing changeset may be placed under mq control with -r/--rev
2017 An existing changeset may be placed under mq control with -r/--rev
2017 (e.g. qimport --rev tip -n patch will place tip under mq control).
2018 (e.g. qimport --rev tip -n patch will place tip under mq control).
2018 With -g/--git, patches imported with --rev will use the git diff
2019 With -g/--git, patches imported with --rev will use the git diff
2019 format. See the diffs help topic for information on why this is
2020 format. See the diffs help topic for information on why this is
2020 important for preserving rename/copy information and permission
2021 important for preserving rename/copy information and permission
2021 changes. Use :hg:`qfinish` to remove changesets from mq control.
2022 changes. Use :hg:`qfinish` to remove changesets from mq control.
2022
2023
2023 To import a patch from standard input, pass - as the patch file.
2024 To import a patch from standard input, pass - as the patch file.
2024 When importing from standard input, a patch name must be specified
2025 When importing from standard input, a patch name must be specified
2025 using the --name flag.
2026 using the --name flag.
2026
2027
2027 To import an existing patch while renaming it::
2028 To import an existing patch while renaming it::
2028
2029
2029 hg qimport -e existing-patch -n new-name
2030 hg qimport -e existing-patch -n new-name
2030
2031
2031 Returns 0 if import succeeded.
2032 Returns 0 if import succeeded.
2032 """
2033 """
2033 lock = repo.lock() # cause this may move phase
2034 lock = repo.lock() # cause this may move phase
2034 try:
2035 try:
2035 q = repo.mq
2036 q = repo.mq
2036 try:
2037 try:
2037 imported = q.qimport(
2038 imported = q.qimport(
2038 repo, filename, patchname=opts.get('name'),
2039 repo, filename, patchname=opts.get('name'),
2039 existing=opts.get('existing'), force=opts.get('force'),
2040 existing=opts.get('existing'), force=opts.get('force'),
2040 rev=opts.get('rev'), git=opts.get('git'))
2041 rev=opts.get('rev'), git=opts.get('git'))
2041 finally:
2042 finally:
2042 q.savedirty()
2043 q.savedirty()
2043
2044
2044
2045
2045 if imported and opts.get('push') and not opts.get('rev'):
2046 if imported and opts.get('push') and not opts.get('rev'):
2046 return q.push(repo, imported[-1])
2047 return q.push(repo, imported[-1])
2047 finally:
2048 finally:
2048 lock.release()
2049 lock.release()
2049 return 0
2050 return 0
2050
2051
2051 def qinit(ui, repo, create):
2052 def qinit(ui, repo, create):
2052 """initialize a new queue repository
2053 """initialize a new queue repository
2053
2054
2054 This command also creates a series file for ordering patches, and
2055 This command also creates a series file for ordering patches, and
2055 an mq-specific .hgignore file in the queue repository, to exclude
2056 an mq-specific .hgignore file in the queue repository, to exclude
2056 the status and guards files (these contain mostly transient state).
2057 the status and guards files (these contain mostly transient state).
2057
2058
2058 Returns 0 if initialization succeeded."""
2059 Returns 0 if initialization succeeded."""
2059 q = repo.mq
2060 q = repo.mq
2060 r = q.init(repo, create)
2061 r = q.init(repo, create)
2061 q.savedirty()
2062 q.savedirty()
2062 if r:
2063 if r:
2063 if not os.path.exists(r.wjoin('.hgignore')):
2064 if not os.path.exists(r.wjoin('.hgignore')):
2064 fp = r.wopener('.hgignore', 'w')
2065 fp = r.wopener('.hgignore', 'w')
2065 fp.write('^\\.hg\n')
2066 fp.write('^\\.hg\n')
2066 fp.write('^\\.mq\n')
2067 fp.write('^\\.mq\n')
2067 fp.write('syntax: glob\n')
2068 fp.write('syntax: glob\n')
2068 fp.write('status\n')
2069 fp.write('status\n')
2069 fp.write('guards\n')
2070 fp.write('guards\n')
2070 fp.close()
2071 fp.close()
2071 if not os.path.exists(r.wjoin('series')):
2072 if not os.path.exists(r.wjoin('series')):
2072 r.wopener('series', 'w').close()
2073 r.wopener('series', 'w').close()
2073 r[None].add(['.hgignore', 'series'])
2074 r[None].add(['.hgignore', 'series'])
2074 commands.add(ui, r)
2075 commands.add(ui, r)
2075 return 0
2076 return 0
2076
2077
2077 @command("^qinit",
2078 @command("^qinit",
2078 [('c', 'create-repo', None, _('create queue repository'))],
2079 [('c', 'create-repo', None, _('create queue repository'))],
2079 _('hg qinit [-c]'))
2080 _('hg qinit [-c]'))
2080 def init(ui, repo, **opts):
2081 def init(ui, repo, **opts):
2081 """init a new queue repository (DEPRECATED)
2082 """init a new queue repository (DEPRECATED)
2082
2083
2083 The queue repository is unversioned by default. If
2084 The queue repository is unversioned by default. If
2084 -c/--create-repo is specified, qinit will create a separate nested
2085 -c/--create-repo is specified, qinit will create a separate nested
2085 repository for patches (qinit -c may also be run later to convert
2086 repository for patches (qinit -c may also be run later to convert
2086 an unversioned patch repository into a versioned one). You can use
2087 an unversioned patch repository into a versioned one). You can use
2087 qcommit to commit changes to this queue repository.
2088 qcommit to commit changes to this queue repository.
2088
2089
2089 This command is deprecated. Without -c, it's implied by other relevant
2090 This command is deprecated. Without -c, it's implied by other relevant
2090 commands. With -c, use :hg:`init --mq` instead."""
2091 commands. With -c, use :hg:`init --mq` instead."""
2091 return qinit(ui, repo, create=opts.get('create_repo'))
2092 return qinit(ui, repo, create=opts.get('create_repo'))
2092
2093
2093 @command("qclone",
2094 @command("qclone",
2094 [('', 'pull', None, _('use pull protocol to copy metadata')),
2095 [('', 'pull', None, _('use pull protocol to copy metadata')),
2095 ('U', 'noupdate', None, _('do not update the new working directories')),
2096 ('U', 'noupdate', None, _('do not update the new working directories')),
2096 ('', 'uncompressed', None,
2097 ('', 'uncompressed', None,
2097 _('use uncompressed transfer (fast over LAN)')),
2098 _('use uncompressed transfer (fast over LAN)')),
2098 ('p', 'patches', '',
2099 ('p', 'patches', '',
2099 _('location of source patch repository'), _('REPO')),
2100 _('location of source patch repository'), _('REPO')),
2100 ] + commands.remoteopts,
2101 ] + commands.remoteopts,
2101 _('hg qclone [OPTION]... SOURCE [DEST]'))
2102 _('hg qclone [OPTION]... SOURCE [DEST]'))
2102 def clone(ui, source, dest=None, **opts):
2103 def clone(ui, source, dest=None, **opts):
2103 '''clone main and patch repository at same time
2104 '''clone main and patch repository at same time
2104
2105
2105 If source is local, destination will have no patches applied. If
2106 If source is local, destination will have no patches applied. If
2106 source is remote, this command can not check if patches are
2107 source is remote, this command can not check if patches are
2107 applied in source, so cannot guarantee that patches are not
2108 applied in source, so cannot guarantee that patches are not
2108 applied in destination. If you clone remote repository, be sure
2109 applied in destination. If you clone remote repository, be sure
2109 before that it has no patches applied.
2110 before that it has no patches applied.
2110
2111
2111 Source patch repository is looked for in <src>/.hg/patches by
2112 Source patch repository is looked for in <src>/.hg/patches by
2112 default. Use -p <url> to change.
2113 default. Use -p <url> to change.
2113
2114
2114 The patch directory must be a nested Mercurial repository, as
2115 The patch directory must be a nested Mercurial repository, as
2115 would be created by :hg:`init --mq`.
2116 would be created by :hg:`init --mq`.
2116
2117
2117 Return 0 on success.
2118 Return 0 on success.
2118 '''
2119 '''
2119 def patchdir(repo):
2120 def patchdir(repo):
2120 """compute a patch repo url from a repo object"""
2121 """compute a patch repo url from a repo object"""
2121 url = repo.url()
2122 url = repo.url()
2122 if url.endswith('/'):
2123 if url.endswith('/'):
2123 url = url[:-1]
2124 url = url[:-1]
2124 return url + '/.hg/patches'
2125 return url + '/.hg/patches'
2125
2126
2126 # main repo (destination and sources)
2127 # main repo (destination and sources)
2127 if dest is None:
2128 if dest is None:
2128 dest = hg.defaultdest(source)
2129 dest = hg.defaultdest(source)
2129 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2130 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2130
2131
2131 # patches repo (source only)
2132 # patches repo (source only)
2132 if opts.get('patches'):
2133 if opts.get('patches'):
2133 patchespath = ui.expandpath(opts.get('patches'))
2134 patchespath = ui.expandpath(opts.get('patches'))
2134 else:
2135 else:
2135 patchespath = patchdir(sr)
2136 patchespath = patchdir(sr)
2136 try:
2137 try:
2137 hg.repository(ui, patchespath)
2138 hg.repository(ui, patchespath)
2138 except error.RepoError:
2139 except error.RepoError:
2139 raise util.Abort(_('versioned patch repository not found'
2140 raise util.Abort(_('versioned patch repository not found'
2140 ' (see init --mq)'))
2141 ' (see init --mq)'))
2141 qbase, destrev = None, None
2142 qbase, destrev = None, None
2142 if sr.local():
2143 if sr.local():
2143 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2144 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2144 qbase = sr.mq.applied[0].node
2145 qbase = sr.mq.applied[0].node
2145 if not hg.islocal(dest):
2146 if not hg.islocal(dest):
2146 heads = set(sr.heads())
2147 heads = set(sr.heads())
2147 destrev = list(heads.difference(sr.heads(qbase)))
2148 destrev = list(heads.difference(sr.heads(qbase)))
2148 destrev.append(sr.changelog.parents(qbase)[0])
2149 destrev.append(sr.changelog.parents(qbase)[0])
2149 elif sr.capable('lookup'):
2150 elif sr.capable('lookup'):
2150 try:
2151 try:
2151 qbase = sr.lookup('qbase')
2152 qbase = sr.lookup('qbase')
2152 except error.RepoError:
2153 except error.RepoError:
2153 pass
2154 pass
2154
2155
2155 ui.note(_('cloning main repository\n'))
2156 ui.note(_('cloning main repository\n'))
2156 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2157 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2157 pull=opts.get('pull'),
2158 pull=opts.get('pull'),
2158 rev=destrev,
2159 rev=destrev,
2159 update=False,
2160 update=False,
2160 stream=opts.get('uncompressed'))
2161 stream=opts.get('uncompressed'))
2161
2162
2162 ui.note(_('cloning patch repository\n'))
2163 ui.note(_('cloning patch repository\n'))
2163 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2164 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2164 pull=opts.get('pull'), update=not opts.get('noupdate'),
2165 pull=opts.get('pull'), update=not opts.get('noupdate'),
2165 stream=opts.get('uncompressed'))
2166 stream=opts.get('uncompressed'))
2166
2167
2167 if dr.local():
2168 if dr.local():
2168 if qbase:
2169 if qbase:
2169 ui.note(_('stripping applied patches from destination '
2170 ui.note(_('stripping applied patches from destination '
2170 'repository\n'))
2171 'repository\n'))
2171 dr.mq.strip(dr, [qbase], update=False, backup=None)
2172 dr.mq.strip(dr, [qbase], update=False, backup=None)
2172 if not opts.get('noupdate'):
2173 if not opts.get('noupdate'):
2173 ui.note(_('updating destination repository\n'))
2174 ui.note(_('updating destination repository\n'))
2174 hg.update(dr, dr.changelog.tip())
2175 hg.update(dr, dr.changelog.tip())
2175
2176
2176 @command("qcommit|qci",
2177 @command("qcommit|qci",
2177 commands.table["^commit|ci"][1],
2178 commands.table["^commit|ci"][1],
2178 _('hg qcommit [OPTION]... [FILE]...'))
2179 _('hg qcommit [OPTION]... [FILE]...'))
2179 def commit(ui, repo, *pats, **opts):
2180 def commit(ui, repo, *pats, **opts):
2180 """commit changes in the queue repository (DEPRECATED)
2181 """commit changes in the queue repository (DEPRECATED)
2181
2182
2182 This command is deprecated; use :hg:`commit --mq` instead."""
2183 This command is deprecated; use :hg:`commit --mq` instead."""
2183 q = repo.mq
2184 q = repo.mq
2184 r = q.qrepo()
2185 r = q.qrepo()
2185 if not r:
2186 if not r:
2186 raise util.Abort('no queue repository')
2187 raise util.Abort('no queue repository')
2187 commands.commit(r.ui, r, *pats, **opts)
2188 commands.commit(r.ui, r, *pats, **opts)
2188
2189
2189 @command("qseries",
2190 @command("qseries",
2190 [('m', 'missing', None, _('print patches not in series')),
2191 [('m', 'missing', None, _('print patches not in series')),
2191 ] + seriesopts,
2192 ] + seriesopts,
2192 _('hg qseries [-ms]'))
2193 _('hg qseries [-ms]'))
2193 def series(ui, repo, **opts):
2194 def series(ui, repo, **opts):
2194 """print the entire series file
2195 """print the entire series file
2195
2196
2196 Returns 0 on success."""
2197 Returns 0 on success."""
2197 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2198 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2198 return 0
2199 return 0
2199
2200
2200 @command("qtop", seriesopts, _('hg qtop [-s]'))
2201 @command("qtop", seriesopts, _('hg qtop [-s]'))
2201 def top(ui, repo, **opts):
2202 def top(ui, repo, **opts):
2202 """print the name of the current patch
2203 """print the name of the current patch
2203
2204
2204 Returns 0 on success."""
2205 Returns 0 on success."""
2205 q = repo.mq
2206 q = repo.mq
2206 t = q.applied and q.seriesend(True) or 0
2207 t = q.applied and q.seriesend(True) or 0
2207 if t:
2208 if t:
2208 q.qseries(repo, start=t - 1, length=1, status='A',
2209 q.qseries(repo, start=t - 1, length=1, status='A',
2209 summary=opts.get('summary'))
2210 summary=opts.get('summary'))
2210 else:
2211 else:
2211 ui.write(_("no patches applied\n"))
2212 ui.write(_("no patches applied\n"))
2212 return 1
2213 return 1
2213
2214
2214 @command("qnext", seriesopts, _('hg qnext [-s]'))
2215 @command("qnext", seriesopts, _('hg qnext [-s]'))
2215 def next(ui, repo, **opts):
2216 def next(ui, repo, **opts):
2216 """print the name of the next pushable patch
2217 """print the name of the next pushable patch
2217
2218
2218 Returns 0 on success."""
2219 Returns 0 on success."""
2219 q = repo.mq
2220 q = repo.mq
2220 end = q.seriesend()
2221 end = q.seriesend()
2221 if end == len(q.series):
2222 if end == len(q.series):
2222 ui.write(_("all patches applied\n"))
2223 ui.write(_("all patches applied\n"))
2223 return 1
2224 return 1
2224 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2225 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2225
2226
2226 @command("qprev", seriesopts, _('hg qprev [-s]'))
2227 @command("qprev", seriesopts, _('hg qprev [-s]'))
2227 def prev(ui, repo, **opts):
2228 def prev(ui, repo, **opts):
2228 """print the name of the preceding applied patch
2229 """print the name of the preceding applied patch
2229
2230
2230 Returns 0 on success."""
2231 Returns 0 on success."""
2231 q = repo.mq
2232 q = repo.mq
2232 l = len(q.applied)
2233 l = len(q.applied)
2233 if l == 1:
2234 if l == 1:
2234 ui.write(_("only one patch applied\n"))
2235 ui.write(_("only one patch applied\n"))
2235 return 1
2236 return 1
2236 if not l:
2237 if not l:
2237 ui.write(_("no patches applied\n"))
2238 ui.write(_("no patches applied\n"))
2238 return 1
2239 return 1
2239 idx = q.series.index(q.applied[-2].name)
2240 idx = q.series.index(q.applied[-2].name)
2240 q.qseries(repo, start=idx, length=1, status='A',
2241 q.qseries(repo, start=idx, length=1, status='A',
2241 summary=opts.get('summary'))
2242 summary=opts.get('summary'))
2242
2243
2243 def setupheaderopts(ui, opts):
2244 def setupheaderopts(ui, opts):
2244 if not opts.get('user') and opts.get('currentuser'):
2245 if not opts.get('user') and opts.get('currentuser'):
2245 opts['user'] = ui.username()
2246 opts['user'] = ui.username()
2246 if not opts.get('date') and opts.get('currentdate'):
2247 if not opts.get('date') and opts.get('currentdate'):
2247 opts['date'] = "%d %d" % util.makedate()
2248 opts['date'] = "%d %d" % util.makedate()
2248
2249
2249 @command("^qnew",
2250 @command("^qnew",
2250 [('e', 'edit', None, _('edit commit message')),
2251 [('e', 'edit', None, _('edit commit message')),
2251 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2252 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2252 ('g', 'git', None, _('use git extended diff format')),
2253 ('g', 'git', None, _('use git extended diff format')),
2253 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2254 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2254 ('u', 'user', '',
2255 ('u', 'user', '',
2255 _('add "From: <USER>" to patch'), _('USER')),
2256 _('add "From: <USER>" to patch'), _('USER')),
2256 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2257 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2257 ('d', 'date', '',
2258 ('d', 'date', '',
2258 _('add "Date: <DATE>" to patch'), _('DATE'))
2259 _('add "Date: <DATE>" to patch'), _('DATE'))
2259 ] + commands.walkopts + commands.commitopts,
2260 ] + commands.walkopts + commands.commitopts,
2260 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2261 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2261 def new(ui, repo, patch, *args, **opts):
2262 def new(ui, repo, patch, *args, **opts):
2262 """create a new patch
2263 """create a new patch
2263
2264
2264 qnew creates a new patch on top of the currently-applied patch (if
2265 qnew creates a new patch on top of the currently-applied patch (if
2265 any). The patch will be initialized with any outstanding changes
2266 any). The patch will be initialized with any outstanding changes
2266 in the working directory. You may also use -I/--include,
2267 in the working directory. You may also use -I/--include,
2267 -X/--exclude, and/or a list of files after the patch name to add
2268 -X/--exclude, and/or a list of files after the patch name to add
2268 only changes to matching files to the new patch, leaving the rest
2269 only changes to matching files to the new patch, leaving the rest
2269 as uncommitted modifications.
2270 as uncommitted modifications.
2270
2271
2271 -u/--user and -d/--date can be used to set the (given) user and
2272 -u/--user and -d/--date can be used to set the (given) user and
2272 date, respectively. -U/--currentuser and -D/--currentdate set user
2273 date, respectively. -U/--currentuser and -D/--currentdate set user
2273 to current user and date to current date.
2274 to current user and date to current date.
2274
2275
2275 -e/--edit, -m/--message or -l/--logfile set the patch header as
2276 -e/--edit, -m/--message or -l/--logfile set the patch header as
2276 well as the commit message. If none is specified, the header is
2277 well as the commit message. If none is specified, the header is
2277 empty and the commit message is '[mq]: PATCH'.
2278 empty and the commit message is '[mq]: PATCH'.
2278
2279
2279 Use the -g/--git option to keep the patch in the git extended diff
2280 Use the -g/--git option to keep the patch in the git extended diff
2280 format. Read the diffs help topic for more information on why this
2281 format. Read the diffs help topic for more information on why this
2281 is important for preserving permission changes and copy/rename
2282 is important for preserving permission changes and copy/rename
2282 information.
2283 information.
2283
2284
2284 Returns 0 on successful creation of a new patch.
2285 Returns 0 on successful creation of a new patch.
2285 """
2286 """
2286 msg = cmdutil.logmessage(ui, opts)
2287 msg = cmdutil.logmessage(ui, opts)
2287 def getmsg():
2288 def getmsg():
2288 return ui.edit(msg, opts.get('user') or ui.username())
2289 return ui.edit(msg, opts.get('user') or ui.username())
2289 q = repo.mq
2290 q = repo.mq
2290 opts['msg'] = msg
2291 opts['msg'] = msg
2291 if opts.get('edit'):
2292 if opts.get('edit'):
2292 opts['msg'] = getmsg
2293 opts['msg'] = getmsg
2293 else:
2294 else:
2294 opts['msg'] = msg
2295 opts['msg'] = msg
2295 setupheaderopts(ui, opts)
2296 setupheaderopts(ui, opts)
2296 q.new(repo, patch, *args, **opts)
2297 q.new(repo, patch, *args, **opts)
2297 q.savedirty()
2298 q.savedirty()
2298 return 0
2299 return 0
2299
2300
2300 @command("^qrefresh",
2301 @command("^qrefresh",
2301 [('e', 'edit', None, _('edit commit message')),
2302 [('e', 'edit', None, _('edit commit message')),
2302 ('g', 'git', None, _('use git extended diff format')),
2303 ('g', 'git', None, _('use git extended diff format')),
2303 ('s', 'short', None,
2304 ('s', 'short', None,
2304 _('refresh only files already in the patch and specified files')),
2305 _('refresh only files already in the patch and specified files')),
2305 ('U', 'currentuser', None,
2306 ('U', 'currentuser', None,
2306 _('add/update author field in patch with current user')),
2307 _('add/update author field in patch with current user')),
2307 ('u', 'user', '',
2308 ('u', 'user', '',
2308 _('add/update author field in patch with given user'), _('USER')),
2309 _('add/update author field in patch with given user'), _('USER')),
2309 ('D', 'currentdate', None,
2310 ('D', 'currentdate', None,
2310 _('add/update date field in patch with current date')),
2311 _('add/update date field in patch with current date')),
2311 ('d', 'date', '',
2312 ('d', 'date', '',
2312 _('add/update date field in patch with given date'), _('DATE'))
2313 _('add/update date field in patch with given date'), _('DATE'))
2313 ] + commands.walkopts + commands.commitopts,
2314 ] + commands.walkopts + commands.commitopts,
2314 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2315 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2315 def refresh(ui, repo, *pats, **opts):
2316 def refresh(ui, repo, *pats, **opts):
2316 """update the current patch
2317 """update the current patch
2317
2318
2318 If any file patterns are provided, the refreshed patch will
2319 If any file patterns are provided, the refreshed patch will
2319 contain only the modifications that match those patterns; the
2320 contain only the modifications that match those patterns; the
2320 remaining modifications will remain in the working directory.
2321 remaining modifications will remain in the working directory.
2321
2322
2322 If -s/--short is specified, files currently included in the patch
2323 If -s/--short is specified, files currently included in the patch
2323 will be refreshed just like matched files and remain in the patch.
2324 will be refreshed just like matched files and remain in the patch.
2324
2325
2325 If -e/--edit is specified, Mercurial will start your configured editor for
2326 If -e/--edit is specified, Mercurial will start your configured editor for
2326 you to enter a message. In case qrefresh fails, you will find a backup of
2327 you to enter a message. In case qrefresh fails, you will find a backup of
2327 your message in ``.hg/last-message.txt``.
2328 your message in ``.hg/last-message.txt``.
2328
2329
2329 hg add/remove/copy/rename work as usual, though you might want to
2330 hg add/remove/copy/rename work as usual, though you might want to
2330 use git-style patches (-g/--git or [diff] git=1) to track copies
2331 use git-style patches (-g/--git or [diff] git=1) to track copies
2331 and renames. See the diffs help topic for more information on the
2332 and renames. See the diffs help topic for more information on the
2332 git diff format.
2333 git diff format.
2333
2334
2334 Returns 0 on success.
2335 Returns 0 on success.
2335 """
2336 """
2336 q = repo.mq
2337 q = repo.mq
2337 message = cmdutil.logmessage(ui, opts)
2338 message = cmdutil.logmessage(ui, opts)
2338 if opts.get('edit'):
2339 if opts.get('edit'):
2339 if not q.applied:
2340 if not q.applied:
2340 ui.write(_("no patches applied\n"))
2341 ui.write(_("no patches applied\n"))
2341 return 1
2342 return 1
2342 if message:
2343 if message:
2343 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2344 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2344 patch = q.applied[-1].name
2345 patch = q.applied[-1].name
2345 ph = patchheader(q.join(patch), q.plainmode)
2346 ph = patchheader(q.join(patch), q.plainmode)
2346 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2347 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2347 # We don't want to lose the patch message if qrefresh fails (issue2062)
2348 # We don't want to lose the patch message if qrefresh fails (issue2062)
2348 repo.savecommitmessage(message)
2349 repo.savecommitmessage(message)
2349 setupheaderopts(ui, opts)
2350 setupheaderopts(ui, opts)
2350 wlock = repo.wlock()
2351 wlock = repo.wlock()
2351 try:
2352 try:
2352 ret = q.refresh(repo, pats, msg=message, **opts)
2353 ret = q.refresh(repo, pats, msg=message, **opts)
2353 q.savedirty()
2354 q.savedirty()
2354 return ret
2355 return ret
2355 finally:
2356 finally:
2356 wlock.release()
2357 wlock.release()
2357
2358
2358 @command("^qdiff",
2359 @command("^qdiff",
2359 commands.diffopts + commands.diffopts2 + commands.walkopts,
2360 commands.diffopts + commands.diffopts2 + commands.walkopts,
2360 _('hg qdiff [OPTION]... [FILE]...'))
2361 _('hg qdiff [OPTION]... [FILE]...'))
2361 def diff(ui, repo, *pats, **opts):
2362 def diff(ui, repo, *pats, **opts):
2362 """diff of the current patch and subsequent modifications
2363 """diff of the current patch and subsequent modifications
2363
2364
2364 Shows a diff which includes the current patch as well as any
2365 Shows a diff which includes the current patch as well as any
2365 changes which have been made in the working directory since the
2366 changes which have been made in the working directory since the
2366 last refresh (thus showing what the current patch would become
2367 last refresh (thus showing what the current patch would become
2367 after a qrefresh).
2368 after a qrefresh).
2368
2369
2369 Use :hg:`diff` if you only want to see the changes made since the
2370 Use :hg:`diff` if you only want to see the changes made since the
2370 last qrefresh, or :hg:`export qtip` if you want to see changes
2371 last qrefresh, or :hg:`export qtip` if you want to see changes
2371 made by the current patch without including changes made since the
2372 made by the current patch without including changes made since the
2372 qrefresh.
2373 qrefresh.
2373
2374
2374 Returns 0 on success.
2375 Returns 0 on success.
2375 """
2376 """
2376 repo.mq.diff(repo, pats, opts)
2377 repo.mq.diff(repo, pats, opts)
2377 return 0
2378 return 0
2378
2379
2379 @command('qfold',
2380 @command('qfold',
2380 [('e', 'edit', None, _('edit patch header')),
2381 [('e', 'edit', None, _('edit patch header')),
2381 ('k', 'keep', None, _('keep folded patch files')),
2382 ('k', 'keep', None, _('keep folded patch files')),
2382 ] + commands.commitopts,
2383 ] + commands.commitopts,
2383 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2384 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2384 def fold(ui, repo, *files, **opts):
2385 def fold(ui, repo, *files, **opts):
2385 """fold the named patches into the current patch
2386 """fold the named patches into the current patch
2386
2387
2387 Patches must not yet be applied. Each patch will be successively
2388 Patches must not yet be applied. Each patch will be successively
2388 applied to the current patch in the order given. If all the
2389 applied to the current patch in the order given. If all the
2389 patches apply successfully, the current patch will be refreshed
2390 patches apply successfully, the current patch will be refreshed
2390 with the new cumulative patch, and the folded patches will be
2391 with the new cumulative patch, and the folded patches will be
2391 deleted. With -k/--keep, the folded patch files will not be
2392 deleted. With -k/--keep, the folded patch files will not be
2392 removed afterwards.
2393 removed afterwards.
2393
2394
2394 The header for each folded patch will be concatenated with the
2395 The header for each folded patch will be concatenated with the
2395 current patch header, separated by a line of ``* * *``.
2396 current patch header, separated by a line of ``* * *``.
2396
2397
2397 Returns 0 on success."""
2398 Returns 0 on success."""
2398 q = repo.mq
2399 q = repo.mq
2399 if not files:
2400 if not files:
2400 raise util.Abort(_('qfold requires at least one patch name'))
2401 raise util.Abort(_('qfold requires at least one patch name'))
2401 if not q.checktoppatch(repo)[0]:
2402 if not q.checktoppatch(repo)[0]:
2402 raise util.Abort(_('no patches applied'))
2403 raise util.Abort(_('no patches applied'))
2403 q.checklocalchanges(repo)
2404 q.checklocalchanges(repo)
2404
2405
2405 message = cmdutil.logmessage(ui, opts)
2406 message = cmdutil.logmessage(ui, opts)
2406 if opts.get('edit'):
2407 if opts.get('edit'):
2407 if message:
2408 if message:
2408 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2409 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2409
2410
2410 parent = q.lookup('qtip')
2411 parent = q.lookup('qtip')
2411 patches = []
2412 patches = []
2412 messages = []
2413 messages = []
2413 for f in files:
2414 for f in files:
2414 p = q.lookup(f)
2415 p = q.lookup(f)
2415 if p in patches or p == parent:
2416 if p in patches or p == parent:
2416 ui.warn(_('Skipping already folded patch %s\n') % p)
2417 ui.warn(_('Skipping already folded patch %s\n') % p)
2417 if q.isapplied(p):
2418 if q.isapplied(p):
2418 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2419 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2419 patches.append(p)
2420 patches.append(p)
2420
2421
2421 for p in patches:
2422 for p in patches:
2422 if not message:
2423 if not message:
2423 ph = patchheader(q.join(p), q.plainmode)
2424 ph = patchheader(q.join(p), q.plainmode)
2424 if ph.message:
2425 if ph.message:
2425 messages.append(ph.message)
2426 messages.append(ph.message)
2426 pf = q.join(p)
2427 pf = q.join(p)
2427 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2428 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2428 if not patchsuccess:
2429 if not patchsuccess:
2429 raise util.Abort(_('error folding patch %s') % p)
2430 raise util.Abort(_('error folding patch %s') % p)
2430
2431
2431 if not message:
2432 if not message:
2432 ph = patchheader(q.join(parent), q.plainmode)
2433 ph = patchheader(q.join(parent), q.plainmode)
2433 message, user = ph.message, ph.user
2434 message, user = ph.message, ph.user
2434 for msg in messages:
2435 for msg in messages:
2435 message.append('* * *')
2436 message.append('* * *')
2436 message.extend(msg)
2437 message.extend(msg)
2437 message = '\n'.join(message)
2438 message = '\n'.join(message)
2438
2439
2439 if opts.get('edit'):
2440 if opts.get('edit'):
2440 message = ui.edit(message, user or ui.username())
2441 message = ui.edit(message, user or ui.username())
2441
2442
2442 diffopts = q.patchopts(q.diffopts(), *patches)
2443 diffopts = q.patchopts(q.diffopts(), *patches)
2443 wlock = repo.wlock()
2444 wlock = repo.wlock()
2444 try:
2445 try:
2445 q.refresh(repo, msg=message, git=diffopts.git)
2446 q.refresh(repo, msg=message, git=diffopts.git)
2446 q.delete(repo, patches, opts)
2447 q.delete(repo, patches, opts)
2447 q.savedirty()
2448 q.savedirty()
2448 finally:
2449 finally:
2449 wlock.release()
2450 wlock.release()
2450
2451
2451 @command("qgoto",
2452 @command("qgoto",
2452 [('f', 'force', None, _('overwrite any local changes'))],
2453 [('f', 'force', None, _('overwrite any local changes'))],
2453 _('hg qgoto [OPTION]... PATCH'))
2454 _('hg qgoto [OPTION]... PATCH'))
2454 def goto(ui, repo, patch, **opts):
2455 def goto(ui, repo, patch, **opts):
2455 '''push or pop patches until named patch is at top of stack
2456 '''push or pop patches until named patch is at top of stack
2456
2457
2457 Returns 0 on success.'''
2458 Returns 0 on success.'''
2458 q = repo.mq
2459 q = repo.mq
2459 patch = q.lookup(patch)
2460 patch = q.lookup(patch)
2460 if q.isapplied(patch):
2461 if q.isapplied(patch):
2461 ret = q.pop(repo, patch, force=opts.get('force'))
2462 ret = q.pop(repo, patch, force=opts.get('force'))
2462 else:
2463 else:
2463 ret = q.push(repo, patch, force=opts.get('force'))
2464 ret = q.push(repo, patch, force=opts.get('force'))
2464 q.savedirty()
2465 q.savedirty()
2465 return ret
2466 return ret
2466
2467
2467 @command("qguard",
2468 @command("qguard",
2468 [('l', 'list', None, _('list all patches and guards')),
2469 [('l', 'list', None, _('list all patches and guards')),
2469 ('n', 'none', None, _('drop all guards'))],
2470 ('n', 'none', None, _('drop all guards'))],
2470 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2471 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2471 def guard(ui, repo, *args, **opts):
2472 def guard(ui, repo, *args, **opts):
2472 '''set or print guards for a patch
2473 '''set or print guards for a patch
2473
2474
2474 Guards control whether a patch can be pushed. A patch with no
2475 Guards control whether a patch can be pushed. A patch with no
2475 guards is always pushed. A patch with a positive guard ("+foo") is
2476 guards is always pushed. A patch with a positive guard ("+foo") is
2476 pushed only if the :hg:`qselect` command has activated it. A patch with
2477 pushed only if the :hg:`qselect` command has activated it. A patch with
2477 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2478 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2478 has activated it.
2479 has activated it.
2479
2480
2480 With no arguments, print the currently active guards.
2481 With no arguments, print the currently active guards.
2481 With arguments, set guards for the named patch.
2482 With arguments, set guards for the named patch.
2482
2483
2483 .. note::
2484 .. note::
2484 Specifying negative guards now requires '--'.
2485 Specifying negative guards now requires '--'.
2485
2486
2486 To set guards on another patch::
2487 To set guards on another patch::
2487
2488
2488 hg qguard other.patch -- +2.6.17 -stable
2489 hg qguard other.patch -- +2.6.17 -stable
2489
2490
2490 Returns 0 on success.
2491 Returns 0 on success.
2491 '''
2492 '''
2492 def status(idx):
2493 def status(idx):
2493 guards = q.seriesguards[idx] or ['unguarded']
2494 guards = q.seriesguards[idx] or ['unguarded']
2494 if q.series[idx] in applied:
2495 if q.series[idx] in applied:
2495 state = 'applied'
2496 state = 'applied'
2496 elif q.pushable(idx)[0]:
2497 elif q.pushable(idx)[0]:
2497 state = 'unapplied'
2498 state = 'unapplied'
2498 else:
2499 else:
2499 state = 'guarded'
2500 state = 'guarded'
2500 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2501 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2501 ui.write('%s: ' % ui.label(q.series[idx], label))
2502 ui.write('%s: ' % ui.label(q.series[idx], label))
2502
2503
2503 for i, guard in enumerate(guards):
2504 for i, guard in enumerate(guards):
2504 if guard.startswith('+'):
2505 if guard.startswith('+'):
2505 ui.write(guard, label='qguard.positive')
2506 ui.write(guard, label='qguard.positive')
2506 elif guard.startswith('-'):
2507 elif guard.startswith('-'):
2507 ui.write(guard, label='qguard.negative')
2508 ui.write(guard, label='qguard.negative')
2508 else:
2509 else:
2509 ui.write(guard, label='qguard.unguarded')
2510 ui.write(guard, label='qguard.unguarded')
2510 if i != len(guards) - 1:
2511 if i != len(guards) - 1:
2511 ui.write(' ')
2512 ui.write(' ')
2512 ui.write('\n')
2513 ui.write('\n')
2513 q = repo.mq
2514 q = repo.mq
2514 applied = set(p.name for p in q.applied)
2515 applied = set(p.name for p in q.applied)
2515 patch = None
2516 patch = None
2516 args = list(args)
2517 args = list(args)
2517 if opts.get('list'):
2518 if opts.get('list'):
2518 if args or opts.get('none'):
2519 if args or opts.get('none'):
2519 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2520 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2520 for i in xrange(len(q.series)):
2521 for i in xrange(len(q.series)):
2521 status(i)
2522 status(i)
2522 return
2523 return
2523 if not args or args[0][0:1] in '-+':
2524 if not args or args[0][0:1] in '-+':
2524 if not q.applied:
2525 if not q.applied:
2525 raise util.Abort(_('no patches applied'))
2526 raise util.Abort(_('no patches applied'))
2526 patch = q.applied[-1].name
2527 patch = q.applied[-1].name
2527 if patch is None and args[0][0:1] not in '-+':
2528 if patch is None and args[0][0:1] not in '-+':
2528 patch = args.pop(0)
2529 patch = args.pop(0)
2529 if patch is None:
2530 if patch is None:
2530 raise util.Abort(_('no patch to work with'))
2531 raise util.Abort(_('no patch to work with'))
2531 if args or opts.get('none'):
2532 if args or opts.get('none'):
2532 idx = q.findseries(patch)
2533 idx = q.findseries(patch)
2533 if idx is None:
2534 if idx is None:
2534 raise util.Abort(_('no patch named %s') % patch)
2535 raise util.Abort(_('no patch named %s') % patch)
2535 q.setguards(idx, args)
2536 q.setguards(idx, args)
2536 q.savedirty()
2537 q.savedirty()
2537 else:
2538 else:
2538 status(q.series.index(q.lookup(patch)))
2539 status(q.series.index(q.lookup(patch)))
2539
2540
2540 @command("qheader", [], _('hg qheader [PATCH]'))
2541 @command("qheader", [], _('hg qheader [PATCH]'))
2541 def header(ui, repo, patch=None):
2542 def header(ui, repo, patch=None):
2542 """print the header of the topmost or specified patch
2543 """print the header of the topmost or specified patch
2543
2544
2544 Returns 0 on success."""
2545 Returns 0 on success."""
2545 q = repo.mq
2546 q = repo.mq
2546
2547
2547 if patch:
2548 if patch:
2548 patch = q.lookup(patch)
2549 patch = q.lookup(patch)
2549 else:
2550 else:
2550 if not q.applied:
2551 if not q.applied:
2551 ui.write(_('no patches applied\n'))
2552 ui.write(_('no patches applied\n'))
2552 return 1
2553 return 1
2553 patch = q.lookup('qtip')
2554 patch = q.lookup('qtip')
2554 ph = patchheader(q.join(patch), q.plainmode)
2555 ph = patchheader(q.join(patch), q.plainmode)
2555
2556
2556 ui.write('\n'.join(ph.message) + '\n')
2557 ui.write('\n'.join(ph.message) + '\n')
2557
2558
2558 def lastsavename(path):
2559 def lastsavename(path):
2559 (directory, base) = os.path.split(path)
2560 (directory, base) = os.path.split(path)
2560 names = os.listdir(directory)
2561 names = os.listdir(directory)
2561 namere = re.compile("%s.([0-9]+)" % base)
2562 namere = re.compile("%s.([0-9]+)" % base)
2562 maxindex = None
2563 maxindex = None
2563 maxname = None
2564 maxname = None
2564 for f in names:
2565 for f in names:
2565 m = namere.match(f)
2566 m = namere.match(f)
2566 if m:
2567 if m:
2567 index = int(m.group(1))
2568 index = int(m.group(1))
2568 if maxindex is None or index > maxindex:
2569 if maxindex is None or index > maxindex:
2569 maxindex = index
2570 maxindex = index
2570 maxname = f
2571 maxname = f
2571 if maxname:
2572 if maxname:
2572 return (os.path.join(directory, maxname), maxindex)
2573 return (os.path.join(directory, maxname), maxindex)
2573 return (None, None)
2574 return (None, None)
2574
2575
2575 def savename(path):
2576 def savename(path):
2576 (last, index) = lastsavename(path)
2577 (last, index) = lastsavename(path)
2577 if last is None:
2578 if last is None:
2578 index = 0
2579 index = 0
2579 newpath = path + ".%d" % (index + 1)
2580 newpath = path + ".%d" % (index + 1)
2580 return newpath
2581 return newpath
2581
2582
2582 @command("^qpush",
2583 @command("^qpush",
2583 [('f', 'force', None, _('apply on top of local changes')),
2584 [('f', 'force', None, _('apply on top of local changes')),
2584 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
2585 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
2585 ('l', 'list', None, _('list patch name in commit text')),
2586 ('l', 'list', None, _('list patch name in commit text')),
2586 ('a', 'all', None, _('apply all patches')),
2587 ('a', 'all', None, _('apply all patches')),
2587 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2588 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2588 ('n', 'name', '',
2589 ('n', 'name', '',
2589 _('merge queue name (DEPRECATED)'), _('NAME')),
2590 _('merge queue name (DEPRECATED)'), _('NAME')),
2590 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2591 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2591 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2592 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2592 def push(ui, repo, patch=None, **opts):
2593 def push(ui, repo, patch=None, **opts):
2593 """push the next patch onto the stack
2594 """push the next patch onto the stack
2594
2595
2595 When -f/--force is applied, all local changes in patched files
2596 When -f/--force is applied, all local changes in patched files
2596 will be lost.
2597 will be lost.
2597
2598
2598 Return 0 on success.
2599 Return 0 on success.
2599 """
2600 """
2600 q = repo.mq
2601 q = repo.mq
2601 mergeq = None
2602 mergeq = None
2602
2603
2603 if opts.get('merge'):
2604 if opts.get('merge'):
2604 if opts.get('name'):
2605 if opts.get('name'):
2605 newpath = repo.join(opts.get('name'))
2606 newpath = repo.join(opts.get('name'))
2606 else:
2607 else:
2607 newpath, i = lastsavename(q.path)
2608 newpath, i = lastsavename(q.path)
2608 if not newpath:
2609 if not newpath:
2609 ui.warn(_("no saved queues found, please use -n\n"))
2610 ui.warn(_("no saved queues found, please use -n\n"))
2610 return 1
2611 return 1
2611 mergeq = queue(ui, repo.path, newpath)
2612 mergeq = queue(ui, repo.path, newpath)
2612 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2613 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2613 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2614 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2614 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2615 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2615 exact=opts.get('exact'))
2616 exact=opts.get('exact'))
2616 return ret
2617 return ret
2617
2618
2618 @command("^qpop",
2619 @command("^qpop",
2619 [('a', 'all', None, _('pop all patches')),
2620 [('a', 'all', None, _('pop all patches')),
2620 ('n', 'name', '',
2621 ('n', 'name', '',
2621 _('queue name to pop (DEPRECATED)'), _('NAME')),
2622 _('queue name to pop (DEPRECATED)'), _('NAME')),
2622 ('f', 'force', None, _('forget any local changes to patched files'))],
2623 ('f', 'force', None, _('forget any local changes to patched files'))],
2623 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2624 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2624 def pop(ui, repo, patch=None, **opts):
2625 def pop(ui, repo, patch=None, **opts):
2625 """pop the current patch off the stack
2626 """pop the current patch off the stack
2626
2627
2627 By default, pops off the top of the patch stack. If given a patch
2628 By default, pops off the top of the patch stack. If given a patch
2628 name, keeps popping off patches until the named patch is at the
2629 name, keeps popping off patches until the named patch is at the
2629 top of the stack.
2630 top of the stack.
2630
2631
2631 Return 0 on success.
2632 Return 0 on success.
2632 """
2633 """
2633 localupdate = True
2634 localupdate = True
2634 if opts.get('name'):
2635 if opts.get('name'):
2635 q = queue(ui, repo.path, repo.join(opts.get('name')))
2636 q = queue(ui, repo.path, repo.join(opts.get('name')))
2636 ui.warn(_('using patch queue: %s\n') % q.path)
2637 ui.warn(_('using patch queue: %s\n') % q.path)
2637 localupdate = False
2638 localupdate = False
2638 else:
2639 else:
2639 q = repo.mq
2640 q = repo.mq
2640 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2641 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2641 all=opts.get('all'))
2642 all=opts.get('all'))
2642 q.savedirty()
2643 q.savedirty()
2643 return ret
2644 return ret
2644
2645
2645 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2646 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2646 def rename(ui, repo, patch, name=None, **opts):
2647 def rename(ui, repo, patch, name=None, **opts):
2647 """rename a patch
2648 """rename a patch
2648
2649
2649 With one argument, renames the current patch to PATCH1.
2650 With one argument, renames the current patch to PATCH1.
2650 With two arguments, renames PATCH1 to PATCH2.
2651 With two arguments, renames PATCH1 to PATCH2.
2651
2652
2652 Returns 0 on success."""
2653 Returns 0 on success."""
2653 q = repo.mq
2654 q = repo.mq
2654 if not name:
2655 if not name:
2655 name = patch
2656 name = patch
2656 patch = None
2657 patch = None
2657
2658
2658 if patch:
2659 if patch:
2659 patch = q.lookup(patch)
2660 patch = q.lookup(patch)
2660 else:
2661 else:
2661 if not q.applied:
2662 if not q.applied:
2662 ui.write(_('no patches applied\n'))
2663 ui.write(_('no patches applied\n'))
2663 return
2664 return
2664 patch = q.lookup('qtip')
2665 patch = q.lookup('qtip')
2665 absdest = q.join(name)
2666 absdest = q.join(name)
2666 if os.path.isdir(absdest):
2667 if os.path.isdir(absdest):
2667 name = normname(os.path.join(name, os.path.basename(patch)))
2668 name = normname(os.path.join(name, os.path.basename(patch)))
2668 absdest = q.join(name)
2669 absdest = q.join(name)
2669 q.checkpatchname(name)
2670 q.checkpatchname(name)
2670
2671
2671 ui.note(_('renaming %s to %s\n') % (patch, name))
2672 ui.note(_('renaming %s to %s\n') % (patch, name))
2672 i = q.findseries(patch)
2673 i = q.findseries(patch)
2673 guards = q.guard_re.findall(q.fullseries[i])
2674 guards = q.guard_re.findall(q.fullseries[i])
2674 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2675 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2675 q.parseseries()
2676 q.parseseries()
2676 q.seriesdirty = True
2677 q.seriesdirty = True
2677
2678
2678 info = q.isapplied(patch)
2679 info = q.isapplied(patch)
2679 if info:
2680 if info:
2680 q.applied[info[0]] = statusentry(info[1], name)
2681 q.applied[info[0]] = statusentry(info[1], name)
2681 q.applieddirty = True
2682 q.applieddirty = True
2682
2683
2683 destdir = os.path.dirname(absdest)
2684 destdir = os.path.dirname(absdest)
2684 if not os.path.isdir(destdir):
2685 if not os.path.isdir(destdir):
2685 os.makedirs(destdir)
2686 os.makedirs(destdir)
2686 util.rename(q.join(patch), absdest)
2687 util.rename(q.join(patch), absdest)
2687 r = q.qrepo()
2688 r = q.qrepo()
2688 if r and patch in r.dirstate:
2689 if r and patch in r.dirstate:
2689 wctx = r[None]
2690 wctx = r[None]
2690 wlock = r.wlock()
2691 wlock = r.wlock()
2691 try:
2692 try:
2692 if r.dirstate[patch] == 'a':
2693 if r.dirstate[patch] == 'a':
2693 r.dirstate.drop(patch)
2694 r.dirstate.drop(patch)
2694 r.dirstate.add(name)
2695 r.dirstate.add(name)
2695 else:
2696 else:
2696 wctx.copy(patch, name)
2697 wctx.copy(patch, name)
2697 wctx.forget([patch])
2698 wctx.forget([patch])
2698 finally:
2699 finally:
2699 wlock.release()
2700 wlock.release()
2700
2701
2701 q.savedirty()
2702 q.savedirty()
2702
2703
2703 @command("qrestore",
2704 @command("qrestore",
2704 [('d', 'delete', None, _('delete save entry')),
2705 [('d', 'delete', None, _('delete save entry')),
2705 ('u', 'update', None, _('update queue working directory'))],
2706 ('u', 'update', None, _('update queue working directory'))],
2706 _('hg qrestore [-d] [-u] REV'))
2707 _('hg qrestore [-d] [-u] REV'))
2707 def restore(ui, repo, rev, **opts):
2708 def restore(ui, repo, rev, **opts):
2708 """restore the queue state saved by a revision (DEPRECATED)
2709 """restore the queue state saved by a revision (DEPRECATED)
2709
2710
2710 This command is deprecated, use :hg:`rebase` instead."""
2711 This command is deprecated, use :hg:`rebase` instead."""
2711 rev = repo.lookup(rev)
2712 rev = repo.lookup(rev)
2712 q = repo.mq
2713 q = repo.mq
2713 q.restore(repo, rev, delete=opts.get('delete'),
2714 q.restore(repo, rev, delete=opts.get('delete'),
2714 qupdate=opts.get('update'))
2715 qupdate=opts.get('update'))
2715 q.savedirty()
2716 q.savedirty()
2716 return 0
2717 return 0
2717
2718
2718 @command("qsave",
2719 @command("qsave",
2719 [('c', 'copy', None, _('copy patch directory')),
2720 [('c', 'copy', None, _('copy patch directory')),
2720 ('n', 'name', '',
2721 ('n', 'name', '',
2721 _('copy directory name'), _('NAME')),
2722 _('copy directory name'), _('NAME')),
2722 ('e', 'empty', None, _('clear queue status file')),
2723 ('e', 'empty', None, _('clear queue status file')),
2723 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2724 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2724 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2725 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2725 def save(ui, repo, **opts):
2726 def save(ui, repo, **opts):
2726 """save current queue state (DEPRECATED)
2727 """save current queue state (DEPRECATED)
2727
2728
2728 This command is deprecated, use :hg:`rebase` instead."""
2729 This command is deprecated, use :hg:`rebase` instead."""
2729 q = repo.mq
2730 q = repo.mq
2730 message = cmdutil.logmessage(ui, opts)
2731 message = cmdutil.logmessage(ui, opts)
2731 ret = q.save(repo, msg=message)
2732 ret = q.save(repo, msg=message)
2732 if ret:
2733 if ret:
2733 return ret
2734 return ret
2734 q.savedirty() # save to .hg/patches before copying
2735 q.savedirty() # save to .hg/patches before copying
2735 if opts.get('copy'):
2736 if opts.get('copy'):
2736 path = q.path
2737 path = q.path
2737 if opts.get('name'):
2738 if opts.get('name'):
2738 newpath = os.path.join(q.basepath, opts.get('name'))
2739 newpath = os.path.join(q.basepath, opts.get('name'))
2739 if os.path.exists(newpath):
2740 if os.path.exists(newpath):
2740 if not os.path.isdir(newpath):
2741 if not os.path.isdir(newpath):
2741 raise util.Abort(_('destination %s exists and is not '
2742 raise util.Abort(_('destination %s exists and is not '
2742 'a directory') % newpath)
2743 'a directory') % newpath)
2743 if not opts.get('force'):
2744 if not opts.get('force'):
2744 raise util.Abort(_('destination %s exists, '
2745 raise util.Abort(_('destination %s exists, '
2745 'use -f to force') % newpath)
2746 'use -f to force') % newpath)
2746 else:
2747 else:
2747 newpath = savename(path)
2748 newpath = savename(path)
2748 ui.warn(_("copy %s to %s\n") % (path, newpath))
2749 ui.warn(_("copy %s to %s\n") % (path, newpath))
2749 util.copyfiles(path, newpath)
2750 util.copyfiles(path, newpath)
2750 if opts.get('empty'):
2751 if opts.get('empty'):
2751 del q.applied[:]
2752 del q.applied[:]
2752 q.applieddirty = True
2753 q.applieddirty = True
2753 q.savedirty()
2754 q.savedirty()
2754 return 0
2755 return 0
2755
2756
2756 @command("strip",
2757 @command("strip",
2757 [
2758 [
2758 ('r', 'rev', [], _('strip specified revision (optional, '
2759 ('r', 'rev', [], _('strip specified revision (optional, '
2759 'can specify revisions without this '
2760 'can specify revisions without this '
2760 'option)'), _('REV')),
2761 'option)'), _('REV')),
2761 ('f', 'force', None, _('force removal of changesets, discard '
2762 ('f', 'force', None, _('force removal of changesets, discard '
2762 'uncommitted changes (no backup)')),
2763 'uncommitted changes (no backup)')),
2763 ('b', 'backup', None, _('bundle only changesets with local revision'
2764 ('b', 'backup', None, _('bundle only changesets with local revision'
2764 ' number greater than REV which are not'
2765 ' number greater than REV which are not'
2765 ' descendants of REV (DEPRECATED)')),
2766 ' descendants of REV (DEPRECATED)')),
2766 ('n', 'no-backup', None, _('no backups')),
2767 ('n', 'no-backup', None, _('no backups')),
2767 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2768 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2768 ('k', 'keep', None, _("do not modify working copy during strip"))],
2769 ('k', 'keep', None, _("do not modify working copy during strip"))],
2769 _('hg strip [-k] [-f] [-n] REV...'))
2770 _('hg strip [-k] [-f] [-n] REV...'))
2770 def strip(ui, repo, *revs, **opts):
2771 def strip(ui, repo, *revs, **opts):
2771 """strip changesets and all their descendants from the repository
2772 """strip changesets and all their descendants from the repository
2772
2773
2773 The strip command removes the specified changesets and all their
2774 The strip command removes the specified changesets and all their
2774 descendants. If the working directory has uncommitted changes, the
2775 descendants. If the working directory has uncommitted changes, the
2775 operation is aborted unless the --force flag is supplied, in which
2776 operation is aborted unless the --force flag is supplied, in which
2776 case changes will be discarded.
2777 case changes will be discarded.
2777
2778
2778 If a parent of the working directory is stripped, then the working
2779 If a parent of the working directory is stripped, then the working
2779 directory will automatically be updated to the most recent
2780 directory will automatically be updated to the most recent
2780 available ancestor of the stripped parent after the operation
2781 available ancestor of the stripped parent after the operation
2781 completes.
2782 completes.
2782
2783
2783 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2784 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2784 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2785 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2785 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2786 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2786 where BUNDLE is the bundle file created by the strip. Note that
2787 where BUNDLE is the bundle file created by the strip. Note that
2787 the local revision numbers will in general be different after the
2788 the local revision numbers will in general be different after the
2788 restore.
2789 restore.
2789
2790
2790 Use the --no-backup option to discard the backup bundle once the
2791 Use the --no-backup option to discard the backup bundle once the
2791 operation completes.
2792 operation completes.
2792
2793
2793 Return 0 on success.
2794 Return 0 on success.
2794 """
2795 """
2795 backup = 'all'
2796 backup = 'all'
2796 if opts.get('backup'):
2797 if opts.get('backup'):
2797 backup = 'strip'
2798 backup = 'strip'
2798 elif opts.get('no_backup') or opts.get('nobackup'):
2799 elif opts.get('no_backup') or opts.get('nobackup'):
2799 backup = 'none'
2800 backup = 'none'
2800
2801
2801 cl = repo.changelog
2802 cl = repo.changelog
2802 revs = list(revs) + opts.get('rev')
2803 revs = list(revs) + opts.get('rev')
2803 revs = set(scmutil.revrange(repo, revs))
2804 revs = set(scmutil.revrange(repo, revs))
2804 if not revs:
2805 if not revs:
2805 raise util.Abort(_('empty revision set'))
2806 raise util.Abort(_('empty revision set'))
2806
2807
2807 descendants = set(cl.descendants(*revs))
2808 descendants = set(cl.descendants(*revs))
2808 strippedrevs = revs.union(descendants)
2809 strippedrevs = revs.union(descendants)
2809 roots = revs.difference(descendants)
2810 roots = revs.difference(descendants)
2810
2811
2811 update = False
2812 update = False
2812 # if one of the wdir parent is stripped we'll need
2813 # if one of the wdir parent is stripped we'll need
2813 # to update away to an earlier revision
2814 # to update away to an earlier revision
2814 for p in repo.dirstate.parents():
2815 for p in repo.dirstate.parents():
2815 if p != nullid and cl.rev(p) in strippedrevs:
2816 if p != nullid and cl.rev(p) in strippedrevs:
2816 update = True
2817 update = True
2817 break
2818 break
2818
2819
2819 rootnodes = set(cl.node(r) for r in roots)
2820 rootnodes = set(cl.node(r) for r in roots)
2820
2821
2821 q = repo.mq
2822 q = repo.mq
2822 if q.applied:
2823 if q.applied:
2823 # refresh queue state if we're about to strip
2824 # refresh queue state if we're about to strip
2824 # applied patches
2825 # applied patches
2825 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2826 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2826 q.applieddirty = True
2827 q.applieddirty = True
2827 start = 0
2828 start = 0
2828 end = len(q.applied)
2829 end = len(q.applied)
2829 for i, statusentry in enumerate(q.applied):
2830 for i, statusentry in enumerate(q.applied):
2830 if statusentry.node in rootnodes:
2831 if statusentry.node in rootnodes:
2831 # if one of the stripped roots is an applied
2832 # if one of the stripped roots is an applied
2832 # patch, only part of the queue is stripped
2833 # patch, only part of the queue is stripped
2833 start = i
2834 start = i
2834 break
2835 break
2835 del q.applied[start:end]
2836 del q.applied[start:end]
2836 q.savedirty()
2837 q.savedirty()
2837
2838
2838 revs = list(rootnodes)
2839 revs = list(rootnodes)
2839 if update and opts.get('keep'):
2840 if update and opts.get('keep'):
2840 wlock = repo.wlock()
2841 wlock = repo.wlock()
2841 try:
2842 try:
2842 urev = repo.mq.qparents(repo, revs[0])
2843 urev = repo.mq.qparents(repo, revs[0])
2843 repo.dirstate.rebuild(urev, repo[urev].manifest())
2844 repo.dirstate.rebuild(urev, repo[urev].manifest())
2844 repo.dirstate.write()
2845 repo.dirstate.write()
2845 update = False
2846 update = False
2846 finally:
2847 finally:
2847 wlock.release()
2848 wlock.release()
2848
2849
2849 repo.mq.strip(repo, revs, backup=backup, update=update,
2850 repo.mq.strip(repo, revs, backup=backup, update=update,
2850 force=opts.get('force'))
2851 force=opts.get('force'))
2851 return 0
2852 return 0
2852
2853
2853 @command("qselect",
2854 @command("qselect",
2854 [('n', 'none', None, _('disable all guards')),
2855 [('n', 'none', None, _('disable all guards')),
2855 ('s', 'series', None, _('list all guards in series file')),
2856 ('s', 'series', None, _('list all guards in series file')),
2856 ('', 'pop', None, _('pop to before first guarded applied patch')),
2857 ('', 'pop', None, _('pop to before first guarded applied patch')),
2857 ('', 'reapply', None, _('pop, then reapply patches'))],
2858 ('', 'reapply', None, _('pop, then reapply patches'))],
2858 _('hg qselect [OPTION]... [GUARD]...'))
2859 _('hg qselect [OPTION]... [GUARD]...'))
2859 def select(ui, repo, *args, **opts):
2860 def select(ui, repo, *args, **opts):
2860 '''set or print guarded patches to push
2861 '''set or print guarded patches to push
2861
2862
2862 Use the :hg:`qguard` command to set or print guards on patch, then use
2863 Use the :hg:`qguard` command to set or print guards on patch, then use
2863 qselect to tell mq which guards to use. A patch will be pushed if
2864 qselect to tell mq which guards to use. A patch will be pushed if
2864 it has no guards or any positive guards match the currently
2865 it has no guards or any positive guards match the currently
2865 selected guard, but will not be pushed if any negative guards
2866 selected guard, but will not be pushed if any negative guards
2866 match the current guard. For example::
2867 match the current guard. For example::
2867
2868
2868 qguard foo.patch -- -stable (negative guard)
2869 qguard foo.patch -- -stable (negative guard)
2869 qguard bar.patch +stable (positive guard)
2870 qguard bar.patch +stable (positive guard)
2870 qselect stable
2871 qselect stable
2871
2872
2872 This activates the "stable" guard. mq will skip foo.patch (because
2873 This activates the "stable" guard. mq will skip foo.patch (because
2873 it has a negative match) but push bar.patch (because it has a
2874 it has a negative match) but push bar.patch (because it has a
2874 positive match).
2875 positive match).
2875
2876
2876 With no arguments, prints the currently active guards.
2877 With no arguments, prints the currently active guards.
2877 With one argument, sets the active guard.
2878 With one argument, sets the active guard.
2878
2879
2879 Use -n/--none to deactivate guards (no other arguments needed).
2880 Use -n/--none to deactivate guards (no other arguments needed).
2880 When no guards are active, patches with positive guards are
2881 When no guards are active, patches with positive guards are
2881 skipped and patches with negative guards are pushed.
2882 skipped and patches with negative guards are pushed.
2882
2883
2883 qselect can change the guards on applied patches. It does not pop
2884 qselect can change the guards on applied patches. It does not pop
2884 guarded patches by default. Use --pop to pop back to the last
2885 guarded patches by default. Use --pop to pop back to the last
2885 applied patch that is not guarded. Use --reapply (which implies
2886 applied patch that is not guarded. Use --reapply (which implies
2886 --pop) to push back to the current patch afterwards, but skip
2887 --pop) to push back to the current patch afterwards, but skip
2887 guarded patches.
2888 guarded patches.
2888
2889
2889 Use -s/--series to print a list of all guards in the series file
2890 Use -s/--series to print a list of all guards in the series file
2890 (no other arguments needed). Use -v for more information.
2891 (no other arguments needed). Use -v for more information.
2891
2892
2892 Returns 0 on success.'''
2893 Returns 0 on success.'''
2893
2894
2894 q = repo.mq
2895 q = repo.mq
2895 guards = q.active()
2896 guards = q.active()
2896 if args or opts.get('none'):
2897 if args or opts.get('none'):
2897 old_unapplied = q.unapplied(repo)
2898 old_unapplied = q.unapplied(repo)
2898 old_guarded = [i for i in xrange(len(q.applied)) if
2899 old_guarded = [i for i in xrange(len(q.applied)) if
2899 not q.pushable(i)[0]]
2900 not q.pushable(i)[0]]
2900 q.setactive(args)
2901 q.setactive(args)
2901 q.savedirty()
2902 q.savedirty()
2902 if not args:
2903 if not args:
2903 ui.status(_('guards deactivated\n'))
2904 ui.status(_('guards deactivated\n'))
2904 if not opts.get('pop') and not opts.get('reapply'):
2905 if not opts.get('pop') and not opts.get('reapply'):
2905 unapplied = q.unapplied(repo)
2906 unapplied = q.unapplied(repo)
2906 guarded = [i for i in xrange(len(q.applied))
2907 guarded = [i for i in xrange(len(q.applied))
2907 if not q.pushable(i)[0]]
2908 if not q.pushable(i)[0]]
2908 if len(unapplied) != len(old_unapplied):
2909 if len(unapplied) != len(old_unapplied):
2909 ui.status(_('number of unguarded, unapplied patches has '
2910 ui.status(_('number of unguarded, unapplied patches has '
2910 'changed from %d to %d\n') %
2911 'changed from %d to %d\n') %
2911 (len(old_unapplied), len(unapplied)))
2912 (len(old_unapplied), len(unapplied)))
2912 if len(guarded) != len(old_guarded):
2913 if len(guarded) != len(old_guarded):
2913 ui.status(_('number of guarded, applied patches has changed '
2914 ui.status(_('number of guarded, applied patches has changed '
2914 'from %d to %d\n') %
2915 'from %d to %d\n') %
2915 (len(old_guarded), len(guarded)))
2916 (len(old_guarded), len(guarded)))
2916 elif opts.get('series'):
2917 elif opts.get('series'):
2917 guards = {}
2918 guards = {}
2918 noguards = 0
2919 noguards = 0
2919 for gs in q.seriesguards:
2920 for gs in q.seriesguards:
2920 if not gs:
2921 if not gs:
2921 noguards += 1
2922 noguards += 1
2922 for g in gs:
2923 for g in gs:
2923 guards.setdefault(g, 0)
2924 guards.setdefault(g, 0)
2924 guards[g] += 1
2925 guards[g] += 1
2925 if ui.verbose:
2926 if ui.verbose:
2926 guards['NONE'] = noguards
2927 guards['NONE'] = noguards
2927 guards = guards.items()
2928 guards = guards.items()
2928 guards.sort(key=lambda x: x[0][1:])
2929 guards.sort(key=lambda x: x[0][1:])
2929 if guards:
2930 if guards:
2930 ui.note(_('guards in series file:\n'))
2931 ui.note(_('guards in series file:\n'))
2931 for guard, count in guards:
2932 for guard, count in guards:
2932 ui.note('%2d ' % count)
2933 ui.note('%2d ' % count)
2933 ui.write(guard, '\n')
2934 ui.write(guard, '\n')
2934 else:
2935 else:
2935 ui.note(_('no guards in series file\n'))
2936 ui.note(_('no guards in series file\n'))
2936 else:
2937 else:
2937 if guards:
2938 if guards:
2938 ui.note(_('active guards:\n'))
2939 ui.note(_('active guards:\n'))
2939 for g in guards:
2940 for g in guards:
2940 ui.write(g, '\n')
2941 ui.write(g, '\n')
2941 else:
2942 else:
2942 ui.write(_('no active guards\n'))
2943 ui.write(_('no active guards\n'))
2943 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2944 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2944 popped = False
2945 popped = False
2945 if opts.get('pop') or opts.get('reapply'):
2946 if opts.get('pop') or opts.get('reapply'):
2946 for i in xrange(len(q.applied)):
2947 for i in xrange(len(q.applied)):
2947 pushable, reason = q.pushable(i)
2948 pushable, reason = q.pushable(i)
2948 if not pushable:
2949 if not pushable:
2949 ui.status(_('popping guarded patches\n'))
2950 ui.status(_('popping guarded patches\n'))
2950 popped = True
2951 popped = True
2951 if i == 0:
2952 if i == 0:
2952 q.pop(repo, all=True)
2953 q.pop(repo, all=True)
2953 else:
2954 else:
2954 q.pop(repo, str(i - 1))
2955 q.pop(repo, str(i - 1))
2955 break
2956 break
2956 if popped:
2957 if popped:
2957 try:
2958 try:
2958 if reapply:
2959 if reapply:
2959 ui.status(_('reapplying unguarded patches\n'))
2960 ui.status(_('reapplying unguarded patches\n'))
2960 q.push(repo, reapply)
2961 q.push(repo, reapply)
2961 finally:
2962 finally:
2962 q.savedirty()
2963 q.savedirty()
2963
2964
2964 @command("qfinish",
2965 @command("qfinish",
2965 [('a', 'applied', None, _('finish all applied changesets'))],
2966 [('a', 'applied', None, _('finish all applied changesets'))],
2966 _('hg qfinish [-a] [REV]...'))
2967 _('hg qfinish [-a] [REV]...'))
2967 def finish(ui, repo, *revrange, **opts):
2968 def finish(ui, repo, *revrange, **opts):
2968 """move applied patches into repository history
2969 """move applied patches into repository history
2969
2970
2970 Finishes the specified revisions (corresponding to applied
2971 Finishes the specified revisions (corresponding to applied
2971 patches) by moving them out of mq control into regular repository
2972 patches) by moving them out of mq control into regular repository
2972 history.
2973 history.
2973
2974
2974 Accepts a revision range or the -a/--applied option. If --applied
2975 Accepts a revision range or the -a/--applied option. If --applied
2975 is specified, all applied mq revisions are removed from mq
2976 is specified, all applied mq revisions are removed from mq
2976 control. Otherwise, the given revisions must be at the base of the
2977 control. Otherwise, the given revisions must be at the base of the
2977 stack of applied patches.
2978 stack of applied patches.
2978
2979
2979 This can be especially useful if your changes have been applied to
2980 This can be especially useful if your changes have been applied to
2980 an upstream repository, or if you are about to push your changes
2981 an upstream repository, or if you are about to push your changes
2981 to upstream.
2982 to upstream.
2982
2983
2983 Returns 0 on success.
2984 Returns 0 on success.
2984 """
2985 """
2985 if not opts.get('applied') and not revrange:
2986 if not opts.get('applied') and not revrange:
2986 raise util.Abort(_('no revisions specified'))
2987 raise util.Abort(_('no revisions specified'))
2987 elif opts.get('applied'):
2988 elif opts.get('applied'):
2988 revrange = ('qbase::qtip',) + revrange
2989 revrange = ('qbase::qtip',) + revrange
2989
2990
2990 q = repo.mq
2991 q = repo.mq
2991 if not q.applied:
2992 if not q.applied:
2992 ui.status(_('no patches applied\n'))
2993 ui.status(_('no patches applied\n'))
2993 return 0
2994 return 0
2994
2995
2995 revs = scmutil.revrange(repo, revrange)
2996 revs = scmutil.revrange(repo, revrange)
2996 if repo['.'].rev() in revs and repo[None].files():
2997 if repo['.'].rev() in revs and repo[None].files():
2997 ui.warn(_('warning: uncommitted changes in the working directory\n'))
2998 ui.warn(_('warning: uncommitted changes in the working directory\n'))
2998 # queue.finish may changes phases but leave the responsability to lock the
2999 # queue.finish may changes phases but leave the responsability to lock the
2999 # repo to the caller to avoid deadlock with wlock. This command code is
3000 # repo to the caller to avoid deadlock with wlock. This command code is
3000 # responsability for this locking.
3001 # responsability for this locking.
3001 lock = repo.lock()
3002 lock = repo.lock()
3002 try:
3003 try:
3003 q.finish(repo, revs)
3004 q.finish(repo, revs)
3004 q.savedirty()
3005 q.savedirty()
3005 finally:
3006 finally:
3006 lock.release()
3007 lock.release()
3007 return 0
3008 return 0
3008
3009
3009 @command("qqueue",
3010 @command("qqueue",
3010 [('l', 'list', False, _('list all available queues')),
3011 [('l', 'list', False, _('list all available queues')),
3011 ('', 'active', False, _('print name of active queue')),
3012 ('', 'active', False, _('print name of active queue')),
3012 ('c', 'create', False, _('create new queue')),
3013 ('c', 'create', False, _('create new queue')),
3013 ('', 'rename', False, _('rename active queue')),
3014 ('', 'rename', False, _('rename active queue')),
3014 ('', 'delete', False, _('delete reference to queue')),
3015 ('', 'delete', False, _('delete reference to queue')),
3015 ('', 'purge', False, _('delete queue, and remove patch dir')),
3016 ('', 'purge', False, _('delete queue, and remove patch dir')),
3016 ],
3017 ],
3017 _('[OPTION] [QUEUE]'))
3018 _('[OPTION] [QUEUE]'))
3018 def qqueue(ui, repo, name=None, **opts):
3019 def qqueue(ui, repo, name=None, **opts):
3019 '''manage multiple patch queues
3020 '''manage multiple patch queues
3020
3021
3021 Supports switching between different patch queues, as well as creating
3022 Supports switching between different patch queues, as well as creating
3022 new patch queues and deleting existing ones.
3023 new patch queues and deleting existing ones.
3023
3024
3024 Omitting a queue name or specifying -l/--list will show you the registered
3025 Omitting a queue name or specifying -l/--list will show you the registered
3025 queues - by default the "normal" patches queue is registered. The currently
3026 queues - by default the "normal" patches queue is registered. The currently
3026 active queue will be marked with "(active)". Specifying --active will print
3027 active queue will be marked with "(active)". Specifying --active will print
3027 only the name of the active queue.
3028 only the name of the active queue.
3028
3029
3029 To create a new queue, use -c/--create. The queue is automatically made
3030 To create a new queue, use -c/--create. The queue is automatically made
3030 active, except in the case where there are applied patches from the
3031 active, except in the case where there are applied patches from the
3031 currently active queue in the repository. Then the queue will only be
3032 currently active queue in the repository. Then the queue will only be
3032 created and switching will fail.
3033 created and switching will fail.
3033
3034
3034 To delete an existing queue, use --delete. You cannot delete the currently
3035 To delete an existing queue, use --delete. You cannot delete the currently
3035 active queue.
3036 active queue.
3036
3037
3037 Returns 0 on success.
3038 Returns 0 on success.
3038 '''
3039 '''
3039 q = repo.mq
3040 q = repo.mq
3040 _defaultqueue = 'patches'
3041 _defaultqueue = 'patches'
3041 _allqueues = 'patches.queues'
3042 _allqueues = 'patches.queues'
3042 _activequeue = 'patches.queue'
3043 _activequeue = 'patches.queue'
3043
3044
3044 def _getcurrent():
3045 def _getcurrent():
3045 cur = os.path.basename(q.path)
3046 cur = os.path.basename(q.path)
3046 if cur.startswith('patches-'):
3047 if cur.startswith('patches-'):
3047 cur = cur[8:]
3048 cur = cur[8:]
3048 return cur
3049 return cur
3049
3050
3050 def _noqueues():
3051 def _noqueues():
3051 try:
3052 try:
3052 fh = repo.opener(_allqueues, 'r')
3053 fh = repo.opener(_allqueues, 'r')
3053 fh.close()
3054 fh.close()
3054 except IOError:
3055 except IOError:
3055 return True
3056 return True
3056
3057
3057 return False
3058 return False
3058
3059
3059 def _getqueues():
3060 def _getqueues():
3060 current = _getcurrent()
3061 current = _getcurrent()
3061
3062
3062 try:
3063 try:
3063 fh = repo.opener(_allqueues, 'r')
3064 fh = repo.opener(_allqueues, 'r')
3064 queues = [queue.strip() for queue in fh if queue.strip()]
3065 queues = [queue.strip() for queue in fh if queue.strip()]
3065 fh.close()
3066 fh.close()
3066 if current not in queues:
3067 if current not in queues:
3067 queues.append(current)
3068 queues.append(current)
3068 except IOError:
3069 except IOError:
3069 queues = [_defaultqueue]
3070 queues = [_defaultqueue]
3070
3071
3071 return sorted(queues)
3072 return sorted(queues)
3072
3073
3073 def _setactive(name):
3074 def _setactive(name):
3074 if q.applied:
3075 if q.applied:
3075 raise util.Abort(_('patches applied - cannot set new queue active'))
3076 raise util.Abort(_('patches applied - cannot set new queue active'))
3076 _setactivenocheck(name)
3077 _setactivenocheck(name)
3077
3078
3078 def _setactivenocheck(name):
3079 def _setactivenocheck(name):
3079 fh = repo.opener(_activequeue, 'w')
3080 fh = repo.opener(_activequeue, 'w')
3080 if name != 'patches':
3081 if name != 'patches':
3081 fh.write(name)
3082 fh.write(name)
3082 fh.close()
3083 fh.close()
3083
3084
3084 def _addqueue(name):
3085 def _addqueue(name):
3085 fh = repo.opener(_allqueues, 'a')
3086 fh = repo.opener(_allqueues, 'a')
3086 fh.write('%s\n' % (name,))
3087 fh.write('%s\n' % (name,))
3087 fh.close()
3088 fh.close()
3088
3089
3089 def _queuedir(name):
3090 def _queuedir(name):
3090 if name == 'patches':
3091 if name == 'patches':
3091 return repo.join('patches')
3092 return repo.join('patches')
3092 else:
3093 else:
3093 return repo.join('patches-' + name)
3094 return repo.join('patches-' + name)
3094
3095
3095 def _validname(name):
3096 def _validname(name):
3096 for n in name:
3097 for n in name:
3097 if n in ':\\/.':
3098 if n in ':\\/.':
3098 return False
3099 return False
3099 return True
3100 return True
3100
3101
3101 def _delete(name):
3102 def _delete(name):
3102 if name not in existing:
3103 if name not in existing:
3103 raise util.Abort(_('cannot delete queue that does not exist'))
3104 raise util.Abort(_('cannot delete queue that does not exist'))
3104
3105
3105 current = _getcurrent()
3106 current = _getcurrent()
3106
3107
3107 if name == current:
3108 if name == current:
3108 raise util.Abort(_('cannot delete currently active queue'))
3109 raise util.Abort(_('cannot delete currently active queue'))
3109
3110
3110 fh = repo.opener('patches.queues.new', 'w')
3111 fh = repo.opener('patches.queues.new', 'w')
3111 for queue in existing:
3112 for queue in existing:
3112 if queue == name:
3113 if queue == name:
3113 continue
3114 continue
3114 fh.write('%s\n' % (queue,))
3115 fh.write('%s\n' % (queue,))
3115 fh.close()
3116 fh.close()
3116 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3117 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3117
3118
3118 if not name or opts.get('list') or opts.get('active'):
3119 if not name or opts.get('list') or opts.get('active'):
3119 current = _getcurrent()
3120 current = _getcurrent()
3120 if opts.get('active'):
3121 if opts.get('active'):
3121 ui.write('%s\n' % (current,))
3122 ui.write('%s\n' % (current,))
3122 return
3123 return
3123 for queue in _getqueues():
3124 for queue in _getqueues():
3124 ui.write('%s' % (queue,))
3125 ui.write('%s' % (queue,))
3125 if queue == current and not ui.quiet:
3126 if queue == current and not ui.quiet:
3126 ui.write(_(' (active)\n'))
3127 ui.write(_(' (active)\n'))
3127 else:
3128 else:
3128 ui.write('\n')
3129 ui.write('\n')
3129 return
3130 return
3130
3131
3131 if not _validname(name):
3132 if not _validname(name):
3132 raise util.Abort(
3133 raise util.Abort(
3133 _('invalid queue name, may not contain the characters ":\\/."'))
3134 _('invalid queue name, may not contain the characters ":\\/."'))
3134
3135
3135 existing = _getqueues()
3136 existing = _getqueues()
3136
3137
3137 if opts.get('create'):
3138 if opts.get('create'):
3138 if name in existing:
3139 if name in existing:
3139 raise util.Abort(_('queue "%s" already exists') % name)
3140 raise util.Abort(_('queue "%s" already exists') % name)
3140 if _noqueues():
3141 if _noqueues():
3141 _addqueue(_defaultqueue)
3142 _addqueue(_defaultqueue)
3142 _addqueue(name)
3143 _addqueue(name)
3143 _setactive(name)
3144 _setactive(name)
3144 elif opts.get('rename'):
3145 elif opts.get('rename'):
3145 current = _getcurrent()
3146 current = _getcurrent()
3146 if name == current:
3147 if name == current:
3147 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3148 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3148 if name in existing:
3149 if name in existing:
3149 raise util.Abort(_('queue "%s" already exists') % name)
3150 raise util.Abort(_('queue "%s" already exists') % name)
3150
3151
3151 olddir = _queuedir(current)
3152 olddir = _queuedir(current)
3152 newdir = _queuedir(name)
3153 newdir = _queuedir(name)
3153
3154
3154 if os.path.exists(newdir):
3155 if os.path.exists(newdir):
3155 raise util.Abort(_('non-queue directory "%s" already exists') %
3156 raise util.Abort(_('non-queue directory "%s" already exists') %
3156 newdir)
3157 newdir)
3157
3158
3158 fh = repo.opener('patches.queues.new', 'w')
3159 fh = repo.opener('patches.queues.new', 'w')
3159 for queue in existing:
3160 for queue in existing:
3160 if queue == current:
3161 if queue == current:
3161 fh.write('%s\n' % (name,))
3162 fh.write('%s\n' % (name,))
3162 if os.path.exists(olddir):
3163 if os.path.exists(olddir):
3163 util.rename(olddir, newdir)
3164 util.rename(olddir, newdir)
3164 else:
3165 else:
3165 fh.write('%s\n' % (queue,))
3166 fh.write('%s\n' % (queue,))
3166 fh.close()
3167 fh.close()
3167 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3168 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3168 _setactivenocheck(name)
3169 _setactivenocheck(name)
3169 elif opts.get('delete'):
3170 elif opts.get('delete'):
3170 _delete(name)
3171 _delete(name)
3171 elif opts.get('purge'):
3172 elif opts.get('purge'):
3172 if name in existing:
3173 if name in existing:
3173 _delete(name)
3174 _delete(name)
3174 qdir = _queuedir(name)
3175 qdir = _queuedir(name)
3175 if os.path.exists(qdir):
3176 if os.path.exists(qdir):
3176 shutil.rmtree(qdir)
3177 shutil.rmtree(qdir)
3177 else:
3178 else:
3178 if name not in existing:
3179 if name not in existing:
3179 raise util.Abort(_('use --create to create a new queue'))
3180 raise util.Abort(_('use --create to create a new queue'))
3180 _setactive(name)
3181 _setactive(name)
3181
3182
3182 def mqphasedefaults(repo, roots):
3183 def mqphasedefaults(repo, roots):
3183 """callback used to set mq changeset as secret when no phase data exists"""
3184 """callback used to set mq changeset as secret when no phase data exists"""
3184 if repo.mq.applied:
3185 if repo.mq.applied:
3185 if repo.ui.configbool('mq', 'secret', False):
3186 if repo.ui.configbool('mq', 'secret', False):
3186 mqphase = phases.secret
3187 mqphase = phases.secret
3187 else:
3188 else:
3188 mqphase = phases.draft
3189 mqphase = phases.draft
3189 qbase = repo[repo.mq.applied[0].node]
3190 qbase = repo[repo.mq.applied[0].node]
3190 roots[mqphase].add(qbase.node())
3191 roots[mqphase].add(qbase.node())
3191 return roots
3192 return roots
3192
3193
3193 def reposetup(ui, repo):
3194 def reposetup(ui, repo):
3194 class mqrepo(repo.__class__):
3195 class mqrepo(repo.__class__):
3195 @util.propertycache
3196 @util.propertycache
3196 def mq(self):
3197 def mq(self):
3197 return queue(self.ui, self.path)
3198 return queue(self.ui, self.path)
3198
3199
3199 def abortifwdirpatched(self, errmsg, force=False):
3200 def abortifwdirpatched(self, errmsg, force=False):
3200 if self.mq.applied and not force:
3201 if self.mq.applied and not force:
3201 parents = self.dirstate.parents()
3202 parents = self.dirstate.parents()
3202 patches = [s.node for s in self.mq.applied]
3203 patches = [s.node for s in self.mq.applied]
3203 if parents[0] in patches or parents[1] in patches:
3204 if parents[0] in patches or parents[1] in patches:
3204 raise util.Abort(errmsg)
3205 raise util.Abort(errmsg)
3205
3206
3206 def commit(self, text="", user=None, date=None, match=None,
3207 def commit(self, text="", user=None, date=None, match=None,
3207 force=False, editor=False, extra={}):
3208 force=False, editor=False, extra={}):
3208 self.abortifwdirpatched(
3209 self.abortifwdirpatched(
3209 _('cannot commit over an applied mq patch'),
3210 _('cannot commit over an applied mq patch'),
3210 force)
3211 force)
3211
3212
3212 return super(mqrepo, self).commit(text, user, date, match, force,
3213 return super(mqrepo, self).commit(text, user, date, match, force,
3213 editor, extra)
3214 editor, extra)
3214
3215
3215 def checkpush(self, force, revs):
3216 def checkpush(self, force, revs):
3216 if self.mq.applied and not force:
3217 if self.mq.applied and not force:
3217 outapplied = [e.node for e in self.mq.applied]
3218 outapplied = [e.node for e in self.mq.applied]
3218 if revs:
3219 if revs:
3219 # Assume applied patches have no non-patch descendants and
3220 # Assume applied patches have no non-patch descendants and
3220 # are not on remote already. Filtering any changeset not
3221 # are not on remote already. Filtering any changeset not
3221 # pushed.
3222 # pushed.
3222 heads = set(revs)
3223 heads = set(revs)
3223 for node in reversed(outapplied):
3224 for node in reversed(outapplied):
3224 if node in heads:
3225 if node in heads:
3225 break
3226 break
3226 else:
3227 else:
3227 outapplied.pop()
3228 outapplied.pop()
3228 # looking for pushed and shared changeset
3229 # looking for pushed and shared changeset
3229 for node in outapplied:
3230 for node in outapplied:
3230 if repo[node].phase() < phases.secret:
3231 if repo[node].phase() < phases.secret:
3231 raise util.Abort(_('source has mq patches applied'))
3232 raise util.Abort(_('source has mq patches applied'))
3232 # no non-secret patches pushed
3233 # no non-secret patches pushed
3233 super(mqrepo, self).checkpush(force, revs)
3234 super(mqrepo, self).checkpush(force, revs)
3234
3235
3235 def _findtags(self):
3236 def _findtags(self):
3236 '''augment tags from base class with patch tags'''
3237 '''augment tags from base class with patch tags'''
3237 result = super(mqrepo, self)._findtags()
3238 result = super(mqrepo, self)._findtags()
3238
3239
3239 q = self.mq
3240 q = self.mq
3240 if not q.applied:
3241 if not q.applied:
3241 return result
3242 return result
3242
3243
3243 mqtags = [(patch.node, patch.name) for patch in q.applied]
3244 mqtags = [(patch.node, patch.name) for patch in q.applied]
3244
3245
3245 try:
3246 try:
3246 self.changelog.rev(mqtags[-1][0])
3247 self.changelog.rev(mqtags[-1][0])
3247 except error.LookupError:
3248 except error.LookupError:
3248 self.ui.warn(_('mq status file refers to unknown node %s\n')
3249 self.ui.warn(_('mq status file refers to unknown node %s\n')
3249 % short(mqtags[-1][0]))
3250 % short(mqtags[-1][0]))
3250 return result
3251 return result
3251
3252
3252 mqtags.append((mqtags[-1][0], 'qtip'))
3253 mqtags.append((mqtags[-1][0], 'qtip'))
3253 mqtags.append((mqtags[0][0], 'qbase'))
3254 mqtags.append((mqtags[0][0], 'qbase'))
3254 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3255 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3255 tags = result[0]
3256 tags = result[0]
3256 for patch in mqtags:
3257 for patch in mqtags:
3257 if patch[1] in tags:
3258 if patch[1] in tags:
3258 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3259 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
3259 % patch[1])
3260 % patch[1])
3260 else:
3261 else:
3261 tags[patch[1]] = patch[0]
3262 tags[patch[1]] = patch[0]
3262
3263
3263 return result
3264 return result
3264
3265
3265 def _branchtags(self, partial, lrev):
3266 def _branchtags(self, partial, lrev):
3266 q = self.mq
3267 q = self.mq
3267 cl = self.changelog
3268 cl = self.changelog
3268 qbase = None
3269 qbase = None
3269 if not q.applied:
3270 if not q.applied:
3270 if getattr(self, '_committingpatch', False):
3271 if getattr(self, '_committingpatch', False):
3271 # Committing a new patch, must be tip
3272 # Committing a new patch, must be tip
3272 qbase = len(cl) - 1
3273 qbase = len(cl) - 1
3273 else:
3274 else:
3274 qbasenode = q.applied[0].node
3275 qbasenode = q.applied[0].node
3275 try:
3276 try:
3276 qbase = cl.rev(qbasenode)
3277 qbase = cl.rev(qbasenode)
3277 except error.LookupError:
3278 except error.LookupError:
3278 self.ui.warn(_('mq status file refers to unknown node %s\n')
3279 self.ui.warn(_('mq status file refers to unknown node %s\n')
3279 % short(qbasenode))
3280 % short(qbasenode))
3280 if qbase is None:
3281 if qbase is None:
3281 return super(mqrepo, self)._branchtags(partial, lrev)
3282 return super(mqrepo, self)._branchtags(partial, lrev)
3282
3283
3283 start = lrev + 1
3284 start = lrev + 1
3284 if start < qbase:
3285 if start < qbase:
3285 # update the cache (excluding the patches) and save it
3286 # update the cache (excluding the patches) and save it
3286 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3287 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3287 self._updatebranchcache(partial, ctxgen)
3288 self._updatebranchcache(partial, ctxgen)
3288 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3289 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3289 start = qbase
3290 start = qbase
3290 # if start = qbase, the cache is as updated as it should be.
3291 # if start = qbase, the cache is as updated as it should be.
3291 # if start > qbase, the cache includes (part of) the patches.
3292 # if start > qbase, the cache includes (part of) the patches.
3292 # we might as well use it, but we won't save it.
3293 # we might as well use it, but we won't save it.
3293
3294
3294 # update the cache up to the tip
3295 # update the cache up to the tip
3295 ctxgen = (self[r] for r in xrange(start, len(cl)))
3296 ctxgen = (self[r] for r in xrange(start, len(cl)))
3296 self._updatebranchcache(partial, ctxgen)
3297 self._updatebranchcache(partial, ctxgen)
3297
3298
3298 return partial
3299 return partial
3299
3300
3300 if repo.local():
3301 if repo.local():
3301 repo.__class__ = mqrepo
3302 repo.__class__ = mqrepo
3302
3303
3303 repo._phasedefaults.append(mqphasedefaults)
3304 repo._phasedefaults.append(mqphasedefaults)
3304
3305
3305 def mqimport(orig, ui, repo, *args, **kwargs):
3306 def mqimport(orig, ui, repo, *args, **kwargs):
3306 if (hasattr(repo, 'abortifwdirpatched')
3307 if (hasattr(repo, 'abortifwdirpatched')
3307 and not kwargs.get('no_commit', False)):
3308 and not kwargs.get('no_commit', False)):
3308 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3309 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3309 kwargs.get('force'))
3310 kwargs.get('force'))
3310 return orig(ui, repo, *args, **kwargs)
3311 return orig(ui, repo, *args, **kwargs)
3311
3312
3312 def mqinit(orig, ui, *args, **kwargs):
3313 def mqinit(orig, ui, *args, **kwargs):
3313 mq = kwargs.pop('mq', None)
3314 mq = kwargs.pop('mq', None)
3314
3315
3315 if not mq:
3316 if not mq:
3316 return orig(ui, *args, **kwargs)
3317 return orig(ui, *args, **kwargs)
3317
3318
3318 if args:
3319 if args:
3319 repopath = args[0]
3320 repopath = args[0]
3320 if not hg.islocal(repopath):
3321 if not hg.islocal(repopath):
3321 raise util.Abort(_('only a local queue repository '
3322 raise util.Abort(_('only a local queue repository '
3322 'may be initialized'))
3323 'may be initialized'))
3323 else:
3324 else:
3324 repopath = cmdutil.findrepo(os.getcwd())
3325 repopath = cmdutil.findrepo(os.getcwd())
3325 if not repopath:
3326 if not repopath:
3326 raise util.Abort(_('there is no Mercurial repository here '
3327 raise util.Abort(_('there is no Mercurial repository here '
3327 '(.hg not found)'))
3328 '(.hg not found)'))
3328 repo = hg.repository(ui, repopath)
3329 repo = hg.repository(ui, repopath)
3329 return qinit(ui, repo, True)
3330 return qinit(ui, repo, True)
3330
3331
3331 def mqcommand(orig, ui, repo, *args, **kwargs):
3332 def mqcommand(orig, ui, repo, *args, **kwargs):
3332 """Add --mq option to operate on patch repository instead of main"""
3333 """Add --mq option to operate on patch repository instead of main"""
3333
3334
3334 # some commands do not like getting unknown options
3335 # some commands do not like getting unknown options
3335 mq = kwargs.pop('mq', None)
3336 mq = kwargs.pop('mq', None)
3336
3337
3337 if not mq:
3338 if not mq:
3338 return orig(ui, repo, *args, **kwargs)
3339 return orig(ui, repo, *args, **kwargs)
3339
3340
3340 q = repo.mq
3341 q = repo.mq
3341 r = q.qrepo()
3342 r = q.qrepo()
3342 if not r:
3343 if not r:
3343 raise util.Abort(_('no queue repository'))
3344 raise util.Abort(_('no queue repository'))
3344 return orig(r.ui, r, *args, **kwargs)
3345 return orig(r.ui, r, *args, **kwargs)
3345
3346
3346 def summary(orig, ui, repo, *args, **kwargs):
3347 def summary(orig, ui, repo, *args, **kwargs):
3347 r = orig(ui, repo, *args, **kwargs)
3348 r = orig(ui, repo, *args, **kwargs)
3348 q = repo.mq
3349 q = repo.mq
3349 m = []
3350 m = []
3350 a, u = len(q.applied), len(q.unapplied(repo))
3351 a, u = len(q.applied), len(q.unapplied(repo))
3351 if a:
3352 if a:
3352 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3353 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3353 if u:
3354 if u:
3354 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3355 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3355 if m:
3356 if m:
3356 ui.write("mq: %s\n" % ', '.join(m))
3357 ui.write("mq: %s\n" % ', '.join(m))
3357 else:
3358 else:
3358 ui.note(_("mq: (empty queue)\n"))
3359 ui.note(_("mq: (empty queue)\n"))
3359 return r
3360 return r
3360
3361
3361 def revsetmq(repo, subset, x):
3362 def revsetmq(repo, subset, x):
3362 """``mq()``
3363 """``mq()``
3363 Changesets managed by MQ.
3364 Changesets managed by MQ.
3364 """
3365 """
3365 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3366 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3366 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3367 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3367 return [r for r in subset if r in applied]
3368 return [r for r in subset if r in applied]
3368
3369
3369 def extsetup(ui):
3370 def extsetup(ui):
3370 revset.symbols['mq'] = revsetmq
3371 revset.symbols['mq'] = revsetmq
3371
3372
3372 # tell hggettext to extract docstrings from these functions:
3373 # tell hggettext to extract docstrings from these functions:
3373 i18nfunctions = [revsetmq]
3374 i18nfunctions = [revsetmq]
3374
3375
3375 def uisetup(ui):
3376 def uisetup(ui):
3376 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3377 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3377
3378
3378 extensions.wrapcommand(commands.table, 'import', mqimport)
3379 extensions.wrapcommand(commands.table, 'import', mqimport)
3379 extensions.wrapcommand(commands.table, 'summary', summary)
3380 extensions.wrapcommand(commands.table, 'summary', summary)
3380
3381
3381 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3382 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3382 entry[1].extend(mqopt)
3383 entry[1].extend(mqopt)
3383
3384
3384 nowrap = set(commands.norepo.split(" "))
3385 nowrap = set(commands.norepo.split(" "))
3385
3386
3386 def dotable(cmdtable):
3387 def dotable(cmdtable):
3387 for cmd in cmdtable.keys():
3388 for cmd in cmdtable.keys():
3388 cmd = cmdutil.parsealiases(cmd)[0]
3389 cmd = cmdutil.parsealiases(cmd)[0]
3389 if cmd in nowrap:
3390 if cmd in nowrap:
3390 continue
3391 continue
3391 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3392 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3392 entry[1].extend(mqopt)
3393 entry[1].extend(mqopt)
3393
3394
3394 dotable(commands.table)
3395 dotable(commands.table)
3395
3396
3396 for extname, extmodule in extensions.extensions():
3397 for extname, extmodule in extensions.extensions():
3397 if extmodule.__file__ != __file__:
3398 if extmodule.__file__ != __file__:
3398 dotable(getattr(extmodule, 'cmdtable', {}))
3399 dotable(getattr(extmodule, 'cmdtable', {}))
3399
3400
3400
3401
3401 colortable = {'qguard.negative': 'red',
3402 colortable = {'qguard.negative': 'red',
3402 'qguard.positive': 'yellow',
3403 'qguard.positive': 'yellow',
3403 'qguard.unguarded': 'green',
3404 'qguard.unguarded': 'green',
3404 'qseries.applied': 'blue bold underline',
3405 'qseries.applied': 'blue bold underline',
3405 'qseries.guarded': 'black bold',
3406 'qseries.guarded': 'black bold',
3406 'qseries.missing': 'red bold',
3407 'qseries.missing': 'red bold',
3407 'qseries.unapplied': 'black bold'}
3408 'qseries.unapplied': 'black bold'}
@@ -1,5795 +1,5795 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, bin, nullid, nullrev, short
8 from node import hex, bin, 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, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge
18 import dagparser, context, simplemerge
19 import random, setdiscovery, treediscovery, dagutil
19 import random, setdiscovery, treediscovery, dagutil
20 import phases
20 import phases
21
21
22 table = {}
22 table = {}
23
23
24 command = cmdutil.command(table)
24 command = cmdutil.command(table)
25
25
26 # common command options
26 # common command options
27
27
28 globalopts = [
28 globalopts = [
29 ('R', 'repository', '',
29 ('R', 'repository', '',
30 _('repository root directory or name of overlay bundle file'),
30 _('repository root directory or name of overlay bundle file'),
31 _('REPO')),
31 _('REPO')),
32 ('', 'cwd', '',
32 ('', 'cwd', '',
33 _('change working directory'), _('DIR')),
33 _('change working directory'), _('DIR')),
34 ('y', 'noninteractive', None,
34 ('y', 'noninteractive', None,
35 _('do not prompt, automatically pick the first choice for all prompts')),
35 _('do not prompt, automatically pick the first choice for all prompts')),
36 ('q', 'quiet', None, _('suppress output')),
36 ('q', 'quiet', None, _('suppress output')),
37 ('v', 'verbose', None, _('enable additional output')),
37 ('v', 'verbose', None, _('enable additional output')),
38 ('', 'config', [],
38 ('', 'config', [],
39 _('set/override config option (use \'section.name=value\')'),
39 _('set/override config option (use \'section.name=value\')'),
40 _('CONFIG')),
40 _('CONFIG')),
41 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debugger', None, _('start debugger')),
42 ('', 'debugger', None, _('start debugger')),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 _('ENCODE')),
44 _('ENCODE')),
45 ('', 'encodingmode', encoding.encodingmode,
45 ('', 'encodingmode', encoding.encodingmode,
46 _('set the charset encoding mode'), _('MODE')),
46 _('set the charset encoding mode'), _('MODE')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'time', None, _('time how long the command takes')),
48 ('', 'time', None, _('time how long the command takes')),
49 ('', 'profile', None, _('print command execution profile')),
49 ('', 'profile', None, _('print command execution profile')),
50 ('', 'version', None, _('output version information and exit')),
50 ('', 'version', None, _('output version information and exit')),
51 ('h', 'help', None, _('display help and exit')),
51 ('h', 'help', None, _('display help and exit')),
52 ]
52 ]
53
53
54 dryrunopts = [('n', 'dry-run', None,
54 dryrunopts = [('n', 'dry-run', None,
55 _('do not perform actions, just print output'))]
55 _('do not perform actions, just print output'))]
56
56
57 remoteopts = [
57 remoteopts = [
58 ('e', 'ssh', '',
58 ('e', 'ssh', '',
59 _('specify ssh command to use'), _('CMD')),
59 _('specify ssh command to use'), _('CMD')),
60 ('', 'remotecmd', '',
60 ('', 'remotecmd', '',
61 _('specify hg command to run on the remote side'), _('CMD')),
61 _('specify hg command to run on the remote side'), _('CMD')),
62 ('', 'insecure', None,
62 ('', 'insecure', None,
63 _('do not verify server certificate (ignoring web.cacerts config)')),
63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 ]
64 ]
65
65
66 walkopts = [
66 walkopts = [
67 ('I', 'include', [],
67 ('I', 'include', [],
68 _('include names matching the given patterns'), _('PATTERN')),
68 _('include names matching the given patterns'), _('PATTERN')),
69 ('X', 'exclude', [],
69 ('X', 'exclude', [],
70 _('exclude names matching the given patterns'), _('PATTERN')),
70 _('exclude names matching the given patterns'), _('PATTERN')),
71 ]
71 ]
72
72
73 commitopts = [
73 commitopts = [
74 ('m', 'message', '',
74 ('m', 'message', '',
75 _('use text as commit message'), _('TEXT')),
75 _('use text as commit message'), _('TEXT')),
76 ('l', 'logfile', '',
76 ('l', 'logfile', '',
77 _('read commit message from file'), _('FILE')),
77 _('read commit message from file'), _('FILE')),
78 ]
78 ]
79
79
80 commitopts2 = [
80 commitopts2 = [
81 ('d', 'date', '',
81 ('d', 'date', '',
82 _('record the specified date as commit date'), _('DATE')),
82 _('record the specified date as commit date'), _('DATE')),
83 ('u', 'user', '',
83 ('u', 'user', '',
84 _('record the specified user as committer'), _('USER')),
84 _('record the specified user as committer'), _('USER')),
85 ]
85 ]
86
86
87 templateopts = [
87 templateopts = [
88 ('', 'style', '',
88 ('', 'style', '',
89 _('display using template map file'), _('STYLE')),
89 _('display using template map file'), _('STYLE')),
90 ('', 'template', '',
90 ('', 'template', '',
91 _('display with template'), _('TEMPLATE')),
91 _('display with template'), _('TEMPLATE')),
92 ]
92 ]
93
93
94 logopts = [
94 logopts = [
95 ('p', 'patch', None, _('show patch')),
95 ('p', 'patch', None, _('show patch')),
96 ('g', 'git', None, _('use git extended diff format')),
96 ('g', 'git', None, _('use git extended diff format')),
97 ('l', 'limit', '',
97 ('l', 'limit', '',
98 _('limit number of changes displayed'), _('NUM')),
98 _('limit number of changes displayed'), _('NUM')),
99 ('M', 'no-merges', None, _('do not show merges')),
99 ('M', 'no-merges', None, _('do not show merges')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ] + templateopts
101 ] + templateopts
102
102
103 diffopts = [
103 diffopts = [
104 ('a', 'text', None, _('treat all files as text')),
104 ('a', 'text', None, _('treat all files as text')),
105 ('g', 'git', None, _('use git extended diff format')),
105 ('g', 'git', None, _('use git extended diff format')),
106 ('', 'nodates', None, _('omit dates from diff headers'))
106 ('', 'nodates', None, _('omit dates from diff headers'))
107 ]
107 ]
108
108
109 diffwsopts = [
109 diffwsopts = [
110 ('w', 'ignore-all-space', None,
110 ('w', 'ignore-all-space', None,
111 _('ignore white space when comparing lines')),
111 _('ignore white space when comparing lines')),
112 ('b', 'ignore-space-change', None,
112 ('b', 'ignore-space-change', None,
113 _('ignore changes in the amount of white space')),
113 _('ignore changes in the amount of white space')),
114 ('B', 'ignore-blank-lines', None,
114 ('B', 'ignore-blank-lines', None,
115 _('ignore changes whose lines are all blank')),
115 _('ignore changes whose lines are all blank')),
116 ]
116 ]
117
117
118 diffopts2 = [
118 diffopts2 = [
119 ('p', 'show-function', None, _('show which function each change is in')),
119 ('p', 'show-function', None, _('show which function each change is in')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 ] + diffwsopts + [
121 ] + diffwsopts + [
122 ('U', 'unified', '',
122 ('U', 'unified', '',
123 _('number of lines of context to show'), _('NUM')),
123 _('number of lines of context to show'), _('NUM')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ]
125 ]
126
126
127 mergetoolopts = [
127 mergetoolopts = [
128 ('t', 'tool', '', _('specify merge tool')),
128 ('t', 'tool', '', _('specify merge tool')),
129 ]
129 ]
130
130
131 similarityopts = [
131 similarityopts = [
132 ('s', 'similarity', '',
132 ('s', 'similarity', '',
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 ]
134 ]
135
135
136 subrepoopts = [
136 subrepoopts = [
137 ('S', 'subrepos', None,
137 ('S', 'subrepos', None,
138 _('recurse into subrepositories'))
138 _('recurse into subrepositories'))
139 ]
139 ]
140
140
141 # Commands start here, listed alphabetically
141 # Commands start here, listed alphabetically
142
142
143 @command('^add',
143 @command('^add',
144 walkopts + subrepoopts + dryrunopts,
144 walkopts + subrepoopts + dryrunopts,
145 _('[OPTION]... [FILE]...'))
145 _('[OPTION]... [FILE]...'))
146 def add(ui, repo, *pats, **opts):
146 def add(ui, repo, *pats, **opts):
147 """add the specified files on the next commit
147 """add the specified files on the next commit
148
148
149 Schedule files to be version controlled and added to the
149 Schedule files to be version controlled and added to the
150 repository.
150 repository.
151
151
152 The files will be added to the repository at the next commit. To
152 The files will be added to the repository at the next commit. To
153 undo an add before that, see :hg:`forget`.
153 undo an add before that, see :hg:`forget`.
154
154
155 If no names are given, add all files to the repository.
155 If no names are given, add all files to the repository.
156
156
157 .. container:: verbose
157 .. container:: verbose
158
158
159 An example showing how new (unknown) files are added
159 An example showing how new (unknown) files are added
160 automatically by :hg:`add`::
160 automatically by :hg:`add`::
161
161
162 $ ls
162 $ ls
163 foo.c
163 foo.c
164 $ hg status
164 $ hg status
165 ? foo.c
165 ? foo.c
166 $ hg add
166 $ hg add
167 adding foo.c
167 adding foo.c
168 $ hg status
168 $ hg status
169 A foo.c
169 A foo.c
170
170
171 Returns 0 if all files are successfully added.
171 Returns 0 if all files are successfully added.
172 """
172 """
173
173
174 m = scmutil.match(repo[None], pats, opts)
174 m = scmutil.match(repo[None], pats, opts)
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 opts.get('subrepos'), prefix="", explicitonly=False)
176 opts.get('subrepos'), prefix="", explicitonly=False)
177 return rejected and 1 or 0
177 return rejected and 1 or 0
178
178
179 @command('addremove',
179 @command('addremove',
180 similarityopts + walkopts + dryrunopts,
180 similarityopts + walkopts + dryrunopts,
181 _('[OPTION]... [FILE]...'))
181 _('[OPTION]... [FILE]...'))
182 def addremove(ui, repo, *pats, **opts):
182 def addremove(ui, repo, *pats, **opts):
183 """add all new files, delete all missing files
183 """add all new files, delete all missing files
184
184
185 Add all new files and remove all missing files from the
185 Add all new files and remove all missing files from the
186 repository.
186 repository.
187
187
188 New files are ignored if they match any of the patterns in
188 New files are ignored if they match any of the patterns in
189 ``.hgignore``. As with add, these changes take effect at the next
189 ``.hgignore``. As with add, these changes take effect at the next
190 commit.
190 commit.
191
191
192 Use the -s/--similarity option to detect renamed files. With a
192 Use the -s/--similarity option to detect renamed files. With a
193 parameter greater than 0, this compares every removed file with
193 parameter greater than 0, this compares every removed file with
194 every added file and records those similar enough as renames. This
194 every added file and records those similar enough as renames. This
195 option takes a percentage between 0 (disabled) and 100 (files must
195 option takes a percentage between 0 (disabled) and 100 (files must
196 be identical) as its parameter. Detecting renamed files this way
196 be identical) as its parameter. Detecting renamed files this way
197 can be expensive. After using this option, :hg:`status -C` can be
197 can be expensive. After using this option, :hg:`status -C` can be
198 used to check which files were identified as moved or renamed.
198 used to check which files were identified as moved or renamed.
199
199
200 Returns 0 if all files are successfully added.
200 Returns 0 if all files are successfully added.
201 """
201 """
202 try:
202 try:
203 sim = float(opts.get('similarity') or 100)
203 sim = float(opts.get('similarity') or 100)
204 except ValueError:
204 except ValueError:
205 raise util.Abort(_('similarity must be a number'))
205 raise util.Abort(_('similarity must be a number'))
206 if sim < 0 or sim > 100:
206 if sim < 0 or sim > 100:
207 raise util.Abort(_('similarity must be between 0 and 100'))
207 raise util.Abort(_('similarity must be between 0 and 100'))
208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
209
209
210 @command('^annotate|blame',
210 @command('^annotate|blame',
211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
212 ('', 'follow', None,
212 ('', 'follow', None,
213 _('follow copies/renames and list the filename (DEPRECATED)')),
213 _('follow copies/renames and list the filename (DEPRECATED)')),
214 ('', 'no-follow', None, _("don't follow copies and renames")),
214 ('', 'no-follow', None, _("don't follow copies and renames")),
215 ('a', 'text', None, _('treat all files as text')),
215 ('a', 'text', None, _('treat all files as text')),
216 ('u', 'user', None, _('list the author (long with -v)')),
216 ('u', 'user', None, _('list the author (long with -v)')),
217 ('f', 'file', None, _('list the filename')),
217 ('f', 'file', None, _('list the filename')),
218 ('d', 'date', None, _('list the date (short with -q)')),
218 ('d', 'date', None, _('list the date (short with -q)')),
219 ('n', 'number', None, _('list the revision number (default)')),
219 ('n', 'number', None, _('list the revision number (default)')),
220 ('c', 'changeset', None, _('list the changeset')),
220 ('c', 'changeset', None, _('list the changeset')),
221 ('l', 'line-number', None, _('show line number at the first appearance'))
221 ('l', 'line-number', None, _('show line number at the first appearance'))
222 ] + diffwsopts + walkopts,
222 ] + diffwsopts + walkopts,
223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
224 def annotate(ui, repo, *pats, **opts):
224 def annotate(ui, repo, *pats, **opts):
225 """show changeset information by line for each file
225 """show changeset information by line for each file
226
226
227 List changes in files, showing the revision id responsible for
227 List changes in files, showing the revision id responsible for
228 each line
228 each line
229
229
230 This command is useful for discovering when a change was made and
230 This command is useful for discovering when a change was made and
231 by whom.
231 by whom.
232
232
233 Without the -a/--text option, annotate will avoid processing files
233 Without the -a/--text option, annotate will avoid processing files
234 it detects as binary. With -a, annotate will annotate the file
234 it detects as binary. With -a, annotate will annotate the file
235 anyway, although the results will probably be neither useful
235 anyway, although the results will probably be neither useful
236 nor desirable.
236 nor desirable.
237
237
238 Returns 0 on success.
238 Returns 0 on success.
239 """
239 """
240 if opts.get('follow'):
240 if opts.get('follow'):
241 # --follow is deprecated and now just an alias for -f/--file
241 # --follow is deprecated and now just an alias for -f/--file
242 # to mimic the behavior of Mercurial before version 1.5
242 # to mimic the behavior of Mercurial before version 1.5
243 opts['file'] = True
243 opts['file'] = True
244
244
245 datefunc = ui.quiet and util.shortdate or util.datestr
245 datefunc = ui.quiet and util.shortdate or util.datestr
246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
247
247
248 if not pats:
248 if not pats:
249 raise util.Abort(_('at least one filename or pattern is required'))
249 raise util.Abort(_('at least one filename or pattern is required'))
250
250
251 hexfn = ui.debugflag and hex or short
251 hexfn = ui.debugflag and hex or short
252
252
253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
254 ('number', ' ', lambda x: str(x[0].rev())),
254 ('number', ' ', lambda x: str(x[0].rev())),
255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
256 ('date', ' ', getdate),
256 ('date', ' ', getdate),
257 ('file', ' ', lambda x: x[0].path()),
257 ('file', ' ', lambda x: x[0].path()),
258 ('line_number', ':', lambda x: str(x[1])),
258 ('line_number', ':', lambda x: str(x[1])),
259 ]
259 ]
260
260
261 if (not opts.get('user') and not opts.get('changeset')
261 if (not opts.get('user') and not opts.get('changeset')
262 and not opts.get('date') and not opts.get('file')):
262 and not opts.get('date') and not opts.get('file')):
263 opts['number'] = True
263 opts['number'] = True
264
264
265 linenumber = opts.get('line_number') is not None
265 linenumber = opts.get('line_number') is not None
266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
267 raise util.Abort(_('at least one of -n/-c is required for -l'))
267 raise util.Abort(_('at least one of -n/-c is required for -l'))
268
268
269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
271
271
272 def bad(x, y):
272 def bad(x, y):
273 raise util.Abort("%s: %s" % (x, y))
273 raise util.Abort("%s: %s" % (x, y))
274
274
275 ctx = scmutil.revsingle(repo, opts.get('rev'))
275 ctx = scmutil.revsingle(repo, opts.get('rev'))
276 m = scmutil.match(ctx, pats, opts)
276 m = scmutil.match(ctx, pats, opts)
277 m.bad = bad
277 m.bad = bad
278 follow = not opts.get('no_follow')
278 follow = not opts.get('no_follow')
279 diffopts = patch.diffopts(ui, opts, section='annotate')
279 diffopts = patch.diffopts(ui, opts, section='annotate')
280 for abs in ctx.walk(m):
280 for abs in ctx.walk(m):
281 fctx = ctx[abs]
281 fctx = ctx[abs]
282 if not opts.get('text') and util.binary(fctx.data()):
282 if not opts.get('text') and util.binary(fctx.data()):
283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
284 continue
284 continue
285
285
286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
287 diffopts=diffopts)
287 diffopts=diffopts)
288 pieces = []
288 pieces = []
289
289
290 for f, sep in funcmap:
290 for f, sep in funcmap:
291 l = [f(n) for n, dummy in lines]
291 l = [f(n) for n, dummy in lines]
292 if l:
292 if l:
293 sized = [(x, encoding.colwidth(x)) for x in l]
293 sized = [(x, encoding.colwidth(x)) for x in l]
294 ml = max([w for x, w in sized])
294 ml = max([w for x, w in sized])
295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
296 for x, w in sized])
296 for x, w in sized])
297
297
298 if pieces:
298 if pieces:
299 for p, l in zip(zip(*pieces), lines):
299 for p, l in zip(zip(*pieces), lines):
300 ui.write("%s: %s" % ("".join(p), l[1]))
300 ui.write("%s: %s" % ("".join(p), l[1]))
301
301
302 if lines and not lines[-1][1].endswith('\n'):
302 if lines and not lines[-1][1].endswith('\n'):
303 ui.write('\n')
303 ui.write('\n')
304
304
305 @command('archive',
305 @command('archive',
306 [('', 'no-decode', None, _('do not pass files through decoders')),
306 [('', 'no-decode', None, _('do not pass files through decoders')),
307 ('p', 'prefix', '', _('directory prefix for files in archive'),
307 ('p', 'prefix', '', _('directory prefix for files in archive'),
308 _('PREFIX')),
308 _('PREFIX')),
309 ('r', 'rev', '', _('revision to distribute'), _('REV')),
309 ('r', 'rev', '', _('revision to distribute'), _('REV')),
310 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
310 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
311 ] + subrepoopts + walkopts,
311 ] + subrepoopts + walkopts,
312 _('[OPTION]... DEST'))
312 _('[OPTION]... DEST'))
313 def archive(ui, repo, dest, **opts):
313 def archive(ui, repo, dest, **opts):
314 '''create an unversioned archive of a repository revision
314 '''create an unversioned archive of a repository revision
315
315
316 By default, the revision used is the parent of the working
316 By default, the revision used is the parent of the working
317 directory; use -r/--rev to specify a different revision.
317 directory; use -r/--rev to specify a different revision.
318
318
319 The archive type is automatically detected based on file
319 The archive type is automatically detected based on file
320 extension (or override using -t/--type).
320 extension (or override using -t/--type).
321
321
322 .. container:: verbose
322 .. container:: verbose
323
323
324 Examples:
324 Examples:
325
325
326 - create a zip file containing the 1.0 release::
326 - create a zip file containing the 1.0 release::
327
327
328 hg archive -r 1.0 project-1.0.zip
328 hg archive -r 1.0 project-1.0.zip
329
329
330 - create a tarball excluding .hg files::
330 - create a tarball excluding .hg files::
331
331
332 hg archive project.tar.gz -X ".hg*"
332 hg archive project.tar.gz -X ".hg*"
333
333
334 Valid types are:
334 Valid types are:
335
335
336 :``files``: a directory full of files (default)
336 :``files``: a directory full of files (default)
337 :``tar``: tar archive, uncompressed
337 :``tar``: tar archive, uncompressed
338 :``tbz2``: tar archive, compressed using bzip2
338 :``tbz2``: tar archive, compressed using bzip2
339 :``tgz``: tar archive, compressed using gzip
339 :``tgz``: tar archive, compressed using gzip
340 :``uzip``: zip archive, uncompressed
340 :``uzip``: zip archive, uncompressed
341 :``zip``: zip archive, compressed using deflate
341 :``zip``: zip archive, compressed using deflate
342
342
343 The exact name of the destination archive or directory is given
343 The exact name of the destination archive or directory is given
344 using a format string; see :hg:`help export` for details.
344 using a format string; see :hg:`help export` for details.
345
345
346 Each member added to an archive file has a directory prefix
346 Each member added to an archive file has a directory prefix
347 prepended. Use -p/--prefix to specify a format string for the
347 prepended. Use -p/--prefix to specify a format string for the
348 prefix. The default is the basename of the archive, with suffixes
348 prefix. The default is the basename of the archive, with suffixes
349 removed.
349 removed.
350
350
351 Returns 0 on success.
351 Returns 0 on success.
352 '''
352 '''
353
353
354 ctx = scmutil.revsingle(repo, opts.get('rev'))
354 ctx = scmutil.revsingle(repo, opts.get('rev'))
355 if not ctx:
355 if not ctx:
356 raise util.Abort(_('no working directory: please specify a revision'))
356 raise util.Abort(_('no working directory: please specify a revision'))
357 node = ctx.node()
357 node = ctx.node()
358 dest = cmdutil.makefilename(repo, dest, node)
358 dest = cmdutil.makefilename(repo, dest, node)
359 if os.path.realpath(dest) == repo.root:
359 if os.path.realpath(dest) == repo.root:
360 raise util.Abort(_('repository root cannot be destination'))
360 raise util.Abort(_('repository root cannot be destination'))
361
361
362 kind = opts.get('type') or archival.guesskind(dest) or 'files'
362 kind = opts.get('type') or archival.guesskind(dest) or 'files'
363 prefix = opts.get('prefix')
363 prefix = opts.get('prefix')
364
364
365 if dest == '-':
365 if dest == '-':
366 if kind == 'files':
366 if kind == 'files':
367 raise util.Abort(_('cannot archive plain files to stdout'))
367 raise util.Abort(_('cannot archive plain files to stdout'))
368 dest = cmdutil.makefileobj(repo, dest)
368 dest = cmdutil.makefileobj(repo, dest)
369 if not prefix:
369 if not prefix:
370 prefix = os.path.basename(repo.root) + '-%h'
370 prefix = os.path.basename(repo.root) + '-%h'
371
371
372 prefix = cmdutil.makefilename(repo, prefix, node)
372 prefix = cmdutil.makefilename(repo, prefix, node)
373 matchfn = scmutil.match(ctx, [], opts)
373 matchfn = scmutil.match(ctx, [], opts)
374 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
374 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
375 matchfn, prefix, subrepos=opts.get('subrepos'))
375 matchfn, prefix, subrepos=opts.get('subrepos'))
376
376
377 @command('backout',
377 @command('backout',
378 [('', 'merge', None, _('merge with old dirstate parent after backout')),
378 [('', 'merge', None, _('merge with old dirstate parent after backout')),
379 ('', 'parent', '',
379 ('', 'parent', '',
380 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
380 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
381 ('r', 'rev', '', _('revision to backout'), _('REV')),
381 ('r', 'rev', '', _('revision to backout'), _('REV')),
382 ] + mergetoolopts + walkopts + commitopts + commitopts2,
382 ] + mergetoolopts + walkopts + commitopts + commitopts2,
383 _('[OPTION]... [-r] REV'))
383 _('[OPTION]... [-r] REV'))
384 def backout(ui, repo, node=None, rev=None, **opts):
384 def backout(ui, repo, node=None, rev=None, **opts):
385 '''reverse effect of earlier changeset
385 '''reverse effect of earlier changeset
386
386
387 Prepare a new changeset with the effect of REV undone in the
387 Prepare a new changeset with the effect of REV undone in the
388 current working directory.
388 current working directory.
389
389
390 If REV is the parent of the working directory, then this new changeset
390 If REV is the parent of the working directory, then this new changeset
391 is committed automatically. Otherwise, hg needs to merge the
391 is committed automatically. Otherwise, hg needs to merge the
392 changes and the merged result is left uncommitted.
392 changes and the merged result is left uncommitted.
393
393
394 .. note::
394 .. note::
395 backout cannot be used to fix either an unwanted or
395 backout cannot be used to fix either an unwanted or
396 incorrect merge.
396 incorrect merge.
397
397
398 .. container:: verbose
398 .. container:: verbose
399
399
400 By default, the pending changeset will have one parent,
400 By default, the pending changeset will have one parent,
401 maintaining a linear history. With --merge, the pending
401 maintaining a linear history. With --merge, the pending
402 changeset will instead have two parents: the old parent of the
402 changeset will instead have two parents: the old parent of the
403 working directory and a new child of REV that simply undoes REV.
403 working directory and a new child of REV that simply undoes REV.
404
404
405 Before version 1.7, the behavior without --merge was equivalent
405 Before version 1.7, the behavior without --merge was equivalent
406 to specifying --merge followed by :hg:`update --clean .` to
406 to specifying --merge followed by :hg:`update --clean .` to
407 cancel the merge and leave the child of REV as a head to be
407 cancel the merge and leave the child of REV as a head to be
408 merged separately.
408 merged separately.
409
409
410 See :hg:`help dates` for a list of formats valid for -d/--date.
410 See :hg:`help dates` for a list of formats valid for -d/--date.
411
411
412 Returns 0 on success.
412 Returns 0 on success.
413 '''
413 '''
414 if rev and node:
414 if rev and node:
415 raise util.Abort(_("please specify just one revision"))
415 raise util.Abort(_("please specify just one revision"))
416
416
417 if not rev:
417 if not rev:
418 rev = node
418 rev = node
419
419
420 if not rev:
420 if not rev:
421 raise util.Abort(_("please specify a revision to backout"))
421 raise util.Abort(_("please specify a revision to backout"))
422
422
423 date = opts.get('date')
423 date = opts.get('date')
424 if date:
424 if date:
425 opts['date'] = util.parsedate(date)
425 opts['date'] = util.parsedate(date)
426
426
427 cmdutil.bailifchanged(repo)
427 cmdutil.bailifchanged(repo)
428 node = scmutil.revsingle(repo, rev).node()
428 node = scmutil.revsingle(repo, rev).node()
429
429
430 op1, op2 = repo.dirstate.parents()
430 op1, op2 = repo.dirstate.parents()
431 a = repo.changelog.ancestor(op1, node)
431 a = repo.changelog.ancestor(op1, node)
432 if a != node:
432 if a != node:
433 raise util.Abort(_('cannot backout change on a different branch'))
433 raise util.Abort(_('cannot backout change on a different branch'))
434
434
435 p1, p2 = repo.changelog.parents(node)
435 p1, p2 = repo.changelog.parents(node)
436 if p1 == nullid:
436 if p1 == nullid:
437 raise util.Abort(_('cannot backout a change with no parents'))
437 raise util.Abort(_('cannot backout a change with no parents'))
438 if p2 != nullid:
438 if p2 != nullid:
439 if not opts.get('parent'):
439 if not opts.get('parent'):
440 raise util.Abort(_('cannot backout a merge changeset'))
440 raise util.Abort(_('cannot backout a merge changeset'))
441 p = repo.lookup(opts['parent'])
441 p = repo.lookup(opts['parent'])
442 if p not in (p1, p2):
442 if p not in (p1, p2):
443 raise util.Abort(_('%s is not a parent of %s') %
443 raise util.Abort(_('%s is not a parent of %s') %
444 (short(p), short(node)))
444 (short(p), short(node)))
445 parent = p
445 parent = p
446 else:
446 else:
447 if opts.get('parent'):
447 if opts.get('parent'):
448 raise util.Abort(_('cannot use --parent on non-merge changeset'))
448 raise util.Abort(_('cannot use --parent on non-merge changeset'))
449 parent = p1
449 parent = p1
450
450
451 # the backout should appear on the same branch
451 # the backout should appear on the same branch
452 branch = repo.dirstate.branch()
452 branch = repo.dirstate.branch()
453 hg.clean(repo, node, show_stats=False)
453 hg.clean(repo, node, show_stats=False)
454 repo.dirstate.setbranch(branch)
454 repo.dirstate.setbranch(branch)
455 revert_opts = opts.copy()
455 revert_opts = opts.copy()
456 revert_opts['date'] = None
456 revert_opts['date'] = None
457 revert_opts['all'] = True
457 revert_opts['all'] = True
458 revert_opts['rev'] = hex(parent)
458 revert_opts['rev'] = hex(parent)
459 revert_opts['no_backup'] = None
459 revert_opts['no_backup'] = None
460 revert(ui, repo, **revert_opts)
460 revert(ui, repo, **revert_opts)
461 if not opts.get('merge') and op1 != node:
461 if not opts.get('merge') and op1 != node:
462 try:
462 try:
463 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
463 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
464 return hg.update(repo, op1)
464 return hg.update(repo, op1)
465 finally:
465 finally:
466 ui.setconfig('ui', 'forcemerge', '')
466 ui.setconfig('ui', 'forcemerge', '')
467
467
468 commit_opts = opts.copy()
468 commit_opts = opts.copy()
469 commit_opts['addremove'] = False
469 commit_opts['addremove'] = False
470 if not commit_opts['message'] and not commit_opts['logfile']:
470 if not commit_opts['message'] and not commit_opts['logfile']:
471 # we don't translate commit messages
471 # we don't translate commit messages
472 commit_opts['message'] = "Backed out changeset %s" % short(node)
472 commit_opts['message'] = "Backed out changeset %s" % short(node)
473 commit_opts['force_editor'] = True
473 commit_opts['force_editor'] = True
474 commit(ui, repo, **commit_opts)
474 commit(ui, repo, **commit_opts)
475 def nice(node):
475 def nice(node):
476 return '%d:%s' % (repo.changelog.rev(node), short(node))
476 return '%d:%s' % (repo.changelog.rev(node), short(node))
477 ui.status(_('changeset %s backs out changeset %s\n') %
477 ui.status(_('changeset %s backs out changeset %s\n') %
478 (nice(repo.changelog.tip()), nice(node)))
478 (nice(repo.changelog.tip()), nice(node)))
479 if opts.get('merge') and op1 != node:
479 if opts.get('merge') and op1 != node:
480 hg.clean(repo, op1, show_stats=False)
480 hg.clean(repo, op1, show_stats=False)
481 ui.status(_('merging with changeset %s\n')
481 ui.status(_('merging with changeset %s\n')
482 % nice(repo.changelog.tip()))
482 % nice(repo.changelog.tip()))
483 try:
483 try:
484 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
484 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
485 return hg.merge(repo, hex(repo.changelog.tip()))
485 return hg.merge(repo, hex(repo.changelog.tip()))
486 finally:
486 finally:
487 ui.setconfig('ui', 'forcemerge', '')
487 ui.setconfig('ui', 'forcemerge', '')
488 return 0
488 return 0
489
489
490 @command('bisect',
490 @command('bisect',
491 [('r', 'reset', False, _('reset bisect state')),
491 [('r', 'reset', False, _('reset bisect state')),
492 ('g', 'good', False, _('mark changeset good')),
492 ('g', 'good', False, _('mark changeset good')),
493 ('b', 'bad', False, _('mark changeset bad')),
493 ('b', 'bad', False, _('mark changeset bad')),
494 ('s', 'skip', False, _('skip testing changeset')),
494 ('s', 'skip', False, _('skip testing changeset')),
495 ('e', 'extend', False, _('extend the bisect range')),
495 ('e', 'extend', False, _('extend the bisect range')),
496 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
496 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
497 ('U', 'noupdate', False, _('do not update to target'))],
497 ('U', 'noupdate', False, _('do not update to target'))],
498 _("[-gbsr] [-U] [-c CMD] [REV]"))
498 _("[-gbsr] [-U] [-c CMD] [REV]"))
499 def bisect(ui, repo, rev=None, extra=None, command=None,
499 def bisect(ui, repo, rev=None, extra=None, command=None,
500 reset=None, good=None, bad=None, skip=None, extend=None,
500 reset=None, good=None, bad=None, skip=None, extend=None,
501 noupdate=None):
501 noupdate=None):
502 """subdivision search of changesets
502 """subdivision search of changesets
503
503
504 This command helps to find changesets which introduce problems. To
504 This command helps to find changesets which introduce problems. To
505 use, mark the earliest changeset you know exhibits the problem as
505 use, mark the earliest changeset you know exhibits the problem as
506 bad, then mark the latest changeset which is free from the problem
506 bad, then mark the latest changeset which is free from the problem
507 as good. Bisect will update your working directory to a revision
507 as good. Bisect will update your working directory to a revision
508 for testing (unless the -U/--noupdate option is specified). Once
508 for testing (unless the -U/--noupdate option is specified). Once
509 you have performed tests, mark the working directory as good or
509 you have performed tests, mark the working directory as good or
510 bad, and bisect will either update to another candidate changeset
510 bad, and bisect will either update to another candidate changeset
511 or announce that it has found the bad revision.
511 or announce that it has found the bad revision.
512
512
513 As a shortcut, you can also use the revision argument to mark a
513 As a shortcut, you can also use the revision argument to mark a
514 revision as good or bad without checking it out first.
514 revision as good or bad without checking it out first.
515
515
516 If you supply a command, it will be used for automatic bisection.
516 If you supply a command, it will be used for automatic bisection.
517 Its exit status will be used to mark revisions as good or bad:
517 Its exit status will be used to mark revisions as good or bad:
518 status 0 means good, 125 means to skip the revision, 127
518 status 0 means good, 125 means to skip the revision, 127
519 (command not found) will abort the bisection, and any other
519 (command not found) will abort the bisection, and any other
520 non-zero exit status means the revision is bad.
520 non-zero exit status means the revision is bad.
521
521
522 .. container:: verbose
522 .. container:: verbose
523
523
524 Some examples:
524 Some examples:
525
525
526 - start a bisection with known bad revision 12, and good revision 34::
526 - start a bisection with known bad revision 12, and good revision 34::
527
527
528 hg bisect --bad 34
528 hg bisect --bad 34
529 hg bisect --good 12
529 hg bisect --good 12
530
530
531 - advance the current bisection by marking current revision as good or
531 - advance the current bisection by marking current revision as good or
532 bad::
532 bad::
533
533
534 hg bisect --good
534 hg bisect --good
535 hg bisect --bad
535 hg bisect --bad
536
536
537 - mark the current revision, or a known revision, to be skipped (eg. if
537 - mark the current revision, or a known revision, to be skipped (eg. if
538 that revision is not usable because of another issue)::
538 that revision is not usable because of another issue)::
539
539
540 hg bisect --skip
540 hg bisect --skip
541 hg bisect --skip 23
541 hg bisect --skip 23
542
542
543 - forget the current bisection::
543 - forget the current bisection::
544
544
545 hg bisect --reset
545 hg bisect --reset
546
546
547 - use 'make && make tests' to automatically find the first broken
547 - use 'make && make tests' to automatically find the first broken
548 revision::
548 revision::
549
549
550 hg bisect --reset
550 hg bisect --reset
551 hg bisect --bad 34
551 hg bisect --bad 34
552 hg bisect --good 12
552 hg bisect --good 12
553 hg bisect --command 'make && make tests'
553 hg bisect --command 'make && make tests'
554
554
555 - see all changesets whose states are already known in the current
555 - see all changesets whose states are already known in the current
556 bisection::
556 bisection::
557
557
558 hg log -r "bisect(pruned)"
558 hg log -r "bisect(pruned)"
559
559
560 - see all changesets that took part in the current bisection::
560 - see all changesets that took part in the current bisection::
561
561
562 hg log -r "bisect(range)"
562 hg log -r "bisect(range)"
563
563
564 - with the graphlog extension, you can even get a nice graph::
564 - with the graphlog extension, you can even get a nice graph::
565
565
566 hg log --graph -r "bisect(range)"
566 hg log --graph -r "bisect(range)"
567
567
568 See :hg:`help revsets` for more about the `bisect()` keyword.
568 See :hg:`help revsets` for more about the `bisect()` keyword.
569
569
570 Returns 0 on success.
570 Returns 0 on success.
571 """
571 """
572 def extendbisectrange(nodes, good):
572 def extendbisectrange(nodes, good):
573 # bisect is incomplete when it ends on a merge node and
573 # bisect is incomplete when it ends on a merge node and
574 # one of the parent was not checked.
574 # one of the parent was not checked.
575 parents = repo[nodes[0]].parents()
575 parents = repo[nodes[0]].parents()
576 if len(parents) > 1:
576 if len(parents) > 1:
577 side = good and state['bad'] or state['good']
577 side = good and state['bad'] or state['good']
578 num = len(set(i.node() for i in parents) & set(side))
578 num = len(set(i.node() for i in parents) & set(side))
579 if num == 1:
579 if num == 1:
580 return parents[0].ancestor(parents[1])
580 return parents[0].ancestor(parents[1])
581 return None
581 return None
582
582
583 def print_result(nodes, good):
583 def print_result(nodes, good):
584 displayer = cmdutil.show_changeset(ui, repo, {})
584 displayer = cmdutil.show_changeset(ui, repo, {})
585 if len(nodes) == 1:
585 if len(nodes) == 1:
586 # narrowed it down to a single revision
586 # narrowed it down to a single revision
587 if good:
587 if good:
588 ui.write(_("The first good revision is:\n"))
588 ui.write(_("The first good revision is:\n"))
589 else:
589 else:
590 ui.write(_("The first bad revision is:\n"))
590 ui.write(_("The first bad revision is:\n"))
591 displayer.show(repo[nodes[0]])
591 displayer.show(repo[nodes[0]])
592 extendnode = extendbisectrange(nodes, good)
592 extendnode = extendbisectrange(nodes, good)
593 if extendnode is not None:
593 if extendnode is not None:
594 ui.write(_('Not all ancestors of this changeset have been'
594 ui.write(_('Not all ancestors of this changeset have been'
595 ' checked.\nUse bisect --extend to continue the '
595 ' checked.\nUse bisect --extend to continue the '
596 'bisection from\nthe common ancestor, %s.\n')
596 'bisection from\nthe common ancestor, %s.\n')
597 % extendnode)
597 % extendnode)
598 else:
598 else:
599 # multiple possible revisions
599 # multiple possible revisions
600 if good:
600 if good:
601 ui.write(_("Due to skipped revisions, the first "
601 ui.write(_("Due to skipped revisions, the first "
602 "good revision could be any of:\n"))
602 "good revision could be any of:\n"))
603 else:
603 else:
604 ui.write(_("Due to skipped revisions, the first "
604 ui.write(_("Due to skipped revisions, the first "
605 "bad revision could be any of:\n"))
605 "bad revision could be any of:\n"))
606 for n in nodes:
606 for n in nodes:
607 displayer.show(repo[n])
607 displayer.show(repo[n])
608 displayer.close()
608 displayer.close()
609
609
610 def check_state(state, interactive=True):
610 def check_state(state, interactive=True):
611 if not state['good'] or not state['bad']:
611 if not state['good'] or not state['bad']:
612 if (good or bad or skip or reset) and interactive:
612 if (good or bad or skip or reset) and interactive:
613 return
613 return
614 if not state['good']:
614 if not state['good']:
615 raise util.Abort(_('cannot bisect (no known good revisions)'))
615 raise util.Abort(_('cannot bisect (no known good revisions)'))
616 else:
616 else:
617 raise util.Abort(_('cannot bisect (no known bad revisions)'))
617 raise util.Abort(_('cannot bisect (no known bad revisions)'))
618 return True
618 return True
619
619
620 # backward compatibility
620 # backward compatibility
621 if rev in "good bad reset init".split():
621 if rev in "good bad reset init".split():
622 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
622 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
623 cmd, rev, extra = rev, extra, None
623 cmd, rev, extra = rev, extra, None
624 if cmd == "good":
624 if cmd == "good":
625 good = True
625 good = True
626 elif cmd == "bad":
626 elif cmd == "bad":
627 bad = True
627 bad = True
628 else:
628 else:
629 reset = True
629 reset = True
630 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
630 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
631 raise util.Abort(_('incompatible arguments'))
631 raise util.Abort(_('incompatible arguments'))
632
632
633 if reset:
633 if reset:
634 p = repo.join("bisect.state")
634 p = repo.join("bisect.state")
635 if os.path.exists(p):
635 if os.path.exists(p):
636 os.unlink(p)
636 os.unlink(p)
637 return
637 return
638
638
639 state = hbisect.load_state(repo)
639 state = hbisect.load_state(repo)
640
640
641 if command:
641 if command:
642 changesets = 1
642 changesets = 1
643 try:
643 try:
644 while changesets:
644 while changesets:
645 # update state
645 # update state
646 status = util.system(command, out=ui.fout)
646 status = util.system(command, out=ui.fout)
647 if status == 125:
647 if status == 125:
648 transition = "skip"
648 transition = "skip"
649 elif status == 0:
649 elif status == 0:
650 transition = "good"
650 transition = "good"
651 # status < 0 means process was killed
651 # status < 0 means process was killed
652 elif status == 127:
652 elif status == 127:
653 raise util.Abort(_("failed to execute %s") % command)
653 raise util.Abort(_("failed to execute %s") % command)
654 elif status < 0:
654 elif status < 0:
655 raise util.Abort(_("%s killed") % command)
655 raise util.Abort(_("%s killed") % command)
656 else:
656 else:
657 transition = "bad"
657 transition = "bad"
658 ctx = scmutil.revsingle(repo, rev)
658 ctx = scmutil.revsingle(repo, rev)
659 rev = None # clear for future iterations
659 rev = None # clear for future iterations
660 state[transition].append(ctx.node())
660 state[transition].append(ctx.node())
661 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
661 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
662 check_state(state, interactive=False)
662 check_state(state, interactive=False)
663 # bisect
663 # bisect
664 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
664 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
665 # update to next check
665 # update to next check
666 cmdutil.bailifchanged(repo)
666 cmdutil.bailifchanged(repo)
667 hg.clean(repo, nodes[0], show_stats=False)
667 hg.clean(repo, nodes[0], show_stats=False)
668 finally:
668 finally:
669 hbisect.save_state(repo, state)
669 hbisect.save_state(repo, state)
670 print_result(nodes, good)
670 print_result(nodes, good)
671 return
671 return
672
672
673 # update state
673 # update state
674
674
675 if rev:
675 if rev:
676 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
676 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
677 else:
677 else:
678 nodes = [repo.lookup('.')]
678 nodes = [repo.lookup('.')]
679
679
680 if good or bad or skip:
680 if good or bad or skip:
681 if good:
681 if good:
682 state['good'] += nodes
682 state['good'] += nodes
683 elif bad:
683 elif bad:
684 state['bad'] += nodes
684 state['bad'] += nodes
685 elif skip:
685 elif skip:
686 state['skip'] += nodes
686 state['skip'] += nodes
687 hbisect.save_state(repo, state)
687 hbisect.save_state(repo, state)
688
688
689 if not check_state(state):
689 if not check_state(state):
690 return
690 return
691
691
692 # actually bisect
692 # actually bisect
693 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
693 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
694 if extend:
694 if extend:
695 if not changesets:
695 if not changesets:
696 extendnode = extendbisectrange(nodes, good)
696 extendnode = extendbisectrange(nodes, good)
697 if extendnode is not None:
697 if extendnode is not None:
698 ui.write(_("Extending search to changeset %d:%s\n"
698 ui.write(_("Extending search to changeset %d:%s\n"
699 % (extendnode.rev(), extendnode)))
699 % (extendnode.rev(), extendnode)))
700 if noupdate:
700 if noupdate:
701 return
701 return
702 cmdutil.bailifchanged(repo)
702 cmdutil.bailifchanged(repo)
703 return hg.clean(repo, extendnode.node())
703 return hg.clean(repo, extendnode.node())
704 raise util.Abort(_("nothing to extend"))
704 raise util.Abort(_("nothing to extend"))
705
705
706 if changesets == 0:
706 if changesets == 0:
707 print_result(nodes, good)
707 print_result(nodes, good)
708 else:
708 else:
709 assert len(nodes) == 1 # only a single node can be tested next
709 assert len(nodes) == 1 # only a single node can be tested next
710 node = nodes[0]
710 node = nodes[0]
711 # compute the approximate number of remaining tests
711 # compute the approximate number of remaining tests
712 tests, size = 0, 2
712 tests, size = 0, 2
713 while size <= changesets:
713 while size <= changesets:
714 tests, size = tests + 1, size * 2
714 tests, size = tests + 1, size * 2
715 rev = repo.changelog.rev(node)
715 rev = repo.changelog.rev(node)
716 ui.write(_("Testing changeset %d:%s "
716 ui.write(_("Testing changeset %d:%s "
717 "(%d changesets remaining, ~%d tests)\n")
717 "(%d changesets remaining, ~%d tests)\n")
718 % (rev, short(node), changesets, tests))
718 % (rev, short(node), changesets, tests))
719 if not noupdate:
719 if not noupdate:
720 cmdutil.bailifchanged(repo)
720 cmdutil.bailifchanged(repo)
721 return hg.clean(repo, node)
721 return hg.clean(repo, node)
722
722
723 @command('bookmarks',
723 @command('bookmarks',
724 [('f', 'force', False, _('force')),
724 [('f', 'force', False, _('force')),
725 ('r', 'rev', '', _('revision'), _('REV')),
725 ('r', 'rev', '', _('revision'), _('REV')),
726 ('d', 'delete', False, _('delete a given bookmark')),
726 ('d', 'delete', False, _('delete a given bookmark')),
727 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
727 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
728 ('i', 'inactive', False, _('mark a bookmark inactive'))],
728 ('i', 'inactive', False, _('mark a bookmark inactive'))],
729 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
729 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
730 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
730 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
731 rename=None, inactive=False):
731 rename=None, inactive=False):
732 '''track a line of development with movable markers
732 '''track a line of development with movable markers
733
733
734 Bookmarks are pointers to certain commits that move when committing.
734 Bookmarks are pointers to certain commits that move when committing.
735 Bookmarks are local. They can be renamed, copied and deleted. It is
735 Bookmarks are local. They can be renamed, copied and deleted. It is
736 possible to use :hg:`merge NAME` to merge from a given bookmark, and
736 possible to use :hg:`merge NAME` to merge from a given bookmark, and
737 :hg:`update NAME` to update to a given bookmark.
737 :hg:`update NAME` to update to a given bookmark.
738
738
739 You can use :hg:`bookmark NAME` to set a bookmark on the working
739 You can use :hg:`bookmark NAME` to set a bookmark on the working
740 directory's parent revision with the given name. If you specify
740 directory's parent revision with the given name. If you specify
741 a revision using -r REV (where REV may be an existing bookmark),
741 a revision using -r REV (where REV may be an existing bookmark),
742 the bookmark is assigned to that revision.
742 the bookmark is assigned to that revision.
743
743
744 Bookmarks can be pushed and pulled between repositories (see :hg:`help
744 Bookmarks can be pushed and pulled between repositories (see :hg:`help
745 push` and :hg:`help pull`). This requires both the local and remote
745 push` and :hg:`help pull`). This requires both the local and remote
746 repositories to support bookmarks. For versions prior to 1.8, this means
746 repositories to support bookmarks. For versions prior to 1.8, this means
747 the bookmarks extension must be enabled.
747 the bookmarks extension must be enabled.
748
748
749 With -i/--inactive, the new bookmark will not be made the active
749 With -i/--inactive, the new bookmark will not be made the active
750 bookmark. If -r/--rev is given, the new bookmark will not be made
750 bookmark. If -r/--rev is given, the new bookmark will not be made
751 active even if -i/--inactive is not given. If no NAME is given, the
751 active even if -i/--inactive is not given. If no NAME is given, the
752 current active bookmark will be marked inactive.
752 current active bookmark will be marked inactive.
753 '''
753 '''
754 hexfn = ui.debugflag and hex or short
754 hexfn = ui.debugflag and hex or short
755 marks = repo._bookmarks
755 marks = repo._bookmarks
756 cur = repo.changectx('.').node()
756 cur = repo.changectx('.').node()
757
757
758 if delete:
758 if delete:
759 if mark is None:
759 if mark is None:
760 raise util.Abort(_("bookmark name required"))
760 raise util.Abort(_("bookmark name required"))
761 if mark not in marks:
761 if mark not in marks:
762 raise util.Abort(_("bookmark '%s' does not exist") % mark)
762 raise util.Abort(_("bookmark '%s' does not exist") % mark)
763 if mark == repo._bookmarkcurrent:
763 if mark == repo._bookmarkcurrent:
764 bookmarks.setcurrent(repo, None)
764 bookmarks.setcurrent(repo, None)
765 del marks[mark]
765 del marks[mark]
766 bookmarks.write(repo)
766 bookmarks.write(repo)
767 return
767 return
768
768
769 if rename:
769 if rename:
770 if rename not in marks:
770 if rename not in marks:
771 raise util.Abort(_("bookmark '%s' does not exist") % rename)
771 raise util.Abort(_("bookmark '%s' does not exist") % rename)
772 if mark in marks and not force:
772 if mark in marks and not force:
773 raise util.Abort(_("bookmark '%s' already exists "
773 raise util.Abort(_("bookmark '%s' already exists "
774 "(use -f to force)") % mark)
774 "(use -f to force)") % mark)
775 if mark is None:
775 if mark is None:
776 raise util.Abort(_("new bookmark name required"))
776 raise util.Abort(_("new bookmark name required"))
777 marks[mark] = marks[rename]
777 marks[mark] = marks[rename]
778 if repo._bookmarkcurrent == rename and not inactive:
778 if repo._bookmarkcurrent == rename and not inactive:
779 bookmarks.setcurrent(repo, mark)
779 bookmarks.setcurrent(repo, mark)
780 del marks[rename]
780 del marks[rename]
781 bookmarks.write(repo)
781 bookmarks.write(repo)
782 return
782 return
783
783
784 if mark is not None:
784 if mark is not None:
785 if "\n" in mark:
785 if "\n" in mark:
786 raise util.Abort(_("bookmark name cannot contain newlines"))
786 raise util.Abort(_("bookmark name cannot contain newlines"))
787 mark = mark.strip()
787 mark = mark.strip()
788 if not mark:
788 if not mark:
789 raise util.Abort(_("bookmark names cannot consist entirely of "
789 raise util.Abort(_("bookmark names cannot consist entirely of "
790 "whitespace"))
790 "whitespace"))
791 if inactive and mark == repo._bookmarkcurrent:
791 if inactive and mark == repo._bookmarkcurrent:
792 bookmarks.setcurrent(repo, None)
792 bookmarks.setcurrent(repo, None)
793 return
793 return
794 if mark in marks and not force:
794 if mark in marks and not force:
795 raise util.Abort(_("bookmark '%s' already exists "
795 raise util.Abort(_("bookmark '%s' already exists "
796 "(use -f to force)") % mark)
796 "(use -f to force)") % mark)
797 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
797 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
798 and not force):
798 and not force):
799 raise util.Abort(
799 raise util.Abort(
800 _("a bookmark cannot have the name of an existing branch"))
800 _("a bookmark cannot have the name of an existing branch"))
801 if rev:
801 if rev:
802 marks[mark] = repo.lookup(rev)
802 marks[mark] = repo.lookup(rev)
803 else:
803 else:
804 marks[mark] = cur
804 marks[mark] = cur
805 if not inactive and cur == marks[mark]:
805 if not inactive and cur == marks[mark]:
806 bookmarks.setcurrent(repo, mark)
806 bookmarks.setcurrent(repo, mark)
807 bookmarks.write(repo)
807 bookmarks.write(repo)
808 return
808 return
809
809
810 if mark is None:
810 if mark is None:
811 if rev:
811 if rev:
812 raise util.Abort(_("bookmark name required"))
812 raise util.Abort(_("bookmark name required"))
813 if len(marks) == 0:
813 if len(marks) == 0:
814 ui.status(_("no bookmarks set\n"))
814 ui.status(_("no bookmarks set\n"))
815 else:
815 else:
816 for bmark, n in sorted(marks.iteritems()):
816 for bmark, n in sorted(marks.iteritems()):
817 current = repo._bookmarkcurrent
817 current = repo._bookmarkcurrent
818 if bmark == current and n == cur:
818 if bmark == current and n == cur:
819 prefix, label = '*', 'bookmarks.current'
819 prefix, label = '*', 'bookmarks.current'
820 else:
820 else:
821 prefix, label = ' ', ''
821 prefix, label = ' ', ''
822
822
823 if ui.quiet:
823 if ui.quiet:
824 ui.write("%s\n" % bmark, label=label)
824 ui.write("%s\n" % bmark, label=label)
825 else:
825 else:
826 ui.write(" %s %-25s %d:%s\n" % (
826 ui.write(" %s %-25s %d:%s\n" % (
827 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
827 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
828 label=label)
828 label=label)
829 return
829 return
830
830
831 @command('branch',
831 @command('branch',
832 [('f', 'force', None,
832 [('f', 'force', None,
833 _('set branch name even if it shadows an existing branch')),
833 _('set branch name even if it shadows an existing branch')),
834 ('C', 'clean', None, _('reset branch name to parent branch name'))],
834 ('C', 'clean', None, _('reset branch name to parent branch name'))],
835 _('[-fC] [NAME]'))
835 _('[-fC] [NAME]'))
836 def branch(ui, repo, label=None, **opts):
836 def branch(ui, repo, label=None, **opts):
837 """set or show the current branch name
837 """set or show the current branch name
838
838
839 .. note::
839 .. note::
840 Branch names are permanent and global. Use :hg:`bookmark` to create a
840 Branch names are permanent and global. Use :hg:`bookmark` to create a
841 light-weight bookmark instead. See :hg:`help glossary` for more
841 light-weight bookmark instead. See :hg:`help glossary` for more
842 information about named branches and bookmarks.
842 information about named branches and bookmarks.
843
843
844 With no argument, show the current branch name. With one argument,
844 With no argument, show the current branch name. With one argument,
845 set the working directory branch name (the branch will not exist
845 set the working directory branch name (the branch will not exist
846 in the repository until the next commit). Standard practice
846 in the repository until the next commit). Standard practice
847 recommends that primary development take place on the 'default'
847 recommends that primary development take place on the 'default'
848 branch.
848 branch.
849
849
850 Unless -f/--force is specified, branch will not let you set a
850 Unless -f/--force is specified, branch will not let you set a
851 branch name that already exists, even if it's inactive.
851 branch name that already exists, even if it's inactive.
852
852
853 Use -C/--clean to reset the working directory branch to that of
853 Use -C/--clean to reset the working directory branch to that of
854 the parent of the working directory, negating a previous branch
854 the parent of the working directory, negating a previous branch
855 change.
855 change.
856
856
857 Use the command :hg:`update` to switch to an existing branch. Use
857 Use the command :hg:`update` to switch to an existing branch. Use
858 :hg:`commit --close-branch` to mark this branch as closed.
858 :hg:`commit --close-branch` to mark this branch as closed.
859
859
860 Returns 0 on success.
860 Returns 0 on success.
861 """
861 """
862
862
863 if opts.get('clean'):
863 if opts.get('clean'):
864 label = repo[None].p1().branch()
864 label = repo[None].p1().branch()
865 repo.dirstate.setbranch(label)
865 repo.dirstate.setbranch(label)
866 ui.status(_('reset working directory to branch %s\n') % label)
866 ui.status(_('reset working directory to branch %s\n') % label)
867 elif label:
867 elif label:
868 if not opts.get('force') and label in repo.branchtags():
868 if not opts.get('force') and label in repo.branchtags():
869 if label not in [p.branch() for p in repo.parents()]:
869 if label not in [p.branch() for p in repo.parents()]:
870 raise util.Abort(_('a branch of the same name already exists'),
870 raise util.Abort(_('a branch of the same name already exists'),
871 # i18n: "it" refers to an existing branch
871 # i18n: "it" refers to an existing branch
872 hint=_("use 'hg update' to switch to it"))
872 hint=_("use 'hg update' to switch to it"))
873 repo.dirstate.setbranch(label)
873 repo.dirstate.setbranch(label)
874 ui.status(_('marked working directory as branch %s\n') % label)
874 ui.status(_('marked working directory as branch %s\n') % label)
875 ui.status(_('(branches are permanent and global, '
875 ui.status(_('(branches are permanent and global, '
876 'did you want a bookmark?)\n'))
876 'did you want a bookmark?)\n'))
877 else:
877 else:
878 ui.write("%s\n" % repo.dirstate.branch())
878 ui.write("%s\n" % repo.dirstate.branch())
879
879
880 @command('branches',
880 @command('branches',
881 [('a', 'active', False, _('show only branches that have unmerged heads')),
881 [('a', 'active', False, _('show only branches that have unmerged heads')),
882 ('c', 'closed', False, _('show normal and closed branches'))],
882 ('c', 'closed', False, _('show normal and closed branches'))],
883 _('[-ac]'))
883 _('[-ac]'))
884 def branches(ui, repo, active=False, closed=False):
884 def branches(ui, repo, active=False, closed=False):
885 """list repository named branches
885 """list repository named branches
886
886
887 List the repository's named branches, indicating which ones are
887 List the repository's named branches, indicating which ones are
888 inactive. If -c/--closed is specified, also list branches which have
888 inactive. If -c/--closed is specified, also list branches which have
889 been marked closed (see :hg:`commit --close-branch`).
889 been marked closed (see :hg:`commit --close-branch`).
890
890
891 If -a/--active is specified, only show active branches. A branch
891 If -a/--active is specified, only show active branches. A branch
892 is considered active if it contains repository heads.
892 is considered active if it contains repository heads.
893
893
894 Use the command :hg:`update` to switch to an existing branch.
894 Use the command :hg:`update` to switch to an existing branch.
895
895
896 Returns 0.
896 Returns 0.
897 """
897 """
898
898
899 hexfunc = ui.debugflag and hex or short
899 hexfunc = ui.debugflag and hex or short
900 activebranches = [repo[n].branch() for n in repo.heads()]
900 activebranches = [repo[n].branch() for n in repo.heads()]
901 def testactive(tag, node):
901 def testactive(tag, node):
902 realhead = tag in activebranches
902 realhead = tag in activebranches
903 open = node in repo.branchheads(tag, closed=False)
903 open = node in repo.branchheads(tag, closed=False)
904 return realhead and open
904 return realhead and open
905 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
905 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
906 for tag, node in repo.branchtags().items()],
906 for tag, node in repo.branchtags().items()],
907 reverse=True)
907 reverse=True)
908
908
909 for isactive, node, tag in branches:
909 for isactive, node, tag in branches:
910 if (not active) or isactive:
910 if (not active) or isactive:
911 if ui.quiet:
911 if ui.quiet:
912 ui.write("%s\n" % tag)
912 ui.write("%s\n" % tag)
913 else:
913 else:
914 hn = repo.lookup(node)
914 hn = repo.lookup(node)
915 if isactive:
915 if isactive:
916 label = 'branches.active'
916 label = 'branches.active'
917 notice = ''
917 notice = ''
918 elif hn not in repo.branchheads(tag, closed=False):
918 elif hn not in repo.branchheads(tag, closed=False):
919 if not closed:
919 if not closed:
920 continue
920 continue
921 label = 'branches.closed'
921 label = 'branches.closed'
922 notice = _(' (closed)')
922 notice = _(' (closed)')
923 else:
923 else:
924 label = 'branches.inactive'
924 label = 'branches.inactive'
925 notice = _(' (inactive)')
925 notice = _(' (inactive)')
926 if tag == repo.dirstate.branch():
926 if tag == repo.dirstate.branch():
927 label = 'branches.current'
927 label = 'branches.current'
928 rev = str(node).rjust(31 - encoding.colwidth(tag))
928 rev = str(node).rjust(31 - encoding.colwidth(tag))
929 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
929 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
930 tag = ui.label(tag, label)
930 tag = ui.label(tag, label)
931 ui.write("%s %s%s\n" % (tag, rev, notice))
931 ui.write("%s %s%s\n" % (tag, rev, notice))
932
932
933 @command('bundle',
933 @command('bundle',
934 [('f', 'force', None, _('run even when the destination is unrelated')),
934 [('f', 'force', None, _('run even when the destination is unrelated')),
935 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
935 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
936 _('REV')),
936 _('REV')),
937 ('b', 'branch', [], _('a specific branch you would like to bundle'),
937 ('b', 'branch', [], _('a specific branch you would like to bundle'),
938 _('BRANCH')),
938 _('BRANCH')),
939 ('', 'base', [],
939 ('', 'base', [],
940 _('a base changeset assumed to be available at the destination'),
940 _('a base changeset assumed to be available at the destination'),
941 _('REV')),
941 _('REV')),
942 ('a', 'all', None, _('bundle all changesets in the repository')),
942 ('a', 'all', None, _('bundle all changesets in the repository')),
943 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
943 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
944 ] + remoteopts,
944 ] + remoteopts,
945 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
945 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
946 def bundle(ui, repo, fname, dest=None, **opts):
946 def bundle(ui, repo, fname, dest=None, **opts):
947 """create a changegroup file
947 """create a changegroup file
948
948
949 Generate a compressed changegroup file collecting changesets not
949 Generate a compressed changegroup file collecting changesets not
950 known to be in another repository.
950 known to be in another repository.
951
951
952 If you omit the destination repository, then hg assumes the
952 If you omit the destination repository, then hg assumes the
953 destination will have all the nodes you specify with --base
953 destination will have all the nodes you specify with --base
954 parameters. To create a bundle containing all changesets, use
954 parameters. To create a bundle containing all changesets, use
955 -a/--all (or --base null).
955 -a/--all (or --base null).
956
956
957 You can change compression method with the -t/--type option.
957 You can change compression method with the -t/--type option.
958 The available compression methods are: none, bzip2, and
958 The available compression methods are: none, bzip2, and
959 gzip (by default, bundles are compressed using bzip2).
959 gzip (by default, bundles are compressed using bzip2).
960
960
961 The bundle file can then be transferred using conventional means
961 The bundle file can then be transferred using conventional means
962 and applied to another repository with the unbundle or pull
962 and applied to another repository with the unbundle or pull
963 command. This is useful when direct push and pull are not
963 command. This is useful when direct push and pull are not
964 available or when exporting an entire repository is undesirable.
964 available or when exporting an entire repository is undesirable.
965
965
966 Applying bundles preserves all changeset contents including
966 Applying bundles preserves all changeset contents including
967 permissions, copy/rename information, and revision history.
967 permissions, copy/rename information, and revision history.
968
968
969 Returns 0 on success, 1 if no changes found.
969 Returns 0 on success, 1 if no changes found.
970 """
970 """
971 revs = None
971 revs = None
972 if 'rev' in opts:
972 if 'rev' in opts:
973 revs = scmutil.revrange(repo, opts['rev'])
973 revs = scmutil.revrange(repo, opts['rev'])
974
974
975 if opts.get('all'):
975 if opts.get('all'):
976 base = ['null']
976 base = ['null']
977 else:
977 else:
978 base = scmutil.revrange(repo, opts.get('base'))
978 base = scmutil.revrange(repo, opts.get('base'))
979 if base:
979 if base:
980 if dest:
980 if dest:
981 raise util.Abort(_("--base is incompatible with specifying "
981 raise util.Abort(_("--base is incompatible with specifying "
982 "a destination"))
982 "a destination"))
983 common = [repo.lookup(rev) for rev in base]
983 common = [repo.lookup(rev) for rev in base]
984 heads = revs and map(repo.lookup, revs) or revs
984 heads = revs and map(repo.lookup, revs) or revs
985 cg = repo.getbundle('bundle', heads=heads, common=common)
985 cg = repo.getbundle('bundle', heads=heads, common=common)
986 outgoing = None
986 outgoing = None
987 else:
987 else:
988 dest = ui.expandpath(dest or 'default-push', dest or 'default')
988 dest = ui.expandpath(dest or 'default-push', dest or 'default')
989 dest, branches = hg.parseurl(dest, opts.get('branch'))
989 dest, branches = hg.parseurl(dest, opts.get('branch'))
990 other = hg.peer(repo, opts, dest)
990 other = hg.peer(repo, opts, dest)
991 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
991 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
992 heads = revs and map(repo.lookup, revs) or revs
992 heads = revs and map(repo.lookup, revs) or revs
993 outgoing = discovery.findcommonoutgoing(repo, other,
993 outgoing = discovery.findcommonoutgoing(repo, other,
994 onlyheads=heads,
994 onlyheads=heads,
995 force=opts.get('force'))
995 force=opts.get('force'))
996 cg = repo.getlocalbundle('bundle', outgoing)
996 cg = repo.getlocalbundle('bundle', outgoing)
997 if not cg:
997 if not cg:
998 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
998 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
999 return 1
999 return 1
1000
1000
1001 bundletype = opts.get('type', 'bzip2').lower()
1001 bundletype = opts.get('type', 'bzip2').lower()
1002 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1002 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1003 bundletype = btypes.get(bundletype)
1003 bundletype = btypes.get(bundletype)
1004 if bundletype not in changegroup.bundletypes:
1004 if bundletype not in changegroup.bundletypes:
1005 raise util.Abort(_('unknown bundle type specified with --type'))
1005 raise util.Abort(_('unknown bundle type specified with --type'))
1006
1006
1007 changegroup.writebundle(cg, fname, bundletype)
1007 changegroup.writebundle(cg, fname, bundletype)
1008
1008
1009 @command('cat',
1009 @command('cat',
1010 [('o', 'output', '',
1010 [('o', 'output', '',
1011 _('print output to file with formatted name'), _('FORMAT')),
1011 _('print output to file with formatted name'), _('FORMAT')),
1012 ('r', 'rev', '', _('print the given revision'), _('REV')),
1012 ('r', 'rev', '', _('print the given revision'), _('REV')),
1013 ('', 'decode', None, _('apply any matching decode filter')),
1013 ('', 'decode', None, _('apply any matching decode filter')),
1014 ] + walkopts,
1014 ] + walkopts,
1015 _('[OPTION]... FILE...'))
1015 _('[OPTION]... FILE...'))
1016 def cat(ui, repo, file1, *pats, **opts):
1016 def cat(ui, repo, file1, *pats, **opts):
1017 """output the current or given revision of files
1017 """output the current or given revision of files
1018
1018
1019 Print the specified files as they were at the given revision. If
1019 Print the specified files as they were at the given revision. If
1020 no revision is given, the parent of the working directory is used,
1020 no revision is given, the parent of the working directory is used,
1021 or tip if no revision is checked out.
1021 or tip if no revision is checked out.
1022
1022
1023 Output may be to a file, in which case the name of the file is
1023 Output may be to a file, in which case the name of the file is
1024 given using a format string. The formatting rules are the same as
1024 given using a format string. The formatting rules are the same as
1025 for the export command, with the following additions:
1025 for the export command, with the following additions:
1026
1026
1027 :``%s``: basename of file being printed
1027 :``%s``: basename of file being printed
1028 :``%d``: dirname of file being printed, or '.' if in repository root
1028 :``%d``: dirname of file being printed, or '.' if in repository root
1029 :``%p``: root-relative path name of file being printed
1029 :``%p``: root-relative path name of file being printed
1030
1030
1031 Returns 0 on success.
1031 Returns 0 on success.
1032 """
1032 """
1033 ctx = scmutil.revsingle(repo, opts.get('rev'))
1033 ctx = scmutil.revsingle(repo, opts.get('rev'))
1034 err = 1
1034 err = 1
1035 m = scmutil.match(ctx, (file1,) + pats, opts)
1035 m = scmutil.match(ctx, (file1,) + pats, opts)
1036 for abs in ctx.walk(m):
1036 for abs in ctx.walk(m):
1037 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1037 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1038 pathname=abs)
1038 pathname=abs)
1039 data = ctx[abs].data()
1039 data = ctx[abs].data()
1040 if opts.get('decode'):
1040 if opts.get('decode'):
1041 data = repo.wwritedata(abs, data)
1041 data = repo.wwritedata(abs, data)
1042 fp.write(data)
1042 fp.write(data)
1043 fp.close()
1043 fp.close()
1044 err = 0
1044 err = 0
1045 return err
1045 return err
1046
1046
1047 @command('^clone',
1047 @command('^clone',
1048 [('U', 'noupdate', None,
1048 [('U', 'noupdate', None,
1049 _('the clone will include an empty working copy (only a repository)')),
1049 _('the clone will include an empty working copy (only a repository)')),
1050 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1050 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1051 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1051 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1052 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1052 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1053 ('', 'pull', None, _('use pull protocol to copy metadata')),
1053 ('', 'pull', None, _('use pull protocol to copy metadata')),
1054 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1054 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1055 ] + remoteopts,
1055 ] + remoteopts,
1056 _('[OPTION]... SOURCE [DEST]'))
1056 _('[OPTION]... SOURCE [DEST]'))
1057 def clone(ui, source, dest=None, **opts):
1057 def clone(ui, source, dest=None, **opts):
1058 """make a copy of an existing repository
1058 """make a copy of an existing repository
1059
1059
1060 Create a copy of an existing repository in a new directory.
1060 Create a copy of an existing repository in a new directory.
1061
1061
1062 If no destination directory name is specified, it defaults to the
1062 If no destination directory name is specified, it defaults to the
1063 basename of the source.
1063 basename of the source.
1064
1064
1065 The location of the source is added to the new repository's
1065 The location of the source is added to the new repository's
1066 ``.hg/hgrc`` file, as the default to be used for future pulls.
1066 ``.hg/hgrc`` file, as the default to be used for future pulls.
1067
1067
1068 Only local paths and ``ssh://`` URLs are supported as
1068 Only local paths and ``ssh://`` URLs are supported as
1069 destinations. For ``ssh://`` destinations, no working directory or
1069 destinations. For ``ssh://`` destinations, no working directory or
1070 ``.hg/hgrc`` will be created on the remote side.
1070 ``.hg/hgrc`` will be created on the remote side.
1071
1071
1072 To pull only a subset of changesets, specify one or more revisions
1072 To pull only a subset of changesets, specify one or more revisions
1073 identifiers with -r/--rev or branches with -b/--branch. The
1073 identifiers with -r/--rev or branches with -b/--branch. The
1074 resulting clone will contain only the specified changesets and
1074 resulting clone will contain only the specified changesets and
1075 their ancestors. These options (or 'clone src#rev dest') imply
1075 their ancestors. These options (or 'clone src#rev dest') imply
1076 --pull, even for local source repositories. Note that specifying a
1076 --pull, even for local source repositories. Note that specifying a
1077 tag will include the tagged changeset but not the changeset
1077 tag will include the tagged changeset but not the changeset
1078 containing the tag.
1078 containing the tag.
1079
1079
1080 To check out a particular version, use -u/--update, or
1080 To check out a particular version, use -u/--update, or
1081 -U/--noupdate to create a clone with no working directory.
1081 -U/--noupdate to create a clone with no working directory.
1082
1082
1083 .. container:: verbose
1083 .. container:: verbose
1084
1084
1085 For efficiency, hardlinks are used for cloning whenever the
1085 For efficiency, hardlinks are used for cloning whenever the
1086 source and destination are on the same filesystem (note this
1086 source and destination are on the same filesystem (note this
1087 applies only to the repository data, not to the working
1087 applies only to the repository data, not to the working
1088 directory). Some filesystems, such as AFS, implement hardlinking
1088 directory). Some filesystems, such as AFS, implement hardlinking
1089 incorrectly, but do not report errors. In these cases, use the
1089 incorrectly, but do not report errors. In these cases, use the
1090 --pull option to avoid hardlinking.
1090 --pull option to avoid hardlinking.
1091
1091
1092 In some cases, you can clone repositories and the working
1092 In some cases, you can clone repositories and the working
1093 directory using full hardlinks with ::
1093 directory using full hardlinks with ::
1094
1094
1095 $ cp -al REPO REPOCLONE
1095 $ cp -al REPO REPOCLONE
1096
1096
1097 This is the fastest way to clone, but it is not always safe. The
1097 This is the fastest way to clone, but it is not always safe. The
1098 operation is not atomic (making sure REPO is not modified during
1098 operation is not atomic (making sure REPO is not modified during
1099 the operation is up to you) and you have to make sure your
1099 the operation is up to you) and you have to make sure your
1100 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1100 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1101 so). Also, this is not compatible with certain extensions that
1101 so). Also, this is not compatible with certain extensions that
1102 place their metadata under the .hg directory, such as mq.
1102 place their metadata under the .hg directory, such as mq.
1103
1103
1104 Mercurial will update the working directory to the first applicable
1104 Mercurial will update the working directory to the first applicable
1105 revision from this list:
1105 revision from this list:
1106
1106
1107 a) null if -U or the source repository has no changesets
1107 a) null if -U or the source repository has no changesets
1108 b) if -u . and the source repository is local, the first parent of
1108 b) if -u . and the source repository is local, the first parent of
1109 the source repository's working directory
1109 the source repository's working directory
1110 c) the changeset specified with -u (if a branch name, this means the
1110 c) the changeset specified with -u (if a branch name, this means the
1111 latest head of that branch)
1111 latest head of that branch)
1112 d) the changeset specified with -r
1112 d) the changeset specified with -r
1113 e) the tipmost head specified with -b
1113 e) the tipmost head specified with -b
1114 f) the tipmost head specified with the url#branch source syntax
1114 f) the tipmost head specified with the url#branch source syntax
1115 g) the tipmost head of the default branch
1115 g) the tipmost head of the default branch
1116 h) tip
1116 h) tip
1117
1117
1118 Examples:
1118 Examples:
1119
1119
1120 - clone a remote repository to a new directory named hg/::
1120 - clone a remote repository to a new directory named hg/::
1121
1121
1122 hg clone http://selenic.com/hg
1122 hg clone http://selenic.com/hg
1123
1123
1124 - create a lightweight local clone::
1124 - create a lightweight local clone::
1125
1125
1126 hg clone project/ project-feature/
1126 hg clone project/ project-feature/
1127
1127
1128 - clone from an absolute path on an ssh server (note double-slash)::
1128 - clone from an absolute path on an ssh server (note double-slash)::
1129
1129
1130 hg clone ssh://user@server//home/projects/alpha/
1130 hg clone ssh://user@server//home/projects/alpha/
1131
1131
1132 - do a high-speed clone over a LAN while checking out a
1132 - do a high-speed clone over a LAN while checking out a
1133 specified version::
1133 specified version::
1134
1134
1135 hg clone --uncompressed http://server/repo -u 1.5
1135 hg clone --uncompressed http://server/repo -u 1.5
1136
1136
1137 - create a repository without changesets after a particular revision::
1137 - create a repository without changesets after a particular revision::
1138
1138
1139 hg clone -r 04e544 experimental/ good/
1139 hg clone -r 04e544 experimental/ good/
1140
1140
1141 - clone (and track) a particular named branch::
1141 - clone (and track) a particular named branch::
1142
1142
1143 hg clone http://selenic.com/hg#stable
1143 hg clone http://selenic.com/hg#stable
1144
1144
1145 See :hg:`help urls` for details on specifying URLs.
1145 See :hg:`help urls` for details on specifying URLs.
1146
1146
1147 Returns 0 on success.
1147 Returns 0 on success.
1148 """
1148 """
1149 if opts.get('noupdate') and opts.get('updaterev'):
1149 if opts.get('noupdate') and opts.get('updaterev'):
1150 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1150 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1151
1151
1152 r = hg.clone(ui, opts, source, dest,
1152 r = hg.clone(ui, opts, source, dest,
1153 pull=opts.get('pull'),
1153 pull=opts.get('pull'),
1154 stream=opts.get('uncompressed'),
1154 stream=opts.get('uncompressed'),
1155 rev=opts.get('rev'),
1155 rev=opts.get('rev'),
1156 update=opts.get('updaterev') or not opts.get('noupdate'),
1156 update=opts.get('updaterev') or not opts.get('noupdate'),
1157 branch=opts.get('branch'))
1157 branch=opts.get('branch'))
1158
1158
1159 return r is None
1159 return r is None
1160
1160
1161 @command('^commit|ci',
1161 @command('^commit|ci',
1162 [('A', 'addremove', None,
1162 [('A', 'addremove', None,
1163 _('mark new/missing files as added/removed before committing')),
1163 _('mark new/missing files as added/removed before committing')),
1164 ('', 'close-branch', None,
1164 ('', 'close-branch', None,
1165 _('mark a branch as closed, hiding it from the branch list')),
1165 _('mark a branch as closed, hiding it from the branch list')),
1166 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1166 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1167 _('[OPTION]... [FILE]...'))
1167 _('[OPTION]... [FILE]...'))
1168 def commit(ui, repo, *pats, **opts):
1168 def commit(ui, repo, *pats, **opts):
1169 """commit the specified files or all outstanding changes
1169 """commit the specified files or all outstanding changes
1170
1170
1171 Commit changes to the given files into the repository. Unlike a
1171 Commit changes to the given files into the repository. Unlike a
1172 centralized SCM, this operation is a local operation. See
1172 centralized SCM, this operation is a local operation. See
1173 :hg:`push` for a way to actively distribute your changes.
1173 :hg:`push` for a way to actively distribute your changes.
1174
1174
1175 If a list of files is omitted, all changes reported by :hg:`status`
1175 If a list of files is omitted, all changes reported by :hg:`status`
1176 will be committed.
1176 will be committed.
1177
1177
1178 If you are committing the result of a merge, do not provide any
1178 If you are committing the result of a merge, do not provide any
1179 filenames or -I/-X filters.
1179 filenames or -I/-X filters.
1180
1180
1181 If no commit message is specified, Mercurial starts your
1181 If no commit message is specified, Mercurial starts your
1182 configured editor where you can enter a message. In case your
1182 configured editor where you can enter a message. In case your
1183 commit fails, you will find a backup of your message in
1183 commit fails, you will find a backup of your message in
1184 ``.hg/last-message.txt``.
1184 ``.hg/last-message.txt``.
1185
1185
1186 See :hg:`help dates` for a list of formats valid for -d/--date.
1186 See :hg:`help dates` for a list of formats valid for -d/--date.
1187
1187
1188 Returns 0 on success, 1 if nothing changed.
1188 Returns 0 on success, 1 if nothing changed.
1189 """
1189 """
1190 if opts.get('subrepos'):
1190 if opts.get('subrepos'):
1191 # Let --subrepos on the command line overide config setting.
1191 # Let --subrepos on the command line overide config setting.
1192 ui.setconfig('ui', 'commitsubrepos', True)
1192 ui.setconfig('ui', 'commitsubrepos', True)
1193
1193
1194 extra = {}
1194 extra = {}
1195 if opts.get('close_branch'):
1195 if opts.get('close_branch'):
1196 if repo['.'].node() not in repo.branchheads():
1196 if repo['.'].node() not in repo.branchheads():
1197 # The topo heads set is included in the branch heads set of the
1197 # The topo heads set is included in the branch heads set of the
1198 # current branch, so it's sufficient to test branchheads
1198 # current branch, so it's sufficient to test branchheads
1199 raise util.Abort(_('can only close branch heads'))
1199 raise util.Abort(_('can only close branch heads'))
1200 extra['close'] = 1
1200 extra['close'] = 1
1201 e = cmdutil.commiteditor
1201 e = cmdutil.commiteditor
1202 if opts.get('force_editor'):
1202 if opts.get('force_editor'):
1203 e = cmdutil.commitforceeditor
1203 e = cmdutil.commitforceeditor
1204
1204
1205 def commitfunc(ui, repo, message, match, opts):
1205 def commitfunc(ui, repo, message, match, opts):
1206 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1206 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1207 editor=e, extra=extra)
1207 editor=e, extra=extra)
1208
1208
1209 branch = repo[None].branch()
1209 branch = repo[None].branch()
1210 bheads = repo.branchheads(branch)
1210 bheads = repo.branchheads(branch)
1211
1211
1212 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1212 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1213 if not node:
1213 if not node:
1214 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1214 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1215 if stat[3]:
1215 if stat[3]:
1216 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1216 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1217 % len(stat[3]))
1217 % len(stat[3]))
1218 else:
1218 else:
1219 ui.status(_("nothing changed\n"))
1219 ui.status(_("nothing changed\n"))
1220 return 1
1220 return 1
1221
1221
1222 ctx = repo[node]
1222 ctx = repo[node]
1223 parents = ctx.parents()
1223 parents = ctx.parents()
1224
1224
1225 if (bheads and node not in bheads and not
1225 if (bheads and node not in bheads and not
1226 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1226 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1227 ui.status(_('created new head\n'))
1227 ui.status(_('created new head\n'))
1228 # The message is not printed for initial roots. For the other
1228 # The message is not printed for initial roots. For the other
1229 # changesets, it is printed in the following situations:
1229 # changesets, it is printed in the following situations:
1230 #
1230 #
1231 # Par column: for the 2 parents with ...
1231 # Par column: for the 2 parents with ...
1232 # N: null or no parent
1232 # N: null or no parent
1233 # B: parent is on another named branch
1233 # B: parent is on another named branch
1234 # C: parent is a regular non head changeset
1234 # C: parent is a regular non head changeset
1235 # H: parent was a branch head of the current branch
1235 # H: parent was a branch head of the current branch
1236 # Msg column: whether we print "created new head" message
1236 # Msg column: whether we print "created new head" message
1237 # In the following, it is assumed that there already exists some
1237 # In the following, it is assumed that there already exists some
1238 # initial branch heads of the current branch, otherwise nothing is
1238 # initial branch heads of the current branch, otherwise nothing is
1239 # printed anyway.
1239 # printed anyway.
1240 #
1240 #
1241 # Par Msg Comment
1241 # Par Msg Comment
1242 # NN y additional topo root
1242 # NN y additional topo root
1243 #
1243 #
1244 # BN y additional branch root
1244 # BN y additional branch root
1245 # CN y additional topo head
1245 # CN y additional topo head
1246 # HN n usual case
1246 # HN n usual case
1247 #
1247 #
1248 # BB y weird additional branch root
1248 # BB y weird additional branch root
1249 # CB y branch merge
1249 # CB y branch merge
1250 # HB n merge with named branch
1250 # HB n merge with named branch
1251 #
1251 #
1252 # CC y additional head from merge
1252 # CC y additional head from merge
1253 # CH n merge with a head
1253 # CH n merge with a head
1254 #
1254 #
1255 # HH n head merge: head count decreases
1255 # HH n head merge: head count decreases
1256
1256
1257 if not opts.get('close_branch'):
1257 if not opts.get('close_branch'):
1258 for r in parents:
1258 for r in parents:
1259 if r.extra().get('close') and r.branch() == branch:
1259 if r.extra().get('close') and r.branch() == branch:
1260 ui.status(_('reopening closed branch head %d\n') % r)
1260 ui.status(_('reopening closed branch head %d\n') % r)
1261
1261
1262 if ui.debugflag:
1262 if ui.debugflag:
1263 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1263 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1264 elif ui.verbose:
1264 elif ui.verbose:
1265 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1265 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1266
1266
1267 @command('copy|cp',
1267 @command('copy|cp',
1268 [('A', 'after', None, _('record a copy that has already occurred')),
1268 [('A', 'after', None, _('record a copy that has already occurred')),
1269 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1269 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1270 ] + walkopts + dryrunopts,
1270 ] + walkopts + dryrunopts,
1271 _('[OPTION]... [SOURCE]... DEST'))
1271 _('[OPTION]... [SOURCE]... DEST'))
1272 def copy(ui, repo, *pats, **opts):
1272 def copy(ui, repo, *pats, **opts):
1273 """mark files as copied for the next commit
1273 """mark files as copied for the next commit
1274
1274
1275 Mark dest as having copies of source files. If dest is a
1275 Mark dest as having copies of source files. If dest is a
1276 directory, copies are put in that directory. If dest is a file,
1276 directory, copies are put in that directory. If dest is a file,
1277 the source must be a single file.
1277 the source must be a single file.
1278
1278
1279 By default, this command copies the contents of files as they
1279 By default, this command copies the contents of files as they
1280 exist in the working directory. If invoked with -A/--after, the
1280 exist in the working directory. If invoked with -A/--after, the
1281 operation is recorded, but no copying is performed.
1281 operation is recorded, but no copying is performed.
1282
1282
1283 This command takes effect with the next commit. To undo a copy
1283 This command takes effect with the next commit. To undo a copy
1284 before that, see :hg:`revert`.
1284 before that, see :hg:`revert`.
1285
1285
1286 Returns 0 on success, 1 if errors are encountered.
1286 Returns 0 on success, 1 if errors are encountered.
1287 """
1287 """
1288 wlock = repo.wlock(False)
1288 wlock = repo.wlock(False)
1289 try:
1289 try:
1290 return cmdutil.copy(ui, repo, pats, opts)
1290 return cmdutil.copy(ui, repo, pats, opts)
1291 finally:
1291 finally:
1292 wlock.release()
1292 wlock.release()
1293
1293
1294 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1294 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1295 def debugancestor(ui, repo, *args):
1295 def debugancestor(ui, repo, *args):
1296 """find the ancestor revision of two revisions in a given index"""
1296 """find the ancestor revision of two revisions in a given index"""
1297 if len(args) == 3:
1297 if len(args) == 3:
1298 index, rev1, rev2 = args
1298 index, rev1, rev2 = args
1299 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1299 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1300 lookup = r.lookup
1300 lookup = r.lookup
1301 elif len(args) == 2:
1301 elif len(args) == 2:
1302 if not repo:
1302 if not repo:
1303 raise util.Abort(_("there is no Mercurial repository here "
1303 raise util.Abort(_("there is no Mercurial repository here "
1304 "(.hg not found)"))
1304 "(.hg not found)"))
1305 rev1, rev2 = args
1305 rev1, rev2 = args
1306 r = repo.changelog
1306 r = repo.changelog
1307 lookup = repo.lookup
1307 lookup = repo.lookup
1308 else:
1308 else:
1309 raise util.Abort(_('either two or three arguments required'))
1309 raise util.Abort(_('either two or three arguments required'))
1310 a = r.ancestor(lookup(rev1), lookup(rev2))
1310 a = r.ancestor(lookup(rev1), lookup(rev2))
1311 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1311 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1312
1312
1313 @command('debugbuilddag',
1313 @command('debugbuilddag',
1314 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1314 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1315 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1315 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1316 ('n', 'new-file', None, _('add new file at each rev'))],
1316 ('n', 'new-file', None, _('add new file at each rev'))],
1317 _('[OPTION]... [TEXT]'))
1317 _('[OPTION]... [TEXT]'))
1318 def debugbuilddag(ui, repo, text=None,
1318 def debugbuilddag(ui, repo, text=None,
1319 mergeable_file=False,
1319 mergeable_file=False,
1320 overwritten_file=False,
1320 overwritten_file=False,
1321 new_file=False):
1321 new_file=False):
1322 """builds a repo with a given DAG from scratch in the current empty repo
1322 """builds a repo with a given DAG from scratch in the current empty repo
1323
1323
1324 The description of the DAG is read from stdin if not given on the
1324 The description of the DAG is read from stdin if not given on the
1325 command line.
1325 command line.
1326
1326
1327 Elements:
1327 Elements:
1328
1328
1329 - "+n" is a linear run of n nodes based on the current default parent
1329 - "+n" is a linear run of n nodes based on the current default parent
1330 - "." is a single node based on the current default parent
1330 - "." is a single node based on the current default parent
1331 - "$" resets the default parent to null (implied at the start);
1331 - "$" resets the default parent to null (implied at the start);
1332 otherwise the default parent is always the last node created
1332 otherwise the default parent is always the last node created
1333 - "<p" sets the default parent to the backref p
1333 - "<p" sets the default parent to the backref p
1334 - "*p" is a fork at parent p, which is a backref
1334 - "*p" is a fork at parent p, which is a backref
1335 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1335 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1336 - "/p2" is a merge of the preceding node and p2
1336 - "/p2" is a merge of the preceding node and p2
1337 - ":tag" defines a local tag for the preceding node
1337 - ":tag" defines a local tag for the preceding node
1338 - "@branch" sets the named branch for subsequent nodes
1338 - "@branch" sets the named branch for subsequent nodes
1339 - "#...\\n" is a comment up to the end of the line
1339 - "#...\\n" is a comment up to the end of the line
1340
1340
1341 Whitespace between the above elements is ignored.
1341 Whitespace between the above elements is ignored.
1342
1342
1343 A backref is either
1343 A backref is either
1344
1344
1345 - a number n, which references the node curr-n, where curr is the current
1345 - a number n, which references the node curr-n, where curr is the current
1346 node, or
1346 node, or
1347 - the name of a local tag you placed earlier using ":tag", or
1347 - the name of a local tag you placed earlier using ":tag", or
1348 - empty to denote the default parent.
1348 - empty to denote the default parent.
1349
1349
1350 All string valued-elements are either strictly alphanumeric, or must
1350 All string valued-elements are either strictly alphanumeric, or must
1351 be enclosed in double quotes ("..."), with "\\" as escape character.
1351 be enclosed in double quotes ("..."), with "\\" as escape character.
1352 """
1352 """
1353
1353
1354 if text is None:
1354 if text is None:
1355 ui.status(_("reading DAG from stdin\n"))
1355 ui.status(_("reading DAG from stdin\n"))
1356 text = ui.fin.read()
1356 text = ui.fin.read()
1357
1357
1358 cl = repo.changelog
1358 cl = repo.changelog
1359 if len(cl) > 0:
1359 if len(cl) > 0:
1360 raise util.Abort(_('repository is not empty'))
1360 raise util.Abort(_('repository is not empty'))
1361
1361
1362 # determine number of revs in DAG
1362 # determine number of revs in DAG
1363 total = 0
1363 total = 0
1364 for type, data in dagparser.parsedag(text):
1364 for type, data in dagparser.parsedag(text):
1365 if type == 'n':
1365 if type == 'n':
1366 total += 1
1366 total += 1
1367
1367
1368 if mergeable_file:
1368 if mergeable_file:
1369 linesperrev = 2
1369 linesperrev = 2
1370 # make a file with k lines per rev
1370 # make a file with k lines per rev
1371 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1371 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1372 initialmergedlines.append("")
1372 initialmergedlines.append("")
1373
1373
1374 tags = []
1374 tags = []
1375
1375
1376 lock = tr = None
1376 lock = tr = None
1377 try:
1377 try:
1378 lock = repo.lock()
1378 lock = repo.lock()
1379 tr = repo.transaction("builddag")
1379 tr = repo.transaction("builddag")
1380
1380
1381 at = -1
1381 at = -1
1382 atbranch = 'default'
1382 atbranch = 'default'
1383 nodeids = []
1383 nodeids = []
1384 id = 0
1384 id = 0
1385 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1385 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1386 for type, data in dagparser.parsedag(text):
1386 for type, data in dagparser.parsedag(text):
1387 if type == 'n':
1387 if type == 'n':
1388 ui.note('node %s\n' % str(data))
1388 ui.note('node %s\n' % str(data))
1389 id, ps = data
1389 id, ps = data
1390
1390
1391 files = []
1391 files = []
1392 fctxs = {}
1392 fctxs = {}
1393
1393
1394 p2 = None
1394 p2 = None
1395 if mergeable_file:
1395 if mergeable_file:
1396 fn = "mf"
1396 fn = "mf"
1397 p1 = repo[ps[0]]
1397 p1 = repo[ps[0]]
1398 if len(ps) > 1:
1398 if len(ps) > 1:
1399 p2 = repo[ps[1]]
1399 p2 = repo[ps[1]]
1400 pa = p1.ancestor(p2)
1400 pa = p1.ancestor(p2)
1401 base, local, other = [x[fn].data() for x in pa, p1, p2]
1401 base, local, other = [x[fn].data() for x in pa, p1, p2]
1402 m3 = simplemerge.Merge3Text(base, local, other)
1402 m3 = simplemerge.Merge3Text(base, local, other)
1403 ml = [l.strip() for l in m3.merge_lines()]
1403 ml = [l.strip() for l in m3.merge_lines()]
1404 ml.append("")
1404 ml.append("")
1405 elif at > 0:
1405 elif at > 0:
1406 ml = p1[fn].data().split("\n")
1406 ml = p1[fn].data().split("\n")
1407 else:
1407 else:
1408 ml = initialmergedlines
1408 ml = initialmergedlines
1409 ml[id * linesperrev] += " r%i" % id
1409 ml[id * linesperrev] += " r%i" % id
1410 mergedtext = "\n".join(ml)
1410 mergedtext = "\n".join(ml)
1411 files.append(fn)
1411 files.append(fn)
1412 fctxs[fn] = context.memfilectx(fn, mergedtext)
1412 fctxs[fn] = context.memfilectx(fn, mergedtext)
1413
1413
1414 if overwritten_file:
1414 if overwritten_file:
1415 fn = "of"
1415 fn = "of"
1416 files.append(fn)
1416 files.append(fn)
1417 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1417 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1418
1418
1419 if new_file:
1419 if new_file:
1420 fn = "nf%i" % id
1420 fn = "nf%i" % id
1421 files.append(fn)
1421 files.append(fn)
1422 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1422 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1423 if len(ps) > 1:
1423 if len(ps) > 1:
1424 if not p2:
1424 if not p2:
1425 p2 = repo[ps[1]]
1425 p2 = repo[ps[1]]
1426 for fn in p2:
1426 for fn in p2:
1427 if fn.startswith("nf"):
1427 if fn.startswith("nf"):
1428 files.append(fn)
1428 files.append(fn)
1429 fctxs[fn] = p2[fn]
1429 fctxs[fn] = p2[fn]
1430
1430
1431 def fctxfn(repo, cx, path):
1431 def fctxfn(repo, cx, path):
1432 return fctxs.get(path)
1432 return fctxs.get(path)
1433
1433
1434 if len(ps) == 0 or ps[0] < 0:
1434 if len(ps) == 0 or ps[0] < 0:
1435 pars = [None, None]
1435 pars = [None, None]
1436 elif len(ps) == 1:
1436 elif len(ps) == 1:
1437 pars = [nodeids[ps[0]], None]
1437 pars = [nodeids[ps[0]], None]
1438 else:
1438 else:
1439 pars = [nodeids[p] for p in ps]
1439 pars = [nodeids[p] for p in ps]
1440 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1440 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1441 date=(id, 0),
1441 date=(id, 0),
1442 user="debugbuilddag",
1442 user="debugbuilddag",
1443 extra={'branch': atbranch})
1443 extra={'branch': atbranch})
1444 nodeid = repo.commitctx(cx)
1444 nodeid = repo.commitctx(cx)
1445 nodeids.append(nodeid)
1445 nodeids.append(nodeid)
1446 at = id
1446 at = id
1447 elif type == 'l':
1447 elif type == 'l':
1448 id, name = data
1448 id, name = data
1449 ui.note('tag %s\n' % name)
1449 ui.note('tag %s\n' % name)
1450 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1450 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1451 elif type == 'a':
1451 elif type == 'a':
1452 ui.note('branch %s\n' % data)
1452 ui.note('branch %s\n' % data)
1453 atbranch = data
1453 atbranch = data
1454 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1454 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1455 tr.close()
1455 tr.close()
1456
1456
1457 if tags:
1457 if tags:
1458 repo.opener.write("localtags", "".join(tags))
1458 repo.opener.write("localtags", "".join(tags))
1459 finally:
1459 finally:
1460 ui.progress(_('building'), None)
1460 ui.progress(_('building'), None)
1461 release(tr, lock)
1461 release(tr, lock)
1462
1462
1463 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1463 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1464 def debugbundle(ui, bundlepath, all=None, **opts):
1464 def debugbundle(ui, bundlepath, all=None, **opts):
1465 """lists the contents of a bundle"""
1465 """lists the contents of a bundle"""
1466 f = url.open(ui, bundlepath)
1466 f = url.open(ui, bundlepath)
1467 try:
1467 try:
1468 gen = changegroup.readbundle(f, bundlepath)
1468 gen = changegroup.readbundle(f, bundlepath)
1469 if all:
1469 if all:
1470 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1470 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1471
1471
1472 def showchunks(named):
1472 def showchunks(named):
1473 ui.write("\n%s\n" % named)
1473 ui.write("\n%s\n" % named)
1474 chain = None
1474 chain = None
1475 while True:
1475 while True:
1476 chunkdata = gen.deltachunk(chain)
1476 chunkdata = gen.deltachunk(chain)
1477 if not chunkdata:
1477 if not chunkdata:
1478 break
1478 break
1479 node = chunkdata['node']
1479 node = chunkdata['node']
1480 p1 = chunkdata['p1']
1480 p1 = chunkdata['p1']
1481 p2 = chunkdata['p2']
1481 p2 = chunkdata['p2']
1482 cs = chunkdata['cs']
1482 cs = chunkdata['cs']
1483 deltabase = chunkdata['deltabase']
1483 deltabase = chunkdata['deltabase']
1484 delta = chunkdata['delta']
1484 delta = chunkdata['delta']
1485 ui.write("%s %s %s %s %s %s\n" %
1485 ui.write("%s %s %s %s %s %s\n" %
1486 (hex(node), hex(p1), hex(p2),
1486 (hex(node), hex(p1), hex(p2),
1487 hex(cs), hex(deltabase), len(delta)))
1487 hex(cs), hex(deltabase), len(delta)))
1488 chain = node
1488 chain = node
1489
1489
1490 chunkdata = gen.changelogheader()
1490 chunkdata = gen.changelogheader()
1491 showchunks("changelog")
1491 showchunks("changelog")
1492 chunkdata = gen.manifestheader()
1492 chunkdata = gen.manifestheader()
1493 showchunks("manifest")
1493 showchunks("manifest")
1494 while True:
1494 while True:
1495 chunkdata = gen.filelogheader()
1495 chunkdata = gen.filelogheader()
1496 if not chunkdata:
1496 if not chunkdata:
1497 break
1497 break
1498 fname = chunkdata['filename']
1498 fname = chunkdata['filename']
1499 showchunks(fname)
1499 showchunks(fname)
1500 else:
1500 else:
1501 chunkdata = gen.changelogheader()
1501 chunkdata = gen.changelogheader()
1502 chain = None
1502 chain = None
1503 while True:
1503 while True:
1504 chunkdata = gen.deltachunk(chain)
1504 chunkdata = gen.deltachunk(chain)
1505 if not chunkdata:
1505 if not chunkdata:
1506 break
1506 break
1507 node = chunkdata['node']
1507 node = chunkdata['node']
1508 ui.write("%s\n" % hex(node))
1508 ui.write("%s\n" % hex(node))
1509 chain = node
1509 chain = node
1510 finally:
1510 finally:
1511 f.close()
1511 f.close()
1512
1512
1513 @command('debugcheckstate', [], '')
1513 @command('debugcheckstate', [], '')
1514 def debugcheckstate(ui, repo):
1514 def debugcheckstate(ui, repo):
1515 """validate the correctness of the current dirstate"""
1515 """validate the correctness of the current dirstate"""
1516 parent1, parent2 = repo.dirstate.parents()
1516 parent1, parent2 = repo.dirstate.parents()
1517 m1 = repo[parent1].manifest()
1517 m1 = repo[parent1].manifest()
1518 m2 = repo[parent2].manifest()
1518 m2 = repo[parent2].manifest()
1519 errors = 0
1519 errors = 0
1520 for f in repo.dirstate:
1520 for f in repo.dirstate:
1521 state = repo.dirstate[f]
1521 state = repo.dirstate[f]
1522 if state in "nr" and f not in m1:
1522 if state in "nr" and f not in m1:
1523 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1523 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1524 errors += 1
1524 errors += 1
1525 if state in "a" and f in m1:
1525 if state in "a" and f in m1:
1526 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1526 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1527 errors += 1
1527 errors += 1
1528 if state in "m" and f not in m1 and f not in m2:
1528 if state in "m" and f not in m1 and f not in m2:
1529 ui.warn(_("%s in state %s, but not in either manifest\n") %
1529 ui.warn(_("%s in state %s, but not in either manifest\n") %
1530 (f, state))
1530 (f, state))
1531 errors += 1
1531 errors += 1
1532 for f in m1:
1532 for f in m1:
1533 state = repo.dirstate[f]
1533 state = repo.dirstate[f]
1534 if state not in "nrm":
1534 if state not in "nrm":
1535 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1535 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1536 errors += 1
1536 errors += 1
1537 if errors:
1537 if errors:
1538 error = _(".hg/dirstate inconsistent with current parent's manifest")
1538 error = _(".hg/dirstate inconsistent with current parent's manifest")
1539 raise util.Abort(error)
1539 raise util.Abort(error)
1540
1540
1541 @command('debugcommands', [], _('[COMMAND]'))
1541 @command('debugcommands', [], _('[COMMAND]'))
1542 def debugcommands(ui, cmd='', *args):
1542 def debugcommands(ui, cmd='', *args):
1543 """list all available commands and options"""
1543 """list all available commands and options"""
1544 for cmd, vals in sorted(table.iteritems()):
1544 for cmd, vals in sorted(table.iteritems()):
1545 cmd = cmd.split('|')[0].strip('^')
1545 cmd = cmd.split('|')[0].strip('^')
1546 opts = ', '.join([i[1] for i in vals[1]])
1546 opts = ', '.join([i[1] for i in vals[1]])
1547 ui.write('%s: %s\n' % (cmd, opts))
1547 ui.write('%s: %s\n' % (cmd, opts))
1548
1548
1549 @command('debugcomplete',
1549 @command('debugcomplete',
1550 [('o', 'options', None, _('show the command options'))],
1550 [('o', 'options', None, _('show the command options'))],
1551 _('[-o] CMD'))
1551 _('[-o] CMD'))
1552 def debugcomplete(ui, cmd='', **opts):
1552 def debugcomplete(ui, cmd='', **opts):
1553 """returns the completion list associated with the given command"""
1553 """returns the completion list associated with the given command"""
1554
1554
1555 if opts.get('options'):
1555 if opts.get('options'):
1556 options = []
1556 options = []
1557 otables = [globalopts]
1557 otables = [globalopts]
1558 if cmd:
1558 if cmd:
1559 aliases, entry = cmdutil.findcmd(cmd, table, False)
1559 aliases, entry = cmdutil.findcmd(cmd, table, False)
1560 otables.append(entry[1])
1560 otables.append(entry[1])
1561 for t in otables:
1561 for t in otables:
1562 for o in t:
1562 for o in t:
1563 if "(DEPRECATED)" in o[3]:
1563 if "(DEPRECATED)" in o[3]:
1564 continue
1564 continue
1565 if o[0]:
1565 if o[0]:
1566 options.append('-%s' % o[0])
1566 options.append('-%s' % o[0])
1567 options.append('--%s' % o[1])
1567 options.append('--%s' % o[1])
1568 ui.write("%s\n" % "\n".join(options))
1568 ui.write("%s\n" % "\n".join(options))
1569 return
1569 return
1570
1570
1571 cmdlist = cmdutil.findpossible(cmd, table)
1571 cmdlist = cmdutil.findpossible(cmd, table)
1572 if ui.verbose:
1572 if ui.verbose:
1573 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1573 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1574 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1574 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1575
1575
1576 @command('debugdag',
1576 @command('debugdag',
1577 [('t', 'tags', None, _('use tags as labels')),
1577 [('t', 'tags', None, _('use tags as labels')),
1578 ('b', 'branches', None, _('annotate with branch names')),
1578 ('b', 'branches', None, _('annotate with branch names')),
1579 ('', 'dots', None, _('use dots for runs')),
1579 ('', 'dots', None, _('use dots for runs')),
1580 ('s', 'spaces', None, _('separate elements by spaces'))],
1580 ('s', 'spaces', None, _('separate elements by spaces'))],
1581 _('[OPTION]... [FILE [REV]...]'))
1581 _('[OPTION]... [FILE [REV]...]'))
1582 def debugdag(ui, repo, file_=None, *revs, **opts):
1582 def debugdag(ui, repo, file_=None, *revs, **opts):
1583 """format the changelog or an index DAG as a concise textual description
1583 """format the changelog or an index DAG as a concise textual description
1584
1584
1585 If you pass a revlog index, the revlog's DAG is emitted. If you list
1585 If you pass a revlog index, the revlog's DAG is emitted. If you list
1586 revision numbers, they get labelled in the output as rN.
1586 revision numbers, they get labelled in the output as rN.
1587
1587
1588 Otherwise, the changelog DAG of the current repo is emitted.
1588 Otherwise, the changelog DAG of the current repo is emitted.
1589 """
1589 """
1590 spaces = opts.get('spaces')
1590 spaces = opts.get('spaces')
1591 dots = opts.get('dots')
1591 dots = opts.get('dots')
1592 if file_:
1592 if file_:
1593 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1593 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1594 revs = set((int(r) for r in revs))
1594 revs = set((int(r) for r in revs))
1595 def events():
1595 def events():
1596 for r in rlog:
1596 for r in rlog:
1597 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1597 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1598 if r in revs:
1598 if r in revs:
1599 yield 'l', (r, "r%i" % r)
1599 yield 'l', (r, "r%i" % r)
1600 elif repo:
1600 elif repo:
1601 cl = repo.changelog
1601 cl = repo.changelog
1602 tags = opts.get('tags')
1602 tags = opts.get('tags')
1603 branches = opts.get('branches')
1603 branches = opts.get('branches')
1604 if tags:
1604 if tags:
1605 labels = {}
1605 labels = {}
1606 for l, n in repo.tags().items():
1606 for l, n in repo.tags().items():
1607 labels.setdefault(cl.rev(n), []).append(l)
1607 labels.setdefault(cl.rev(n), []).append(l)
1608 def events():
1608 def events():
1609 b = "default"
1609 b = "default"
1610 for r in cl:
1610 for r in cl:
1611 if branches:
1611 if branches:
1612 newb = cl.read(cl.node(r))[5]['branch']
1612 newb = cl.read(cl.node(r))[5]['branch']
1613 if newb != b:
1613 if newb != b:
1614 yield 'a', newb
1614 yield 'a', newb
1615 b = newb
1615 b = newb
1616 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1616 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1617 if tags:
1617 if tags:
1618 ls = labels.get(r)
1618 ls = labels.get(r)
1619 if ls:
1619 if ls:
1620 for l in ls:
1620 for l in ls:
1621 yield 'l', (r, l)
1621 yield 'l', (r, l)
1622 else:
1622 else:
1623 raise util.Abort(_('need repo for changelog dag'))
1623 raise util.Abort(_('need repo for changelog dag'))
1624
1624
1625 for line in dagparser.dagtextlines(events(),
1625 for line in dagparser.dagtextlines(events(),
1626 addspaces=spaces,
1626 addspaces=spaces,
1627 wraplabels=True,
1627 wraplabels=True,
1628 wrapannotations=True,
1628 wrapannotations=True,
1629 wrapnonlinear=dots,
1629 wrapnonlinear=dots,
1630 usedots=dots,
1630 usedots=dots,
1631 maxlinewidth=70):
1631 maxlinewidth=70):
1632 ui.write(line)
1632 ui.write(line)
1633 ui.write("\n")
1633 ui.write("\n")
1634
1634
1635 @command('debugdata',
1635 @command('debugdata',
1636 [('c', 'changelog', False, _('open changelog')),
1636 [('c', 'changelog', False, _('open changelog')),
1637 ('m', 'manifest', False, _('open manifest'))],
1637 ('m', 'manifest', False, _('open manifest'))],
1638 _('-c|-m|FILE REV'))
1638 _('-c|-m|FILE REV'))
1639 def debugdata(ui, repo, file_, rev = None, **opts):
1639 def debugdata(ui, repo, file_, rev = None, **opts):
1640 """dump the contents of a data file revision"""
1640 """dump the contents of a data file revision"""
1641 if opts.get('changelog') or opts.get('manifest'):
1641 if opts.get('changelog') or opts.get('manifest'):
1642 file_, rev = None, file_
1642 file_, rev = None, file_
1643 elif rev is None:
1643 elif rev is None:
1644 raise error.CommandError('debugdata', _('invalid arguments'))
1644 raise error.CommandError('debugdata', _('invalid arguments'))
1645 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1645 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1646 try:
1646 try:
1647 ui.write(r.revision(r.lookup(rev)))
1647 ui.write(r.revision(r.lookup(rev)))
1648 except KeyError:
1648 except KeyError:
1649 raise util.Abort(_('invalid revision identifier %s') % rev)
1649 raise util.Abort(_('invalid revision identifier %s') % rev)
1650
1650
1651 @command('debugdate',
1651 @command('debugdate',
1652 [('e', 'extended', None, _('try extended date formats'))],
1652 [('e', 'extended', None, _('try extended date formats'))],
1653 _('[-e] DATE [RANGE]'))
1653 _('[-e] DATE [RANGE]'))
1654 def debugdate(ui, date, range=None, **opts):
1654 def debugdate(ui, date, range=None, **opts):
1655 """parse and display a date"""
1655 """parse and display a date"""
1656 if opts["extended"]:
1656 if opts["extended"]:
1657 d = util.parsedate(date, util.extendeddateformats)
1657 d = util.parsedate(date, util.extendeddateformats)
1658 else:
1658 else:
1659 d = util.parsedate(date)
1659 d = util.parsedate(date)
1660 ui.write("internal: %s %s\n" % d)
1660 ui.write("internal: %s %s\n" % d)
1661 ui.write("standard: %s\n" % util.datestr(d))
1661 ui.write("standard: %s\n" % util.datestr(d))
1662 if range:
1662 if range:
1663 m = util.matchdate(range)
1663 m = util.matchdate(range)
1664 ui.write("match: %s\n" % m(d[0]))
1664 ui.write("match: %s\n" % m(d[0]))
1665
1665
1666 @command('debugdiscovery',
1666 @command('debugdiscovery',
1667 [('', 'old', None, _('use old-style discovery')),
1667 [('', 'old', None, _('use old-style discovery')),
1668 ('', 'nonheads', None,
1668 ('', 'nonheads', None,
1669 _('use old-style discovery with non-heads included')),
1669 _('use old-style discovery with non-heads included')),
1670 ] + remoteopts,
1670 ] + remoteopts,
1671 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1671 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1672 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1672 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1673 """runs the changeset discovery protocol in isolation"""
1673 """runs the changeset discovery protocol in isolation"""
1674 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1674 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1675 remote = hg.peer(repo, opts, remoteurl)
1675 remote = hg.peer(repo, opts, remoteurl)
1676 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1676 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1677
1677
1678 # make sure tests are repeatable
1678 # make sure tests are repeatable
1679 random.seed(12323)
1679 random.seed(12323)
1680
1680
1681 def doit(localheads, remoteheads):
1681 def doit(localheads, remoteheads):
1682 if opts.get('old'):
1682 if opts.get('old'):
1683 if localheads:
1683 if localheads:
1684 raise util.Abort('cannot use localheads with old style discovery')
1684 raise util.Abort('cannot use localheads with old style discovery')
1685 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1685 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1686 force=True)
1686 force=True)
1687 common = set(common)
1687 common = set(common)
1688 if not opts.get('nonheads'):
1688 if not opts.get('nonheads'):
1689 ui.write("unpruned common: %s\n" % " ".join([short(n)
1689 ui.write("unpruned common: %s\n" % " ".join([short(n)
1690 for n in common]))
1690 for n in common]))
1691 dag = dagutil.revlogdag(repo.changelog)
1691 dag = dagutil.revlogdag(repo.changelog)
1692 all = dag.ancestorset(dag.internalizeall(common))
1692 all = dag.ancestorset(dag.internalizeall(common))
1693 common = dag.externalizeall(dag.headsetofconnecteds(all))
1693 common = dag.externalizeall(dag.headsetofconnecteds(all))
1694 else:
1694 else:
1695 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1695 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1696 common = set(common)
1696 common = set(common)
1697 rheads = set(hds)
1697 rheads = set(hds)
1698 lheads = set(repo.heads())
1698 lheads = set(repo.heads())
1699 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1699 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1700 if lheads <= common:
1700 if lheads <= common:
1701 ui.write("local is subset\n")
1701 ui.write("local is subset\n")
1702 elif rheads <= common:
1702 elif rheads <= common:
1703 ui.write("remote is subset\n")
1703 ui.write("remote is subset\n")
1704
1704
1705 serverlogs = opts.get('serverlog')
1705 serverlogs = opts.get('serverlog')
1706 if serverlogs:
1706 if serverlogs:
1707 for filename in serverlogs:
1707 for filename in serverlogs:
1708 logfile = open(filename, 'r')
1708 logfile = open(filename, 'r')
1709 try:
1709 try:
1710 line = logfile.readline()
1710 line = logfile.readline()
1711 while line:
1711 while line:
1712 parts = line.strip().split(';')
1712 parts = line.strip().split(';')
1713 op = parts[1]
1713 op = parts[1]
1714 if op == 'cg':
1714 if op == 'cg':
1715 pass
1715 pass
1716 elif op == 'cgss':
1716 elif op == 'cgss':
1717 doit(parts[2].split(' '), parts[3].split(' '))
1717 doit(parts[2].split(' '), parts[3].split(' '))
1718 elif op == 'unb':
1718 elif op == 'unb':
1719 doit(parts[3].split(' '), parts[2].split(' '))
1719 doit(parts[3].split(' '), parts[2].split(' '))
1720 line = logfile.readline()
1720 line = logfile.readline()
1721 finally:
1721 finally:
1722 logfile.close()
1722 logfile.close()
1723
1723
1724 else:
1724 else:
1725 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1725 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1726 opts.get('remote_head'))
1726 opts.get('remote_head'))
1727 localrevs = opts.get('local_head')
1727 localrevs = opts.get('local_head')
1728 doit(localrevs, remoterevs)
1728 doit(localrevs, remoterevs)
1729
1729
1730 @command('debugfileset', [], ('REVSPEC'))
1730 @command('debugfileset', [], ('REVSPEC'))
1731 def debugfileset(ui, repo, expr):
1731 def debugfileset(ui, repo, expr):
1732 '''parse and apply a fileset specification'''
1732 '''parse and apply a fileset specification'''
1733 if ui.verbose:
1733 if ui.verbose:
1734 tree = fileset.parse(expr)[0]
1734 tree = fileset.parse(expr)[0]
1735 ui.note(tree, "\n")
1735 ui.note(tree, "\n")
1736
1736
1737 for f in fileset.getfileset(repo[None], expr):
1737 for f in fileset.getfileset(repo[None], expr):
1738 ui.write("%s\n" % f)
1738 ui.write("%s\n" % f)
1739
1739
1740 @command('debugfsinfo', [], _('[PATH]'))
1740 @command('debugfsinfo', [], _('[PATH]'))
1741 def debugfsinfo(ui, path = "."):
1741 def debugfsinfo(ui, path = "."):
1742 """show information detected about current filesystem"""
1742 """show information detected about current filesystem"""
1743 util.writefile('.debugfsinfo', '')
1743 util.writefile('.debugfsinfo', '')
1744 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1744 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1745 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1745 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1746 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1746 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1747 and 'yes' or 'no'))
1747 and 'yes' or 'no'))
1748 os.unlink('.debugfsinfo')
1748 os.unlink('.debugfsinfo')
1749
1749
1750 @command('debuggetbundle',
1750 @command('debuggetbundle',
1751 [('H', 'head', [], _('id of head node'), _('ID')),
1751 [('H', 'head', [], _('id of head node'), _('ID')),
1752 ('C', 'common', [], _('id of common node'), _('ID')),
1752 ('C', 'common', [], _('id of common node'), _('ID')),
1753 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1753 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1754 _('REPO FILE [-H|-C ID]...'))
1754 _('REPO FILE [-H|-C ID]...'))
1755 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1755 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1756 """retrieves a bundle from a repo
1756 """retrieves a bundle from a repo
1757
1757
1758 Every ID must be a full-length hex node id string. Saves the bundle to the
1758 Every ID must be a full-length hex node id string. Saves the bundle to the
1759 given file.
1759 given file.
1760 """
1760 """
1761 repo = hg.peer(ui, opts, repopath)
1761 repo = hg.peer(ui, opts, repopath)
1762 if not repo.capable('getbundle'):
1762 if not repo.capable('getbundle'):
1763 raise util.Abort("getbundle() not supported by target repository")
1763 raise util.Abort("getbundle() not supported by target repository")
1764 args = {}
1764 args = {}
1765 if common:
1765 if common:
1766 args['common'] = [bin(s) for s in common]
1766 args['common'] = [bin(s) for s in common]
1767 if head:
1767 if head:
1768 args['heads'] = [bin(s) for s in head]
1768 args['heads'] = [bin(s) for s in head]
1769 bundle = repo.getbundle('debug', **args)
1769 bundle = repo.getbundle('debug', **args)
1770
1770
1771 bundletype = opts.get('type', 'bzip2').lower()
1771 bundletype = opts.get('type', 'bzip2').lower()
1772 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1772 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1773 bundletype = btypes.get(bundletype)
1773 bundletype = btypes.get(bundletype)
1774 if bundletype not in changegroup.bundletypes:
1774 if bundletype not in changegroup.bundletypes:
1775 raise util.Abort(_('unknown bundle type specified with --type'))
1775 raise util.Abort(_('unknown bundle type specified with --type'))
1776 changegroup.writebundle(bundle, bundlepath, bundletype)
1776 changegroup.writebundle(bundle, bundlepath, bundletype)
1777
1777
1778 @command('debugignore', [], '')
1778 @command('debugignore', [], '')
1779 def debugignore(ui, repo, *values, **opts):
1779 def debugignore(ui, repo, *values, **opts):
1780 """display the combined ignore pattern"""
1780 """display the combined ignore pattern"""
1781 ignore = repo.dirstate._ignore
1781 ignore = repo.dirstate._ignore
1782 includepat = getattr(ignore, 'includepat', None)
1782 includepat = getattr(ignore, 'includepat', None)
1783 if includepat is not None:
1783 if includepat is not None:
1784 ui.write("%s\n" % includepat)
1784 ui.write("%s\n" % includepat)
1785 else:
1785 else:
1786 raise util.Abort(_("no ignore patterns found"))
1786 raise util.Abort(_("no ignore patterns found"))
1787
1787
1788 @command('debugindex',
1788 @command('debugindex',
1789 [('c', 'changelog', False, _('open changelog')),
1789 [('c', 'changelog', False, _('open changelog')),
1790 ('m', 'manifest', False, _('open manifest')),
1790 ('m', 'manifest', False, _('open manifest')),
1791 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1791 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1792 _('[-f FORMAT] -c|-m|FILE'))
1792 _('[-f FORMAT] -c|-m|FILE'))
1793 def debugindex(ui, repo, file_ = None, **opts):
1793 def debugindex(ui, repo, file_ = None, **opts):
1794 """dump the contents of an index file"""
1794 """dump the contents of an index file"""
1795 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1795 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1796 format = opts.get('format', 0)
1796 format = opts.get('format', 0)
1797 if format not in (0, 1):
1797 if format not in (0, 1):
1798 raise util.Abort(_("unknown format %d") % format)
1798 raise util.Abort(_("unknown format %d") % format)
1799
1799
1800 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1800 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1801 if generaldelta:
1801 if generaldelta:
1802 basehdr = ' delta'
1802 basehdr = ' delta'
1803 else:
1803 else:
1804 basehdr = ' base'
1804 basehdr = ' base'
1805
1805
1806 if format == 0:
1806 if format == 0:
1807 ui.write(" rev offset length " + basehdr + " linkrev"
1807 ui.write(" rev offset length " + basehdr + " linkrev"
1808 " nodeid p1 p2\n")
1808 " nodeid p1 p2\n")
1809 elif format == 1:
1809 elif format == 1:
1810 ui.write(" rev flag offset length"
1810 ui.write(" rev flag offset length"
1811 " size " + basehdr + " link p1 p2 nodeid\n")
1811 " size " + basehdr + " link p1 p2 nodeid\n")
1812
1812
1813 for i in r:
1813 for i in r:
1814 node = r.node(i)
1814 node = r.node(i)
1815 if generaldelta:
1815 if generaldelta:
1816 base = r.deltaparent(i)
1816 base = r.deltaparent(i)
1817 else:
1817 else:
1818 base = r.chainbase(i)
1818 base = r.chainbase(i)
1819 if format == 0:
1819 if format == 0:
1820 try:
1820 try:
1821 pp = r.parents(node)
1821 pp = r.parents(node)
1822 except:
1822 except:
1823 pp = [nullid, nullid]
1823 pp = [nullid, nullid]
1824 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1824 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1825 i, r.start(i), r.length(i), base, r.linkrev(i),
1825 i, r.start(i), r.length(i), base, r.linkrev(i),
1826 short(node), short(pp[0]), short(pp[1])))
1826 short(node), short(pp[0]), short(pp[1])))
1827 elif format == 1:
1827 elif format == 1:
1828 pr = r.parentrevs(i)
1828 pr = r.parentrevs(i)
1829 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1829 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1830 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1830 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1831 base, r.linkrev(i), pr[0], pr[1], short(node)))
1831 base, r.linkrev(i), pr[0], pr[1], short(node)))
1832
1832
1833 @command('debugindexdot', [], _('FILE'))
1833 @command('debugindexdot', [], _('FILE'))
1834 def debugindexdot(ui, repo, file_):
1834 def debugindexdot(ui, repo, file_):
1835 """dump an index DAG as a graphviz dot file"""
1835 """dump an index DAG as a graphviz dot file"""
1836 r = None
1836 r = None
1837 if repo:
1837 if repo:
1838 filelog = repo.file(file_)
1838 filelog = repo.file(file_)
1839 if len(filelog):
1839 if len(filelog):
1840 r = filelog
1840 r = filelog
1841 if not r:
1841 if not r:
1842 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1842 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1843 ui.write("digraph G {\n")
1843 ui.write("digraph G {\n")
1844 for i in r:
1844 for i in r:
1845 node = r.node(i)
1845 node = r.node(i)
1846 pp = r.parents(node)
1846 pp = r.parents(node)
1847 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1847 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1848 if pp[1] != nullid:
1848 if pp[1] != nullid:
1849 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1849 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1850 ui.write("}\n")
1850 ui.write("}\n")
1851
1851
1852 @command('debuginstall', [], '')
1852 @command('debuginstall', [], '')
1853 def debuginstall(ui):
1853 def debuginstall(ui):
1854 '''test Mercurial installation
1854 '''test Mercurial installation
1855
1855
1856 Returns 0 on success.
1856 Returns 0 on success.
1857 '''
1857 '''
1858
1858
1859 def writetemp(contents):
1859 def writetemp(contents):
1860 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1860 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1861 f = os.fdopen(fd, "wb")
1861 f = os.fdopen(fd, "wb")
1862 f.write(contents)
1862 f.write(contents)
1863 f.close()
1863 f.close()
1864 return name
1864 return name
1865
1865
1866 problems = 0
1866 problems = 0
1867
1867
1868 # encoding
1868 # encoding
1869 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1869 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1870 try:
1870 try:
1871 encoding.fromlocal("test")
1871 encoding.fromlocal("test")
1872 except util.Abort, inst:
1872 except util.Abort, inst:
1873 ui.write(" %s\n" % inst)
1873 ui.write(" %s\n" % inst)
1874 ui.write(_(" (check that your locale is properly set)\n"))
1874 ui.write(_(" (check that your locale is properly set)\n"))
1875 problems += 1
1875 problems += 1
1876
1876
1877 # compiled modules
1877 # compiled modules
1878 ui.status(_("Checking installed modules (%s)...\n")
1878 ui.status(_("Checking installed modules (%s)...\n")
1879 % os.path.dirname(__file__))
1879 % os.path.dirname(__file__))
1880 try:
1880 try:
1881 import bdiff, mpatch, base85, osutil
1881 import bdiff, mpatch, base85, osutil
1882 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1882 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1883 except Exception, inst:
1883 except Exception, inst:
1884 ui.write(" %s\n" % inst)
1884 ui.write(" %s\n" % inst)
1885 ui.write(_(" One or more extensions could not be found"))
1885 ui.write(_(" One or more extensions could not be found"))
1886 ui.write(_(" (check that you compiled the extensions)\n"))
1886 ui.write(_(" (check that you compiled the extensions)\n"))
1887 problems += 1
1887 problems += 1
1888
1888
1889 # templates
1889 # templates
1890 import templater
1890 import templater
1891 p = templater.templatepath()
1891 p = templater.templatepath()
1892 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1892 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1893 try:
1893 try:
1894 templater.templater(templater.templatepath("map-cmdline.default"))
1894 templater.templater(templater.templatepath("map-cmdline.default"))
1895 except Exception, inst:
1895 except Exception, inst:
1896 ui.write(" %s\n" % inst)
1896 ui.write(" %s\n" % inst)
1897 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1897 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1898 problems += 1
1898 problems += 1
1899
1899
1900 # editor
1900 # editor
1901 ui.status(_("Checking commit editor...\n"))
1901 ui.status(_("Checking commit editor...\n"))
1902 editor = ui.geteditor()
1902 editor = ui.geteditor()
1903 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1903 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1904 if not cmdpath:
1904 if not cmdpath:
1905 if editor == 'vi':
1905 if editor == 'vi':
1906 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1906 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1907 ui.write(_(" (specify a commit editor in your configuration"
1907 ui.write(_(" (specify a commit editor in your configuration"
1908 " file)\n"))
1908 " file)\n"))
1909 else:
1909 else:
1910 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1910 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1911 ui.write(_(" (specify a commit editor in your configuration"
1911 ui.write(_(" (specify a commit editor in your configuration"
1912 " file)\n"))
1912 " file)\n"))
1913 problems += 1
1913 problems += 1
1914
1914
1915 # check username
1915 # check username
1916 ui.status(_("Checking username...\n"))
1916 ui.status(_("Checking username...\n"))
1917 try:
1917 try:
1918 ui.username()
1918 ui.username()
1919 except util.Abort, e:
1919 except util.Abort, e:
1920 ui.write(" %s\n" % e)
1920 ui.write(" %s\n" % e)
1921 ui.write(_(" (specify a username in your configuration file)\n"))
1921 ui.write(_(" (specify a username in your configuration file)\n"))
1922 problems += 1
1922 problems += 1
1923
1923
1924 if not problems:
1924 if not problems:
1925 ui.status(_("No problems detected\n"))
1925 ui.status(_("No problems detected\n"))
1926 else:
1926 else:
1927 ui.write(_("%s problems detected,"
1927 ui.write(_("%s problems detected,"
1928 " please check your install!\n") % problems)
1928 " please check your install!\n") % problems)
1929
1929
1930 return problems
1930 return problems
1931
1931
1932 @command('debugknown', [], _('REPO ID...'))
1932 @command('debugknown', [], _('REPO ID...'))
1933 def debugknown(ui, repopath, *ids, **opts):
1933 def debugknown(ui, repopath, *ids, **opts):
1934 """test whether node ids are known to a repo
1934 """test whether node ids are known to a repo
1935
1935
1936 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1936 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1937 indicating unknown/known.
1937 indicating unknown/known.
1938 """
1938 """
1939 repo = hg.peer(ui, opts, repopath)
1939 repo = hg.peer(ui, opts, repopath)
1940 if not repo.capable('known'):
1940 if not repo.capable('known'):
1941 raise util.Abort("known() not supported by target repository")
1941 raise util.Abort("known() not supported by target repository")
1942 flags = repo.known([bin(s) for s in ids])
1942 flags = repo.known([bin(s) for s in ids])
1943 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1943 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1944
1944
1945 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1945 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1946 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1946 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1947 '''access the pushkey key/value protocol
1947 '''access the pushkey key/value protocol
1948
1948
1949 With two args, list the keys in the given namespace.
1949 With two args, list the keys in the given namespace.
1950
1950
1951 With five args, set a key to new if it currently is set to old.
1951 With five args, set a key to new if it currently is set to old.
1952 Reports success or failure.
1952 Reports success or failure.
1953 '''
1953 '''
1954
1954
1955 target = hg.peer(ui, {}, repopath)
1955 target = hg.peer(ui, {}, repopath)
1956 if keyinfo:
1956 if keyinfo:
1957 key, old, new = keyinfo
1957 key, old, new = keyinfo
1958 r = target.pushkey(namespace, key, old, new)
1958 r = target.pushkey(namespace, key, old, new)
1959 ui.status(str(r) + '\n')
1959 ui.status(str(r) + '\n')
1960 return not r
1960 return not r
1961 else:
1961 else:
1962 for k, v in target.listkeys(namespace).iteritems():
1962 for k, v in target.listkeys(namespace).iteritems():
1963 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1963 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1964 v.encode('string-escape')))
1964 v.encode('string-escape')))
1965
1965
1966 @command('debugrebuildstate',
1966 @command('debugrebuildstate',
1967 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1967 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1968 _('[-r REV] [REV]'))
1968 _('[-r REV] [REV]'))
1969 def debugrebuildstate(ui, repo, rev="tip"):
1969 def debugrebuildstate(ui, repo, rev="tip"):
1970 """rebuild the dirstate as it would look like for the given revision"""
1970 """rebuild the dirstate as it would look like for the given revision"""
1971 ctx = scmutil.revsingle(repo, rev)
1971 ctx = scmutil.revsingle(repo, rev)
1972 wlock = repo.wlock()
1972 wlock = repo.wlock()
1973 try:
1973 try:
1974 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1974 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1975 finally:
1975 finally:
1976 wlock.release()
1976 wlock.release()
1977
1977
1978 @command('debugrename',
1978 @command('debugrename',
1979 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1979 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1980 _('[-r REV] FILE'))
1980 _('[-r REV] FILE'))
1981 def debugrename(ui, repo, file1, *pats, **opts):
1981 def debugrename(ui, repo, file1, *pats, **opts):
1982 """dump rename information"""
1982 """dump rename information"""
1983
1983
1984 ctx = scmutil.revsingle(repo, opts.get('rev'))
1984 ctx = scmutil.revsingle(repo, opts.get('rev'))
1985 m = scmutil.match(ctx, (file1,) + pats, opts)
1985 m = scmutil.match(ctx, (file1,) + pats, opts)
1986 for abs in ctx.walk(m):
1986 for abs in ctx.walk(m):
1987 fctx = ctx[abs]
1987 fctx = ctx[abs]
1988 o = fctx.filelog().renamed(fctx.filenode())
1988 o = fctx.filelog().renamed(fctx.filenode())
1989 rel = m.rel(abs)
1989 rel = m.rel(abs)
1990 if o:
1990 if o:
1991 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1991 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1992 else:
1992 else:
1993 ui.write(_("%s not renamed\n") % rel)
1993 ui.write(_("%s not renamed\n") % rel)
1994
1994
1995 @command('debugrevlog',
1995 @command('debugrevlog',
1996 [('c', 'changelog', False, _('open changelog')),
1996 [('c', 'changelog', False, _('open changelog')),
1997 ('m', 'manifest', False, _('open manifest')),
1997 ('m', 'manifest', False, _('open manifest')),
1998 ('d', 'dump', False, _('dump index data'))],
1998 ('d', 'dump', False, _('dump index data'))],
1999 _('-c|-m|FILE'))
1999 _('-c|-m|FILE'))
2000 def debugrevlog(ui, repo, file_ = None, **opts):
2000 def debugrevlog(ui, repo, file_ = None, **opts):
2001 """show data and statistics about a revlog"""
2001 """show data and statistics about a revlog"""
2002 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2002 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2003
2003
2004 if opts.get("dump"):
2004 if opts.get("dump"):
2005 numrevs = len(r)
2005 numrevs = len(r)
2006 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2006 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2007 " rawsize totalsize compression heads\n")
2007 " rawsize totalsize compression heads\n")
2008 ts = 0
2008 ts = 0
2009 heads = set()
2009 heads = set()
2010 for rev in xrange(numrevs):
2010 for rev in xrange(numrevs):
2011 dbase = r.deltaparent(rev)
2011 dbase = r.deltaparent(rev)
2012 if dbase == -1:
2012 if dbase == -1:
2013 dbase = rev
2013 dbase = rev
2014 cbase = r.chainbase(rev)
2014 cbase = r.chainbase(rev)
2015 p1, p2 = r.parentrevs(rev)
2015 p1, p2 = r.parentrevs(rev)
2016 rs = r.rawsize(rev)
2016 rs = r.rawsize(rev)
2017 ts = ts + rs
2017 ts = ts + rs
2018 heads -= set(r.parentrevs(rev))
2018 heads -= set(r.parentrevs(rev))
2019 heads.add(rev)
2019 heads.add(rev)
2020 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2020 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2021 (rev, p1, p2, r.start(rev), r.end(rev),
2021 (rev, p1, p2, r.start(rev), r.end(rev),
2022 r.start(dbase), r.start(cbase),
2022 r.start(dbase), r.start(cbase),
2023 r.start(p1), r.start(p2),
2023 r.start(p1), r.start(p2),
2024 rs, ts, ts / r.end(rev), len(heads)))
2024 rs, ts, ts / r.end(rev), len(heads)))
2025 return 0
2025 return 0
2026
2026
2027 v = r.version
2027 v = r.version
2028 format = v & 0xFFFF
2028 format = v & 0xFFFF
2029 flags = []
2029 flags = []
2030 gdelta = False
2030 gdelta = False
2031 if v & revlog.REVLOGNGINLINEDATA:
2031 if v & revlog.REVLOGNGINLINEDATA:
2032 flags.append('inline')
2032 flags.append('inline')
2033 if v & revlog.REVLOGGENERALDELTA:
2033 if v & revlog.REVLOGGENERALDELTA:
2034 gdelta = True
2034 gdelta = True
2035 flags.append('generaldelta')
2035 flags.append('generaldelta')
2036 if not flags:
2036 if not flags:
2037 flags = ['(none)']
2037 flags = ['(none)']
2038
2038
2039 nummerges = 0
2039 nummerges = 0
2040 numfull = 0
2040 numfull = 0
2041 numprev = 0
2041 numprev = 0
2042 nump1 = 0
2042 nump1 = 0
2043 nump2 = 0
2043 nump2 = 0
2044 numother = 0
2044 numother = 0
2045 nump1prev = 0
2045 nump1prev = 0
2046 nump2prev = 0
2046 nump2prev = 0
2047 chainlengths = []
2047 chainlengths = []
2048
2048
2049 datasize = [None, 0, 0L]
2049 datasize = [None, 0, 0L]
2050 fullsize = [None, 0, 0L]
2050 fullsize = [None, 0, 0L]
2051 deltasize = [None, 0, 0L]
2051 deltasize = [None, 0, 0L]
2052
2052
2053 def addsize(size, l):
2053 def addsize(size, l):
2054 if l[0] is None or size < l[0]:
2054 if l[0] is None or size < l[0]:
2055 l[0] = size
2055 l[0] = size
2056 if size > l[1]:
2056 if size > l[1]:
2057 l[1] = size
2057 l[1] = size
2058 l[2] += size
2058 l[2] += size
2059
2059
2060 numrevs = len(r)
2060 numrevs = len(r)
2061 for rev in xrange(numrevs):
2061 for rev in xrange(numrevs):
2062 p1, p2 = r.parentrevs(rev)
2062 p1, p2 = r.parentrevs(rev)
2063 delta = r.deltaparent(rev)
2063 delta = r.deltaparent(rev)
2064 if format > 0:
2064 if format > 0:
2065 addsize(r.rawsize(rev), datasize)
2065 addsize(r.rawsize(rev), datasize)
2066 if p2 != nullrev:
2066 if p2 != nullrev:
2067 nummerges += 1
2067 nummerges += 1
2068 size = r.length(rev)
2068 size = r.length(rev)
2069 if delta == nullrev:
2069 if delta == nullrev:
2070 chainlengths.append(0)
2070 chainlengths.append(0)
2071 numfull += 1
2071 numfull += 1
2072 addsize(size, fullsize)
2072 addsize(size, fullsize)
2073 else:
2073 else:
2074 chainlengths.append(chainlengths[delta] + 1)
2074 chainlengths.append(chainlengths[delta] + 1)
2075 addsize(size, deltasize)
2075 addsize(size, deltasize)
2076 if delta == rev - 1:
2076 if delta == rev - 1:
2077 numprev += 1
2077 numprev += 1
2078 if delta == p1:
2078 if delta == p1:
2079 nump1prev += 1
2079 nump1prev += 1
2080 elif delta == p2:
2080 elif delta == p2:
2081 nump2prev += 1
2081 nump2prev += 1
2082 elif delta == p1:
2082 elif delta == p1:
2083 nump1 += 1
2083 nump1 += 1
2084 elif delta == p2:
2084 elif delta == p2:
2085 nump2 += 1
2085 nump2 += 1
2086 elif delta != nullrev:
2086 elif delta != nullrev:
2087 numother += 1
2087 numother += 1
2088
2088
2089 numdeltas = numrevs - numfull
2089 numdeltas = numrevs - numfull
2090 numoprev = numprev - nump1prev - nump2prev
2090 numoprev = numprev - nump1prev - nump2prev
2091 totalrawsize = datasize[2]
2091 totalrawsize = datasize[2]
2092 datasize[2] /= numrevs
2092 datasize[2] /= numrevs
2093 fulltotal = fullsize[2]
2093 fulltotal = fullsize[2]
2094 fullsize[2] /= numfull
2094 fullsize[2] /= numfull
2095 deltatotal = deltasize[2]
2095 deltatotal = deltasize[2]
2096 deltasize[2] /= numrevs - numfull
2096 deltasize[2] /= numrevs - numfull
2097 totalsize = fulltotal + deltatotal
2097 totalsize = fulltotal + deltatotal
2098 avgchainlen = sum(chainlengths) / numrevs
2098 avgchainlen = sum(chainlengths) / numrevs
2099 compratio = totalrawsize / totalsize
2099 compratio = totalrawsize / totalsize
2100
2100
2101 basedfmtstr = '%%%dd\n'
2101 basedfmtstr = '%%%dd\n'
2102 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2102 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2103
2103
2104 def dfmtstr(max):
2104 def dfmtstr(max):
2105 return basedfmtstr % len(str(max))
2105 return basedfmtstr % len(str(max))
2106 def pcfmtstr(max, padding=0):
2106 def pcfmtstr(max, padding=0):
2107 return basepcfmtstr % (len(str(max)), ' ' * padding)
2107 return basepcfmtstr % (len(str(max)), ' ' * padding)
2108
2108
2109 def pcfmt(value, total):
2109 def pcfmt(value, total):
2110 return (value, 100 * float(value) / total)
2110 return (value, 100 * float(value) / total)
2111
2111
2112 ui.write('format : %d\n' % format)
2112 ui.write('format : %d\n' % format)
2113 ui.write('flags : %s\n' % ', '.join(flags))
2113 ui.write('flags : %s\n' % ', '.join(flags))
2114
2114
2115 ui.write('\n')
2115 ui.write('\n')
2116 fmt = pcfmtstr(totalsize)
2116 fmt = pcfmtstr(totalsize)
2117 fmt2 = dfmtstr(totalsize)
2117 fmt2 = dfmtstr(totalsize)
2118 ui.write('revisions : ' + fmt2 % numrevs)
2118 ui.write('revisions : ' + fmt2 % numrevs)
2119 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2119 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2120 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2120 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2121 ui.write('revisions : ' + fmt2 % numrevs)
2121 ui.write('revisions : ' + fmt2 % numrevs)
2122 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2122 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2123 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2123 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2124 ui.write('revision size : ' + fmt2 % totalsize)
2124 ui.write('revision size : ' + fmt2 % totalsize)
2125 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2125 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2126 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2126 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2127
2127
2128 ui.write('\n')
2128 ui.write('\n')
2129 fmt = dfmtstr(max(avgchainlen, compratio))
2129 fmt = dfmtstr(max(avgchainlen, compratio))
2130 ui.write('avg chain length : ' + fmt % avgchainlen)
2130 ui.write('avg chain length : ' + fmt % avgchainlen)
2131 ui.write('compression ratio : ' + fmt % compratio)
2131 ui.write('compression ratio : ' + fmt % compratio)
2132
2132
2133 if format > 0:
2133 if format > 0:
2134 ui.write('\n')
2134 ui.write('\n')
2135 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2135 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2136 % tuple(datasize))
2136 % tuple(datasize))
2137 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2137 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2138 % tuple(fullsize))
2138 % tuple(fullsize))
2139 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2139 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2140 % tuple(deltasize))
2140 % tuple(deltasize))
2141
2141
2142 if numdeltas > 0:
2142 if numdeltas > 0:
2143 ui.write('\n')
2143 ui.write('\n')
2144 fmt = pcfmtstr(numdeltas)
2144 fmt = pcfmtstr(numdeltas)
2145 fmt2 = pcfmtstr(numdeltas, 4)
2145 fmt2 = pcfmtstr(numdeltas, 4)
2146 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2146 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2147 if numprev > 0:
2147 if numprev > 0:
2148 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2148 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2149 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2149 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2150 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2150 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2151 if gdelta:
2151 if gdelta:
2152 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2152 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2153 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2153 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2154 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2154 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2155
2155
2156 @command('debugrevspec', [], ('REVSPEC'))
2156 @command('debugrevspec', [], ('REVSPEC'))
2157 def debugrevspec(ui, repo, expr):
2157 def debugrevspec(ui, repo, expr):
2158 """parse and apply a revision specification
2158 """parse and apply a revision specification
2159
2159
2160 Use --verbose to print the parsed tree before and after aliases
2160 Use --verbose to print the parsed tree before and after aliases
2161 expansion.
2161 expansion.
2162 """
2162 """
2163 if ui.verbose:
2163 if ui.verbose:
2164 tree = revset.parse(expr)[0]
2164 tree = revset.parse(expr)[0]
2165 ui.note(revset.prettyformat(tree), "\n")
2165 ui.note(revset.prettyformat(tree), "\n")
2166 newtree = revset.findaliases(ui, tree)
2166 newtree = revset.findaliases(ui, tree)
2167 if newtree != tree:
2167 if newtree != tree:
2168 ui.note(revset.prettyformat(newtree), "\n")
2168 ui.note(revset.prettyformat(newtree), "\n")
2169 func = revset.match(ui, expr)
2169 func = revset.match(ui, expr)
2170 for c in func(repo, range(len(repo))):
2170 for c in func(repo, range(len(repo))):
2171 ui.write("%s\n" % c)
2171 ui.write("%s\n" % c)
2172
2172
2173 @command('debugsetparents', [], _('REV1 [REV2]'))
2173 @command('debugsetparents', [], _('REV1 [REV2]'))
2174 def debugsetparents(ui, repo, rev1, rev2=None):
2174 def debugsetparents(ui, repo, rev1, rev2=None):
2175 """manually set the parents of the current working directory
2175 """manually set the parents of the current working directory
2176
2176
2177 This is useful for writing repository conversion tools, but should
2177 This is useful for writing repository conversion tools, but should
2178 be used with care.
2178 be used with care.
2179
2179
2180 Returns 0 on success.
2180 Returns 0 on success.
2181 """
2181 """
2182
2182
2183 r1 = scmutil.revsingle(repo, rev1).node()
2183 r1 = scmutil.revsingle(repo, rev1).node()
2184 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2184 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2185
2185
2186 wlock = repo.wlock()
2186 wlock = repo.wlock()
2187 try:
2187 try:
2188 repo.dirstate.setparents(r1, r2)
2188 repo.dirstate.setparents(r1, r2)
2189 finally:
2189 finally:
2190 wlock.release()
2190 wlock.release()
2191
2191
2192 @command('debugstate',
2192 @command('debugstate',
2193 [('', 'nodates', None, _('do not display the saved mtime')),
2193 [('', 'nodates', None, _('do not display the saved mtime')),
2194 ('', 'datesort', None, _('sort by saved mtime'))],
2194 ('', 'datesort', None, _('sort by saved mtime'))],
2195 _('[OPTION]...'))
2195 _('[OPTION]...'))
2196 def debugstate(ui, repo, nodates=None, datesort=None):
2196 def debugstate(ui, repo, nodates=None, datesort=None):
2197 """show the contents of the current dirstate"""
2197 """show the contents of the current dirstate"""
2198 timestr = ""
2198 timestr = ""
2199 showdate = not nodates
2199 showdate = not nodates
2200 if datesort:
2200 if datesort:
2201 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2201 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2202 else:
2202 else:
2203 keyfunc = None # sort by filename
2203 keyfunc = None # sort by filename
2204 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2204 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2205 if showdate:
2205 if showdate:
2206 if ent[3] == -1:
2206 if ent[3] == -1:
2207 # Pad or slice to locale representation
2207 # Pad or slice to locale representation
2208 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2208 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2209 time.localtime(0)))
2209 time.localtime(0)))
2210 timestr = 'unset'
2210 timestr = 'unset'
2211 timestr = (timestr[:locale_len] +
2211 timestr = (timestr[:locale_len] +
2212 ' ' * (locale_len - len(timestr)))
2212 ' ' * (locale_len - len(timestr)))
2213 else:
2213 else:
2214 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2214 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2215 time.localtime(ent[3]))
2215 time.localtime(ent[3]))
2216 if ent[1] & 020000:
2216 if ent[1] & 020000:
2217 mode = 'lnk'
2217 mode = 'lnk'
2218 else:
2218 else:
2219 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2219 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2220 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2220 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2221 for f in repo.dirstate.copies():
2221 for f in repo.dirstate.copies():
2222 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2222 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2223
2223
2224 @command('debugsub',
2224 @command('debugsub',
2225 [('r', 'rev', '',
2225 [('r', 'rev', '',
2226 _('revision to check'), _('REV'))],
2226 _('revision to check'), _('REV'))],
2227 _('[-r REV] [REV]'))
2227 _('[-r REV] [REV]'))
2228 def debugsub(ui, repo, rev=None):
2228 def debugsub(ui, repo, rev=None):
2229 ctx = scmutil.revsingle(repo, rev, None)
2229 ctx = scmutil.revsingle(repo, rev, None)
2230 for k, v in sorted(ctx.substate.items()):
2230 for k, v in sorted(ctx.substate.items()):
2231 ui.write('path %s\n' % k)
2231 ui.write('path %s\n' % k)
2232 ui.write(' source %s\n' % v[0])
2232 ui.write(' source %s\n' % v[0])
2233 ui.write(' revision %s\n' % v[1])
2233 ui.write(' revision %s\n' % v[1])
2234
2234
2235 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2235 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2236 def debugwalk(ui, repo, *pats, **opts):
2236 def debugwalk(ui, repo, *pats, **opts):
2237 """show how files match on given patterns"""
2237 """show how files match on given patterns"""
2238 m = scmutil.match(repo[None], pats, opts)
2238 m = scmutil.match(repo[None], pats, opts)
2239 items = list(repo.walk(m))
2239 items = list(repo.walk(m))
2240 if not items:
2240 if not items:
2241 return
2241 return
2242 fmt = 'f %%-%ds %%-%ds %%s' % (
2242 fmt = 'f %%-%ds %%-%ds %%s' % (
2243 max([len(abs) for abs in items]),
2243 max([len(abs) for abs in items]),
2244 max([len(m.rel(abs)) for abs in items]))
2244 max([len(m.rel(abs)) for abs in items]))
2245 for abs in items:
2245 for abs in items:
2246 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2246 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2247 ui.write("%s\n" % line.rstrip())
2247 ui.write("%s\n" % line.rstrip())
2248
2248
2249 @command('debugwireargs',
2249 @command('debugwireargs',
2250 [('', 'three', '', 'three'),
2250 [('', 'three', '', 'three'),
2251 ('', 'four', '', 'four'),
2251 ('', 'four', '', 'four'),
2252 ('', 'five', '', 'five'),
2252 ('', 'five', '', 'five'),
2253 ] + remoteopts,
2253 ] + remoteopts,
2254 _('REPO [OPTIONS]... [ONE [TWO]]'))
2254 _('REPO [OPTIONS]... [ONE [TWO]]'))
2255 def debugwireargs(ui, repopath, *vals, **opts):
2255 def debugwireargs(ui, repopath, *vals, **opts):
2256 repo = hg.peer(ui, opts, repopath)
2256 repo = hg.peer(ui, opts, repopath)
2257 for opt in remoteopts:
2257 for opt in remoteopts:
2258 del opts[opt[1]]
2258 del opts[opt[1]]
2259 args = {}
2259 args = {}
2260 for k, v in opts.iteritems():
2260 for k, v in opts.iteritems():
2261 if v:
2261 if v:
2262 args[k] = v
2262 args[k] = v
2263 # run twice to check that we don't mess up the stream for the next command
2263 # run twice to check that we don't mess up the stream for the next command
2264 res1 = repo.debugwireargs(*vals, **args)
2264 res1 = repo.debugwireargs(*vals, **args)
2265 res2 = repo.debugwireargs(*vals, **args)
2265 res2 = repo.debugwireargs(*vals, **args)
2266 ui.write("%s\n" % res1)
2266 ui.write("%s\n" % res1)
2267 if res1 != res2:
2267 if res1 != res2:
2268 ui.warn("%s\n" % res2)
2268 ui.warn("%s\n" % res2)
2269
2269
2270 @command('^diff',
2270 @command('^diff',
2271 [('r', 'rev', [], _('revision'), _('REV')),
2271 [('r', 'rev', [], _('revision'), _('REV')),
2272 ('c', 'change', '', _('change made by revision'), _('REV'))
2272 ('c', 'change', '', _('change made by revision'), _('REV'))
2273 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2273 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2274 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2274 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2275 def diff(ui, repo, *pats, **opts):
2275 def diff(ui, repo, *pats, **opts):
2276 """diff repository (or selected files)
2276 """diff repository (or selected files)
2277
2277
2278 Show differences between revisions for the specified files.
2278 Show differences between revisions for the specified files.
2279
2279
2280 Differences between files are shown using the unified diff format.
2280 Differences between files are shown using the unified diff format.
2281
2281
2282 .. note::
2282 .. note::
2283 diff may generate unexpected results for merges, as it will
2283 diff may generate unexpected results for merges, as it will
2284 default to comparing against the working directory's first
2284 default to comparing against the working directory's first
2285 parent changeset if no revisions are specified.
2285 parent changeset if no revisions are specified.
2286
2286
2287 When two revision arguments are given, then changes are shown
2287 When two revision arguments are given, then changes are shown
2288 between those revisions. If only one revision is specified then
2288 between those revisions. If only one revision is specified then
2289 that revision is compared to the working directory, and, when no
2289 that revision is compared to the working directory, and, when no
2290 revisions are specified, the working directory files are compared
2290 revisions are specified, the working directory files are compared
2291 to its parent.
2291 to its parent.
2292
2292
2293 Alternatively you can specify -c/--change with a revision to see
2293 Alternatively you can specify -c/--change with a revision to see
2294 the changes in that changeset relative to its first parent.
2294 the changes in that changeset relative to its first parent.
2295
2295
2296 Without the -a/--text option, diff will avoid generating diffs of
2296 Without the -a/--text option, diff will avoid generating diffs of
2297 files it detects as binary. With -a, diff will generate a diff
2297 files it detects as binary. With -a, diff will generate a diff
2298 anyway, probably with undesirable results.
2298 anyway, probably with undesirable results.
2299
2299
2300 Use the -g/--git option to generate diffs in the git extended diff
2300 Use the -g/--git option to generate diffs in the git extended diff
2301 format. For more information, read :hg:`help diffs`.
2301 format. For more information, read :hg:`help diffs`.
2302
2302
2303 .. container:: verbose
2303 .. container:: verbose
2304
2304
2305 Examples:
2305 Examples:
2306
2306
2307 - compare a file in the current working directory to its parent::
2307 - compare a file in the current working directory to its parent::
2308
2308
2309 hg diff foo.c
2309 hg diff foo.c
2310
2310
2311 - compare two historical versions of a directory, with rename info::
2311 - compare two historical versions of a directory, with rename info::
2312
2312
2313 hg diff --git -r 1.0:1.2 lib/
2313 hg diff --git -r 1.0:1.2 lib/
2314
2314
2315 - get change stats relative to the last change on some date::
2315 - get change stats relative to the last change on some date::
2316
2316
2317 hg diff --stat -r "date('may 2')"
2317 hg diff --stat -r "date('may 2')"
2318
2318
2319 - diff all newly-added files that contain a keyword::
2319 - diff all newly-added files that contain a keyword::
2320
2320
2321 hg diff "set:added() and grep(GNU)"
2321 hg diff "set:added() and grep(GNU)"
2322
2322
2323 - compare a revision and its parents::
2323 - compare a revision and its parents::
2324
2324
2325 hg diff -c 9353 # compare against first parent
2325 hg diff -c 9353 # compare against first parent
2326 hg diff -r 9353^:9353 # same using revset syntax
2326 hg diff -r 9353^:9353 # same using revset syntax
2327 hg diff -r 9353^2:9353 # compare against the second parent
2327 hg diff -r 9353^2:9353 # compare against the second parent
2328
2328
2329 Returns 0 on success.
2329 Returns 0 on success.
2330 """
2330 """
2331
2331
2332 revs = opts.get('rev')
2332 revs = opts.get('rev')
2333 change = opts.get('change')
2333 change = opts.get('change')
2334 stat = opts.get('stat')
2334 stat = opts.get('stat')
2335 reverse = opts.get('reverse')
2335 reverse = opts.get('reverse')
2336
2336
2337 if revs and change:
2337 if revs and change:
2338 msg = _('cannot specify --rev and --change at the same time')
2338 msg = _('cannot specify --rev and --change at the same time')
2339 raise util.Abort(msg)
2339 raise util.Abort(msg)
2340 elif change:
2340 elif change:
2341 node2 = scmutil.revsingle(repo, change, None).node()
2341 node2 = scmutil.revsingle(repo, change, None).node()
2342 node1 = repo[node2].p1().node()
2342 node1 = repo[node2].p1().node()
2343 else:
2343 else:
2344 node1, node2 = scmutil.revpair(repo, revs)
2344 node1, node2 = scmutil.revpair(repo, revs)
2345
2345
2346 if reverse:
2346 if reverse:
2347 node1, node2 = node2, node1
2347 node1, node2 = node2, node1
2348
2348
2349 diffopts = patch.diffopts(ui, opts)
2349 diffopts = patch.diffopts(ui, opts)
2350 m = scmutil.match(repo[node2], pats, opts)
2350 m = scmutil.match(repo[node2], pats, opts)
2351 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2351 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2352 listsubrepos=opts.get('subrepos'))
2352 listsubrepos=opts.get('subrepos'))
2353
2353
2354 @command('^export',
2354 @command('^export',
2355 [('o', 'output', '',
2355 [('o', 'output', '',
2356 _('print output to file with formatted name'), _('FORMAT')),
2356 _('print output to file with formatted name'), _('FORMAT')),
2357 ('', 'switch-parent', None, _('diff against the second parent')),
2357 ('', 'switch-parent', None, _('diff against the second parent')),
2358 ('r', 'rev', [], _('revisions to export'), _('REV')),
2358 ('r', 'rev', [], _('revisions to export'), _('REV')),
2359 ] + diffopts,
2359 ] + diffopts,
2360 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2360 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2361 def export(ui, repo, *changesets, **opts):
2361 def export(ui, repo, *changesets, **opts):
2362 """dump the header and diffs for one or more changesets
2362 """dump the header and diffs for one or more changesets
2363
2363
2364 Print the changeset header and diffs for one or more revisions.
2364 Print the changeset header and diffs for one or more revisions.
2365
2365
2366 The information shown in the changeset header is: author, date,
2366 The information shown in the changeset header is: author, date,
2367 branch name (if non-default), changeset hash, parent(s) and commit
2367 branch name (if non-default), changeset hash, parent(s) and commit
2368 comment.
2368 comment.
2369
2369
2370 .. note::
2370 .. note::
2371 export may generate unexpected diff output for merge
2371 export may generate unexpected diff output for merge
2372 changesets, as it will compare the merge changeset against its
2372 changesets, as it will compare the merge changeset against its
2373 first parent only.
2373 first parent only.
2374
2374
2375 Output may be to a file, in which case the name of the file is
2375 Output may be to a file, in which case the name of the file is
2376 given using a format string. The formatting rules are as follows:
2376 given using a format string. The formatting rules are as follows:
2377
2377
2378 :``%%``: literal "%" character
2378 :``%%``: literal "%" character
2379 :``%H``: changeset hash (40 hexadecimal digits)
2379 :``%H``: changeset hash (40 hexadecimal digits)
2380 :``%N``: number of patches being generated
2380 :``%N``: number of patches being generated
2381 :``%R``: changeset revision number
2381 :``%R``: changeset revision number
2382 :``%b``: basename of the exporting repository
2382 :``%b``: basename of the exporting repository
2383 :``%h``: short-form changeset hash (12 hexadecimal digits)
2383 :``%h``: short-form changeset hash (12 hexadecimal digits)
2384 :``%m``: first line of the commit message (only alphanumeric characters)
2384 :``%m``: first line of the commit message (only alphanumeric characters)
2385 :``%n``: zero-padded sequence number, starting at 1
2385 :``%n``: zero-padded sequence number, starting at 1
2386 :``%r``: zero-padded changeset revision number
2386 :``%r``: zero-padded changeset revision number
2387
2387
2388 Without the -a/--text option, export will avoid generating diffs
2388 Without the -a/--text option, export will avoid generating diffs
2389 of files it detects as binary. With -a, export will generate a
2389 of files it detects as binary. With -a, export will generate a
2390 diff anyway, probably with undesirable results.
2390 diff anyway, probably with undesirable results.
2391
2391
2392 Use the -g/--git option to generate diffs in the git extended diff
2392 Use the -g/--git option to generate diffs in the git extended diff
2393 format. See :hg:`help diffs` for more information.
2393 format. See :hg:`help diffs` for more information.
2394
2394
2395 With the --switch-parent option, the diff will be against the
2395 With the --switch-parent option, the diff will be against the
2396 second parent. It can be useful to review a merge.
2396 second parent. It can be useful to review a merge.
2397
2397
2398 .. container:: verbose
2398 .. container:: verbose
2399
2399
2400 Examples:
2400 Examples:
2401
2401
2402 - use export and import to transplant a bugfix to the current
2402 - use export and import to transplant a bugfix to the current
2403 branch::
2403 branch::
2404
2404
2405 hg export -r 9353 | hg import -
2405 hg export -r 9353 | hg import -
2406
2406
2407 - export all the changesets between two revisions to a file with
2407 - export all the changesets between two revisions to a file with
2408 rename information::
2408 rename information::
2409
2409
2410 hg export --git -r 123:150 > changes.txt
2410 hg export --git -r 123:150 > changes.txt
2411
2411
2412 - split outgoing changes into a series of patches with
2412 - split outgoing changes into a series of patches with
2413 descriptive names::
2413 descriptive names::
2414
2414
2415 hg export -r "outgoing()" -o "%n-%m.patch"
2415 hg export -r "outgoing()" -o "%n-%m.patch"
2416
2416
2417 Returns 0 on success.
2417 Returns 0 on success.
2418 """
2418 """
2419 changesets += tuple(opts.get('rev', []))
2419 changesets += tuple(opts.get('rev', []))
2420 if not changesets:
2420 if not changesets:
2421 raise util.Abort(_("export requires at least one changeset"))
2421 raise util.Abort(_("export requires at least one changeset"))
2422 revs = scmutil.revrange(repo, changesets)
2422 revs = scmutil.revrange(repo, changesets)
2423 if len(revs) > 1:
2423 if len(revs) > 1:
2424 ui.note(_('exporting patches:\n'))
2424 ui.note(_('exporting patches:\n'))
2425 else:
2425 else:
2426 ui.note(_('exporting patch:\n'))
2426 ui.note(_('exporting patch:\n'))
2427 cmdutil.export(repo, revs, template=opts.get('output'),
2427 cmdutil.export(repo, revs, template=opts.get('output'),
2428 switch_parent=opts.get('switch_parent'),
2428 switch_parent=opts.get('switch_parent'),
2429 opts=patch.diffopts(ui, opts))
2429 opts=patch.diffopts(ui, opts))
2430
2430
2431 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2431 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2432 def forget(ui, repo, *pats, **opts):
2432 def forget(ui, repo, *pats, **opts):
2433 """forget the specified files on the next commit
2433 """forget the specified files on the next commit
2434
2434
2435 Mark the specified files so they will no longer be tracked
2435 Mark the specified files so they will no longer be tracked
2436 after the next commit.
2436 after the next commit.
2437
2437
2438 This only removes files from the current branch, not from the
2438 This only removes files from the current branch, not from the
2439 entire project history, and it does not delete them from the
2439 entire project history, and it does not delete them from the
2440 working directory.
2440 working directory.
2441
2441
2442 To undo a forget before the next commit, see :hg:`add`.
2442 To undo a forget before the next commit, see :hg:`add`.
2443
2443
2444 .. container:: verbose
2444 .. container:: verbose
2445
2445
2446 Examples:
2446 Examples:
2447
2447
2448 - forget newly-added binary files::
2448 - forget newly-added binary files::
2449
2449
2450 hg forget "set:added() and binary()"
2450 hg forget "set:added() and binary()"
2451
2451
2452 - forget files that would be excluded by .hgignore::
2452 - forget files that would be excluded by .hgignore::
2453
2453
2454 hg forget "set:hgignore()"
2454 hg forget "set:hgignore()"
2455
2455
2456 Returns 0 on success.
2456 Returns 0 on success.
2457 """
2457 """
2458
2458
2459 if not pats:
2459 if not pats:
2460 raise util.Abort(_('no files specified'))
2460 raise util.Abort(_('no files specified'))
2461
2461
2462 m = scmutil.match(repo[None], pats, opts)
2462 m = scmutil.match(repo[None], pats, opts)
2463 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2463 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2464 return rejected and 1 or 0
2464 return rejected and 1 or 0
2465
2465
2466 @command(
2466 @command(
2467 'graft',
2467 'graft',
2468 [('c', 'continue', False, _('resume interrupted graft')),
2468 [('c', 'continue', False, _('resume interrupted graft')),
2469 ('e', 'edit', False, _('invoke editor on commit messages')),
2469 ('e', 'edit', False, _('invoke editor on commit messages')),
2470 ('D', 'currentdate', False,
2470 ('D', 'currentdate', False,
2471 _('record the current date as commit date')),
2471 _('record the current date as commit date')),
2472 ('U', 'currentuser', False,
2472 ('U', 'currentuser', False,
2473 _('record the current user as committer'), _('DATE'))]
2473 _('record the current user as committer'), _('DATE'))]
2474 + commitopts2 + mergetoolopts,
2474 + commitopts2 + mergetoolopts,
2475 _('[OPTION]... REVISION...'))
2475 _('[OPTION]... REVISION...'))
2476 def graft(ui, repo, *revs, **opts):
2476 def graft(ui, repo, *revs, **opts):
2477 '''copy changes from other branches onto the current branch
2477 '''copy changes from other branches onto the current branch
2478
2478
2479 This command uses Mercurial's merge logic to copy individual
2479 This command uses Mercurial's merge logic to copy individual
2480 changes from other branches without merging branches in the
2480 changes from other branches without merging branches in the
2481 history graph. This is sometimes known as 'backporting' or
2481 history graph. This is sometimes known as 'backporting' or
2482 'cherry-picking'. By default, graft will copy user, date, and
2482 'cherry-picking'. By default, graft will copy user, date, and
2483 description from the source changesets.
2483 description from the source changesets.
2484
2484
2485 Changesets that are ancestors of the current revision, that have
2485 Changesets that are ancestors of the current revision, that have
2486 already been grafted, or that are merges will be skipped.
2486 already been grafted, or that are merges will be skipped.
2487
2487
2488 If a graft merge results in conflicts, the graft process is
2488 If a graft merge results in conflicts, the graft process is
2489 interrupted so that the current merge can be manually resolved.
2489 interrupted so that the current merge can be manually resolved.
2490 Once all conflicts are addressed, the graft process can be
2490 Once all conflicts are addressed, the graft process can be
2491 continued with the -c/--continue option.
2491 continued with the -c/--continue option.
2492
2492
2493 .. note::
2493 .. note::
2494 The -c/--continue option does not reapply earlier options.
2494 The -c/--continue option does not reapply earlier options.
2495
2495
2496 .. container:: verbose
2496 .. container:: verbose
2497
2497
2498 Examples:
2498 Examples:
2499
2499
2500 - copy a single change to the stable branch and edit its description::
2500 - copy a single change to the stable branch and edit its description::
2501
2501
2502 hg update stable
2502 hg update stable
2503 hg graft --edit 9393
2503 hg graft --edit 9393
2504
2504
2505 - graft a range of changesets with one exception, updating dates::
2505 - graft a range of changesets with one exception, updating dates::
2506
2506
2507 hg graft -D "2085::2093 and not 2091"
2507 hg graft -D "2085::2093 and not 2091"
2508
2508
2509 - continue a graft after resolving conflicts::
2509 - continue a graft after resolving conflicts::
2510
2510
2511 hg graft -c
2511 hg graft -c
2512
2512
2513 - show the source of a grafted changeset::
2513 - show the source of a grafted changeset::
2514
2514
2515 hg log --debug -r tip
2515 hg log --debug -r tip
2516
2516
2517 Returns 0 on successful completion.
2517 Returns 0 on successful completion.
2518 '''
2518 '''
2519
2519
2520 if not opts.get('user') and opts.get('currentuser'):
2520 if not opts.get('user') and opts.get('currentuser'):
2521 opts['user'] = ui.username()
2521 opts['user'] = ui.username()
2522 if not opts.get('date') and opts.get('currentdate'):
2522 if not opts.get('date') and opts.get('currentdate'):
2523 opts['date'] = "%d %d" % util.makedate()
2523 opts['date'] = "%d %d" % util.makedate()
2524
2524
2525 editor = None
2525 editor = None
2526 if opts.get('edit'):
2526 if opts.get('edit'):
2527 editor = cmdutil.commitforceeditor
2527 editor = cmdutil.commitforceeditor
2528
2528
2529 cont = False
2529 cont = False
2530 if opts['continue']:
2530 if opts['continue']:
2531 cont = True
2531 cont = True
2532 if revs:
2532 if revs:
2533 raise util.Abort(_("can't specify --continue and revisions"))
2533 raise util.Abort(_("can't specify --continue and revisions"))
2534 # read in unfinished revisions
2534 # read in unfinished revisions
2535 try:
2535 try:
2536 nodes = repo.opener.read('graftstate').splitlines()
2536 nodes = repo.opener.read('graftstate').splitlines()
2537 revs = [repo[node].rev() for node in nodes]
2537 revs = [repo[node].rev() for node in nodes]
2538 except IOError, inst:
2538 except IOError, inst:
2539 if inst.errno != errno.ENOENT:
2539 if inst.errno != errno.ENOENT:
2540 raise
2540 raise
2541 raise util.Abort(_("no graft state found, can't continue"))
2541 raise util.Abort(_("no graft state found, can't continue"))
2542 else:
2542 else:
2543 cmdutil.bailifchanged(repo)
2543 cmdutil.bailifchanged(repo)
2544 if not revs:
2544 if not revs:
2545 raise util.Abort(_('no revisions specified'))
2545 raise util.Abort(_('no revisions specified'))
2546 revs = scmutil.revrange(repo, revs)
2546 revs = scmutil.revrange(repo, revs)
2547
2547
2548 # check for merges
2548 # check for merges
2549 for rev in repo.revs('%ld and merge()', revs):
2549 for rev in repo.revs('%ld and merge()', revs):
2550 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2550 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2551 revs.remove(rev)
2551 revs.remove(rev)
2552 if not revs:
2552 if not revs:
2553 return -1
2553 return -1
2554
2554
2555 # check for ancestors of dest branch
2555 # check for ancestors of dest branch
2556 for rev in repo.revs('::. and %ld', revs):
2556 for rev in repo.revs('::. and %ld', revs):
2557 ui.warn(_('skipping ancestor revision %s\n') % rev)
2557 ui.warn(_('skipping ancestor revision %s\n') % rev)
2558 revs.remove(rev)
2558 revs.remove(rev)
2559 if not revs:
2559 if not revs:
2560 return -1
2560 return -1
2561
2561
2562 # analyze revs for earlier grafts
2562 # analyze revs for earlier grafts
2563 ids = {}
2563 ids = {}
2564 for ctx in repo.set("%ld", revs):
2564 for ctx in repo.set("%ld", revs):
2565 ids[ctx.hex()] = ctx.rev()
2565 ids[ctx.hex()] = ctx.rev()
2566 n = ctx.extra().get('source')
2566 n = ctx.extra().get('source')
2567 if n:
2567 if n:
2568 ids[n] = ctx.rev()
2568 ids[n] = ctx.rev()
2569
2569
2570 # check ancestors for earlier grafts
2570 # check ancestors for earlier grafts
2571 ui.debug('scanning for duplicate grafts\n')
2571 ui.debug('scanning for duplicate grafts\n')
2572 for ctx in repo.set("::. - ::%ld", revs):
2572 for ctx in repo.set("::. - ::%ld", revs):
2573 n = ctx.extra().get('source')
2573 n = ctx.extra().get('source')
2574 if n in ids:
2574 if n in ids:
2575 r = repo[n].rev()
2575 r = repo[n].rev()
2576 if r in revs:
2576 if r in revs:
2577 ui.warn(_('skipping already grafted revision %s\n') % r)
2577 ui.warn(_('skipping already grafted revision %s\n') % r)
2578 revs.remove(r)
2578 revs.remove(r)
2579 elif ids[n] in revs:
2579 elif ids[n] in revs:
2580 ui.warn(_('skipping already grafted revision %s '
2580 ui.warn(_('skipping already grafted revision %s '
2581 '(same origin %d)\n') % (ids[n], r))
2581 '(same origin %d)\n') % (ids[n], r))
2582 revs.remove(ids[n])
2582 revs.remove(ids[n])
2583 elif ctx.hex() in ids:
2583 elif ctx.hex() in ids:
2584 r = ids[ctx.hex()]
2584 r = ids[ctx.hex()]
2585 ui.warn(_('skipping already grafted revision %s '
2585 ui.warn(_('skipping already grafted revision %s '
2586 '(was grafted from %d)\n') % (r, ctx.rev()))
2586 '(was grafted from %d)\n') % (r, ctx.rev()))
2587 revs.remove(r)
2587 revs.remove(r)
2588 if not revs:
2588 if not revs:
2589 return -1
2589 return -1
2590
2590
2591 for pos, ctx in enumerate(repo.set("%ld", revs)):
2591 for pos, ctx in enumerate(repo.set("%ld", revs)):
2592 current = repo['.']
2592 current = repo['.']
2593 ui.status(_('grafting revision %s\n') % ctx.rev())
2593 ui.status(_('grafting revision %s\n') % ctx.rev())
2594
2594
2595 # we don't merge the first commit when continuing
2595 # we don't merge the first commit when continuing
2596 if not cont:
2596 if not cont:
2597 # perform the graft merge with p1(rev) as 'ancestor'
2597 # perform the graft merge with p1(rev) as 'ancestor'
2598 try:
2598 try:
2599 # ui.forcemerge is an internal variable, do not document
2599 # ui.forcemerge is an internal variable, do not document
2600 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2600 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2601 stats = mergemod.update(repo, ctx.node(), True, True, False,
2601 stats = mergemod.update(repo, ctx.node(), True, True, False,
2602 ctx.p1().node())
2602 ctx.p1().node())
2603 finally:
2603 finally:
2604 ui.setconfig('ui', 'forcemerge', '')
2604 ui.setconfig('ui', 'forcemerge', '')
2605 # drop the second merge parent
2605 # drop the second merge parent
2606 repo.dirstate.setparents(current.node(), nullid)
2606 repo.dirstate.setparents(current.node(), nullid)
2607 repo.dirstate.write()
2607 repo.dirstate.write()
2608 # fix up dirstate for copies and renames
2608 # fix up dirstate for copies and renames
2609 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2609 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2610 # report any conflicts
2610 # report any conflicts
2611 if stats and stats[3] > 0:
2611 if stats and stats[3] > 0:
2612 # write out state for --continue
2612 # write out state for --continue
2613 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2613 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2614 repo.opener.write('graftstate', ''.join(nodelines))
2614 repo.opener.write('graftstate', ''.join(nodelines))
2615 raise util.Abort(
2615 raise util.Abort(
2616 _("unresolved conflicts, can't continue"),
2616 _("unresolved conflicts, can't continue"),
2617 hint=_('use hg resolve and hg graft --continue'))
2617 hint=_('use hg resolve and hg graft --continue'))
2618 else:
2618 else:
2619 cont = False
2619 cont = False
2620
2620
2621 # commit
2621 # commit
2622 source = ctx.extra().get('source')
2622 source = ctx.extra().get('source')
2623 if not source:
2623 if not source:
2624 source = ctx.hex()
2624 source = ctx.hex()
2625 extra = {'source': source}
2625 extra = {'source': source}
2626 user = ctx.user()
2626 user = ctx.user()
2627 if opts.get('user'):
2627 if opts.get('user'):
2628 user = opts['user']
2628 user = opts['user']
2629 date = ctx.date()
2629 date = ctx.date()
2630 if opts.get('date'):
2630 if opts.get('date'):
2631 date = opts['date']
2631 date = opts['date']
2632 repo.commit(text=ctx.description(), user=user,
2632 repo.commit(text=ctx.description(), user=user,
2633 date=date, extra=extra, editor=editor)
2633 date=date, extra=extra, editor=editor)
2634
2634
2635 # remove state when we complete successfully
2635 # remove state when we complete successfully
2636 if os.path.exists(repo.join('graftstate')):
2636 if os.path.exists(repo.join('graftstate')):
2637 util.unlinkpath(repo.join('graftstate'))
2637 util.unlinkpath(repo.join('graftstate'))
2638
2638
2639 return 0
2639 return 0
2640
2640
2641 @command('grep',
2641 @command('grep',
2642 [('0', 'print0', None, _('end fields with NUL')),
2642 [('0', 'print0', None, _('end fields with NUL')),
2643 ('', 'all', None, _('print all revisions that match')),
2643 ('', 'all', None, _('print all revisions that match')),
2644 ('a', 'text', None, _('treat all files as text')),
2644 ('a', 'text', None, _('treat all files as text')),
2645 ('f', 'follow', None,
2645 ('f', 'follow', None,
2646 _('follow changeset history,'
2646 _('follow changeset history,'
2647 ' or file history across copies and renames')),
2647 ' or file history across copies and renames')),
2648 ('i', 'ignore-case', None, _('ignore case when matching')),
2648 ('i', 'ignore-case', None, _('ignore case when matching')),
2649 ('l', 'files-with-matches', None,
2649 ('l', 'files-with-matches', None,
2650 _('print only filenames and revisions that match')),
2650 _('print only filenames and revisions that match')),
2651 ('n', 'line-number', None, _('print matching line numbers')),
2651 ('n', 'line-number', None, _('print matching line numbers')),
2652 ('r', 'rev', [],
2652 ('r', 'rev', [],
2653 _('only search files changed within revision range'), _('REV')),
2653 _('only search files changed within revision range'), _('REV')),
2654 ('u', 'user', None, _('list the author (long with -v)')),
2654 ('u', 'user', None, _('list the author (long with -v)')),
2655 ('d', 'date', None, _('list the date (short with -q)')),
2655 ('d', 'date', None, _('list the date (short with -q)')),
2656 ] + walkopts,
2656 ] + walkopts,
2657 _('[OPTION]... PATTERN [FILE]...'))
2657 _('[OPTION]... PATTERN [FILE]...'))
2658 def grep(ui, repo, pattern, *pats, **opts):
2658 def grep(ui, repo, pattern, *pats, **opts):
2659 """search for a pattern in specified files and revisions
2659 """search for a pattern in specified files and revisions
2660
2660
2661 Search revisions of files for a regular expression.
2661 Search revisions of files for a regular expression.
2662
2662
2663 This command behaves differently than Unix grep. It only accepts
2663 This command behaves differently than Unix grep. It only accepts
2664 Python/Perl regexps. It searches repository history, not the
2664 Python/Perl regexps. It searches repository history, not the
2665 working directory. It always prints the revision number in which a
2665 working directory. It always prints the revision number in which a
2666 match appears.
2666 match appears.
2667
2667
2668 By default, grep only prints output for the first revision of a
2668 By default, grep only prints output for the first revision of a
2669 file in which it finds a match. To get it to print every revision
2669 file in which it finds a match. To get it to print every revision
2670 that contains a change in match status ("-" for a match that
2670 that contains a change in match status ("-" for a match that
2671 becomes a non-match, or "+" for a non-match that becomes a match),
2671 becomes a non-match, or "+" for a non-match that becomes a match),
2672 use the --all flag.
2672 use the --all flag.
2673
2673
2674 Returns 0 if a match is found, 1 otherwise.
2674 Returns 0 if a match is found, 1 otherwise.
2675 """
2675 """
2676 reflags = re.M
2676 reflags = re.M
2677 if opts.get('ignore_case'):
2677 if opts.get('ignore_case'):
2678 reflags |= re.I
2678 reflags |= re.I
2679 try:
2679 try:
2680 regexp = re.compile(pattern, reflags)
2680 regexp = re.compile(pattern, reflags)
2681 except re.error, inst:
2681 except re.error, inst:
2682 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2682 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2683 return 1
2683 return 1
2684 sep, eol = ':', '\n'
2684 sep, eol = ':', '\n'
2685 if opts.get('print0'):
2685 if opts.get('print0'):
2686 sep = eol = '\0'
2686 sep = eol = '\0'
2687
2687
2688 getfile = util.lrucachefunc(repo.file)
2688 getfile = util.lrucachefunc(repo.file)
2689
2689
2690 def matchlines(body):
2690 def matchlines(body):
2691 begin = 0
2691 begin = 0
2692 linenum = 0
2692 linenum = 0
2693 while True:
2693 while True:
2694 match = regexp.search(body, begin)
2694 match = regexp.search(body, begin)
2695 if not match:
2695 if not match:
2696 break
2696 break
2697 mstart, mend = match.span()
2697 mstart, mend = match.span()
2698 linenum += body.count('\n', begin, mstart) + 1
2698 linenum += body.count('\n', begin, mstart) + 1
2699 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2699 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2700 begin = body.find('\n', mend) + 1 or len(body) + 1
2700 begin = body.find('\n', mend) + 1 or len(body) + 1
2701 lend = begin - 1
2701 lend = begin - 1
2702 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2702 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2703
2703
2704 class linestate(object):
2704 class linestate(object):
2705 def __init__(self, line, linenum, colstart, colend):
2705 def __init__(self, line, linenum, colstart, colend):
2706 self.line = line
2706 self.line = line
2707 self.linenum = linenum
2707 self.linenum = linenum
2708 self.colstart = colstart
2708 self.colstart = colstart
2709 self.colend = colend
2709 self.colend = colend
2710
2710
2711 def __hash__(self):
2711 def __hash__(self):
2712 return hash((self.linenum, self.line))
2712 return hash((self.linenum, self.line))
2713
2713
2714 def __eq__(self, other):
2714 def __eq__(self, other):
2715 return self.line == other.line
2715 return self.line == other.line
2716
2716
2717 matches = {}
2717 matches = {}
2718 copies = {}
2718 copies = {}
2719 def grepbody(fn, rev, body):
2719 def grepbody(fn, rev, body):
2720 matches[rev].setdefault(fn, [])
2720 matches[rev].setdefault(fn, [])
2721 m = matches[rev][fn]
2721 m = matches[rev][fn]
2722 for lnum, cstart, cend, line in matchlines(body):
2722 for lnum, cstart, cend, line in matchlines(body):
2723 s = linestate(line, lnum, cstart, cend)
2723 s = linestate(line, lnum, cstart, cend)
2724 m.append(s)
2724 m.append(s)
2725
2725
2726 def difflinestates(a, b):
2726 def difflinestates(a, b):
2727 sm = difflib.SequenceMatcher(None, a, b)
2727 sm = difflib.SequenceMatcher(None, a, b)
2728 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2728 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2729 if tag == 'insert':
2729 if tag == 'insert':
2730 for i in xrange(blo, bhi):
2730 for i in xrange(blo, bhi):
2731 yield ('+', b[i])
2731 yield ('+', b[i])
2732 elif tag == 'delete':
2732 elif tag == 'delete':
2733 for i in xrange(alo, ahi):
2733 for i in xrange(alo, ahi):
2734 yield ('-', a[i])
2734 yield ('-', a[i])
2735 elif tag == 'replace':
2735 elif tag == 'replace':
2736 for i in xrange(alo, ahi):
2736 for i in xrange(alo, ahi):
2737 yield ('-', a[i])
2737 yield ('-', a[i])
2738 for i in xrange(blo, bhi):
2738 for i in xrange(blo, bhi):
2739 yield ('+', b[i])
2739 yield ('+', b[i])
2740
2740
2741 def display(fn, ctx, pstates, states):
2741 def display(fn, ctx, pstates, states):
2742 rev = ctx.rev()
2742 rev = ctx.rev()
2743 datefunc = ui.quiet and util.shortdate or util.datestr
2743 datefunc = ui.quiet and util.shortdate or util.datestr
2744 found = False
2744 found = False
2745 filerevmatches = {}
2745 filerevmatches = {}
2746 def binary():
2746 def binary():
2747 flog = getfile(fn)
2747 flog = getfile(fn)
2748 return util.binary(flog.read(ctx.filenode(fn)))
2748 return util.binary(flog.read(ctx.filenode(fn)))
2749
2749
2750 if opts.get('all'):
2750 if opts.get('all'):
2751 iter = difflinestates(pstates, states)
2751 iter = difflinestates(pstates, states)
2752 else:
2752 else:
2753 iter = [('', l) for l in states]
2753 iter = [('', l) for l in states]
2754 for change, l in iter:
2754 for change, l in iter:
2755 cols = [fn, str(rev)]
2755 cols = [fn, str(rev)]
2756 before, match, after = None, None, None
2756 before, match, after = None, None, None
2757 if opts.get('line_number'):
2757 if opts.get('line_number'):
2758 cols.append(str(l.linenum))
2758 cols.append(str(l.linenum))
2759 if opts.get('all'):
2759 if opts.get('all'):
2760 cols.append(change)
2760 cols.append(change)
2761 if opts.get('user'):
2761 if opts.get('user'):
2762 cols.append(ui.shortuser(ctx.user()))
2762 cols.append(ui.shortuser(ctx.user()))
2763 if opts.get('date'):
2763 if opts.get('date'):
2764 cols.append(datefunc(ctx.date()))
2764 cols.append(datefunc(ctx.date()))
2765 if opts.get('files_with_matches'):
2765 if opts.get('files_with_matches'):
2766 c = (fn, rev)
2766 c = (fn, rev)
2767 if c in filerevmatches:
2767 if c in filerevmatches:
2768 continue
2768 continue
2769 filerevmatches[c] = 1
2769 filerevmatches[c] = 1
2770 else:
2770 else:
2771 before = l.line[:l.colstart]
2771 before = l.line[:l.colstart]
2772 match = l.line[l.colstart:l.colend]
2772 match = l.line[l.colstart:l.colend]
2773 after = l.line[l.colend:]
2773 after = l.line[l.colend:]
2774 ui.write(sep.join(cols))
2774 ui.write(sep.join(cols))
2775 if before is not None:
2775 if before is not None:
2776 if not opts.get('text') and binary():
2776 if not opts.get('text') and binary():
2777 ui.write(sep + " Binary file matches")
2777 ui.write(sep + " Binary file matches")
2778 else:
2778 else:
2779 ui.write(sep + before)
2779 ui.write(sep + before)
2780 ui.write(match, label='grep.match')
2780 ui.write(match, label='grep.match')
2781 ui.write(after)
2781 ui.write(after)
2782 ui.write(eol)
2782 ui.write(eol)
2783 found = True
2783 found = True
2784 return found
2784 return found
2785
2785
2786 skip = {}
2786 skip = {}
2787 revfiles = {}
2787 revfiles = {}
2788 matchfn = scmutil.match(repo[None], pats, opts)
2788 matchfn = scmutil.match(repo[None], pats, opts)
2789 found = False
2789 found = False
2790 follow = opts.get('follow')
2790 follow = opts.get('follow')
2791
2791
2792 def prep(ctx, fns):
2792 def prep(ctx, fns):
2793 rev = ctx.rev()
2793 rev = ctx.rev()
2794 pctx = ctx.p1()
2794 pctx = ctx.p1()
2795 parent = pctx.rev()
2795 parent = pctx.rev()
2796 matches.setdefault(rev, {})
2796 matches.setdefault(rev, {})
2797 matches.setdefault(parent, {})
2797 matches.setdefault(parent, {})
2798 files = revfiles.setdefault(rev, [])
2798 files = revfiles.setdefault(rev, [])
2799 for fn in fns:
2799 for fn in fns:
2800 flog = getfile(fn)
2800 flog = getfile(fn)
2801 try:
2801 try:
2802 fnode = ctx.filenode(fn)
2802 fnode = ctx.filenode(fn)
2803 except error.LookupError:
2803 except error.LookupError:
2804 continue
2804 continue
2805
2805
2806 copied = flog.renamed(fnode)
2806 copied = flog.renamed(fnode)
2807 copy = follow and copied and copied[0]
2807 copy = follow and copied and copied[0]
2808 if copy:
2808 if copy:
2809 copies.setdefault(rev, {})[fn] = copy
2809 copies.setdefault(rev, {})[fn] = copy
2810 if fn in skip:
2810 if fn in skip:
2811 if copy:
2811 if copy:
2812 skip[copy] = True
2812 skip[copy] = True
2813 continue
2813 continue
2814 files.append(fn)
2814 files.append(fn)
2815
2815
2816 if fn not in matches[rev]:
2816 if fn not in matches[rev]:
2817 grepbody(fn, rev, flog.read(fnode))
2817 grepbody(fn, rev, flog.read(fnode))
2818
2818
2819 pfn = copy or fn
2819 pfn = copy or fn
2820 if pfn not in matches[parent]:
2820 if pfn not in matches[parent]:
2821 try:
2821 try:
2822 fnode = pctx.filenode(pfn)
2822 fnode = pctx.filenode(pfn)
2823 grepbody(pfn, parent, flog.read(fnode))
2823 grepbody(pfn, parent, flog.read(fnode))
2824 except error.LookupError:
2824 except error.LookupError:
2825 pass
2825 pass
2826
2826
2827 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2827 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2828 rev = ctx.rev()
2828 rev = ctx.rev()
2829 parent = ctx.p1().rev()
2829 parent = ctx.p1().rev()
2830 for fn in sorted(revfiles.get(rev, [])):
2830 for fn in sorted(revfiles.get(rev, [])):
2831 states = matches[rev][fn]
2831 states = matches[rev][fn]
2832 copy = copies.get(rev, {}).get(fn)
2832 copy = copies.get(rev, {}).get(fn)
2833 if fn in skip:
2833 if fn in skip:
2834 if copy:
2834 if copy:
2835 skip[copy] = True
2835 skip[copy] = True
2836 continue
2836 continue
2837 pstates = matches.get(parent, {}).get(copy or fn, [])
2837 pstates = matches.get(parent, {}).get(copy or fn, [])
2838 if pstates or states:
2838 if pstates or states:
2839 r = display(fn, ctx, pstates, states)
2839 r = display(fn, ctx, pstates, states)
2840 found = found or r
2840 found = found or r
2841 if r and not opts.get('all'):
2841 if r and not opts.get('all'):
2842 skip[fn] = True
2842 skip[fn] = True
2843 if copy:
2843 if copy:
2844 skip[copy] = True
2844 skip[copy] = True
2845 del matches[rev]
2845 del matches[rev]
2846 del revfiles[rev]
2846 del revfiles[rev]
2847
2847
2848 return not found
2848 return not found
2849
2849
2850 @command('heads',
2850 @command('heads',
2851 [('r', 'rev', '',
2851 [('r', 'rev', '',
2852 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2852 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2853 ('t', 'topo', False, _('show topological heads only')),
2853 ('t', 'topo', False, _('show topological heads only')),
2854 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2854 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2855 ('c', 'closed', False, _('show normal and closed branch heads')),
2855 ('c', 'closed', False, _('show normal and closed branch heads')),
2856 ] + templateopts,
2856 ] + templateopts,
2857 _('[-ac] [-r STARTREV] [REV]...'))
2857 _('[-ac] [-r STARTREV] [REV]...'))
2858 def heads(ui, repo, *branchrevs, **opts):
2858 def heads(ui, repo, *branchrevs, **opts):
2859 """show current repository heads or show branch heads
2859 """show current repository heads or show branch heads
2860
2860
2861 With no arguments, show all repository branch heads.
2861 With no arguments, show all repository branch heads.
2862
2862
2863 Repository "heads" are changesets with no child changesets. They are
2863 Repository "heads" are changesets with no child changesets. They are
2864 where development generally takes place and are the usual targets
2864 where development generally takes place and are the usual targets
2865 for update and merge operations. Branch heads are changesets that have
2865 for update and merge operations. Branch heads are changesets that have
2866 no child changeset on the same branch.
2866 no child changeset on the same branch.
2867
2867
2868 If one or more REVs are given, only branch heads on the branches
2868 If one or more REVs are given, only branch heads on the branches
2869 associated with the specified changesets are shown. This means
2869 associated with the specified changesets are shown. This means
2870 that you can use :hg:`heads foo` to see the heads on a branch
2870 that you can use :hg:`heads foo` to see the heads on a branch
2871 named ``foo``.
2871 named ``foo``.
2872
2872
2873 If -c/--closed is specified, also show branch heads marked closed
2873 If -c/--closed is specified, also show branch heads marked closed
2874 (see :hg:`commit --close-branch`).
2874 (see :hg:`commit --close-branch`).
2875
2875
2876 If STARTREV is specified, only those heads that are descendants of
2876 If STARTREV is specified, only those heads that are descendants of
2877 STARTREV will be displayed.
2877 STARTREV will be displayed.
2878
2878
2879 If -t/--topo is specified, named branch mechanics will be ignored and only
2879 If -t/--topo is specified, named branch mechanics will be ignored and only
2880 changesets without children will be shown.
2880 changesets without children will be shown.
2881
2881
2882 Returns 0 if matching heads are found, 1 if not.
2882 Returns 0 if matching heads are found, 1 if not.
2883 """
2883 """
2884
2884
2885 start = None
2885 start = None
2886 if 'rev' in opts:
2886 if 'rev' in opts:
2887 start = scmutil.revsingle(repo, opts['rev'], None).node()
2887 start = scmutil.revsingle(repo, opts['rev'], None).node()
2888
2888
2889 if opts.get('topo'):
2889 if opts.get('topo'):
2890 heads = [repo[h] for h in repo.heads(start)]
2890 heads = [repo[h] for h in repo.heads(start)]
2891 else:
2891 else:
2892 heads = []
2892 heads = []
2893 for branch in repo.branchmap():
2893 for branch in repo.branchmap():
2894 heads += repo.branchheads(branch, start, opts.get('closed'))
2894 heads += repo.branchheads(branch, start, opts.get('closed'))
2895 heads = [repo[h] for h in heads]
2895 heads = [repo[h] for h in heads]
2896
2896
2897 if branchrevs:
2897 if branchrevs:
2898 branches = set(repo[br].branch() for br in branchrevs)
2898 branches = set(repo[br].branch() for br in branchrevs)
2899 heads = [h for h in heads if h.branch() in branches]
2899 heads = [h for h in heads if h.branch() in branches]
2900
2900
2901 if opts.get('active') and branchrevs:
2901 if opts.get('active') and branchrevs:
2902 dagheads = repo.heads(start)
2902 dagheads = repo.heads(start)
2903 heads = [h for h in heads if h.node() in dagheads]
2903 heads = [h for h in heads if h.node() in dagheads]
2904
2904
2905 if branchrevs:
2905 if branchrevs:
2906 haveheads = set(h.branch() for h in heads)
2906 haveheads = set(h.branch() for h in heads)
2907 if branches - haveheads:
2907 if branches - haveheads:
2908 headless = ', '.join(b for b in branches - haveheads)
2908 headless = ', '.join(b for b in branches - haveheads)
2909 msg = _('no open branch heads found on branches %s')
2909 msg = _('no open branch heads found on branches %s')
2910 if opts.get('rev'):
2910 if opts.get('rev'):
2911 msg += _(' (started at %s)' % opts['rev'])
2911 msg += _(' (started at %s)') % opts['rev']
2912 ui.warn((msg + '\n') % headless)
2912 ui.warn((msg + '\n') % headless)
2913
2913
2914 if not heads:
2914 if not heads:
2915 return 1
2915 return 1
2916
2916
2917 heads = sorted(heads, key=lambda x: -x.rev())
2917 heads = sorted(heads, key=lambda x: -x.rev())
2918 displayer = cmdutil.show_changeset(ui, repo, opts)
2918 displayer = cmdutil.show_changeset(ui, repo, opts)
2919 for ctx in heads:
2919 for ctx in heads:
2920 displayer.show(ctx)
2920 displayer.show(ctx)
2921 displayer.close()
2921 displayer.close()
2922
2922
2923 @command('help',
2923 @command('help',
2924 [('e', 'extension', None, _('show only help for extensions')),
2924 [('e', 'extension', None, _('show only help for extensions')),
2925 ('c', 'command', None, _('show only help for commands'))],
2925 ('c', 'command', None, _('show only help for commands'))],
2926 _('[-ec] [TOPIC]'))
2926 _('[-ec] [TOPIC]'))
2927 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2927 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2928 """show help for a given topic or a help overview
2928 """show help for a given topic or a help overview
2929
2929
2930 With no arguments, print a list of commands with short help messages.
2930 With no arguments, print a list of commands with short help messages.
2931
2931
2932 Given a topic, extension, or command name, print help for that
2932 Given a topic, extension, or command name, print help for that
2933 topic.
2933 topic.
2934
2934
2935 Returns 0 if successful.
2935 Returns 0 if successful.
2936 """
2936 """
2937
2937
2938 textwidth = min(ui.termwidth(), 80) - 2
2938 textwidth = min(ui.termwidth(), 80) - 2
2939
2939
2940 def optrst(options):
2940 def optrst(options):
2941 data = []
2941 data = []
2942 multioccur = False
2942 multioccur = False
2943 for option in options:
2943 for option in options:
2944 if len(option) == 5:
2944 if len(option) == 5:
2945 shortopt, longopt, default, desc, optlabel = option
2945 shortopt, longopt, default, desc, optlabel = option
2946 else:
2946 else:
2947 shortopt, longopt, default, desc = option
2947 shortopt, longopt, default, desc = option
2948 optlabel = _("VALUE") # default label
2948 optlabel = _("VALUE") # default label
2949
2949
2950 if _("DEPRECATED") in desc and not ui.verbose:
2950 if _("DEPRECATED") in desc and not ui.verbose:
2951 continue
2951 continue
2952
2952
2953 so = ''
2953 so = ''
2954 if shortopt:
2954 if shortopt:
2955 so = '-' + shortopt
2955 so = '-' + shortopt
2956 lo = '--' + longopt
2956 lo = '--' + longopt
2957 if default:
2957 if default:
2958 desc += _(" (default: %s)") % default
2958 desc += _(" (default: %s)") % default
2959
2959
2960 if isinstance(default, list):
2960 if isinstance(default, list):
2961 lo += " %s [+]" % optlabel
2961 lo += " %s [+]" % optlabel
2962 multioccur = True
2962 multioccur = True
2963 elif (default is not None) and not isinstance(default, bool):
2963 elif (default is not None) and not isinstance(default, bool):
2964 lo += " %s" % optlabel
2964 lo += " %s" % optlabel
2965
2965
2966 data.append((so, lo, desc))
2966 data.append((so, lo, desc))
2967
2967
2968 rst = minirst.maketable(data, 1)
2968 rst = minirst.maketable(data, 1)
2969
2969
2970 if multioccur:
2970 if multioccur:
2971 rst += _("\n[+] marked option can be specified multiple times\n")
2971 rst += _("\n[+] marked option can be specified multiple times\n")
2972
2972
2973 return rst
2973 return rst
2974
2974
2975 # list all option lists
2975 # list all option lists
2976 def opttext(optlist, width):
2976 def opttext(optlist, width):
2977 rst = ''
2977 rst = ''
2978 if not optlist:
2978 if not optlist:
2979 return ''
2979 return ''
2980
2980
2981 for title, options in optlist:
2981 for title, options in optlist:
2982 rst += '\n%s\n' % title
2982 rst += '\n%s\n' % title
2983 if options:
2983 if options:
2984 rst += "\n"
2984 rst += "\n"
2985 rst += optrst(options)
2985 rst += optrst(options)
2986 rst += '\n'
2986 rst += '\n'
2987
2987
2988 return '\n' + minirst.format(rst, width)
2988 return '\n' + minirst.format(rst, width)
2989
2989
2990 def addglobalopts(optlist, aliases):
2990 def addglobalopts(optlist, aliases):
2991 if ui.quiet:
2991 if ui.quiet:
2992 return []
2992 return []
2993
2993
2994 if ui.verbose:
2994 if ui.verbose:
2995 optlist.append((_("global options:"), globalopts))
2995 optlist.append((_("global options:"), globalopts))
2996 if name == 'shortlist':
2996 if name == 'shortlist':
2997 optlist.append((_('use "hg help" for the full list '
2997 optlist.append((_('use "hg help" for the full list '
2998 'of commands'), ()))
2998 'of commands'), ()))
2999 else:
2999 else:
3000 if name == 'shortlist':
3000 if name == 'shortlist':
3001 msg = _('use "hg help" for the full list of commands '
3001 msg = _('use "hg help" for the full list of commands '
3002 'or "hg -v" for details')
3002 'or "hg -v" for details')
3003 elif name and not full:
3003 elif name and not full:
3004 msg = _('use "hg help %s" to show the full help text' % name)
3004 msg = _('use "hg help %s" to show the full help text') % name
3005 elif aliases:
3005 elif aliases:
3006 msg = _('use "hg -v help%s" to show builtin aliases and '
3006 msg = _('use "hg -v help%s" to show builtin aliases and '
3007 'global options') % (name and " " + name or "")
3007 'global options') % (name and " " + name or "")
3008 else:
3008 else:
3009 msg = _('use "hg -v help %s" to show more info') % name
3009 msg = _('use "hg -v help %s" to show more info') % name
3010 optlist.append((msg, ()))
3010 optlist.append((msg, ()))
3011
3011
3012 def helpcmd(name):
3012 def helpcmd(name):
3013 try:
3013 try:
3014 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3014 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3015 except error.AmbiguousCommand, inst:
3015 except error.AmbiguousCommand, inst:
3016 # py3k fix: except vars can't be used outside the scope of the
3016 # py3k fix: except vars can't be used outside the scope of the
3017 # except block, nor can be used inside a lambda. python issue4617
3017 # except block, nor can be used inside a lambda. python issue4617
3018 prefix = inst.args[0]
3018 prefix = inst.args[0]
3019 select = lambda c: c.lstrip('^').startswith(prefix)
3019 select = lambda c: c.lstrip('^').startswith(prefix)
3020 helplist(select)
3020 helplist(select)
3021 return
3021 return
3022
3022
3023 # check if it's an invalid alias and display its error if it is
3023 # check if it's an invalid alias and display its error if it is
3024 if getattr(entry[0], 'badalias', False):
3024 if getattr(entry[0], 'badalias', False):
3025 if not unknowncmd:
3025 if not unknowncmd:
3026 entry[0](ui)
3026 entry[0](ui)
3027 return
3027 return
3028
3028
3029 rst = ""
3029 rst = ""
3030
3030
3031 # synopsis
3031 # synopsis
3032 if len(entry) > 2:
3032 if len(entry) > 2:
3033 if entry[2].startswith('hg'):
3033 if entry[2].startswith('hg'):
3034 rst += "%s\n" % entry[2]
3034 rst += "%s\n" % entry[2]
3035 else:
3035 else:
3036 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3036 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3037 else:
3037 else:
3038 rst += 'hg %s\n' % aliases[0]
3038 rst += 'hg %s\n' % aliases[0]
3039
3039
3040 # aliases
3040 # aliases
3041 if full and not ui.quiet and len(aliases) > 1:
3041 if full and not ui.quiet and len(aliases) > 1:
3042 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3042 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3043
3043
3044 # description
3044 # description
3045 doc = gettext(entry[0].__doc__)
3045 doc = gettext(entry[0].__doc__)
3046 if not doc:
3046 if not doc:
3047 doc = _("(no help text available)")
3047 doc = _("(no help text available)")
3048 if util.safehasattr(entry[0], 'definition'): # aliased command
3048 if util.safehasattr(entry[0], 'definition'): # aliased command
3049 if entry[0].definition.startswith('!'): # shell alias
3049 if entry[0].definition.startswith('!'): # shell alias
3050 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3050 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3051 else:
3051 else:
3052 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3052 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3053 if ui.quiet or not full:
3053 if ui.quiet or not full:
3054 doc = doc.splitlines()[0]
3054 doc = doc.splitlines()[0]
3055 rst += "\n" + doc + "\n"
3055 rst += "\n" + doc + "\n"
3056
3056
3057 # check if this command shadows a non-trivial (multi-line)
3057 # check if this command shadows a non-trivial (multi-line)
3058 # extension help text
3058 # extension help text
3059 try:
3059 try:
3060 mod = extensions.find(name)
3060 mod = extensions.find(name)
3061 doc = gettext(mod.__doc__) or ''
3061 doc = gettext(mod.__doc__) or ''
3062 if '\n' in doc.strip():
3062 if '\n' in doc.strip():
3063 msg = _('use "hg help -e %s" to show help for '
3063 msg = _('use "hg help -e %s" to show help for '
3064 'the %s extension') % (name, name)
3064 'the %s extension') % (name, name)
3065 rst += '\n%s\n' % msg
3065 rst += '\n%s\n' % msg
3066 except KeyError:
3066 except KeyError:
3067 pass
3067 pass
3068
3068
3069 # options
3069 # options
3070 if not ui.quiet and entry[1]:
3070 if not ui.quiet and entry[1]:
3071 rst += '\n'
3071 rst += '\n'
3072 rst += _("options:")
3072 rst += _("options:")
3073 rst += '\n\n'
3073 rst += '\n\n'
3074 rst += optrst(entry[1])
3074 rst += optrst(entry[1])
3075
3075
3076 if ui.verbose:
3076 if ui.verbose:
3077 rst += '\n'
3077 rst += '\n'
3078 rst += _("global options:")
3078 rst += _("global options:")
3079 rst += '\n\n'
3079 rst += '\n\n'
3080 rst += optrst(globalopts)
3080 rst += optrst(globalopts)
3081
3081
3082 keep = ui.verbose and ['verbose'] or []
3082 keep = ui.verbose and ['verbose'] or []
3083 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3083 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3084 ui.write(formatted)
3084 ui.write(formatted)
3085
3085
3086 if not ui.verbose:
3086 if not ui.verbose:
3087 if not full:
3087 if not full:
3088 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3088 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3089 % name)
3089 % name)
3090 elif not ui.quiet:
3090 elif not ui.quiet:
3091 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3091 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3092
3092
3093
3093
3094 def helplist(select=None):
3094 def helplist(select=None):
3095 # list of commands
3095 # list of commands
3096 if name == "shortlist":
3096 if name == "shortlist":
3097 header = _('basic commands:\n\n')
3097 header = _('basic commands:\n\n')
3098 else:
3098 else:
3099 header = _('list of commands:\n\n')
3099 header = _('list of commands:\n\n')
3100
3100
3101 h = {}
3101 h = {}
3102 cmds = {}
3102 cmds = {}
3103 for c, e in table.iteritems():
3103 for c, e in table.iteritems():
3104 f = c.split("|", 1)[0]
3104 f = c.split("|", 1)[0]
3105 if select and not select(f):
3105 if select and not select(f):
3106 continue
3106 continue
3107 if (not select and name != 'shortlist' and
3107 if (not select and name != 'shortlist' and
3108 e[0].__module__ != __name__):
3108 e[0].__module__ != __name__):
3109 continue
3109 continue
3110 if name == "shortlist" and not f.startswith("^"):
3110 if name == "shortlist" and not f.startswith("^"):
3111 continue
3111 continue
3112 f = f.lstrip("^")
3112 f = f.lstrip("^")
3113 if not ui.debugflag and f.startswith("debug"):
3113 if not ui.debugflag and f.startswith("debug"):
3114 continue
3114 continue
3115 doc = e[0].__doc__
3115 doc = e[0].__doc__
3116 if doc and 'DEPRECATED' in doc and not ui.verbose:
3116 if doc and 'DEPRECATED' in doc and not ui.verbose:
3117 continue
3117 continue
3118 doc = gettext(doc)
3118 doc = gettext(doc)
3119 if not doc:
3119 if not doc:
3120 doc = _("(no help text available)")
3120 doc = _("(no help text available)")
3121 h[f] = doc.splitlines()[0].rstrip()
3121 h[f] = doc.splitlines()[0].rstrip()
3122 cmds[f] = c.lstrip("^")
3122 cmds[f] = c.lstrip("^")
3123
3123
3124 if not h:
3124 if not h:
3125 ui.status(_('no commands defined\n'))
3125 ui.status(_('no commands defined\n'))
3126 return
3126 return
3127
3127
3128 ui.status(header)
3128 ui.status(header)
3129 fns = sorted(h)
3129 fns = sorted(h)
3130 m = max(map(len, fns))
3130 m = max(map(len, fns))
3131 for f in fns:
3131 for f in fns:
3132 if ui.verbose:
3132 if ui.verbose:
3133 commands = cmds[f].replace("|",", ")
3133 commands = cmds[f].replace("|",", ")
3134 ui.write(" %s:\n %s\n"%(commands, h[f]))
3134 ui.write(" %s:\n %s\n"%(commands, h[f]))
3135 else:
3135 else:
3136 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3136 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3137 initindent=' %-*s ' % (m, f),
3137 initindent=' %-*s ' % (m, f),
3138 hangindent=' ' * (m + 4))))
3138 hangindent=' ' * (m + 4))))
3139
3139
3140 if not name:
3140 if not name:
3141 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3141 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3142 if text:
3142 if text:
3143 ui.write("\n%s" % minirst.format(text, textwidth))
3143 ui.write("\n%s" % minirst.format(text, textwidth))
3144
3144
3145 ui.write(_("\nadditional help topics:\n\n"))
3145 ui.write(_("\nadditional help topics:\n\n"))
3146 topics = []
3146 topics = []
3147 for names, header, doc in help.helptable:
3147 for names, header, doc in help.helptable:
3148 topics.append((sorted(names, key=len, reverse=True)[0], header))
3148 topics.append((sorted(names, key=len, reverse=True)[0], header))
3149 topics_len = max([len(s[0]) for s in topics])
3149 topics_len = max([len(s[0]) for s in topics])
3150 for t, desc in topics:
3150 for t, desc in topics:
3151 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3151 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3152
3152
3153 optlist = []
3153 optlist = []
3154 addglobalopts(optlist, True)
3154 addglobalopts(optlist, True)
3155 ui.write(opttext(optlist, textwidth))
3155 ui.write(opttext(optlist, textwidth))
3156
3156
3157 def helptopic(name):
3157 def helptopic(name):
3158 for names, header, doc in help.helptable:
3158 for names, header, doc in help.helptable:
3159 if name in names:
3159 if name in names:
3160 break
3160 break
3161 else:
3161 else:
3162 raise error.UnknownCommand(name)
3162 raise error.UnknownCommand(name)
3163
3163
3164 # description
3164 # description
3165 if not doc:
3165 if not doc:
3166 doc = _("(no help text available)")
3166 doc = _("(no help text available)")
3167 if util.safehasattr(doc, '__call__'):
3167 if util.safehasattr(doc, '__call__'):
3168 doc = doc()
3168 doc = doc()
3169
3169
3170 ui.write("%s\n\n" % header)
3170 ui.write("%s\n\n" % header)
3171 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3171 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3172 try:
3172 try:
3173 cmdutil.findcmd(name, table)
3173 cmdutil.findcmd(name, table)
3174 ui.write(_('\nuse "hg help -c %s" to see help for '
3174 ui.write(_('\nuse "hg help -c %s" to see help for '
3175 'the %s command\n') % (name, name))
3175 'the %s command\n') % (name, name))
3176 except error.UnknownCommand:
3176 except error.UnknownCommand:
3177 pass
3177 pass
3178
3178
3179 def helpext(name):
3179 def helpext(name):
3180 try:
3180 try:
3181 mod = extensions.find(name)
3181 mod = extensions.find(name)
3182 doc = gettext(mod.__doc__) or _('no help text available')
3182 doc = gettext(mod.__doc__) or _('no help text available')
3183 except KeyError:
3183 except KeyError:
3184 mod = None
3184 mod = None
3185 doc = extensions.disabledext(name)
3185 doc = extensions.disabledext(name)
3186 if not doc:
3186 if not doc:
3187 raise error.UnknownCommand(name)
3187 raise error.UnknownCommand(name)
3188
3188
3189 if '\n' not in doc:
3189 if '\n' not in doc:
3190 head, tail = doc, ""
3190 head, tail = doc, ""
3191 else:
3191 else:
3192 head, tail = doc.split('\n', 1)
3192 head, tail = doc.split('\n', 1)
3193 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3193 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3194 if tail:
3194 if tail:
3195 ui.write(minirst.format(tail, textwidth))
3195 ui.write(minirst.format(tail, textwidth))
3196 ui.status('\n')
3196 ui.status('\n')
3197
3197
3198 if mod:
3198 if mod:
3199 try:
3199 try:
3200 ct = mod.cmdtable
3200 ct = mod.cmdtable
3201 except AttributeError:
3201 except AttributeError:
3202 ct = {}
3202 ct = {}
3203 modcmds = set([c.split('|', 1)[0] for c in ct])
3203 modcmds = set([c.split('|', 1)[0] for c in ct])
3204 helplist(modcmds.__contains__)
3204 helplist(modcmds.__contains__)
3205 else:
3205 else:
3206 ui.write(_('use "hg help extensions" for information on enabling '
3206 ui.write(_('use "hg help extensions" for information on enabling '
3207 'extensions\n'))
3207 'extensions\n'))
3208
3208
3209 def helpextcmd(name):
3209 def helpextcmd(name):
3210 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3210 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3211 doc = gettext(mod.__doc__).splitlines()[0]
3211 doc = gettext(mod.__doc__).splitlines()[0]
3212
3212
3213 msg = help.listexts(_("'%s' is provided by the following "
3213 msg = help.listexts(_("'%s' is provided by the following "
3214 "extension:") % cmd, {ext: doc}, indent=4)
3214 "extension:") % cmd, {ext: doc}, indent=4)
3215 ui.write(minirst.format(msg, textwidth))
3215 ui.write(minirst.format(msg, textwidth))
3216 ui.write('\n')
3216 ui.write('\n')
3217 ui.write(_('use "hg help extensions" for information on enabling '
3217 ui.write(_('use "hg help extensions" for information on enabling '
3218 'extensions\n'))
3218 'extensions\n'))
3219
3219
3220 if name and name != 'shortlist':
3220 if name and name != 'shortlist':
3221 i = None
3221 i = None
3222 if unknowncmd:
3222 if unknowncmd:
3223 queries = (helpextcmd,)
3223 queries = (helpextcmd,)
3224 elif opts.get('extension'):
3224 elif opts.get('extension'):
3225 queries = (helpext,)
3225 queries = (helpext,)
3226 elif opts.get('command'):
3226 elif opts.get('command'):
3227 queries = (helpcmd,)
3227 queries = (helpcmd,)
3228 else:
3228 else:
3229 queries = (helptopic, helpcmd, helpext, helpextcmd)
3229 queries = (helptopic, helpcmd, helpext, helpextcmd)
3230 for f in queries:
3230 for f in queries:
3231 try:
3231 try:
3232 f(name)
3232 f(name)
3233 i = None
3233 i = None
3234 break
3234 break
3235 except error.UnknownCommand, inst:
3235 except error.UnknownCommand, inst:
3236 i = inst
3236 i = inst
3237 if i:
3237 if i:
3238 raise i
3238 raise i
3239 else:
3239 else:
3240 # program name
3240 # program name
3241 ui.status(_("Mercurial Distributed SCM\n"))
3241 ui.status(_("Mercurial Distributed SCM\n"))
3242 ui.status('\n')
3242 ui.status('\n')
3243 helplist()
3243 helplist()
3244
3244
3245
3245
3246 @command('identify|id',
3246 @command('identify|id',
3247 [('r', 'rev', '',
3247 [('r', 'rev', '',
3248 _('identify the specified revision'), _('REV')),
3248 _('identify the specified revision'), _('REV')),
3249 ('n', 'num', None, _('show local revision number')),
3249 ('n', 'num', None, _('show local revision number')),
3250 ('i', 'id', None, _('show global revision id')),
3250 ('i', 'id', None, _('show global revision id')),
3251 ('b', 'branch', None, _('show branch')),
3251 ('b', 'branch', None, _('show branch')),
3252 ('t', 'tags', None, _('show tags')),
3252 ('t', 'tags', None, _('show tags')),
3253 ('B', 'bookmarks', None, _('show bookmarks')),
3253 ('B', 'bookmarks', None, _('show bookmarks')),
3254 ] + remoteopts,
3254 ] + remoteopts,
3255 _('[-nibtB] [-r REV] [SOURCE]'))
3255 _('[-nibtB] [-r REV] [SOURCE]'))
3256 def identify(ui, repo, source=None, rev=None,
3256 def identify(ui, repo, source=None, rev=None,
3257 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3257 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3258 """identify the working copy or specified revision
3258 """identify the working copy or specified revision
3259
3259
3260 Print a summary identifying the repository state at REV using one or
3260 Print a summary identifying the repository state at REV using one or
3261 two parent hash identifiers, followed by a "+" if the working
3261 two parent hash identifiers, followed by a "+" if the working
3262 directory has uncommitted changes, the branch name (if not default),
3262 directory has uncommitted changes, the branch name (if not default),
3263 a list of tags, and a list of bookmarks.
3263 a list of tags, and a list of bookmarks.
3264
3264
3265 When REV is not given, print a summary of the current state of the
3265 When REV is not given, print a summary of the current state of the
3266 repository.
3266 repository.
3267
3267
3268 Specifying a path to a repository root or Mercurial bundle will
3268 Specifying a path to a repository root or Mercurial bundle will
3269 cause lookup to operate on that repository/bundle.
3269 cause lookup to operate on that repository/bundle.
3270
3270
3271 .. container:: verbose
3271 .. container:: verbose
3272
3272
3273 Examples:
3273 Examples:
3274
3274
3275 - generate a build identifier for the working directory::
3275 - generate a build identifier for the working directory::
3276
3276
3277 hg id --id > build-id.dat
3277 hg id --id > build-id.dat
3278
3278
3279 - find the revision corresponding to a tag::
3279 - find the revision corresponding to a tag::
3280
3280
3281 hg id -n -r 1.3
3281 hg id -n -r 1.3
3282
3282
3283 - check the most recent revision of a remote repository::
3283 - check the most recent revision of a remote repository::
3284
3284
3285 hg id -r tip http://selenic.com/hg/
3285 hg id -r tip http://selenic.com/hg/
3286
3286
3287 Returns 0 if successful.
3287 Returns 0 if successful.
3288 """
3288 """
3289
3289
3290 if not repo and not source:
3290 if not repo and not source:
3291 raise util.Abort(_("there is no Mercurial repository here "
3291 raise util.Abort(_("there is no Mercurial repository here "
3292 "(.hg not found)"))
3292 "(.hg not found)"))
3293
3293
3294 hexfunc = ui.debugflag and hex or short
3294 hexfunc = ui.debugflag and hex or short
3295 default = not (num or id or branch or tags or bookmarks)
3295 default = not (num or id or branch or tags or bookmarks)
3296 output = []
3296 output = []
3297 revs = []
3297 revs = []
3298
3298
3299 if source:
3299 if source:
3300 source, branches = hg.parseurl(ui.expandpath(source))
3300 source, branches = hg.parseurl(ui.expandpath(source))
3301 repo = hg.peer(ui, opts, source)
3301 repo = hg.peer(ui, opts, source)
3302 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3302 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3303
3303
3304 if not repo.local():
3304 if not repo.local():
3305 if num or branch or tags:
3305 if num or branch or tags:
3306 raise util.Abort(
3306 raise util.Abort(
3307 _("can't query remote revision number, branch, or tags"))
3307 _("can't query remote revision number, branch, or tags"))
3308 if not rev and revs:
3308 if not rev and revs:
3309 rev = revs[0]
3309 rev = revs[0]
3310 if not rev:
3310 if not rev:
3311 rev = "tip"
3311 rev = "tip"
3312
3312
3313 remoterev = repo.lookup(rev)
3313 remoterev = repo.lookup(rev)
3314 if default or id:
3314 if default or id:
3315 output = [hexfunc(remoterev)]
3315 output = [hexfunc(remoterev)]
3316
3316
3317 def getbms():
3317 def getbms():
3318 bms = []
3318 bms = []
3319
3319
3320 if 'bookmarks' in repo.listkeys('namespaces'):
3320 if 'bookmarks' in repo.listkeys('namespaces'):
3321 hexremoterev = hex(remoterev)
3321 hexremoterev = hex(remoterev)
3322 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3322 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3323 if bmr == hexremoterev]
3323 if bmr == hexremoterev]
3324
3324
3325 return bms
3325 return bms
3326
3326
3327 if bookmarks:
3327 if bookmarks:
3328 output.extend(getbms())
3328 output.extend(getbms())
3329 elif default and not ui.quiet:
3329 elif default and not ui.quiet:
3330 # multiple bookmarks for a single parent separated by '/'
3330 # multiple bookmarks for a single parent separated by '/'
3331 bm = '/'.join(getbms())
3331 bm = '/'.join(getbms())
3332 if bm:
3332 if bm:
3333 output.append(bm)
3333 output.append(bm)
3334 else:
3334 else:
3335 if not rev:
3335 if not rev:
3336 ctx = repo[None]
3336 ctx = repo[None]
3337 parents = ctx.parents()
3337 parents = ctx.parents()
3338 changed = ""
3338 changed = ""
3339 if default or id or num:
3339 if default or id or num:
3340 changed = util.any(repo.status()) and "+" or ""
3340 changed = util.any(repo.status()) and "+" or ""
3341 if default or id:
3341 if default or id:
3342 output = ["%s%s" %
3342 output = ["%s%s" %
3343 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3343 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3344 if num:
3344 if num:
3345 output.append("%s%s" %
3345 output.append("%s%s" %
3346 ('+'.join([str(p.rev()) for p in parents]), changed))
3346 ('+'.join([str(p.rev()) for p in parents]), changed))
3347 else:
3347 else:
3348 ctx = scmutil.revsingle(repo, rev)
3348 ctx = scmutil.revsingle(repo, rev)
3349 if default or id:
3349 if default or id:
3350 output = [hexfunc(ctx.node())]
3350 output = [hexfunc(ctx.node())]
3351 if num:
3351 if num:
3352 output.append(str(ctx.rev()))
3352 output.append(str(ctx.rev()))
3353
3353
3354 if default and not ui.quiet:
3354 if default and not ui.quiet:
3355 b = ctx.branch()
3355 b = ctx.branch()
3356 if b != 'default':
3356 if b != 'default':
3357 output.append("(%s)" % b)
3357 output.append("(%s)" % b)
3358
3358
3359 # multiple tags for a single parent separated by '/'
3359 # multiple tags for a single parent separated by '/'
3360 t = '/'.join(ctx.tags())
3360 t = '/'.join(ctx.tags())
3361 if t:
3361 if t:
3362 output.append(t)
3362 output.append(t)
3363
3363
3364 # multiple bookmarks for a single parent separated by '/'
3364 # multiple bookmarks for a single parent separated by '/'
3365 bm = '/'.join(ctx.bookmarks())
3365 bm = '/'.join(ctx.bookmarks())
3366 if bm:
3366 if bm:
3367 output.append(bm)
3367 output.append(bm)
3368 else:
3368 else:
3369 if branch:
3369 if branch:
3370 output.append(ctx.branch())
3370 output.append(ctx.branch())
3371
3371
3372 if tags:
3372 if tags:
3373 output.extend(ctx.tags())
3373 output.extend(ctx.tags())
3374
3374
3375 if bookmarks:
3375 if bookmarks:
3376 output.extend(ctx.bookmarks())
3376 output.extend(ctx.bookmarks())
3377
3377
3378 ui.write("%s\n" % ' '.join(output))
3378 ui.write("%s\n" % ' '.join(output))
3379
3379
3380 @command('import|patch',
3380 @command('import|patch',
3381 [('p', 'strip', 1,
3381 [('p', 'strip', 1,
3382 _('directory strip option for patch. This has the same '
3382 _('directory strip option for patch. This has the same '
3383 'meaning as the corresponding patch option'), _('NUM')),
3383 'meaning as the corresponding patch option'), _('NUM')),
3384 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3384 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3385 ('e', 'edit', False, _('invoke editor on commit messages')),
3385 ('e', 'edit', False, _('invoke editor on commit messages')),
3386 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3386 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3387 ('', 'no-commit', None,
3387 ('', 'no-commit', None,
3388 _("don't commit, just update the working directory")),
3388 _("don't commit, just update the working directory")),
3389 ('', 'bypass', None,
3389 ('', 'bypass', None,
3390 _("apply patch without touching the working directory")),
3390 _("apply patch without touching the working directory")),
3391 ('', 'exact', None,
3391 ('', 'exact', None,
3392 _('apply patch to the nodes from which it was generated')),
3392 _('apply patch to the nodes from which it was generated')),
3393 ('', 'import-branch', None,
3393 ('', 'import-branch', None,
3394 _('use any branch information in patch (implied by --exact)'))] +
3394 _('use any branch information in patch (implied by --exact)'))] +
3395 commitopts + commitopts2 + similarityopts,
3395 commitopts + commitopts2 + similarityopts,
3396 _('[OPTION]... PATCH...'))
3396 _('[OPTION]... PATCH...'))
3397 def import_(ui, repo, patch1=None, *patches, **opts):
3397 def import_(ui, repo, patch1=None, *patches, **opts):
3398 """import an ordered set of patches
3398 """import an ordered set of patches
3399
3399
3400 Import a list of patches and commit them individually (unless
3400 Import a list of patches and commit them individually (unless
3401 --no-commit is specified).
3401 --no-commit is specified).
3402
3402
3403 If there are outstanding changes in the working directory, import
3403 If there are outstanding changes in the working directory, import
3404 will abort unless given the -f/--force flag.
3404 will abort unless given the -f/--force flag.
3405
3405
3406 You can import a patch straight from a mail message. Even patches
3406 You can import a patch straight from a mail message. Even patches
3407 as attachments work (to use the body part, it must have type
3407 as attachments work (to use the body part, it must have type
3408 text/plain or text/x-patch). From and Subject headers of email
3408 text/plain or text/x-patch). From and Subject headers of email
3409 message are used as default committer and commit message. All
3409 message are used as default committer and commit message. All
3410 text/plain body parts before first diff are added to commit
3410 text/plain body parts before first diff are added to commit
3411 message.
3411 message.
3412
3412
3413 If the imported patch was generated by :hg:`export`, user and
3413 If the imported patch was generated by :hg:`export`, user and
3414 description from patch override values from message headers and
3414 description from patch override values from message headers and
3415 body. Values given on command line with -m/--message and -u/--user
3415 body. Values given on command line with -m/--message and -u/--user
3416 override these.
3416 override these.
3417
3417
3418 If --exact is specified, import will set the working directory to
3418 If --exact is specified, import will set the working directory to
3419 the parent of each patch before applying it, and will abort if the
3419 the parent of each patch before applying it, and will abort if the
3420 resulting changeset has a different ID than the one recorded in
3420 resulting changeset has a different ID than the one recorded in
3421 the patch. This may happen due to character set problems or other
3421 the patch. This may happen due to character set problems or other
3422 deficiencies in the text patch format.
3422 deficiencies in the text patch format.
3423
3423
3424 Use --bypass to apply and commit patches directly to the
3424 Use --bypass to apply and commit patches directly to the
3425 repository, not touching the working directory. Without --exact,
3425 repository, not touching the working directory. Without --exact,
3426 patches will be applied on top of the working directory parent
3426 patches will be applied on top of the working directory parent
3427 revision.
3427 revision.
3428
3428
3429 With -s/--similarity, hg will attempt to discover renames and
3429 With -s/--similarity, hg will attempt to discover renames and
3430 copies in the patch in the same way as :hg:`addremove`.
3430 copies in the patch in the same way as :hg:`addremove`.
3431
3431
3432 To read a patch from standard input, use "-" as the patch name. If
3432 To read a patch from standard input, use "-" as the patch name. If
3433 a URL is specified, the patch will be downloaded from it.
3433 a URL is specified, the patch will be downloaded from it.
3434 See :hg:`help dates` for a list of formats valid for -d/--date.
3434 See :hg:`help dates` for a list of formats valid for -d/--date.
3435
3435
3436 .. container:: verbose
3436 .. container:: verbose
3437
3437
3438 Examples:
3438 Examples:
3439
3439
3440 - import a traditional patch from a website and detect renames::
3440 - import a traditional patch from a website and detect renames::
3441
3441
3442 hg import -s 80 http://example.com/bugfix.patch
3442 hg import -s 80 http://example.com/bugfix.patch
3443
3443
3444 - import a changeset from an hgweb server::
3444 - import a changeset from an hgweb server::
3445
3445
3446 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3446 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3447
3447
3448 - import all the patches in an Unix-style mbox::
3448 - import all the patches in an Unix-style mbox::
3449
3449
3450 hg import incoming-patches.mbox
3450 hg import incoming-patches.mbox
3451
3451
3452 - attempt to exactly restore an exported changeset (not always
3452 - attempt to exactly restore an exported changeset (not always
3453 possible)::
3453 possible)::
3454
3454
3455 hg import --exact proposed-fix.patch
3455 hg import --exact proposed-fix.patch
3456
3456
3457 Returns 0 on success.
3457 Returns 0 on success.
3458 """
3458 """
3459
3459
3460 if not patch1:
3460 if not patch1:
3461 raise util.Abort(_('need at least one patch to import'))
3461 raise util.Abort(_('need at least one patch to import'))
3462
3462
3463 patches = (patch1,) + patches
3463 patches = (patch1,) + patches
3464
3464
3465 date = opts.get('date')
3465 date = opts.get('date')
3466 if date:
3466 if date:
3467 opts['date'] = util.parsedate(date)
3467 opts['date'] = util.parsedate(date)
3468
3468
3469 editor = cmdutil.commiteditor
3469 editor = cmdutil.commiteditor
3470 if opts.get('edit'):
3470 if opts.get('edit'):
3471 editor = cmdutil.commitforceeditor
3471 editor = cmdutil.commitforceeditor
3472
3472
3473 update = not opts.get('bypass')
3473 update = not opts.get('bypass')
3474 if not update and opts.get('no_commit'):
3474 if not update and opts.get('no_commit'):
3475 raise util.Abort(_('cannot use --no-commit with --bypass'))
3475 raise util.Abort(_('cannot use --no-commit with --bypass'))
3476 try:
3476 try:
3477 sim = float(opts.get('similarity') or 0)
3477 sim = float(opts.get('similarity') or 0)
3478 except ValueError:
3478 except ValueError:
3479 raise util.Abort(_('similarity must be a number'))
3479 raise util.Abort(_('similarity must be a number'))
3480 if sim < 0 or sim > 100:
3480 if sim < 0 or sim > 100:
3481 raise util.Abort(_('similarity must be between 0 and 100'))
3481 raise util.Abort(_('similarity must be between 0 and 100'))
3482 if sim and not update:
3482 if sim and not update:
3483 raise util.Abort(_('cannot use --similarity with --bypass'))
3483 raise util.Abort(_('cannot use --similarity with --bypass'))
3484
3484
3485 if (opts.get('exact') or not opts.get('force')) and update:
3485 if (opts.get('exact') or not opts.get('force')) and update:
3486 cmdutil.bailifchanged(repo)
3486 cmdutil.bailifchanged(repo)
3487
3487
3488 base = opts["base"]
3488 base = opts["base"]
3489 strip = opts["strip"]
3489 strip = opts["strip"]
3490 wlock = lock = tr = None
3490 wlock = lock = tr = None
3491 msgs = []
3491 msgs = []
3492
3492
3493 def checkexact(repo, n, nodeid):
3493 def checkexact(repo, n, nodeid):
3494 if opts.get('exact') and hex(n) != nodeid:
3494 if opts.get('exact') and hex(n) != nodeid:
3495 repo.rollback()
3495 repo.rollback()
3496 raise util.Abort(_('patch is damaged or loses information'))
3496 raise util.Abort(_('patch is damaged or loses information'))
3497
3497
3498 def tryone(ui, hunk, parents):
3498 def tryone(ui, hunk, parents):
3499 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3499 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3500 patch.extract(ui, hunk)
3500 patch.extract(ui, hunk)
3501
3501
3502 if not tmpname:
3502 if not tmpname:
3503 return (None, None)
3503 return (None, None)
3504 msg = _('applied to working directory')
3504 msg = _('applied to working directory')
3505
3505
3506 try:
3506 try:
3507 cmdline_message = cmdutil.logmessage(ui, opts)
3507 cmdline_message = cmdutil.logmessage(ui, opts)
3508 if cmdline_message:
3508 if cmdline_message:
3509 # pickup the cmdline msg
3509 # pickup the cmdline msg
3510 message = cmdline_message
3510 message = cmdline_message
3511 elif message:
3511 elif message:
3512 # pickup the patch msg
3512 # pickup the patch msg
3513 message = message.strip()
3513 message = message.strip()
3514 else:
3514 else:
3515 # launch the editor
3515 # launch the editor
3516 message = None
3516 message = None
3517 ui.debug('message:\n%s\n' % message)
3517 ui.debug('message:\n%s\n' % message)
3518
3518
3519 if len(parents) == 1:
3519 if len(parents) == 1:
3520 parents.append(repo[nullid])
3520 parents.append(repo[nullid])
3521 if opts.get('exact'):
3521 if opts.get('exact'):
3522 if not nodeid or not p1:
3522 if not nodeid or not p1:
3523 raise util.Abort(_('not a Mercurial patch'))
3523 raise util.Abort(_('not a Mercurial patch'))
3524 p1 = repo[p1]
3524 p1 = repo[p1]
3525 p2 = repo[p2 or nullid]
3525 p2 = repo[p2 or nullid]
3526 elif p2:
3526 elif p2:
3527 try:
3527 try:
3528 p1 = repo[p1]
3528 p1 = repo[p1]
3529 p2 = repo[p2]
3529 p2 = repo[p2]
3530 # Without any options, consider p2 only if the
3530 # Without any options, consider p2 only if the
3531 # patch is being applied on top of the recorded
3531 # patch is being applied on top of the recorded
3532 # first parent.
3532 # first parent.
3533 if p1 != parents[0]:
3533 if p1 != parents[0]:
3534 p1 = parents[0]
3534 p1 = parents[0]
3535 p2 = repo[nullid]
3535 p2 = repo[nullid]
3536 except error.RepoError:
3536 except error.RepoError:
3537 p1, p2 = parents
3537 p1, p2 = parents
3538 else:
3538 else:
3539 p1, p2 = parents
3539 p1, p2 = parents
3540
3540
3541 n = None
3541 n = None
3542 if update:
3542 if update:
3543 if p1 != parents[0]:
3543 if p1 != parents[0]:
3544 hg.clean(repo, p1.node())
3544 hg.clean(repo, p1.node())
3545 if p2 != parents[1]:
3545 if p2 != parents[1]:
3546 repo.dirstate.setparents(p1.node(), p2.node())
3546 repo.dirstate.setparents(p1.node(), p2.node())
3547
3547
3548 if opts.get('exact') or opts.get('import_branch'):
3548 if opts.get('exact') or opts.get('import_branch'):
3549 repo.dirstate.setbranch(branch or 'default')
3549 repo.dirstate.setbranch(branch or 'default')
3550
3550
3551 files = set()
3551 files = set()
3552 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3552 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3553 eolmode=None, similarity=sim / 100.0)
3553 eolmode=None, similarity=sim / 100.0)
3554 files = list(files)
3554 files = list(files)
3555 if opts.get('no_commit'):
3555 if opts.get('no_commit'):
3556 if message:
3556 if message:
3557 msgs.append(message)
3557 msgs.append(message)
3558 else:
3558 else:
3559 if opts.get('exact') or p2:
3559 if opts.get('exact') or p2:
3560 # If you got here, you either use --force and know what
3560 # If you got here, you either use --force and know what
3561 # you are doing or used --exact or a merge patch while
3561 # you are doing or used --exact or a merge patch while
3562 # being updated to its first parent.
3562 # being updated to its first parent.
3563 m = None
3563 m = None
3564 else:
3564 else:
3565 m = scmutil.matchfiles(repo, files or [])
3565 m = scmutil.matchfiles(repo, files or [])
3566 n = repo.commit(message, opts.get('user') or user,
3566 n = repo.commit(message, opts.get('user') or user,
3567 opts.get('date') or date, match=m,
3567 opts.get('date') or date, match=m,
3568 editor=editor)
3568 editor=editor)
3569 checkexact(repo, n, nodeid)
3569 checkexact(repo, n, nodeid)
3570 else:
3570 else:
3571 if opts.get('exact') or opts.get('import_branch'):
3571 if opts.get('exact') or opts.get('import_branch'):
3572 branch = branch or 'default'
3572 branch = branch or 'default'
3573 else:
3573 else:
3574 branch = p1.branch()
3574 branch = p1.branch()
3575 store = patch.filestore()
3575 store = patch.filestore()
3576 try:
3576 try:
3577 files = set()
3577 files = set()
3578 try:
3578 try:
3579 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3579 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3580 files, eolmode=None)
3580 files, eolmode=None)
3581 except patch.PatchError, e:
3581 except patch.PatchError, e:
3582 raise util.Abort(str(e))
3582 raise util.Abort(str(e))
3583 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3583 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3584 message,
3584 message,
3585 opts.get('user') or user,
3585 opts.get('user') or user,
3586 opts.get('date') or date,
3586 opts.get('date') or date,
3587 branch, files, store,
3587 branch, files, store,
3588 editor=cmdutil.commiteditor)
3588 editor=cmdutil.commiteditor)
3589 repo.savecommitmessage(memctx.description())
3589 repo.savecommitmessage(memctx.description())
3590 n = memctx.commit()
3590 n = memctx.commit()
3591 checkexact(repo, n, nodeid)
3591 checkexact(repo, n, nodeid)
3592 finally:
3592 finally:
3593 store.close()
3593 store.close()
3594 if n:
3594 if n:
3595 # i18n: refers to a short changeset id
3595 # i18n: refers to a short changeset id
3596 msg = _('created %s') % short(n)
3596 msg = _('created %s') % short(n)
3597 return (msg, n)
3597 return (msg, n)
3598 finally:
3598 finally:
3599 os.unlink(tmpname)
3599 os.unlink(tmpname)
3600
3600
3601 try:
3601 try:
3602 try:
3602 try:
3603 wlock = repo.wlock()
3603 wlock = repo.wlock()
3604 lock = repo.lock()
3604 lock = repo.lock()
3605 tr = repo.transaction('import')
3605 tr = repo.transaction('import')
3606 parents = repo.parents()
3606 parents = repo.parents()
3607 for patchurl in patches:
3607 for patchurl in patches:
3608 if patchurl == '-':
3608 if patchurl == '-':
3609 ui.status(_('applying patch from stdin\n'))
3609 ui.status(_('applying patch from stdin\n'))
3610 patchfile = ui.fin
3610 patchfile = ui.fin
3611 patchurl = 'stdin' # for error message
3611 patchurl = 'stdin' # for error message
3612 else:
3612 else:
3613 patchurl = os.path.join(base, patchurl)
3613 patchurl = os.path.join(base, patchurl)
3614 ui.status(_('applying %s\n') % patchurl)
3614 ui.status(_('applying %s\n') % patchurl)
3615 patchfile = url.open(ui, patchurl)
3615 patchfile = url.open(ui, patchurl)
3616
3616
3617 haspatch = False
3617 haspatch = False
3618 for hunk in patch.split(patchfile):
3618 for hunk in patch.split(patchfile):
3619 (msg, node) = tryone(ui, hunk, parents)
3619 (msg, node) = tryone(ui, hunk, parents)
3620 if msg:
3620 if msg:
3621 haspatch = True
3621 haspatch = True
3622 ui.note(msg + '\n')
3622 ui.note(msg + '\n')
3623 if update or opts.get('exact'):
3623 if update or opts.get('exact'):
3624 parents = repo.parents()
3624 parents = repo.parents()
3625 else:
3625 else:
3626 parents = [repo[node]]
3626 parents = [repo[node]]
3627
3627
3628 if not haspatch:
3628 if not haspatch:
3629 raise util.Abort(_('%s: no diffs found') % patchurl)
3629 raise util.Abort(_('%s: no diffs found') % patchurl)
3630
3630
3631 tr.close()
3631 tr.close()
3632 if msgs:
3632 if msgs:
3633 repo.savecommitmessage('\n* * *\n'.join(msgs))
3633 repo.savecommitmessage('\n* * *\n'.join(msgs))
3634 except:
3634 except:
3635 # wlock.release() indirectly calls dirstate.write(): since
3635 # wlock.release() indirectly calls dirstate.write(): since
3636 # we're crashing, we do not want to change the working dir
3636 # we're crashing, we do not want to change the working dir
3637 # parent after all, so make sure it writes nothing
3637 # parent after all, so make sure it writes nothing
3638 repo.dirstate.invalidate()
3638 repo.dirstate.invalidate()
3639 raise
3639 raise
3640 finally:
3640 finally:
3641 if tr:
3641 if tr:
3642 tr.release()
3642 tr.release()
3643 release(lock, wlock)
3643 release(lock, wlock)
3644
3644
3645 @command('incoming|in',
3645 @command('incoming|in',
3646 [('f', 'force', None,
3646 [('f', 'force', None,
3647 _('run even if remote repository is unrelated')),
3647 _('run even if remote repository is unrelated')),
3648 ('n', 'newest-first', None, _('show newest record first')),
3648 ('n', 'newest-first', None, _('show newest record first')),
3649 ('', 'bundle', '',
3649 ('', 'bundle', '',
3650 _('file to store the bundles into'), _('FILE')),
3650 _('file to store the bundles into'), _('FILE')),
3651 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3651 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3652 ('B', 'bookmarks', False, _("compare bookmarks")),
3652 ('B', 'bookmarks', False, _("compare bookmarks")),
3653 ('b', 'branch', [],
3653 ('b', 'branch', [],
3654 _('a specific branch you would like to pull'), _('BRANCH')),
3654 _('a specific branch you would like to pull'), _('BRANCH')),
3655 ] + logopts + remoteopts + subrepoopts,
3655 ] + logopts + remoteopts + subrepoopts,
3656 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3656 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3657 def incoming(ui, repo, source="default", **opts):
3657 def incoming(ui, repo, source="default", **opts):
3658 """show new changesets found in source
3658 """show new changesets found in source
3659
3659
3660 Show new changesets found in the specified path/URL or the default
3660 Show new changesets found in the specified path/URL or the default
3661 pull location. These are the changesets that would have been pulled
3661 pull location. These are the changesets that would have been pulled
3662 if a pull at the time you issued this command.
3662 if a pull at the time you issued this command.
3663
3663
3664 For remote repository, using --bundle avoids downloading the
3664 For remote repository, using --bundle avoids downloading the
3665 changesets twice if the incoming is followed by a pull.
3665 changesets twice if the incoming is followed by a pull.
3666
3666
3667 See pull for valid source format details.
3667 See pull for valid source format details.
3668
3668
3669 Returns 0 if there are incoming changes, 1 otherwise.
3669 Returns 0 if there are incoming changes, 1 otherwise.
3670 """
3670 """
3671 if opts.get('bundle') and opts.get('subrepos'):
3671 if opts.get('bundle') and opts.get('subrepos'):
3672 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3672 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3673
3673
3674 if opts.get('bookmarks'):
3674 if opts.get('bookmarks'):
3675 source, branches = hg.parseurl(ui.expandpath(source),
3675 source, branches = hg.parseurl(ui.expandpath(source),
3676 opts.get('branch'))
3676 opts.get('branch'))
3677 other = hg.peer(repo, opts, source)
3677 other = hg.peer(repo, opts, source)
3678 if 'bookmarks' not in other.listkeys('namespaces'):
3678 if 'bookmarks' not in other.listkeys('namespaces'):
3679 ui.warn(_("remote doesn't support bookmarks\n"))
3679 ui.warn(_("remote doesn't support bookmarks\n"))
3680 return 0
3680 return 0
3681 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3681 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3682 return bookmarks.diff(ui, repo, other)
3682 return bookmarks.diff(ui, repo, other)
3683
3683
3684 repo._subtoppath = ui.expandpath(source)
3684 repo._subtoppath = ui.expandpath(source)
3685 try:
3685 try:
3686 return hg.incoming(ui, repo, source, opts)
3686 return hg.incoming(ui, repo, source, opts)
3687 finally:
3687 finally:
3688 del repo._subtoppath
3688 del repo._subtoppath
3689
3689
3690
3690
3691 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3691 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3692 def init(ui, dest=".", **opts):
3692 def init(ui, dest=".", **opts):
3693 """create a new repository in the given directory
3693 """create a new repository in the given directory
3694
3694
3695 Initialize a new repository in the given directory. If the given
3695 Initialize a new repository in the given directory. If the given
3696 directory does not exist, it will be created.
3696 directory does not exist, it will be created.
3697
3697
3698 If no directory is given, the current directory is used.
3698 If no directory is given, the current directory is used.
3699
3699
3700 It is possible to specify an ``ssh://`` URL as the destination.
3700 It is possible to specify an ``ssh://`` URL as the destination.
3701 See :hg:`help urls` for more information.
3701 See :hg:`help urls` for more information.
3702
3702
3703 Returns 0 on success.
3703 Returns 0 on success.
3704 """
3704 """
3705 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3705 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3706
3706
3707 @command('locate',
3707 @command('locate',
3708 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3708 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3709 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3709 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3710 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3710 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3711 ] + walkopts,
3711 ] + walkopts,
3712 _('[OPTION]... [PATTERN]...'))
3712 _('[OPTION]... [PATTERN]...'))
3713 def locate(ui, repo, *pats, **opts):
3713 def locate(ui, repo, *pats, **opts):
3714 """locate files matching specific patterns
3714 """locate files matching specific patterns
3715
3715
3716 Print files under Mercurial control in the working directory whose
3716 Print files under Mercurial control in the working directory whose
3717 names match the given patterns.
3717 names match the given patterns.
3718
3718
3719 By default, this command searches all directories in the working
3719 By default, this command searches all directories in the working
3720 directory. To search just the current directory and its
3720 directory. To search just the current directory and its
3721 subdirectories, use "--include .".
3721 subdirectories, use "--include .".
3722
3722
3723 If no patterns are given to match, this command prints the names
3723 If no patterns are given to match, this command prints the names
3724 of all files under Mercurial control in the working directory.
3724 of all files under Mercurial control in the working directory.
3725
3725
3726 If you want to feed the output of this command into the "xargs"
3726 If you want to feed the output of this command into the "xargs"
3727 command, use the -0 option to both this command and "xargs". This
3727 command, use the -0 option to both this command and "xargs". This
3728 will avoid the problem of "xargs" treating single filenames that
3728 will avoid the problem of "xargs" treating single filenames that
3729 contain whitespace as multiple filenames.
3729 contain whitespace as multiple filenames.
3730
3730
3731 Returns 0 if a match is found, 1 otherwise.
3731 Returns 0 if a match is found, 1 otherwise.
3732 """
3732 """
3733 end = opts.get('print0') and '\0' or '\n'
3733 end = opts.get('print0') and '\0' or '\n'
3734 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3734 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3735
3735
3736 ret = 1
3736 ret = 1
3737 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3737 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3738 m.bad = lambda x, y: False
3738 m.bad = lambda x, y: False
3739 for abs in repo[rev].walk(m):
3739 for abs in repo[rev].walk(m):
3740 if not rev and abs not in repo.dirstate:
3740 if not rev and abs not in repo.dirstate:
3741 continue
3741 continue
3742 if opts.get('fullpath'):
3742 if opts.get('fullpath'):
3743 ui.write(repo.wjoin(abs), end)
3743 ui.write(repo.wjoin(abs), end)
3744 else:
3744 else:
3745 ui.write(((pats and m.rel(abs)) or abs), end)
3745 ui.write(((pats and m.rel(abs)) or abs), end)
3746 ret = 0
3746 ret = 0
3747
3747
3748 return ret
3748 return ret
3749
3749
3750 @command('^log|history',
3750 @command('^log|history',
3751 [('f', 'follow', None,
3751 [('f', 'follow', None,
3752 _('follow changeset history, or file history across copies and renames')),
3752 _('follow changeset history, or file history across copies and renames')),
3753 ('', 'follow-first', None,
3753 ('', 'follow-first', None,
3754 _('only follow the first parent of merge changesets (DEPRECATED)')),
3754 _('only follow the first parent of merge changesets (DEPRECATED)')),
3755 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3755 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3756 ('C', 'copies', None, _('show copied files')),
3756 ('C', 'copies', None, _('show copied files')),
3757 ('k', 'keyword', [],
3757 ('k', 'keyword', [],
3758 _('do case-insensitive search for a given text'), _('TEXT')),
3758 _('do case-insensitive search for a given text'), _('TEXT')),
3759 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3759 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3760 ('', 'removed', None, _('include revisions where files were removed')),
3760 ('', 'removed', None, _('include revisions where files were removed')),
3761 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3761 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3762 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3762 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3763 ('', 'only-branch', [],
3763 ('', 'only-branch', [],
3764 _('show only changesets within the given named branch (DEPRECATED)'),
3764 _('show only changesets within the given named branch (DEPRECATED)'),
3765 _('BRANCH')),
3765 _('BRANCH')),
3766 ('b', 'branch', [],
3766 ('b', 'branch', [],
3767 _('show changesets within the given named branch'), _('BRANCH')),
3767 _('show changesets within the given named branch'), _('BRANCH')),
3768 ('P', 'prune', [],
3768 ('P', 'prune', [],
3769 _('do not display revision or any of its ancestors'), _('REV')),
3769 _('do not display revision or any of its ancestors'), _('REV')),
3770 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3770 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3771 ] + logopts + walkopts,
3771 ] + logopts + walkopts,
3772 _('[OPTION]... [FILE]'))
3772 _('[OPTION]... [FILE]'))
3773 def log(ui, repo, *pats, **opts):
3773 def log(ui, repo, *pats, **opts):
3774 """show revision history of entire repository or files
3774 """show revision history of entire repository or files
3775
3775
3776 Print the revision history of the specified files or the entire
3776 Print the revision history of the specified files or the entire
3777 project.
3777 project.
3778
3778
3779 If no revision range is specified, the default is ``tip:0`` unless
3779 If no revision range is specified, the default is ``tip:0`` unless
3780 --follow is set, in which case the working directory parent is
3780 --follow is set, in which case the working directory parent is
3781 used as the starting revision.
3781 used as the starting revision.
3782
3782
3783 File history is shown without following rename or copy history of
3783 File history is shown without following rename or copy history of
3784 files. Use -f/--follow with a filename to follow history across
3784 files. Use -f/--follow with a filename to follow history across
3785 renames and copies. --follow without a filename will only show
3785 renames and copies. --follow without a filename will only show
3786 ancestors or descendants of the starting revision.
3786 ancestors or descendants of the starting revision.
3787
3787
3788 By default this command prints revision number and changeset id,
3788 By default this command prints revision number and changeset id,
3789 tags, non-trivial parents, user, date and time, and a summary for
3789 tags, non-trivial parents, user, date and time, and a summary for
3790 each commit. When the -v/--verbose switch is used, the list of
3790 each commit. When the -v/--verbose switch is used, the list of
3791 changed files and full commit message are shown.
3791 changed files and full commit message are shown.
3792
3792
3793 .. note::
3793 .. note::
3794 log -p/--patch may generate unexpected diff output for merge
3794 log -p/--patch may generate unexpected diff output for merge
3795 changesets, as it will only compare the merge changeset against
3795 changesets, as it will only compare the merge changeset against
3796 its first parent. Also, only files different from BOTH parents
3796 its first parent. Also, only files different from BOTH parents
3797 will appear in files:.
3797 will appear in files:.
3798
3798
3799 .. note::
3799 .. note::
3800 for performance reasons, log FILE may omit duplicate changes
3800 for performance reasons, log FILE may omit duplicate changes
3801 made on branches and will not show deletions. To see all
3801 made on branches and will not show deletions. To see all
3802 changes including duplicates and deletions, use the --removed
3802 changes including duplicates and deletions, use the --removed
3803 switch.
3803 switch.
3804
3804
3805 .. container:: verbose
3805 .. container:: verbose
3806
3806
3807 Some examples:
3807 Some examples:
3808
3808
3809 - changesets with full descriptions and file lists::
3809 - changesets with full descriptions and file lists::
3810
3810
3811 hg log -v
3811 hg log -v
3812
3812
3813 - changesets ancestral to the working directory::
3813 - changesets ancestral to the working directory::
3814
3814
3815 hg log -f
3815 hg log -f
3816
3816
3817 - last 10 commits on the current branch::
3817 - last 10 commits on the current branch::
3818
3818
3819 hg log -l 10 -b .
3819 hg log -l 10 -b .
3820
3820
3821 - changesets showing all modifications of a file, including removals::
3821 - changesets showing all modifications of a file, including removals::
3822
3822
3823 hg log --removed file.c
3823 hg log --removed file.c
3824
3824
3825 - all changesets that touch a directory, with diffs, excluding merges::
3825 - all changesets that touch a directory, with diffs, excluding merges::
3826
3826
3827 hg log -Mp lib/
3827 hg log -Mp lib/
3828
3828
3829 - all revision numbers that match a keyword::
3829 - all revision numbers that match a keyword::
3830
3830
3831 hg log -k bug --template "{rev}\\n"
3831 hg log -k bug --template "{rev}\\n"
3832
3832
3833 - check if a given changeset is included is a tagged release::
3833 - check if a given changeset is included is a tagged release::
3834
3834
3835 hg log -r "a21ccf and ancestor(1.9)"
3835 hg log -r "a21ccf and ancestor(1.9)"
3836
3836
3837 - find all changesets by some user in a date range::
3837 - find all changesets by some user in a date range::
3838
3838
3839 hg log -k alice -d "may 2008 to jul 2008"
3839 hg log -k alice -d "may 2008 to jul 2008"
3840
3840
3841 - summary of all changesets after the last tag::
3841 - summary of all changesets after the last tag::
3842
3842
3843 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3843 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3844
3844
3845 See :hg:`help dates` for a list of formats valid for -d/--date.
3845 See :hg:`help dates` for a list of formats valid for -d/--date.
3846
3846
3847 See :hg:`help revisions` and :hg:`help revsets` for more about
3847 See :hg:`help revisions` and :hg:`help revsets` for more about
3848 specifying revisions.
3848 specifying revisions.
3849
3849
3850 Returns 0 on success.
3850 Returns 0 on success.
3851 """
3851 """
3852
3852
3853 matchfn = scmutil.match(repo[None], pats, opts)
3853 matchfn = scmutil.match(repo[None], pats, opts)
3854 limit = cmdutil.loglimit(opts)
3854 limit = cmdutil.loglimit(opts)
3855 count = 0
3855 count = 0
3856
3856
3857 getrenamed, endrev = None, None
3857 getrenamed, endrev = None, None
3858 if opts.get('copies'):
3858 if opts.get('copies'):
3859 if opts.get('rev'):
3859 if opts.get('rev'):
3860 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3860 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3861 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3861 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3862
3862
3863 df = False
3863 df = False
3864 if opts["date"]:
3864 if opts["date"]:
3865 df = util.matchdate(opts["date"])
3865 df = util.matchdate(opts["date"])
3866
3866
3867 branches = opts.get('branch', []) + opts.get('only_branch', [])
3867 branches = opts.get('branch', []) + opts.get('only_branch', [])
3868 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3868 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3869
3869
3870 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3870 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3871 def prep(ctx, fns):
3871 def prep(ctx, fns):
3872 rev = ctx.rev()
3872 rev = ctx.rev()
3873 parents = [p for p in repo.changelog.parentrevs(rev)
3873 parents = [p for p in repo.changelog.parentrevs(rev)
3874 if p != nullrev]
3874 if p != nullrev]
3875 if opts.get('no_merges') and len(parents) == 2:
3875 if opts.get('no_merges') and len(parents) == 2:
3876 return
3876 return
3877 if opts.get('only_merges') and len(parents) != 2:
3877 if opts.get('only_merges') and len(parents) != 2:
3878 return
3878 return
3879 if opts.get('branch') and ctx.branch() not in opts['branch']:
3879 if opts.get('branch') and ctx.branch() not in opts['branch']:
3880 return
3880 return
3881 if not opts.get('hidden') and ctx.hidden():
3881 if not opts.get('hidden') and ctx.hidden():
3882 return
3882 return
3883 if df and not df(ctx.date()[0]):
3883 if df and not df(ctx.date()[0]):
3884 return
3884 return
3885
3885
3886 lower = encoding.lower
3886 lower = encoding.lower
3887 if opts.get('user'):
3887 if opts.get('user'):
3888 luser = lower(ctx.user())
3888 luser = lower(ctx.user())
3889 for k in [lower(x) for x in opts['user']]:
3889 for k in [lower(x) for x in opts['user']]:
3890 if (k in luser):
3890 if (k in luser):
3891 break
3891 break
3892 else:
3892 else:
3893 return
3893 return
3894 if opts.get('keyword'):
3894 if opts.get('keyword'):
3895 luser = lower(ctx.user())
3895 luser = lower(ctx.user())
3896 ldesc = lower(ctx.description())
3896 ldesc = lower(ctx.description())
3897 lfiles = lower(" ".join(ctx.files()))
3897 lfiles = lower(" ".join(ctx.files()))
3898 for k in [lower(x) for x in opts['keyword']]:
3898 for k in [lower(x) for x in opts['keyword']]:
3899 if (k in luser or k in ldesc or k in lfiles):
3899 if (k in luser or k in ldesc or k in lfiles):
3900 break
3900 break
3901 else:
3901 else:
3902 return
3902 return
3903
3903
3904 copies = None
3904 copies = None
3905 if getrenamed is not None and rev:
3905 if getrenamed is not None and rev:
3906 copies = []
3906 copies = []
3907 for fn in ctx.files():
3907 for fn in ctx.files():
3908 rename = getrenamed(fn, rev)
3908 rename = getrenamed(fn, rev)
3909 if rename:
3909 if rename:
3910 copies.append((fn, rename[0]))
3910 copies.append((fn, rename[0]))
3911
3911
3912 revmatchfn = None
3912 revmatchfn = None
3913 if opts.get('patch') or opts.get('stat'):
3913 if opts.get('patch') or opts.get('stat'):
3914 if opts.get('follow') or opts.get('follow_first'):
3914 if opts.get('follow') or opts.get('follow_first'):
3915 # note: this might be wrong when following through merges
3915 # note: this might be wrong when following through merges
3916 revmatchfn = scmutil.match(repo[None], fns, default='path')
3916 revmatchfn = scmutil.match(repo[None], fns, default='path')
3917 else:
3917 else:
3918 revmatchfn = matchfn
3918 revmatchfn = matchfn
3919
3919
3920 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3920 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3921
3921
3922 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3922 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3923 if count == limit:
3923 if count == limit:
3924 break
3924 break
3925 if displayer.flush(ctx.rev()):
3925 if displayer.flush(ctx.rev()):
3926 count += 1
3926 count += 1
3927 displayer.close()
3927 displayer.close()
3928
3928
3929 @command('manifest',
3929 @command('manifest',
3930 [('r', 'rev', '', _('revision to display'), _('REV')),
3930 [('r', 'rev', '', _('revision to display'), _('REV')),
3931 ('', 'all', False, _("list files from all revisions"))],
3931 ('', 'all', False, _("list files from all revisions"))],
3932 _('[-r REV]'))
3932 _('[-r REV]'))
3933 def manifest(ui, repo, node=None, rev=None, **opts):
3933 def manifest(ui, repo, node=None, rev=None, **opts):
3934 """output the current or given revision of the project manifest
3934 """output the current or given revision of the project manifest
3935
3935
3936 Print a list of version controlled files for the given revision.
3936 Print a list of version controlled files for the given revision.
3937 If no revision is given, the first parent of the working directory
3937 If no revision is given, the first parent of the working directory
3938 is used, or the null revision if no revision is checked out.
3938 is used, or the null revision if no revision is checked out.
3939
3939
3940 With -v, print file permissions, symlink and executable bits.
3940 With -v, print file permissions, symlink and executable bits.
3941 With --debug, print file revision hashes.
3941 With --debug, print file revision hashes.
3942
3942
3943 If option --all is specified, the list of all files from all revisions
3943 If option --all is specified, the list of all files from all revisions
3944 is printed. This includes deleted and renamed files.
3944 is printed. This includes deleted and renamed files.
3945
3945
3946 Returns 0 on success.
3946 Returns 0 on success.
3947 """
3947 """
3948 if opts.get('all'):
3948 if opts.get('all'):
3949 if rev or node:
3949 if rev or node:
3950 raise util.Abort(_("can't specify a revision with --all"))
3950 raise util.Abort(_("can't specify a revision with --all"))
3951
3951
3952 res = []
3952 res = []
3953 prefix = "data/"
3953 prefix = "data/"
3954 suffix = ".i"
3954 suffix = ".i"
3955 plen = len(prefix)
3955 plen = len(prefix)
3956 slen = len(suffix)
3956 slen = len(suffix)
3957 lock = repo.lock()
3957 lock = repo.lock()
3958 try:
3958 try:
3959 for fn, b, size in repo.store.datafiles():
3959 for fn, b, size in repo.store.datafiles():
3960 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3960 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3961 res.append(fn[plen:-slen])
3961 res.append(fn[plen:-slen])
3962 finally:
3962 finally:
3963 lock.release()
3963 lock.release()
3964 for f in sorted(res):
3964 for f in sorted(res):
3965 ui.write("%s\n" % f)
3965 ui.write("%s\n" % f)
3966 return
3966 return
3967
3967
3968 if rev and node:
3968 if rev and node:
3969 raise util.Abort(_("please specify just one revision"))
3969 raise util.Abort(_("please specify just one revision"))
3970
3970
3971 if not node:
3971 if not node:
3972 node = rev
3972 node = rev
3973
3973
3974 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3974 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3975 ctx = scmutil.revsingle(repo, node)
3975 ctx = scmutil.revsingle(repo, node)
3976 for f in ctx:
3976 for f in ctx:
3977 if ui.debugflag:
3977 if ui.debugflag:
3978 ui.write("%40s " % hex(ctx.manifest()[f]))
3978 ui.write("%40s " % hex(ctx.manifest()[f]))
3979 if ui.verbose:
3979 if ui.verbose:
3980 ui.write(decor[ctx.flags(f)])
3980 ui.write(decor[ctx.flags(f)])
3981 ui.write("%s\n" % f)
3981 ui.write("%s\n" % f)
3982
3982
3983 @command('^merge',
3983 @command('^merge',
3984 [('f', 'force', None, _('force a merge with outstanding changes')),
3984 [('f', 'force', None, _('force a merge with outstanding changes')),
3985 ('r', 'rev', '', _('revision to merge'), _('REV')),
3985 ('r', 'rev', '', _('revision to merge'), _('REV')),
3986 ('P', 'preview', None,
3986 ('P', 'preview', None,
3987 _('review revisions to merge (no merge is performed)'))
3987 _('review revisions to merge (no merge is performed)'))
3988 ] + mergetoolopts,
3988 ] + mergetoolopts,
3989 _('[-P] [-f] [[-r] REV]'))
3989 _('[-P] [-f] [[-r] REV]'))
3990 def merge(ui, repo, node=None, **opts):
3990 def merge(ui, repo, node=None, **opts):
3991 """merge working directory with another revision
3991 """merge working directory with another revision
3992
3992
3993 The current working directory is updated with all changes made in
3993 The current working directory is updated with all changes made in
3994 the requested revision since the last common predecessor revision.
3994 the requested revision since the last common predecessor revision.
3995
3995
3996 Files that changed between either parent are marked as changed for
3996 Files that changed between either parent are marked as changed for
3997 the next commit and a commit must be performed before any further
3997 the next commit and a commit must be performed before any further
3998 updates to the repository are allowed. The next commit will have
3998 updates to the repository are allowed. The next commit will have
3999 two parents.
3999 two parents.
4000
4000
4001 ``--tool`` can be used to specify the merge tool used for file
4001 ``--tool`` can be used to specify the merge tool used for file
4002 merges. It overrides the HGMERGE environment variable and your
4002 merges. It overrides the HGMERGE environment variable and your
4003 configuration files. See :hg:`help merge-tools` for options.
4003 configuration files. See :hg:`help merge-tools` for options.
4004
4004
4005 If no revision is specified, the working directory's parent is a
4005 If no revision is specified, the working directory's parent is a
4006 head revision, and the current branch contains exactly one other
4006 head revision, and the current branch contains exactly one other
4007 head, the other head is merged with by default. Otherwise, an
4007 head, the other head is merged with by default. Otherwise, an
4008 explicit revision with which to merge with must be provided.
4008 explicit revision with which to merge with must be provided.
4009
4009
4010 :hg:`resolve` must be used to resolve unresolved files.
4010 :hg:`resolve` must be used to resolve unresolved files.
4011
4011
4012 To undo an uncommitted merge, use :hg:`update --clean .` which
4012 To undo an uncommitted merge, use :hg:`update --clean .` which
4013 will check out a clean copy of the original merge parent, losing
4013 will check out a clean copy of the original merge parent, losing
4014 all changes.
4014 all changes.
4015
4015
4016 Returns 0 on success, 1 if there are unresolved files.
4016 Returns 0 on success, 1 if there are unresolved files.
4017 """
4017 """
4018
4018
4019 if opts.get('rev') and node:
4019 if opts.get('rev') and node:
4020 raise util.Abort(_("please specify just one revision"))
4020 raise util.Abort(_("please specify just one revision"))
4021 if not node:
4021 if not node:
4022 node = opts.get('rev')
4022 node = opts.get('rev')
4023
4023
4024 if not node:
4024 if not node:
4025 branch = repo[None].branch()
4025 branch = repo[None].branch()
4026 bheads = repo.branchheads(branch)
4026 bheads = repo.branchheads(branch)
4027 if len(bheads) > 2:
4027 if len(bheads) > 2:
4028 raise util.Abort(_("branch '%s' has %d heads - "
4028 raise util.Abort(_("branch '%s' has %d heads - "
4029 "please merge with an explicit rev")
4029 "please merge with an explicit rev")
4030 % (branch, len(bheads)),
4030 % (branch, len(bheads)),
4031 hint=_("run 'hg heads .' to see heads"))
4031 hint=_("run 'hg heads .' to see heads"))
4032
4032
4033 parent = repo.dirstate.p1()
4033 parent = repo.dirstate.p1()
4034 if len(bheads) == 1:
4034 if len(bheads) == 1:
4035 if len(repo.heads()) > 1:
4035 if len(repo.heads()) > 1:
4036 raise util.Abort(_("branch '%s' has one head - "
4036 raise util.Abort(_("branch '%s' has one head - "
4037 "please merge with an explicit rev")
4037 "please merge with an explicit rev")
4038 % branch,
4038 % branch,
4039 hint=_("run 'hg heads' to see all heads"))
4039 hint=_("run 'hg heads' to see all heads"))
4040 msg, hint = _('nothing to merge'), None
4040 msg, hint = _('nothing to merge'), None
4041 if parent != repo.lookup(branch):
4041 if parent != repo.lookup(branch):
4042 hint = _("use 'hg update' instead")
4042 hint = _("use 'hg update' instead")
4043 raise util.Abort(msg, hint=hint)
4043 raise util.Abort(msg, hint=hint)
4044
4044
4045 if parent not in bheads:
4045 if parent not in bheads:
4046 raise util.Abort(_('working directory not at a head revision'),
4046 raise util.Abort(_('working directory not at a head revision'),
4047 hint=_("use 'hg update' or merge with an "
4047 hint=_("use 'hg update' or merge with an "
4048 "explicit revision"))
4048 "explicit revision"))
4049 node = parent == bheads[0] and bheads[-1] or bheads[0]
4049 node = parent == bheads[0] and bheads[-1] or bheads[0]
4050 else:
4050 else:
4051 node = scmutil.revsingle(repo, node).node()
4051 node = scmutil.revsingle(repo, node).node()
4052
4052
4053 if opts.get('preview'):
4053 if opts.get('preview'):
4054 # find nodes that are ancestors of p2 but not of p1
4054 # find nodes that are ancestors of p2 but not of p1
4055 p1 = repo.lookup('.')
4055 p1 = repo.lookup('.')
4056 p2 = repo.lookup(node)
4056 p2 = repo.lookup(node)
4057 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4057 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4058
4058
4059 displayer = cmdutil.show_changeset(ui, repo, opts)
4059 displayer = cmdutil.show_changeset(ui, repo, opts)
4060 for node in nodes:
4060 for node in nodes:
4061 displayer.show(repo[node])
4061 displayer.show(repo[node])
4062 displayer.close()
4062 displayer.close()
4063 return 0
4063 return 0
4064
4064
4065 try:
4065 try:
4066 # ui.forcemerge is an internal variable, do not document
4066 # ui.forcemerge is an internal variable, do not document
4067 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4067 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4068 return hg.merge(repo, node, force=opts.get('force'))
4068 return hg.merge(repo, node, force=opts.get('force'))
4069 finally:
4069 finally:
4070 ui.setconfig('ui', 'forcemerge', '')
4070 ui.setconfig('ui', 'forcemerge', '')
4071
4071
4072 @command('outgoing|out',
4072 @command('outgoing|out',
4073 [('f', 'force', None, _('run even when the destination is unrelated')),
4073 [('f', 'force', None, _('run even when the destination is unrelated')),
4074 ('r', 'rev', [],
4074 ('r', 'rev', [],
4075 _('a changeset intended to be included in the destination'), _('REV')),
4075 _('a changeset intended to be included in the destination'), _('REV')),
4076 ('n', 'newest-first', None, _('show newest record first')),
4076 ('n', 'newest-first', None, _('show newest record first')),
4077 ('B', 'bookmarks', False, _('compare bookmarks')),
4077 ('B', 'bookmarks', False, _('compare bookmarks')),
4078 ('b', 'branch', [], _('a specific branch you would like to push'),
4078 ('b', 'branch', [], _('a specific branch you would like to push'),
4079 _('BRANCH')),
4079 _('BRANCH')),
4080 ] + logopts + remoteopts + subrepoopts,
4080 ] + logopts + remoteopts + subrepoopts,
4081 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4081 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4082 def outgoing(ui, repo, dest=None, **opts):
4082 def outgoing(ui, repo, dest=None, **opts):
4083 """show changesets not found in the destination
4083 """show changesets not found in the destination
4084
4084
4085 Show changesets not found in the specified destination repository
4085 Show changesets not found in the specified destination repository
4086 or the default push location. These are the changesets that would
4086 or the default push location. These are the changesets that would
4087 be pushed if a push was requested.
4087 be pushed if a push was requested.
4088
4088
4089 See pull for details of valid destination formats.
4089 See pull for details of valid destination formats.
4090
4090
4091 Returns 0 if there are outgoing changes, 1 otherwise.
4091 Returns 0 if there are outgoing changes, 1 otherwise.
4092 """
4092 """
4093
4093
4094 if opts.get('bookmarks'):
4094 if opts.get('bookmarks'):
4095 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4095 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4096 dest, branches = hg.parseurl(dest, opts.get('branch'))
4096 dest, branches = hg.parseurl(dest, opts.get('branch'))
4097 other = hg.peer(repo, opts, dest)
4097 other = hg.peer(repo, opts, dest)
4098 if 'bookmarks' not in other.listkeys('namespaces'):
4098 if 'bookmarks' not in other.listkeys('namespaces'):
4099 ui.warn(_("remote doesn't support bookmarks\n"))
4099 ui.warn(_("remote doesn't support bookmarks\n"))
4100 return 0
4100 return 0
4101 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4101 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4102 return bookmarks.diff(ui, other, repo)
4102 return bookmarks.diff(ui, other, repo)
4103
4103
4104 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4104 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4105 try:
4105 try:
4106 return hg.outgoing(ui, repo, dest, opts)
4106 return hg.outgoing(ui, repo, dest, opts)
4107 finally:
4107 finally:
4108 del repo._subtoppath
4108 del repo._subtoppath
4109
4109
4110 @command('parents',
4110 @command('parents',
4111 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4111 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4112 ] + templateopts,
4112 ] + templateopts,
4113 _('[-r REV] [FILE]'))
4113 _('[-r REV] [FILE]'))
4114 def parents(ui, repo, file_=None, **opts):
4114 def parents(ui, repo, file_=None, **opts):
4115 """show the parents of the working directory or revision
4115 """show the parents of the working directory or revision
4116
4116
4117 Print the working directory's parent revisions. If a revision is
4117 Print the working directory's parent revisions. If a revision is
4118 given via -r/--rev, the parent of that revision will be printed.
4118 given via -r/--rev, the parent of that revision will be printed.
4119 If a file argument is given, the revision in which the file was
4119 If a file argument is given, the revision in which the file was
4120 last changed (before the working directory revision or the
4120 last changed (before the working directory revision or the
4121 argument to --rev if given) is printed.
4121 argument to --rev if given) is printed.
4122
4122
4123 Returns 0 on success.
4123 Returns 0 on success.
4124 """
4124 """
4125
4125
4126 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4126 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4127
4127
4128 if file_:
4128 if file_:
4129 m = scmutil.match(ctx, (file_,), opts)
4129 m = scmutil.match(ctx, (file_,), opts)
4130 if m.anypats() or len(m.files()) != 1:
4130 if m.anypats() or len(m.files()) != 1:
4131 raise util.Abort(_('can only specify an explicit filename'))
4131 raise util.Abort(_('can only specify an explicit filename'))
4132 file_ = m.files()[0]
4132 file_ = m.files()[0]
4133 filenodes = []
4133 filenodes = []
4134 for cp in ctx.parents():
4134 for cp in ctx.parents():
4135 if not cp:
4135 if not cp:
4136 continue
4136 continue
4137 try:
4137 try:
4138 filenodes.append(cp.filenode(file_))
4138 filenodes.append(cp.filenode(file_))
4139 except error.LookupError:
4139 except error.LookupError:
4140 pass
4140 pass
4141 if not filenodes:
4141 if not filenodes:
4142 raise util.Abort(_("'%s' not found in manifest!") % file_)
4142 raise util.Abort(_("'%s' not found in manifest!") % file_)
4143 fl = repo.file(file_)
4143 fl = repo.file(file_)
4144 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4144 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4145 else:
4145 else:
4146 p = [cp.node() for cp in ctx.parents()]
4146 p = [cp.node() for cp in ctx.parents()]
4147
4147
4148 displayer = cmdutil.show_changeset(ui, repo, opts)
4148 displayer = cmdutil.show_changeset(ui, repo, opts)
4149 for n in p:
4149 for n in p:
4150 if n != nullid:
4150 if n != nullid:
4151 displayer.show(repo[n])
4151 displayer.show(repo[n])
4152 displayer.close()
4152 displayer.close()
4153
4153
4154 @command('paths', [], _('[NAME]'))
4154 @command('paths', [], _('[NAME]'))
4155 def paths(ui, repo, search=None):
4155 def paths(ui, repo, search=None):
4156 """show aliases for remote repositories
4156 """show aliases for remote repositories
4157
4157
4158 Show definition of symbolic path name NAME. If no name is given,
4158 Show definition of symbolic path name NAME. If no name is given,
4159 show definition of all available names.
4159 show definition of all available names.
4160
4160
4161 Option -q/--quiet suppresses all output when searching for NAME
4161 Option -q/--quiet suppresses all output when searching for NAME
4162 and shows only the path names when listing all definitions.
4162 and shows only the path names when listing all definitions.
4163
4163
4164 Path names are defined in the [paths] section of your
4164 Path names are defined in the [paths] section of your
4165 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4165 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4166 repository, ``.hg/hgrc`` is used, too.
4166 repository, ``.hg/hgrc`` is used, too.
4167
4167
4168 The path names ``default`` and ``default-push`` have a special
4168 The path names ``default`` and ``default-push`` have a special
4169 meaning. When performing a push or pull operation, they are used
4169 meaning. When performing a push or pull operation, they are used
4170 as fallbacks if no location is specified on the command-line.
4170 as fallbacks if no location is specified on the command-line.
4171 When ``default-push`` is set, it will be used for push and
4171 When ``default-push`` is set, it will be used for push and
4172 ``default`` will be used for pull; otherwise ``default`` is used
4172 ``default`` will be used for pull; otherwise ``default`` is used
4173 as the fallback for both. When cloning a repository, the clone
4173 as the fallback for both. When cloning a repository, the clone
4174 source is written as ``default`` in ``.hg/hgrc``. Note that
4174 source is written as ``default`` in ``.hg/hgrc``. Note that
4175 ``default`` and ``default-push`` apply to all inbound (e.g.
4175 ``default`` and ``default-push`` apply to all inbound (e.g.
4176 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4176 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4177 :hg:`bundle`) operations.
4177 :hg:`bundle`) operations.
4178
4178
4179 See :hg:`help urls` for more information.
4179 See :hg:`help urls` for more information.
4180
4180
4181 Returns 0 on success.
4181 Returns 0 on success.
4182 """
4182 """
4183 if search:
4183 if search:
4184 for name, path in ui.configitems("paths"):
4184 for name, path in ui.configitems("paths"):
4185 if name == search:
4185 if name == search:
4186 ui.status("%s\n" % util.hidepassword(path))
4186 ui.status("%s\n" % util.hidepassword(path))
4187 return
4187 return
4188 if not ui.quiet:
4188 if not ui.quiet:
4189 ui.warn(_("not found!\n"))
4189 ui.warn(_("not found!\n"))
4190 return 1
4190 return 1
4191 else:
4191 else:
4192 for name, path in ui.configitems("paths"):
4192 for name, path in ui.configitems("paths"):
4193 if ui.quiet:
4193 if ui.quiet:
4194 ui.write("%s\n" % name)
4194 ui.write("%s\n" % name)
4195 else:
4195 else:
4196 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4196 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4197
4197
4198 @command('^phase',
4198 @command('^phase',
4199 [('p', 'public', False, _('set changeset phase to public')),
4199 [('p', 'public', False, _('set changeset phase to public')),
4200 ('d', 'draft', False, _('set changeset phase to draft')),
4200 ('d', 'draft', False, _('set changeset phase to draft')),
4201 ('s', 'secret', False, _('set changeset phase to secret')),
4201 ('s', 'secret', False, _('set changeset phase to secret')),
4202 ('f', 'force', False, _('allow to move boundary backward')),
4202 ('f', 'force', False, _('allow to move boundary backward')),
4203 ('r', 'rev', [], _('target revision'), _('REV')),
4203 ('r', 'rev', [], _('target revision'), _('REV')),
4204 ],
4204 ],
4205 _('[-p|-d|-s] [-f] [-r] REV...'))
4205 _('[-p|-d|-s] [-f] [-r] REV...'))
4206 def phase(ui, repo, *revs, **opts):
4206 def phase(ui, repo, *revs, **opts):
4207 """set or show the current phase name
4207 """set or show the current phase name
4208
4208
4209 With no argument, show the phase name of specified revisions.
4209 With no argument, show the phase name of specified revisions.
4210
4210
4211 With one of -p/--public, -d/--draft or -s/--secret, change the
4211 With one of -p/--public, -d/--draft or -s/--secret, change the
4212 phase value of the specified revisions.
4212 phase value of the specified revisions.
4213
4213
4214 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4214 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4215 lower phase to an higher phase. Phases are ordered as follows::
4215 lower phase to an higher phase. Phases are ordered as follows::
4216
4216
4217 public < draft < secret
4217 public < draft < secret
4218
4218
4219 Return 0 on success, 1 if no phases were changed or some could not
4219 Return 0 on success, 1 if no phases were changed or some could not
4220 be changed.
4220 be changed.
4221 """
4221 """
4222 # search for a unique phase argument
4222 # search for a unique phase argument
4223 targetphase = None
4223 targetphase = None
4224 for idx, name in enumerate(phases.phasenames):
4224 for idx, name in enumerate(phases.phasenames):
4225 if opts[name]:
4225 if opts[name]:
4226 if targetphase is not None:
4226 if targetphase is not None:
4227 raise util.Abort(_('only one phase can be specified'))
4227 raise util.Abort(_('only one phase can be specified'))
4228 targetphase = idx
4228 targetphase = idx
4229
4229
4230 # look for specified revision
4230 # look for specified revision
4231 revs = list(revs)
4231 revs = list(revs)
4232 revs.extend(opts['rev'])
4232 revs.extend(opts['rev'])
4233 if not revs:
4233 if not revs:
4234 raise util.Abort(_('no revisions specified'))
4234 raise util.Abort(_('no revisions specified'))
4235
4235
4236 revs = scmutil.revrange(repo, revs)
4236 revs = scmutil.revrange(repo, revs)
4237
4237
4238 lock = None
4238 lock = None
4239 ret = 0
4239 ret = 0
4240 if targetphase is None:
4240 if targetphase is None:
4241 # display
4241 # display
4242 for r in revs:
4242 for r in revs:
4243 ctx = repo[r]
4243 ctx = repo[r]
4244 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4244 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4245 else:
4245 else:
4246 lock = repo.lock()
4246 lock = repo.lock()
4247 try:
4247 try:
4248 # set phase
4248 # set phase
4249 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4249 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4250 if not nodes:
4250 if not nodes:
4251 raise util.Abort(_('empty revision set'))
4251 raise util.Abort(_('empty revision set'))
4252 olddata = repo._phaserev[:]
4252 olddata = repo._phaserev[:]
4253 phases.advanceboundary(repo, targetphase, nodes)
4253 phases.advanceboundary(repo, targetphase, nodes)
4254 if opts['force']:
4254 if opts['force']:
4255 phases.retractboundary(repo, targetphase, nodes)
4255 phases.retractboundary(repo, targetphase, nodes)
4256 finally:
4256 finally:
4257 lock.release()
4257 lock.release()
4258 if olddata is not None:
4258 if olddata is not None:
4259 changes = 0
4259 changes = 0
4260 newdata = repo._phaserev
4260 newdata = repo._phaserev
4261 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4261 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4262 rejected = [n for n in nodes
4262 rejected = [n for n in nodes
4263 if newdata[repo[n].rev()] < targetphase]
4263 if newdata[repo[n].rev()] < targetphase]
4264 if rejected:
4264 if rejected:
4265 ui.warn(_('cannot move %i changesets to a more permissive '
4265 ui.warn(_('cannot move %i changesets to a more permissive '
4266 'phase, use --force\n') % len(rejected))
4266 'phase, use --force\n') % len(rejected))
4267 ret = 1
4267 ret = 1
4268 if changes:
4268 if changes:
4269 msg = _('phase changed for %i changesets\n') % changes
4269 msg = _('phase changed for %i changesets\n') % changes
4270 if ret:
4270 if ret:
4271 ui.status(msg)
4271 ui.status(msg)
4272 else:
4272 else:
4273 ui.note(msg)
4273 ui.note(msg)
4274 else:
4274 else:
4275 ui.warn(_('no phases changed\n'))
4275 ui.warn(_('no phases changed\n'))
4276 ret = 1
4276 ret = 1
4277 return ret
4277 return ret
4278
4278
4279 def postincoming(ui, repo, modheads, optupdate, checkout):
4279 def postincoming(ui, repo, modheads, optupdate, checkout):
4280 if modheads == 0:
4280 if modheads == 0:
4281 return
4281 return
4282 if optupdate:
4282 if optupdate:
4283 movemarkfrom = repo['.'].node()
4283 movemarkfrom = repo['.'].node()
4284 try:
4284 try:
4285 ret = hg.update(repo, checkout)
4285 ret = hg.update(repo, checkout)
4286 except util.Abort, inst:
4286 except util.Abort, inst:
4287 ui.warn(_("not updating: %s\n" % str(inst)))
4287 ui.warn(_("not updating: %s\n") % str(inst))
4288 return 0
4288 return 0
4289 if not ret and not checkout:
4289 if not ret and not checkout:
4290 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4290 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4291 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4291 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4292 return ret
4292 return ret
4293 if modheads > 1:
4293 if modheads > 1:
4294 currentbranchheads = len(repo.branchheads())
4294 currentbranchheads = len(repo.branchheads())
4295 if currentbranchheads == modheads:
4295 if currentbranchheads == modheads:
4296 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4296 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4297 elif currentbranchheads > 1:
4297 elif currentbranchheads > 1:
4298 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4298 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4299 else:
4299 else:
4300 ui.status(_("(run 'hg heads' to see heads)\n"))
4300 ui.status(_("(run 'hg heads' to see heads)\n"))
4301 else:
4301 else:
4302 ui.status(_("(run 'hg update' to get a working copy)\n"))
4302 ui.status(_("(run 'hg update' to get a working copy)\n"))
4303
4303
4304 @command('^pull',
4304 @command('^pull',
4305 [('u', 'update', None,
4305 [('u', 'update', None,
4306 _('update to new branch head if changesets were pulled')),
4306 _('update to new branch head if changesets were pulled')),
4307 ('f', 'force', None, _('run even when remote repository is unrelated')),
4307 ('f', 'force', None, _('run even when remote repository is unrelated')),
4308 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4308 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4309 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4309 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4310 ('b', 'branch', [], _('a specific branch you would like to pull'),
4310 ('b', 'branch', [], _('a specific branch you would like to pull'),
4311 _('BRANCH')),
4311 _('BRANCH')),
4312 ] + remoteopts,
4312 ] + remoteopts,
4313 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4313 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4314 def pull(ui, repo, source="default", **opts):
4314 def pull(ui, repo, source="default", **opts):
4315 """pull changes from the specified source
4315 """pull changes from the specified source
4316
4316
4317 Pull changes from a remote repository to a local one.
4317 Pull changes from a remote repository to a local one.
4318
4318
4319 This finds all changes from the repository at the specified path
4319 This finds all changes from the repository at the specified path
4320 or URL and adds them to a local repository (the current one unless
4320 or URL and adds them to a local repository (the current one unless
4321 -R is specified). By default, this does not update the copy of the
4321 -R is specified). By default, this does not update the copy of the
4322 project in the working directory.
4322 project in the working directory.
4323
4323
4324 Use :hg:`incoming` if you want to see what would have been added
4324 Use :hg:`incoming` if you want to see what would have been added
4325 by a pull at the time you issued this command. If you then decide
4325 by a pull at the time you issued this command. If you then decide
4326 to add those changes to the repository, you should use :hg:`pull
4326 to add those changes to the repository, you should use :hg:`pull
4327 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4327 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4328
4328
4329 If SOURCE is omitted, the 'default' path will be used.
4329 If SOURCE is omitted, the 'default' path will be used.
4330 See :hg:`help urls` for more information.
4330 See :hg:`help urls` for more information.
4331
4331
4332 Returns 0 on success, 1 if an update had unresolved files.
4332 Returns 0 on success, 1 if an update had unresolved files.
4333 """
4333 """
4334 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4334 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4335 other = hg.peer(repo, opts, source)
4335 other = hg.peer(repo, opts, source)
4336 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4336 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4337 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4337 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4338
4338
4339 if opts.get('bookmark'):
4339 if opts.get('bookmark'):
4340 if not revs:
4340 if not revs:
4341 revs = []
4341 revs = []
4342 rb = other.listkeys('bookmarks')
4342 rb = other.listkeys('bookmarks')
4343 for b in opts['bookmark']:
4343 for b in opts['bookmark']:
4344 if b not in rb:
4344 if b not in rb:
4345 raise util.Abort(_('remote bookmark %s not found!') % b)
4345 raise util.Abort(_('remote bookmark %s not found!') % b)
4346 revs.append(rb[b])
4346 revs.append(rb[b])
4347
4347
4348 if revs:
4348 if revs:
4349 try:
4349 try:
4350 revs = [other.lookup(rev) for rev in revs]
4350 revs = [other.lookup(rev) for rev in revs]
4351 except error.CapabilityError:
4351 except error.CapabilityError:
4352 err = _("other repository doesn't support revision lookup, "
4352 err = _("other repository doesn't support revision lookup, "
4353 "so a rev cannot be specified.")
4353 "so a rev cannot be specified.")
4354 raise util.Abort(err)
4354 raise util.Abort(err)
4355
4355
4356 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4356 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4357 bookmarks.updatefromremote(ui, repo, other, source)
4357 bookmarks.updatefromremote(ui, repo, other, source)
4358 if checkout:
4358 if checkout:
4359 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4359 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4360 repo._subtoppath = source
4360 repo._subtoppath = source
4361 try:
4361 try:
4362 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4362 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4363
4363
4364 finally:
4364 finally:
4365 del repo._subtoppath
4365 del repo._subtoppath
4366
4366
4367 # update specified bookmarks
4367 # update specified bookmarks
4368 if opts.get('bookmark'):
4368 if opts.get('bookmark'):
4369 for b in opts['bookmark']:
4369 for b in opts['bookmark']:
4370 # explicit pull overrides local bookmark if any
4370 # explicit pull overrides local bookmark if any
4371 ui.status(_("importing bookmark %s\n") % b)
4371 ui.status(_("importing bookmark %s\n") % b)
4372 repo._bookmarks[b] = repo[rb[b]].node()
4372 repo._bookmarks[b] = repo[rb[b]].node()
4373 bookmarks.write(repo)
4373 bookmarks.write(repo)
4374
4374
4375 return ret
4375 return ret
4376
4376
4377 @command('^push',
4377 @command('^push',
4378 [('f', 'force', None, _('force push')),
4378 [('f', 'force', None, _('force push')),
4379 ('r', 'rev', [],
4379 ('r', 'rev', [],
4380 _('a changeset intended to be included in the destination'),
4380 _('a changeset intended to be included in the destination'),
4381 _('REV')),
4381 _('REV')),
4382 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4382 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4383 ('b', 'branch', [],
4383 ('b', 'branch', [],
4384 _('a specific branch you would like to push'), _('BRANCH')),
4384 _('a specific branch you would like to push'), _('BRANCH')),
4385 ('', 'new-branch', False, _('allow pushing a new branch')),
4385 ('', 'new-branch', False, _('allow pushing a new branch')),
4386 ] + remoteopts,
4386 ] + remoteopts,
4387 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4387 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4388 def push(ui, repo, dest=None, **opts):
4388 def push(ui, repo, dest=None, **opts):
4389 """push changes to the specified destination
4389 """push changes to the specified destination
4390
4390
4391 Push changesets from the local repository to the specified
4391 Push changesets from the local repository to the specified
4392 destination.
4392 destination.
4393
4393
4394 This operation is symmetrical to pull: it is identical to a pull
4394 This operation is symmetrical to pull: it is identical to a pull
4395 in the destination repository from the current one.
4395 in the destination repository from the current one.
4396
4396
4397 By default, push will not allow creation of new heads at the
4397 By default, push will not allow creation of new heads at the
4398 destination, since multiple heads would make it unclear which head
4398 destination, since multiple heads would make it unclear which head
4399 to use. In this situation, it is recommended to pull and merge
4399 to use. In this situation, it is recommended to pull and merge
4400 before pushing.
4400 before pushing.
4401
4401
4402 Use --new-branch if you want to allow push to create a new named
4402 Use --new-branch if you want to allow push to create a new named
4403 branch that is not present at the destination. This allows you to
4403 branch that is not present at the destination. This allows you to
4404 only create a new branch without forcing other changes.
4404 only create a new branch without forcing other changes.
4405
4405
4406 Use -f/--force to override the default behavior and push all
4406 Use -f/--force to override the default behavior and push all
4407 changesets on all branches.
4407 changesets on all branches.
4408
4408
4409 If -r/--rev is used, the specified revision and all its ancestors
4409 If -r/--rev is used, the specified revision and all its ancestors
4410 will be pushed to the remote repository.
4410 will be pushed to the remote repository.
4411
4411
4412 Please see :hg:`help urls` for important details about ``ssh://``
4412 Please see :hg:`help urls` for important details about ``ssh://``
4413 URLs. If DESTINATION is omitted, a default path will be used.
4413 URLs. If DESTINATION is omitted, a default path will be used.
4414
4414
4415 Returns 0 if push was successful, 1 if nothing to push.
4415 Returns 0 if push was successful, 1 if nothing to push.
4416 """
4416 """
4417
4417
4418 if opts.get('bookmark'):
4418 if opts.get('bookmark'):
4419 for b in opts['bookmark']:
4419 for b in opts['bookmark']:
4420 # translate -B options to -r so changesets get pushed
4420 # translate -B options to -r so changesets get pushed
4421 if b in repo._bookmarks:
4421 if b in repo._bookmarks:
4422 opts.setdefault('rev', []).append(b)
4422 opts.setdefault('rev', []).append(b)
4423 else:
4423 else:
4424 # if we try to push a deleted bookmark, translate it to null
4424 # if we try to push a deleted bookmark, translate it to null
4425 # this lets simultaneous -r, -b options continue working
4425 # this lets simultaneous -r, -b options continue working
4426 opts.setdefault('rev', []).append("null")
4426 opts.setdefault('rev', []).append("null")
4427
4427
4428 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4428 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4429 dest, branches = hg.parseurl(dest, opts.get('branch'))
4429 dest, branches = hg.parseurl(dest, opts.get('branch'))
4430 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4430 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4431 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4431 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4432 other = hg.peer(repo, opts, dest)
4432 other = hg.peer(repo, opts, dest)
4433 if revs:
4433 if revs:
4434 revs = [repo.lookup(rev) for rev in revs]
4434 revs = [repo.lookup(rev) for rev in revs]
4435
4435
4436 repo._subtoppath = dest
4436 repo._subtoppath = dest
4437 try:
4437 try:
4438 # push subrepos depth-first for coherent ordering
4438 # push subrepos depth-first for coherent ordering
4439 c = repo['']
4439 c = repo['']
4440 subs = c.substate # only repos that are committed
4440 subs = c.substate # only repos that are committed
4441 for s in sorted(subs):
4441 for s in sorted(subs):
4442 if c.sub(s).push(opts) == 0:
4442 if c.sub(s).push(opts) == 0:
4443 return False
4443 return False
4444 finally:
4444 finally:
4445 del repo._subtoppath
4445 del repo._subtoppath
4446 result = repo.push(other, opts.get('force'), revs=revs,
4446 result = repo.push(other, opts.get('force'), revs=revs,
4447 newbranch=opts.get('new_branch'))
4447 newbranch=opts.get('new_branch'))
4448
4448
4449 result = not result
4449 result = not result
4450
4450
4451 if opts.get('bookmark'):
4451 if opts.get('bookmark'):
4452 rb = other.listkeys('bookmarks')
4452 rb = other.listkeys('bookmarks')
4453 for b in opts['bookmark']:
4453 for b in opts['bookmark']:
4454 # explicit push overrides remote bookmark if any
4454 # explicit push overrides remote bookmark if any
4455 if b in repo._bookmarks:
4455 if b in repo._bookmarks:
4456 ui.status(_("exporting bookmark %s\n") % b)
4456 ui.status(_("exporting bookmark %s\n") % b)
4457 new = repo[b].hex()
4457 new = repo[b].hex()
4458 elif b in rb:
4458 elif b in rb:
4459 ui.status(_("deleting remote bookmark %s\n") % b)
4459 ui.status(_("deleting remote bookmark %s\n") % b)
4460 new = '' # delete
4460 new = '' # delete
4461 else:
4461 else:
4462 ui.warn(_('bookmark %s does not exist on the local '
4462 ui.warn(_('bookmark %s does not exist on the local '
4463 'or remote repository!\n') % b)
4463 'or remote repository!\n') % b)
4464 return 2
4464 return 2
4465 old = rb.get(b, '')
4465 old = rb.get(b, '')
4466 r = other.pushkey('bookmarks', b, old, new)
4466 r = other.pushkey('bookmarks', b, old, new)
4467 if not r:
4467 if not r:
4468 ui.warn(_('updating bookmark %s failed!\n') % b)
4468 ui.warn(_('updating bookmark %s failed!\n') % b)
4469 if not result:
4469 if not result:
4470 result = 2
4470 result = 2
4471
4471
4472 return result
4472 return result
4473
4473
4474 @command('recover', [])
4474 @command('recover', [])
4475 def recover(ui, repo):
4475 def recover(ui, repo):
4476 """roll back an interrupted transaction
4476 """roll back an interrupted transaction
4477
4477
4478 Recover from an interrupted commit or pull.
4478 Recover from an interrupted commit or pull.
4479
4479
4480 This command tries to fix the repository status after an
4480 This command tries to fix the repository status after an
4481 interrupted operation. It should only be necessary when Mercurial
4481 interrupted operation. It should only be necessary when Mercurial
4482 suggests it.
4482 suggests it.
4483
4483
4484 Returns 0 if successful, 1 if nothing to recover or verify fails.
4484 Returns 0 if successful, 1 if nothing to recover or verify fails.
4485 """
4485 """
4486 if repo.recover():
4486 if repo.recover():
4487 return hg.verify(repo)
4487 return hg.verify(repo)
4488 return 1
4488 return 1
4489
4489
4490 @command('^remove|rm',
4490 @command('^remove|rm',
4491 [('A', 'after', None, _('record delete for missing files')),
4491 [('A', 'after', None, _('record delete for missing files')),
4492 ('f', 'force', None,
4492 ('f', 'force', None,
4493 _('remove (and delete) file even if added or modified')),
4493 _('remove (and delete) file even if added or modified')),
4494 ] + walkopts,
4494 ] + walkopts,
4495 _('[OPTION]... FILE...'))
4495 _('[OPTION]... FILE...'))
4496 def remove(ui, repo, *pats, **opts):
4496 def remove(ui, repo, *pats, **opts):
4497 """remove the specified files on the next commit
4497 """remove the specified files on the next commit
4498
4498
4499 Schedule the indicated files for removal from the current branch.
4499 Schedule the indicated files for removal from the current branch.
4500
4500
4501 This command schedules the files to be removed at the next commit.
4501 This command schedules the files to be removed at the next commit.
4502 To undo a remove before that, see :hg:`revert`. To undo added
4502 To undo a remove before that, see :hg:`revert`. To undo added
4503 files, see :hg:`forget`.
4503 files, see :hg:`forget`.
4504
4504
4505 .. container:: verbose
4505 .. container:: verbose
4506
4506
4507 -A/--after can be used to remove only files that have already
4507 -A/--after can be used to remove only files that have already
4508 been deleted, -f/--force can be used to force deletion, and -Af
4508 been deleted, -f/--force can be used to force deletion, and -Af
4509 can be used to remove files from the next revision without
4509 can be used to remove files from the next revision without
4510 deleting them from the working directory.
4510 deleting them from the working directory.
4511
4511
4512 The following table details the behavior of remove for different
4512 The following table details the behavior of remove for different
4513 file states (columns) and option combinations (rows). The file
4513 file states (columns) and option combinations (rows). The file
4514 states are Added [A], Clean [C], Modified [M] and Missing [!]
4514 states are Added [A], Clean [C], Modified [M] and Missing [!]
4515 (as reported by :hg:`status`). The actions are Warn, Remove
4515 (as reported by :hg:`status`). The actions are Warn, Remove
4516 (from branch) and Delete (from disk):
4516 (from branch) and Delete (from disk):
4517
4517
4518 ======= == == == ==
4518 ======= == == == ==
4519 A C M !
4519 A C M !
4520 ======= == == == ==
4520 ======= == == == ==
4521 none W RD W R
4521 none W RD W R
4522 -f R RD RD R
4522 -f R RD RD R
4523 -A W W W R
4523 -A W W W R
4524 -Af R R R R
4524 -Af R R R R
4525 ======= == == == ==
4525 ======= == == == ==
4526
4526
4527 Note that remove never deletes files in Added [A] state from the
4527 Note that remove never deletes files in Added [A] state from the
4528 working directory, not even if option --force is specified.
4528 working directory, not even if option --force is specified.
4529
4529
4530 Returns 0 on success, 1 if any warnings encountered.
4530 Returns 0 on success, 1 if any warnings encountered.
4531 """
4531 """
4532
4532
4533 ret = 0
4533 ret = 0
4534 after, force = opts.get('after'), opts.get('force')
4534 after, force = opts.get('after'), opts.get('force')
4535 if not pats and not after:
4535 if not pats and not after:
4536 raise util.Abort(_('no files specified'))
4536 raise util.Abort(_('no files specified'))
4537
4537
4538 m = scmutil.match(repo[None], pats, opts)
4538 m = scmutil.match(repo[None], pats, opts)
4539 s = repo.status(match=m, clean=True)
4539 s = repo.status(match=m, clean=True)
4540 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4540 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4541
4541
4542 for f in m.files():
4542 for f in m.files():
4543 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4543 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4544 if os.path.exists(m.rel(f)):
4544 if os.path.exists(m.rel(f)):
4545 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4545 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4546 ret = 1
4546 ret = 1
4547
4547
4548 if force:
4548 if force:
4549 list = modified + deleted + clean + added
4549 list = modified + deleted + clean + added
4550 elif after:
4550 elif after:
4551 list = deleted
4551 list = deleted
4552 for f in modified + added + clean:
4552 for f in modified + added + clean:
4553 ui.warn(_('not removing %s: file still exists (use -f'
4553 ui.warn(_('not removing %s: file still exists (use -f'
4554 ' to force removal)\n') % m.rel(f))
4554 ' to force removal)\n') % m.rel(f))
4555 ret = 1
4555 ret = 1
4556 else:
4556 else:
4557 list = deleted + clean
4557 list = deleted + clean
4558 for f in modified:
4558 for f in modified:
4559 ui.warn(_('not removing %s: file is modified (use -f'
4559 ui.warn(_('not removing %s: file is modified (use -f'
4560 ' to force removal)\n') % m.rel(f))
4560 ' to force removal)\n') % m.rel(f))
4561 ret = 1
4561 ret = 1
4562 for f in added:
4562 for f in added:
4563 ui.warn(_('not removing %s: file has been marked for add'
4563 ui.warn(_('not removing %s: file has been marked for add'
4564 ' (use forget to undo)\n') % m.rel(f))
4564 ' (use forget to undo)\n') % m.rel(f))
4565 ret = 1
4565 ret = 1
4566
4566
4567 for f in sorted(list):
4567 for f in sorted(list):
4568 if ui.verbose or not m.exact(f):
4568 if ui.verbose or not m.exact(f):
4569 ui.status(_('removing %s\n') % m.rel(f))
4569 ui.status(_('removing %s\n') % m.rel(f))
4570
4570
4571 wlock = repo.wlock()
4571 wlock = repo.wlock()
4572 try:
4572 try:
4573 if not after:
4573 if not after:
4574 for f in list:
4574 for f in list:
4575 if f in added:
4575 if f in added:
4576 continue # we never unlink added files on remove
4576 continue # we never unlink added files on remove
4577 try:
4577 try:
4578 util.unlinkpath(repo.wjoin(f))
4578 util.unlinkpath(repo.wjoin(f))
4579 except OSError, inst:
4579 except OSError, inst:
4580 if inst.errno != errno.ENOENT:
4580 if inst.errno != errno.ENOENT:
4581 raise
4581 raise
4582 repo[None].forget(list)
4582 repo[None].forget(list)
4583 finally:
4583 finally:
4584 wlock.release()
4584 wlock.release()
4585
4585
4586 return ret
4586 return ret
4587
4587
4588 @command('rename|move|mv',
4588 @command('rename|move|mv',
4589 [('A', 'after', None, _('record a rename that has already occurred')),
4589 [('A', 'after', None, _('record a rename that has already occurred')),
4590 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4590 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4591 ] + walkopts + dryrunopts,
4591 ] + walkopts + dryrunopts,
4592 _('[OPTION]... SOURCE... DEST'))
4592 _('[OPTION]... SOURCE... DEST'))
4593 def rename(ui, repo, *pats, **opts):
4593 def rename(ui, repo, *pats, **opts):
4594 """rename files; equivalent of copy + remove
4594 """rename files; equivalent of copy + remove
4595
4595
4596 Mark dest as copies of sources; mark sources for deletion. If dest
4596 Mark dest as copies of sources; mark sources for deletion. If dest
4597 is a directory, copies are put in that directory. If dest is a
4597 is a directory, copies are put in that directory. If dest is a
4598 file, there can only be one source.
4598 file, there can only be one source.
4599
4599
4600 By default, this command copies the contents of files as they
4600 By default, this command copies the contents of files as they
4601 exist in the working directory. If invoked with -A/--after, the
4601 exist in the working directory. If invoked with -A/--after, the
4602 operation is recorded, but no copying is performed.
4602 operation is recorded, but no copying is performed.
4603
4603
4604 This command takes effect at the next commit. To undo a rename
4604 This command takes effect at the next commit. To undo a rename
4605 before that, see :hg:`revert`.
4605 before that, see :hg:`revert`.
4606
4606
4607 Returns 0 on success, 1 if errors are encountered.
4607 Returns 0 on success, 1 if errors are encountered.
4608 """
4608 """
4609 wlock = repo.wlock(False)
4609 wlock = repo.wlock(False)
4610 try:
4610 try:
4611 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4611 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4612 finally:
4612 finally:
4613 wlock.release()
4613 wlock.release()
4614
4614
4615 @command('resolve',
4615 @command('resolve',
4616 [('a', 'all', None, _('select all unresolved files')),
4616 [('a', 'all', None, _('select all unresolved files')),
4617 ('l', 'list', None, _('list state of files needing merge')),
4617 ('l', 'list', None, _('list state of files needing merge')),
4618 ('m', 'mark', None, _('mark files as resolved')),
4618 ('m', 'mark', None, _('mark files as resolved')),
4619 ('u', 'unmark', None, _('mark files as unresolved')),
4619 ('u', 'unmark', None, _('mark files as unresolved')),
4620 ('n', 'no-status', None, _('hide status prefix'))]
4620 ('n', 'no-status', None, _('hide status prefix'))]
4621 + mergetoolopts + walkopts,
4621 + mergetoolopts + walkopts,
4622 _('[OPTION]... [FILE]...'))
4622 _('[OPTION]... [FILE]...'))
4623 def resolve(ui, repo, *pats, **opts):
4623 def resolve(ui, repo, *pats, **opts):
4624 """redo merges or set/view the merge status of files
4624 """redo merges or set/view the merge status of files
4625
4625
4626 Merges with unresolved conflicts are often the result of
4626 Merges with unresolved conflicts are often the result of
4627 non-interactive merging using the ``internal:merge`` configuration
4627 non-interactive merging using the ``internal:merge`` configuration
4628 setting, or a command-line merge tool like ``diff3``. The resolve
4628 setting, or a command-line merge tool like ``diff3``. The resolve
4629 command is used to manage the files involved in a merge, after
4629 command is used to manage the files involved in a merge, after
4630 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4630 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4631 working directory must have two parents). See :hg:`help
4631 working directory must have two parents). See :hg:`help
4632 merge-tools` for information on configuring merge tools.
4632 merge-tools` for information on configuring merge tools.
4633
4633
4634 The resolve command can be used in the following ways:
4634 The resolve command can be used in the following ways:
4635
4635
4636 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4636 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4637 files, discarding any previous merge attempts. Re-merging is not
4637 files, discarding any previous merge attempts. Re-merging is not
4638 performed for files already marked as resolved. Use ``--all/-a``
4638 performed for files already marked as resolved. Use ``--all/-a``
4639 to select all unresolved files. ``--tool`` can be used to specify
4639 to select all unresolved files. ``--tool`` can be used to specify
4640 the merge tool used for the given files. It overrides the HGMERGE
4640 the merge tool used for the given files. It overrides the HGMERGE
4641 environment variable and your configuration files. Previous file
4641 environment variable and your configuration files. Previous file
4642 contents are saved with a ``.orig`` suffix.
4642 contents are saved with a ``.orig`` suffix.
4643
4643
4644 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4644 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4645 (e.g. after having manually fixed-up the files). The default is
4645 (e.g. after having manually fixed-up the files). The default is
4646 to mark all unresolved files.
4646 to mark all unresolved files.
4647
4647
4648 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4648 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4649 default is to mark all resolved files.
4649 default is to mark all resolved files.
4650
4650
4651 - :hg:`resolve -l`: list files which had or still have conflicts.
4651 - :hg:`resolve -l`: list files which had or still have conflicts.
4652 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4652 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4653
4653
4654 Note that Mercurial will not let you commit files with unresolved
4654 Note that Mercurial will not let you commit files with unresolved
4655 merge conflicts. You must use :hg:`resolve -m ...` before you can
4655 merge conflicts. You must use :hg:`resolve -m ...` before you can
4656 commit after a conflicting merge.
4656 commit after a conflicting merge.
4657
4657
4658 Returns 0 on success, 1 if any files fail a resolve attempt.
4658 Returns 0 on success, 1 if any files fail a resolve attempt.
4659 """
4659 """
4660
4660
4661 all, mark, unmark, show, nostatus = \
4661 all, mark, unmark, show, nostatus = \
4662 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4662 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4663
4663
4664 if (show and (mark or unmark)) or (mark and unmark):
4664 if (show and (mark or unmark)) or (mark and unmark):
4665 raise util.Abort(_("too many options specified"))
4665 raise util.Abort(_("too many options specified"))
4666 if pats and all:
4666 if pats and all:
4667 raise util.Abort(_("can't specify --all and patterns"))
4667 raise util.Abort(_("can't specify --all and patterns"))
4668 if not (all or pats or show or mark or unmark):
4668 if not (all or pats or show or mark or unmark):
4669 raise util.Abort(_('no files or directories specified; '
4669 raise util.Abort(_('no files or directories specified; '
4670 'use --all to remerge all files'))
4670 'use --all to remerge all files'))
4671
4671
4672 ms = mergemod.mergestate(repo)
4672 ms = mergemod.mergestate(repo)
4673 m = scmutil.match(repo[None], pats, opts)
4673 m = scmutil.match(repo[None], pats, opts)
4674 ret = 0
4674 ret = 0
4675
4675
4676 for f in ms:
4676 for f in ms:
4677 if m(f):
4677 if m(f):
4678 if show:
4678 if show:
4679 if nostatus:
4679 if nostatus:
4680 ui.write("%s\n" % f)
4680 ui.write("%s\n" % f)
4681 else:
4681 else:
4682 ui.write("%s %s\n" % (ms[f].upper(), f),
4682 ui.write("%s %s\n" % (ms[f].upper(), f),
4683 label='resolve.' +
4683 label='resolve.' +
4684 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4684 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4685 elif mark:
4685 elif mark:
4686 ms.mark(f, "r")
4686 ms.mark(f, "r")
4687 elif unmark:
4687 elif unmark:
4688 ms.mark(f, "u")
4688 ms.mark(f, "u")
4689 else:
4689 else:
4690 wctx = repo[None]
4690 wctx = repo[None]
4691 mctx = wctx.parents()[-1]
4691 mctx = wctx.parents()[-1]
4692
4692
4693 # backup pre-resolve (merge uses .orig for its own purposes)
4693 # backup pre-resolve (merge uses .orig for its own purposes)
4694 a = repo.wjoin(f)
4694 a = repo.wjoin(f)
4695 util.copyfile(a, a + ".resolve")
4695 util.copyfile(a, a + ".resolve")
4696
4696
4697 try:
4697 try:
4698 # resolve file
4698 # resolve file
4699 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4699 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4700 if ms.resolve(f, wctx, mctx):
4700 if ms.resolve(f, wctx, mctx):
4701 ret = 1
4701 ret = 1
4702 finally:
4702 finally:
4703 ui.setconfig('ui', 'forcemerge', '')
4703 ui.setconfig('ui', 'forcemerge', '')
4704
4704
4705 # replace filemerge's .orig file with our resolve file
4705 # replace filemerge's .orig file with our resolve file
4706 util.rename(a + ".resolve", a + ".orig")
4706 util.rename(a + ".resolve", a + ".orig")
4707
4707
4708 ms.commit()
4708 ms.commit()
4709 return ret
4709 return ret
4710
4710
4711 @command('revert',
4711 @command('revert',
4712 [('a', 'all', None, _('revert all changes when no arguments given')),
4712 [('a', 'all', None, _('revert all changes when no arguments given')),
4713 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4713 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4714 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4714 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4715 ('C', 'no-backup', None, _('do not save backup copies of files')),
4715 ('C', 'no-backup', None, _('do not save backup copies of files')),
4716 ] + walkopts + dryrunopts,
4716 ] + walkopts + dryrunopts,
4717 _('[OPTION]... [-r REV] [NAME]...'))
4717 _('[OPTION]... [-r REV] [NAME]...'))
4718 def revert(ui, repo, *pats, **opts):
4718 def revert(ui, repo, *pats, **opts):
4719 """restore files to their checkout state
4719 """restore files to their checkout state
4720
4720
4721 .. note::
4721 .. note::
4722 To check out earlier revisions, you should use :hg:`update REV`.
4722 To check out earlier revisions, you should use :hg:`update REV`.
4723 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4723 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4724
4724
4725 With no revision specified, revert the specified files or directories
4725 With no revision specified, revert the specified files or directories
4726 to the contents they had in the parent of the working directory.
4726 to the contents they had in the parent of the working directory.
4727 This restores the contents of files to an unmodified
4727 This restores the contents of files to an unmodified
4728 state and unschedules adds, removes, copies, and renames. If the
4728 state and unschedules adds, removes, copies, and renames. If the
4729 working directory has two parents, you must explicitly specify a
4729 working directory has two parents, you must explicitly specify a
4730 revision.
4730 revision.
4731
4731
4732 Using the -r/--rev or -d/--date options, revert the given files or
4732 Using the -r/--rev or -d/--date options, revert the given files or
4733 directories to their states as of a specific revision. Because
4733 directories to their states as of a specific revision. Because
4734 revert does not change the working directory parents, this will
4734 revert does not change the working directory parents, this will
4735 cause these files to appear modified. This can be helpful to "back
4735 cause these files to appear modified. This can be helpful to "back
4736 out" some or all of an earlier change. See :hg:`backout` for a
4736 out" some or all of an earlier change. See :hg:`backout` for a
4737 related method.
4737 related method.
4738
4738
4739 Modified files are saved with a .orig suffix before reverting.
4739 Modified files are saved with a .orig suffix before reverting.
4740 To disable these backups, use --no-backup.
4740 To disable these backups, use --no-backup.
4741
4741
4742 See :hg:`help dates` for a list of formats valid for -d/--date.
4742 See :hg:`help dates` for a list of formats valid for -d/--date.
4743
4743
4744 Returns 0 on success.
4744 Returns 0 on success.
4745 """
4745 """
4746
4746
4747 if opts.get("date"):
4747 if opts.get("date"):
4748 if opts.get("rev"):
4748 if opts.get("rev"):
4749 raise util.Abort(_("you can't specify a revision and a date"))
4749 raise util.Abort(_("you can't specify a revision and a date"))
4750 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4750 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4751
4751
4752 parent, p2 = repo.dirstate.parents()
4752 parent, p2 = repo.dirstate.parents()
4753 if not opts.get('rev') and p2 != nullid:
4753 if not opts.get('rev') and p2 != nullid:
4754 # revert after merge is a trap for new users (issue2915)
4754 # revert after merge is a trap for new users (issue2915)
4755 raise util.Abort(_('uncommitted merge with no revision specified'),
4755 raise util.Abort(_('uncommitted merge with no revision specified'),
4756 hint=_('use "hg update" or see "hg help revert"'))
4756 hint=_('use "hg update" or see "hg help revert"'))
4757
4757
4758 ctx = scmutil.revsingle(repo, opts.get('rev'))
4758 ctx = scmutil.revsingle(repo, opts.get('rev'))
4759 node = ctx.node()
4759 node = ctx.node()
4760
4760
4761 if not pats and not opts.get('all'):
4761 if not pats and not opts.get('all'):
4762 msg = _("no files or directories specified")
4762 msg = _("no files or directories specified")
4763 if p2 != nullid:
4763 if p2 != nullid:
4764 hint = _("uncommitted merge, use --all to discard all changes,"
4764 hint = _("uncommitted merge, use --all to discard all changes,"
4765 " or 'hg update -C .' to abort the merge")
4765 " or 'hg update -C .' to abort the merge")
4766 raise util.Abort(msg, hint=hint)
4766 raise util.Abort(msg, hint=hint)
4767 dirty = util.any(repo.status())
4767 dirty = util.any(repo.status())
4768 if node != parent:
4768 if node != parent:
4769 if dirty:
4769 if dirty:
4770 hint = _("uncommitted changes, use --all to discard all"
4770 hint = _("uncommitted changes, use --all to discard all"
4771 " changes, or 'hg update %s' to update") % ctx.rev()
4771 " changes, or 'hg update %s' to update") % ctx.rev()
4772 else:
4772 else:
4773 hint = _("use --all to revert all files,"
4773 hint = _("use --all to revert all files,"
4774 " or 'hg update %s' to update") % ctx.rev()
4774 " or 'hg update %s' to update") % ctx.rev()
4775 elif dirty:
4775 elif dirty:
4776 hint = _("uncommitted changes, use --all to discard all changes")
4776 hint = _("uncommitted changes, use --all to discard all changes")
4777 else:
4777 else:
4778 hint = _("use --all to revert all files")
4778 hint = _("use --all to revert all files")
4779 raise util.Abort(msg, hint=hint)
4779 raise util.Abort(msg, hint=hint)
4780
4780
4781 mf = ctx.manifest()
4781 mf = ctx.manifest()
4782 if node == parent:
4782 if node == parent:
4783 pmf = mf
4783 pmf = mf
4784 else:
4784 else:
4785 pmf = None
4785 pmf = None
4786
4786
4787 # need all matching names in dirstate and manifest of target rev,
4787 # need all matching names in dirstate and manifest of target rev,
4788 # so have to walk both. do not print errors if files exist in one
4788 # so have to walk both. do not print errors if files exist in one
4789 # but not other.
4789 # but not other.
4790
4790
4791 names = {}
4791 names = {}
4792
4792
4793 wlock = repo.wlock()
4793 wlock = repo.wlock()
4794 try:
4794 try:
4795 # walk dirstate.
4795 # walk dirstate.
4796
4796
4797 m = scmutil.match(repo[None], pats, opts)
4797 m = scmutil.match(repo[None], pats, opts)
4798 m.bad = lambda x, y: False
4798 m.bad = lambda x, y: False
4799 for abs in repo.walk(m):
4799 for abs in repo.walk(m):
4800 names[abs] = m.rel(abs), m.exact(abs)
4800 names[abs] = m.rel(abs), m.exact(abs)
4801
4801
4802 # walk target manifest.
4802 # walk target manifest.
4803
4803
4804 def badfn(path, msg):
4804 def badfn(path, msg):
4805 if path in names:
4805 if path in names:
4806 return
4806 return
4807 if path in repo[node].substate:
4807 if path in repo[node].substate:
4808 ui.warn("%s: %s\n" % (m.rel(path),
4808 ui.warn("%s: %s\n" % (m.rel(path),
4809 'reverting subrepos is unsupported'))
4809 'reverting subrepos is unsupported'))
4810 return
4810 return
4811 path_ = path + '/'
4811 path_ = path + '/'
4812 for f in names:
4812 for f in names:
4813 if f.startswith(path_):
4813 if f.startswith(path_):
4814 return
4814 return
4815 ui.warn("%s: %s\n" % (m.rel(path), msg))
4815 ui.warn("%s: %s\n" % (m.rel(path), msg))
4816
4816
4817 m = scmutil.match(repo[node], pats, opts)
4817 m = scmutil.match(repo[node], pats, opts)
4818 m.bad = badfn
4818 m.bad = badfn
4819 for abs in repo[node].walk(m):
4819 for abs in repo[node].walk(m):
4820 if abs not in names:
4820 if abs not in names:
4821 names[abs] = m.rel(abs), m.exact(abs)
4821 names[abs] = m.rel(abs), m.exact(abs)
4822
4822
4823 m = scmutil.matchfiles(repo, names)
4823 m = scmutil.matchfiles(repo, names)
4824 changes = repo.status(match=m)[:4]
4824 changes = repo.status(match=m)[:4]
4825 modified, added, removed, deleted = map(set, changes)
4825 modified, added, removed, deleted = map(set, changes)
4826
4826
4827 # if f is a rename, also revert the source
4827 # if f is a rename, also revert the source
4828 cwd = repo.getcwd()
4828 cwd = repo.getcwd()
4829 for f in added:
4829 for f in added:
4830 src = repo.dirstate.copied(f)
4830 src = repo.dirstate.copied(f)
4831 if src and src not in names and repo.dirstate[src] == 'r':
4831 if src and src not in names and repo.dirstate[src] == 'r':
4832 removed.add(src)
4832 removed.add(src)
4833 names[src] = (repo.pathto(src, cwd), True)
4833 names[src] = (repo.pathto(src, cwd), True)
4834
4834
4835 def removeforget(abs):
4835 def removeforget(abs):
4836 if repo.dirstate[abs] == 'a':
4836 if repo.dirstate[abs] == 'a':
4837 return _('forgetting %s\n')
4837 return _('forgetting %s\n')
4838 return _('removing %s\n')
4838 return _('removing %s\n')
4839
4839
4840 revert = ([], _('reverting %s\n'))
4840 revert = ([], _('reverting %s\n'))
4841 add = ([], _('adding %s\n'))
4841 add = ([], _('adding %s\n'))
4842 remove = ([], removeforget)
4842 remove = ([], removeforget)
4843 undelete = ([], _('undeleting %s\n'))
4843 undelete = ([], _('undeleting %s\n'))
4844
4844
4845 disptable = (
4845 disptable = (
4846 # dispatch table:
4846 # dispatch table:
4847 # file state
4847 # file state
4848 # action if in target manifest
4848 # action if in target manifest
4849 # action if not in target manifest
4849 # action if not in target manifest
4850 # make backup if in target manifest
4850 # make backup if in target manifest
4851 # make backup if not in target manifest
4851 # make backup if not in target manifest
4852 (modified, revert, remove, True, True),
4852 (modified, revert, remove, True, True),
4853 (added, revert, remove, True, False),
4853 (added, revert, remove, True, False),
4854 (removed, undelete, None, False, False),
4854 (removed, undelete, None, False, False),
4855 (deleted, revert, remove, False, False),
4855 (deleted, revert, remove, False, False),
4856 )
4856 )
4857
4857
4858 for abs, (rel, exact) in sorted(names.items()):
4858 for abs, (rel, exact) in sorted(names.items()):
4859 mfentry = mf.get(abs)
4859 mfentry = mf.get(abs)
4860 target = repo.wjoin(abs)
4860 target = repo.wjoin(abs)
4861 def handle(xlist, dobackup):
4861 def handle(xlist, dobackup):
4862 xlist[0].append(abs)
4862 xlist[0].append(abs)
4863 if (dobackup and not opts.get('no_backup') and
4863 if (dobackup and not opts.get('no_backup') and
4864 os.path.lexists(target)):
4864 os.path.lexists(target)):
4865 bakname = "%s.orig" % rel
4865 bakname = "%s.orig" % rel
4866 ui.note(_('saving current version of %s as %s\n') %
4866 ui.note(_('saving current version of %s as %s\n') %
4867 (rel, bakname))
4867 (rel, bakname))
4868 if not opts.get('dry_run'):
4868 if not opts.get('dry_run'):
4869 util.rename(target, bakname)
4869 util.rename(target, bakname)
4870 if ui.verbose or not exact:
4870 if ui.verbose or not exact:
4871 msg = xlist[1]
4871 msg = xlist[1]
4872 if not isinstance(msg, basestring):
4872 if not isinstance(msg, basestring):
4873 msg = msg(abs)
4873 msg = msg(abs)
4874 ui.status(msg % rel)
4874 ui.status(msg % rel)
4875 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4875 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4876 if abs not in table:
4876 if abs not in table:
4877 continue
4877 continue
4878 # file has changed in dirstate
4878 # file has changed in dirstate
4879 if mfentry:
4879 if mfentry:
4880 handle(hitlist, backuphit)
4880 handle(hitlist, backuphit)
4881 elif misslist is not None:
4881 elif misslist is not None:
4882 handle(misslist, backupmiss)
4882 handle(misslist, backupmiss)
4883 break
4883 break
4884 else:
4884 else:
4885 if abs not in repo.dirstate:
4885 if abs not in repo.dirstate:
4886 if mfentry:
4886 if mfentry:
4887 handle(add, True)
4887 handle(add, True)
4888 elif exact:
4888 elif exact:
4889 ui.warn(_('file not managed: %s\n') % rel)
4889 ui.warn(_('file not managed: %s\n') % rel)
4890 continue
4890 continue
4891 # file has not changed in dirstate
4891 # file has not changed in dirstate
4892 if node == parent:
4892 if node == parent:
4893 if exact:
4893 if exact:
4894 ui.warn(_('no changes needed to %s\n') % rel)
4894 ui.warn(_('no changes needed to %s\n') % rel)
4895 continue
4895 continue
4896 if pmf is None:
4896 if pmf is None:
4897 # only need parent manifest in this unlikely case,
4897 # only need parent manifest in this unlikely case,
4898 # so do not read by default
4898 # so do not read by default
4899 pmf = repo[parent].manifest()
4899 pmf = repo[parent].manifest()
4900 if abs in pmf and mfentry:
4900 if abs in pmf and mfentry:
4901 # if version of file is same in parent and target
4901 # if version of file is same in parent and target
4902 # manifests, do nothing
4902 # manifests, do nothing
4903 if (pmf[abs] != mfentry or
4903 if (pmf[abs] != mfentry or
4904 pmf.flags(abs) != mf.flags(abs)):
4904 pmf.flags(abs) != mf.flags(abs)):
4905 handle(revert, False)
4905 handle(revert, False)
4906 else:
4906 else:
4907 handle(remove, False)
4907 handle(remove, False)
4908
4908
4909 if not opts.get('dry_run'):
4909 if not opts.get('dry_run'):
4910 def checkout(f):
4910 def checkout(f):
4911 fc = ctx[f]
4911 fc = ctx[f]
4912 repo.wwrite(f, fc.data(), fc.flags())
4912 repo.wwrite(f, fc.data(), fc.flags())
4913
4913
4914 audit_path = scmutil.pathauditor(repo.root)
4914 audit_path = scmutil.pathauditor(repo.root)
4915 for f in remove[0]:
4915 for f in remove[0]:
4916 if repo.dirstate[f] == 'a':
4916 if repo.dirstate[f] == 'a':
4917 repo.dirstate.drop(f)
4917 repo.dirstate.drop(f)
4918 continue
4918 continue
4919 audit_path(f)
4919 audit_path(f)
4920 try:
4920 try:
4921 util.unlinkpath(repo.wjoin(f))
4921 util.unlinkpath(repo.wjoin(f))
4922 except OSError:
4922 except OSError:
4923 pass
4923 pass
4924 repo.dirstate.remove(f)
4924 repo.dirstate.remove(f)
4925
4925
4926 normal = None
4926 normal = None
4927 if node == parent:
4927 if node == parent:
4928 # We're reverting to our parent. If possible, we'd like status
4928 # We're reverting to our parent. If possible, we'd like status
4929 # to report the file as clean. We have to use normallookup for
4929 # to report the file as clean. We have to use normallookup for
4930 # merges to avoid losing information about merged/dirty files.
4930 # merges to avoid losing information about merged/dirty files.
4931 if p2 != nullid:
4931 if p2 != nullid:
4932 normal = repo.dirstate.normallookup
4932 normal = repo.dirstate.normallookup
4933 else:
4933 else:
4934 normal = repo.dirstate.normal
4934 normal = repo.dirstate.normal
4935 for f in revert[0]:
4935 for f in revert[0]:
4936 checkout(f)
4936 checkout(f)
4937 if normal:
4937 if normal:
4938 normal(f)
4938 normal(f)
4939
4939
4940 for f in add[0]:
4940 for f in add[0]:
4941 checkout(f)
4941 checkout(f)
4942 repo.dirstate.add(f)
4942 repo.dirstate.add(f)
4943
4943
4944 normal = repo.dirstate.normallookup
4944 normal = repo.dirstate.normallookup
4945 if node == parent and p2 == nullid:
4945 if node == parent and p2 == nullid:
4946 normal = repo.dirstate.normal
4946 normal = repo.dirstate.normal
4947 for f in undelete[0]:
4947 for f in undelete[0]:
4948 checkout(f)
4948 checkout(f)
4949 normal(f)
4949 normal(f)
4950
4950
4951 finally:
4951 finally:
4952 wlock.release()
4952 wlock.release()
4953
4953
4954 @command('rollback', dryrunopts +
4954 @command('rollback', dryrunopts +
4955 [('f', 'force', False, _('ignore safety measures'))])
4955 [('f', 'force', False, _('ignore safety measures'))])
4956 def rollback(ui, repo, **opts):
4956 def rollback(ui, repo, **opts):
4957 """roll back the last transaction (dangerous)
4957 """roll back the last transaction (dangerous)
4958
4958
4959 This command should be used with care. There is only one level of
4959 This command should be used with care. There is only one level of
4960 rollback, and there is no way to undo a rollback. It will also
4960 rollback, and there is no way to undo a rollback. It will also
4961 restore the dirstate at the time of the last transaction, losing
4961 restore the dirstate at the time of the last transaction, losing
4962 any dirstate changes since that time. This command does not alter
4962 any dirstate changes since that time. This command does not alter
4963 the working directory.
4963 the working directory.
4964
4964
4965 Transactions are used to encapsulate the effects of all commands
4965 Transactions are used to encapsulate the effects of all commands
4966 that create new changesets or propagate existing changesets into a
4966 that create new changesets or propagate existing changesets into a
4967 repository. For example, the following commands are transactional,
4967 repository. For example, the following commands are transactional,
4968 and their effects can be rolled back:
4968 and their effects can be rolled back:
4969
4969
4970 - commit
4970 - commit
4971 - import
4971 - import
4972 - pull
4972 - pull
4973 - push (with this repository as the destination)
4973 - push (with this repository as the destination)
4974 - unbundle
4974 - unbundle
4975
4975
4976 To avoid permanent data loss, rollback will refuse to rollback a
4976 To avoid permanent data loss, rollback will refuse to rollback a
4977 commit transaction if it isn't checked out. Use --force to
4977 commit transaction if it isn't checked out. Use --force to
4978 override this protection.
4978 override this protection.
4979
4979
4980 This command is not intended for use on public repositories. Once
4980 This command is not intended for use on public repositories. Once
4981 changes are visible for pull by other users, rolling a transaction
4981 changes are visible for pull by other users, rolling a transaction
4982 back locally is ineffective (someone else may already have pulled
4982 back locally is ineffective (someone else may already have pulled
4983 the changes). Furthermore, a race is possible with readers of the
4983 the changes). Furthermore, a race is possible with readers of the
4984 repository; for example an in-progress pull from the repository
4984 repository; for example an in-progress pull from the repository
4985 may fail if a rollback is performed.
4985 may fail if a rollback is performed.
4986
4986
4987 Returns 0 on success, 1 if no rollback data is available.
4987 Returns 0 on success, 1 if no rollback data is available.
4988 """
4988 """
4989 return repo.rollback(dryrun=opts.get('dry_run'),
4989 return repo.rollback(dryrun=opts.get('dry_run'),
4990 force=opts.get('force'))
4990 force=opts.get('force'))
4991
4991
4992 @command('root', [])
4992 @command('root', [])
4993 def root(ui, repo):
4993 def root(ui, repo):
4994 """print the root (top) of the current working directory
4994 """print the root (top) of the current working directory
4995
4995
4996 Print the root directory of the current repository.
4996 Print the root directory of the current repository.
4997
4997
4998 Returns 0 on success.
4998 Returns 0 on success.
4999 """
4999 """
5000 ui.write(repo.root + "\n")
5000 ui.write(repo.root + "\n")
5001
5001
5002 @command('^serve',
5002 @command('^serve',
5003 [('A', 'accesslog', '', _('name of access log file to write to'),
5003 [('A', 'accesslog', '', _('name of access log file to write to'),
5004 _('FILE')),
5004 _('FILE')),
5005 ('d', 'daemon', None, _('run server in background')),
5005 ('d', 'daemon', None, _('run server in background')),
5006 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5006 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5007 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5007 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5008 # use string type, then we can check if something was passed
5008 # use string type, then we can check if something was passed
5009 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5009 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5010 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5010 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5011 _('ADDR')),
5011 _('ADDR')),
5012 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5012 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5013 _('PREFIX')),
5013 _('PREFIX')),
5014 ('n', 'name', '',
5014 ('n', 'name', '',
5015 _('name to show in web pages (default: working directory)'), _('NAME')),
5015 _('name to show in web pages (default: working directory)'), _('NAME')),
5016 ('', 'web-conf', '',
5016 ('', 'web-conf', '',
5017 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5017 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5018 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5018 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5019 _('FILE')),
5019 _('FILE')),
5020 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5020 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5021 ('', 'stdio', None, _('for remote clients')),
5021 ('', 'stdio', None, _('for remote clients')),
5022 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5022 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5023 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5023 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5024 ('', 'style', '', _('template style to use'), _('STYLE')),
5024 ('', 'style', '', _('template style to use'), _('STYLE')),
5025 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5025 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5026 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5026 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5027 _('[OPTION]...'))
5027 _('[OPTION]...'))
5028 def serve(ui, repo, **opts):
5028 def serve(ui, repo, **opts):
5029 """start stand-alone webserver
5029 """start stand-alone webserver
5030
5030
5031 Start a local HTTP repository browser and pull server. You can use
5031 Start a local HTTP repository browser and pull server. You can use
5032 this for ad-hoc sharing and browsing of repositories. It is
5032 this for ad-hoc sharing and browsing of repositories. It is
5033 recommended to use a real web server to serve a repository for
5033 recommended to use a real web server to serve a repository for
5034 longer periods of time.
5034 longer periods of time.
5035
5035
5036 Please note that the server does not implement access control.
5036 Please note that the server does not implement access control.
5037 This means that, by default, anybody can read from the server and
5037 This means that, by default, anybody can read from the server and
5038 nobody can write to it by default. Set the ``web.allow_push``
5038 nobody can write to it by default. Set the ``web.allow_push``
5039 option to ``*`` to allow everybody to push to the server. You
5039 option to ``*`` to allow everybody to push to the server. You
5040 should use a real web server if you need to authenticate users.
5040 should use a real web server if you need to authenticate users.
5041
5041
5042 By default, the server logs accesses to stdout and errors to
5042 By default, the server logs accesses to stdout and errors to
5043 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5043 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5044 files.
5044 files.
5045
5045
5046 To have the server choose a free port number to listen on, specify
5046 To have the server choose a free port number to listen on, specify
5047 a port number of 0; in this case, the server will print the port
5047 a port number of 0; in this case, the server will print the port
5048 number it uses.
5048 number it uses.
5049
5049
5050 Returns 0 on success.
5050 Returns 0 on success.
5051 """
5051 """
5052
5052
5053 if opts["stdio"] and opts["cmdserver"]:
5053 if opts["stdio"] and opts["cmdserver"]:
5054 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5054 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5055
5055
5056 def checkrepo():
5056 def checkrepo():
5057 if repo is None:
5057 if repo is None:
5058 raise error.RepoError(_("There is no Mercurial repository here"
5058 raise error.RepoError(_("There is no Mercurial repository here"
5059 " (.hg not found)"))
5059 " (.hg not found)"))
5060
5060
5061 if opts["stdio"]:
5061 if opts["stdio"]:
5062 checkrepo()
5062 checkrepo()
5063 s = sshserver.sshserver(ui, repo)
5063 s = sshserver.sshserver(ui, repo)
5064 s.serve_forever()
5064 s.serve_forever()
5065
5065
5066 if opts["cmdserver"]:
5066 if opts["cmdserver"]:
5067 checkrepo()
5067 checkrepo()
5068 s = commandserver.server(ui, repo, opts["cmdserver"])
5068 s = commandserver.server(ui, repo, opts["cmdserver"])
5069 return s.serve()
5069 return s.serve()
5070
5070
5071 # this way we can check if something was given in the command-line
5071 # this way we can check if something was given in the command-line
5072 if opts.get('port'):
5072 if opts.get('port'):
5073 opts['port'] = util.getport(opts.get('port'))
5073 opts['port'] = util.getport(opts.get('port'))
5074
5074
5075 baseui = repo and repo.baseui or ui
5075 baseui = repo and repo.baseui or ui
5076 optlist = ("name templates style address port prefix ipv6"
5076 optlist = ("name templates style address port prefix ipv6"
5077 " accesslog errorlog certificate encoding")
5077 " accesslog errorlog certificate encoding")
5078 for o in optlist.split():
5078 for o in optlist.split():
5079 val = opts.get(o, '')
5079 val = opts.get(o, '')
5080 if val in (None, ''): # should check against default options instead
5080 if val in (None, ''): # should check against default options instead
5081 continue
5081 continue
5082 baseui.setconfig("web", o, val)
5082 baseui.setconfig("web", o, val)
5083 if repo and repo.ui != baseui:
5083 if repo and repo.ui != baseui:
5084 repo.ui.setconfig("web", o, val)
5084 repo.ui.setconfig("web", o, val)
5085
5085
5086 o = opts.get('web_conf') or opts.get('webdir_conf')
5086 o = opts.get('web_conf') or opts.get('webdir_conf')
5087 if not o:
5087 if not o:
5088 if not repo:
5088 if not repo:
5089 raise error.RepoError(_("There is no Mercurial repository"
5089 raise error.RepoError(_("There is no Mercurial repository"
5090 " here (.hg not found)"))
5090 " here (.hg not found)"))
5091 o = repo.root
5091 o = repo.root
5092
5092
5093 app = hgweb.hgweb(o, baseui=ui)
5093 app = hgweb.hgweb(o, baseui=ui)
5094
5094
5095 class service(object):
5095 class service(object):
5096 def init(self):
5096 def init(self):
5097 util.setsignalhandler()
5097 util.setsignalhandler()
5098 self.httpd = hgweb.server.create_server(ui, app)
5098 self.httpd = hgweb.server.create_server(ui, app)
5099
5099
5100 if opts['port'] and not ui.verbose:
5100 if opts['port'] and not ui.verbose:
5101 return
5101 return
5102
5102
5103 if self.httpd.prefix:
5103 if self.httpd.prefix:
5104 prefix = self.httpd.prefix.strip('/') + '/'
5104 prefix = self.httpd.prefix.strip('/') + '/'
5105 else:
5105 else:
5106 prefix = ''
5106 prefix = ''
5107
5107
5108 port = ':%d' % self.httpd.port
5108 port = ':%d' % self.httpd.port
5109 if port == ':80':
5109 if port == ':80':
5110 port = ''
5110 port = ''
5111
5111
5112 bindaddr = self.httpd.addr
5112 bindaddr = self.httpd.addr
5113 if bindaddr == '0.0.0.0':
5113 if bindaddr == '0.0.0.0':
5114 bindaddr = '*'
5114 bindaddr = '*'
5115 elif ':' in bindaddr: # IPv6
5115 elif ':' in bindaddr: # IPv6
5116 bindaddr = '[%s]' % bindaddr
5116 bindaddr = '[%s]' % bindaddr
5117
5117
5118 fqaddr = self.httpd.fqaddr
5118 fqaddr = self.httpd.fqaddr
5119 if ':' in fqaddr:
5119 if ':' in fqaddr:
5120 fqaddr = '[%s]' % fqaddr
5120 fqaddr = '[%s]' % fqaddr
5121 if opts['port']:
5121 if opts['port']:
5122 write = ui.status
5122 write = ui.status
5123 else:
5123 else:
5124 write = ui.write
5124 write = ui.write
5125 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5125 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5126 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5126 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5127
5127
5128 def run(self):
5128 def run(self):
5129 self.httpd.serve_forever()
5129 self.httpd.serve_forever()
5130
5130
5131 service = service()
5131 service = service()
5132
5132
5133 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5133 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5134
5134
5135 @command('showconfig|debugconfig',
5135 @command('showconfig|debugconfig',
5136 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5136 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5137 _('[-u] [NAME]...'))
5137 _('[-u] [NAME]...'))
5138 def showconfig(ui, repo, *values, **opts):
5138 def showconfig(ui, repo, *values, **opts):
5139 """show combined config settings from all hgrc files
5139 """show combined config settings from all hgrc files
5140
5140
5141 With no arguments, print names and values of all config items.
5141 With no arguments, print names and values of all config items.
5142
5142
5143 With one argument of the form section.name, print just the value
5143 With one argument of the form section.name, print just the value
5144 of that config item.
5144 of that config item.
5145
5145
5146 With multiple arguments, print names and values of all config
5146 With multiple arguments, print names and values of all config
5147 items with matching section names.
5147 items with matching section names.
5148
5148
5149 With --debug, the source (filename and line number) is printed
5149 With --debug, the source (filename and line number) is printed
5150 for each config item.
5150 for each config item.
5151
5151
5152 Returns 0 on success.
5152 Returns 0 on success.
5153 """
5153 """
5154
5154
5155 for f in scmutil.rcpath():
5155 for f in scmutil.rcpath():
5156 ui.debug('read config from: %s\n' % f)
5156 ui.debug('read config from: %s\n' % f)
5157 untrusted = bool(opts.get('untrusted'))
5157 untrusted = bool(opts.get('untrusted'))
5158 if values:
5158 if values:
5159 sections = [v for v in values if '.' not in v]
5159 sections = [v for v in values if '.' not in v]
5160 items = [v for v in values if '.' in v]
5160 items = [v for v in values if '.' in v]
5161 if len(items) > 1 or items and sections:
5161 if len(items) > 1 or items and sections:
5162 raise util.Abort(_('only one config item permitted'))
5162 raise util.Abort(_('only one config item permitted'))
5163 for section, name, value in ui.walkconfig(untrusted=untrusted):
5163 for section, name, value in ui.walkconfig(untrusted=untrusted):
5164 value = str(value).replace('\n', '\\n')
5164 value = str(value).replace('\n', '\\n')
5165 sectname = section + '.' + name
5165 sectname = section + '.' + name
5166 if values:
5166 if values:
5167 for v in values:
5167 for v in values:
5168 if v == section:
5168 if v == section:
5169 ui.debug('%s: ' %
5169 ui.debug('%s: ' %
5170 ui.configsource(section, name, untrusted))
5170 ui.configsource(section, name, untrusted))
5171 ui.write('%s=%s\n' % (sectname, value))
5171 ui.write('%s=%s\n' % (sectname, value))
5172 elif v == sectname:
5172 elif v == sectname:
5173 ui.debug('%s: ' %
5173 ui.debug('%s: ' %
5174 ui.configsource(section, name, untrusted))
5174 ui.configsource(section, name, untrusted))
5175 ui.write(value, '\n')
5175 ui.write(value, '\n')
5176 else:
5176 else:
5177 ui.debug('%s: ' %
5177 ui.debug('%s: ' %
5178 ui.configsource(section, name, untrusted))
5178 ui.configsource(section, name, untrusted))
5179 ui.write('%s=%s\n' % (sectname, value))
5179 ui.write('%s=%s\n' % (sectname, value))
5180
5180
5181 @command('^status|st',
5181 @command('^status|st',
5182 [('A', 'all', None, _('show status of all files')),
5182 [('A', 'all', None, _('show status of all files')),
5183 ('m', 'modified', None, _('show only modified files')),
5183 ('m', 'modified', None, _('show only modified files')),
5184 ('a', 'added', None, _('show only added files')),
5184 ('a', 'added', None, _('show only added files')),
5185 ('r', 'removed', None, _('show only removed files')),
5185 ('r', 'removed', None, _('show only removed files')),
5186 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5186 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5187 ('c', 'clean', None, _('show only files without changes')),
5187 ('c', 'clean', None, _('show only files without changes')),
5188 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5188 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5189 ('i', 'ignored', None, _('show only ignored files')),
5189 ('i', 'ignored', None, _('show only ignored files')),
5190 ('n', 'no-status', None, _('hide status prefix')),
5190 ('n', 'no-status', None, _('hide status prefix')),
5191 ('C', 'copies', None, _('show source of copied files')),
5191 ('C', 'copies', None, _('show source of copied files')),
5192 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5192 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5193 ('', 'rev', [], _('show difference from revision'), _('REV')),
5193 ('', 'rev', [], _('show difference from revision'), _('REV')),
5194 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5194 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5195 ] + walkopts + subrepoopts,
5195 ] + walkopts + subrepoopts,
5196 _('[OPTION]... [FILE]...'))
5196 _('[OPTION]... [FILE]...'))
5197 def status(ui, repo, *pats, **opts):
5197 def status(ui, repo, *pats, **opts):
5198 """show changed files in the working directory
5198 """show changed files in the working directory
5199
5199
5200 Show status of files in the repository. If names are given, only
5200 Show status of files in the repository. If names are given, only
5201 files that match are shown. Files that are clean or ignored or
5201 files that match are shown. Files that are clean or ignored or
5202 the source of a copy/move operation, are not listed unless
5202 the source of a copy/move operation, are not listed unless
5203 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5203 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5204 Unless options described with "show only ..." are given, the
5204 Unless options described with "show only ..." are given, the
5205 options -mardu are used.
5205 options -mardu are used.
5206
5206
5207 Option -q/--quiet hides untracked (unknown and ignored) files
5207 Option -q/--quiet hides untracked (unknown and ignored) files
5208 unless explicitly requested with -u/--unknown or -i/--ignored.
5208 unless explicitly requested with -u/--unknown or -i/--ignored.
5209
5209
5210 .. note::
5210 .. note::
5211 status may appear to disagree with diff if permissions have
5211 status may appear to disagree with diff if permissions have
5212 changed or a merge has occurred. The standard diff format does
5212 changed or a merge has occurred. The standard diff format does
5213 not report permission changes and diff only reports changes
5213 not report permission changes and diff only reports changes
5214 relative to one merge parent.
5214 relative to one merge parent.
5215
5215
5216 If one revision is given, it is used as the base revision.
5216 If one revision is given, it is used as the base revision.
5217 If two revisions are given, the differences between them are
5217 If two revisions are given, the differences between them are
5218 shown. The --change option can also be used as a shortcut to list
5218 shown. The --change option can also be used as a shortcut to list
5219 the changed files of a revision from its first parent.
5219 the changed files of a revision from its first parent.
5220
5220
5221 The codes used to show the status of files are::
5221 The codes used to show the status of files are::
5222
5222
5223 M = modified
5223 M = modified
5224 A = added
5224 A = added
5225 R = removed
5225 R = removed
5226 C = clean
5226 C = clean
5227 ! = missing (deleted by non-hg command, but still tracked)
5227 ! = missing (deleted by non-hg command, but still tracked)
5228 ? = not tracked
5228 ? = not tracked
5229 I = ignored
5229 I = ignored
5230 = origin of the previous file listed as A (added)
5230 = origin of the previous file listed as A (added)
5231
5231
5232 .. container:: verbose
5232 .. container:: verbose
5233
5233
5234 Examples:
5234 Examples:
5235
5235
5236 - show changes in the working directory relative to a
5236 - show changes in the working directory relative to a
5237 changeset::
5237 changeset::
5238
5238
5239 hg status --rev 9353
5239 hg status --rev 9353
5240
5240
5241 - show all changes including copies in an existing changeset::
5241 - show all changes including copies in an existing changeset::
5242
5242
5243 hg status --copies --change 9353
5243 hg status --copies --change 9353
5244
5244
5245 - get a NUL separated list of added files, suitable for xargs::
5245 - get a NUL separated list of added files, suitable for xargs::
5246
5246
5247 hg status -an0
5247 hg status -an0
5248
5248
5249 Returns 0 on success.
5249 Returns 0 on success.
5250 """
5250 """
5251
5251
5252 revs = opts.get('rev')
5252 revs = opts.get('rev')
5253 change = opts.get('change')
5253 change = opts.get('change')
5254
5254
5255 if revs and change:
5255 if revs and change:
5256 msg = _('cannot specify --rev and --change at the same time')
5256 msg = _('cannot specify --rev and --change at the same time')
5257 raise util.Abort(msg)
5257 raise util.Abort(msg)
5258 elif change:
5258 elif change:
5259 node2 = scmutil.revsingle(repo, change, None).node()
5259 node2 = scmutil.revsingle(repo, change, None).node()
5260 node1 = repo[node2].p1().node()
5260 node1 = repo[node2].p1().node()
5261 else:
5261 else:
5262 node1, node2 = scmutil.revpair(repo, revs)
5262 node1, node2 = scmutil.revpair(repo, revs)
5263
5263
5264 cwd = (pats and repo.getcwd()) or ''
5264 cwd = (pats and repo.getcwd()) or ''
5265 end = opts.get('print0') and '\0' or '\n'
5265 end = opts.get('print0') and '\0' or '\n'
5266 copy = {}
5266 copy = {}
5267 states = 'modified added removed deleted unknown ignored clean'.split()
5267 states = 'modified added removed deleted unknown ignored clean'.split()
5268 show = [k for k in states if opts.get(k)]
5268 show = [k for k in states if opts.get(k)]
5269 if opts.get('all'):
5269 if opts.get('all'):
5270 show += ui.quiet and (states[:4] + ['clean']) or states
5270 show += ui.quiet and (states[:4] + ['clean']) or states
5271 if not show:
5271 if not show:
5272 show = ui.quiet and states[:4] or states[:5]
5272 show = ui.quiet and states[:4] or states[:5]
5273
5273
5274 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5274 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5275 'ignored' in show, 'clean' in show, 'unknown' in show,
5275 'ignored' in show, 'clean' in show, 'unknown' in show,
5276 opts.get('subrepos'))
5276 opts.get('subrepos'))
5277 changestates = zip(states, 'MAR!?IC', stat)
5277 changestates = zip(states, 'MAR!?IC', stat)
5278
5278
5279 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5279 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5280 copy = copies.pathcopies(repo[node1], repo[node2])
5280 copy = copies.pathcopies(repo[node1], repo[node2])
5281
5281
5282 fm = ui.formatter('status', opts)
5282 fm = ui.formatter('status', opts)
5283 format = '%s %s' + end
5283 format = '%s %s' + end
5284 if opts.get('no_status'):
5284 if opts.get('no_status'):
5285 format = '%.0s%s' + end
5285 format = '%.0s%s' + end
5286
5286
5287 for state, char, files in changestates:
5287 for state, char, files in changestates:
5288 if state in show:
5288 if state in show:
5289 label = 'status.' + state
5289 label = 'status.' + state
5290 for f in files:
5290 for f in files:
5291 fm.startitem()
5291 fm.startitem()
5292 fm.write("status path", format, char,
5292 fm.write("status path", format, char,
5293 repo.pathto(f, cwd), label=label)
5293 repo.pathto(f, cwd), label=label)
5294 if f in copy:
5294 if f in copy:
5295 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5295 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5296 label='status.copied')
5296 label='status.copied')
5297 fm.end()
5297 fm.end()
5298
5298
5299 @command('^summary|sum',
5299 @command('^summary|sum',
5300 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5300 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5301 def summary(ui, repo, **opts):
5301 def summary(ui, repo, **opts):
5302 """summarize working directory state
5302 """summarize working directory state
5303
5303
5304 This generates a brief summary of the working directory state,
5304 This generates a brief summary of the working directory state,
5305 including parents, branch, commit status, and available updates.
5305 including parents, branch, commit status, and available updates.
5306
5306
5307 With the --remote option, this will check the default paths for
5307 With the --remote option, this will check the default paths for
5308 incoming and outgoing changes. This can be time-consuming.
5308 incoming and outgoing changes. This can be time-consuming.
5309
5309
5310 Returns 0 on success.
5310 Returns 0 on success.
5311 """
5311 """
5312
5312
5313 ctx = repo[None]
5313 ctx = repo[None]
5314 parents = ctx.parents()
5314 parents = ctx.parents()
5315 pnode = parents[0].node()
5315 pnode = parents[0].node()
5316 marks = []
5316 marks = []
5317
5317
5318 for p in parents:
5318 for p in parents:
5319 # label with log.changeset (instead of log.parent) since this
5319 # label with log.changeset (instead of log.parent) since this
5320 # shows a working directory parent *changeset*:
5320 # shows a working directory parent *changeset*:
5321 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5321 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5322 label='log.changeset')
5322 label='log.changeset')
5323 ui.write(' '.join(p.tags()), label='log.tag')
5323 ui.write(' '.join(p.tags()), label='log.tag')
5324 if p.bookmarks():
5324 if p.bookmarks():
5325 marks.extend(p.bookmarks())
5325 marks.extend(p.bookmarks())
5326 if p.rev() == -1:
5326 if p.rev() == -1:
5327 if not len(repo):
5327 if not len(repo):
5328 ui.write(_(' (empty repository)'))
5328 ui.write(_(' (empty repository)'))
5329 else:
5329 else:
5330 ui.write(_(' (no revision checked out)'))
5330 ui.write(_(' (no revision checked out)'))
5331 ui.write('\n')
5331 ui.write('\n')
5332 if p.description():
5332 if p.description():
5333 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5333 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5334 label='log.summary')
5334 label='log.summary')
5335
5335
5336 branch = ctx.branch()
5336 branch = ctx.branch()
5337 bheads = repo.branchheads(branch)
5337 bheads = repo.branchheads(branch)
5338 m = _('branch: %s\n') % branch
5338 m = _('branch: %s\n') % branch
5339 if branch != 'default':
5339 if branch != 'default':
5340 ui.write(m, label='log.branch')
5340 ui.write(m, label='log.branch')
5341 else:
5341 else:
5342 ui.status(m, label='log.branch')
5342 ui.status(m, label='log.branch')
5343
5343
5344 if marks:
5344 if marks:
5345 current = repo._bookmarkcurrent
5345 current = repo._bookmarkcurrent
5346 ui.write(_('bookmarks:'), label='log.bookmark')
5346 ui.write(_('bookmarks:'), label='log.bookmark')
5347 if current is not None:
5347 if current is not None:
5348 try:
5348 try:
5349 marks.remove(current)
5349 marks.remove(current)
5350 ui.write(' *' + current, label='bookmarks.current')
5350 ui.write(' *' + current, label='bookmarks.current')
5351 except ValueError:
5351 except ValueError:
5352 # current bookmark not in parent ctx marks
5352 # current bookmark not in parent ctx marks
5353 pass
5353 pass
5354 for m in marks:
5354 for m in marks:
5355 ui.write(' ' + m, label='log.bookmark')
5355 ui.write(' ' + m, label='log.bookmark')
5356 ui.write('\n', label='log.bookmark')
5356 ui.write('\n', label='log.bookmark')
5357
5357
5358 st = list(repo.status(unknown=True))[:6]
5358 st = list(repo.status(unknown=True))[:6]
5359
5359
5360 c = repo.dirstate.copies()
5360 c = repo.dirstate.copies()
5361 copied, renamed = [], []
5361 copied, renamed = [], []
5362 for d, s in c.iteritems():
5362 for d, s in c.iteritems():
5363 if s in st[2]:
5363 if s in st[2]:
5364 st[2].remove(s)
5364 st[2].remove(s)
5365 renamed.append(d)
5365 renamed.append(d)
5366 else:
5366 else:
5367 copied.append(d)
5367 copied.append(d)
5368 if d in st[1]:
5368 if d in st[1]:
5369 st[1].remove(d)
5369 st[1].remove(d)
5370 st.insert(3, renamed)
5370 st.insert(3, renamed)
5371 st.insert(4, copied)
5371 st.insert(4, copied)
5372
5372
5373 ms = mergemod.mergestate(repo)
5373 ms = mergemod.mergestate(repo)
5374 st.append([f for f in ms if ms[f] == 'u'])
5374 st.append([f for f in ms if ms[f] == 'u'])
5375
5375
5376 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5376 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5377 st.append(subs)
5377 st.append(subs)
5378
5378
5379 labels = [ui.label(_('%d modified'), 'status.modified'),
5379 labels = [ui.label(_('%d modified'), 'status.modified'),
5380 ui.label(_('%d added'), 'status.added'),
5380 ui.label(_('%d added'), 'status.added'),
5381 ui.label(_('%d removed'), 'status.removed'),
5381 ui.label(_('%d removed'), 'status.removed'),
5382 ui.label(_('%d renamed'), 'status.copied'),
5382 ui.label(_('%d renamed'), 'status.copied'),
5383 ui.label(_('%d copied'), 'status.copied'),
5383 ui.label(_('%d copied'), 'status.copied'),
5384 ui.label(_('%d deleted'), 'status.deleted'),
5384 ui.label(_('%d deleted'), 'status.deleted'),
5385 ui.label(_('%d unknown'), 'status.unknown'),
5385 ui.label(_('%d unknown'), 'status.unknown'),
5386 ui.label(_('%d ignored'), 'status.ignored'),
5386 ui.label(_('%d ignored'), 'status.ignored'),
5387 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5387 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5388 ui.label(_('%d subrepos'), 'status.modified')]
5388 ui.label(_('%d subrepos'), 'status.modified')]
5389 t = []
5389 t = []
5390 for s, l in zip(st, labels):
5390 for s, l in zip(st, labels):
5391 if s:
5391 if s:
5392 t.append(l % len(s))
5392 t.append(l % len(s))
5393
5393
5394 t = ', '.join(t)
5394 t = ', '.join(t)
5395 cleanworkdir = False
5395 cleanworkdir = False
5396
5396
5397 if len(parents) > 1:
5397 if len(parents) > 1:
5398 t += _(' (merge)')
5398 t += _(' (merge)')
5399 elif branch != parents[0].branch():
5399 elif branch != parents[0].branch():
5400 t += _(' (new branch)')
5400 t += _(' (new branch)')
5401 elif (parents[0].extra().get('close') and
5401 elif (parents[0].extra().get('close') and
5402 pnode in repo.branchheads(branch, closed=True)):
5402 pnode in repo.branchheads(branch, closed=True)):
5403 t += _(' (head closed)')
5403 t += _(' (head closed)')
5404 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5404 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5405 t += _(' (clean)')
5405 t += _(' (clean)')
5406 cleanworkdir = True
5406 cleanworkdir = True
5407 elif pnode not in bheads:
5407 elif pnode not in bheads:
5408 t += _(' (new branch head)')
5408 t += _(' (new branch head)')
5409
5409
5410 if cleanworkdir:
5410 if cleanworkdir:
5411 ui.status(_('commit: %s\n') % t.strip())
5411 ui.status(_('commit: %s\n') % t.strip())
5412 else:
5412 else:
5413 ui.write(_('commit: %s\n') % t.strip())
5413 ui.write(_('commit: %s\n') % t.strip())
5414
5414
5415 # all ancestors of branch heads - all ancestors of parent = new csets
5415 # all ancestors of branch heads - all ancestors of parent = new csets
5416 new = [0] * len(repo)
5416 new = [0] * len(repo)
5417 cl = repo.changelog
5417 cl = repo.changelog
5418 for a in [cl.rev(n) for n in bheads]:
5418 for a in [cl.rev(n) for n in bheads]:
5419 new[a] = 1
5419 new[a] = 1
5420 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5420 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5421 new[a] = 1
5421 new[a] = 1
5422 for a in [p.rev() for p in parents]:
5422 for a in [p.rev() for p in parents]:
5423 if a >= 0:
5423 if a >= 0:
5424 new[a] = 0
5424 new[a] = 0
5425 for a in cl.ancestors(*[p.rev() for p in parents]):
5425 for a in cl.ancestors(*[p.rev() for p in parents]):
5426 new[a] = 0
5426 new[a] = 0
5427 new = sum(new)
5427 new = sum(new)
5428
5428
5429 if new == 0:
5429 if new == 0:
5430 ui.status(_('update: (current)\n'))
5430 ui.status(_('update: (current)\n'))
5431 elif pnode not in bheads:
5431 elif pnode not in bheads:
5432 ui.write(_('update: %d new changesets (update)\n') % new)
5432 ui.write(_('update: %d new changesets (update)\n') % new)
5433 else:
5433 else:
5434 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5434 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5435 (new, len(bheads)))
5435 (new, len(bheads)))
5436
5436
5437 if opts.get('remote'):
5437 if opts.get('remote'):
5438 t = []
5438 t = []
5439 source, branches = hg.parseurl(ui.expandpath('default'))
5439 source, branches = hg.parseurl(ui.expandpath('default'))
5440 other = hg.peer(repo, {}, source)
5440 other = hg.peer(repo, {}, source)
5441 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5441 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5442 ui.debug('comparing with %s\n' % util.hidepassword(source))
5442 ui.debug('comparing with %s\n' % util.hidepassword(source))
5443 repo.ui.pushbuffer()
5443 repo.ui.pushbuffer()
5444 commoninc = discovery.findcommonincoming(repo, other)
5444 commoninc = discovery.findcommonincoming(repo, other)
5445 _common, incoming, _rheads = commoninc
5445 _common, incoming, _rheads = commoninc
5446 repo.ui.popbuffer()
5446 repo.ui.popbuffer()
5447 if incoming:
5447 if incoming:
5448 t.append(_('1 or more incoming'))
5448 t.append(_('1 or more incoming'))
5449
5449
5450 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5450 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5451 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5451 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5452 if source != dest:
5452 if source != dest:
5453 other = hg.peer(repo, {}, dest)
5453 other = hg.peer(repo, {}, dest)
5454 commoninc = None
5454 commoninc = None
5455 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5455 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5456 repo.ui.pushbuffer()
5456 repo.ui.pushbuffer()
5457 outgoing = discovery.findcommonoutgoing(repo, other,
5457 outgoing = discovery.findcommonoutgoing(repo, other,
5458 commoninc=commoninc)
5458 commoninc=commoninc)
5459 repo.ui.popbuffer()
5459 repo.ui.popbuffer()
5460 o = outgoing.missing
5460 o = outgoing.missing
5461 if o:
5461 if o:
5462 t.append(_('%d outgoing') % len(o))
5462 t.append(_('%d outgoing') % len(o))
5463 if 'bookmarks' in other.listkeys('namespaces'):
5463 if 'bookmarks' in other.listkeys('namespaces'):
5464 lmarks = repo.listkeys('bookmarks')
5464 lmarks = repo.listkeys('bookmarks')
5465 rmarks = other.listkeys('bookmarks')
5465 rmarks = other.listkeys('bookmarks')
5466 diff = set(rmarks) - set(lmarks)
5466 diff = set(rmarks) - set(lmarks)
5467 if len(diff) > 0:
5467 if len(diff) > 0:
5468 t.append(_('%d incoming bookmarks') % len(diff))
5468 t.append(_('%d incoming bookmarks') % len(diff))
5469 diff = set(lmarks) - set(rmarks)
5469 diff = set(lmarks) - set(rmarks)
5470 if len(diff) > 0:
5470 if len(diff) > 0:
5471 t.append(_('%d outgoing bookmarks') % len(diff))
5471 t.append(_('%d outgoing bookmarks') % len(diff))
5472
5472
5473 if t:
5473 if t:
5474 ui.write(_('remote: %s\n') % (', '.join(t)))
5474 ui.write(_('remote: %s\n') % (', '.join(t)))
5475 else:
5475 else:
5476 ui.status(_('remote: (synced)\n'))
5476 ui.status(_('remote: (synced)\n'))
5477
5477
5478 @command('tag',
5478 @command('tag',
5479 [('f', 'force', None, _('force tag')),
5479 [('f', 'force', None, _('force tag')),
5480 ('l', 'local', None, _('make the tag local')),
5480 ('l', 'local', None, _('make the tag local')),
5481 ('r', 'rev', '', _('revision to tag'), _('REV')),
5481 ('r', 'rev', '', _('revision to tag'), _('REV')),
5482 ('', 'remove', None, _('remove a tag')),
5482 ('', 'remove', None, _('remove a tag')),
5483 # -l/--local is already there, commitopts cannot be used
5483 # -l/--local is already there, commitopts cannot be used
5484 ('e', 'edit', None, _('edit commit message')),
5484 ('e', 'edit', None, _('edit commit message')),
5485 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5485 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5486 ] + commitopts2,
5486 ] + commitopts2,
5487 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5487 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5488 def tag(ui, repo, name1, *names, **opts):
5488 def tag(ui, repo, name1, *names, **opts):
5489 """add one or more tags for the current or given revision
5489 """add one or more tags for the current or given revision
5490
5490
5491 Name a particular revision using <name>.
5491 Name a particular revision using <name>.
5492
5492
5493 Tags are used to name particular revisions of the repository and are
5493 Tags are used to name particular revisions of the repository and are
5494 very useful to compare different revisions, to go back to significant
5494 very useful to compare different revisions, to go back to significant
5495 earlier versions or to mark branch points as releases, etc. Changing
5495 earlier versions or to mark branch points as releases, etc. Changing
5496 an existing tag is normally disallowed; use -f/--force to override.
5496 an existing tag is normally disallowed; use -f/--force to override.
5497
5497
5498 If no revision is given, the parent of the working directory is
5498 If no revision is given, the parent of the working directory is
5499 used, or tip if no revision is checked out.
5499 used, or tip if no revision is checked out.
5500
5500
5501 To facilitate version control, distribution, and merging of tags,
5501 To facilitate version control, distribution, and merging of tags,
5502 they are stored as a file named ".hgtags" which is managed similarly
5502 they are stored as a file named ".hgtags" which is managed similarly
5503 to other project files and can be hand-edited if necessary. This
5503 to other project files and can be hand-edited if necessary. This
5504 also means that tagging creates a new commit. The file
5504 also means that tagging creates a new commit. The file
5505 ".hg/localtags" is used for local tags (not shared among
5505 ".hg/localtags" is used for local tags (not shared among
5506 repositories).
5506 repositories).
5507
5507
5508 Tag commits are usually made at the head of a branch. If the parent
5508 Tag commits are usually made at the head of a branch. If the parent
5509 of the working directory is not a branch head, :hg:`tag` aborts; use
5509 of the working directory is not a branch head, :hg:`tag` aborts; use
5510 -f/--force to force the tag commit to be based on a non-head
5510 -f/--force to force the tag commit to be based on a non-head
5511 changeset.
5511 changeset.
5512
5512
5513 See :hg:`help dates` for a list of formats valid for -d/--date.
5513 See :hg:`help dates` for a list of formats valid for -d/--date.
5514
5514
5515 Since tag names have priority over branch names during revision
5515 Since tag names have priority over branch names during revision
5516 lookup, using an existing branch name as a tag name is discouraged.
5516 lookup, using an existing branch name as a tag name is discouraged.
5517
5517
5518 Returns 0 on success.
5518 Returns 0 on success.
5519 """
5519 """
5520 wlock = lock = None
5520 wlock = lock = None
5521 try:
5521 try:
5522 wlock = repo.wlock()
5522 wlock = repo.wlock()
5523 lock = repo.lock()
5523 lock = repo.lock()
5524 rev_ = "."
5524 rev_ = "."
5525 names = [t.strip() for t in (name1,) + names]
5525 names = [t.strip() for t in (name1,) + names]
5526 if len(names) != len(set(names)):
5526 if len(names) != len(set(names)):
5527 raise util.Abort(_('tag names must be unique'))
5527 raise util.Abort(_('tag names must be unique'))
5528 for n in names:
5528 for n in names:
5529 if n in ['tip', '.', 'null']:
5529 if n in ['tip', '.', 'null']:
5530 raise util.Abort(_("the name '%s' is reserved") % n)
5530 raise util.Abort(_("the name '%s' is reserved") % n)
5531 if not n:
5531 if not n:
5532 raise util.Abort(_('tag names cannot consist entirely of '
5532 raise util.Abort(_('tag names cannot consist entirely of '
5533 'whitespace'))
5533 'whitespace'))
5534 if opts.get('rev') and opts.get('remove'):
5534 if opts.get('rev') and opts.get('remove'):
5535 raise util.Abort(_("--rev and --remove are incompatible"))
5535 raise util.Abort(_("--rev and --remove are incompatible"))
5536 if opts.get('rev'):
5536 if opts.get('rev'):
5537 rev_ = opts['rev']
5537 rev_ = opts['rev']
5538 message = opts.get('message')
5538 message = opts.get('message')
5539 if opts.get('remove'):
5539 if opts.get('remove'):
5540 expectedtype = opts.get('local') and 'local' or 'global'
5540 expectedtype = opts.get('local') and 'local' or 'global'
5541 for n in names:
5541 for n in names:
5542 if not repo.tagtype(n):
5542 if not repo.tagtype(n):
5543 raise util.Abort(_("tag '%s' does not exist") % n)
5543 raise util.Abort(_("tag '%s' does not exist") % n)
5544 if repo.tagtype(n) != expectedtype:
5544 if repo.tagtype(n) != expectedtype:
5545 if expectedtype == 'global':
5545 if expectedtype == 'global':
5546 raise util.Abort(_("tag '%s' is not a global tag") % n)
5546 raise util.Abort(_("tag '%s' is not a global tag") % n)
5547 else:
5547 else:
5548 raise util.Abort(_("tag '%s' is not a local tag") % n)
5548 raise util.Abort(_("tag '%s' is not a local tag") % n)
5549 rev_ = nullid
5549 rev_ = nullid
5550 if not message:
5550 if not message:
5551 # we don't translate commit messages
5551 # we don't translate commit messages
5552 message = 'Removed tag %s' % ', '.join(names)
5552 message = 'Removed tag %s' % ', '.join(names)
5553 elif not opts.get('force'):
5553 elif not opts.get('force'):
5554 for n in names:
5554 for n in names:
5555 if n in repo.tags():
5555 if n in repo.tags():
5556 raise util.Abort(_("tag '%s' already exists "
5556 raise util.Abort(_("tag '%s' already exists "
5557 "(use -f to force)") % n)
5557 "(use -f to force)") % n)
5558 if not opts.get('local'):
5558 if not opts.get('local'):
5559 p1, p2 = repo.dirstate.parents()
5559 p1, p2 = repo.dirstate.parents()
5560 if p2 != nullid:
5560 if p2 != nullid:
5561 raise util.Abort(_('uncommitted merge'))
5561 raise util.Abort(_('uncommitted merge'))
5562 bheads = repo.branchheads()
5562 bheads = repo.branchheads()
5563 if not opts.get('force') and bheads and p1 not in bheads:
5563 if not opts.get('force') and bheads and p1 not in bheads:
5564 raise util.Abort(_('not at a branch head (use -f to force)'))
5564 raise util.Abort(_('not at a branch head (use -f to force)'))
5565 r = scmutil.revsingle(repo, rev_).node()
5565 r = scmutil.revsingle(repo, rev_).node()
5566
5566
5567 if not message:
5567 if not message:
5568 # we don't translate commit messages
5568 # we don't translate commit messages
5569 message = ('Added tag %s for changeset %s' %
5569 message = ('Added tag %s for changeset %s' %
5570 (', '.join(names), short(r)))
5570 (', '.join(names), short(r)))
5571
5571
5572 date = opts.get('date')
5572 date = opts.get('date')
5573 if date:
5573 if date:
5574 date = util.parsedate(date)
5574 date = util.parsedate(date)
5575
5575
5576 if opts.get('edit'):
5576 if opts.get('edit'):
5577 message = ui.edit(message, ui.username())
5577 message = ui.edit(message, ui.username())
5578
5578
5579 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5579 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5580 finally:
5580 finally:
5581 release(lock, wlock)
5581 release(lock, wlock)
5582
5582
5583 @command('tags', [], '')
5583 @command('tags', [], '')
5584 def tags(ui, repo):
5584 def tags(ui, repo):
5585 """list repository tags
5585 """list repository tags
5586
5586
5587 This lists both regular and local tags. When the -v/--verbose
5587 This lists both regular and local tags. When the -v/--verbose
5588 switch is used, a third column "local" is printed for local tags.
5588 switch is used, a third column "local" is printed for local tags.
5589
5589
5590 Returns 0 on success.
5590 Returns 0 on success.
5591 """
5591 """
5592
5592
5593 hexfunc = ui.debugflag and hex or short
5593 hexfunc = ui.debugflag and hex or short
5594 tagtype = ""
5594 tagtype = ""
5595
5595
5596 for t, n in reversed(repo.tagslist()):
5596 for t, n in reversed(repo.tagslist()):
5597 if ui.quiet:
5597 if ui.quiet:
5598 ui.write("%s\n" % t, label='tags.normal')
5598 ui.write("%s\n" % t, label='tags.normal')
5599 continue
5599 continue
5600
5600
5601 hn = hexfunc(n)
5601 hn = hexfunc(n)
5602 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5602 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5603 rev = ui.label(r, 'log.changeset')
5603 rev = ui.label(r, 'log.changeset')
5604 spaces = " " * (30 - encoding.colwidth(t))
5604 spaces = " " * (30 - encoding.colwidth(t))
5605
5605
5606 tag = ui.label(t, 'tags.normal')
5606 tag = ui.label(t, 'tags.normal')
5607 if ui.verbose:
5607 if ui.verbose:
5608 if repo.tagtype(t) == 'local':
5608 if repo.tagtype(t) == 'local':
5609 tagtype = " local"
5609 tagtype = " local"
5610 tag = ui.label(t, 'tags.local')
5610 tag = ui.label(t, 'tags.local')
5611 else:
5611 else:
5612 tagtype = ""
5612 tagtype = ""
5613 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5613 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5614
5614
5615 @command('tip',
5615 @command('tip',
5616 [('p', 'patch', None, _('show patch')),
5616 [('p', 'patch', None, _('show patch')),
5617 ('g', 'git', None, _('use git extended diff format')),
5617 ('g', 'git', None, _('use git extended diff format')),
5618 ] + templateopts,
5618 ] + templateopts,
5619 _('[-p] [-g]'))
5619 _('[-p] [-g]'))
5620 def tip(ui, repo, **opts):
5620 def tip(ui, repo, **opts):
5621 """show the tip revision
5621 """show the tip revision
5622
5622
5623 The tip revision (usually just called the tip) is the changeset
5623 The tip revision (usually just called the tip) is the changeset
5624 most recently added to the repository (and therefore the most
5624 most recently added to the repository (and therefore the most
5625 recently changed head).
5625 recently changed head).
5626
5626
5627 If you have just made a commit, that commit will be the tip. If
5627 If you have just made a commit, that commit will be the tip. If
5628 you have just pulled changes from another repository, the tip of
5628 you have just pulled changes from another repository, the tip of
5629 that repository becomes the current tip. The "tip" tag is special
5629 that repository becomes the current tip. The "tip" tag is special
5630 and cannot be renamed or assigned to a different changeset.
5630 and cannot be renamed or assigned to a different changeset.
5631
5631
5632 Returns 0 on success.
5632 Returns 0 on success.
5633 """
5633 """
5634 displayer = cmdutil.show_changeset(ui, repo, opts)
5634 displayer = cmdutil.show_changeset(ui, repo, opts)
5635 displayer.show(repo[len(repo) - 1])
5635 displayer.show(repo[len(repo) - 1])
5636 displayer.close()
5636 displayer.close()
5637
5637
5638 @command('unbundle',
5638 @command('unbundle',
5639 [('u', 'update', None,
5639 [('u', 'update', None,
5640 _('update to new branch head if changesets were unbundled'))],
5640 _('update to new branch head if changesets were unbundled'))],
5641 _('[-u] FILE...'))
5641 _('[-u] FILE...'))
5642 def unbundle(ui, repo, fname1, *fnames, **opts):
5642 def unbundle(ui, repo, fname1, *fnames, **opts):
5643 """apply one or more changegroup files
5643 """apply one or more changegroup files
5644
5644
5645 Apply one or more compressed changegroup files generated by the
5645 Apply one or more compressed changegroup files generated by the
5646 bundle command.
5646 bundle command.
5647
5647
5648 Returns 0 on success, 1 if an update has unresolved files.
5648 Returns 0 on success, 1 if an update has unresolved files.
5649 """
5649 """
5650 fnames = (fname1,) + fnames
5650 fnames = (fname1,) + fnames
5651
5651
5652 lock = repo.lock()
5652 lock = repo.lock()
5653 wc = repo['.']
5653 wc = repo['.']
5654 try:
5654 try:
5655 for fname in fnames:
5655 for fname in fnames:
5656 f = url.open(ui, fname)
5656 f = url.open(ui, fname)
5657 gen = changegroup.readbundle(f, fname)
5657 gen = changegroup.readbundle(f, fname)
5658 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5658 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5659 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5659 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5660 finally:
5660 finally:
5661 lock.release()
5661 lock.release()
5662 return postincoming(ui, repo, modheads, opts.get('update'), None)
5662 return postincoming(ui, repo, modheads, opts.get('update'), None)
5663
5663
5664 @command('^update|up|checkout|co',
5664 @command('^update|up|checkout|co',
5665 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5665 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5666 ('c', 'check', None,
5666 ('c', 'check', None,
5667 _('update across branches if no uncommitted changes')),
5667 _('update across branches if no uncommitted changes')),
5668 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5668 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5669 ('r', 'rev', '', _('revision'), _('REV'))],
5669 ('r', 'rev', '', _('revision'), _('REV'))],
5670 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5670 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5671 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5671 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5672 """update working directory (or switch revisions)
5672 """update working directory (or switch revisions)
5673
5673
5674 Update the repository's working directory to the specified
5674 Update the repository's working directory to the specified
5675 changeset. If no changeset is specified, update to the tip of the
5675 changeset. If no changeset is specified, update to the tip of the
5676 current named branch and move the current bookmark (see :hg:`help
5676 current named branch and move the current bookmark (see :hg:`help
5677 bookmarks`).
5677 bookmarks`).
5678
5678
5679 If the changeset is not a descendant of the working directory's
5679 If the changeset is not a descendant of the working directory's
5680 parent, the update is aborted. With the -c/--check option, the
5680 parent, the update is aborted. With the -c/--check option, the
5681 working directory is checked for uncommitted changes; if none are
5681 working directory is checked for uncommitted changes; if none are
5682 found, the working directory is updated to the specified
5682 found, the working directory is updated to the specified
5683 changeset.
5683 changeset.
5684
5684
5685 Update sets the working directory's parent revison to the specified
5685 Update sets the working directory's parent revison to the specified
5686 changeset (see :hg:`help parents`).
5686 changeset (see :hg:`help parents`).
5687
5687
5688 The following rules apply when the working directory contains
5688 The following rules apply when the working directory contains
5689 uncommitted changes:
5689 uncommitted changes:
5690
5690
5691 1. If neither -c/--check nor -C/--clean is specified, and if
5691 1. If neither -c/--check nor -C/--clean is specified, and if
5692 the requested changeset is an ancestor or descendant of
5692 the requested changeset is an ancestor or descendant of
5693 the working directory's parent, the uncommitted changes
5693 the working directory's parent, the uncommitted changes
5694 are merged into the requested changeset and the merged
5694 are merged into the requested changeset and the merged
5695 result is left uncommitted. If the requested changeset is
5695 result is left uncommitted. If the requested changeset is
5696 not an ancestor or descendant (that is, it is on another
5696 not an ancestor or descendant (that is, it is on another
5697 branch), the update is aborted and the uncommitted changes
5697 branch), the update is aborted and the uncommitted changes
5698 are preserved.
5698 are preserved.
5699
5699
5700 2. With the -c/--check option, the update is aborted and the
5700 2. With the -c/--check option, the update is aborted and the
5701 uncommitted changes are preserved.
5701 uncommitted changes are preserved.
5702
5702
5703 3. With the -C/--clean option, uncommitted changes are discarded and
5703 3. With the -C/--clean option, uncommitted changes are discarded and
5704 the working directory is updated to the requested changeset.
5704 the working directory is updated to the requested changeset.
5705
5705
5706 Use null as the changeset to remove the working directory (like
5706 Use null as the changeset to remove the working directory (like
5707 :hg:`clone -U`).
5707 :hg:`clone -U`).
5708
5708
5709 If you want to revert just one file to an older revision, use
5709 If you want to revert just one file to an older revision, use
5710 :hg:`revert [-r REV] NAME`.
5710 :hg:`revert [-r REV] NAME`.
5711
5711
5712 See :hg:`help dates` for a list of formats valid for -d/--date.
5712 See :hg:`help dates` for a list of formats valid for -d/--date.
5713
5713
5714 Returns 0 on success, 1 if there are unresolved files.
5714 Returns 0 on success, 1 if there are unresolved files.
5715 """
5715 """
5716 if rev and node:
5716 if rev and node:
5717 raise util.Abort(_("please specify just one revision"))
5717 raise util.Abort(_("please specify just one revision"))
5718
5718
5719 if rev is None or rev == '':
5719 if rev is None or rev == '':
5720 rev = node
5720 rev = node
5721
5721
5722 # with no argument, we also move the current bookmark, if any
5722 # with no argument, we also move the current bookmark, if any
5723 movemarkfrom = None
5723 movemarkfrom = None
5724 if rev is None or node == '':
5724 if rev is None or node == '':
5725 movemarkfrom = repo['.'].node()
5725 movemarkfrom = repo['.'].node()
5726
5726
5727 # if we defined a bookmark, we have to remember the original bookmark name
5727 # if we defined a bookmark, we have to remember the original bookmark name
5728 brev = rev
5728 brev = rev
5729 rev = scmutil.revsingle(repo, rev, rev).rev()
5729 rev = scmutil.revsingle(repo, rev, rev).rev()
5730
5730
5731 if check and clean:
5731 if check and clean:
5732 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5732 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5733
5733
5734 if date:
5734 if date:
5735 if rev is not None:
5735 if rev is not None:
5736 raise util.Abort(_("you can't specify a revision and a date"))
5736 raise util.Abort(_("you can't specify a revision and a date"))
5737 rev = cmdutil.finddate(ui, repo, date)
5737 rev = cmdutil.finddate(ui, repo, date)
5738
5738
5739 if check:
5739 if check:
5740 # we could use dirty() but we can ignore merge and branch trivia
5740 # we could use dirty() but we can ignore merge and branch trivia
5741 c = repo[None]
5741 c = repo[None]
5742 if c.modified() or c.added() or c.removed():
5742 if c.modified() or c.added() or c.removed():
5743 raise util.Abort(_("uncommitted local changes"))
5743 raise util.Abort(_("uncommitted local changes"))
5744 if not rev:
5744 if not rev:
5745 rev = repo[repo[None].branch()].rev()
5745 rev = repo[repo[None].branch()].rev()
5746 mergemod._checkunknown(repo, repo[None], repo[rev])
5746 mergemod._checkunknown(repo, repo[None], repo[rev])
5747
5747
5748 if clean:
5748 if clean:
5749 ret = hg.clean(repo, rev)
5749 ret = hg.clean(repo, rev)
5750 else:
5750 else:
5751 ret = hg.update(repo, rev)
5751 ret = hg.update(repo, rev)
5752
5752
5753 if not ret and movemarkfrom:
5753 if not ret and movemarkfrom:
5754 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5754 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5755 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5755 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5756 elif brev in repo._bookmarks:
5756 elif brev in repo._bookmarks:
5757 bookmarks.setcurrent(repo, brev)
5757 bookmarks.setcurrent(repo, brev)
5758 elif brev:
5758 elif brev:
5759 bookmarks.unsetcurrent(repo)
5759 bookmarks.unsetcurrent(repo)
5760
5760
5761 return ret
5761 return ret
5762
5762
5763 @command('verify', [])
5763 @command('verify', [])
5764 def verify(ui, repo):
5764 def verify(ui, repo):
5765 """verify the integrity of the repository
5765 """verify the integrity of the repository
5766
5766
5767 Verify the integrity of the current repository.
5767 Verify the integrity of the current repository.
5768
5768
5769 This will perform an extensive check of the repository's
5769 This will perform an extensive check of the repository's
5770 integrity, validating the hashes and checksums of each entry in
5770 integrity, validating the hashes and checksums of each entry in
5771 the changelog, manifest, and tracked files, as well as the
5771 the changelog, manifest, and tracked files, as well as the
5772 integrity of their crosslinks and indices.
5772 integrity of their crosslinks and indices.
5773
5773
5774 Returns 0 on success, 1 if errors are encountered.
5774 Returns 0 on success, 1 if errors are encountered.
5775 """
5775 """
5776 return hg.verify(repo)
5776 return hg.verify(repo)
5777
5777
5778 @command('version', [])
5778 @command('version', [])
5779 def version_(ui):
5779 def version_(ui):
5780 """output version and copyright information"""
5780 """output version and copyright information"""
5781 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5781 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5782 % util.version())
5782 % util.version())
5783 ui.status(_(
5783 ui.status(_(
5784 "(see http://mercurial.selenic.com for more information)\n"
5784 "(see http://mercurial.selenic.com for more information)\n"
5785 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5785 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5786 "This is free software; see the source for copying conditions. "
5786 "This is free software; see the source for copying conditions. "
5787 "There is NO\nwarranty; "
5787 "There is NO\nwarranty; "
5788 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5788 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5789 ))
5789 ))
5790
5790
5791 norepo = ("clone init version help debugcommands debugcomplete"
5791 norepo = ("clone init version help debugcommands debugcomplete"
5792 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5792 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5793 " debugknown debuggetbundle debugbundle")
5793 " debugknown debuggetbundle debugbundle")
5794 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5794 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5795 " debugdata debugindex debugindexdot debugrevlog")
5795 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,659 +1,656 b''
1 $ check_code="$TESTDIR"/../contrib/check-code.py
1 $ check_code="$TESTDIR"/../contrib/check-code.py
2 $ cd "$TESTDIR"/..
2 $ cd "$TESTDIR"/..
3 $ if ! hg identify -q > /dev/null; then
3 $ if ! hg identify -q > /dev/null; then
4 > echo "skipped: not a Mercurial working dir" >&2
4 > echo "skipped: not a Mercurial working dir" >&2
5 > exit 80
5 > exit 80
6 > fi
6 > fi
7 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
7 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
8
8
9 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
9 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
10 contrib/check-code.py:0:
10 contrib/check-code.py:0:
11 > # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
11 > # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
12 warning: line over 80 characters
12 warning: line over 80 characters
13 contrib/perf.py:0:
13 contrib/perf.py:0:
14 > except:
14 > except:
15 warning: naked except clause
15 warning: naked except clause
16 contrib/perf.py:0:
16 contrib/perf.py:0:
17 > #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False))))
17 > #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False))))
18 warning: line over 80 characters
18 warning: line over 80 characters
19 contrib/perf.py:0:
19 contrib/perf.py:0:
20 > except:
20 > except:
21 warning: naked except clause
21 warning: naked except clause
22 contrib/setup3k.py:0:
22 contrib/setup3k.py:0:
23 > except:
23 > except:
24 warning: naked except clause
24 warning: naked except clause
25 contrib/setup3k.py:0:
25 contrib/setup3k.py:0:
26 > except:
26 > except:
27 warning: naked except clause
27 warning: naked except clause
28 contrib/setup3k.py:0:
28 contrib/setup3k.py:0:
29 > except:
29 > except:
30 warning: naked except clause
30 warning: naked except clause
31 warning: naked except clause
31 warning: naked except clause
32 warning: naked except clause
32 warning: naked except clause
33 contrib/shrink-revlog.py:0:
33 contrib/shrink-revlog.py:0:
34 > '(You can delete those files when you are satisfied that your\n'
34 > '(You can delete those files when you are satisfied that your\n'
35 warning: line over 80 characters
35 warning: line over 80 characters
36 contrib/shrink-revlog.py:0:
36 contrib/shrink-revlog.py:0:
37 > ('', 'sort', 'reversepostorder', 'name of sort algorithm to use'),
37 > ('', 'sort', 'reversepostorder', 'name of sort algorithm to use'),
38 warning: line over 80 characters
38 warning: line over 80 characters
39 contrib/shrink-revlog.py:0:
39 contrib/shrink-revlog.py:0:
40 > [('', 'revlog', '', _('index (.i) file of the revlog to shrink')),
40 > [('', 'revlog', '', _('index (.i) file of the revlog to shrink')),
41 warning: line over 80 characters
41 warning: line over 80 characters
42 contrib/shrink-revlog.py:0:
42 contrib/shrink-revlog.py:0:
43 > except:
43 > except:
44 warning: naked except clause
44 warning: naked except clause
45 doc/gendoc.py:0:
45 doc/gendoc.py:0:
46 > "together with Mercurial. Help for other extensions is available "
46 > "together with Mercurial. Help for other extensions is available "
47 warning: line over 80 characters
47 warning: line over 80 characters
48 hgext/bugzilla.py:0:
48 hgext/bugzilla.py:0:
49 > raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
49 > raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
50 warning: line over 80 characters
50 warning: line over 80 characters
51 hgext/bugzilla.py:0:
51 hgext/bugzilla.py:0:
52 > bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
52 > bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
53 warning: line over 80 characters
53 warning: line over 80 characters
54 hgext/convert/__init__.py:0:
54 hgext/convert/__init__.py:0:
55 > ('', 'ancestors', '', _('show current changeset in ancestor branches')),
55 > ('', 'ancestors', '', _('show current changeset in ancestor branches')),
56 warning: line over 80 characters
56 warning: line over 80 characters
57 hgext/convert/bzr.py:0:
57 hgext/convert/bzr.py:0:
58 > except:
58 > except:
59 warning: naked except clause
59 warning: naked except clause
60 hgext/convert/common.py:0:
60 hgext/convert/common.py:0:
61 > except:
61 > except:
62 warning: naked except clause
62 warning: naked except clause
63 hgext/convert/common.py:0:
63 hgext/convert/common.py:0:
64 > except:
64 > except:
65 warning: naked except clause
65 warning: naked except clause
66 warning: naked except clause
66 warning: naked except clause
67 hgext/convert/convcmd.py:0:
67 hgext/convert/convcmd.py:0:
68 > except:
68 > except:
69 warning: naked except clause
69 warning: naked except clause
70 hgext/convert/cvs.py:0:
70 hgext/convert/cvs.py:0:
71 > # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
71 > # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
72 warning: line over 80 characters
72 warning: line over 80 characters
73 hgext/convert/cvsps.py:0:
73 hgext/convert/cvsps.py:0:
74 > assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
74 > assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
75 warning: line over 80 characters
75 warning: line over 80 characters
76 hgext/convert/cvsps.py:0:
76 hgext/convert/cvsps.py:0:
77 > ui.write('Ancestors: %s\n' % (','.join(r)))
77 > ui.write('Ancestors: %s\n' % (','.join(r)))
78 warning: unwrapped ui message
78 warning: unwrapped ui message
79 hgext/convert/cvsps.py:0:
79 hgext/convert/cvsps.py:0:
80 > ui.write('Parent: %d\n' % cs.parents[0].id)
80 > ui.write('Parent: %d\n' % cs.parents[0].id)
81 warning: unwrapped ui message
81 warning: unwrapped ui message
82 hgext/convert/cvsps.py:0:
82 hgext/convert/cvsps.py:0:
83 > ui.write('Parents: %s\n' %
83 > ui.write('Parents: %s\n' %
84 warning: unwrapped ui message
84 warning: unwrapped ui message
85 hgext/convert/cvsps.py:0:
85 hgext/convert/cvsps.py:0:
86 > except:
86 > except:
87 warning: naked except clause
87 warning: naked except clause
88 hgext/convert/cvsps.py:0:
88 hgext/convert/cvsps.py:0:
89 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
89 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
90 warning: unwrapped ui message
90 warning: unwrapped ui message
91 hgext/convert/cvsps.py:0:
91 hgext/convert/cvsps.py:0:
92 > ui.write('Author: %s\n' % cs.author)
92 > ui.write('Author: %s\n' % cs.author)
93 warning: unwrapped ui message
93 warning: unwrapped ui message
94 hgext/convert/cvsps.py:0:
94 hgext/convert/cvsps.py:0:
95 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
95 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
96 warning: unwrapped ui message
96 warning: unwrapped ui message
97 hgext/convert/cvsps.py:0:
97 hgext/convert/cvsps.py:0:
98 > ui.write('Date: %s\n' % util.datestr(cs.date,
98 > ui.write('Date: %s\n' % util.datestr(cs.date,
99 warning: unwrapped ui message
99 warning: unwrapped ui message
100 hgext/convert/cvsps.py:0:
100 hgext/convert/cvsps.py:0:
101 > ui.write('Log:\n')
101 > ui.write('Log:\n')
102 warning: unwrapped ui message
102 warning: unwrapped ui message
103 hgext/convert/cvsps.py:0:
103 hgext/convert/cvsps.py:0:
104 > ui.write('Members: \n')
104 > ui.write('Members: \n')
105 warning: unwrapped ui message
105 warning: unwrapped ui message
106 hgext/convert/cvsps.py:0:
106 hgext/convert/cvsps.py:0:
107 > ui.write('PatchSet %d \n' % cs.id)
107 > ui.write('PatchSet %d \n' % cs.id)
108 warning: unwrapped ui message
108 warning: unwrapped ui message
109 hgext/convert/cvsps.py:0:
109 hgext/convert/cvsps.py:0:
110 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
110 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
111 warning: unwrapped ui message
111 warning: unwrapped ui message
112 hgext/convert/git.py:0:
112 hgext/convert/git.py:0:
113 > except:
113 > except:
114 warning: naked except clause
114 warning: naked except clause
115 hgext/convert/git.py:0:
115 hgext/convert/git.py:0:
116 > fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
116 > fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
117 warning: line over 80 characters
117 warning: line over 80 characters
118 hgext/convert/hg.py:0:
118 hgext/convert/hg.py:0:
119 > # detect missing revlogs and abort on errors or populate self.ignored
119 > # detect missing revlogs and abort on errors or populate self.ignored
120 warning: line over 80 characters
120 warning: line over 80 characters
121 hgext/convert/hg.py:0:
121 hgext/convert/hg.py:0:
122 > except:
122 > except:
123 warning: naked except clause
123 warning: naked except clause
124 warning: naked except clause
124 warning: naked except clause
125 hgext/convert/hg.py:0:
125 hgext/convert/hg.py:0:
126 > except:
126 > except:
127 warning: naked except clause
127 warning: naked except clause
128 hgext/convert/monotone.py:0:
128 hgext/convert/monotone.py:0:
129 > except:
129 > except:
130 warning: naked except clause
130 warning: naked except clause
131 hgext/convert/monotone.py:0:
131 hgext/convert/monotone.py:0:
132 > except:
132 > except:
133 warning: naked except clause
133 warning: naked except clause
134 hgext/convert/subversion.py:0:
134 hgext/convert/subversion.py:0:
135 > raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
135 > raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
136 warning: line over 80 characters
136 warning: line over 80 characters
137 hgext/convert/subversion.py:0:
137 hgext/convert/subversion.py:0:
138 > except:
138 > except:
139 warning: naked except clause
139 warning: naked except clause
140 hgext/convert/subversion.py:0:
140 hgext/convert/subversion.py:0:
141 > args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
141 > args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
142 warning: line over 80 characters
142 warning: line over 80 characters
143 hgext/convert/subversion.py:0:
143 hgext/convert/subversion.py:0:
144 > self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
144 > self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
145 warning: line over 80 characters
145 warning: line over 80 characters
146 hgext/convert/subversion.py:0:
146 hgext/convert/subversion.py:0:
147 > except:
147 > except:
148 warning: naked except clause
148 warning: naked except clause
149 hgext/convert/subversion.py:0:
149 hgext/convert/subversion.py:0:
150 > def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
150 > def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
151 warning: line over 80 characters
151 warning: line over 80 characters
152 hgext/eol.py:0:
152 hgext/eol.py:0:
153 > if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
153 > if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
154 warning: line over 80 characters
154 warning: line over 80 characters
155 warning: line over 80 characters
155 warning: line over 80 characters
156 hgext/gpg.py:0:
156 hgext/gpg.py:0:
157 > except:
157 > except:
158 warning: naked except clause
158 warning: naked except clause
159 hgext/hgcia.py:0:
159 hgext/hgcia.py:0:
160 > except:
160 > except:
161 warning: naked except clause
161 warning: naked except clause
162 hgext/hgk.py:0:
162 hgext/hgk.py:0:
163 > ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
163 > ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
164 warning: line over 80 characters
164 warning: line over 80 characters
165 hgext/hgk.py:0:
165 hgext/hgk.py:0:
166 > ui.write("parent %s\n" % p)
166 > ui.write("parent %s\n" % p)
167 warning: unwrapped ui message
167 warning: unwrapped ui message
168 hgext/hgk.py:0:
168 hgext/hgk.py:0:
169 > ui.write('k=%s\nv=%s\n' % (name, value))
169 > ui.write('k=%s\nv=%s\n' % (name, value))
170 warning: unwrapped ui message
170 warning: unwrapped ui message
171 hgext/hgk.py:0:
171 hgext/hgk.py:0:
172 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
172 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
173 warning: unwrapped ui message
173 warning: unwrapped ui message
174 hgext/hgk.py:0:
174 hgext/hgk.py:0:
175 > ui.write("branch %s\n\n" % ctx.branch())
175 > ui.write("branch %s\n\n" % ctx.branch())
176 warning: unwrapped ui message
176 warning: unwrapped ui message
177 hgext/hgk.py:0:
177 hgext/hgk.py:0:
178 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
178 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
179 warning: unwrapped ui message
179 warning: unwrapped ui message
180 hgext/hgk.py:0:
180 hgext/hgk.py:0:
181 > ui.write("revision %d\n" % ctx.rev())
181 > ui.write("revision %d\n" % ctx.rev())
182 warning: unwrapped ui message
182 warning: unwrapped ui message
183 hgext/hgk.py:0:
183 hgext/hgk.py:0:
184 > ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
184 > ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
185 warning: line over 80 characters
185 warning: line over 80 characters
186 warning: unwrapped ui message
186 warning: unwrapped ui message
187 hgext/highlight/__init__.py:0:
187 hgext/highlight/__init__.py:0:
188 > extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
188 > extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
189 warning: line over 80 characters
189 warning: line over 80 characters
190 hgext/highlight/__init__.py:0:
190 hgext/highlight/__init__.py:0:
191 > return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
191 > return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
192 warning: line over 80 characters
192 warning: line over 80 characters
193 hgext/inotify/__init__.py:0:
193 hgext/inotify/__init__.py:0:
194 > if self._inotifyon and not ignored and not subrepos and not self._dirty:
194 > if self._inotifyon and not ignored and not subrepos and not self._dirty:
195 warning: line over 80 characters
195 warning: line over 80 characters
196 hgext/inotify/server.py:0:
196 hgext/inotify/server.py:0:
197 > except:
197 > except:
198 warning: naked except clause
198 warning: naked except clause
199 hgext/inotify/server.py:0:
199 hgext/inotify/server.py:0:
200 > except:
200 > except:
201 warning: naked except clause
201 warning: naked except clause
202 hgext/keyword.py:0:
202 hgext/keyword.py:0:
203 > ui.note("hg ci -m '%s'\n" % msg)
203 > ui.note("hg ci -m '%s'\n" % msg)
204 warning: unwrapped ui message
204 warning: unwrapped ui message
205 hgext/largefiles/overrides.py:0:
205 hgext/largefiles/overrides.py:0:
206 > # When we call orig below it creates the standins but we don't add them
206 > # When we call orig below it creates the standins but we don't add them
207 warning: line over 80 characters
207 warning: line over 80 characters
208 hgext/largefiles/reposetup.py:0:
208 hgext/largefiles/reposetup.py:0:
209 > if os.path.exists(self.wjoin(lfutil.standin(lfile))):
209 > if os.path.exists(self.wjoin(lfutil.standin(lfile))):
210 warning: line over 80 characters
210 warning: line over 80 characters
211 hgext/mq.py:0:
211 hgext/mq.py:0:
212 > raise util.Abort(_("%s does not have a parent recorded" % root))
213 warning: line over 80 characters
214 hgext/mq.py:0:
215 > raise util.Abort(_("cannot push --exact with applied patches"))
212 > raise util.Abort(_("cannot push --exact with applied patches"))
216 warning: line over 80 characters
213 warning: line over 80 characters
217 hgext/mq.py:0:
214 hgext/mq.py:0:
218 > raise util.Abort(_("cannot use --exact and --move together"))
215 > raise util.Abort(_("cannot use --exact and --move together"))
219 warning: line over 80 characters
216 warning: line over 80 characters
220 hgext/mq.py:0:
217 hgext/mq.py:0:
221 > self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
218 > self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
222 warning: line over 80 characters
219 warning: line over 80 characters
223 hgext/mq.py:0:
220 hgext/mq.py:0:
224 > except:
221 > except:
225 warning: naked except clause
222 warning: naked except clause
226 warning: naked except clause
223 warning: naked except clause
227 hgext/mq.py:0:
224 hgext/mq.py:0:
228 > except:
225 > except:
229 warning: naked except clause
226 warning: naked except clause
230 warning: naked except clause
227 warning: naked except clause
231 warning: naked except clause
228 warning: naked except clause
232 warning: naked except clause
229 warning: naked except clause
233 hgext/mq.py:0:
230 hgext/mq.py:0:
234 > raise util.Abort(_('cannot mix -l/--list with options or arguments'))
231 > raise util.Abort(_('cannot mix -l/--list with options or arguments'))
235 warning: line over 80 characters
232 warning: line over 80 characters
236 hgext/mq.py:0:
233 hgext/mq.py:0:
237 > raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
234 > raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
238 warning: line over 80 characters
235 warning: line over 80 characters
239 hgext/mq.py:0:
236 hgext/mq.py:0:
240 > ('', 'move', None, _('reorder patch series and apply only the patch'))],
237 > ('', 'move', None, _('reorder patch series and apply only the patch'))],
241 warning: line over 80 characters
238 warning: line over 80 characters
242 hgext/mq.py:0:
239 hgext/mq.py:0:
243 > ('U', 'noupdate', None, _('do not update the new working directories')),
240 > ('U', 'noupdate', None, _('do not update the new working directories')),
244 warning: line over 80 characters
241 warning: line over 80 characters
245 hgext/mq.py:0:
242 hgext/mq.py:0:
246 > ('e', 'exact', None, _('apply the target patch to its recorded parent')),
243 > ('e', 'exact', None, _('apply the target patch to its recorded parent')),
247 warning: line over 80 characters
244 warning: line over 80 characters
248 hgext/mq.py:0:
245 hgext/mq.py:0:
249 > except:
246 > except:
250 warning: naked except clause
247 warning: naked except clause
251 hgext/mq.py:0:
248 hgext/mq.py:0:
252 > ui.write("mq: %s\n" % ', '.join(m))
249 > ui.write("mq: %s\n" % ', '.join(m))
253 warning: unwrapped ui message
250 warning: unwrapped ui message
254 hgext/mq.py:0:
251 hgext/mq.py:0:
255 > repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
252 > repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
256 warning: line over 80 characters
253 warning: line over 80 characters
257 hgext/notify.py:0:
254 hgext/notify.py:0:
258 > ui.note(_('notify: suppressing notification for merge %d:%s\n') %
255 > ui.note(_('notify: suppressing notification for merge %d:%s\n') %
259 warning: line over 80 characters
256 warning: line over 80 characters
260 hgext/patchbomb.py:0:
257 hgext/patchbomb.py:0:
261 > binnode, seqno=idx, total=total)
258 > binnode, seqno=idx, total=total)
262 warning: line over 80 characters
259 warning: line over 80 characters
263 hgext/patchbomb.py:0:
260 hgext/patchbomb.py:0:
264 > except:
261 > except:
265 warning: naked except clause
262 warning: naked except clause
266 hgext/patchbomb.py:0:
263 hgext/patchbomb.py:0:
267 > ui.write('Subject: %s\n' % subj)
264 > ui.write('Subject: %s\n' % subj)
268 warning: unwrapped ui message
265 warning: unwrapped ui message
269 hgext/patchbomb.py:0:
266 hgext/patchbomb.py:0:
270 > p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
267 > p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
271 warning: line over 80 characters
268 warning: line over 80 characters
272 hgext/patchbomb.py:0:
269 hgext/patchbomb.py:0:
273 > ui.write('From: %s\n' % sender)
270 > ui.write('From: %s\n' % sender)
274 warning: unwrapped ui message
271 warning: unwrapped ui message
275 hgext/record.py:0:
272 hgext/record.py:0:
276 > ignoreblanklines=opts.get('ignore_blank_lines'))
273 > ignoreblanklines=opts.get('ignore_blank_lines'))
277 warning: line over 80 characters
274 warning: line over 80 characters
278 hgext/record.py:0:
275 hgext/record.py:0:
279 > ignorewsamount=opts.get('ignore_space_change'),
276 > ignorewsamount=opts.get('ignore_space_change'),
280 warning: line over 80 characters
277 warning: line over 80 characters
281 hgext/zeroconf/__init__.py:0:
278 hgext/zeroconf/__init__.py:0:
282 > publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
279 > publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
283 warning: line over 80 characters
280 warning: line over 80 characters
284 hgext/zeroconf/__init__.py:0:
281 hgext/zeroconf/__init__.py:0:
285 > except:
282 > except:
286 warning: naked except clause
283 warning: naked except clause
287 warning: naked except clause
284 warning: naked except clause
288 mercurial/bundlerepo.py:0:
285 mercurial/bundlerepo.py:0:
289 > is a bundlerepo for the obtained bundle when the original "other" is remote.
286 > is a bundlerepo for the obtained bundle when the original "other" is remote.
290 warning: line over 80 characters
287 warning: line over 80 characters
291 mercurial/bundlerepo.py:0:
288 mercurial/bundlerepo.py:0:
292 > "local" is a local repo from which to obtain the actual incoming changesets; it
289 > "local" is a local repo from which to obtain the actual incoming changesets; it
293 warning: line over 80 characters
290 warning: line over 80 characters
294 mercurial/bundlerepo.py:0:
291 mercurial/bundlerepo.py:0:
295 > tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
292 > tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
296 warning: line over 80 characters
293 warning: line over 80 characters
297 mercurial/commands.py:0:
294 mercurial/commands.py:0:
298 > " size " + basehdr + " link p1 p2 nodeid\n")
295 > " size " + basehdr + " link p1 p2 nodeid\n")
299 warning: line over 80 characters
296 warning: line over 80 characters
300 mercurial/commands.py:0:
297 mercurial/commands.py:0:
301 > raise util.Abort('cannot use localheads with old style discovery')
298 > raise util.Abort('cannot use localheads with old style discovery')
302 warning: line over 80 characters
299 warning: line over 80 characters
303 mercurial/commands.py:0:
300 mercurial/commands.py:0:
304 > ui.note('branch %s\n' % data)
301 > ui.note('branch %s\n' % data)
305 warning: unwrapped ui message
302 warning: unwrapped ui message
306 mercurial/commands.py:0:
303 mercurial/commands.py:0:
307 > ui.note('node %s\n' % str(data))
304 > ui.note('node %s\n' % str(data))
308 warning: unwrapped ui message
305 warning: unwrapped ui message
309 mercurial/commands.py:0:
306 mercurial/commands.py:0:
310 > ui.note('tag %s\n' % name)
307 > ui.note('tag %s\n' % name)
311 warning: unwrapped ui message
308 warning: unwrapped ui message
312 mercurial/commands.py:0:
309 mercurial/commands.py:0:
313 > ui.write("unpruned common: %s\n" % " ".join([short(n)
310 > ui.write("unpruned common: %s\n" % " ".join([short(n)
314 warning: unwrapped ui message
311 warning: unwrapped ui message
315 mercurial/commands.py:0:
312 mercurial/commands.py:0:
316 > yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
313 > yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
317 warning: line over 80 characters
314 warning: line over 80 characters
318 mercurial/commands.py:0:
315 mercurial/commands.py:0:
319 > yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
316 > yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
320 warning: line over 80 characters
317 warning: line over 80 characters
321 mercurial/commands.py:0:
318 mercurial/commands.py:0:
322 > except:
319 > except:
323 warning: naked except clause
320 warning: naked except clause
324 mercurial/commands.py:0:
321 mercurial/commands.py:0:
325 > ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
322 > ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
326 warning: line over 80 characters
323 warning: line over 80 characters
327 mercurial/commands.py:0:
324 mercurial/commands.py:0:
328 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
325 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
329 warning: unwrapped ui message
326 warning: unwrapped ui message
330 mercurial/commands.py:0:
327 mercurial/commands.py:0:
331 > ui.write("local is subset\n")
328 > ui.write("local is subset\n")
332 warning: unwrapped ui message
329 warning: unwrapped ui message
333 mercurial/commands.py:0:
330 mercurial/commands.py:0:
334 > ui.write("remote is subset\n")
331 > ui.write("remote is subset\n")
335 warning: unwrapped ui message
332 warning: unwrapped ui message
336 mercurial/commands.py:0:
333 mercurial/commands.py:0:
337 > ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
334 > ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
338 warning: line over 80 characters
335 warning: line over 80 characters
339 mercurial/commands.py:0:
336 mercurial/commands.py:0:
340 > ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
337 > ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
341 warning: line over 80 characters
338 warning: line over 80 characters
342 mercurial/commands.py:0:
339 mercurial/commands.py:0:
343 > ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
340 > ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
344 warning: line over 80 characters
341 warning: line over 80 characters
345 mercurial/commands.py:0:
342 mercurial/commands.py:0:
346 > ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
343 > ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
347 warning: line over 80 characters
344 warning: line over 80 characters
348 warning: unwrapped ui message
345 warning: unwrapped ui message
349 mercurial/commands.py:0:
346 mercurial/commands.py:0:
350 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
347 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
351 warning: unwrapped ui message
348 warning: unwrapped ui message
352 mercurial/commands.py:0:
349 mercurial/commands.py:0:
353 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
350 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
354 warning: unwrapped ui message
351 warning: unwrapped ui message
355 mercurial/commands.py:0:
352 mercurial/commands.py:0:
356 > cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
353 > cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
357 warning: line over 80 characters
354 warning: line over 80 characters
358 mercurial/commands.py:0:
355 mercurial/commands.py:0:
359 > except:
356 > except:
360 warning: naked except clause
357 warning: naked except clause
361 mercurial/commands.py:0:
358 mercurial/commands.py:0:
362 > revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
359 > revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
363 warning: line over 80 characters
360 warning: line over 80 characters
364 mercurial/commands.py:0:
361 mercurial/commands.py:0:
365 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
362 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
366 warning: unwrapped ui message
363 warning: unwrapped ui message
367 mercurial/commands.py:0:
364 mercurial/commands.py:0:
368 > ui.write("match: %s\n" % m(d[0]))
365 > ui.write("match: %s\n" % m(d[0]))
369 warning: unwrapped ui message
366 warning: unwrapped ui message
370 mercurial/commands.py:0:
367 mercurial/commands.py:0:
371 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
368 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
372 warning: unwrapped ui message
369 warning: unwrapped ui message
373 mercurial/commands.py:0:
370 mercurial/commands.py:0:
374 > ui.write('path %s\n' % k)
371 > ui.write('path %s\n' % k)
375 warning: unwrapped ui message
372 warning: unwrapped ui message
376 mercurial/commands.py:0:
373 mercurial/commands.py:0:
377 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
374 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
378 warning: unwrapped ui message
375 warning: unwrapped ui message
379 mercurial/commands.py:0:
376 mercurial/commands.py:0:
380 > Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
377 > Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
381 warning: line over 80 characters
378 warning: line over 80 characters
382 mercurial/commands.py:0:
379 mercurial/commands.py:0:
383 > remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
380 > remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
384 warning: line over 80 characters
381 warning: line over 80 characters
385 mercurial/commands.py:0:
382 mercurial/commands.py:0:
386 > ui.write("digraph G {\n")
383 > ui.write("digraph G {\n")
387 warning: unwrapped ui message
384 warning: unwrapped ui message
388 mercurial/commands.py:0:
385 mercurial/commands.py:0:
389 > ui.write("internal: %s %s\n" % d)
386 > ui.write("internal: %s %s\n" % d)
390 warning: unwrapped ui message
387 warning: unwrapped ui message
391 mercurial/commands.py:0:
388 mercurial/commands.py:0:
392 > ui.write("standard: %s\n" % util.datestr(d))
389 > ui.write("standard: %s\n" % util.datestr(d))
393 warning: unwrapped ui message
390 warning: unwrapped ui message
394 mercurial/commands.py:0:
391 mercurial/commands.py:0:
395 > ui.write('avg chain length : ' + fmt % avgchainlen)
392 > ui.write('avg chain length : ' + fmt % avgchainlen)
396 warning: unwrapped ui message
393 warning: unwrapped ui message
397 mercurial/commands.py:0:
394 mercurial/commands.py:0:
398 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
395 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
399 warning: unwrapped ui message
396 warning: unwrapped ui message
400 mercurial/commands.py:0:
397 mercurial/commands.py:0:
401 > ui.write('compression ratio : ' + fmt % compratio)
398 > ui.write('compression ratio : ' + fmt % compratio)
402 warning: unwrapped ui message
399 warning: unwrapped ui message
403 mercurial/commands.py:0:
400 mercurial/commands.py:0:
404 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
401 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
405 warning: unwrapped ui message
402 warning: unwrapped ui message
406 mercurial/commands.py:0:
403 mercurial/commands.py:0:
407 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
404 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
408 warning: unwrapped ui message
405 warning: unwrapped ui message
409 mercurial/commands.py:0:
406 mercurial/commands.py:0:
410 > ui.write('flags : %s\n' % ', '.join(flags))
407 > ui.write('flags : %s\n' % ', '.join(flags))
411 warning: unwrapped ui message
408 warning: unwrapped ui message
412 mercurial/commands.py:0:
409 mercurial/commands.py:0:
413 > ui.write('format : %d\n' % format)
410 > ui.write('format : %d\n' % format)
414 warning: unwrapped ui message
411 warning: unwrapped ui message
415 mercurial/commands.py:0:
412 mercurial/commands.py:0:
416 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
413 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
417 warning: unwrapped ui message
414 warning: unwrapped ui message
418 mercurial/commands.py:0:
415 mercurial/commands.py:0:
419 > ui.write('revision size : ' + fmt2 % totalsize)
416 > ui.write('revision size : ' + fmt2 % totalsize)
420 warning: unwrapped ui message
417 warning: unwrapped ui message
421 mercurial/commands.py:0:
418 mercurial/commands.py:0:
422 > ui.write('revisions : ' + fmt2 % numrevs)
419 > ui.write('revisions : ' + fmt2 % numrevs)
423 warning: unwrapped ui message
420 warning: unwrapped ui message
424 warning: unwrapped ui message
421 warning: unwrapped ui message
425 mercurial/commands.py:0:
422 mercurial/commands.py:0:
426 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
423 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
427 warning: unwrapped ui message
424 warning: unwrapped ui message
428 mercurial/commandserver.py:0:
425 mercurial/commandserver.py:0:
429 > # the ui here is really the repo ui so take its baseui so we don't end up
426 > # the ui here is really the repo ui so take its baseui so we don't end up
430 warning: line over 80 characters
427 warning: line over 80 characters
431 mercurial/context.py:0:
428 mercurial/context.py:0:
432 > return self._manifestdelta[path], self._manifestdelta.flags(path)
429 > return self._manifestdelta[path], self._manifestdelta.flags(path)
433 warning: line over 80 characters
430 warning: line over 80 characters
434 mercurial/dagparser.py:0:
431 mercurial/dagparser.py:0:
435 > raise util.Abort(_("invalid character in dag description: %s...") % s)
432 > raise util.Abort(_("invalid character in dag description: %s...") % s)
436 warning: line over 80 characters
433 warning: line over 80 characters
437 mercurial/dagparser.py:0:
434 mercurial/dagparser.py:0:
438 > >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))])
435 > >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))])
439 warning: line over 80 characters
436 warning: line over 80 characters
440 mercurial/dirstate.py:0:
437 mercurial/dirstate.py:0:
441 > if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
438 > if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
442 warning: line over 80 characters
439 warning: line over 80 characters
443 mercurial/discovery.py:0:
440 mercurial/discovery.py:0:
444 > If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
441 > If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
445 warning: line over 80 characters
442 warning: line over 80 characters
446 mercurial/discovery.py:0:
443 mercurial/discovery.py:0:
447 > def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
444 > def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
448 warning: line over 80 characters
445 warning: line over 80 characters
449 mercurial/dispatch.py:0:
446 mercurial/dispatch.py:0:
450 > " (.hg not found)") % os.getcwd())
447 > " (.hg not found)") % os.getcwd())
451 warning: line over 80 characters
448 warning: line over 80 characters
452 mercurial/dispatch.py:0:
449 mercurial/dispatch.py:0:
453 > aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
450 > aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
454 warning: line over 80 characters
451 warning: line over 80 characters
455 mercurial/dispatch.py:0:
452 mercurial/dispatch.py:0:
456 > except:
453 > except:
457 warning: naked except clause
454 warning: naked except clause
458 mercurial/dispatch.py:0:
455 mercurial/dispatch.py:0:
459 > return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
456 > return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
460 warning: line over 80 characters
457 warning: line over 80 characters
461 mercurial/dispatch.py:0:
458 mercurial/dispatch.py:0:
462 > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
459 > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
463 warning: line over 80 characters
460 warning: line over 80 characters
464 mercurial/dispatch.py:0:
461 mercurial/dispatch.py:0:
465 > except:
462 > except:
466 warning: naked except clause
463 warning: naked except clause
467 mercurial/hg.py:0:
464 mercurial/hg.py:0:
468 > except:
465 > except:
469 warning: naked except clause
466 warning: naked except clause
470 mercurial/hgweb/hgweb_mod.py:0:
467 mercurial/hgweb/hgweb_mod.py:0:
471 > self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
468 > self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
472 warning: line over 80 characters
469 warning: line over 80 characters
473 mercurial/keepalive.py:0:
470 mercurial/keepalive.py:0:
474 > except:
471 > except:
475 warning: naked except clause
472 warning: naked except clause
476 mercurial/keepalive.py:0:
473 mercurial/keepalive.py:0:
477 > except:
474 > except:
478 warning: naked except clause
475 warning: naked except clause
479 mercurial/localrepo.py:0:
476 mercurial/localrepo.py:0:
480 > # we return an integer indicating remote head count change
477 > # we return an integer indicating remote head count change
481 warning: line over 80 characters
478 warning: line over 80 characters
482 mercurial/localrepo.py:0:
479 mercurial/localrepo.py:0:
483 > raise util.Abort(_("empty or missing revlog for %s") % fname)
480 > raise util.Abort(_("empty or missing revlog for %s") % fname)
484 warning: line over 80 characters
481 warning: line over 80 characters
485 warning: line over 80 characters
482 warning: line over 80 characters
486 mercurial/localrepo.py:0:
483 mercurial/localrepo.py:0:
487 > if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
484 > if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
488 warning: line over 80 characters
485 warning: line over 80 characters
489 mercurial/localrepo.py:0:
486 mercurial/localrepo.py:0:
490 > self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
487 > self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
491 warning: line over 80 characters
488 warning: line over 80 characters
492 mercurial/localrepo.py:0:
489 mercurial/localrepo.py:0:
493 > # new requirements = old non-format requirements + new format-related
490 > # new requirements = old non-format requirements + new format-related
494 warning: line over 80 characters
491 warning: line over 80 characters
495 mercurial/localrepo.py:0:
492 mercurial/localrepo.py:0:
496 > except:
493 > except:
497 warning: naked except clause
494 warning: naked except clause
498 mercurial/localrepo.py:0:
495 mercurial/localrepo.py:0:
499 > """return status of files between two nodes or node and working directory
496 > """return status of files between two nodes or node and working directory
500 warning: line over 80 characters
497 warning: line over 80 characters
501 mercurial/localrepo.py:0:
498 mercurial/localrepo.py:0:
502 > '''Returns a tagscache object that contains various tags related caches.'''
499 > '''Returns a tagscache object that contains various tags related caches.'''
503 warning: line over 80 characters
500 warning: line over 80 characters
504 mercurial/manifest.py:0:
501 mercurial/manifest.py:0:
505 > return "".join(struct.pack(">lll", start, end, len(content)) + content
502 > return "".join(struct.pack(">lll", start, end, len(content)) + content
506 warning: line over 80 characters
503 warning: line over 80 characters
507 mercurial/merge.py:0:
504 mercurial/merge.py:0:
508 > subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
505 > subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
509 warning: line over 80 characters
506 warning: line over 80 characters
510 mercurial/patch.py:0:
507 mercurial/patch.py:0:
511 > modified, added, removed, copy, getfilectx, opts, losedata, prefix)
508 > modified, added, removed, copy, getfilectx, opts, losedata, prefix)
512 warning: line over 80 characters
509 warning: line over 80 characters
513 mercurial/patch.py:0:
510 mercurial/patch.py:0:
514 > diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
511 > diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
515 warning: line over 80 characters
512 warning: line over 80 characters
516 mercurial/patch.py:0:
513 mercurial/patch.py:0:
517 > output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
514 > output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
518 warning: line over 80 characters
515 warning: line over 80 characters
519 mercurial/patch.py:0:
516 mercurial/patch.py:0:
520 > except:
517 > except:
521 warning: naked except clause
518 warning: naked except clause
522 mercurial/pure/base85.py:0:
519 mercurial/pure/base85.py:0:
523 > raise OverflowError('Base85 overflow in hunk starting at byte %d' % i)
520 > raise OverflowError('Base85 overflow in hunk starting at byte %d' % i)
524 warning: line over 80 characters
521 warning: line over 80 characters
525 mercurial/pure/mpatch.py:0:
522 mercurial/pure/mpatch.py:0:
526 > frags.extend(reversed(new)) # what was left at the end
523 > frags.extend(reversed(new)) # what was left at the end
527 warning: line over 80 characters
524 warning: line over 80 characters
528 mercurial/repair.py:0:
525 mercurial/repair.py:0:
529 > except:
526 > except:
530 warning: naked except clause
527 warning: naked except clause
531 mercurial/repair.py:0:
528 mercurial/repair.py:0:
532 > except:
529 > except:
533 warning: naked except clause
530 warning: naked except clause
534 mercurial/revset.py:0:
531 mercurial/revset.py:0:
535 > elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
532 > elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
536 warning: line over 80 characters
533 warning: line over 80 characters
537 mercurial/revset.py:0:
534 mercurial/revset.py:0:
538 > Changesets that are the Nth ancestor (first parents only) of a changeset in set.
535 > Changesets that are the Nth ancestor (first parents only) of a changeset in set.
539 warning: line over 80 characters
536 warning: line over 80 characters
540 mercurial/scmutil.py:0:
537 mercurial/scmutil.py:0:
541 > raise util.Abort(_("path '%s' is inside nested repo %r") %
538 > raise util.Abort(_("path '%s' is inside nested repo %r") %
542 warning: line over 80 characters
539 warning: line over 80 characters
543 mercurial/scmutil.py:0:
540 mercurial/scmutil.py:0:
544 > "requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
541 > "requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
545 warning: line over 80 characters
542 warning: line over 80 characters
546 mercurial/scmutil.py:0:
543 mercurial/scmutil.py:0:
547 > elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
544 > elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
548 warning: line over 80 characters
545 warning: line over 80 characters
549 mercurial/setdiscovery.py:0:
546 mercurial/setdiscovery.py:0:
550 > # treat remote heads (and maybe own heads) as a first implicit sample response
547 > # treat remote heads (and maybe own heads) as a first implicit sample response
551 warning: line over 80 characters
548 warning: line over 80 characters
552 mercurial/setdiscovery.py:0:
549 mercurial/setdiscovery.py:0:
553 > undecided = dag.nodeset() # own nodes where I don't know if remote knows them
550 > undecided = dag.nodeset() # own nodes where I don't know if remote knows them
554 warning: line over 80 characters
551 warning: line over 80 characters
555 mercurial/similar.py:0:
552 mercurial/similar.py:0:
556 > repo.ui.progress(_('searching for similar files'), i, total=len(removed))
553 > repo.ui.progress(_('searching for similar files'), i, total=len(removed))
557 warning: line over 80 characters
554 warning: line over 80 characters
558 mercurial/simplemerge.py:0:
555 mercurial/simplemerge.py:0:
559 > for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
556 > for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
560 warning: line over 80 characters
557 warning: line over 80 characters
561 mercurial/sshrepo.py:0:
558 mercurial/sshrepo.py:0:
562 > self._abort(error.RepoError(_("no suitable response from remote hg")))
559 > self._abort(error.RepoError(_("no suitable response from remote hg")))
563 warning: line over 80 characters
560 warning: line over 80 characters
564 mercurial/sshrepo.py:0:
561 mercurial/sshrepo.py:0:
565 > except:
562 > except:
566 warning: naked except clause
563 warning: naked except clause
567 mercurial/subrepo.py:0:
564 mercurial/subrepo.py:0:
568 > other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
565 > other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
569 warning: line over 80 characters
566 warning: line over 80 characters
570 mercurial/subrepo.py:0:
567 mercurial/subrepo.py:0:
571 > msg = (_(' subrepository sources for %s differ (in checked out version)\n'
568 > msg = (_(' subrepository sources for %s differ (in checked out version)\n'
572 warning: line over 80 characters
569 warning: line over 80 characters
573 mercurial/transaction.py:0:
570 mercurial/transaction.py:0:
574 > except:
571 > except:
575 warning: naked except clause
572 warning: naked except clause
576 mercurial/ui.py:0:
573 mercurial/ui.py:0:
577 > traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
574 > traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
578 warning: line over 80 characters
575 warning: line over 80 characters
579 mercurial/url.py:0:
576 mercurial/url.py:0:
580 > conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
577 > conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
581 warning: line over 80 characters
578 warning: line over 80 characters
582 mercurial/util.py:0:
579 mercurial/util.py:0:
583 > except:
580 > except:
584 warning: naked except clause
581 warning: naked except clause
585 mercurial/util.py:0:
582 mercurial/util.py:0:
586 > except:
583 > except:
587 warning: naked except clause
584 warning: naked except clause
588 mercurial/verify.py:0:
585 mercurial/verify.py:0:
589 > except:
586 > except:
590 warning: naked except clause
587 warning: naked except clause
591 mercurial/verify.py:0:
588 mercurial/verify.py:0:
592 > except:
589 > except:
593 warning: naked except clause
590 warning: naked except clause
594 mercurial/wireproto.py:0:
591 mercurial/wireproto.py:0:
595 > # Assuming the future to be filled with the result from the batched request
592 > # Assuming the future to be filled with the result from the batched request
596 warning: line over 80 characters
593 warning: line over 80 characters
597 mercurial/wireproto.py:0:
594 mercurial/wireproto.py:0:
598 > '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)'''
595 > '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)'''
599 warning: line over 80 characters
596 warning: line over 80 characters
600 mercurial/wireproto.py:0:
597 mercurial/wireproto.py:0:
601 > All methods invoked on instances of this class are simply queued and return a
598 > All methods invoked on instances of this class are simply queued and return a
602 warning: line over 80 characters
599 warning: line over 80 characters
603 mercurial/wireproto.py:0:
600 mercurial/wireproto.py:0:
604 > The decorator returns a function which wraps this coroutine as a plain method,
601 > The decorator returns a function which wraps this coroutine as a plain method,
605 warning: line over 80 characters
602 warning: line over 80 characters
606 setup.py:0:
603 setup.py:0:
607 > raise SystemExit("Python headers are required to build Mercurial")
604 > raise SystemExit("Python headers are required to build Mercurial")
608 warning: line over 80 characters
605 warning: line over 80 characters
609 setup.py:0:
606 setup.py:0:
610 > except:
607 > except:
611 warning: naked except clause
608 warning: naked except clause
612 setup.py:0:
609 setup.py:0:
613 > # build_py), it will not find osutil & friends, thinking that those modules are
610 > # build_py), it will not find osutil & friends, thinking that those modules are
614 warning: line over 80 characters
611 warning: line over 80 characters
615 setup.py:0:
612 setup.py:0:
616 > except:
613 > except:
617 warning: naked except clause
614 warning: naked except clause
618 warning: naked except clause
615 warning: naked except clause
619 setup.py:0:
616 setup.py:0:
620 > isironpython = platform.python_implementation().lower().find("ironpython") != -1
617 > isironpython = platform.python_implementation().lower().find("ironpython") != -1
621 warning: line over 80 characters
618 warning: line over 80 characters
622 setup.py:0:
619 setup.py:0:
623 > except:
620 > except:
624 warning: naked except clause
621 warning: naked except clause
625 warning: naked except clause
622 warning: naked except clause
626 warning: naked except clause
623 warning: naked except clause
627 tests/autodiff.py:0:
624 tests/autodiff.py:0:
628 > ui.write('data lost for: %s\n' % fn)
625 > ui.write('data lost for: %s\n' % fn)
629 warning: unwrapped ui message
626 warning: unwrapped ui message
630 tests/run-tests.py:0:
627 tests/run-tests.py:0:
631 > except:
628 > except:
632 warning: naked except clause
629 warning: naked except clause
633 tests/test-commandserver.py:0:
630 tests/test-commandserver.py:0:
634 > 'hooks.pre-identify=python:test-commandserver.hook', 'id'],
631 > 'hooks.pre-identify=python:test-commandserver.hook', 'id'],
635 warning: line over 80 characters
632 warning: line over 80 characters
636 tests/test-commandserver.py:0:
633 tests/test-commandserver.py:0:
637 > # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it
634 > # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it
638 warning: line over 80 characters
635 warning: line over 80 characters
639 tests/test-commandserver.py:0:
636 tests/test-commandserver.py:0:
640 > print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data))
637 > print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data))
641 warning: line over 80 characters
638 warning: line over 80 characters
642 tests/test-filecache.py:0:
639 tests/test-filecache.py:0:
643 > except:
640 > except:
644 warning: naked except clause
641 warning: naked except clause
645 tests/test-filecache.py:0:
642 tests/test-filecache.py:0:
646 > if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
643 > if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
647 warning: line over 80 characters
644 warning: line over 80 characters
648 tests/test-ui-color.py:0:
645 tests/test-ui-color.py:0:
649 > testui.warn('warning\n')
646 > testui.warn('warning\n')
650 warning: unwrapped ui message
647 warning: unwrapped ui message
651 tests/test-ui-color.py:0:
648 tests/test-ui-color.py:0:
652 > testui.write('buffered\n')
649 > testui.write('buffered\n')
653 warning: unwrapped ui message
650 warning: unwrapped ui message
654 tests/test-walkrepo.py:0:
651 tests/test-walkrepo.py:0:
655 > print "Found %d repositories when I should have found 2" % (len(reposet),)
652 > print "Found %d repositories when I should have found 2" % (len(reposet),)
656 warning: line over 80 characters
653 warning: line over 80 characters
657 tests/test-walkrepo.py:0:
654 tests/test-walkrepo.py:0:
658 > print "Found %d repositories when I should have found 3" % (len(reposet),)
655 > print "Found %d repositories when I should have found 3" % (len(reposet),)
659 warning: line over 80 characters
656 warning: line over 80 characters
General Comments 0
You need to be logged in to leave comments. Login now