##// END OF EJS Templates
Lowercase error messages
Martin Geisler -
r12067:a4fbbe0f stable
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,288 +1,288 b''
1 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
1 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 '''commands to sign and verify changesets'''
6 '''commands to sign and verify changesets'''
7
7
8 import os, tempfile, binascii
8 import os, tempfile, binascii
9 from mercurial import util, commands, match
9 from mercurial import util, commands, match
10 from mercurial import node as hgnode
10 from mercurial import node as hgnode
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 class gpg(object):
13 class gpg(object):
14 def __init__(self, path, key=None):
14 def __init__(self, path, key=None):
15 self.path = path
15 self.path = path
16 self.key = (key and " --local-user \"%s\"" % key) or ""
16 self.key = (key and " --local-user \"%s\"" % key) or ""
17
17
18 def sign(self, data):
18 def sign(self, data):
19 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
19 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
20 return util.filter(data, gpgcmd)
20 return util.filter(data, gpgcmd)
21
21
22 def verify(self, data, sig):
22 def verify(self, data, sig):
23 """ returns of the good and bad signatures"""
23 """ returns of the good and bad signatures"""
24 sigfile = datafile = None
24 sigfile = datafile = None
25 try:
25 try:
26 # create temporary files
26 # create temporary files
27 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
27 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
28 fp = os.fdopen(fd, 'wb')
28 fp = os.fdopen(fd, 'wb')
29 fp.write(sig)
29 fp.write(sig)
30 fp.close()
30 fp.close()
31 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
31 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
32 fp = os.fdopen(fd, 'wb')
32 fp = os.fdopen(fd, 'wb')
33 fp.write(data)
33 fp.write(data)
34 fp.close()
34 fp.close()
35 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
35 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
36 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
36 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
37 ret = util.filter("", gpgcmd)
37 ret = util.filter("", gpgcmd)
38 finally:
38 finally:
39 for f in (sigfile, datafile):
39 for f in (sigfile, datafile):
40 try:
40 try:
41 if f:
41 if f:
42 os.unlink(f)
42 os.unlink(f)
43 except:
43 except:
44 pass
44 pass
45 keys = []
45 keys = []
46 key, fingerprint = None, None
46 key, fingerprint = None, None
47 err = ""
47 err = ""
48 for l in ret.splitlines():
48 for l in ret.splitlines():
49 # see DETAILS in the gnupg documentation
49 # see DETAILS in the gnupg documentation
50 # filter the logger output
50 # filter the logger output
51 if not l.startswith("[GNUPG:]"):
51 if not l.startswith("[GNUPG:]"):
52 continue
52 continue
53 l = l[9:]
53 l = l[9:]
54 if l.startswith("ERRSIG"):
54 if l.startswith("ERRSIG"):
55 err = _("error while verifying signature")
55 err = _("error while verifying signature")
56 break
56 break
57 elif l.startswith("VALIDSIG"):
57 elif l.startswith("VALIDSIG"):
58 # fingerprint of the primary key
58 # fingerprint of the primary key
59 fingerprint = l.split()[10]
59 fingerprint = l.split()[10]
60 elif (l.startswith("GOODSIG") or
60 elif (l.startswith("GOODSIG") or
61 l.startswith("EXPSIG") or
61 l.startswith("EXPSIG") or
62 l.startswith("EXPKEYSIG") or
62 l.startswith("EXPKEYSIG") or
63 l.startswith("BADSIG")):
63 l.startswith("BADSIG")):
64 if key is not None:
64 if key is not None:
65 keys.append(key + [fingerprint])
65 keys.append(key + [fingerprint])
66 key = l.split(" ", 2)
66 key = l.split(" ", 2)
67 fingerprint = None
67 fingerprint = None
68 if err:
68 if err:
69 return err, []
69 return err, []
70 if key is not None:
70 if key is not None:
71 keys.append(key + [fingerprint])
71 keys.append(key + [fingerprint])
72 return err, keys
72 return err, keys
73
73
74 def newgpg(ui, **opts):
74 def newgpg(ui, **opts):
75 """create a new gpg instance"""
75 """create a new gpg instance"""
76 gpgpath = ui.config("gpg", "cmd", "gpg")
76 gpgpath = ui.config("gpg", "cmd", "gpg")
77 gpgkey = opts.get('key')
77 gpgkey = opts.get('key')
78 if not gpgkey:
78 if not gpgkey:
79 gpgkey = ui.config("gpg", "key", None)
79 gpgkey = ui.config("gpg", "key", None)
80 return gpg(gpgpath, gpgkey)
80 return gpg(gpgpath, gpgkey)
81
81
82 def sigwalk(repo):
82 def sigwalk(repo):
83 """
83 """
84 walk over every sigs, yields a couple
84 walk over every sigs, yields a couple
85 ((node, version, sig), (filename, linenumber))
85 ((node, version, sig), (filename, linenumber))
86 """
86 """
87 def parsefile(fileiter, context):
87 def parsefile(fileiter, context):
88 ln = 1
88 ln = 1
89 for l in fileiter:
89 for l in fileiter:
90 if not l:
90 if not l:
91 continue
91 continue
92 yield (l.split(" ", 2), (context, ln))
92 yield (l.split(" ", 2), (context, ln))
93 ln += 1
93 ln += 1
94
94
95 # read the heads
95 # read the heads
96 fl = repo.file(".hgsigs")
96 fl = repo.file(".hgsigs")
97 for r in reversed(fl.heads()):
97 for r in reversed(fl.heads()):
98 fn = ".hgsigs|%s" % hgnode.short(r)
98 fn = ".hgsigs|%s" % hgnode.short(r)
99 for item in parsefile(fl.read(r).splitlines(), fn):
99 for item in parsefile(fl.read(r).splitlines(), fn):
100 yield item
100 yield item
101 try:
101 try:
102 # read local signatures
102 # read local signatures
103 fn = "localsigs"
103 fn = "localsigs"
104 for item in parsefile(repo.opener(fn), fn):
104 for item in parsefile(repo.opener(fn), fn):
105 yield item
105 yield item
106 except IOError:
106 except IOError:
107 pass
107 pass
108
108
109 def getkeys(ui, repo, mygpg, sigdata, context):
109 def getkeys(ui, repo, mygpg, sigdata, context):
110 """get the keys who signed a data"""
110 """get the keys who signed a data"""
111 fn, ln = context
111 fn, ln = context
112 node, version, sig = sigdata
112 node, version, sig = sigdata
113 prefix = "%s:%d" % (fn, ln)
113 prefix = "%s:%d" % (fn, ln)
114 node = hgnode.bin(node)
114 node = hgnode.bin(node)
115
115
116 data = node2txt(repo, node, version)
116 data = node2txt(repo, node, version)
117 sig = binascii.a2b_base64(sig)
117 sig = binascii.a2b_base64(sig)
118 err, keys = mygpg.verify(data, sig)
118 err, keys = mygpg.verify(data, sig)
119 if err:
119 if err:
120 ui.warn("%s:%d %s\n" % (fn, ln , err))
120 ui.warn("%s:%d %s\n" % (fn, ln , err))
121 return None
121 return None
122
122
123 validkeys = []
123 validkeys = []
124 # warn for expired key and/or sigs
124 # warn for expired key and/or sigs
125 for key in keys:
125 for key in keys:
126 if key[0] == "BADSIG":
126 if key[0] == "BADSIG":
127 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
127 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
128 continue
128 continue
129 if key[0] == "EXPSIG":
129 if key[0] == "EXPSIG":
130 ui.write(_("%s Note: Signature has expired"
130 ui.write(_("%s Note: Signature has expired"
131 " (signed by: \"%s\")\n") % (prefix, key[2]))
131 " (signed by: \"%s\")\n") % (prefix, key[2]))
132 elif key[0] == "EXPKEYSIG":
132 elif key[0] == "EXPKEYSIG":
133 ui.write(_("%s Note: This key has expired"
133 ui.write(_("%s Note: This key has expired"
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
135 validkeys.append((key[1], key[2], key[3]))
135 validkeys.append((key[1], key[2], key[3]))
136 return validkeys
136 return validkeys
137
137
138 def sigs(ui, repo):
138 def sigs(ui, repo):
139 """list signed changesets"""
139 """list signed changesets"""
140 mygpg = newgpg(ui)
140 mygpg = newgpg(ui)
141 revs = {}
141 revs = {}
142
142
143 for data, context in sigwalk(repo):
143 for data, context in sigwalk(repo):
144 node, version, sig = data
144 node, version, sig = data
145 fn, ln = context
145 fn, ln = context
146 try:
146 try:
147 n = repo.lookup(node)
147 n = repo.lookup(node)
148 except KeyError:
148 except KeyError:
149 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
149 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
150 continue
150 continue
151 r = repo.changelog.rev(n)
151 r = repo.changelog.rev(n)
152 keys = getkeys(ui, repo, mygpg, data, context)
152 keys = getkeys(ui, repo, mygpg, data, context)
153 if not keys:
153 if not keys:
154 continue
154 continue
155 revs.setdefault(r, [])
155 revs.setdefault(r, [])
156 revs[r].extend(keys)
156 revs[r].extend(keys)
157 for rev in sorted(revs, reverse=True):
157 for rev in sorted(revs, reverse=True):
158 for k in revs[rev]:
158 for k in revs[rev]:
159 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
159 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
160 ui.write("%-30s %s\n" % (keystr(ui, k), r))
160 ui.write("%-30s %s\n" % (keystr(ui, k), r))
161
161
162 def check(ui, repo, rev):
162 def check(ui, repo, rev):
163 """verify all the signatures there may be for a particular revision"""
163 """verify all the signatures there may be for a particular revision"""
164 mygpg = newgpg(ui)
164 mygpg = newgpg(ui)
165 rev = repo.lookup(rev)
165 rev = repo.lookup(rev)
166 hexrev = hgnode.hex(rev)
166 hexrev = hgnode.hex(rev)
167 keys = []
167 keys = []
168
168
169 for data, context in sigwalk(repo):
169 for data, context in sigwalk(repo):
170 node, version, sig = data
170 node, version, sig = data
171 if node == hexrev:
171 if node == hexrev:
172 k = getkeys(ui, repo, mygpg, data, context)
172 k = getkeys(ui, repo, mygpg, data, context)
173 if k:
173 if k:
174 keys.extend(k)
174 keys.extend(k)
175
175
176 if not keys:
176 if not keys:
177 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
177 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
178 return
178 return
179
179
180 # print summary
180 # print summary
181 ui.write("%s is signed by:\n" % hgnode.short(rev))
181 ui.write("%s is signed by:\n" % hgnode.short(rev))
182 for key in keys:
182 for key in keys:
183 ui.write(" %s\n" % keystr(ui, key))
183 ui.write(" %s\n" % keystr(ui, key))
184
184
185 def keystr(ui, key):
185 def keystr(ui, key):
186 """associate a string to a key (username, comment)"""
186 """associate a string to a key (username, comment)"""
187 keyid, user, fingerprint = key
187 keyid, user, fingerprint = key
188 comment = ui.config("gpg", fingerprint, None)
188 comment = ui.config("gpg", fingerprint, None)
189 if comment:
189 if comment:
190 return "%s (%s)" % (user, comment)
190 return "%s (%s)" % (user, comment)
191 else:
191 else:
192 return user
192 return user
193
193
194 def sign(ui, repo, *revs, **opts):
194 def sign(ui, repo, *revs, **opts):
195 """add a signature for the current or given revision
195 """add a signature for the current or given revision
196
196
197 If no revision is given, the parent of the working directory is used,
197 If no revision is given, the parent of the working directory is used,
198 or tip if no revision is checked out.
198 or tip if no revision is checked out.
199
199
200 See :hg:`help dates` for a list of formats valid for -d/--date.
200 See :hg:`help dates` for a list of formats valid for -d/--date.
201 """
201 """
202
202
203 mygpg = newgpg(ui, **opts)
203 mygpg = newgpg(ui, **opts)
204 sigver = "0"
204 sigver = "0"
205 sigmessage = ""
205 sigmessage = ""
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 if revs:
211 if revs:
212 nodes = [repo.lookup(n) for n in revs]
212 nodes = [repo.lookup(n) for n in revs]
213 else:
213 else:
214 nodes = [node for node in repo.dirstate.parents()
214 nodes = [node for node in repo.dirstate.parents()
215 if node != hgnode.nullid]
215 if node != hgnode.nullid]
216 if len(nodes) > 1:
216 if len(nodes) > 1:
217 raise util.Abort(_('uncommitted merge - please provide a '
217 raise util.Abort(_('uncommitted merge - please provide a '
218 'specific revision'))
218 'specific revision'))
219 if not nodes:
219 if not nodes:
220 nodes = [repo.changelog.tip()]
220 nodes = [repo.changelog.tip()]
221
221
222 for n in nodes:
222 for n in nodes:
223 hexnode = hgnode.hex(n)
223 hexnode = hgnode.hex(n)
224 ui.write(_("Signing %d:%s\n") % (repo.changelog.rev(n),
224 ui.write(_("Signing %d:%s\n") % (repo.changelog.rev(n),
225 hgnode.short(n)))
225 hgnode.short(n)))
226 # build data
226 # build data
227 data = node2txt(repo, n, sigver)
227 data = node2txt(repo, n, sigver)
228 sig = mygpg.sign(data)
228 sig = mygpg.sign(data)
229 if not sig:
229 if not sig:
230 raise util.Abort(_("Error while signing"))
230 raise util.abort(_("error while signing"))
231 sig = binascii.b2a_base64(sig)
231 sig = binascii.b2a_base64(sig)
232 sig = sig.replace("\n", "")
232 sig = sig.replace("\n", "")
233 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
233 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
234
234
235 # write it
235 # write it
236 if opts['local']:
236 if opts['local']:
237 repo.opener("localsigs", "ab").write(sigmessage)
237 repo.opener("localsigs", "ab").write(sigmessage)
238 return
238 return
239
239
240 msigs = match.exact(repo.root, '', ['.hgsigs'])
240 msigs = match.exact(repo.root, '', ['.hgsigs'])
241 s = repo.status(match=msigs, unknown=True, ignored=True)[:6]
241 s = repo.status(match=msigs, unknown=True, ignored=True)[:6]
242 if util.any(s) and not opts["force"]:
242 if util.any(s) and not opts["force"]:
243 raise util.Abort(_("working copy of .hgsigs is changed "
243 raise util.Abort(_("working copy of .hgsigs is changed "
244 "(please commit .hgsigs manually "
244 "(please commit .hgsigs manually "
245 "or use --force)"))
245 "or use --force)"))
246
246
247 repo.wfile(".hgsigs", "ab").write(sigmessage)
247 repo.wfile(".hgsigs", "ab").write(sigmessage)
248
248
249 if '.hgsigs' not in repo.dirstate:
249 if '.hgsigs' not in repo.dirstate:
250 repo[None].add([".hgsigs"])
250 repo[None].add([".hgsigs"])
251
251
252 if opts["no_commit"]:
252 if opts["no_commit"]:
253 return
253 return
254
254
255 message = opts['message']
255 message = opts['message']
256 if not message:
256 if not message:
257 # we don't translate commit messages
257 # we don't translate commit messages
258 message = "\n".join(["Added signature for changeset %s"
258 message = "\n".join(["Added signature for changeset %s"
259 % hgnode.short(n)
259 % hgnode.short(n)
260 for n in nodes])
260 for n in nodes])
261 try:
261 try:
262 repo.commit(message, opts['user'], opts['date'], match=msigs)
262 repo.commit(message, opts['user'], opts['date'], match=msigs)
263 except ValueError, inst:
263 except ValueError, inst:
264 raise util.Abort(str(inst))
264 raise util.Abort(str(inst))
265
265
266 def node2txt(repo, node, ver):
266 def node2txt(repo, node, ver):
267 """map a manifest into some text"""
267 """map a manifest into some text"""
268 if ver == "0":
268 if ver == "0":
269 return "%s\n" % hgnode.hex(node)
269 return "%s\n" % hgnode.hex(node)
270 else:
270 else:
271 raise util.Abort(_("unknown signature version"))
271 raise util.Abort(_("unknown signature version"))
272
272
273 cmdtable = {
273 cmdtable = {
274 "sign":
274 "sign":
275 (sign,
275 (sign,
276 [('l', 'local', None, _('make the signature local')),
276 [('l', 'local', None, _('make the signature local')),
277 ('f', 'force', None, _('sign even if the sigfile is modified')),
277 ('f', 'force', None, _('sign even if the sigfile is modified')),
278 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
278 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
279 ('k', 'key', '',
279 ('k', 'key', '',
280 _('the key id to sign with'), _('ID')),
280 _('the key id to sign with'), _('ID')),
281 ('m', 'message', '',
281 ('m', 'message', '',
282 _('commit message'), _('TEXT')),
282 _('commit message'), _('TEXT')),
283 ] + commands.commitopts2,
283 ] + commands.commitopts2,
284 _('hg sign [OPTION]... [REVISION]...')),
284 _('hg sign [OPTION]... [REVISION]...')),
285 "sigcheck": (check, [], _('hg sigcheck REVISION')),
285 "sigcheck": (check, [], _('hg sigcheck REVISION')),
286 "sigs": (sigs, [], _('hg sigs')),
286 "sigs": (sigs, [], _('hg sigs')),
287 }
287 }
288
288
@@ -1,3025 +1,3025 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 You will by default be managing a patch queue named "patches". You can
41 You will by default be managing a patch queue named "patches". You can
42 create other, independent patch queues with the :hg:`qqueue` command.
42 create other, independent patch queues with the :hg:`qqueue` command.
43 '''
43 '''
44
44
45 from mercurial.i18n import _
45 from mercurial.i18n import _
46 from mercurial.node import bin, hex, short, nullid, nullrev
46 from mercurial.node import bin, hex, short, nullid, nullrev
47 from mercurial.lock import release
47 from mercurial.lock import release
48 from mercurial import commands, cmdutil, hg, patch, util
48 from mercurial import commands, cmdutil, hg, patch, util
49 from mercurial import repair, extensions, url, error
49 from mercurial import repair, extensions, url, error
50 import os, sys, re, errno
50 import os, sys, re, errno
51
51
52 commands.norepo += " qclone"
52 commands.norepo += " qclone"
53
53
54 # Patch names looks like unix-file names.
54 # Patch names looks like unix-file names.
55 # They must be joinable with queue directory and result in the patch path.
55 # They must be joinable with queue directory and result in the patch path.
56 normname = util.normpath
56 normname = util.normpath
57
57
58 class statusentry(object):
58 class statusentry(object):
59 def __init__(self, node, name):
59 def __init__(self, node, name):
60 self.node, self.name = node, name
60 self.node, self.name = node, name
61 def __repr__(self):
61 def __repr__(self):
62 return hex(self.node) + ':' + self.name
62 return hex(self.node) + ':' + self.name
63
63
64 class patchheader(object):
64 class patchheader(object):
65 def __init__(self, pf, plainmode=False):
65 def __init__(self, pf, plainmode=False):
66 def eatdiff(lines):
66 def eatdiff(lines):
67 while lines:
67 while lines:
68 l = lines[-1]
68 l = lines[-1]
69 if (l.startswith("diff -") or
69 if (l.startswith("diff -") or
70 l.startswith("Index:") or
70 l.startswith("Index:") or
71 l.startswith("===========")):
71 l.startswith("===========")):
72 del lines[-1]
72 del lines[-1]
73 else:
73 else:
74 break
74 break
75 def eatempty(lines):
75 def eatempty(lines):
76 while lines:
76 while lines:
77 if not lines[-1].strip():
77 if not lines[-1].strip():
78 del lines[-1]
78 del lines[-1]
79 else:
79 else:
80 break
80 break
81
81
82 message = []
82 message = []
83 comments = []
83 comments = []
84 user = None
84 user = None
85 date = None
85 date = None
86 parent = None
86 parent = None
87 format = None
87 format = None
88 subject = None
88 subject = None
89 diffstart = 0
89 diffstart = 0
90
90
91 for line in file(pf):
91 for line in file(pf):
92 line = line.rstrip()
92 line = line.rstrip()
93 if (line.startswith('diff --git')
93 if (line.startswith('diff --git')
94 or (diffstart and line.startswith('+++ '))):
94 or (diffstart and line.startswith('+++ '))):
95 diffstart = 2
95 diffstart = 2
96 break
96 break
97 diffstart = 0 # reset
97 diffstart = 0 # reset
98 if line.startswith("--- "):
98 if line.startswith("--- "):
99 diffstart = 1
99 diffstart = 1
100 continue
100 continue
101 elif format == "hgpatch":
101 elif format == "hgpatch":
102 # parse values when importing the result of an hg export
102 # parse values when importing the result of an hg export
103 if line.startswith("# User "):
103 if line.startswith("# User "):
104 user = line[7:]
104 user = line[7:]
105 elif line.startswith("# Date "):
105 elif line.startswith("# Date "):
106 date = line[7:]
106 date = line[7:]
107 elif line.startswith("# Parent "):
107 elif line.startswith("# Parent "):
108 parent = line[9:]
108 parent = line[9:]
109 elif not line.startswith("# ") and line:
109 elif not line.startswith("# ") and line:
110 message.append(line)
110 message.append(line)
111 format = None
111 format = None
112 elif line == '# HG changeset patch':
112 elif line == '# HG changeset patch':
113 message = []
113 message = []
114 format = "hgpatch"
114 format = "hgpatch"
115 elif (format != "tagdone" and (line.startswith("Subject: ") or
115 elif (format != "tagdone" and (line.startswith("Subject: ") or
116 line.startswith("subject: "))):
116 line.startswith("subject: "))):
117 subject = line[9:]
117 subject = line[9:]
118 format = "tag"
118 format = "tag"
119 elif (format != "tagdone" and (line.startswith("From: ") or
119 elif (format != "tagdone" and (line.startswith("From: ") or
120 line.startswith("from: "))):
120 line.startswith("from: "))):
121 user = line[6:]
121 user = line[6:]
122 format = "tag"
122 format = "tag"
123 elif (format != "tagdone" and (line.startswith("Date: ") or
123 elif (format != "tagdone" and (line.startswith("Date: ") or
124 line.startswith("date: "))):
124 line.startswith("date: "))):
125 date = line[6:]
125 date = line[6:]
126 format = "tag"
126 format = "tag"
127 elif format == "tag" and line == "":
127 elif format == "tag" and line == "":
128 # when looking for tags (subject: from: etc) they
128 # when looking for tags (subject: from: etc) they
129 # end once you find a blank line in the source
129 # end once you find a blank line in the source
130 format = "tagdone"
130 format = "tagdone"
131 elif message or line:
131 elif message or line:
132 message.append(line)
132 message.append(line)
133 comments.append(line)
133 comments.append(line)
134
134
135 eatdiff(message)
135 eatdiff(message)
136 eatdiff(comments)
136 eatdiff(comments)
137 eatempty(message)
137 eatempty(message)
138 eatempty(comments)
138 eatempty(comments)
139
139
140 # make sure message isn't empty
140 # make sure message isn't empty
141 if format and format.startswith("tag") and subject:
141 if format and format.startswith("tag") and subject:
142 message.insert(0, "")
142 message.insert(0, "")
143 message.insert(0, subject)
143 message.insert(0, subject)
144
144
145 self.message = message
145 self.message = message
146 self.comments = comments
146 self.comments = comments
147 self.user = user
147 self.user = user
148 self.date = date
148 self.date = date
149 self.parent = parent
149 self.parent = parent
150 self.haspatch = diffstart > 1
150 self.haspatch = diffstart > 1
151 self.plainmode = plainmode
151 self.plainmode = plainmode
152
152
153 def setuser(self, user):
153 def setuser(self, user):
154 if not self.updateheader(['From: ', '# User '], user):
154 if not self.updateheader(['From: ', '# User '], user):
155 try:
155 try:
156 patchheaderat = self.comments.index('# HG changeset patch')
156 patchheaderat = self.comments.index('# HG changeset patch')
157 self.comments.insert(patchheaderat + 1, '# User ' + user)
157 self.comments.insert(patchheaderat + 1, '# User ' + user)
158 except ValueError:
158 except ValueError:
159 if self.plainmode or self._hasheader(['Date: ']):
159 if self.plainmode or self._hasheader(['Date: ']):
160 self.comments = ['From: ' + user] + self.comments
160 self.comments = ['From: ' + user] + self.comments
161 else:
161 else:
162 tmp = ['# HG changeset patch', '# User ' + user, '']
162 tmp = ['# HG changeset patch', '# User ' + user, '']
163 self.comments = tmp + self.comments
163 self.comments = tmp + self.comments
164 self.user = user
164 self.user = user
165
165
166 def setdate(self, date):
166 def setdate(self, date):
167 if not self.updateheader(['Date: ', '# Date '], date):
167 if not self.updateheader(['Date: ', '# Date '], date):
168 try:
168 try:
169 patchheaderat = self.comments.index('# HG changeset patch')
169 patchheaderat = self.comments.index('# HG changeset patch')
170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
170 self.comments.insert(patchheaderat + 1, '# Date ' + date)
171 except ValueError:
171 except ValueError:
172 if self.plainmode or self._hasheader(['From: ']):
172 if self.plainmode or self._hasheader(['From: ']):
173 self.comments = ['Date: ' + date] + self.comments
173 self.comments = ['Date: ' + date] + self.comments
174 else:
174 else:
175 tmp = ['# HG changeset patch', '# Date ' + date, '']
175 tmp = ['# HG changeset patch', '# Date ' + date, '']
176 self.comments = tmp + self.comments
176 self.comments = tmp + self.comments
177 self.date = date
177 self.date = date
178
178
179 def setparent(self, parent):
179 def setparent(self, parent):
180 if not self.updateheader(['# Parent '], parent):
180 if not self.updateheader(['# Parent '], parent):
181 try:
181 try:
182 patchheaderat = self.comments.index('# HG changeset patch')
182 patchheaderat = self.comments.index('# HG changeset patch')
183 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
183 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
184 except ValueError:
184 except ValueError:
185 pass
185 pass
186 self.parent = parent
186 self.parent = parent
187
187
188 def setmessage(self, message):
188 def setmessage(self, message):
189 if self.comments:
189 if self.comments:
190 self._delmsg()
190 self._delmsg()
191 self.message = [message]
191 self.message = [message]
192 self.comments += self.message
192 self.comments += self.message
193
193
194 def updateheader(self, prefixes, new):
194 def updateheader(self, prefixes, new):
195 '''Update all references to a field in the patch header.
195 '''Update all references to a field in the patch header.
196 Return whether the field is present.'''
196 Return whether the field is present.'''
197 res = False
197 res = False
198 for prefix in prefixes:
198 for prefix in prefixes:
199 for i in xrange(len(self.comments)):
199 for i in xrange(len(self.comments)):
200 if self.comments[i].startswith(prefix):
200 if self.comments[i].startswith(prefix):
201 self.comments[i] = prefix + new
201 self.comments[i] = prefix + new
202 res = True
202 res = True
203 break
203 break
204 return res
204 return res
205
205
206 def _hasheader(self, prefixes):
206 def _hasheader(self, prefixes):
207 '''Check if a header starts with any of the given prefixes.'''
207 '''Check if a header starts with any of the given prefixes.'''
208 for prefix in prefixes:
208 for prefix in prefixes:
209 for comment in self.comments:
209 for comment in self.comments:
210 if comment.startswith(prefix):
210 if comment.startswith(prefix):
211 return True
211 return True
212 return False
212 return False
213
213
214 def __str__(self):
214 def __str__(self):
215 if not self.comments:
215 if not self.comments:
216 return ''
216 return ''
217 return '\n'.join(self.comments) + '\n\n'
217 return '\n'.join(self.comments) + '\n\n'
218
218
219 def _delmsg(self):
219 def _delmsg(self):
220 '''Remove existing message, keeping the rest of the comments fields.
220 '''Remove existing message, keeping the rest of the comments fields.
221 If comments contains 'subject: ', message will prepend
221 If comments contains 'subject: ', message will prepend
222 the field and a blank line.'''
222 the field and a blank line.'''
223 if self.message:
223 if self.message:
224 subj = 'subject: ' + self.message[0].lower()
224 subj = 'subject: ' + self.message[0].lower()
225 for i in xrange(len(self.comments)):
225 for i in xrange(len(self.comments)):
226 if subj == self.comments[i].lower():
226 if subj == self.comments[i].lower():
227 del self.comments[i]
227 del self.comments[i]
228 self.message = self.message[2:]
228 self.message = self.message[2:]
229 break
229 break
230 ci = 0
230 ci = 0
231 for mi in self.message:
231 for mi in self.message:
232 while mi != self.comments[ci]:
232 while mi != self.comments[ci]:
233 ci += 1
233 ci += 1
234 del self.comments[ci]
234 del self.comments[ci]
235
235
236 class queue(object):
236 class queue(object):
237 def __init__(self, ui, path, patchdir=None):
237 def __init__(self, ui, path, patchdir=None):
238 self.basepath = path
238 self.basepath = path
239 try:
239 try:
240 fh = open(os.path.join(path, 'patches.queue'))
240 fh = open(os.path.join(path, 'patches.queue'))
241 cur = fh.read().rstrip()
241 cur = fh.read().rstrip()
242 if not cur:
242 if not cur:
243 curpath = os.path.join(path, 'patches')
243 curpath = os.path.join(path, 'patches')
244 else:
244 else:
245 curpath = os.path.join(path, 'patches-' + cur)
245 curpath = os.path.join(path, 'patches-' + cur)
246 except IOError:
246 except IOError:
247 curpath = os.path.join(path, 'patches')
247 curpath = os.path.join(path, 'patches')
248 self.path = patchdir or curpath
248 self.path = patchdir or curpath
249 self.opener = util.opener(self.path)
249 self.opener = util.opener(self.path)
250 self.ui = ui
250 self.ui = ui
251 self.applied_dirty = 0
251 self.applied_dirty = 0
252 self.series_dirty = 0
252 self.series_dirty = 0
253 self.added = []
253 self.added = []
254 self.series_path = "series"
254 self.series_path = "series"
255 self.status_path = "status"
255 self.status_path = "status"
256 self.guards_path = "guards"
256 self.guards_path = "guards"
257 self.active_guards = None
257 self.active_guards = None
258 self.guards_dirty = False
258 self.guards_dirty = False
259 # Handle mq.git as a bool with extended values
259 # Handle mq.git as a bool with extended values
260 try:
260 try:
261 gitmode = ui.configbool('mq', 'git', None)
261 gitmode = ui.configbool('mq', 'git', None)
262 if gitmode is None:
262 if gitmode is None:
263 raise error.ConfigError()
263 raise error.ConfigError()
264 self.gitmode = gitmode and 'yes' or 'no'
264 self.gitmode = gitmode and 'yes' or 'no'
265 except error.ConfigError:
265 except error.ConfigError:
266 self.gitmode = ui.config('mq', 'git', 'auto').lower()
266 self.gitmode = ui.config('mq', 'git', 'auto').lower()
267 self.plainmode = ui.configbool('mq', 'plain', False)
267 self.plainmode = ui.configbool('mq', 'plain', False)
268
268
269 @util.propertycache
269 @util.propertycache
270 def applied(self):
270 def applied(self):
271 if os.path.exists(self.join(self.status_path)):
271 if os.path.exists(self.join(self.status_path)):
272 def parse(l):
272 def parse(l):
273 n, name = l.split(':', 1)
273 n, name = l.split(':', 1)
274 return statusentry(bin(n), name)
274 return statusentry(bin(n), name)
275 lines = self.opener(self.status_path).read().splitlines()
275 lines = self.opener(self.status_path).read().splitlines()
276 return [parse(l) for l in lines]
276 return [parse(l) for l in lines]
277 return []
277 return []
278
278
279 @util.propertycache
279 @util.propertycache
280 def full_series(self):
280 def full_series(self):
281 if os.path.exists(self.join(self.series_path)):
281 if os.path.exists(self.join(self.series_path)):
282 return self.opener(self.series_path).read().splitlines()
282 return self.opener(self.series_path).read().splitlines()
283 return []
283 return []
284
284
285 @util.propertycache
285 @util.propertycache
286 def series(self):
286 def series(self):
287 self.parse_series()
287 self.parse_series()
288 return self.series
288 return self.series
289
289
290 @util.propertycache
290 @util.propertycache
291 def series_guards(self):
291 def series_guards(self):
292 self.parse_series()
292 self.parse_series()
293 return self.series_guards
293 return self.series_guards
294
294
295 def invalidate(self):
295 def invalidate(self):
296 for a in 'applied full_series series series_guards'.split():
296 for a in 'applied full_series series series_guards'.split():
297 if a in self.__dict__:
297 if a in self.__dict__:
298 delattr(self, a)
298 delattr(self, a)
299 self.applied_dirty = 0
299 self.applied_dirty = 0
300 self.series_dirty = 0
300 self.series_dirty = 0
301 self.guards_dirty = False
301 self.guards_dirty = False
302 self.active_guards = None
302 self.active_guards = None
303
303
304 def diffopts(self, opts={}, patchfn=None):
304 def diffopts(self, opts={}, patchfn=None):
305 diffopts = patch.diffopts(self.ui, opts)
305 diffopts = patch.diffopts(self.ui, opts)
306 if self.gitmode == 'auto':
306 if self.gitmode == 'auto':
307 diffopts.upgrade = True
307 diffopts.upgrade = True
308 elif self.gitmode == 'keep':
308 elif self.gitmode == 'keep':
309 pass
309 pass
310 elif self.gitmode in ('yes', 'no'):
310 elif self.gitmode in ('yes', 'no'):
311 diffopts.git = self.gitmode == 'yes'
311 diffopts.git = self.gitmode == 'yes'
312 else:
312 else:
313 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
313 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
314 ' got %s') % self.gitmode)
314 ' got %s') % self.gitmode)
315 if patchfn:
315 if patchfn:
316 diffopts = self.patchopts(diffopts, patchfn)
316 diffopts = self.patchopts(diffopts, patchfn)
317 return diffopts
317 return diffopts
318
318
319 def patchopts(self, diffopts, *patches):
319 def patchopts(self, diffopts, *patches):
320 """Return a copy of input diff options with git set to true if
320 """Return a copy of input diff options with git set to true if
321 referenced patch is a git patch and should be preserved as such.
321 referenced patch is a git patch and should be preserved as such.
322 """
322 """
323 diffopts = diffopts.copy()
323 diffopts = diffopts.copy()
324 if not diffopts.git and self.gitmode == 'keep':
324 if not diffopts.git and self.gitmode == 'keep':
325 for patchfn in patches:
325 for patchfn in patches:
326 patchf = self.opener(patchfn, 'r')
326 patchf = self.opener(patchfn, 'r')
327 # if the patch was a git patch, refresh it as a git patch
327 # if the patch was a git patch, refresh it as a git patch
328 for line in patchf:
328 for line in patchf:
329 if line.startswith('diff --git'):
329 if line.startswith('diff --git'):
330 diffopts.git = True
330 diffopts.git = True
331 break
331 break
332 patchf.close()
332 patchf.close()
333 return diffopts
333 return diffopts
334
334
335 def join(self, *p):
335 def join(self, *p):
336 return os.path.join(self.path, *p)
336 return os.path.join(self.path, *p)
337
337
338 def find_series(self, patch):
338 def find_series(self, patch):
339 def matchpatch(l):
339 def matchpatch(l):
340 l = l.split('#', 1)[0]
340 l = l.split('#', 1)[0]
341 return l.strip() == patch
341 return l.strip() == patch
342 for index, l in enumerate(self.full_series):
342 for index, l in enumerate(self.full_series):
343 if matchpatch(l):
343 if matchpatch(l):
344 return index
344 return index
345 return None
345 return None
346
346
347 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
347 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
348
348
349 def parse_series(self):
349 def parse_series(self):
350 self.series = []
350 self.series = []
351 self.series_guards = []
351 self.series_guards = []
352 for l in self.full_series:
352 for l in self.full_series:
353 h = l.find('#')
353 h = l.find('#')
354 if h == -1:
354 if h == -1:
355 patch = l
355 patch = l
356 comment = ''
356 comment = ''
357 elif h == 0:
357 elif h == 0:
358 continue
358 continue
359 else:
359 else:
360 patch = l[:h]
360 patch = l[:h]
361 comment = l[h:]
361 comment = l[h:]
362 patch = patch.strip()
362 patch = patch.strip()
363 if patch:
363 if patch:
364 if patch in self.series:
364 if patch in self.series:
365 raise util.Abort(_('%s appears more than once in %s') %
365 raise util.Abort(_('%s appears more than once in %s') %
366 (patch, self.join(self.series_path)))
366 (patch, self.join(self.series_path)))
367 self.series.append(patch)
367 self.series.append(patch)
368 self.series_guards.append(self.guard_re.findall(comment))
368 self.series_guards.append(self.guard_re.findall(comment))
369
369
370 def check_guard(self, guard):
370 def check_guard(self, guard):
371 if not guard:
371 if not guard:
372 return _('guard cannot be an empty string')
372 return _('guard cannot be an empty string')
373 bad_chars = '# \t\r\n\f'
373 bad_chars = '# \t\r\n\f'
374 first = guard[0]
374 first = guard[0]
375 if first in '-+':
375 if first in '-+':
376 return (_('guard %r starts with invalid character: %r') %
376 return (_('guard %r starts with invalid character: %r') %
377 (guard, first))
377 (guard, first))
378 for c in bad_chars:
378 for c in bad_chars:
379 if c in guard:
379 if c in guard:
380 return _('invalid character in guard %r: %r') % (guard, c)
380 return _('invalid character in guard %r: %r') % (guard, c)
381
381
382 def set_active(self, guards):
382 def set_active(self, guards):
383 for guard in guards:
383 for guard in guards:
384 bad = self.check_guard(guard)
384 bad = self.check_guard(guard)
385 if bad:
385 if bad:
386 raise util.Abort(bad)
386 raise util.Abort(bad)
387 guards = sorted(set(guards))
387 guards = sorted(set(guards))
388 self.ui.debug('active guards: %s\n' % ' '.join(guards))
388 self.ui.debug('active guards: %s\n' % ' '.join(guards))
389 self.active_guards = guards
389 self.active_guards = guards
390 self.guards_dirty = True
390 self.guards_dirty = True
391
391
392 def active(self):
392 def active(self):
393 if self.active_guards is None:
393 if self.active_guards is None:
394 self.active_guards = []
394 self.active_guards = []
395 try:
395 try:
396 guards = self.opener(self.guards_path).read().split()
396 guards = self.opener(self.guards_path).read().split()
397 except IOError, err:
397 except IOError, err:
398 if err.errno != errno.ENOENT:
398 if err.errno != errno.ENOENT:
399 raise
399 raise
400 guards = []
400 guards = []
401 for i, guard in enumerate(guards):
401 for i, guard in enumerate(guards):
402 bad = self.check_guard(guard)
402 bad = self.check_guard(guard)
403 if bad:
403 if bad:
404 self.ui.warn('%s:%d: %s\n' %
404 self.ui.warn('%s:%d: %s\n' %
405 (self.join(self.guards_path), i + 1, bad))
405 (self.join(self.guards_path), i + 1, bad))
406 else:
406 else:
407 self.active_guards.append(guard)
407 self.active_guards.append(guard)
408 return self.active_guards
408 return self.active_guards
409
409
410 def set_guards(self, idx, guards):
410 def set_guards(self, idx, guards):
411 for g in guards:
411 for g in guards:
412 if len(g) < 2:
412 if len(g) < 2:
413 raise util.Abort(_('guard %r too short') % g)
413 raise util.Abort(_('guard %r too short') % g)
414 if g[0] not in '-+':
414 if g[0] not in '-+':
415 raise util.Abort(_('guard %r starts with invalid char') % g)
415 raise util.Abort(_('guard %r starts with invalid char') % g)
416 bad = self.check_guard(g[1:])
416 bad = self.check_guard(g[1:])
417 if bad:
417 if bad:
418 raise util.Abort(bad)
418 raise util.Abort(bad)
419 drop = self.guard_re.sub('', self.full_series[idx])
419 drop = self.guard_re.sub('', self.full_series[idx])
420 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
420 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
421 self.parse_series()
421 self.parse_series()
422 self.series_dirty = True
422 self.series_dirty = True
423
423
424 def pushable(self, idx):
424 def pushable(self, idx):
425 if isinstance(idx, str):
425 if isinstance(idx, str):
426 idx = self.series.index(idx)
426 idx = self.series.index(idx)
427 patchguards = self.series_guards[idx]
427 patchguards = self.series_guards[idx]
428 if not patchguards:
428 if not patchguards:
429 return True, None
429 return True, None
430 guards = self.active()
430 guards = self.active()
431 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
431 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
432 if exactneg:
432 if exactneg:
433 return False, exactneg[0]
433 return False, exactneg[0]
434 pos = [g for g in patchguards if g[0] == '+']
434 pos = [g for g in patchguards if g[0] == '+']
435 exactpos = [g for g in pos if g[1:] in guards]
435 exactpos = [g for g in pos if g[1:] in guards]
436 if pos:
436 if pos:
437 if exactpos:
437 if exactpos:
438 return True, exactpos[0]
438 return True, exactpos[0]
439 return False, pos
439 return False, pos
440 return True, ''
440 return True, ''
441
441
442 def explain_pushable(self, idx, all_patches=False):
442 def explain_pushable(self, idx, all_patches=False):
443 write = all_patches and self.ui.write or self.ui.warn
443 write = all_patches and self.ui.write or self.ui.warn
444 if all_patches or self.ui.verbose:
444 if all_patches or self.ui.verbose:
445 if isinstance(idx, str):
445 if isinstance(idx, str):
446 idx = self.series.index(idx)
446 idx = self.series.index(idx)
447 pushable, why = self.pushable(idx)
447 pushable, why = self.pushable(idx)
448 if all_patches and pushable:
448 if all_patches and pushable:
449 if why is None:
449 if why is None:
450 write(_('allowing %s - no guards in effect\n') %
450 write(_('allowing %s - no guards in effect\n') %
451 self.series[idx])
451 self.series[idx])
452 else:
452 else:
453 if not why:
453 if not why:
454 write(_('allowing %s - no matching negative guards\n') %
454 write(_('allowing %s - no matching negative guards\n') %
455 self.series[idx])
455 self.series[idx])
456 else:
456 else:
457 write(_('allowing %s - guarded by %r\n') %
457 write(_('allowing %s - guarded by %r\n') %
458 (self.series[idx], why))
458 (self.series[idx], why))
459 if not pushable:
459 if not pushable:
460 if why:
460 if why:
461 write(_('skipping %s - guarded by %r\n') %
461 write(_('skipping %s - guarded by %r\n') %
462 (self.series[idx], why))
462 (self.series[idx], why))
463 else:
463 else:
464 write(_('skipping %s - no matching guards\n') %
464 write(_('skipping %s - no matching guards\n') %
465 self.series[idx])
465 self.series[idx])
466
466
467 def save_dirty(self):
467 def save_dirty(self):
468 def write_list(items, path):
468 def write_list(items, path):
469 fp = self.opener(path, 'w')
469 fp = self.opener(path, 'w')
470 for i in items:
470 for i in items:
471 fp.write("%s\n" % i)
471 fp.write("%s\n" % i)
472 fp.close()
472 fp.close()
473 if self.applied_dirty:
473 if self.applied_dirty:
474 write_list(map(str, self.applied), self.status_path)
474 write_list(map(str, self.applied), self.status_path)
475 if self.series_dirty:
475 if self.series_dirty:
476 write_list(self.full_series, self.series_path)
476 write_list(self.full_series, self.series_path)
477 if self.guards_dirty:
477 if self.guards_dirty:
478 write_list(self.active_guards, self.guards_path)
478 write_list(self.active_guards, self.guards_path)
479 if self.added:
479 if self.added:
480 qrepo = self.qrepo()
480 qrepo = self.qrepo()
481 if qrepo:
481 if qrepo:
482 qrepo[None].add(self.added)
482 qrepo[None].add(self.added)
483 self.added = []
483 self.added = []
484
484
485 def removeundo(self, repo):
485 def removeundo(self, repo):
486 undo = repo.sjoin('undo')
486 undo = repo.sjoin('undo')
487 if not os.path.exists(undo):
487 if not os.path.exists(undo):
488 return
488 return
489 try:
489 try:
490 os.unlink(undo)
490 os.unlink(undo)
491 except OSError, inst:
491 except OSError, inst:
492 self.ui.warn(_('error removing undo: %s\n') % str(inst))
492 self.ui.warn(_('error removing undo: %s\n') % str(inst))
493
493
494 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
494 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
495 fp=None, changes=None, opts={}):
495 fp=None, changes=None, opts={}):
496 stat = opts.get('stat')
496 stat = opts.get('stat')
497 m = cmdutil.match(repo, files, opts)
497 m = cmdutil.match(repo, files, opts)
498 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
498 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
499 changes, stat, fp)
499 changes, stat, fp)
500
500
501 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
501 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
502 # first try just applying the patch
502 # first try just applying the patch
503 (err, n) = self.apply(repo, [patch], update_status=False,
503 (err, n) = self.apply(repo, [patch], update_status=False,
504 strict=True, merge=rev)
504 strict=True, merge=rev)
505
505
506 if err == 0:
506 if err == 0:
507 return (err, n)
507 return (err, n)
508
508
509 if n is None:
509 if n is None:
510 raise util.Abort(_("apply failed for patch %s") % patch)
510 raise util.Abort(_("apply failed for patch %s") % patch)
511
511
512 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
512 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
513
513
514 # apply failed, strip away that rev and merge.
514 # apply failed, strip away that rev and merge.
515 hg.clean(repo, head)
515 hg.clean(repo, head)
516 self.strip(repo, n, update=False, backup='strip')
516 self.strip(repo, n, update=False, backup='strip')
517
517
518 ctx = repo[rev]
518 ctx = repo[rev]
519 ret = hg.merge(repo, rev)
519 ret = hg.merge(repo, rev)
520 if ret:
520 if ret:
521 raise util.Abort(_("update returned %d") % ret)
521 raise util.Abort(_("update returned %d") % ret)
522 n = repo.commit(ctx.description(), ctx.user(), force=True)
522 n = repo.commit(ctx.description(), ctx.user(), force=True)
523 if n is None:
523 if n is None:
524 raise util.Abort(_("repo commit failed"))
524 raise util.Abort(_("repo commit failed"))
525 try:
525 try:
526 ph = patchheader(mergeq.join(patch), self.plainmode)
526 ph = patchheader(mergeq.join(patch), self.plainmode)
527 except:
527 except:
528 raise util.Abort(_("unable to read %s") % patch)
528 raise util.Abort(_("unable to read %s") % patch)
529
529
530 diffopts = self.patchopts(diffopts, patch)
530 diffopts = self.patchopts(diffopts, patch)
531 patchf = self.opener(patch, "w")
531 patchf = self.opener(patch, "w")
532 comments = str(ph)
532 comments = str(ph)
533 if comments:
533 if comments:
534 patchf.write(comments)
534 patchf.write(comments)
535 self.printdiff(repo, diffopts, head, n, fp=patchf)
535 self.printdiff(repo, diffopts, head, n, fp=patchf)
536 patchf.close()
536 patchf.close()
537 self.removeundo(repo)
537 self.removeundo(repo)
538 return (0, n)
538 return (0, n)
539
539
540 def qparents(self, repo, rev=None):
540 def qparents(self, repo, rev=None):
541 if rev is None:
541 if rev is None:
542 (p1, p2) = repo.dirstate.parents()
542 (p1, p2) = repo.dirstate.parents()
543 if p2 == nullid:
543 if p2 == nullid:
544 return p1
544 return p1
545 if not self.applied:
545 if not self.applied:
546 return None
546 return None
547 return self.applied[-1].node
547 return self.applied[-1].node
548 p1, p2 = repo.changelog.parents(rev)
548 p1, p2 = repo.changelog.parents(rev)
549 if p2 != nullid and p2 in [x.node for x in self.applied]:
549 if p2 != nullid and p2 in [x.node for x in self.applied]:
550 return p2
550 return p2
551 return p1
551 return p1
552
552
553 def mergepatch(self, repo, mergeq, series, diffopts):
553 def mergepatch(self, repo, mergeq, series, diffopts):
554 if not self.applied:
554 if not self.applied:
555 # each of the patches merged in will have two parents. This
555 # each of the patches merged in will have two parents. This
556 # can confuse the qrefresh, qdiff, and strip code because it
556 # can confuse the qrefresh, qdiff, and strip code because it
557 # needs to know which parent is actually in the patch queue.
557 # needs to know which parent is actually in the patch queue.
558 # so, we insert a merge marker with only one parent. This way
558 # so, we insert a merge marker with only one parent. This way
559 # the first patch in the queue is never a merge patch
559 # the first patch in the queue is never a merge patch
560 #
560 #
561 pname = ".hg.patches.merge.marker"
561 pname = ".hg.patches.merge.marker"
562 n = repo.commit('[mq]: merge marker', force=True)
562 n = repo.commit('[mq]: merge marker', force=True)
563 self.removeundo(repo)
563 self.removeundo(repo)
564 self.applied.append(statusentry(n, pname))
564 self.applied.append(statusentry(n, pname))
565 self.applied_dirty = 1
565 self.applied_dirty = 1
566
566
567 head = self.qparents(repo)
567 head = self.qparents(repo)
568
568
569 for patch in series:
569 for patch in series:
570 patch = mergeq.lookup(patch, strict=True)
570 patch = mergeq.lookup(patch, strict=True)
571 if not patch:
571 if not patch:
572 self.ui.warn(_("patch %s does not exist\n") % patch)
572 self.ui.warn(_("patch %s does not exist\n") % patch)
573 return (1, None)
573 return (1, None)
574 pushable, reason = self.pushable(patch)
574 pushable, reason = self.pushable(patch)
575 if not pushable:
575 if not pushable:
576 self.explain_pushable(patch, all_patches=True)
576 self.explain_pushable(patch, all_patches=True)
577 continue
577 continue
578 info = mergeq.isapplied(patch)
578 info = mergeq.isapplied(patch)
579 if not info:
579 if not info:
580 self.ui.warn(_("patch %s is not applied\n") % patch)
580 self.ui.warn(_("patch %s is not applied\n") % patch)
581 return (1, None)
581 return (1, None)
582 rev = info[1]
582 rev = info[1]
583 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
583 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
584 if head:
584 if head:
585 self.applied.append(statusentry(head, patch))
585 self.applied.append(statusentry(head, patch))
586 self.applied_dirty = 1
586 self.applied_dirty = 1
587 if err:
587 if err:
588 return (err, head)
588 return (err, head)
589 self.save_dirty()
589 self.save_dirty()
590 return (0, head)
590 return (0, head)
591
591
592 def patch(self, repo, patchfile):
592 def patch(self, repo, patchfile):
593 '''Apply patchfile to the working directory.
593 '''Apply patchfile to the working directory.
594 patchfile: name of patch file'''
594 patchfile: name of patch file'''
595 files = {}
595 files = {}
596 try:
596 try:
597 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
597 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
598 files=files, eolmode=None)
598 files=files, eolmode=None)
599 except Exception, inst:
599 except Exception, inst:
600 self.ui.note(str(inst) + '\n')
600 self.ui.note(str(inst) + '\n')
601 if not self.ui.verbose:
601 if not self.ui.verbose:
602 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
602 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
603 return (False, files, False)
603 return (False, files, False)
604
604
605 return (True, files, fuzz)
605 return (True, files, fuzz)
606
606
607 def apply(self, repo, series, list=False, update_status=True,
607 def apply(self, repo, series, list=False, update_status=True,
608 strict=False, patchdir=None, merge=None, all_files=None):
608 strict=False, patchdir=None, merge=None, all_files=None):
609 wlock = lock = tr = None
609 wlock = lock = tr = None
610 try:
610 try:
611 wlock = repo.wlock()
611 wlock = repo.wlock()
612 lock = repo.lock()
612 lock = repo.lock()
613 tr = repo.transaction("qpush")
613 tr = repo.transaction("qpush")
614 try:
614 try:
615 ret = self._apply(repo, series, list, update_status,
615 ret = self._apply(repo, series, list, update_status,
616 strict, patchdir, merge, all_files=all_files)
616 strict, patchdir, merge, all_files=all_files)
617 tr.close()
617 tr.close()
618 self.save_dirty()
618 self.save_dirty()
619 return ret
619 return ret
620 except:
620 except:
621 try:
621 try:
622 tr.abort()
622 tr.abort()
623 finally:
623 finally:
624 repo.invalidate()
624 repo.invalidate()
625 repo.dirstate.invalidate()
625 repo.dirstate.invalidate()
626 raise
626 raise
627 finally:
627 finally:
628 release(tr, lock, wlock)
628 release(tr, lock, wlock)
629 self.removeundo(repo)
629 self.removeundo(repo)
630
630
631 def _apply(self, repo, series, list=False, update_status=True,
631 def _apply(self, repo, series, list=False, update_status=True,
632 strict=False, patchdir=None, merge=None, all_files=None):
632 strict=False, patchdir=None, merge=None, all_files=None):
633 '''returns (error, hash)
633 '''returns (error, hash)
634 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
634 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
635 # TODO unify with commands.py
635 # TODO unify with commands.py
636 if not patchdir:
636 if not patchdir:
637 patchdir = self.path
637 patchdir = self.path
638 err = 0
638 err = 0
639 n = None
639 n = None
640 for patchname in series:
640 for patchname in series:
641 pushable, reason = self.pushable(patchname)
641 pushable, reason = self.pushable(patchname)
642 if not pushable:
642 if not pushable:
643 self.explain_pushable(patchname, all_patches=True)
643 self.explain_pushable(patchname, all_patches=True)
644 continue
644 continue
645 self.ui.status(_("applying %s\n") % patchname)
645 self.ui.status(_("applying %s\n") % patchname)
646 pf = os.path.join(patchdir, patchname)
646 pf = os.path.join(patchdir, patchname)
647
647
648 try:
648 try:
649 ph = patchheader(self.join(patchname), self.plainmode)
649 ph = patchheader(self.join(patchname), self.plainmode)
650 except:
650 except:
651 self.ui.warn(_("unable to read %s\n") % patchname)
651 self.ui.warn(_("unable to read %s\n") % patchname)
652 err = 1
652 err = 1
653 break
653 break
654
654
655 message = ph.message
655 message = ph.message
656 if not message:
656 if not message:
657 message = "imported patch %s\n" % patchname
657 message = "imported patch %s\n" % patchname
658 else:
658 else:
659 if list:
659 if list:
660 message.append("\nimported patch %s" % patchname)
660 message.append("\nimported patch %s" % patchname)
661 message = '\n'.join(message)
661 message = '\n'.join(message)
662
662
663 if ph.haspatch:
663 if ph.haspatch:
664 (patcherr, files, fuzz) = self.patch(repo, pf)
664 (patcherr, files, fuzz) = self.patch(repo, pf)
665 if all_files is not None:
665 if all_files is not None:
666 all_files.update(files)
666 all_files.update(files)
667 patcherr = not patcherr
667 patcherr = not patcherr
668 else:
668 else:
669 self.ui.warn(_("patch %s is empty\n") % patchname)
669 self.ui.warn(_("patch %s is empty\n") % patchname)
670 patcherr, files, fuzz = 0, [], 0
670 patcherr, files, fuzz = 0, [], 0
671
671
672 if merge and files:
672 if merge and files:
673 # Mark as removed/merged and update dirstate parent info
673 # Mark as removed/merged and update dirstate parent info
674 removed = []
674 removed = []
675 merged = []
675 merged = []
676 for f in files:
676 for f in files:
677 if os.path.exists(repo.wjoin(f)):
677 if os.path.exists(repo.wjoin(f)):
678 merged.append(f)
678 merged.append(f)
679 else:
679 else:
680 removed.append(f)
680 removed.append(f)
681 for f in removed:
681 for f in removed:
682 repo.dirstate.remove(f)
682 repo.dirstate.remove(f)
683 for f in merged:
683 for f in merged:
684 repo.dirstate.merge(f)
684 repo.dirstate.merge(f)
685 p1, p2 = repo.dirstate.parents()
685 p1, p2 = repo.dirstate.parents()
686 repo.dirstate.setparents(p1, merge)
686 repo.dirstate.setparents(p1, merge)
687
687
688 files = patch.updatedir(self.ui, repo, files)
688 files = patch.updatedir(self.ui, repo, files)
689 match = cmdutil.matchfiles(repo, files or [])
689 match = cmdutil.matchfiles(repo, files or [])
690 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
690 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
691
691
692 if n is None:
692 if n is None:
693 raise util.Abort(_("repo commit failed"))
693 raise util.Abort(_("repo commit failed"))
694
694
695 if update_status:
695 if update_status:
696 self.applied.append(statusentry(n, patchname))
696 self.applied.append(statusentry(n, patchname))
697
697
698 if patcherr:
698 if patcherr:
699 self.ui.warn(_("patch failed, rejects left in working dir\n"))
699 self.ui.warn(_("patch failed, rejects left in working dir\n"))
700 err = 2
700 err = 2
701 break
701 break
702
702
703 if fuzz and strict:
703 if fuzz and strict:
704 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
704 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
705 err = 3
705 err = 3
706 break
706 break
707 return (err, n)
707 return (err, n)
708
708
709 def _cleanup(self, patches, numrevs, keep=False):
709 def _cleanup(self, patches, numrevs, keep=False):
710 if not keep:
710 if not keep:
711 r = self.qrepo()
711 r = self.qrepo()
712 if r:
712 if r:
713 r[None].remove(patches, True)
713 r[None].remove(patches, True)
714 else:
714 else:
715 for p in patches:
715 for p in patches:
716 os.unlink(self.join(p))
716 os.unlink(self.join(p))
717
717
718 if numrevs:
718 if numrevs:
719 del self.applied[:numrevs]
719 del self.applied[:numrevs]
720 self.applied_dirty = 1
720 self.applied_dirty = 1
721
721
722 for i in sorted([self.find_series(p) for p in patches], reverse=True):
722 for i in sorted([self.find_series(p) for p in patches], reverse=True):
723 del self.full_series[i]
723 del self.full_series[i]
724 self.parse_series()
724 self.parse_series()
725 self.series_dirty = 1
725 self.series_dirty = 1
726
726
727 def _revpatches(self, repo, revs):
727 def _revpatches(self, repo, revs):
728 firstrev = repo[self.applied[0].node].rev()
728 firstrev = repo[self.applied[0].node].rev()
729 patches = []
729 patches = []
730 for i, rev in enumerate(revs):
730 for i, rev in enumerate(revs):
731
731
732 if rev < firstrev:
732 if rev < firstrev:
733 raise util.Abort(_('revision %d is not managed') % rev)
733 raise util.Abort(_('revision %d is not managed') % rev)
734
734
735 ctx = repo[rev]
735 ctx = repo[rev]
736 base = self.applied[i].node
736 base = self.applied[i].node
737 if ctx.node() != base:
737 if ctx.node() != base:
738 msg = _('cannot delete revision %d above applied patches')
738 msg = _('cannot delete revision %d above applied patches')
739 raise util.Abort(msg % rev)
739 raise util.Abort(msg % rev)
740
740
741 patch = self.applied[i].name
741 patch = self.applied[i].name
742 for fmt in ('[mq]: %s', 'imported patch %s'):
742 for fmt in ('[mq]: %s', 'imported patch %s'):
743 if ctx.description() == fmt % patch:
743 if ctx.description() == fmt % patch:
744 msg = _('patch %s finalized without changeset message\n')
744 msg = _('patch %s finalized without changeset message\n')
745 repo.ui.status(msg % patch)
745 repo.ui.status(msg % patch)
746 break
746 break
747
747
748 patches.append(patch)
748 patches.append(patch)
749 return patches
749 return patches
750
750
751 def finish(self, repo, revs):
751 def finish(self, repo, revs):
752 patches = self._revpatches(repo, sorted(revs))
752 patches = self._revpatches(repo, sorted(revs))
753 self._cleanup(patches, len(patches))
753 self._cleanup(patches, len(patches))
754
754
755 def delete(self, repo, patches, opts):
755 def delete(self, repo, patches, opts):
756 if not patches and not opts.get('rev'):
756 if not patches and not opts.get('rev'):
757 raise util.Abort(_('qdelete requires at least one revision or '
757 raise util.Abort(_('qdelete requires at least one revision or '
758 'patch name'))
758 'patch name'))
759
759
760 realpatches = []
760 realpatches = []
761 for patch in patches:
761 for patch in patches:
762 patch = self.lookup(patch, strict=True)
762 patch = self.lookup(patch, strict=True)
763 info = self.isapplied(patch)
763 info = self.isapplied(patch)
764 if info:
764 if info:
765 raise util.Abort(_("cannot delete applied patch %s") % patch)
765 raise util.Abort(_("cannot delete applied patch %s") % patch)
766 if patch not in self.series:
766 if patch not in self.series:
767 raise util.Abort(_("patch %s not in series file") % patch)
767 raise util.Abort(_("patch %s not in series file") % patch)
768 realpatches.append(patch)
768 realpatches.append(patch)
769
769
770 numrevs = 0
770 numrevs = 0
771 if opts.get('rev'):
771 if opts.get('rev'):
772 if not self.applied:
772 if not self.applied:
773 raise util.Abort(_('no patches applied'))
773 raise util.Abort(_('no patches applied'))
774 revs = cmdutil.revrange(repo, opts['rev'])
774 revs = cmdutil.revrange(repo, opts['rev'])
775 if len(revs) > 1 and revs[0] > revs[1]:
775 if len(revs) > 1 and revs[0] > revs[1]:
776 revs.reverse()
776 revs.reverse()
777 revpatches = self._revpatches(repo, revs)
777 revpatches = self._revpatches(repo, revs)
778 realpatches += revpatches
778 realpatches += revpatches
779 numrevs = len(revpatches)
779 numrevs = len(revpatches)
780
780
781 self._cleanup(realpatches, numrevs, opts.get('keep'))
781 self._cleanup(realpatches, numrevs, opts.get('keep'))
782
782
783 def check_toppatch(self, repo):
783 def check_toppatch(self, repo):
784 if self.applied:
784 if self.applied:
785 top = self.applied[-1].node
785 top = self.applied[-1].node
786 patch = self.applied[-1].name
786 patch = self.applied[-1].name
787 pp = repo.dirstate.parents()
787 pp = repo.dirstate.parents()
788 if top not in pp:
788 if top not in pp:
789 raise util.Abort(_("working directory revision is not qtip"))
789 raise util.Abort(_("working directory revision is not qtip"))
790 return top, patch
790 return top, patch
791 return None, None
791 return None, None
792
792
793 def check_localchanges(self, repo, force=False, refresh=True):
793 def check_localchanges(self, repo, force=False, refresh=True):
794 m, a, r, d = repo.status()[:4]
794 m, a, r, d = repo.status()[:4]
795 if (m or a or r or d) and not force:
795 if (m or a or r or d) and not force:
796 if refresh:
796 if refresh:
797 raise util.Abort(_("local changes found, refresh first"))
797 raise util.Abort(_("local changes found, refresh first"))
798 else:
798 else:
799 raise util.Abort(_("local changes found"))
799 raise util.Abort(_("local changes found"))
800 return m, a, r, d
800 return m, a, r, d
801
801
802 _reserved = ('series', 'status', 'guards')
802 _reserved = ('series', 'status', 'guards')
803 def check_reserved_name(self, name):
803 def check_reserved_name(self, name):
804 if (name in self._reserved or name.startswith('.hg')
804 if (name in self._reserved or name.startswith('.hg')
805 or name.startswith('.mq') or '#' in name or ':' in name):
805 or name.startswith('.mq') or '#' in name or ':' in name):
806 raise util.Abort(_('"%s" cannot be used as the name of a patch')
806 raise util.Abort(_('"%s" cannot be used as the name of a patch')
807 % name)
807 % name)
808
808
809 def new(self, repo, patchfn, *pats, **opts):
809 def new(self, repo, patchfn, *pats, **opts):
810 """options:
810 """options:
811 msg: a string or a no-argument function returning a string
811 msg: a string or a no-argument function returning a string
812 """
812 """
813 msg = opts.get('msg')
813 msg = opts.get('msg')
814 user = opts.get('user')
814 user = opts.get('user')
815 date = opts.get('date')
815 date = opts.get('date')
816 if date:
816 if date:
817 date = util.parsedate(date)
817 date = util.parsedate(date)
818 diffopts = self.diffopts({'git': opts.get('git')})
818 diffopts = self.diffopts({'git': opts.get('git')})
819 self.check_reserved_name(patchfn)
819 self.check_reserved_name(patchfn)
820 if os.path.exists(self.join(patchfn)):
820 if os.path.exists(self.join(patchfn)):
821 raise util.Abort(_('patch "%s" already exists') % patchfn)
821 raise util.Abort(_('patch "%s" already exists') % patchfn)
822 if opts.get('include') or opts.get('exclude') or pats:
822 if opts.get('include') or opts.get('exclude') or pats:
823 match = cmdutil.match(repo, pats, opts)
823 match = cmdutil.match(repo, pats, opts)
824 # detect missing files in pats
824 # detect missing files in pats
825 def badfn(f, msg):
825 def badfn(f, msg):
826 raise util.Abort('%s: %s' % (f, msg))
826 raise util.Abort('%s: %s' % (f, msg))
827 match.bad = badfn
827 match.bad = badfn
828 m, a, r, d = repo.status(match=match)[:4]
828 m, a, r, d = repo.status(match=match)[:4]
829 else:
829 else:
830 m, a, r, d = self.check_localchanges(repo, force=True)
830 m, a, r, d = self.check_localchanges(repo, force=True)
831 match = cmdutil.matchfiles(repo, m + a + r)
831 match = cmdutil.matchfiles(repo, m + a + r)
832 if len(repo[None].parents()) > 1:
832 if len(repo[None].parents()) > 1:
833 raise util.Abort(_('cannot manage merge changesets'))
833 raise util.Abort(_('cannot manage merge changesets'))
834 commitfiles = m + a + r
834 commitfiles = m + a + r
835 self.check_toppatch(repo)
835 self.check_toppatch(repo)
836 insert = self.full_series_end()
836 insert = self.full_series_end()
837 wlock = repo.wlock()
837 wlock = repo.wlock()
838 try:
838 try:
839 # if patch file write fails, abort early
839 # if patch file write fails, abort early
840 p = self.opener(patchfn, "w")
840 p = self.opener(patchfn, "w")
841 try:
841 try:
842 if self.plainmode:
842 if self.plainmode:
843 if user:
843 if user:
844 p.write("From: " + user + "\n")
844 p.write("From: " + user + "\n")
845 if not date:
845 if not date:
846 p.write("\n")
846 p.write("\n")
847 if date:
847 if date:
848 p.write("Date: %d %d\n\n" % date)
848 p.write("Date: %d %d\n\n" % date)
849 else:
849 else:
850 p.write("# HG changeset patch\n")
850 p.write("# HG changeset patch\n")
851 p.write("# Parent "
851 p.write("# Parent "
852 + hex(repo[None].parents()[0].node()) + "\n")
852 + hex(repo[None].parents()[0].node()) + "\n")
853 if user:
853 if user:
854 p.write("# User " + user + "\n")
854 p.write("# User " + user + "\n")
855 if date:
855 if date:
856 p.write("# Date %s %s\n\n" % date)
856 p.write("# Date %s %s\n\n" % date)
857 if hasattr(msg, '__call__'):
857 if hasattr(msg, '__call__'):
858 msg = msg()
858 msg = msg()
859 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
859 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
860 n = repo.commit(commitmsg, user, date, match=match, force=True)
860 n = repo.commit(commitmsg, user, date, match=match, force=True)
861 if n is None:
861 if n is None:
862 raise util.Abort(_("repo commit failed"))
862 raise util.Abort(_("repo commit failed"))
863 try:
863 try:
864 self.full_series[insert:insert] = [patchfn]
864 self.full_series[insert:insert] = [patchfn]
865 self.applied.append(statusentry(n, patchfn))
865 self.applied.append(statusentry(n, patchfn))
866 self.parse_series()
866 self.parse_series()
867 self.series_dirty = 1
867 self.series_dirty = 1
868 self.applied_dirty = 1
868 self.applied_dirty = 1
869 if msg:
869 if msg:
870 msg = msg + "\n\n"
870 msg = msg + "\n\n"
871 p.write(msg)
871 p.write(msg)
872 if commitfiles:
872 if commitfiles:
873 parent = self.qparents(repo, n)
873 parent = self.qparents(repo, n)
874 chunks = patch.diff(repo, node1=parent, node2=n,
874 chunks = patch.diff(repo, node1=parent, node2=n,
875 match=match, opts=diffopts)
875 match=match, opts=diffopts)
876 for chunk in chunks:
876 for chunk in chunks:
877 p.write(chunk)
877 p.write(chunk)
878 p.close()
878 p.close()
879 wlock.release()
879 wlock.release()
880 wlock = None
880 wlock = None
881 r = self.qrepo()
881 r = self.qrepo()
882 if r:
882 if r:
883 r[None].add([patchfn])
883 r[None].add([patchfn])
884 except:
884 except:
885 repo.rollback()
885 repo.rollback()
886 raise
886 raise
887 except Exception:
887 except Exception:
888 patchpath = self.join(patchfn)
888 patchpath = self.join(patchfn)
889 try:
889 try:
890 os.unlink(patchpath)
890 os.unlink(patchpath)
891 except:
891 except:
892 self.ui.warn(_('error unlinking %s\n') % patchpath)
892 self.ui.warn(_('error unlinking %s\n') % patchpath)
893 raise
893 raise
894 self.removeundo(repo)
894 self.removeundo(repo)
895 finally:
895 finally:
896 release(wlock)
896 release(wlock)
897
897
898 def strip(self, repo, rev, update=True, backup="all", force=None):
898 def strip(self, repo, rev, update=True, backup="all", force=None):
899 wlock = lock = None
899 wlock = lock = None
900 try:
900 try:
901 wlock = repo.wlock()
901 wlock = repo.wlock()
902 lock = repo.lock()
902 lock = repo.lock()
903
903
904 if update:
904 if update:
905 self.check_localchanges(repo, force=force, refresh=False)
905 self.check_localchanges(repo, force=force, refresh=False)
906 urev = self.qparents(repo, rev)
906 urev = self.qparents(repo, rev)
907 hg.clean(repo, urev)
907 hg.clean(repo, urev)
908 repo.dirstate.write()
908 repo.dirstate.write()
909
909
910 self.removeundo(repo)
910 self.removeundo(repo)
911 repair.strip(self.ui, repo, rev, backup)
911 repair.strip(self.ui, repo, rev, backup)
912 # strip may have unbundled a set of backed up revisions after
912 # strip may have unbundled a set of backed up revisions after
913 # the actual strip
913 # the actual strip
914 self.removeundo(repo)
914 self.removeundo(repo)
915 finally:
915 finally:
916 release(lock, wlock)
916 release(lock, wlock)
917
917
918 def isapplied(self, patch):
918 def isapplied(self, patch):
919 """returns (index, rev, patch)"""
919 """returns (index, rev, patch)"""
920 for i, a in enumerate(self.applied):
920 for i, a in enumerate(self.applied):
921 if a.name == patch:
921 if a.name == patch:
922 return (i, a.node, a.name)
922 return (i, a.node, a.name)
923 return None
923 return None
924
924
925 # if the exact patch name does not exist, we try a few
925 # if the exact patch name does not exist, we try a few
926 # variations. If strict is passed, we try only #1
926 # variations. If strict is passed, we try only #1
927 #
927 #
928 # 1) a number to indicate an offset in the series file
928 # 1) a number to indicate an offset in the series file
929 # 2) a unique substring of the patch name was given
929 # 2) a unique substring of the patch name was given
930 # 3) patchname[-+]num to indicate an offset in the series file
930 # 3) patchname[-+]num to indicate an offset in the series file
931 def lookup(self, patch, strict=False):
931 def lookup(self, patch, strict=False):
932 patch = patch and str(patch)
932 patch = patch and str(patch)
933
933
934 def partial_name(s):
934 def partial_name(s):
935 if s in self.series:
935 if s in self.series:
936 return s
936 return s
937 matches = [x for x in self.series if s in x]
937 matches = [x for x in self.series if s in x]
938 if len(matches) > 1:
938 if len(matches) > 1:
939 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
939 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
940 for m in matches:
940 for m in matches:
941 self.ui.warn(' %s\n' % m)
941 self.ui.warn(' %s\n' % m)
942 return None
942 return None
943 if matches:
943 if matches:
944 return matches[0]
944 return matches[0]
945 if self.series and self.applied:
945 if self.series and self.applied:
946 if s == 'qtip':
946 if s == 'qtip':
947 return self.series[self.series_end(True)-1]
947 return self.series[self.series_end(True)-1]
948 if s == 'qbase':
948 if s == 'qbase':
949 return self.series[0]
949 return self.series[0]
950 return None
950 return None
951
951
952 if patch is None:
952 if patch is None:
953 return None
953 return None
954 if patch in self.series:
954 if patch in self.series:
955 return patch
955 return patch
956
956
957 if not os.path.isfile(self.join(patch)):
957 if not os.path.isfile(self.join(patch)):
958 try:
958 try:
959 sno = int(patch)
959 sno = int(patch)
960 except (ValueError, OverflowError):
960 except (ValueError, OverflowError):
961 pass
961 pass
962 else:
962 else:
963 if -len(self.series) <= sno < len(self.series):
963 if -len(self.series) <= sno < len(self.series):
964 return self.series[sno]
964 return self.series[sno]
965
965
966 if not strict:
966 if not strict:
967 res = partial_name(patch)
967 res = partial_name(patch)
968 if res:
968 if res:
969 return res
969 return res
970 minus = patch.rfind('-')
970 minus = patch.rfind('-')
971 if minus >= 0:
971 if minus >= 0:
972 res = partial_name(patch[:minus])
972 res = partial_name(patch[:minus])
973 if res:
973 if res:
974 i = self.series.index(res)
974 i = self.series.index(res)
975 try:
975 try:
976 off = int(patch[minus + 1:] or 1)
976 off = int(patch[minus + 1:] or 1)
977 except (ValueError, OverflowError):
977 except (ValueError, OverflowError):
978 pass
978 pass
979 else:
979 else:
980 if i - off >= 0:
980 if i - off >= 0:
981 return self.series[i - off]
981 return self.series[i - off]
982 plus = patch.rfind('+')
982 plus = patch.rfind('+')
983 if plus >= 0:
983 if plus >= 0:
984 res = partial_name(patch[:plus])
984 res = partial_name(patch[:plus])
985 if res:
985 if res:
986 i = self.series.index(res)
986 i = self.series.index(res)
987 try:
987 try:
988 off = int(patch[plus + 1:] or 1)
988 off = int(patch[plus + 1:] or 1)
989 except (ValueError, OverflowError):
989 except (ValueError, OverflowError):
990 pass
990 pass
991 else:
991 else:
992 if i + off < len(self.series):
992 if i + off < len(self.series):
993 return self.series[i + off]
993 return self.series[i + off]
994 raise util.Abort(_("patch %s not in series") % patch)
994 raise util.Abort(_("patch %s not in series") % patch)
995
995
996 def push(self, repo, patch=None, force=False, list=False,
996 def push(self, repo, patch=None, force=False, list=False,
997 mergeq=None, all=False, move=False):
997 mergeq=None, all=False, move=False):
998 diffopts = self.diffopts()
998 diffopts = self.diffopts()
999 wlock = repo.wlock()
999 wlock = repo.wlock()
1000 try:
1000 try:
1001 heads = []
1001 heads = []
1002 for b, ls in repo.branchmap().iteritems():
1002 for b, ls in repo.branchmap().iteritems():
1003 heads += ls
1003 heads += ls
1004 if not heads:
1004 if not heads:
1005 heads = [nullid]
1005 heads = [nullid]
1006 if repo.dirstate.parents()[0] not in heads:
1006 if repo.dirstate.parents()[0] not in heads:
1007 self.ui.status(_("(working directory not at a head)\n"))
1007 self.ui.status(_("(working directory not at a head)\n"))
1008
1008
1009 if not self.series:
1009 if not self.series:
1010 self.ui.warn(_('no patches in series\n'))
1010 self.ui.warn(_('no patches in series\n'))
1011 return 0
1011 return 0
1012
1012
1013 patch = self.lookup(patch)
1013 patch = self.lookup(patch)
1014 # Suppose our series file is: A B C and the current 'top'
1014 # Suppose our series file is: A B C and the current 'top'
1015 # patch is B. qpush C should be performed (moving forward)
1015 # patch is B. qpush C should be performed (moving forward)
1016 # qpush B is a NOP (no change) qpush A is an error (can't
1016 # qpush B is a NOP (no change) qpush A is an error (can't
1017 # go backwards with qpush)
1017 # go backwards with qpush)
1018 if patch:
1018 if patch:
1019 info = self.isapplied(patch)
1019 info = self.isapplied(patch)
1020 if info:
1020 if info:
1021 if info[0] < len(self.applied) - 1:
1021 if info[0] < len(self.applied) - 1:
1022 raise util.Abort(
1022 raise util.Abort(
1023 _("cannot push to a previous patch: %s") % patch)
1023 _("cannot push to a previous patch: %s") % patch)
1024 self.ui.warn(
1024 self.ui.warn(
1025 _('qpush: %s is already at the top\n') % patch)
1025 _('qpush: %s is already at the top\n') % patch)
1026 return 0
1026 return 0
1027 pushable, reason = self.pushable(patch)
1027 pushable, reason = self.pushable(patch)
1028 if not pushable:
1028 if not pushable:
1029 if reason:
1029 if reason:
1030 reason = _('guarded by %r') % reason
1030 reason = _('guarded by %r') % reason
1031 else:
1031 else:
1032 reason = _('no matching guards')
1032 reason = _('no matching guards')
1033 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1033 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1034 return 1
1034 return 1
1035 elif all:
1035 elif all:
1036 patch = self.series[-1]
1036 patch = self.series[-1]
1037 if self.isapplied(patch):
1037 if self.isapplied(patch):
1038 self.ui.warn(_('all patches are currently applied\n'))
1038 self.ui.warn(_('all patches are currently applied\n'))
1039 return 0
1039 return 0
1040
1040
1041 # Following the above example, starting at 'top' of B:
1041 # Following the above example, starting at 'top' of B:
1042 # qpush should be performed (pushes C), but a subsequent
1042 # qpush should be performed (pushes C), but a subsequent
1043 # qpush without an argument is an error (nothing to
1043 # qpush without an argument is an error (nothing to
1044 # apply). This allows a loop of "...while hg qpush..." to
1044 # apply). This allows a loop of "...while hg qpush..." to
1045 # work as it detects an error when done
1045 # work as it detects an error when done
1046 start = self.series_end()
1046 start = self.series_end()
1047 if start == len(self.series):
1047 if start == len(self.series):
1048 self.ui.warn(_('patch series already fully applied\n'))
1048 self.ui.warn(_('patch series already fully applied\n'))
1049 return 1
1049 return 1
1050 if not force:
1050 if not force:
1051 self.check_localchanges(repo)
1051 self.check_localchanges(repo)
1052
1052
1053 if move:
1053 if move:
1054 if not patch:
1054 if not patch:
1055 raise util.Abort(_("please specify the patch to move"))
1055 raise util.Abort(_("please specify the patch to move"))
1056 for i, rpn in enumerate(self.full_series[start:]):
1056 for i, rpn in enumerate(self.full_series[start:]):
1057 # strip markers for patch guards
1057 # strip markers for patch guards
1058 if self.guard_re.split(rpn, 1)[0] == patch:
1058 if self.guard_re.split(rpn, 1)[0] == patch:
1059 break
1059 break
1060 index = start + i
1060 index = start + i
1061 assert index < len(self.full_series)
1061 assert index < len(self.full_series)
1062 fullpatch = self.full_series[index]
1062 fullpatch = self.full_series[index]
1063 del self.full_series[index]
1063 del self.full_series[index]
1064 self.full_series.insert(start, fullpatch)
1064 self.full_series.insert(start, fullpatch)
1065 self.parse_series()
1065 self.parse_series()
1066 self.series_dirty = 1
1066 self.series_dirty = 1
1067
1067
1068 self.applied_dirty = 1
1068 self.applied_dirty = 1
1069 if start > 0:
1069 if start > 0:
1070 self.check_toppatch(repo)
1070 self.check_toppatch(repo)
1071 if not patch:
1071 if not patch:
1072 patch = self.series[start]
1072 patch = self.series[start]
1073 end = start + 1
1073 end = start + 1
1074 else:
1074 else:
1075 end = self.series.index(patch, start) + 1
1075 end = self.series.index(patch, start) + 1
1076
1076
1077 s = self.series[start:end]
1077 s = self.series[start:end]
1078 all_files = set()
1078 all_files = set()
1079 try:
1079 try:
1080 if mergeq:
1080 if mergeq:
1081 ret = self.mergepatch(repo, mergeq, s, diffopts)
1081 ret = self.mergepatch(repo, mergeq, s, diffopts)
1082 else:
1082 else:
1083 ret = self.apply(repo, s, list, all_files=all_files)
1083 ret = self.apply(repo, s, list, all_files=all_files)
1084 except:
1084 except:
1085 self.ui.warn(_('cleaning up working directory...'))
1085 self.ui.warn(_('cleaning up working directory...'))
1086 node = repo.dirstate.parents()[0]
1086 node = repo.dirstate.parents()[0]
1087 hg.revert(repo, node, None)
1087 hg.revert(repo, node, None)
1088 # only remove unknown files that we know we touched or
1088 # only remove unknown files that we know we touched or
1089 # created while patching
1089 # created while patching
1090 for f in all_files:
1090 for f in all_files:
1091 if f not in repo.dirstate:
1091 if f not in repo.dirstate:
1092 try:
1092 try:
1093 util.unlink(repo.wjoin(f))
1093 util.unlink(repo.wjoin(f))
1094 except OSError, inst:
1094 except OSError, inst:
1095 if inst.errno != errno.ENOENT:
1095 if inst.errno != errno.ENOENT:
1096 raise
1096 raise
1097 self.ui.warn(_('done\n'))
1097 self.ui.warn(_('done\n'))
1098 raise
1098 raise
1099
1099
1100 if not self.applied:
1100 if not self.applied:
1101 return ret[0]
1101 return ret[0]
1102 top = self.applied[-1].name
1102 top = self.applied[-1].name
1103 if ret[0] and ret[0] > 1:
1103 if ret[0] and ret[0] > 1:
1104 msg = _("errors during apply, please fix and refresh %s\n")
1104 msg = _("errors during apply, please fix and refresh %s\n")
1105 self.ui.write(msg % top)
1105 self.ui.write(msg % top)
1106 else:
1106 else:
1107 self.ui.write(_("now at: %s\n") % top)
1107 self.ui.write(_("now at: %s\n") % top)
1108 return ret[0]
1108 return ret[0]
1109
1109
1110 finally:
1110 finally:
1111 wlock.release()
1111 wlock.release()
1112
1112
1113 def pop(self, repo, patch=None, force=False, update=True, all=False):
1113 def pop(self, repo, patch=None, force=False, update=True, all=False):
1114 wlock = repo.wlock()
1114 wlock = repo.wlock()
1115 try:
1115 try:
1116 if patch:
1116 if patch:
1117 # index, rev, patch
1117 # index, rev, patch
1118 info = self.isapplied(patch)
1118 info = self.isapplied(patch)
1119 if not info:
1119 if not info:
1120 patch = self.lookup(patch)
1120 patch = self.lookup(patch)
1121 info = self.isapplied(patch)
1121 info = self.isapplied(patch)
1122 if not info:
1122 if not info:
1123 raise util.Abort(_("patch %s is not applied") % patch)
1123 raise util.Abort(_("patch %s is not applied") % patch)
1124
1124
1125 if not self.applied:
1125 if not self.applied:
1126 # Allow qpop -a to work repeatedly,
1126 # Allow qpop -a to work repeatedly,
1127 # but not qpop without an argument
1127 # but not qpop without an argument
1128 self.ui.warn(_("no patches applied\n"))
1128 self.ui.warn(_("no patches applied\n"))
1129 return not all
1129 return not all
1130
1130
1131 if all:
1131 if all:
1132 start = 0
1132 start = 0
1133 elif patch:
1133 elif patch:
1134 start = info[0] + 1
1134 start = info[0] + 1
1135 else:
1135 else:
1136 start = len(self.applied) - 1
1136 start = len(self.applied) - 1
1137
1137
1138 if start >= len(self.applied):
1138 if start >= len(self.applied):
1139 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1139 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1140 return
1140 return
1141
1141
1142 if not update:
1142 if not update:
1143 parents = repo.dirstate.parents()
1143 parents = repo.dirstate.parents()
1144 rr = [x.node for x in self.applied]
1144 rr = [x.node for x in self.applied]
1145 for p in parents:
1145 for p in parents:
1146 if p in rr:
1146 if p in rr:
1147 self.ui.warn(_("qpop: forcing dirstate update\n"))
1147 self.ui.warn(_("qpop: forcing dirstate update\n"))
1148 update = True
1148 update = True
1149 else:
1149 else:
1150 parents = [p.node() for p in repo[None].parents()]
1150 parents = [p.node() for p in repo[None].parents()]
1151 needupdate = False
1151 needupdate = False
1152 for entry in self.applied[start:]:
1152 for entry in self.applied[start:]:
1153 if entry.node in parents:
1153 if entry.node in parents:
1154 needupdate = True
1154 needupdate = True
1155 break
1155 break
1156 update = needupdate
1156 update = needupdate
1157
1157
1158 if not force and update:
1158 if not force and update:
1159 self.check_localchanges(repo)
1159 self.check_localchanges(repo)
1160
1160
1161 self.applied_dirty = 1
1161 self.applied_dirty = 1
1162 end = len(self.applied)
1162 end = len(self.applied)
1163 rev = self.applied[start].node
1163 rev = self.applied[start].node
1164 if update:
1164 if update:
1165 top = self.check_toppatch(repo)[0]
1165 top = self.check_toppatch(repo)[0]
1166
1166
1167 try:
1167 try:
1168 heads = repo.changelog.heads(rev)
1168 heads = repo.changelog.heads(rev)
1169 except error.LookupError:
1169 except error.LookupError:
1170 node = short(rev)
1170 node = short(rev)
1171 raise util.Abort(_('trying to pop unknown node %s') % node)
1171 raise util.Abort(_('trying to pop unknown node %s') % node)
1172
1172
1173 if heads != [self.applied[-1].node]:
1173 if heads != [self.applied[-1].node]:
1174 raise util.Abort(_("popping would remove a revision not "
1174 raise util.Abort(_("popping would remove a revision not "
1175 "managed by this patch queue"))
1175 "managed by this patch queue"))
1176
1176
1177 # we know there are no local changes, so we can make a simplified
1177 # we know there are no local changes, so we can make a simplified
1178 # form of hg.update.
1178 # form of hg.update.
1179 if update:
1179 if update:
1180 qp = self.qparents(repo, rev)
1180 qp = self.qparents(repo, rev)
1181 ctx = repo[qp]
1181 ctx = repo[qp]
1182 m, a, r, d = repo.status(qp, top)[:4]
1182 m, a, r, d = repo.status(qp, top)[:4]
1183 if d:
1183 if d:
1184 raise util.Abort(_("deletions found between repo revs"))
1184 raise util.Abort(_("deletions found between repo revs"))
1185 for f in a:
1185 for f in a:
1186 try:
1186 try:
1187 util.unlink(repo.wjoin(f))
1187 util.unlink(repo.wjoin(f))
1188 except OSError, e:
1188 except OSError, e:
1189 if e.errno != errno.ENOENT:
1189 if e.errno != errno.ENOENT:
1190 raise
1190 raise
1191 repo.dirstate.forget(f)
1191 repo.dirstate.forget(f)
1192 for f in m + r:
1192 for f in m + r:
1193 fctx = ctx[f]
1193 fctx = ctx[f]
1194 repo.wwrite(f, fctx.data(), fctx.flags())
1194 repo.wwrite(f, fctx.data(), fctx.flags())
1195 repo.dirstate.normal(f)
1195 repo.dirstate.normal(f)
1196 repo.dirstate.setparents(qp, nullid)
1196 repo.dirstate.setparents(qp, nullid)
1197 for patch in reversed(self.applied[start:end]):
1197 for patch in reversed(self.applied[start:end]):
1198 self.ui.status(_("popping %s\n") % patch.name)
1198 self.ui.status(_("popping %s\n") % patch.name)
1199 del self.applied[start:end]
1199 del self.applied[start:end]
1200 self.strip(repo, rev, update=False, backup='strip')
1200 self.strip(repo, rev, update=False, backup='strip')
1201 if self.applied:
1201 if self.applied:
1202 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1202 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1203 else:
1203 else:
1204 self.ui.write(_("patch queue now empty\n"))
1204 self.ui.write(_("patch queue now empty\n"))
1205 finally:
1205 finally:
1206 wlock.release()
1206 wlock.release()
1207
1207
1208 def diff(self, repo, pats, opts):
1208 def diff(self, repo, pats, opts):
1209 top, patch = self.check_toppatch(repo)
1209 top, patch = self.check_toppatch(repo)
1210 if not top:
1210 if not top:
1211 self.ui.write(_("no patches applied\n"))
1211 self.ui.write(_("no patches applied\n"))
1212 return
1212 return
1213 qp = self.qparents(repo, top)
1213 qp = self.qparents(repo, top)
1214 if opts.get('reverse'):
1214 if opts.get('reverse'):
1215 node1, node2 = None, qp
1215 node1, node2 = None, qp
1216 else:
1216 else:
1217 node1, node2 = qp, None
1217 node1, node2 = qp, None
1218 diffopts = self.diffopts(opts, patch)
1218 diffopts = self.diffopts(opts, patch)
1219 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1219 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1220
1220
1221 def refresh(self, repo, pats=None, **opts):
1221 def refresh(self, repo, pats=None, **opts):
1222 if not self.applied:
1222 if not self.applied:
1223 self.ui.write(_("no patches applied\n"))
1223 self.ui.write(_("no patches applied\n"))
1224 return 1
1224 return 1
1225 msg = opts.get('msg', '').rstrip()
1225 msg = opts.get('msg', '').rstrip()
1226 newuser = opts.get('user')
1226 newuser = opts.get('user')
1227 newdate = opts.get('date')
1227 newdate = opts.get('date')
1228 if newdate:
1228 if newdate:
1229 newdate = '%d %d' % util.parsedate(newdate)
1229 newdate = '%d %d' % util.parsedate(newdate)
1230 wlock = repo.wlock()
1230 wlock = repo.wlock()
1231
1231
1232 try:
1232 try:
1233 self.check_toppatch(repo)
1233 self.check_toppatch(repo)
1234 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1234 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1235 if repo.changelog.heads(top) != [top]:
1235 if repo.changelog.heads(top) != [top]:
1236 raise util.Abort(_("cannot refresh a revision with children"))
1236 raise util.Abort(_("cannot refresh a revision with children"))
1237
1237
1238 cparents = repo.changelog.parents(top)
1238 cparents = repo.changelog.parents(top)
1239 patchparent = self.qparents(repo, top)
1239 patchparent = self.qparents(repo, top)
1240 ph = patchheader(self.join(patchfn), self.plainmode)
1240 ph = patchheader(self.join(patchfn), self.plainmode)
1241 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1241 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1242 if msg:
1242 if msg:
1243 ph.setmessage(msg)
1243 ph.setmessage(msg)
1244 if newuser:
1244 if newuser:
1245 ph.setuser(newuser)
1245 ph.setuser(newuser)
1246 if newdate:
1246 if newdate:
1247 ph.setdate(newdate)
1247 ph.setdate(newdate)
1248 ph.setparent(hex(patchparent))
1248 ph.setparent(hex(patchparent))
1249
1249
1250 # only commit new patch when write is complete
1250 # only commit new patch when write is complete
1251 patchf = self.opener(patchfn, 'w', atomictemp=True)
1251 patchf = self.opener(patchfn, 'w', atomictemp=True)
1252
1252
1253 comments = str(ph)
1253 comments = str(ph)
1254 if comments:
1254 if comments:
1255 patchf.write(comments)
1255 patchf.write(comments)
1256
1256
1257 # update the dirstate in place, strip off the qtip commit
1257 # update the dirstate in place, strip off the qtip commit
1258 # and then commit.
1258 # and then commit.
1259 #
1259 #
1260 # this should really read:
1260 # this should really read:
1261 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1261 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1262 # but we do it backwards to take advantage of manifest/chlog
1262 # but we do it backwards to take advantage of manifest/chlog
1263 # caching against the next repo.status call
1263 # caching against the next repo.status call
1264 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1264 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1265 changes = repo.changelog.read(top)
1265 changes = repo.changelog.read(top)
1266 man = repo.manifest.read(changes[0])
1266 man = repo.manifest.read(changes[0])
1267 aaa = aa[:]
1267 aaa = aa[:]
1268 matchfn = cmdutil.match(repo, pats, opts)
1268 matchfn = cmdutil.match(repo, pats, opts)
1269 # in short mode, we only diff the files included in the
1269 # in short mode, we only diff the files included in the
1270 # patch already plus specified files
1270 # patch already plus specified files
1271 if opts.get('short'):
1271 if opts.get('short'):
1272 # if amending a patch, we start with existing
1272 # if amending a patch, we start with existing
1273 # files plus specified files - unfiltered
1273 # files plus specified files - unfiltered
1274 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1274 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1275 # filter with inc/exl options
1275 # filter with inc/exl options
1276 matchfn = cmdutil.match(repo, opts=opts)
1276 matchfn = cmdutil.match(repo, opts=opts)
1277 else:
1277 else:
1278 match = cmdutil.matchall(repo)
1278 match = cmdutil.matchall(repo)
1279 m, a, r, d = repo.status(match=match)[:4]
1279 m, a, r, d = repo.status(match=match)[:4]
1280
1280
1281 # we might end up with files that were added between
1281 # we might end up with files that were added between
1282 # qtip and the dirstate parent, but then changed in the
1282 # qtip and the dirstate parent, but then changed in the
1283 # local dirstate. in this case, we want them to only
1283 # local dirstate. in this case, we want them to only
1284 # show up in the added section
1284 # show up in the added section
1285 for x in m:
1285 for x in m:
1286 if x not in aa:
1286 if x not in aa:
1287 mm.append(x)
1287 mm.append(x)
1288 # we might end up with files added by the local dirstate that
1288 # we might end up with files added by the local dirstate that
1289 # were deleted by the patch. In this case, they should only
1289 # were deleted by the patch. In this case, they should only
1290 # show up in the changed section.
1290 # show up in the changed section.
1291 for x in a:
1291 for x in a:
1292 if x in dd:
1292 if x in dd:
1293 del dd[dd.index(x)]
1293 del dd[dd.index(x)]
1294 mm.append(x)
1294 mm.append(x)
1295 else:
1295 else:
1296 aa.append(x)
1296 aa.append(x)
1297 # make sure any files deleted in the local dirstate
1297 # make sure any files deleted in the local dirstate
1298 # are not in the add or change column of the patch
1298 # are not in the add or change column of the patch
1299 forget = []
1299 forget = []
1300 for x in d + r:
1300 for x in d + r:
1301 if x in aa:
1301 if x in aa:
1302 del aa[aa.index(x)]
1302 del aa[aa.index(x)]
1303 forget.append(x)
1303 forget.append(x)
1304 continue
1304 continue
1305 elif x in mm:
1305 elif x in mm:
1306 del mm[mm.index(x)]
1306 del mm[mm.index(x)]
1307 dd.append(x)
1307 dd.append(x)
1308
1308
1309 m = list(set(mm))
1309 m = list(set(mm))
1310 r = list(set(dd))
1310 r = list(set(dd))
1311 a = list(set(aa))
1311 a = list(set(aa))
1312 c = [filter(matchfn, l) for l in (m, a, r)]
1312 c = [filter(matchfn, l) for l in (m, a, r)]
1313 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1313 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1314 chunks = patch.diff(repo, patchparent, match=match,
1314 chunks = patch.diff(repo, patchparent, match=match,
1315 changes=c, opts=diffopts)
1315 changes=c, opts=diffopts)
1316 for chunk in chunks:
1316 for chunk in chunks:
1317 patchf.write(chunk)
1317 patchf.write(chunk)
1318
1318
1319 try:
1319 try:
1320 if diffopts.git or diffopts.upgrade:
1320 if diffopts.git or diffopts.upgrade:
1321 copies = {}
1321 copies = {}
1322 for dst in a:
1322 for dst in a:
1323 src = repo.dirstate.copied(dst)
1323 src = repo.dirstate.copied(dst)
1324 # during qfold, the source file for copies may
1324 # during qfold, the source file for copies may
1325 # be removed. Treat this as a simple add.
1325 # be removed. Treat this as a simple add.
1326 if src is not None and src in repo.dirstate:
1326 if src is not None and src in repo.dirstate:
1327 copies.setdefault(src, []).append(dst)
1327 copies.setdefault(src, []).append(dst)
1328 repo.dirstate.add(dst)
1328 repo.dirstate.add(dst)
1329 # remember the copies between patchparent and qtip
1329 # remember the copies between patchparent and qtip
1330 for dst in aaa:
1330 for dst in aaa:
1331 f = repo.file(dst)
1331 f = repo.file(dst)
1332 src = f.renamed(man[dst])
1332 src = f.renamed(man[dst])
1333 if src:
1333 if src:
1334 copies.setdefault(src[0], []).extend(
1334 copies.setdefault(src[0], []).extend(
1335 copies.get(dst, []))
1335 copies.get(dst, []))
1336 if dst in a:
1336 if dst in a:
1337 copies[src[0]].append(dst)
1337 copies[src[0]].append(dst)
1338 # we can't copy a file created by the patch itself
1338 # we can't copy a file created by the patch itself
1339 if dst in copies:
1339 if dst in copies:
1340 del copies[dst]
1340 del copies[dst]
1341 for src, dsts in copies.iteritems():
1341 for src, dsts in copies.iteritems():
1342 for dst in dsts:
1342 for dst in dsts:
1343 repo.dirstate.copy(src, dst)
1343 repo.dirstate.copy(src, dst)
1344 else:
1344 else:
1345 for dst in a:
1345 for dst in a:
1346 repo.dirstate.add(dst)
1346 repo.dirstate.add(dst)
1347 # Drop useless copy information
1347 # Drop useless copy information
1348 for f in list(repo.dirstate.copies()):
1348 for f in list(repo.dirstate.copies()):
1349 repo.dirstate.copy(None, f)
1349 repo.dirstate.copy(None, f)
1350 for f in r:
1350 for f in r:
1351 repo.dirstate.remove(f)
1351 repo.dirstate.remove(f)
1352 # if the patch excludes a modified file, mark that
1352 # if the patch excludes a modified file, mark that
1353 # file with mtime=0 so status can see it.
1353 # file with mtime=0 so status can see it.
1354 mm = []
1354 mm = []
1355 for i in xrange(len(m)-1, -1, -1):
1355 for i in xrange(len(m)-1, -1, -1):
1356 if not matchfn(m[i]):
1356 if not matchfn(m[i]):
1357 mm.append(m[i])
1357 mm.append(m[i])
1358 del m[i]
1358 del m[i]
1359 for f in m:
1359 for f in m:
1360 repo.dirstate.normal(f)
1360 repo.dirstate.normal(f)
1361 for f in mm:
1361 for f in mm:
1362 repo.dirstate.normallookup(f)
1362 repo.dirstate.normallookup(f)
1363 for f in forget:
1363 for f in forget:
1364 repo.dirstate.forget(f)
1364 repo.dirstate.forget(f)
1365
1365
1366 if not msg:
1366 if not msg:
1367 if not ph.message:
1367 if not ph.message:
1368 message = "[mq]: %s\n" % patchfn
1368 message = "[mq]: %s\n" % patchfn
1369 else:
1369 else:
1370 message = "\n".join(ph.message)
1370 message = "\n".join(ph.message)
1371 else:
1371 else:
1372 message = msg
1372 message = msg
1373
1373
1374 user = ph.user or changes[1]
1374 user = ph.user or changes[1]
1375
1375
1376 # assumes strip can roll itself back if interrupted
1376 # assumes strip can roll itself back if interrupted
1377 repo.dirstate.setparents(*cparents)
1377 repo.dirstate.setparents(*cparents)
1378 self.applied.pop()
1378 self.applied.pop()
1379 self.applied_dirty = 1
1379 self.applied_dirty = 1
1380 self.strip(repo, top, update=False,
1380 self.strip(repo, top, update=False,
1381 backup='strip')
1381 backup='strip')
1382 except:
1382 except:
1383 repo.dirstate.invalidate()
1383 repo.dirstate.invalidate()
1384 raise
1384 raise
1385
1385
1386 try:
1386 try:
1387 # might be nice to attempt to roll back strip after this
1387 # might be nice to attempt to roll back strip after this
1388 patchf.rename()
1388 patchf.rename()
1389 n = repo.commit(message, user, ph.date, match=match,
1389 n = repo.commit(message, user, ph.date, match=match,
1390 force=True)
1390 force=True)
1391 self.applied.append(statusentry(n, patchfn))
1391 self.applied.append(statusentry(n, patchfn))
1392 except:
1392 except:
1393 ctx = repo[cparents[0]]
1393 ctx = repo[cparents[0]]
1394 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1394 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1395 self.save_dirty()
1395 self.save_dirty()
1396 self.ui.warn(_('refresh interrupted while patch was popped! '
1396 self.ui.warn(_('refresh interrupted while patch was popped! '
1397 '(revert --all, qpush to recover)\n'))
1397 '(revert --all, qpush to recover)\n'))
1398 raise
1398 raise
1399 finally:
1399 finally:
1400 wlock.release()
1400 wlock.release()
1401 self.removeundo(repo)
1401 self.removeundo(repo)
1402
1402
1403 def init(self, repo, create=False):
1403 def init(self, repo, create=False):
1404 if not create and os.path.isdir(self.path):
1404 if not create and os.path.isdir(self.path):
1405 raise util.Abort(_("patch queue directory already exists"))
1405 raise util.Abort(_("patch queue directory already exists"))
1406 try:
1406 try:
1407 os.mkdir(self.path)
1407 os.mkdir(self.path)
1408 except OSError, inst:
1408 except OSError, inst:
1409 if inst.errno != errno.EEXIST or not create:
1409 if inst.errno != errno.EEXIST or not create:
1410 raise
1410 raise
1411 if create:
1411 if create:
1412 return self.qrepo(create=True)
1412 return self.qrepo(create=True)
1413
1413
1414 def unapplied(self, repo, patch=None):
1414 def unapplied(self, repo, patch=None):
1415 if patch and patch not in self.series:
1415 if patch and patch not in self.series:
1416 raise util.Abort(_("patch %s is not in series file") % patch)
1416 raise util.Abort(_("patch %s is not in series file") % patch)
1417 if not patch:
1417 if not patch:
1418 start = self.series_end()
1418 start = self.series_end()
1419 else:
1419 else:
1420 start = self.series.index(patch) + 1
1420 start = self.series.index(patch) + 1
1421 unapplied = []
1421 unapplied = []
1422 for i in xrange(start, len(self.series)):
1422 for i in xrange(start, len(self.series)):
1423 pushable, reason = self.pushable(i)
1423 pushable, reason = self.pushable(i)
1424 if pushable:
1424 if pushable:
1425 unapplied.append((i, self.series[i]))
1425 unapplied.append((i, self.series[i]))
1426 self.explain_pushable(i)
1426 self.explain_pushable(i)
1427 return unapplied
1427 return unapplied
1428
1428
1429 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1429 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1430 summary=False):
1430 summary=False):
1431 def displayname(pfx, patchname, state):
1431 def displayname(pfx, patchname, state):
1432 if pfx:
1432 if pfx:
1433 self.ui.write(pfx)
1433 self.ui.write(pfx)
1434 if summary:
1434 if summary:
1435 ph = patchheader(self.join(patchname), self.plainmode)
1435 ph = patchheader(self.join(patchname), self.plainmode)
1436 msg = ph.message and ph.message[0] or ''
1436 msg = ph.message and ph.message[0] or ''
1437 if self.ui.formatted():
1437 if self.ui.formatted():
1438 width = util.termwidth() - len(pfx) - len(patchname) - 2
1438 width = util.termwidth() - len(pfx) - len(patchname) - 2
1439 if width > 0:
1439 if width > 0:
1440 msg = util.ellipsis(msg, width)
1440 msg = util.ellipsis(msg, width)
1441 else:
1441 else:
1442 msg = ''
1442 msg = ''
1443 self.ui.write(patchname, label='qseries.' + state)
1443 self.ui.write(patchname, label='qseries.' + state)
1444 self.ui.write(': ')
1444 self.ui.write(': ')
1445 self.ui.write(msg, label='qseries.message.' + state)
1445 self.ui.write(msg, label='qseries.message.' + state)
1446 else:
1446 else:
1447 self.ui.write(patchname, label='qseries.' + state)
1447 self.ui.write(patchname, label='qseries.' + state)
1448 self.ui.write('\n')
1448 self.ui.write('\n')
1449
1449
1450 applied = set([p.name for p in self.applied])
1450 applied = set([p.name for p in self.applied])
1451 if length is None:
1451 if length is None:
1452 length = len(self.series) - start
1452 length = len(self.series) - start
1453 if not missing:
1453 if not missing:
1454 if self.ui.verbose:
1454 if self.ui.verbose:
1455 idxwidth = len(str(start + length - 1))
1455 idxwidth = len(str(start + length - 1))
1456 for i in xrange(start, start + length):
1456 for i in xrange(start, start + length):
1457 patch = self.series[i]
1457 patch = self.series[i]
1458 if patch in applied:
1458 if patch in applied:
1459 char, state = 'A', 'applied'
1459 char, state = 'A', 'applied'
1460 elif self.pushable(i)[0]:
1460 elif self.pushable(i)[0]:
1461 char, state = 'U', 'unapplied'
1461 char, state = 'U', 'unapplied'
1462 else:
1462 else:
1463 char, state = 'G', 'guarded'
1463 char, state = 'G', 'guarded'
1464 pfx = ''
1464 pfx = ''
1465 if self.ui.verbose:
1465 if self.ui.verbose:
1466 pfx = '%*d %s ' % (idxwidth, i, char)
1466 pfx = '%*d %s ' % (idxwidth, i, char)
1467 elif status and status != char:
1467 elif status and status != char:
1468 continue
1468 continue
1469 displayname(pfx, patch, state)
1469 displayname(pfx, patch, state)
1470 else:
1470 else:
1471 msng_list = []
1471 msng_list = []
1472 for root, dirs, files in os.walk(self.path):
1472 for root, dirs, files in os.walk(self.path):
1473 d = root[len(self.path) + 1:]
1473 d = root[len(self.path) + 1:]
1474 for f in files:
1474 for f in files:
1475 fl = os.path.join(d, f)
1475 fl = os.path.join(d, f)
1476 if (fl not in self.series and
1476 if (fl not in self.series and
1477 fl not in (self.status_path, self.series_path,
1477 fl not in (self.status_path, self.series_path,
1478 self.guards_path)
1478 self.guards_path)
1479 and not fl.startswith('.')):
1479 and not fl.startswith('.')):
1480 msng_list.append(fl)
1480 msng_list.append(fl)
1481 for x in sorted(msng_list):
1481 for x in sorted(msng_list):
1482 pfx = self.ui.verbose and ('D ') or ''
1482 pfx = self.ui.verbose and ('D ') or ''
1483 displayname(pfx, x, 'missing')
1483 displayname(pfx, x, 'missing')
1484
1484
1485 def issaveline(self, l):
1485 def issaveline(self, l):
1486 if l.name == '.hg.patches.save.line':
1486 if l.name == '.hg.patches.save.line':
1487 return True
1487 return True
1488
1488
1489 def qrepo(self, create=False):
1489 def qrepo(self, create=False):
1490 ui = self.ui.copy()
1490 ui = self.ui.copy()
1491 ui.setconfig('paths', 'default', '', overlay=False)
1491 ui.setconfig('paths', 'default', '', overlay=False)
1492 ui.setconfig('paths', 'default-push', '', overlay=False)
1492 ui.setconfig('paths', 'default-push', '', overlay=False)
1493 if create or os.path.isdir(self.join(".hg")):
1493 if create or os.path.isdir(self.join(".hg")):
1494 return hg.repository(ui, path=self.path, create=create)
1494 return hg.repository(ui, path=self.path, create=create)
1495
1495
1496 def restore(self, repo, rev, delete=None, qupdate=None):
1496 def restore(self, repo, rev, delete=None, qupdate=None):
1497 desc = repo[rev].description().strip()
1497 desc = repo[rev].description().strip()
1498 lines = desc.splitlines()
1498 lines = desc.splitlines()
1499 i = 0
1499 i = 0
1500 datastart = None
1500 datastart = None
1501 series = []
1501 series = []
1502 applied = []
1502 applied = []
1503 qpp = None
1503 qpp = None
1504 for i, line in enumerate(lines):
1504 for i, line in enumerate(lines):
1505 if line == 'Patch Data:':
1505 if line == 'Patch Data:':
1506 datastart = i + 1
1506 datastart = i + 1
1507 elif line.startswith('Dirstate:'):
1507 elif line.startswith('Dirstate:'):
1508 l = line.rstrip()
1508 l = line.rstrip()
1509 l = l[10:].split(' ')
1509 l = l[10:].split(' ')
1510 qpp = [bin(x) for x in l]
1510 qpp = [bin(x) for x in l]
1511 elif datastart != None:
1511 elif datastart != None:
1512 l = line.rstrip()
1512 l = line.rstrip()
1513 n, name = l.split(':', 1)
1513 n, name = l.split(':', 1)
1514 if n:
1514 if n:
1515 applied.append(statusentry(bin(n), name))
1515 applied.append(statusentry(bin(n), name))
1516 else:
1516 else:
1517 series.append(l)
1517 series.append(l)
1518 if datastart is None:
1518 if datastart is None:
1519 self.ui.warn(_("No saved patch data found\n"))
1519 self.ui.warn(_("No saved patch data found\n"))
1520 return 1
1520 return 1
1521 self.ui.warn(_("restoring status: %s\n") % lines[0])
1521 self.ui.warn(_("restoring status: %s\n") % lines[0])
1522 self.full_series = series
1522 self.full_series = series
1523 self.applied = applied
1523 self.applied = applied
1524 self.parse_series()
1524 self.parse_series()
1525 self.series_dirty = 1
1525 self.series_dirty = 1
1526 self.applied_dirty = 1
1526 self.applied_dirty = 1
1527 heads = repo.changelog.heads()
1527 heads = repo.changelog.heads()
1528 if delete:
1528 if delete:
1529 if rev not in heads:
1529 if rev not in heads:
1530 self.ui.warn(_("save entry has children, leaving it alone\n"))
1530 self.ui.warn(_("save entry has children, leaving it alone\n"))
1531 else:
1531 else:
1532 self.ui.warn(_("removing save entry %s\n") % short(rev))
1532 self.ui.warn(_("removing save entry %s\n") % short(rev))
1533 pp = repo.dirstate.parents()
1533 pp = repo.dirstate.parents()
1534 if rev in pp:
1534 if rev in pp:
1535 update = True
1535 update = True
1536 else:
1536 else:
1537 update = False
1537 update = False
1538 self.strip(repo, rev, update=update, backup='strip')
1538 self.strip(repo, rev, update=update, backup='strip')
1539 if qpp:
1539 if qpp:
1540 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1540 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1541 (short(qpp[0]), short(qpp[1])))
1541 (short(qpp[0]), short(qpp[1])))
1542 if qupdate:
1542 if qupdate:
1543 self.ui.status(_("queue directory updating\n"))
1543 self.ui.status(_("queue directory updating\n"))
1544 r = self.qrepo()
1544 r = self.qrepo()
1545 if not r:
1545 if not r:
1546 self.ui.warn(_("Unable to load queue repository\n"))
1546 self.ui.warn(_("Unable to load queue repository\n"))
1547 return 1
1547 return 1
1548 hg.clean(r, qpp[0])
1548 hg.clean(r, qpp[0])
1549
1549
1550 def save(self, repo, msg=None):
1550 def save(self, repo, msg=None):
1551 if not self.applied:
1551 if not self.applied:
1552 self.ui.warn(_("save: no patches applied, exiting\n"))
1552 self.ui.warn(_("save: no patches applied, exiting\n"))
1553 return 1
1553 return 1
1554 if self.issaveline(self.applied[-1]):
1554 if self.issaveline(self.applied[-1]):
1555 self.ui.warn(_("status is already saved\n"))
1555 self.ui.warn(_("status is already saved\n"))
1556 return 1
1556 return 1
1557
1557
1558 if not msg:
1558 if not msg:
1559 msg = _("hg patches saved state")
1559 msg = _("hg patches saved state")
1560 else:
1560 else:
1561 msg = "hg patches: " + msg.rstrip('\r\n')
1561 msg = "hg patches: " + msg.rstrip('\r\n')
1562 r = self.qrepo()
1562 r = self.qrepo()
1563 if r:
1563 if r:
1564 pp = r.dirstate.parents()
1564 pp = r.dirstate.parents()
1565 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1565 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1566 msg += "\n\nPatch Data:\n"
1566 msg += "\n\nPatch Data:\n"
1567 msg += ''.join('%s\n' % x for x in self.applied)
1567 msg += ''.join('%s\n' % x for x in self.applied)
1568 msg += ''.join(':%s\n' % x for x in self.full_series)
1568 msg += ''.join(':%s\n' % x for x in self.full_series)
1569 n = repo.commit(msg, force=True)
1569 n = repo.commit(msg, force=True)
1570 if not n:
1570 if not n:
1571 self.ui.warn(_("repo commit failed\n"))
1571 self.ui.warn(_("repo commit failed\n"))
1572 return 1
1572 return 1
1573 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1573 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1574 self.applied_dirty = 1
1574 self.applied_dirty = 1
1575 self.removeundo(repo)
1575 self.removeundo(repo)
1576
1576
1577 def full_series_end(self):
1577 def full_series_end(self):
1578 if self.applied:
1578 if self.applied:
1579 p = self.applied[-1].name
1579 p = self.applied[-1].name
1580 end = self.find_series(p)
1580 end = self.find_series(p)
1581 if end is None:
1581 if end is None:
1582 return len(self.full_series)
1582 return len(self.full_series)
1583 return end + 1
1583 return end + 1
1584 return 0
1584 return 0
1585
1585
1586 def series_end(self, all_patches=False):
1586 def series_end(self, all_patches=False):
1587 """If all_patches is False, return the index of the next pushable patch
1587 """If all_patches is False, return the index of the next pushable patch
1588 in the series, or the series length. If all_patches is True, return the
1588 in the series, or the series length. If all_patches is True, return the
1589 index of the first patch past the last applied one.
1589 index of the first patch past the last applied one.
1590 """
1590 """
1591 end = 0
1591 end = 0
1592 def next(start):
1592 def next(start):
1593 if all_patches or start >= len(self.series):
1593 if all_patches or start >= len(self.series):
1594 return start
1594 return start
1595 for i in xrange(start, len(self.series)):
1595 for i in xrange(start, len(self.series)):
1596 p, reason = self.pushable(i)
1596 p, reason = self.pushable(i)
1597 if p:
1597 if p:
1598 break
1598 break
1599 self.explain_pushable(i)
1599 self.explain_pushable(i)
1600 return i
1600 return i
1601 if self.applied:
1601 if self.applied:
1602 p = self.applied[-1].name
1602 p = self.applied[-1].name
1603 try:
1603 try:
1604 end = self.series.index(p)
1604 end = self.series.index(p)
1605 except ValueError:
1605 except ValueError:
1606 return 0
1606 return 0
1607 return next(end + 1)
1607 return next(end + 1)
1608 return next(end)
1608 return next(end)
1609
1609
1610 def appliedname(self, index):
1610 def appliedname(self, index):
1611 pname = self.applied[index].name
1611 pname = self.applied[index].name
1612 if not self.ui.verbose:
1612 if not self.ui.verbose:
1613 p = pname
1613 p = pname
1614 else:
1614 else:
1615 p = str(self.series.index(pname)) + " " + pname
1615 p = str(self.series.index(pname)) + " " + pname
1616 return p
1616 return p
1617
1617
1618 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1618 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1619 force=None, git=False):
1619 force=None, git=False):
1620 def checkseries(patchname):
1620 def checkseries(patchname):
1621 if patchname in self.series:
1621 if patchname in self.series:
1622 raise util.Abort(_('patch %s is already in the series file')
1622 raise util.Abort(_('patch %s is already in the series file')
1623 % patchname)
1623 % patchname)
1624 def checkfile(patchname):
1624 def checkfile(patchname):
1625 if not force and os.path.exists(self.join(patchname)):
1625 if not force and os.path.exists(self.join(patchname)):
1626 raise util.Abort(_('patch "%s" already exists')
1626 raise util.Abort(_('patch "%s" already exists')
1627 % patchname)
1627 % patchname)
1628
1628
1629 if rev:
1629 if rev:
1630 if files:
1630 if files:
1631 raise util.Abort(_('option "-r" not valid when importing '
1631 raise util.Abort(_('option "-r" not valid when importing '
1632 'files'))
1632 'files'))
1633 rev = cmdutil.revrange(repo, rev)
1633 rev = cmdutil.revrange(repo, rev)
1634 rev.sort(reverse=True)
1634 rev.sort(reverse=True)
1635 if (len(files) > 1 or len(rev) > 1) and patchname:
1635 if (len(files) > 1 or len(rev) > 1) and patchname:
1636 raise util.Abort(_('option "-n" not valid when importing multiple '
1636 raise util.Abort(_('option "-n" not valid when importing multiple '
1637 'patches'))
1637 'patches'))
1638 if rev:
1638 if rev:
1639 # If mq patches are applied, we can only import revisions
1639 # If mq patches are applied, we can only import revisions
1640 # that form a linear path to qbase.
1640 # that form a linear path to qbase.
1641 # Otherwise, they should form a linear path to a head.
1641 # Otherwise, they should form a linear path to a head.
1642 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1642 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1643 if len(heads) > 1:
1643 if len(heads) > 1:
1644 raise util.Abort(_('revision %d is the root of more than one '
1644 raise util.Abort(_('revision %d is the root of more than one '
1645 'branch') % rev[-1])
1645 'branch') % rev[-1])
1646 if self.applied:
1646 if self.applied:
1647 base = repo.changelog.node(rev[0])
1647 base = repo.changelog.node(rev[0])
1648 if base in [n.node for n in self.applied]:
1648 if base in [n.node for n in self.applied]:
1649 raise util.Abort(_('revision %d is already managed')
1649 raise util.Abort(_('revision %d is already managed')
1650 % rev[0])
1650 % rev[0])
1651 if heads != [self.applied[-1].node]:
1651 if heads != [self.applied[-1].node]:
1652 raise util.Abort(_('revision %d is not the parent of '
1652 raise util.Abort(_('revision %d is not the parent of '
1653 'the queue') % rev[0])
1653 'the queue') % rev[0])
1654 base = repo.changelog.rev(self.applied[0].node)
1654 base = repo.changelog.rev(self.applied[0].node)
1655 lastparent = repo.changelog.parentrevs(base)[0]
1655 lastparent = repo.changelog.parentrevs(base)[0]
1656 else:
1656 else:
1657 if heads != [repo.changelog.node(rev[0])]:
1657 if heads != [repo.changelog.node(rev[0])]:
1658 raise util.Abort(_('revision %d has unmanaged children')
1658 raise util.Abort(_('revision %d has unmanaged children')
1659 % rev[0])
1659 % rev[0])
1660 lastparent = None
1660 lastparent = None
1661
1661
1662 diffopts = self.diffopts({'git': git})
1662 diffopts = self.diffopts({'git': git})
1663 for r in rev:
1663 for r in rev:
1664 p1, p2 = repo.changelog.parentrevs(r)
1664 p1, p2 = repo.changelog.parentrevs(r)
1665 n = repo.changelog.node(r)
1665 n = repo.changelog.node(r)
1666 if p2 != nullrev:
1666 if p2 != nullrev:
1667 raise util.Abort(_('cannot import merge revision %d') % r)
1667 raise util.Abort(_('cannot import merge revision %d') % r)
1668 if lastparent and lastparent != r:
1668 if lastparent and lastparent != r:
1669 raise util.Abort(_('revision %d is not the parent of %d')
1669 raise util.Abort(_('revision %d is not the parent of %d')
1670 % (r, lastparent))
1670 % (r, lastparent))
1671 lastparent = p1
1671 lastparent = p1
1672
1672
1673 if not patchname:
1673 if not patchname:
1674 patchname = normname('%d.diff' % r)
1674 patchname = normname('%d.diff' % r)
1675 self.check_reserved_name(patchname)
1675 self.check_reserved_name(patchname)
1676 checkseries(patchname)
1676 checkseries(patchname)
1677 checkfile(patchname)
1677 checkfile(patchname)
1678 self.full_series.insert(0, patchname)
1678 self.full_series.insert(0, patchname)
1679
1679
1680 patchf = self.opener(patchname, "w")
1680 patchf = self.opener(patchname, "w")
1681 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1681 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1682 patchf.close()
1682 patchf.close()
1683
1683
1684 se = statusentry(n, patchname)
1684 se = statusentry(n, patchname)
1685 self.applied.insert(0, se)
1685 self.applied.insert(0, se)
1686
1686
1687 self.added.append(patchname)
1687 self.added.append(patchname)
1688 patchname = None
1688 patchname = None
1689 self.parse_series()
1689 self.parse_series()
1690 self.applied_dirty = 1
1690 self.applied_dirty = 1
1691 self.series_dirty = True
1691 self.series_dirty = True
1692
1692
1693 for i, filename in enumerate(files):
1693 for i, filename in enumerate(files):
1694 if existing:
1694 if existing:
1695 if filename == '-':
1695 if filename == '-':
1696 raise util.Abort(_('-e is incompatible with import from -'))
1696 raise util.Abort(_('-e is incompatible with import from -'))
1697 if not patchname:
1697 if not patchname:
1698 patchname = normname(filename)
1698 patchname = normname(filename)
1699 self.check_reserved_name(patchname)
1699 self.check_reserved_name(patchname)
1700 if not os.path.isfile(self.join(patchname)):
1700 if not os.path.isfile(self.join(patchname)):
1701 raise util.Abort(_("patch %s does not exist") % patchname)
1701 raise util.Abort(_("patch %s does not exist") % patchname)
1702 else:
1702 else:
1703 try:
1703 try:
1704 if filename == '-':
1704 if filename == '-':
1705 if not patchname:
1705 if not patchname:
1706 raise util.Abort(
1706 raise util.Abort(
1707 _('need --name to import a patch from -'))
1707 _('need --name to import a patch from -'))
1708 text = sys.stdin.read()
1708 text = sys.stdin.read()
1709 else:
1709 else:
1710 text = url.open(self.ui, filename).read()
1710 text = url.open(self.ui, filename).read()
1711 except (OSError, IOError):
1711 except (OSError, IOError):
1712 raise util.Abort(_("unable to read file %s") % filename)
1712 raise util.Abort(_("unable to read file %s") % filename)
1713 if not patchname:
1713 if not patchname:
1714 patchname = normname(os.path.basename(filename))
1714 patchname = normname(os.path.basename(filename))
1715 self.check_reserved_name(patchname)
1715 self.check_reserved_name(patchname)
1716 checkfile(patchname)
1716 checkfile(patchname)
1717 patchf = self.opener(patchname, "w")
1717 patchf = self.opener(patchname, "w")
1718 patchf.write(text)
1718 patchf.write(text)
1719 if not force:
1719 if not force:
1720 checkseries(patchname)
1720 checkseries(patchname)
1721 if patchname not in self.series:
1721 if patchname not in self.series:
1722 index = self.full_series_end() + i
1722 index = self.full_series_end() + i
1723 self.full_series[index:index] = [patchname]
1723 self.full_series[index:index] = [patchname]
1724 self.parse_series()
1724 self.parse_series()
1725 self.series_dirty = True
1725 self.series_dirty = True
1726 self.ui.warn(_("adding %s to series file\n") % patchname)
1726 self.ui.warn(_("adding %s to series file\n") % patchname)
1727 self.added.append(patchname)
1727 self.added.append(patchname)
1728 patchname = None
1728 patchname = None
1729
1729
1730 def delete(ui, repo, *patches, **opts):
1730 def delete(ui, repo, *patches, **opts):
1731 """remove patches from queue
1731 """remove patches from queue
1732
1732
1733 The patches must not be applied, and at least one patch is required. With
1733 The patches must not be applied, and at least one patch is required. With
1734 -k/--keep, the patch files are preserved in the patch directory.
1734 -k/--keep, the patch files are preserved in the patch directory.
1735
1735
1736 To stop managing a patch and move it into permanent history,
1736 To stop managing a patch and move it into permanent history,
1737 use the :hg:`qfinish` command."""
1737 use the :hg:`qfinish` command."""
1738 q = repo.mq
1738 q = repo.mq
1739 q.delete(repo, patches, opts)
1739 q.delete(repo, patches, opts)
1740 q.save_dirty()
1740 q.save_dirty()
1741 return 0
1741 return 0
1742
1742
1743 def applied(ui, repo, patch=None, **opts):
1743 def applied(ui, repo, patch=None, **opts):
1744 """print the patches already applied"""
1744 """print the patches already applied"""
1745
1745
1746 q = repo.mq
1746 q = repo.mq
1747 l = len(q.applied)
1747 l = len(q.applied)
1748
1748
1749 if patch:
1749 if patch:
1750 if patch not in q.series:
1750 if patch not in q.series:
1751 raise util.Abort(_("patch %s is not in series file") % patch)
1751 raise util.Abort(_("patch %s is not in series file") % patch)
1752 end = q.series.index(patch) + 1
1752 end = q.series.index(patch) + 1
1753 else:
1753 else:
1754 end = q.series_end(True)
1754 end = q.series_end(True)
1755
1755
1756 if opts.get('last') and not end:
1756 if opts.get('last') and not end:
1757 ui.write(_("no patches applied\n"))
1757 ui.write(_("no patches applied\n"))
1758 return 1
1758 return 1
1759 elif opts.get('last') and end == 1:
1759 elif opts.get('last') and end == 1:
1760 ui.write(_("only one patch applied\n"))
1760 ui.write(_("only one patch applied\n"))
1761 return 1
1761 return 1
1762 elif opts.get('last'):
1762 elif opts.get('last'):
1763 start = end - 2
1763 start = end - 2
1764 end = 1
1764 end = 1
1765 else:
1765 else:
1766 start = 0
1766 start = 0
1767
1767
1768 return q.qseries(repo, length=end, start=start, status='A',
1768 return q.qseries(repo, length=end, start=start, status='A',
1769 summary=opts.get('summary'))
1769 summary=opts.get('summary'))
1770
1770
1771 def unapplied(ui, repo, patch=None, **opts):
1771 def unapplied(ui, repo, patch=None, **opts):
1772 """print the patches not yet applied"""
1772 """print the patches not yet applied"""
1773
1773
1774 q = repo.mq
1774 q = repo.mq
1775 if patch:
1775 if patch:
1776 if patch not in q.series:
1776 if patch not in q.series:
1777 raise util.Abort(_("patch %s is not in series file") % patch)
1777 raise util.Abort(_("patch %s is not in series file") % patch)
1778 start = q.series.index(patch) + 1
1778 start = q.series.index(patch) + 1
1779 else:
1779 else:
1780 start = q.series_end(True)
1780 start = q.series_end(True)
1781
1781
1782 if start == len(q.series) and opts.get('first'):
1782 if start == len(q.series) and opts.get('first'):
1783 ui.write(_("all patches applied\n"))
1783 ui.write(_("all patches applied\n"))
1784 return 1
1784 return 1
1785
1785
1786 length = opts.get('first') and 1 or None
1786 length = opts.get('first') and 1 or None
1787 return q.qseries(repo, start=start, length=length, status='U',
1787 return q.qseries(repo, start=start, length=length, status='U',
1788 summary=opts.get('summary'))
1788 summary=opts.get('summary'))
1789
1789
1790 def qimport(ui, repo, *filename, **opts):
1790 def qimport(ui, repo, *filename, **opts):
1791 """import a patch
1791 """import a patch
1792
1792
1793 The patch is inserted into the series after the last applied
1793 The patch is inserted into the series after the last applied
1794 patch. If no patches have been applied, qimport prepends the patch
1794 patch. If no patches have been applied, qimport prepends the patch
1795 to the series.
1795 to the series.
1796
1796
1797 The patch will have the same name as its source file unless you
1797 The patch will have the same name as its source file unless you
1798 give it a new one with -n/--name.
1798 give it a new one with -n/--name.
1799
1799
1800 You can register an existing patch inside the patch directory with
1800 You can register an existing patch inside the patch directory with
1801 the -e/--existing flag.
1801 the -e/--existing flag.
1802
1802
1803 With -f/--force, an existing patch of the same name will be
1803 With -f/--force, an existing patch of the same name will be
1804 overwritten.
1804 overwritten.
1805
1805
1806 An existing changeset may be placed under mq control with -r/--rev
1806 An existing changeset may be placed under mq control with -r/--rev
1807 (e.g. qimport --rev tip -n patch will place tip under mq control).
1807 (e.g. qimport --rev tip -n patch will place tip under mq control).
1808 With -g/--git, patches imported with --rev will use the git diff
1808 With -g/--git, patches imported with --rev will use the git diff
1809 format. See the diffs help topic for information on why this is
1809 format. See the diffs help topic for information on why this is
1810 important for preserving rename/copy information and permission
1810 important for preserving rename/copy information and permission
1811 changes.
1811 changes.
1812
1812
1813 To import a patch from standard input, pass - as the patch file.
1813 To import a patch from standard input, pass - as the patch file.
1814 When importing from standard input, a patch name must be specified
1814 When importing from standard input, a patch name must be specified
1815 using the --name flag.
1815 using the --name flag.
1816 """
1816 """
1817 q = repo.mq
1817 q = repo.mq
1818 try:
1818 try:
1819 q.qimport(repo, filename, patchname=opts['name'],
1819 q.qimport(repo, filename, patchname=opts['name'],
1820 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1820 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1821 git=opts['git'])
1821 git=opts['git'])
1822 finally:
1822 finally:
1823 q.save_dirty()
1823 q.save_dirty()
1824
1824
1825 if opts.get('push') and not opts.get('rev'):
1825 if opts.get('push') and not opts.get('rev'):
1826 return q.push(repo, None)
1826 return q.push(repo, None)
1827 return 0
1827 return 0
1828
1828
1829 def qinit(ui, repo, create):
1829 def qinit(ui, repo, create):
1830 """initialize a new queue repository
1830 """initialize a new queue repository
1831
1831
1832 This command also creates a series file for ordering patches, and
1832 This command also creates a series file for ordering patches, and
1833 an mq-specific .hgignore file in the queue repository, to exclude
1833 an mq-specific .hgignore file in the queue repository, to exclude
1834 the status and guards files (these contain mostly transient state)."""
1834 the status and guards files (these contain mostly transient state)."""
1835 q = repo.mq
1835 q = repo.mq
1836 r = q.init(repo, create)
1836 r = q.init(repo, create)
1837 q.save_dirty()
1837 q.save_dirty()
1838 if r:
1838 if r:
1839 if not os.path.exists(r.wjoin('.hgignore')):
1839 if not os.path.exists(r.wjoin('.hgignore')):
1840 fp = r.wopener('.hgignore', 'w')
1840 fp = r.wopener('.hgignore', 'w')
1841 fp.write('^\\.hg\n')
1841 fp.write('^\\.hg\n')
1842 fp.write('^\\.mq\n')
1842 fp.write('^\\.mq\n')
1843 fp.write('syntax: glob\n')
1843 fp.write('syntax: glob\n')
1844 fp.write('status\n')
1844 fp.write('status\n')
1845 fp.write('guards\n')
1845 fp.write('guards\n')
1846 fp.close()
1846 fp.close()
1847 if not os.path.exists(r.wjoin('series')):
1847 if not os.path.exists(r.wjoin('series')):
1848 r.wopener('series', 'w').close()
1848 r.wopener('series', 'w').close()
1849 r[None].add(['.hgignore', 'series'])
1849 r[None].add(['.hgignore', 'series'])
1850 commands.add(ui, r)
1850 commands.add(ui, r)
1851 return 0
1851 return 0
1852
1852
1853 def init(ui, repo, **opts):
1853 def init(ui, repo, **opts):
1854 """init a new queue repository (DEPRECATED)
1854 """init a new queue repository (DEPRECATED)
1855
1855
1856 The queue repository is unversioned by default. If
1856 The queue repository is unversioned by default. If
1857 -c/--create-repo is specified, qinit will create a separate nested
1857 -c/--create-repo is specified, qinit will create a separate nested
1858 repository for patches (qinit -c may also be run later to convert
1858 repository for patches (qinit -c may also be run later to convert
1859 an unversioned patch repository into a versioned one). You can use
1859 an unversioned patch repository into a versioned one). You can use
1860 qcommit to commit changes to this queue repository.
1860 qcommit to commit changes to this queue repository.
1861
1861
1862 This command is deprecated. Without -c, it's implied by other relevant
1862 This command is deprecated. Without -c, it's implied by other relevant
1863 commands. With -c, use :hg:`init --mq` instead."""
1863 commands. With -c, use :hg:`init --mq` instead."""
1864 return qinit(ui, repo, create=opts['create_repo'])
1864 return qinit(ui, repo, create=opts['create_repo'])
1865
1865
1866 def clone(ui, source, dest=None, **opts):
1866 def clone(ui, source, dest=None, **opts):
1867 '''clone main and patch repository at same time
1867 '''clone main and patch repository at same time
1868
1868
1869 If source is local, destination will have no patches applied. If
1869 If source is local, destination will have no patches applied. If
1870 source is remote, this command can not check if patches are
1870 source is remote, this command can not check if patches are
1871 applied in source, so cannot guarantee that patches are not
1871 applied in source, so cannot guarantee that patches are not
1872 applied in destination. If you clone remote repository, be sure
1872 applied in destination. If you clone remote repository, be sure
1873 before that it has no patches applied.
1873 before that it has no patches applied.
1874
1874
1875 Source patch repository is looked for in <src>/.hg/patches by
1875 Source patch repository is looked for in <src>/.hg/patches by
1876 default. Use -p <url> to change.
1876 default. Use -p <url> to change.
1877
1877
1878 The patch directory must be a nested Mercurial repository, as
1878 The patch directory must be a nested Mercurial repository, as
1879 would be created by :hg:`init --mq`.
1879 would be created by :hg:`init --mq`.
1880 '''
1880 '''
1881 def patchdir(repo):
1881 def patchdir(repo):
1882 url = repo.url()
1882 url = repo.url()
1883 if url.endswith('/'):
1883 if url.endswith('/'):
1884 url = url[:-1]
1884 url = url[:-1]
1885 return url + '/.hg/patches'
1885 return url + '/.hg/patches'
1886 if dest is None:
1886 if dest is None:
1887 dest = hg.defaultdest(source)
1887 dest = hg.defaultdest(source)
1888 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1888 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1889 if opts['patches']:
1889 if opts['patches']:
1890 patchespath = ui.expandpath(opts['patches'])
1890 patchespath = ui.expandpath(opts['patches'])
1891 else:
1891 else:
1892 patchespath = patchdir(sr)
1892 patchespath = patchdir(sr)
1893 try:
1893 try:
1894 hg.repository(ui, patchespath)
1894 hg.repository(ui, patchespath)
1895 except error.RepoError:
1895 except error.RepoError:
1896 raise util.Abort(_('versioned patch repository not found'
1896 raise util.Abort(_('versioned patch repository not found'
1897 ' (see init --mq)'))
1897 ' (see init --mq)'))
1898 qbase, destrev = None, None
1898 qbase, destrev = None, None
1899 if sr.local():
1899 if sr.local():
1900 if sr.mq.applied:
1900 if sr.mq.applied:
1901 qbase = sr.mq.applied[0].node
1901 qbase = sr.mq.applied[0].node
1902 if not hg.islocal(dest):
1902 if not hg.islocal(dest):
1903 heads = set(sr.heads())
1903 heads = set(sr.heads())
1904 destrev = list(heads.difference(sr.heads(qbase)))
1904 destrev = list(heads.difference(sr.heads(qbase)))
1905 destrev.append(sr.changelog.parents(qbase)[0])
1905 destrev.append(sr.changelog.parents(qbase)[0])
1906 elif sr.capable('lookup'):
1906 elif sr.capable('lookup'):
1907 try:
1907 try:
1908 qbase = sr.lookup('qbase')
1908 qbase = sr.lookup('qbase')
1909 except error.RepoError:
1909 except error.RepoError:
1910 pass
1910 pass
1911 ui.note(_('cloning main repository\n'))
1911 ui.note(_('cloning main repository\n'))
1912 sr, dr = hg.clone(ui, sr.url(), dest,
1912 sr, dr = hg.clone(ui, sr.url(), dest,
1913 pull=opts['pull'],
1913 pull=opts['pull'],
1914 rev=destrev,
1914 rev=destrev,
1915 update=False,
1915 update=False,
1916 stream=opts['uncompressed'])
1916 stream=opts['uncompressed'])
1917 ui.note(_('cloning patch repository\n'))
1917 ui.note(_('cloning patch repository\n'))
1918 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1918 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1919 pull=opts['pull'], update=not opts['noupdate'],
1919 pull=opts['pull'], update=not opts['noupdate'],
1920 stream=opts['uncompressed'])
1920 stream=opts['uncompressed'])
1921 if dr.local():
1921 if dr.local():
1922 if qbase:
1922 if qbase:
1923 ui.note(_('stripping applied patches from destination '
1923 ui.note(_('stripping applied patches from destination '
1924 'repository\n'))
1924 'repository\n'))
1925 dr.mq.strip(dr, qbase, update=False, backup=None)
1925 dr.mq.strip(dr, qbase, update=False, backup=None)
1926 if not opts['noupdate']:
1926 if not opts['noupdate']:
1927 ui.note(_('updating destination repository\n'))
1927 ui.note(_('updating destination repository\n'))
1928 hg.update(dr, dr.changelog.tip())
1928 hg.update(dr, dr.changelog.tip())
1929
1929
1930 def commit(ui, repo, *pats, **opts):
1930 def commit(ui, repo, *pats, **opts):
1931 """commit changes in the queue repository (DEPRECATED)
1931 """commit changes in the queue repository (DEPRECATED)
1932
1932
1933 This command is deprecated; use :hg:`commit --mq` instead."""
1933 This command is deprecated; use :hg:`commit --mq` instead."""
1934 q = repo.mq
1934 q = repo.mq
1935 r = q.qrepo()
1935 r = q.qrepo()
1936 if not r:
1936 if not r:
1937 raise util.Abort('no queue repository')
1937 raise util.Abort('no queue repository')
1938 commands.commit(r.ui, r, *pats, **opts)
1938 commands.commit(r.ui, r, *pats, **opts)
1939
1939
1940 def series(ui, repo, **opts):
1940 def series(ui, repo, **opts):
1941 """print the entire series file"""
1941 """print the entire series file"""
1942 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1942 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1943 return 0
1943 return 0
1944
1944
1945 def top(ui, repo, **opts):
1945 def top(ui, repo, **opts):
1946 """print the name of the current patch"""
1946 """print the name of the current patch"""
1947 q = repo.mq
1947 q = repo.mq
1948 t = q.applied and q.series_end(True) or 0
1948 t = q.applied and q.series_end(True) or 0
1949 if t:
1949 if t:
1950 return q.qseries(repo, start=t - 1, length=1, status='A',
1950 return q.qseries(repo, start=t - 1, length=1, status='A',
1951 summary=opts.get('summary'))
1951 summary=opts.get('summary'))
1952 else:
1952 else:
1953 ui.write(_("no patches applied\n"))
1953 ui.write(_("no patches applied\n"))
1954 return 1
1954 return 1
1955
1955
1956 def next(ui, repo, **opts):
1956 def next(ui, repo, **opts):
1957 """print the name of the next patch"""
1957 """print the name of the next patch"""
1958 q = repo.mq
1958 q = repo.mq
1959 end = q.series_end()
1959 end = q.series_end()
1960 if end == len(q.series):
1960 if end == len(q.series):
1961 ui.write(_("all patches applied\n"))
1961 ui.write(_("all patches applied\n"))
1962 return 1
1962 return 1
1963 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1963 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1964
1964
1965 def prev(ui, repo, **opts):
1965 def prev(ui, repo, **opts):
1966 """print the name of the previous patch"""
1966 """print the name of the previous patch"""
1967 q = repo.mq
1967 q = repo.mq
1968 l = len(q.applied)
1968 l = len(q.applied)
1969 if l == 1:
1969 if l == 1:
1970 ui.write(_("only one patch applied\n"))
1970 ui.write(_("only one patch applied\n"))
1971 return 1
1971 return 1
1972 if not l:
1972 if not l:
1973 ui.write(_("no patches applied\n"))
1973 ui.write(_("no patches applied\n"))
1974 return 1
1974 return 1
1975 return q.qseries(repo, start=l - 2, length=1, status='A',
1975 return q.qseries(repo, start=l - 2, length=1, status='A',
1976 summary=opts.get('summary'))
1976 summary=opts.get('summary'))
1977
1977
1978 def setupheaderopts(ui, opts):
1978 def setupheaderopts(ui, opts):
1979 if not opts.get('user') and opts.get('currentuser'):
1979 if not opts.get('user') and opts.get('currentuser'):
1980 opts['user'] = ui.username()
1980 opts['user'] = ui.username()
1981 if not opts.get('date') and opts.get('currentdate'):
1981 if not opts.get('date') and opts.get('currentdate'):
1982 opts['date'] = "%d %d" % util.makedate()
1982 opts['date'] = "%d %d" % util.makedate()
1983
1983
1984 def new(ui, repo, patch, *args, **opts):
1984 def new(ui, repo, patch, *args, **opts):
1985 """create a new patch
1985 """create a new patch
1986
1986
1987 qnew creates a new patch on top of the currently-applied patch (if
1987 qnew creates a new patch on top of the currently-applied patch (if
1988 any). The patch will be initialized with any outstanding changes
1988 any). The patch will be initialized with any outstanding changes
1989 in the working directory. You may also use -I/--include,
1989 in the working directory. You may also use -I/--include,
1990 -X/--exclude, and/or a list of files after the patch name to add
1990 -X/--exclude, and/or a list of files after the patch name to add
1991 only changes to matching files to the new patch, leaving the rest
1991 only changes to matching files to the new patch, leaving the rest
1992 as uncommitted modifications.
1992 as uncommitted modifications.
1993
1993
1994 -u/--user and -d/--date can be used to set the (given) user and
1994 -u/--user and -d/--date can be used to set the (given) user and
1995 date, respectively. -U/--currentuser and -D/--currentdate set user
1995 date, respectively. -U/--currentuser and -D/--currentdate set user
1996 to current user and date to current date.
1996 to current user and date to current date.
1997
1997
1998 -e/--edit, -m/--message or -l/--logfile set the patch header as
1998 -e/--edit, -m/--message or -l/--logfile set the patch header as
1999 well as the commit message. If none is specified, the header is
1999 well as the commit message. If none is specified, the header is
2000 empty and the commit message is '[mq]: PATCH'.
2000 empty and the commit message is '[mq]: PATCH'.
2001
2001
2002 Use the -g/--git option to keep the patch in the git extended diff
2002 Use the -g/--git option to keep the patch in the git extended diff
2003 format. Read the diffs help topic for more information on why this
2003 format. Read the diffs help topic for more information on why this
2004 is important for preserving permission changes and copy/rename
2004 is important for preserving permission changes and copy/rename
2005 information.
2005 information.
2006 """
2006 """
2007 msg = cmdutil.logmessage(opts)
2007 msg = cmdutil.logmessage(opts)
2008 def getmsg():
2008 def getmsg():
2009 return ui.edit(msg, ui.username())
2009 return ui.edit(msg, ui.username())
2010 q = repo.mq
2010 q = repo.mq
2011 opts['msg'] = msg
2011 opts['msg'] = msg
2012 if opts.get('edit'):
2012 if opts.get('edit'):
2013 opts['msg'] = getmsg
2013 opts['msg'] = getmsg
2014 else:
2014 else:
2015 opts['msg'] = msg
2015 opts['msg'] = msg
2016 setupheaderopts(ui, opts)
2016 setupheaderopts(ui, opts)
2017 q.new(repo, patch, *args, **opts)
2017 q.new(repo, patch, *args, **opts)
2018 q.save_dirty()
2018 q.save_dirty()
2019 return 0
2019 return 0
2020
2020
2021 def refresh(ui, repo, *pats, **opts):
2021 def refresh(ui, repo, *pats, **opts):
2022 """update the current patch
2022 """update the current patch
2023
2023
2024 If any file patterns are provided, the refreshed patch will
2024 If any file patterns are provided, the refreshed patch will
2025 contain only the modifications that match those patterns; the
2025 contain only the modifications that match those patterns; the
2026 remaining modifications will remain in the working directory.
2026 remaining modifications will remain in the working directory.
2027
2027
2028 If -s/--short is specified, files currently included in the patch
2028 If -s/--short is specified, files currently included in the patch
2029 will be refreshed just like matched files and remain in the patch.
2029 will be refreshed just like matched files and remain in the patch.
2030
2030
2031 hg add/remove/copy/rename work as usual, though you might want to
2031 hg add/remove/copy/rename work as usual, though you might want to
2032 use git-style patches (-g/--git or [diff] git=1) to track copies
2032 use git-style patches (-g/--git or [diff] git=1) to track copies
2033 and renames. See the diffs help topic for more information on the
2033 and renames. See the diffs help topic for more information on the
2034 git diff format.
2034 git diff format.
2035 """
2035 """
2036 q = repo.mq
2036 q = repo.mq
2037 message = cmdutil.logmessage(opts)
2037 message = cmdutil.logmessage(opts)
2038 if opts['edit']:
2038 if opts['edit']:
2039 if not q.applied:
2039 if not q.applied:
2040 ui.write(_("no patches applied\n"))
2040 ui.write(_("no patches applied\n"))
2041 return 1
2041 return 1
2042 if message:
2042 if message:
2043 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2043 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2044 patch = q.applied[-1].name
2044 patch = q.applied[-1].name
2045 ph = patchheader(q.join(patch), q.plainmode)
2045 ph = patchheader(q.join(patch), q.plainmode)
2046 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2046 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2047 setupheaderopts(ui, opts)
2047 setupheaderopts(ui, opts)
2048 ret = q.refresh(repo, pats, msg=message, **opts)
2048 ret = q.refresh(repo, pats, msg=message, **opts)
2049 q.save_dirty()
2049 q.save_dirty()
2050 return ret
2050 return ret
2051
2051
2052 def diff(ui, repo, *pats, **opts):
2052 def diff(ui, repo, *pats, **opts):
2053 """diff of the current patch and subsequent modifications
2053 """diff of the current patch and subsequent modifications
2054
2054
2055 Shows a diff which includes the current patch as well as any
2055 Shows a diff which includes the current patch as well as any
2056 changes which have been made in the working directory since the
2056 changes which have been made in the working directory since the
2057 last refresh (thus showing what the current patch would become
2057 last refresh (thus showing what the current patch would become
2058 after a qrefresh).
2058 after a qrefresh).
2059
2059
2060 Use :hg:`diff` if you only want to see the changes made since the
2060 Use :hg:`diff` if you only want to see the changes made since the
2061 last qrefresh, or :hg:`export qtip` if you want to see changes
2061 last qrefresh, or :hg:`export qtip` if you want to see changes
2062 made by the current patch without including changes made since the
2062 made by the current patch without including changes made since the
2063 qrefresh.
2063 qrefresh.
2064 """
2064 """
2065 repo.mq.diff(repo, pats, opts)
2065 repo.mq.diff(repo, pats, opts)
2066 return 0
2066 return 0
2067
2067
2068 def fold(ui, repo, *files, **opts):
2068 def fold(ui, repo, *files, **opts):
2069 """fold the named patches into the current patch
2069 """fold the named patches into the current patch
2070
2070
2071 Patches must not yet be applied. Each patch will be successively
2071 Patches must not yet be applied. Each patch will be successively
2072 applied to the current patch in the order given. If all the
2072 applied to the current patch in the order given. If all the
2073 patches apply successfully, the current patch will be refreshed
2073 patches apply successfully, the current patch will be refreshed
2074 with the new cumulative patch, and the folded patches will be
2074 with the new cumulative patch, and the folded patches will be
2075 deleted. With -k/--keep, the folded patch files will not be
2075 deleted. With -k/--keep, the folded patch files will not be
2076 removed afterwards.
2076 removed afterwards.
2077
2077
2078 The header for each folded patch will be concatenated with the
2078 The header for each folded patch will be concatenated with the
2079 current patch header, separated by a line of '* * *'."""
2079 current patch header, separated by a line of '* * *'."""
2080
2080
2081 q = repo.mq
2081 q = repo.mq
2082
2082
2083 if not files:
2083 if not files:
2084 raise util.Abort(_('qfold requires at least one patch name'))
2084 raise util.Abort(_('qfold requires at least one patch name'))
2085 if not q.check_toppatch(repo)[0]:
2085 if not q.check_toppatch(repo)[0]:
2086 raise util.Abort(_('No patches applied'))
2086 raise util.Abort(_('no patches applied'))
2087 q.check_localchanges(repo)
2087 q.check_localchanges(repo)
2088
2088
2089 message = cmdutil.logmessage(opts)
2089 message = cmdutil.logmessage(opts)
2090 if opts['edit']:
2090 if opts['edit']:
2091 if message:
2091 if message:
2092 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2092 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2093
2093
2094 parent = q.lookup('qtip')
2094 parent = q.lookup('qtip')
2095 patches = []
2095 patches = []
2096 messages = []
2096 messages = []
2097 for f in files:
2097 for f in files:
2098 p = q.lookup(f)
2098 p = q.lookup(f)
2099 if p in patches or p == parent:
2099 if p in patches or p == parent:
2100 ui.warn(_('Skipping already folded patch %s') % p)
2100 ui.warn(_('Skipping already folded patch %s') % p)
2101 if q.isapplied(p):
2101 if q.isapplied(p):
2102 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2102 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2103 patches.append(p)
2103 patches.append(p)
2104
2104
2105 for p in patches:
2105 for p in patches:
2106 if not message:
2106 if not message:
2107 ph = patchheader(q.join(p), q.plainmode)
2107 ph = patchheader(q.join(p), q.plainmode)
2108 if ph.message:
2108 if ph.message:
2109 messages.append(ph.message)
2109 messages.append(ph.message)
2110 pf = q.join(p)
2110 pf = q.join(p)
2111 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2111 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2112 if not patchsuccess:
2112 if not patchsuccess:
2113 raise util.Abort(_('Error folding patch %s') % p)
2113 raise util.Abort(_('error folding patch %s') % p)
2114 patch.updatedir(ui, repo, files)
2114 patch.updatedir(ui, repo, files)
2115
2115
2116 if not message:
2116 if not message:
2117 ph = patchheader(q.join(parent), q.plainmode)
2117 ph = patchheader(q.join(parent), q.plainmode)
2118 message, user = ph.message, ph.user
2118 message, user = ph.message, ph.user
2119 for msg in messages:
2119 for msg in messages:
2120 message.append('* * *')
2120 message.append('* * *')
2121 message.extend(msg)
2121 message.extend(msg)
2122 message = '\n'.join(message)
2122 message = '\n'.join(message)
2123
2123
2124 if opts['edit']:
2124 if opts['edit']:
2125 message = ui.edit(message, user or ui.username())
2125 message = ui.edit(message, user or ui.username())
2126
2126
2127 diffopts = q.patchopts(q.diffopts(), *patches)
2127 diffopts = q.patchopts(q.diffopts(), *patches)
2128 q.refresh(repo, msg=message, git=diffopts.git)
2128 q.refresh(repo, msg=message, git=diffopts.git)
2129 q.delete(repo, patches, opts)
2129 q.delete(repo, patches, opts)
2130 q.save_dirty()
2130 q.save_dirty()
2131
2131
2132 def goto(ui, repo, patch, **opts):
2132 def goto(ui, repo, patch, **opts):
2133 '''push or pop patches until named patch is at top of stack'''
2133 '''push or pop patches until named patch is at top of stack'''
2134 q = repo.mq
2134 q = repo.mq
2135 patch = q.lookup(patch)
2135 patch = q.lookup(patch)
2136 if q.isapplied(patch):
2136 if q.isapplied(patch):
2137 ret = q.pop(repo, patch, force=opts['force'])
2137 ret = q.pop(repo, patch, force=opts['force'])
2138 else:
2138 else:
2139 ret = q.push(repo, patch, force=opts['force'])
2139 ret = q.push(repo, patch, force=opts['force'])
2140 q.save_dirty()
2140 q.save_dirty()
2141 return ret
2141 return ret
2142
2142
2143 def guard(ui, repo, *args, **opts):
2143 def guard(ui, repo, *args, **opts):
2144 '''set or print guards for a patch
2144 '''set or print guards for a patch
2145
2145
2146 Guards control whether a patch can be pushed. A patch with no
2146 Guards control whether a patch can be pushed. A patch with no
2147 guards is always pushed. A patch with a positive guard ("+foo") is
2147 guards is always pushed. A patch with a positive guard ("+foo") is
2148 pushed only if the :hg:`qselect` command has activated it. A patch with
2148 pushed only if the :hg:`qselect` command has activated it. A patch with
2149 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2149 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2150 has activated it.
2150 has activated it.
2151
2151
2152 With no arguments, print the currently active guards.
2152 With no arguments, print the currently active guards.
2153 With arguments, set guards for the named patch.
2153 With arguments, set guards for the named patch.
2154 NOTE: Specifying negative guards now requires '--'.
2154 NOTE: Specifying negative guards now requires '--'.
2155
2155
2156 To set guards on another patch::
2156 To set guards on another patch::
2157
2157
2158 hg qguard other.patch -- +2.6.17 -stable
2158 hg qguard other.patch -- +2.6.17 -stable
2159 '''
2159 '''
2160 def status(idx):
2160 def status(idx):
2161 guards = q.series_guards[idx] or ['unguarded']
2161 guards = q.series_guards[idx] or ['unguarded']
2162 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2162 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2163 for i, guard in enumerate(guards):
2163 for i, guard in enumerate(guards):
2164 if guard.startswith('+'):
2164 if guard.startswith('+'):
2165 ui.write(guard, label='qguard.positive')
2165 ui.write(guard, label='qguard.positive')
2166 elif guard.startswith('-'):
2166 elif guard.startswith('-'):
2167 ui.write(guard, label='qguard.negative')
2167 ui.write(guard, label='qguard.negative')
2168 else:
2168 else:
2169 ui.write(guard, label='qguard.unguarded')
2169 ui.write(guard, label='qguard.unguarded')
2170 if i != len(guards) - 1:
2170 if i != len(guards) - 1:
2171 ui.write(' ')
2171 ui.write(' ')
2172 ui.write('\n')
2172 ui.write('\n')
2173 q = repo.mq
2173 q = repo.mq
2174 patch = None
2174 patch = None
2175 args = list(args)
2175 args = list(args)
2176 if opts['list']:
2176 if opts['list']:
2177 if args or opts['none']:
2177 if args or opts['none']:
2178 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2178 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2179 for i in xrange(len(q.series)):
2179 for i in xrange(len(q.series)):
2180 status(i)
2180 status(i)
2181 return
2181 return
2182 if not args or args[0][0:1] in '-+':
2182 if not args or args[0][0:1] in '-+':
2183 if not q.applied:
2183 if not q.applied:
2184 raise util.Abort(_('no patches applied'))
2184 raise util.Abort(_('no patches applied'))
2185 patch = q.applied[-1].name
2185 patch = q.applied[-1].name
2186 if patch is None and args[0][0:1] not in '-+':
2186 if patch is None and args[0][0:1] not in '-+':
2187 patch = args.pop(0)
2187 patch = args.pop(0)
2188 if patch is None:
2188 if patch is None:
2189 raise util.Abort(_('no patch to work with'))
2189 raise util.Abort(_('no patch to work with'))
2190 if args or opts['none']:
2190 if args or opts['none']:
2191 idx = q.find_series(patch)
2191 idx = q.find_series(patch)
2192 if idx is None:
2192 if idx is None:
2193 raise util.Abort(_('no patch named %s') % patch)
2193 raise util.Abort(_('no patch named %s') % patch)
2194 q.set_guards(idx, args)
2194 q.set_guards(idx, args)
2195 q.save_dirty()
2195 q.save_dirty()
2196 else:
2196 else:
2197 status(q.series.index(q.lookup(patch)))
2197 status(q.series.index(q.lookup(patch)))
2198
2198
2199 def header(ui, repo, patch=None):
2199 def header(ui, repo, patch=None):
2200 """print the header of the topmost or specified patch"""
2200 """print the header of the topmost or specified patch"""
2201 q = repo.mq
2201 q = repo.mq
2202
2202
2203 if patch:
2203 if patch:
2204 patch = q.lookup(patch)
2204 patch = q.lookup(patch)
2205 else:
2205 else:
2206 if not q.applied:
2206 if not q.applied:
2207 ui.write(_('no patches applied\n'))
2207 ui.write(_('no patches applied\n'))
2208 return 1
2208 return 1
2209 patch = q.lookup('qtip')
2209 patch = q.lookup('qtip')
2210 ph = patchheader(q.join(patch), q.plainmode)
2210 ph = patchheader(q.join(patch), q.plainmode)
2211
2211
2212 ui.write('\n'.join(ph.message) + '\n')
2212 ui.write('\n'.join(ph.message) + '\n')
2213
2213
2214 def lastsavename(path):
2214 def lastsavename(path):
2215 (directory, base) = os.path.split(path)
2215 (directory, base) = os.path.split(path)
2216 names = os.listdir(directory)
2216 names = os.listdir(directory)
2217 namere = re.compile("%s.([0-9]+)" % base)
2217 namere = re.compile("%s.([0-9]+)" % base)
2218 maxindex = None
2218 maxindex = None
2219 maxname = None
2219 maxname = None
2220 for f in names:
2220 for f in names:
2221 m = namere.match(f)
2221 m = namere.match(f)
2222 if m:
2222 if m:
2223 index = int(m.group(1))
2223 index = int(m.group(1))
2224 if maxindex is None or index > maxindex:
2224 if maxindex is None or index > maxindex:
2225 maxindex = index
2225 maxindex = index
2226 maxname = f
2226 maxname = f
2227 if maxname:
2227 if maxname:
2228 return (os.path.join(directory, maxname), maxindex)
2228 return (os.path.join(directory, maxname), maxindex)
2229 return (None, None)
2229 return (None, None)
2230
2230
2231 def savename(path):
2231 def savename(path):
2232 (last, index) = lastsavename(path)
2232 (last, index) = lastsavename(path)
2233 if last is None:
2233 if last is None:
2234 index = 0
2234 index = 0
2235 newpath = path + ".%d" % (index + 1)
2235 newpath = path + ".%d" % (index + 1)
2236 return newpath
2236 return newpath
2237
2237
2238 def push(ui, repo, patch=None, **opts):
2238 def push(ui, repo, patch=None, **opts):
2239 """push the next patch onto the stack
2239 """push the next patch onto the stack
2240
2240
2241 When -f/--force is applied, all local changes in patched files
2241 When -f/--force is applied, all local changes in patched files
2242 will be lost.
2242 will be lost.
2243 """
2243 """
2244 q = repo.mq
2244 q = repo.mq
2245 mergeq = None
2245 mergeq = None
2246
2246
2247 if opts['merge']:
2247 if opts['merge']:
2248 if opts['name']:
2248 if opts['name']:
2249 newpath = repo.join(opts['name'])
2249 newpath = repo.join(opts['name'])
2250 else:
2250 else:
2251 newpath, i = lastsavename(q.path)
2251 newpath, i = lastsavename(q.path)
2252 if not newpath:
2252 if not newpath:
2253 ui.warn(_("no saved queues found, please use -n\n"))
2253 ui.warn(_("no saved queues found, please use -n\n"))
2254 return 1
2254 return 1
2255 mergeq = queue(ui, repo.join(""), newpath)
2255 mergeq = queue(ui, repo.join(""), newpath)
2256 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2256 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2257 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2257 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2258 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2258 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2259 return ret
2259 return ret
2260
2260
2261 def pop(ui, repo, patch=None, **opts):
2261 def pop(ui, repo, patch=None, **opts):
2262 """pop the current patch off the stack
2262 """pop the current patch off the stack
2263
2263
2264 By default, pops off the top of the patch stack. If given a patch
2264 By default, pops off the top of the patch stack. If given a patch
2265 name, keeps popping off patches until the named patch is at the
2265 name, keeps popping off patches until the named patch is at the
2266 top of the stack.
2266 top of the stack.
2267 """
2267 """
2268 localupdate = True
2268 localupdate = True
2269 if opts['name']:
2269 if opts['name']:
2270 q = queue(ui, repo.join(""), repo.join(opts['name']))
2270 q = queue(ui, repo.join(""), repo.join(opts['name']))
2271 ui.warn(_('using patch queue: %s\n') % q.path)
2271 ui.warn(_('using patch queue: %s\n') % q.path)
2272 localupdate = False
2272 localupdate = False
2273 else:
2273 else:
2274 q = repo.mq
2274 q = repo.mq
2275 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2275 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2276 all=opts['all'])
2276 all=opts['all'])
2277 q.save_dirty()
2277 q.save_dirty()
2278 return ret
2278 return ret
2279
2279
2280 def rename(ui, repo, patch, name=None, **opts):
2280 def rename(ui, repo, patch, name=None, **opts):
2281 """rename a patch
2281 """rename a patch
2282
2282
2283 With one argument, renames the current patch to PATCH1.
2283 With one argument, renames the current patch to PATCH1.
2284 With two arguments, renames PATCH1 to PATCH2."""
2284 With two arguments, renames PATCH1 to PATCH2."""
2285
2285
2286 q = repo.mq
2286 q = repo.mq
2287
2287
2288 if not name:
2288 if not name:
2289 name = patch
2289 name = patch
2290 patch = None
2290 patch = None
2291
2291
2292 if patch:
2292 if patch:
2293 patch = q.lookup(patch)
2293 patch = q.lookup(patch)
2294 else:
2294 else:
2295 if not q.applied:
2295 if not q.applied:
2296 ui.write(_('no patches applied\n'))
2296 ui.write(_('no patches applied\n'))
2297 return
2297 return
2298 patch = q.lookup('qtip')
2298 patch = q.lookup('qtip')
2299 absdest = q.join(name)
2299 absdest = q.join(name)
2300 if os.path.isdir(absdest):
2300 if os.path.isdir(absdest):
2301 name = normname(os.path.join(name, os.path.basename(patch)))
2301 name = normname(os.path.join(name, os.path.basename(patch)))
2302 absdest = q.join(name)
2302 absdest = q.join(name)
2303 if os.path.exists(absdest):
2303 if os.path.exists(absdest):
2304 raise util.Abort(_('%s already exists') % absdest)
2304 raise util.Abort(_('%s already exists') % absdest)
2305
2305
2306 if name in q.series:
2306 if name in q.series:
2307 raise util.Abort(
2307 raise util.Abort(
2308 _('A patch named %s already exists in the series file') % name)
2308 _('A patch named %s already exists in the series file') % name)
2309
2309
2310 ui.note(_('renaming %s to %s\n') % (patch, name))
2310 ui.note(_('renaming %s to %s\n') % (patch, name))
2311 i = q.find_series(patch)
2311 i = q.find_series(patch)
2312 guards = q.guard_re.findall(q.full_series[i])
2312 guards = q.guard_re.findall(q.full_series[i])
2313 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2313 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2314 q.parse_series()
2314 q.parse_series()
2315 q.series_dirty = 1
2315 q.series_dirty = 1
2316
2316
2317 info = q.isapplied(patch)
2317 info = q.isapplied(patch)
2318 if info:
2318 if info:
2319 q.applied[info[0]] = statusentry(info[1], name)
2319 q.applied[info[0]] = statusentry(info[1], name)
2320 q.applied_dirty = 1
2320 q.applied_dirty = 1
2321
2321
2322 destdir = os.path.dirname(absdest)
2322 destdir = os.path.dirname(absdest)
2323 if not os.path.isdir(destdir):
2323 if not os.path.isdir(destdir):
2324 os.makedirs(destdir)
2324 os.makedirs(destdir)
2325 util.rename(q.join(patch), absdest)
2325 util.rename(q.join(patch), absdest)
2326 r = q.qrepo()
2326 r = q.qrepo()
2327 if r:
2327 if r:
2328 wctx = r[None]
2328 wctx = r[None]
2329 wlock = r.wlock()
2329 wlock = r.wlock()
2330 try:
2330 try:
2331 if r.dirstate[patch] == 'a':
2331 if r.dirstate[patch] == 'a':
2332 r.dirstate.forget(patch)
2332 r.dirstate.forget(patch)
2333 r.dirstate.add(name)
2333 r.dirstate.add(name)
2334 else:
2334 else:
2335 if r.dirstate[name] == 'r':
2335 if r.dirstate[name] == 'r':
2336 wctx.undelete([name])
2336 wctx.undelete([name])
2337 wctx.copy(patch, name)
2337 wctx.copy(patch, name)
2338 wctx.remove([patch], False)
2338 wctx.remove([patch], False)
2339 finally:
2339 finally:
2340 wlock.release()
2340 wlock.release()
2341
2341
2342 q.save_dirty()
2342 q.save_dirty()
2343
2343
2344 def restore(ui, repo, rev, **opts):
2344 def restore(ui, repo, rev, **opts):
2345 """restore the queue state saved by a revision (DEPRECATED)
2345 """restore the queue state saved by a revision (DEPRECATED)
2346
2346
2347 This command is deprecated, use rebase --mq instead."""
2347 This command is deprecated, use rebase --mq instead."""
2348 rev = repo.lookup(rev)
2348 rev = repo.lookup(rev)
2349 q = repo.mq
2349 q = repo.mq
2350 q.restore(repo, rev, delete=opts['delete'],
2350 q.restore(repo, rev, delete=opts['delete'],
2351 qupdate=opts['update'])
2351 qupdate=opts['update'])
2352 q.save_dirty()
2352 q.save_dirty()
2353 return 0
2353 return 0
2354
2354
2355 def save(ui, repo, **opts):
2355 def save(ui, repo, **opts):
2356 """save current queue state (DEPRECATED)
2356 """save current queue state (DEPRECATED)
2357
2357
2358 This command is deprecated, use rebase --mq instead."""
2358 This command is deprecated, use rebase --mq instead."""
2359 q = repo.mq
2359 q = repo.mq
2360 message = cmdutil.logmessage(opts)
2360 message = cmdutil.logmessage(opts)
2361 ret = q.save(repo, msg=message)
2361 ret = q.save(repo, msg=message)
2362 if ret:
2362 if ret:
2363 return ret
2363 return ret
2364 q.save_dirty()
2364 q.save_dirty()
2365 if opts['copy']:
2365 if opts['copy']:
2366 path = q.path
2366 path = q.path
2367 if opts['name']:
2367 if opts['name']:
2368 newpath = os.path.join(q.basepath, opts['name'])
2368 newpath = os.path.join(q.basepath, opts['name'])
2369 if os.path.exists(newpath):
2369 if os.path.exists(newpath):
2370 if not os.path.isdir(newpath):
2370 if not os.path.isdir(newpath):
2371 raise util.Abort(_('destination %s exists and is not '
2371 raise util.Abort(_('destination %s exists and is not '
2372 'a directory') % newpath)
2372 'a directory') % newpath)
2373 if not opts['force']:
2373 if not opts['force']:
2374 raise util.Abort(_('destination %s exists, '
2374 raise util.Abort(_('destination %s exists, '
2375 'use -f to force') % newpath)
2375 'use -f to force') % newpath)
2376 else:
2376 else:
2377 newpath = savename(path)
2377 newpath = savename(path)
2378 ui.warn(_("copy %s to %s\n") % (path, newpath))
2378 ui.warn(_("copy %s to %s\n") % (path, newpath))
2379 util.copyfiles(path, newpath)
2379 util.copyfiles(path, newpath)
2380 if opts['empty']:
2380 if opts['empty']:
2381 try:
2381 try:
2382 os.unlink(q.join(q.status_path))
2382 os.unlink(q.join(q.status_path))
2383 except:
2383 except:
2384 pass
2384 pass
2385 return 0
2385 return 0
2386
2386
2387 def strip(ui, repo, rev, **opts):
2387 def strip(ui, repo, rev, **opts):
2388 """strip a changeset and all its descendants from the repository
2388 """strip a changeset and all its descendants from the repository
2389
2389
2390 The strip command removes all changesets whose local revision
2390 The strip command removes all changesets whose local revision
2391 number is greater than or equal to REV, and then restores any
2391 number is greater than or equal to REV, and then restores any
2392 changesets that are not descendants of REV. If the working
2392 changesets that are not descendants of REV. If the working
2393 directory has uncommitted changes, the operation is aborted unless
2393 directory has uncommitted changes, the operation is aborted unless
2394 the --force flag is supplied.
2394 the --force flag is supplied.
2395
2395
2396 If a parent of the working directory is stripped, then the working
2396 If a parent of the working directory is stripped, then the working
2397 directory will automatically be updated to the most recent
2397 directory will automatically be updated to the most recent
2398 available ancestor of the stripped parent after the operation
2398 available ancestor of the stripped parent after the operation
2399 completes.
2399 completes.
2400
2400
2401 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2401 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2402 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2402 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2403 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2403 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2404 where BUNDLE is the bundle file created by the strip. Note that
2404 where BUNDLE is the bundle file created by the strip. Note that
2405 the local revision numbers will in general be different after the
2405 the local revision numbers will in general be different after the
2406 restore.
2406 restore.
2407
2407
2408 Use the --nobackup option to discard the backup bundle once the
2408 Use the --nobackup option to discard the backup bundle once the
2409 operation completes.
2409 operation completes.
2410 """
2410 """
2411 backup = 'all'
2411 backup = 'all'
2412 if opts['backup']:
2412 if opts['backup']:
2413 backup = 'strip'
2413 backup = 'strip'
2414 elif opts['nobackup']:
2414 elif opts['nobackup']:
2415 backup = 'none'
2415 backup = 'none'
2416
2416
2417 rev = repo.lookup(rev)
2417 rev = repo.lookup(rev)
2418 p = repo.dirstate.parents()
2418 p = repo.dirstate.parents()
2419 cl = repo.changelog
2419 cl = repo.changelog
2420 update = True
2420 update = True
2421 if p[0] == nullid:
2421 if p[0] == nullid:
2422 update = False
2422 update = False
2423 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2423 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2424 update = False
2424 update = False
2425 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2425 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2426 update = False
2426 update = False
2427
2427
2428 q = repo.mq
2428 q = repo.mq
2429 if q.applied:
2429 if q.applied:
2430 if rev == cl.ancestor(repo.lookup('qtip'), rev):
2430 if rev == cl.ancestor(repo.lookup('qtip'), rev):
2431 q.applied_dirty = True
2431 q.applied_dirty = True
2432 start = 0
2432 start = 0
2433 end = len(q.applied)
2433 end = len(q.applied)
2434 applied_list = [i.node for i in q.applied]
2434 applied_list = [i.node for i in q.applied]
2435 if rev in applied_list:
2435 if rev in applied_list:
2436 start = applied_list.index(rev)
2436 start = applied_list.index(rev)
2437 del q.applied[start:end]
2437 del q.applied[start:end]
2438 q.save_dirty()
2438 q.save_dirty()
2439
2439
2440 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2440 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2441 return 0
2441 return 0
2442
2442
2443 def select(ui, repo, *args, **opts):
2443 def select(ui, repo, *args, **opts):
2444 '''set or print guarded patches to push
2444 '''set or print guarded patches to push
2445
2445
2446 Use the :hg:`qguard` command to set or print guards on patch, then use
2446 Use the :hg:`qguard` command to set or print guards on patch, then use
2447 qselect to tell mq which guards to use. A patch will be pushed if
2447 qselect to tell mq which guards to use. A patch will be pushed if
2448 it has no guards or any positive guards match the currently
2448 it has no guards or any positive guards match the currently
2449 selected guard, but will not be pushed if any negative guards
2449 selected guard, but will not be pushed if any negative guards
2450 match the current guard. For example::
2450 match the current guard. For example::
2451
2451
2452 qguard foo.patch -stable (negative guard)
2452 qguard foo.patch -stable (negative guard)
2453 qguard bar.patch +stable (positive guard)
2453 qguard bar.patch +stable (positive guard)
2454 qselect stable
2454 qselect stable
2455
2455
2456 This activates the "stable" guard. mq will skip foo.patch (because
2456 This activates the "stable" guard. mq will skip foo.patch (because
2457 it has a negative match) but push bar.patch (because it has a
2457 it has a negative match) but push bar.patch (because it has a
2458 positive match).
2458 positive match).
2459
2459
2460 With no arguments, prints the currently active guards.
2460 With no arguments, prints the currently active guards.
2461 With one argument, sets the active guard.
2461 With one argument, sets the active guard.
2462
2462
2463 Use -n/--none to deactivate guards (no other arguments needed).
2463 Use -n/--none to deactivate guards (no other arguments needed).
2464 When no guards are active, patches with positive guards are
2464 When no guards are active, patches with positive guards are
2465 skipped and patches with negative guards are pushed.
2465 skipped and patches with negative guards are pushed.
2466
2466
2467 qselect can change the guards on applied patches. It does not pop
2467 qselect can change the guards on applied patches. It does not pop
2468 guarded patches by default. Use --pop to pop back to the last
2468 guarded patches by default. Use --pop to pop back to the last
2469 applied patch that is not guarded. Use --reapply (which implies
2469 applied patch that is not guarded. Use --reapply (which implies
2470 --pop) to push back to the current patch afterwards, but skip
2470 --pop) to push back to the current patch afterwards, but skip
2471 guarded patches.
2471 guarded patches.
2472
2472
2473 Use -s/--series to print a list of all guards in the series file
2473 Use -s/--series to print a list of all guards in the series file
2474 (no other arguments needed). Use -v for more information.'''
2474 (no other arguments needed). Use -v for more information.'''
2475
2475
2476 q = repo.mq
2476 q = repo.mq
2477 guards = q.active()
2477 guards = q.active()
2478 if args or opts['none']:
2478 if args or opts['none']:
2479 old_unapplied = q.unapplied(repo)
2479 old_unapplied = q.unapplied(repo)
2480 old_guarded = [i for i in xrange(len(q.applied)) if
2480 old_guarded = [i for i in xrange(len(q.applied)) if
2481 not q.pushable(i)[0]]
2481 not q.pushable(i)[0]]
2482 q.set_active(args)
2482 q.set_active(args)
2483 q.save_dirty()
2483 q.save_dirty()
2484 if not args:
2484 if not args:
2485 ui.status(_('guards deactivated\n'))
2485 ui.status(_('guards deactivated\n'))
2486 if not opts['pop'] and not opts['reapply']:
2486 if not opts['pop'] and not opts['reapply']:
2487 unapplied = q.unapplied(repo)
2487 unapplied = q.unapplied(repo)
2488 guarded = [i for i in xrange(len(q.applied))
2488 guarded = [i for i in xrange(len(q.applied))
2489 if not q.pushable(i)[0]]
2489 if not q.pushable(i)[0]]
2490 if len(unapplied) != len(old_unapplied):
2490 if len(unapplied) != len(old_unapplied):
2491 ui.status(_('number of unguarded, unapplied patches has '
2491 ui.status(_('number of unguarded, unapplied patches has '
2492 'changed from %d to %d\n') %
2492 'changed from %d to %d\n') %
2493 (len(old_unapplied), len(unapplied)))
2493 (len(old_unapplied), len(unapplied)))
2494 if len(guarded) != len(old_guarded):
2494 if len(guarded) != len(old_guarded):
2495 ui.status(_('number of guarded, applied patches has changed '
2495 ui.status(_('number of guarded, applied patches has changed '
2496 'from %d to %d\n') %
2496 'from %d to %d\n') %
2497 (len(old_guarded), len(guarded)))
2497 (len(old_guarded), len(guarded)))
2498 elif opts['series']:
2498 elif opts['series']:
2499 guards = {}
2499 guards = {}
2500 noguards = 0
2500 noguards = 0
2501 for gs in q.series_guards:
2501 for gs in q.series_guards:
2502 if not gs:
2502 if not gs:
2503 noguards += 1
2503 noguards += 1
2504 for g in gs:
2504 for g in gs:
2505 guards.setdefault(g, 0)
2505 guards.setdefault(g, 0)
2506 guards[g] += 1
2506 guards[g] += 1
2507 if ui.verbose:
2507 if ui.verbose:
2508 guards['NONE'] = noguards
2508 guards['NONE'] = noguards
2509 guards = guards.items()
2509 guards = guards.items()
2510 guards.sort(key=lambda x: x[0][1:])
2510 guards.sort(key=lambda x: x[0][1:])
2511 if guards:
2511 if guards:
2512 ui.note(_('guards in series file:\n'))
2512 ui.note(_('guards in series file:\n'))
2513 for guard, count in guards:
2513 for guard, count in guards:
2514 ui.note('%2d ' % count)
2514 ui.note('%2d ' % count)
2515 ui.write(guard, '\n')
2515 ui.write(guard, '\n')
2516 else:
2516 else:
2517 ui.note(_('no guards in series file\n'))
2517 ui.note(_('no guards in series file\n'))
2518 else:
2518 else:
2519 if guards:
2519 if guards:
2520 ui.note(_('active guards:\n'))
2520 ui.note(_('active guards:\n'))
2521 for g in guards:
2521 for g in guards:
2522 ui.write(g, '\n')
2522 ui.write(g, '\n')
2523 else:
2523 else:
2524 ui.write(_('no active guards\n'))
2524 ui.write(_('no active guards\n'))
2525 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2525 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2526 popped = False
2526 popped = False
2527 if opts['pop'] or opts['reapply']:
2527 if opts['pop'] or opts['reapply']:
2528 for i in xrange(len(q.applied)):
2528 for i in xrange(len(q.applied)):
2529 pushable, reason = q.pushable(i)
2529 pushable, reason = q.pushable(i)
2530 if not pushable:
2530 if not pushable:
2531 ui.status(_('popping guarded patches\n'))
2531 ui.status(_('popping guarded patches\n'))
2532 popped = True
2532 popped = True
2533 if i == 0:
2533 if i == 0:
2534 q.pop(repo, all=True)
2534 q.pop(repo, all=True)
2535 else:
2535 else:
2536 q.pop(repo, i - 1)
2536 q.pop(repo, i - 1)
2537 break
2537 break
2538 if popped:
2538 if popped:
2539 try:
2539 try:
2540 if reapply:
2540 if reapply:
2541 ui.status(_('reapplying unguarded patches\n'))
2541 ui.status(_('reapplying unguarded patches\n'))
2542 q.push(repo, reapply)
2542 q.push(repo, reapply)
2543 finally:
2543 finally:
2544 q.save_dirty()
2544 q.save_dirty()
2545
2545
2546 def finish(ui, repo, *revrange, **opts):
2546 def finish(ui, repo, *revrange, **opts):
2547 """move applied patches into repository history
2547 """move applied patches into repository history
2548
2548
2549 Finishes the specified revisions (corresponding to applied
2549 Finishes the specified revisions (corresponding to applied
2550 patches) by moving them out of mq control into regular repository
2550 patches) by moving them out of mq control into regular repository
2551 history.
2551 history.
2552
2552
2553 Accepts a revision range or the -a/--applied option. If --applied
2553 Accepts a revision range or the -a/--applied option. If --applied
2554 is specified, all applied mq revisions are removed from mq
2554 is specified, all applied mq revisions are removed from mq
2555 control. Otherwise, the given revisions must be at the base of the
2555 control. Otherwise, the given revisions must be at the base of the
2556 stack of applied patches.
2556 stack of applied patches.
2557
2557
2558 This can be especially useful if your changes have been applied to
2558 This can be especially useful if your changes have been applied to
2559 an upstream repository, or if you are about to push your changes
2559 an upstream repository, or if you are about to push your changes
2560 to upstream.
2560 to upstream.
2561 """
2561 """
2562 if not opts['applied'] and not revrange:
2562 if not opts['applied'] and not revrange:
2563 raise util.Abort(_('no revisions specified'))
2563 raise util.Abort(_('no revisions specified'))
2564 elif opts['applied']:
2564 elif opts['applied']:
2565 revrange = ('qbase:qtip',) + revrange
2565 revrange = ('qbase:qtip',) + revrange
2566
2566
2567 q = repo.mq
2567 q = repo.mq
2568 if not q.applied:
2568 if not q.applied:
2569 ui.status(_('no patches applied\n'))
2569 ui.status(_('no patches applied\n'))
2570 return 0
2570 return 0
2571
2571
2572 revs = cmdutil.revrange(repo, revrange)
2572 revs = cmdutil.revrange(repo, revrange)
2573 q.finish(repo, revs)
2573 q.finish(repo, revs)
2574 q.save_dirty()
2574 q.save_dirty()
2575 return 0
2575 return 0
2576
2576
2577 def qqueue(ui, repo, name=None, **opts):
2577 def qqueue(ui, repo, name=None, **opts):
2578 '''manage multiple patch queues
2578 '''manage multiple patch queues
2579
2579
2580 Supports switching between different patch queues, as well as creating
2580 Supports switching between different patch queues, as well as creating
2581 new patch queues and deleting existing ones.
2581 new patch queues and deleting existing ones.
2582
2582
2583 Omitting a queue name or specifying -l/--list will show you the registered
2583 Omitting a queue name or specifying -l/--list will show you the registered
2584 queues - by default the "normal" patches queue is registered. The currently
2584 queues - by default the "normal" patches queue is registered. The currently
2585 active queue will be marked with "(active)".
2585 active queue will be marked with "(active)".
2586
2586
2587 To create a new queue, use -c/--create. The queue is automatically made
2587 To create a new queue, use -c/--create. The queue is automatically made
2588 active, except in the case where there are applied patches from the
2588 active, except in the case where there are applied patches from the
2589 currently active queue in the repository. Then the queue will only be
2589 currently active queue in the repository. Then the queue will only be
2590 created and switching will fail.
2590 created and switching will fail.
2591
2591
2592 To delete an existing queue, use --delete. You cannot delete the currently
2592 To delete an existing queue, use --delete. You cannot delete the currently
2593 active queue.
2593 active queue.
2594 '''
2594 '''
2595
2595
2596 q = repo.mq
2596 q = repo.mq
2597
2597
2598 _defaultqueue = 'patches'
2598 _defaultqueue = 'patches'
2599 _allqueues = 'patches.queues'
2599 _allqueues = 'patches.queues'
2600 _activequeue = 'patches.queue'
2600 _activequeue = 'patches.queue'
2601
2601
2602 def _getcurrent():
2602 def _getcurrent():
2603 cur = os.path.basename(q.path)
2603 cur = os.path.basename(q.path)
2604 if cur.startswith('patches-'):
2604 if cur.startswith('patches-'):
2605 cur = cur[8:]
2605 cur = cur[8:]
2606 return cur
2606 return cur
2607
2607
2608 def _noqueues():
2608 def _noqueues():
2609 try:
2609 try:
2610 fh = repo.opener(_allqueues, 'r')
2610 fh = repo.opener(_allqueues, 'r')
2611 fh.close()
2611 fh.close()
2612 except IOError:
2612 except IOError:
2613 return True
2613 return True
2614
2614
2615 return False
2615 return False
2616
2616
2617 def _getqueues():
2617 def _getqueues():
2618 current = _getcurrent()
2618 current = _getcurrent()
2619
2619
2620 try:
2620 try:
2621 fh = repo.opener(_allqueues, 'r')
2621 fh = repo.opener(_allqueues, 'r')
2622 queues = [queue.strip() for queue in fh if queue.strip()]
2622 queues = [queue.strip() for queue in fh if queue.strip()]
2623 if current not in queues:
2623 if current not in queues:
2624 queues.append(current)
2624 queues.append(current)
2625 except IOError:
2625 except IOError:
2626 queues = [_defaultqueue]
2626 queues = [_defaultqueue]
2627
2627
2628 return sorted(queues)
2628 return sorted(queues)
2629
2629
2630 def _setactive(name):
2630 def _setactive(name):
2631 if q.applied:
2631 if q.applied:
2632 raise util.Abort(_('patches applied - cannot set new queue active'))
2632 raise util.Abort(_('patches applied - cannot set new queue active'))
2633
2633
2634 fh = repo.opener(_activequeue, 'w')
2634 fh = repo.opener(_activequeue, 'w')
2635 if name != 'patches':
2635 if name != 'patches':
2636 fh.write(name)
2636 fh.write(name)
2637 fh.close()
2637 fh.close()
2638
2638
2639 def _addqueue(name):
2639 def _addqueue(name):
2640 fh = repo.opener(_allqueues, 'a')
2640 fh = repo.opener(_allqueues, 'a')
2641 fh.write('%s\n' % (name,))
2641 fh.write('%s\n' % (name,))
2642 fh.close()
2642 fh.close()
2643
2643
2644 def _validname(name):
2644 def _validname(name):
2645 for n in name:
2645 for n in name:
2646 if n in ':\\/.':
2646 if n in ':\\/.':
2647 return False
2647 return False
2648 return True
2648 return True
2649
2649
2650 if not name or opts.get('list'):
2650 if not name or opts.get('list'):
2651 current = _getcurrent()
2651 current = _getcurrent()
2652 for queue in _getqueues():
2652 for queue in _getqueues():
2653 ui.write('%s' % (queue,))
2653 ui.write('%s' % (queue,))
2654 if queue == current:
2654 if queue == current:
2655 ui.write(_(' (active)\n'))
2655 ui.write(_(' (active)\n'))
2656 else:
2656 else:
2657 ui.write('\n')
2657 ui.write('\n')
2658 return
2658 return
2659
2659
2660 if not _validname(name):
2660 if not _validname(name):
2661 raise util.Abort(
2661 raise util.Abort(
2662 _('invalid queue name, may not contain the characters ":\\/."'))
2662 _('invalid queue name, may not contain the characters ":\\/."'))
2663
2663
2664 existing = _getqueues()
2664 existing = _getqueues()
2665
2665
2666 if opts.get('create'):
2666 if opts.get('create'):
2667 if name in existing:
2667 if name in existing:
2668 raise util.Abort(_('queue "%s" already exists') % name)
2668 raise util.Abort(_('queue "%s" already exists') % name)
2669 if _noqueues():
2669 if _noqueues():
2670 _addqueue(_defaultqueue)
2670 _addqueue(_defaultqueue)
2671 _addqueue(name)
2671 _addqueue(name)
2672 _setactive(name)
2672 _setactive(name)
2673 elif opts.get('delete'):
2673 elif opts.get('delete'):
2674 if name not in existing:
2674 if name not in existing:
2675 raise util.Abort(_('cannot delete queue that does not exist'))
2675 raise util.Abort(_('cannot delete queue that does not exist'))
2676
2676
2677 current = _getcurrent()
2677 current = _getcurrent()
2678
2678
2679 if name == current:
2679 if name == current:
2680 raise util.Abort(_('cannot delete currently active queue'))
2680 raise util.Abort(_('cannot delete currently active queue'))
2681
2681
2682 fh = repo.opener('patches.queues.new', 'w')
2682 fh = repo.opener('patches.queues.new', 'w')
2683 for queue in existing:
2683 for queue in existing:
2684 if queue == name:
2684 if queue == name:
2685 continue
2685 continue
2686 fh.write('%s\n' % (queue,))
2686 fh.write('%s\n' % (queue,))
2687 fh.close()
2687 fh.close()
2688 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2688 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2689 else:
2689 else:
2690 if name not in existing:
2690 if name not in existing:
2691 raise util.Abort(_('use --create to create a new queue'))
2691 raise util.Abort(_('use --create to create a new queue'))
2692 _setactive(name)
2692 _setactive(name)
2693
2693
2694 def reposetup(ui, repo):
2694 def reposetup(ui, repo):
2695 class mqrepo(repo.__class__):
2695 class mqrepo(repo.__class__):
2696 @util.propertycache
2696 @util.propertycache
2697 def mq(self):
2697 def mq(self):
2698 return queue(self.ui, self.join(""))
2698 return queue(self.ui, self.join(""))
2699
2699
2700 def abort_if_wdir_patched(self, errmsg, force=False):
2700 def abort_if_wdir_patched(self, errmsg, force=False):
2701 if self.mq.applied and not force:
2701 if self.mq.applied and not force:
2702 parent = self.dirstate.parents()[0]
2702 parent = self.dirstate.parents()[0]
2703 if parent in [s.node for s in self.mq.applied]:
2703 if parent in [s.node for s in self.mq.applied]:
2704 raise util.Abort(errmsg)
2704 raise util.Abort(errmsg)
2705
2705
2706 def commit(self, text="", user=None, date=None, match=None,
2706 def commit(self, text="", user=None, date=None, match=None,
2707 force=False, editor=False, extra={}):
2707 force=False, editor=False, extra={}):
2708 self.abort_if_wdir_patched(
2708 self.abort_if_wdir_patched(
2709 _('cannot commit over an applied mq patch'),
2709 _('cannot commit over an applied mq patch'),
2710 force)
2710 force)
2711
2711
2712 return super(mqrepo, self).commit(text, user, date, match, force,
2712 return super(mqrepo, self).commit(text, user, date, match, force,
2713 editor, extra)
2713 editor, extra)
2714
2714
2715 def push(self, remote, force=False, revs=None, newbranch=False):
2715 def push(self, remote, force=False, revs=None, newbranch=False):
2716 if self.mq.applied and not force and not revs:
2716 if self.mq.applied and not force and not revs:
2717 raise util.Abort(_('source has mq patches applied'))
2717 raise util.Abort(_('source has mq patches applied'))
2718 return super(mqrepo, self).push(remote, force, revs, newbranch)
2718 return super(mqrepo, self).push(remote, force, revs, newbranch)
2719
2719
2720 def _findtags(self):
2720 def _findtags(self):
2721 '''augment tags from base class with patch tags'''
2721 '''augment tags from base class with patch tags'''
2722 result = super(mqrepo, self)._findtags()
2722 result = super(mqrepo, self)._findtags()
2723
2723
2724 q = self.mq
2724 q = self.mq
2725 if not q.applied:
2725 if not q.applied:
2726 return result
2726 return result
2727
2727
2728 mqtags = [(patch.node, patch.name) for patch in q.applied]
2728 mqtags = [(patch.node, patch.name) for patch in q.applied]
2729
2729
2730 if mqtags[-1][0] not in self.changelog.nodemap:
2730 if mqtags[-1][0] not in self.changelog.nodemap:
2731 self.ui.warn(_('mq status file refers to unknown node %s\n')
2731 self.ui.warn(_('mq status file refers to unknown node %s\n')
2732 % short(mqtags[-1][0]))
2732 % short(mqtags[-1][0]))
2733 return result
2733 return result
2734
2734
2735 mqtags.append((mqtags[-1][0], 'qtip'))
2735 mqtags.append((mqtags[-1][0], 'qtip'))
2736 mqtags.append((mqtags[0][0], 'qbase'))
2736 mqtags.append((mqtags[0][0], 'qbase'))
2737 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2737 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2738 tags = result[0]
2738 tags = result[0]
2739 for patch in mqtags:
2739 for patch in mqtags:
2740 if patch[1] in tags:
2740 if patch[1] in tags:
2741 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2741 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2742 % patch[1])
2742 % patch[1])
2743 else:
2743 else:
2744 tags[patch[1]] = patch[0]
2744 tags[patch[1]] = patch[0]
2745
2745
2746 return result
2746 return result
2747
2747
2748 def _branchtags(self, partial, lrev):
2748 def _branchtags(self, partial, lrev):
2749 q = self.mq
2749 q = self.mq
2750 if not q.applied:
2750 if not q.applied:
2751 return super(mqrepo, self)._branchtags(partial, lrev)
2751 return super(mqrepo, self)._branchtags(partial, lrev)
2752
2752
2753 cl = self.changelog
2753 cl = self.changelog
2754 qbasenode = q.applied[0].node
2754 qbasenode = q.applied[0].node
2755 if qbasenode not in cl.nodemap:
2755 if qbasenode not in cl.nodemap:
2756 self.ui.warn(_('mq status file refers to unknown node %s\n')
2756 self.ui.warn(_('mq status file refers to unknown node %s\n')
2757 % short(qbasenode))
2757 % short(qbasenode))
2758 return super(mqrepo, self)._branchtags(partial, lrev)
2758 return super(mqrepo, self)._branchtags(partial, lrev)
2759
2759
2760 qbase = cl.rev(qbasenode)
2760 qbase = cl.rev(qbasenode)
2761 start = lrev + 1
2761 start = lrev + 1
2762 if start < qbase:
2762 if start < qbase:
2763 # update the cache (excluding the patches) and save it
2763 # update the cache (excluding the patches) and save it
2764 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2764 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2765 self._updatebranchcache(partial, ctxgen)
2765 self._updatebranchcache(partial, ctxgen)
2766 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2766 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2767 start = qbase
2767 start = qbase
2768 # if start = qbase, the cache is as updated as it should be.
2768 # if start = qbase, the cache is as updated as it should be.
2769 # if start > qbase, the cache includes (part of) the patches.
2769 # if start > qbase, the cache includes (part of) the patches.
2770 # we might as well use it, but we won't save it.
2770 # we might as well use it, but we won't save it.
2771
2771
2772 # update the cache up to the tip
2772 # update the cache up to the tip
2773 ctxgen = (self[r] for r in xrange(start, len(cl)))
2773 ctxgen = (self[r] for r in xrange(start, len(cl)))
2774 self._updatebranchcache(partial, ctxgen)
2774 self._updatebranchcache(partial, ctxgen)
2775
2775
2776 return partial
2776 return partial
2777
2777
2778 if repo.local():
2778 if repo.local():
2779 repo.__class__ = mqrepo
2779 repo.__class__ = mqrepo
2780
2780
2781 def mqimport(orig, ui, repo, *args, **kwargs):
2781 def mqimport(orig, ui, repo, *args, **kwargs):
2782 if (hasattr(repo, 'abort_if_wdir_patched')
2782 if (hasattr(repo, 'abort_if_wdir_patched')
2783 and not kwargs.get('no_commit', False)):
2783 and not kwargs.get('no_commit', False)):
2784 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2784 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2785 kwargs.get('force'))
2785 kwargs.get('force'))
2786 return orig(ui, repo, *args, **kwargs)
2786 return orig(ui, repo, *args, **kwargs)
2787
2787
2788 def mqinit(orig, ui, *args, **kwargs):
2788 def mqinit(orig, ui, *args, **kwargs):
2789 mq = kwargs.pop('mq', None)
2789 mq = kwargs.pop('mq', None)
2790
2790
2791 if not mq:
2791 if not mq:
2792 return orig(ui, *args, **kwargs)
2792 return orig(ui, *args, **kwargs)
2793
2793
2794 if args:
2794 if args:
2795 repopath = args[0]
2795 repopath = args[0]
2796 if not hg.islocal(repopath):
2796 if not hg.islocal(repopath):
2797 raise util.Abort(_('only a local queue repository '
2797 raise util.Abort(_('only a local queue repository '
2798 'may be initialized'))
2798 'may be initialized'))
2799 else:
2799 else:
2800 repopath = cmdutil.findrepo(os.getcwd())
2800 repopath = cmdutil.findrepo(os.getcwd())
2801 if not repopath:
2801 if not repopath:
2802 raise util.Abort(_('There is no Mercurial repository here '
2802 raise util.Abort(_('there is no Mercurial repository here '
2803 '(.hg not found)'))
2803 '(.hg not found)'))
2804 repo = hg.repository(ui, repopath)
2804 repo = hg.repository(ui, repopath)
2805 return qinit(ui, repo, True)
2805 return qinit(ui, repo, True)
2806
2806
2807 def mqcommand(orig, ui, repo, *args, **kwargs):
2807 def mqcommand(orig, ui, repo, *args, **kwargs):
2808 """Add --mq option to operate on patch repository instead of main"""
2808 """Add --mq option to operate on patch repository instead of main"""
2809
2809
2810 # some commands do not like getting unknown options
2810 # some commands do not like getting unknown options
2811 mq = kwargs.pop('mq', None)
2811 mq = kwargs.pop('mq', None)
2812
2812
2813 if not mq:
2813 if not mq:
2814 return orig(ui, repo, *args, **kwargs)
2814 return orig(ui, repo, *args, **kwargs)
2815
2815
2816 q = repo.mq
2816 q = repo.mq
2817 r = q.qrepo()
2817 r = q.qrepo()
2818 if not r:
2818 if not r:
2819 raise util.Abort(_('no queue repository'))
2819 raise util.Abort(_('no queue repository'))
2820 return orig(r.ui, r, *args, **kwargs)
2820 return orig(r.ui, r, *args, **kwargs)
2821
2821
2822 def summary(orig, ui, repo, *args, **kwargs):
2822 def summary(orig, ui, repo, *args, **kwargs):
2823 r = orig(ui, repo, *args, **kwargs)
2823 r = orig(ui, repo, *args, **kwargs)
2824 q = repo.mq
2824 q = repo.mq
2825 m = []
2825 m = []
2826 a, u = len(q.applied), len(q.unapplied(repo))
2826 a, u = len(q.applied), len(q.unapplied(repo))
2827 if a:
2827 if a:
2828 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
2828 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
2829 if u:
2829 if u:
2830 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
2830 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
2831 if m:
2831 if m:
2832 ui.write("mq: %s\n" % ', '.join(m))
2832 ui.write("mq: %s\n" % ', '.join(m))
2833 else:
2833 else:
2834 ui.note(_("mq: (empty queue)\n"))
2834 ui.note(_("mq: (empty queue)\n"))
2835 return r
2835 return r
2836
2836
2837 def uisetup(ui):
2837 def uisetup(ui):
2838 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2838 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2839
2839
2840 extensions.wrapcommand(commands.table, 'import', mqimport)
2840 extensions.wrapcommand(commands.table, 'import', mqimport)
2841 extensions.wrapcommand(commands.table, 'summary', summary)
2841 extensions.wrapcommand(commands.table, 'summary', summary)
2842
2842
2843 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2843 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2844 entry[1].extend(mqopt)
2844 entry[1].extend(mqopt)
2845
2845
2846 norepo = commands.norepo.split(" ")
2846 norepo = commands.norepo.split(" ")
2847 for cmd in commands.table.keys():
2847 for cmd in commands.table.keys():
2848 cmd = cmdutil.parsealiases(cmd)[0]
2848 cmd = cmdutil.parsealiases(cmd)[0]
2849 if cmd in norepo:
2849 if cmd in norepo:
2850 continue
2850 continue
2851 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2851 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2852 entry[1].extend(mqopt)
2852 entry[1].extend(mqopt)
2853
2853
2854 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2854 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2855
2855
2856 cmdtable = {
2856 cmdtable = {
2857 "qapplied":
2857 "qapplied":
2858 (applied,
2858 (applied,
2859 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2859 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2860 _('hg qapplied [-1] [-s] [PATCH]')),
2860 _('hg qapplied [-1] [-s] [PATCH]')),
2861 "qclone":
2861 "qclone":
2862 (clone,
2862 (clone,
2863 [('', 'pull', None, _('use pull protocol to copy metadata')),
2863 [('', 'pull', None, _('use pull protocol to copy metadata')),
2864 ('U', 'noupdate', None, _('do not update the new working directories')),
2864 ('U', 'noupdate', None, _('do not update the new working directories')),
2865 ('', 'uncompressed', None,
2865 ('', 'uncompressed', None,
2866 _('use uncompressed transfer (fast over LAN)')),
2866 _('use uncompressed transfer (fast over LAN)')),
2867 ('p', 'patches', '',
2867 ('p', 'patches', '',
2868 _('location of source patch repository'), _('REPO')),
2868 _('location of source patch repository'), _('REPO')),
2869 ] + commands.remoteopts,
2869 ] + commands.remoteopts,
2870 _('hg qclone [OPTION]... SOURCE [DEST]')),
2870 _('hg qclone [OPTION]... SOURCE [DEST]')),
2871 "qcommit|qci":
2871 "qcommit|qci":
2872 (commit,
2872 (commit,
2873 commands.table["^commit|ci"][1],
2873 commands.table["^commit|ci"][1],
2874 _('hg qcommit [OPTION]... [FILE]...')),
2874 _('hg qcommit [OPTION]... [FILE]...')),
2875 "^qdiff":
2875 "^qdiff":
2876 (diff,
2876 (diff,
2877 commands.diffopts + commands.diffopts2 + commands.walkopts,
2877 commands.diffopts + commands.diffopts2 + commands.walkopts,
2878 _('hg qdiff [OPTION]... [FILE]...')),
2878 _('hg qdiff [OPTION]... [FILE]...')),
2879 "qdelete|qremove|qrm":
2879 "qdelete|qremove|qrm":
2880 (delete,
2880 (delete,
2881 [('k', 'keep', None, _('keep patch file')),
2881 [('k', 'keep', None, _('keep patch file')),
2882 ('r', 'rev', [],
2882 ('r', 'rev', [],
2883 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2883 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2884 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2884 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2885 'qfold':
2885 'qfold':
2886 (fold,
2886 (fold,
2887 [('e', 'edit', None, _('edit patch header')),
2887 [('e', 'edit', None, _('edit patch header')),
2888 ('k', 'keep', None, _('keep folded patch files')),
2888 ('k', 'keep', None, _('keep folded patch files')),
2889 ] + commands.commitopts,
2889 ] + commands.commitopts,
2890 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2890 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2891 'qgoto':
2891 'qgoto':
2892 (goto,
2892 (goto,
2893 [('f', 'force', None, _('overwrite any local changes'))],
2893 [('f', 'force', None, _('overwrite any local changes'))],
2894 _('hg qgoto [OPTION]... PATCH')),
2894 _('hg qgoto [OPTION]... PATCH')),
2895 'qguard':
2895 'qguard':
2896 (guard,
2896 (guard,
2897 [('l', 'list', None, _('list all patches and guards')),
2897 [('l', 'list', None, _('list all patches and guards')),
2898 ('n', 'none', None, _('drop all guards'))],
2898 ('n', 'none', None, _('drop all guards'))],
2899 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2899 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2900 'qheader': (header, [], _('hg qheader [PATCH]')),
2900 'qheader': (header, [], _('hg qheader [PATCH]')),
2901 "qimport":
2901 "qimport":
2902 (qimport,
2902 (qimport,
2903 [('e', 'existing', None, _('import file in patch directory')),
2903 [('e', 'existing', None, _('import file in patch directory')),
2904 ('n', 'name', '',
2904 ('n', 'name', '',
2905 _('name of patch file'), _('NAME')),
2905 _('name of patch file'), _('NAME')),
2906 ('f', 'force', None, _('overwrite existing files')),
2906 ('f', 'force', None, _('overwrite existing files')),
2907 ('r', 'rev', [],
2907 ('r', 'rev', [],
2908 _('place existing revisions under mq control'), _('REV')),
2908 _('place existing revisions under mq control'), _('REV')),
2909 ('g', 'git', None, _('use git extended diff format')),
2909 ('g', 'git', None, _('use git extended diff format')),
2910 ('P', 'push', None, _('qpush after importing'))],
2910 ('P', 'push', None, _('qpush after importing'))],
2911 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2911 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2912 "^qinit":
2912 "^qinit":
2913 (init,
2913 (init,
2914 [('c', 'create-repo', None, _('create queue repository'))],
2914 [('c', 'create-repo', None, _('create queue repository'))],
2915 _('hg qinit [-c]')),
2915 _('hg qinit [-c]')),
2916 "^qnew":
2916 "^qnew":
2917 (new,
2917 (new,
2918 [('e', 'edit', None, _('edit commit message')),
2918 [('e', 'edit', None, _('edit commit message')),
2919 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2919 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2920 ('g', 'git', None, _('use git extended diff format')),
2920 ('g', 'git', None, _('use git extended diff format')),
2921 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2921 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2922 ('u', 'user', '',
2922 ('u', 'user', '',
2923 _('add "From: <USER>" to patch'), _('USER')),
2923 _('add "From: <USER>" to patch'), _('USER')),
2924 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2924 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2925 ('d', 'date', '',
2925 ('d', 'date', '',
2926 _('add "Date: <DATE>" to patch'), _('DATE'))
2926 _('add "Date: <DATE>" to patch'), _('DATE'))
2927 ] + commands.walkopts + commands.commitopts,
2927 ] + commands.walkopts + commands.commitopts,
2928 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2928 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2929 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2929 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2930 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2930 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2931 "^qpop":
2931 "^qpop":
2932 (pop,
2932 (pop,
2933 [('a', 'all', None, _('pop all patches')),
2933 [('a', 'all', None, _('pop all patches')),
2934 ('n', 'name', '',
2934 ('n', 'name', '',
2935 _('queue name to pop (DEPRECATED)'), _('NAME')),
2935 _('queue name to pop (DEPRECATED)'), _('NAME')),
2936 ('f', 'force', None, _('forget any local changes to patched files'))],
2936 ('f', 'force', None, _('forget any local changes to patched files'))],
2937 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2937 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2938 "^qpush":
2938 "^qpush":
2939 (push,
2939 (push,
2940 [('f', 'force', None, _('apply if the patch has rejects')),
2940 [('f', 'force', None, _('apply if the patch has rejects')),
2941 ('l', 'list', None, _('list patch name in commit text')),
2941 ('l', 'list', None, _('list patch name in commit text')),
2942 ('a', 'all', None, _('apply all patches')),
2942 ('a', 'all', None, _('apply all patches')),
2943 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2943 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2944 ('n', 'name', '',
2944 ('n', 'name', '',
2945 _('merge queue name (DEPRECATED)'), _('NAME')),
2945 _('merge queue name (DEPRECATED)'), _('NAME')),
2946 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2946 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2947 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2947 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2948 "^qrefresh":
2948 "^qrefresh":
2949 (refresh,
2949 (refresh,
2950 [('e', 'edit', None, _('edit commit message')),
2950 [('e', 'edit', None, _('edit commit message')),
2951 ('g', 'git', None, _('use git extended diff format')),
2951 ('g', 'git', None, _('use git extended diff format')),
2952 ('s', 'short', None,
2952 ('s', 'short', None,
2953 _('refresh only files already in the patch and specified files')),
2953 _('refresh only files already in the patch and specified files')),
2954 ('U', 'currentuser', None,
2954 ('U', 'currentuser', None,
2955 _('add/update author field in patch with current user')),
2955 _('add/update author field in patch with current user')),
2956 ('u', 'user', '',
2956 ('u', 'user', '',
2957 _('add/update author field in patch with given user'), _('USER')),
2957 _('add/update author field in patch with given user'), _('USER')),
2958 ('D', 'currentdate', None,
2958 ('D', 'currentdate', None,
2959 _('add/update date field in patch with current date')),
2959 _('add/update date field in patch with current date')),
2960 ('d', 'date', '',
2960 ('d', 'date', '',
2961 _('add/update date field in patch with given date'), _('DATE'))
2961 _('add/update date field in patch with given date'), _('DATE'))
2962 ] + commands.walkopts + commands.commitopts,
2962 ] + commands.walkopts + commands.commitopts,
2963 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2963 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2964 'qrename|qmv':
2964 'qrename|qmv':
2965 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2965 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2966 "qrestore":
2966 "qrestore":
2967 (restore,
2967 (restore,
2968 [('d', 'delete', None, _('delete save entry')),
2968 [('d', 'delete', None, _('delete save entry')),
2969 ('u', 'update', None, _('update queue working directory'))],
2969 ('u', 'update', None, _('update queue working directory'))],
2970 _('hg qrestore [-d] [-u] REV')),
2970 _('hg qrestore [-d] [-u] REV')),
2971 "qsave":
2971 "qsave":
2972 (save,
2972 (save,
2973 [('c', 'copy', None, _('copy patch directory')),
2973 [('c', 'copy', None, _('copy patch directory')),
2974 ('n', 'name', '',
2974 ('n', 'name', '',
2975 _('copy directory name'), _('NAME')),
2975 _('copy directory name'), _('NAME')),
2976 ('e', 'empty', None, _('clear queue status file')),
2976 ('e', 'empty', None, _('clear queue status file')),
2977 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2977 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2978 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2978 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2979 "qselect":
2979 "qselect":
2980 (select,
2980 (select,
2981 [('n', 'none', None, _('disable all guards')),
2981 [('n', 'none', None, _('disable all guards')),
2982 ('s', 'series', None, _('list all guards in series file')),
2982 ('s', 'series', None, _('list all guards in series file')),
2983 ('', 'pop', None, _('pop to before first guarded applied patch')),
2983 ('', 'pop', None, _('pop to before first guarded applied patch')),
2984 ('', 'reapply', None, _('pop, then reapply patches'))],
2984 ('', 'reapply', None, _('pop, then reapply patches'))],
2985 _('hg qselect [OPTION]... [GUARD]...')),
2985 _('hg qselect [OPTION]... [GUARD]...')),
2986 "qseries":
2986 "qseries":
2987 (series,
2987 (series,
2988 [('m', 'missing', None, _('print patches not in series')),
2988 [('m', 'missing', None, _('print patches not in series')),
2989 ] + seriesopts,
2989 ] + seriesopts,
2990 _('hg qseries [-ms]')),
2990 _('hg qseries [-ms]')),
2991 "strip":
2991 "strip":
2992 (strip,
2992 (strip,
2993 [('f', 'force', None, _('force removal of changesets even if the '
2993 [('f', 'force', None, _('force removal of changesets even if the '
2994 'working directory has uncommitted changes')),
2994 'working directory has uncommitted changes')),
2995 ('b', 'backup', None, _('bundle only changesets with local revision'
2995 ('b', 'backup', None, _('bundle only changesets with local revision'
2996 ' number greater than REV which are not'
2996 ' number greater than REV which are not'
2997 ' descendants of REV (DEPRECATED)')),
2997 ' descendants of REV (DEPRECATED)')),
2998 ('n', 'nobackup', None, _('no backups'))],
2998 ('n', 'nobackup', None, _('no backups'))],
2999 _('hg strip [-f] [-n] REV')),
2999 _('hg strip [-f] [-n] REV')),
3000 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3000 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3001 "qunapplied":
3001 "qunapplied":
3002 (unapplied,
3002 (unapplied,
3003 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3003 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3004 _('hg qunapplied [-1] [-s] [PATCH]')),
3004 _('hg qunapplied [-1] [-s] [PATCH]')),
3005 "qfinish":
3005 "qfinish":
3006 (finish,
3006 (finish,
3007 [('a', 'applied', None, _('finish all applied changesets'))],
3007 [('a', 'applied', None, _('finish all applied changesets'))],
3008 _('hg qfinish [-a] [REV]...')),
3008 _('hg qfinish [-a] [REV]...')),
3009 'qqueue':
3009 'qqueue':
3010 (qqueue,
3010 (qqueue,
3011 [
3011 [
3012 ('l', 'list', False, _('list all available queues')),
3012 ('l', 'list', False, _('list all available queues')),
3013 ('c', 'create', False, _('create new queue')),
3013 ('c', 'create', False, _('create new queue')),
3014 ('', 'delete', False, _('delete reference to queue')),
3014 ('', 'delete', False, _('delete reference to queue')),
3015 ],
3015 ],
3016 _('[OPTION] [QUEUE]')),
3016 _('[OPTION] [QUEUE]')),
3017 }
3017 }
3018
3018
3019 colortable = {'qguard.negative': 'red',
3019 colortable = {'qguard.negative': 'red',
3020 'qguard.positive': 'yellow',
3020 'qguard.positive': 'yellow',
3021 'qguard.unguarded': 'green',
3021 'qguard.unguarded': 'green',
3022 'qseries.applied': 'blue bold underline',
3022 'qseries.applied': 'blue bold underline',
3023 'qseries.guarded': 'black bold',
3023 'qseries.guarded': 'black bold',
3024 'qseries.missing': 'red bold',
3024 'qseries.missing': 'red bold',
3025 'qseries.unapplied': 'black bold'}
3025 'qseries.unapplied': 'black bold'}
@@ -1,620 +1,620 b''
1 # Patch transplanting extension for Mercurial
1 # Patch transplanting extension for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.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 '''command to transplant changesets from another branch
8 '''command to transplant changesets from another branch
9
9
10 This extension allows you to transplant patches from another branch.
10 This extension allows you to transplant patches from another branch.
11
11
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 map from a changeset hash to its hash in the source repository.
13 map from a changeset hash to its hash in the source repository.
14 '''
14 '''
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 import os, tempfile
17 import os, tempfile
18 from mercurial import bundlerepo, changegroup, cmdutil, hg, merge, match
18 from mercurial import bundlerepo, changegroup, cmdutil, hg, merge, match
19 from mercurial import patch, revlog, util, error, discovery
19 from mercurial import patch, revlog, util, error, discovery
20
20
21 class transplantentry(object):
21 class transplantentry(object):
22 def __init__(self, lnode, rnode):
22 def __init__(self, lnode, rnode):
23 self.lnode = lnode
23 self.lnode = lnode
24 self.rnode = rnode
24 self.rnode = rnode
25
25
26 class transplants(object):
26 class transplants(object):
27 def __init__(self, path=None, transplantfile=None, opener=None):
27 def __init__(self, path=None, transplantfile=None, opener=None):
28 self.path = path
28 self.path = path
29 self.transplantfile = transplantfile
29 self.transplantfile = transplantfile
30 self.opener = opener
30 self.opener = opener
31
31
32 if not opener:
32 if not opener:
33 self.opener = util.opener(self.path)
33 self.opener = util.opener(self.path)
34 self.transplants = []
34 self.transplants = []
35 self.dirty = False
35 self.dirty = False
36 self.read()
36 self.read()
37
37
38 def read(self):
38 def read(self):
39 abspath = os.path.join(self.path, self.transplantfile)
39 abspath = os.path.join(self.path, self.transplantfile)
40 if self.transplantfile and os.path.exists(abspath):
40 if self.transplantfile and os.path.exists(abspath):
41 for line in self.opener(self.transplantfile).read().splitlines():
41 for line in self.opener(self.transplantfile).read().splitlines():
42 lnode, rnode = map(revlog.bin, line.split(':'))
42 lnode, rnode = map(revlog.bin, line.split(':'))
43 self.transplants.append(transplantentry(lnode, rnode))
43 self.transplants.append(transplantentry(lnode, rnode))
44
44
45 def write(self):
45 def write(self):
46 if self.dirty and self.transplantfile:
46 if self.dirty and self.transplantfile:
47 if not os.path.isdir(self.path):
47 if not os.path.isdir(self.path):
48 os.mkdir(self.path)
48 os.mkdir(self.path)
49 fp = self.opener(self.transplantfile, 'w')
49 fp = self.opener(self.transplantfile, 'w')
50 for c in self.transplants:
50 for c in self.transplants:
51 l, r = map(revlog.hex, (c.lnode, c.rnode))
51 l, r = map(revlog.hex, (c.lnode, c.rnode))
52 fp.write(l + ':' + r + '\n')
52 fp.write(l + ':' + r + '\n')
53 fp.close()
53 fp.close()
54 self.dirty = False
54 self.dirty = False
55
55
56 def get(self, rnode):
56 def get(self, rnode):
57 return [t for t in self.transplants if t.rnode == rnode]
57 return [t for t in self.transplants if t.rnode == rnode]
58
58
59 def set(self, lnode, rnode):
59 def set(self, lnode, rnode):
60 self.transplants.append(transplantentry(lnode, rnode))
60 self.transplants.append(transplantentry(lnode, rnode))
61 self.dirty = True
61 self.dirty = True
62
62
63 def remove(self, transplant):
63 def remove(self, transplant):
64 del self.transplants[self.transplants.index(transplant)]
64 del self.transplants[self.transplants.index(transplant)]
65 self.dirty = True
65 self.dirty = True
66
66
67 class transplanter(object):
67 class transplanter(object):
68 def __init__(self, ui, repo):
68 def __init__(self, ui, repo):
69 self.ui = ui
69 self.ui = ui
70 self.path = repo.join('transplant')
70 self.path = repo.join('transplant')
71 self.opener = util.opener(self.path)
71 self.opener = util.opener(self.path)
72 self.transplants = transplants(self.path, 'transplants',
72 self.transplants = transplants(self.path, 'transplants',
73 opener=self.opener)
73 opener=self.opener)
74
74
75 def applied(self, repo, node, parent):
75 def applied(self, repo, node, parent):
76 '''returns True if a node is already an ancestor of parent
76 '''returns True if a node is already an ancestor of parent
77 or has already been transplanted'''
77 or has already been transplanted'''
78 if hasnode(repo, node):
78 if hasnode(repo, node):
79 if node in repo.changelog.reachable(parent, stop=node):
79 if node in repo.changelog.reachable(parent, stop=node):
80 return True
80 return True
81 for t in self.transplants.get(node):
81 for t in self.transplants.get(node):
82 # it might have been stripped
82 # it might have been stripped
83 if not hasnode(repo, t.lnode):
83 if not hasnode(repo, t.lnode):
84 self.transplants.remove(t)
84 self.transplants.remove(t)
85 return False
85 return False
86 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
86 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
87 return True
87 return True
88 return False
88 return False
89
89
90 def apply(self, repo, source, revmap, merges, opts={}):
90 def apply(self, repo, source, revmap, merges, opts={}):
91 '''apply the revisions in revmap one by one in revision order'''
91 '''apply the revisions in revmap one by one in revision order'''
92 revs = sorted(revmap)
92 revs = sorted(revmap)
93 p1, p2 = repo.dirstate.parents()
93 p1, p2 = repo.dirstate.parents()
94 pulls = []
94 pulls = []
95 diffopts = patch.diffopts(self.ui, opts)
95 diffopts = patch.diffopts(self.ui, opts)
96 diffopts.git = True
96 diffopts.git = True
97
97
98 lock = wlock = None
98 lock = wlock = None
99 try:
99 try:
100 wlock = repo.wlock()
100 wlock = repo.wlock()
101 lock = repo.lock()
101 lock = repo.lock()
102 for rev in revs:
102 for rev in revs:
103 node = revmap[rev]
103 node = revmap[rev]
104 revstr = '%s:%s' % (rev, revlog.short(node))
104 revstr = '%s:%s' % (rev, revlog.short(node))
105
105
106 if self.applied(repo, node, p1):
106 if self.applied(repo, node, p1):
107 self.ui.warn(_('skipping already applied revision %s\n') %
107 self.ui.warn(_('skipping already applied revision %s\n') %
108 revstr)
108 revstr)
109 continue
109 continue
110
110
111 parents = source.changelog.parents(node)
111 parents = source.changelog.parents(node)
112 if not opts.get('filter'):
112 if not opts.get('filter'):
113 # If the changeset parent is the same as the
113 # If the changeset parent is the same as the
114 # wdir's parent, just pull it.
114 # wdir's parent, just pull it.
115 if parents[0] == p1:
115 if parents[0] == p1:
116 pulls.append(node)
116 pulls.append(node)
117 p1 = node
117 p1 = node
118 continue
118 continue
119 if pulls:
119 if pulls:
120 if source != repo:
120 if source != repo:
121 repo.pull(source, heads=pulls)
121 repo.pull(source, heads=pulls)
122 merge.update(repo, pulls[-1], False, False, None)
122 merge.update(repo, pulls[-1], False, False, None)
123 p1, p2 = repo.dirstate.parents()
123 p1, p2 = repo.dirstate.parents()
124 pulls = []
124 pulls = []
125
125
126 domerge = False
126 domerge = False
127 if node in merges:
127 if node in merges:
128 # pulling all the merge revs at once would mean we
128 # pulling all the merge revs at once would mean we
129 # couldn't transplant after the latest even if
129 # couldn't transplant after the latest even if
130 # transplants before them fail.
130 # transplants before them fail.
131 domerge = True
131 domerge = True
132 if not hasnode(repo, node):
132 if not hasnode(repo, node):
133 repo.pull(source, heads=[node])
133 repo.pull(source, heads=[node])
134
134
135 if parents[1] != revlog.nullid:
135 if parents[1] != revlog.nullid:
136 self.ui.note(_('skipping merge changeset %s:%s\n')
136 self.ui.note(_('skipping merge changeset %s:%s\n')
137 % (rev, revlog.short(node)))
137 % (rev, revlog.short(node)))
138 patchfile = None
138 patchfile = None
139 else:
139 else:
140 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
140 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
141 fp = os.fdopen(fd, 'w')
141 fp = os.fdopen(fd, 'w')
142 gen = patch.diff(source, parents[0], node, opts=diffopts)
142 gen = patch.diff(source, parents[0], node, opts=diffopts)
143 for chunk in gen:
143 for chunk in gen:
144 fp.write(chunk)
144 fp.write(chunk)
145 fp.close()
145 fp.close()
146
146
147 del revmap[rev]
147 del revmap[rev]
148 if patchfile or domerge:
148 if patchfile or domerge:
149 try:
149 try:
150 n = self.applyone(repo, node,
150 n = self.applyone(repo, node,
151 source.changelog.read(node),
151 source.changelog.read(node),
152 patchfile, merge=domerge,
152 patchfile, merge=domerge,
153 log=opts.get('log'),
153 log=opts.get('log'),
154 filter=opts.get('filter'))
154 filter=opts.get('filter'))
155 if n and domerge:
155 if n and domerge:
156 self.ui.status(_('%s merged at %s\n') % (revstr,
156 self.ui.status(_('%s merged at %s\n') % (revstr,
157 revlog.short(n)))
157 revlog.short(n)))
158 elif n:
158 elif n:
159 self.ui.status(_('%s transplanted to %s\n')
159 self.ui.status(_('%s transplanted to %s\n')
160 % (revlog.short(node),
160 % (revlog.short(node),
161 revlog.short(n)))
161 revlog.short(n)))
162 finally:
162 finally:
163 if patchfile:
163 if patchfile:
164 os.unlink(patchfile)
164 os.unlink(patchfile)
165 if pulls:
165 if pulls:
166 repo.pull(source, heads=pulls)
166 repo.pull(source, heads=pulls)
167 merge.update(repo, pulls[-1], False, False, None)
167 merge.update(repo, pulls[-1], False, False, None)
168 finally:
168 finally:
169 self.saveseries(revmap, merges)
169 self.saveseries(revmap, merges)
170 self.transplants.write()
170 self.transplants.write()
171 lock.release()
171 lock.release()
172 wlock.release()
172 wlock.release()
173
173
174 def filter(self, filter, changelog, patchfile):
174 def filter(self, filter, changelog, patchfile):
175 '''arbitrarily rewrite changeset before applying it'''
175 '''arbitrarily rewrite changeset before applying it'''
176
176
177 self.ui.status(_('filtering %s\n') % patchfile)
177 self.ui.status(_('filtering %s\n') % patchfile)
178 user, date, msg = (changelog[1], changelog[2], changelog[4])
178 user, date, msg = (changelog[1], changelog[2], changelog[4])
179
179
180 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
180 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
181 fp = os.fdopen(fd, 'w')
181 fp = os.fdopen(fd, 'w')
182 fp.write("# HG changeset patch\n")
182 fp.write("# HG changeset patch\n")
183 fp.write("# User %s\n" % user)
183 fp.write("# User %s\n" % user)
184 fp.write("# Date %d %d\n" % date)
184 fp.write("# Date %d %d\n" % date)
185 fp.write(msg + '\n')
185 fp.write(msg + '\n')
186 fp.close()
186 fp.close()
187
187
188 try:
188 try:
189 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
189 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
190 util.shellquote(patchfile)),
190 util.shellquote(patchfile)),
191 environ={'HGUSER': changelog[1]},
191 environ={'HGUSER': changelog[1]},
192 onerr=util.Abort, errprefix=_('filter failed'))
192 onerr=util.Abort, errprefix=_('filter failed'))
193 user, date, msg = self.parselog(file(headerfile))[1:4]
193 user, date, msg = self.parselog(file(headerfile))[1:4]
194 finally:
194 finally:
195 os.unlink(headerfile)
195 os.unlink(headerfile)
196
196
197 return (user, date, msg)
197 return (user, date, msg)
198
198
199 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
199 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
200 filter=None):
200 filter=None):
201 '''apply the patch in patchfile to the repository as a transplant'''
201 '''apply the patch in patchfile to the repository as a transplant'''
202 (manifest, user, (time, timezone), files, message) = cl[:5]
202 (manifest, user, (time, timezone), files, message) = cl[:5]
203 date = "%d %d" % (time, timezone)
203 date = "%d %d" % (time, timezone)
204 extra = {'transplant_source': node}
204 extra = {'transplant_source': node}
205 if filter:
205 if filter:
206 (user, date, message) = self.filter(filter, cl, patchfile)
206 (user, date, message) = self.filter(filter, cl, patchfile)
207
207
208 if log:
208 if log:
209 # we don't translate messages inserted into commits
209 # we don't translate messages inserted into commits
210 message += '\n(transplanted from %s)' % revlog.hex(node)
210 message += '\n(transplanted from %s)' % revlog.hex(node)
211
211
212 self.ui.status(_('applying %s\n') % revlog.short(node))
212 self.ui.status(_('applying %s\n') % revlog.short(node))
213 self.ui.note('%s %s\n%s\n' % (user, date, message))
213 self.ui.note('%s %s\n%s\n' % (user, date, message))
214
214
215 if not patchfile and not merge:
215 if not patchfile and not merge:
216 raise util.Abort(_('can only omit patchfile if merging'))
216 raise util.Abort(_('can only omit patchfile if merging'))
217 if patchfile:
217 if patchfile:
218 try:
218 try:
219 files = {}
219 files = {}
220 try:
220 try:
221 patch.patch(patchfile, self.ui, cwd=repo.root,
221 patch.patch(patchfile, self.ui, cwd=repo.root,
222 files=files, eolmode=None)
222 files=files, eolmode=None)
223 if not files:
223 if not files:
224 self.ui.warn(_('%s: empty changeset')
224 self.ui.warn(_('%s: empty changeset')
225 % revlog.hex(node))
225 % revlog.hex(node))
226 return None
226 return None
227 finally:
227 finally:
228 files = patch.updatedir(self.ui, repo, files)
228 files = patch.updatedir(self.ui, repo, files)
229 except Exception, inst:
229 except Exception, inst:
230 seriespath = os.path.join(self.path, 'series')
230 seriespath = os.path.join(self.path, 'series')
231 if os.path.exists(seriespath):
231 if os.path.exists(seriespath):
232 os.unlink(seriespath)
232 os.unlink(seriespath)
233 p1 = repo.dirstate.parents()[0]
233 p1 = repo.dirstate.parents()[0]
234 p2 = node
234 p2 = node
235 self.log(user, date, message, p1, p2, merge=merge)
235 self.log(user, date, message, p1, p2, merge=merge)
236 self.ui.write(str(inst) + '\n')
236 self.ui.write(str(inst) + '\n')
237 raise util.Abort(_('Fix up the merge and run '
237 raise util.Abort(_('fix up the merge and run '
238 'hg transplant --continue'))
238 'hg transplant --continue'))
239 else:
239 else:
240 files = None
240 files = None
241 if merge:
241 if merge:
242 p1, p2 = repo.dirstate.parents()
242 p1, p2 = repo.dirstate.parents()
243 repo.dirstate.setparents(p1, node)
243 repo.dirstate.setparents(p1, node)
244 m = match.always(repo.root, '')
244 m = match.always(repo.root, '')
245 else:
245 else:
246 m = match.exact(repo.root, '', files)
246 m = match.exact(repo.root, '', files)
247
247
248 n = repo.commit(message, user, date, extra=extra, match=m)
248 n = repo.commit(message, user, date, extra=extra, match=m)
249 if not n:
249 if not n:
250 # Crash here to prevent an unclear crash later, in
250 # Crash here to prevent an unclear crash later, in
251 # transplants.write(). This can happen if patch.patch()
251 # transplants.write(). This can happen if patch.patch()
252 # does nothing but claims success or if repo.status() fails
252 # does nothing but claims success or if repo.status() fails
253 # to report changes done by patch.patch(). These both
253 # to report changes done by patch.patch(). These both
254 # appear to be bugs in other parts of Mercurial, but dying
254 # appear to be bugs in other parts of Mercurial, but dying
255 # here, as soon as we can detect the problem, is preferable
255 # here, as soon as we can detect the problem, is preferable
256 # to silently dropping changesets on the floor.
256 # to silently dropping changesets on the floor.
257 raise RuntimeError('nothing committed after transplant')
257 raise RuntimeError('nothing committed after transplant')
258 if not merge:
258 if not merge:
259 self.transplants.set(n, node)
259 self.transplants.set(n, node)
260
260
261 return n
261 return n
262
262
263 def resume(self, repo, source, opts=None):
263 def resume(self, repo, source, opts=None):
264 '''recover last transaction and apply remaining changesets'''
264 '''recover last transaction and apply remaining changesets'''
265 if os.path.exists(os.path.join(self.path, 'journal')):
265 if os.path.exists(os.path.join(self.path, 'journal')):
266 n, node = self.recover(repo)
266 n, node = self.recover(repo)
267 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
267 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
268 revlog.short(n)))
268 revlog.short(n)))
269 seriespath = os.path.join(self.path, 'series')
269 seriespath = os.path.join(self.path, 'series')
270 if not os.path.exists(seriespath):
270 if not os.path.exists(seriespath):
271 self.transplants.write()
271 self.transplants.write()
272 return
272 return
273 nodes, merges = self.readseries()
273 nodes, merges = self.readseries()
274 revmap = {}
274 revmap = {}
275 for n in nodes:
275 for n in nodes:
276 revmap[source.changelog.rev(n)] = n
276 revmap[source.changelog.rev(n)] = n
277 os.unlink(seriespath)
277 os.unlink(seriespath)
278
278
279 self.apply(repo, source, revmap, merges, opts)
279 self.apply(repo, source, revmap, merges, opts)
280
280
281 def recover(self, repo):
281 def recover(self, repo):
282 '''commit working directory using journal metadata'''
282 '''commit working directory using journal metadata'''
283 node, user, date, message, parents = self.readlog()
283 node, user, date, message, parents = self.readlog()
284 merge = len(parents) == 2
284 merge = len(parents) == 2
285
285
286 if not user or not date or not message or not parents[0]:
286 if not user or not date or not message or not parents[0]:
287 raise util.Abort(_('transplant log file is corrupt'))
287 raise util.Abort(_('transplant log file is corrupt'))
288
288
289 extra = {'transplant_source': node}
289 extra = {'transplant_source': node}
290 wlock = repo.wlock()
290 wlock = repo.wlock()
291 try:
291 try:
292 p1, p2 = repo.dirstate.parents()
292 p1, p2 = repo.dirstate.parents()
293 if p1 != parents[0]:
293 if p1 != parents[0]:
294 raise util.Abort(
294 raise util.Abort(
295 _('working dir not at transplant parent %s') %
295 _('working dir not at transplant parent %s') %
296 revlog.hex(parents[0]))
296 revlog.hex(parents[0]))
297 if merge:
297 if merge:
298 repo.dirstate.setparents(p1, parents[1])
298 repo.dirstate.setparents(p1, parents[1])
299 n = repo.commit(message, user, date, extra=extra)
299 n = repo.commit(message, user, date, extra=extra)
300 if not n:
300 if not n:
301 raise util.Abort(_('commit failed'))
301 raise util.Abort(_('commit failed'))
302 if not merge:
302 if not merge:
303 self.transplants.set(n, node)
303 self.transplants.set(n, node)
304 self.unlog()
304 self.unlog()
305
305
306 return n, node
306 return n, node
307 finally:
307 finally:
308 wlock.release()
308 wlock.release()
309
309
310 def readseries(self):
310 def readseries(self):
311 nodes = []
311 nodes = []
312 merges = []
312 merges = []
313 cur = nodes
313 cur = nodes
314 for line in self.opener('series').read().splitlines():
314 for line in self.opener('series').read().splitlines():
315 if line.startswith('# Merges'):
315 if line.startswith('# Merges'):
316 cur = merges
316 cur = merges
317 continue
317 continue
318 cur.append(revlog.bin(line))
318 cur.append(revlog.bin(line))
319
319
320 return (nodes, merges)
320 return (nodes, merges)
321
321
322 def saveseries(self, revmap, merges):
322 def saveseries(self, revmap, merges):
323 if not revmap:
323 if not revmap:
324 return
324 return
325
325
326 if not os.path.isdir(self.path):
326 if not os.path.isdir(self.path):
327 os.mkdir(self.path)
327 os.mkdir(self.path)
328 series = self.opener('series', 'w')
328 series = self.opener('series', 'w')
329 for rev in sorted(revmap):
329 for rev in sorted(revmap):
330 series.write(revlog.hex(revmap[rev]) + '\n')
330 series.write(revlog.hex(revmap[rev]) + '\n')
331 if merges:
331 if merges:
332 series.write('# Merges\n')
332 series.write('# Merges\n')
333 for m in merges:
333 for m in merges:
334 series.write(revlog.hex(m) + '\n')
334 series.write(revlog.hex(m) + '\n')
335 series.close()
335 series.close()
336
336
337 def parselog(self, fp):
337 def parselog(self, fp):
338 parents = []
338 parents = []
339 message = []
339 message = []
340 node = revlog.nullid
340 node = revlog.nullid
341 inmsg = False
341 inmsg = False
342 for line in fp.read().splitlines():
342 for line in fp.read().splitlines():
343 if inmsg:
343 if inmsg:
344 message.append(line)
344 message.append(line)
345 elif line.startswith('# User '):
345 elif line.startswith('# User '):
346 user = line[7:]
346 user = line[7:]
347 elif line.startswith('# Date '):
347 elif line.startswith('# Date '):
348 date = line[7:]
348 date = line[7:]
349 elif line.startswith('# Node ID '):
349 elif line.startswith('# Node ID '):
350 node = revlog.bin(line[10:])
350 node = revlog.bin(line[10:])
351 elif line.startswith('# Parent '):
351 elif line.startswith('# Parent '):
352 parents.append(revlog.bin(line[9:]))
352 parents.append(revlog.bin(line[9:]))
353 elif not line.startswith('# '):
353 elif not line.startswith('# '):
354 inmsg = True
354 inmsg = True
355 message.append(line)
355 message.append(line)
356 return (node, user, date, '\n'.join(message), parents)
356 return (node, user, date, '\n'.join(message), parents)
357
357
358 def log(self, user, date, message, p1, p2, merge=False):
358 def log(self, user, date, message, p1, p2, merge=False):
359 '''journal changelog metadata for later recover'''
359 '''journal changelog metadata for later recover'''
360
360
361 if not os.path.isdir(self.path):
361 if not os.path.isdir(self.path):
362 os.mkdir(self.path)
362 os.mkdir(self.path)
363 fp = self.opener('journal', 'w')
363 fp = self.opener('journal', 'w')
364 fp.write('# User %s\n' % user)
364 fp.write('# User %s\n' % user)
365 fp.write('# Date %s\n' % date)
365 fp.write('# Date %s\n' % date)
366 fp.write('# Node ID %s\n' % revlog.hex(p2))
366 fp.write('# Node ID %s\n' % revlog.hex(p2))
367 fp.write('# Parent ' + revlog.hex(p1) + '\n')
367 fp.write('# Parent ' + revlog.hex(p1) + '\n')
368 if merge:
368 if merge:
369 fp.write('# Parent ' + revlog.hex(p2) + '\n')
369 fp.write('# Parent ' + revlog.hex(p2) + '\n')
370 fp.write(message.rstrip() + '\n')
370 fp.write(message.rstrip() + '\n')
371 fp.close()
371 fp.close()
372
372
373 def readlog(self):
373 def readlog(self):
374 return self.parselog(self.opener('journal'))
374 return self.parselog(self.opener('journal'))
375
375
376 def unlog(self):
376 def unlog(self):
377 '''remove changelog journal'''
377 '''remove changelog journal'''
378 absdst = os.path.join(self.path, 'journal')
378 absdst = os.path.join(self.path, 'journal')
379 if os.path.exists(absdst):
379 if os.path.exists(absdst):
380 os.unlink(absdst)
380 os.unlink(absdst)
381
381
382 def transplantfilter(self, repo, source, root):
382 def transplantfilter(self, repo, source, root):
383 def matchfn(node):
383 def matchfn(node):
384 if self.applied(repo, node, root):
384 if self.applied(repo, node, root):
385 return False
385 return False
386 if source.changelog.parents(node)[1] != revlog.nullid:
386 if source.changelog.parents(node)[1] != revlog.nullid:
387 return False
387 return False
388 extra = source.changelog.read(node)[5]
388 extra = source.changelog.read(node)[5]
389 cnode = extra.get('transplant_source')
389 cnode = extra.get('transplant_source')
390 if cnode and self.applied(repo, cnode, root):
390 if cnode and self.applied(repo, cnode, root):
391 return False
391 return False
392 return True
392 return True
393
393
394 return matchfn
394 return matchfn
395
395
396 def hasnode(repo, node):
396 def hasnode(repo, node):
397 try:
397 try:
398 return repo.changelog.rev(node) != None
398 return repo.changelog.rev(node) != None
399 except error.RevlogError:
399 except error.RevlogError:
400 return False
400 return False
401
401
402 def browserevs(ui, repo, nodes, opts):
402 def browserevs(ui, repo, nodes, opts):
403 '''interactively transplant changesets'''
403 '''interactively transplant changesets'''
404 def browsehelp(ui):
404 def browsehelp(ui):
405 ui.write(_('y: transplant this changeset\n'
405 ui.write(_('y: transplant this changeset\n'
406 'n: skip this changeset\n'
406 'n: skip this changeset\n'
407 'm: merge at this changeset\n'
407 'm: merge at this changeset\n'
408 'p: show patch\n'
408 'p: show patch\n'
409 'c: commit selected changesets\n'
409 'c: commit selected changesets\n'
410 'q: cancel transplant\n'
410 'q: cancel transplant\n'
411 '?: show this help\n'))
411 '?: show this help\n'))
412
412
413 displayer = cmdutil.show_changeset(ui, repo, opts)
413 displayer = cmdutil.show_changeset(ui, repo, opts)
414 transplants = []
414 transplants = []
415 merges = []
415 merges = []
416 for node in nodes:
416 for node in nodes:
417 displayer.show(repo[node])
417 displayer.show(repo[node])
418 action = None
418 action = None
419 while not action:
419 while not action:
420 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
420 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
421 if action == '?':
421 if action == '?':
422 browsehelp(ui)
422 browsehelp(ui)
423 action = None
423 action = None
424 elif action == 'p':
424 elif action == 'p':
425 parent = repo.changelog.parents(node)[0]
425 parent = repo.changelog.parents(node)[0]
426 for chunk in patch.diff(repo, parent, node):
426 for chunk in patch.diff(repo, parent, node):
427 ui.write(chunk)
427 ui.write(chunk)
428 action = None
428 action = None
429 elif action not in ('y', 'n', 'm', 'c', 'q'):
429 elif action not in ('y', 'n', 'm', 'c', 'q'):
430 ui.write(_('no such option\n'))
430 ui.write(_('no such option\n'))
431 action = None
431 action = None
432 if action == 'y':
432 if action == 'y':
433 transplants.append(node)
433 transplants.append(node)
434 elif action == 'm':
434 elif action == 'm':
435 merges.append(node)
435 merges.append(node)
436 elif action == 'c':
436 elif action == 'c':
437 break
437 break
438 elif action == 'q':
438 elif action == 'q':
439 transplants = ()
439 transplants = ()
440 merges = ()
440 merges = ()
441 break
441 break
442 displayer.close()
442 displayer.close()
443 return (transplants, merges)
443 return (transplants, merges)
444
444
445 def transplant(ui, repo, *revs, **opts):
445 def transplant(ui, repo, *revs, **opts):
446 '''transplant changesets from another branch
446 '''transplant changesets from another branch
447
447
448 Selected changesets will be applied on top of the current working
448 Selected changesets will be applied on top of the current working
449 directory with the log of the original changeset. If --log is
449 directory with the log of the original changeset. If --log is
450 specified, log messages will have a comment appended of the form::
450 specified, log messages will have a comment appended of the form::
451
451
452 (transplanted from CHANGESETHASH)
452 (transplanted from CHANGESETHASH)
453
453
454 You can rewrite the changelog message with the --filter option.
454 You can rewrite the changelog message with the --filter option.
455 Its argument will be invoked with the current changelog message as
455 Its argument will be invoked with the current changelog message as
456 $1 and the patch as $2.
456 $1 and the patch as $2.
457
457
458 If --source/-s is specified, selects changesets from the named
458 If --source/-s is specified, selects changesets from the named
459 repository. If --branch/-b is specified, selects changesets from
459 repository. If --branch/-b is specified, selects changesets from
460 the branch holding the named revision, up to that revision. If
460 the branch holding the named revision, up to that revision. If
461 --all/-a is specified, all changesets on the branch will be
461 --all/-a is specified, all changesets on the branch will be
462 transplanted, otherwise you will be prompted to select the
462 transplanted, otherwise you will be prompted to select the
463 changesets you want.
463 changesets you want.
464
464
465 :hg:`transplant --branch REVISION --all` will rebase the selected
465 :hg:`transplant --branch REVISION --all` will rebase the selected
466 branch (up to the named revision) onto your current working
466 branch (up to the named revision) onto your current working
467 directory.
467 directory.
468
468
469 You can optionally mark selected transplanted changesets as merge
469 You can optionally mark selected transplanted changesets as merge
470 changesets. You will not be prompted to transplant any ancestors
470 changesets. You will not be prompted to transplant any ancestors
471 of a merged transplant, and you can merge descendants of them
471 of a merged transplant, and you can merge descendants of them
472 normally instead of transplanting them.
472 normally instead of transplanting them.
473
473
474 If no merges or revisions are provided, :hg:`transplant` will
474 If no merges or revisions are provided, :hg:`transplant` will
475 start an interactive changeset browser.
475 start an interactive changeset browser.
476
476
477 If a changeset application fails, you can fix the merge by hand
477 If a changeset application fails, you can fix the merge by hand
478 and then resume where you left off by calling :hg:`transplant
478 and then resume where you left off by calling :hg:`transplant
479 --continue/-c`.
479 --continue/-c`.
480 '''
480 '''
481 def getremotechanges(repo, url):
481 def getremotechanges(repo, url):
482 sourcerepo = ui.expandpath(url)
482 sourcerepo = ui.expandpath(url)
483 source = hg.repository(ui, sourcerepo)
483 source = hg.repository(ui, sourcerepo)
484 tmp = discovery.findcommonincoming(repo, source, force=True)
484 tmp = discovery.findcommonincoming(repo, source, force=True)
485 common, incoming, rheads = tmp
485 common, incoming, rheads = tmp
486 if not incoming:
486 if not incoming:
487 return (source, None, None)
487 return (source, None, None)
488
488
489 bundle = None
489 bundle = None
490 if not source.local():
490 if not source.local():
491 if source.capable('changegroupsubset'):
491 if source.capable('changegroupsubset'):
492 cg = source.changegroupsubset(incoming, rheads, 'incoming')
492 cg = source.changegroupsubset(incoming, rheads, 'incoming')
493 else:
493 else:
494 cg = source.changegroup(incoming, 'incoming')
494 cg = source.changegroup(incoming, 'incoming')
495 bundle = changegroup.writebundle(cg, None, 'HG10UN')
495 bundle = changegroup.writebundle(cg, None, 'HG10UN')
496 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
496 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
497
497
498 return (source, incoming, bundle)
498 return (source, incoming, bundle)
499
499
500 def incwalk(repo, incoming, branches, match=util.always):
500 def incwalk(repo, incoming, branches, match=util.always):
501 if not branches:
501 if not branches:
502 branches = None
502 branches = None
503 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
503 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
504 if match(node):
504 if match(node):
505 yield node
505 yield node
506
506
507 def transplantwalk(repo, root, branches, match=util.always):
507 def transplantwalk(repo, root, branches, match=util.always):
508 if not branches:
508 if not branches:
509 branches = repo.heads()
509 branches = repo.heads()
510 ancestors = []
510 ancestors = []
511 for branch in branches:
511 for branch in branches:
512 ancestors.append(repo.changelog.ancestor(root, branch))
512 ancestors.append(repo.changelog.ancestor(root, branch))
513 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
513 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
514 if match(node):
514 if match(node):
515 yield node
515 yield node
516
516
517 def checkopts(opts, revs):
517 def checkopts(opts, revs):
518 if opts.get('continue'):
518 if opts.get('continue'):
519 if opts.get('branch') or opts.get('all') or opts.get('merge'):
519 if opts.get('branch') or opts.get('all') or opts.get('merge'):
520 raise util.Abort(_('--continue is incompatible with '
520 raise util.Abort(_('--continue is incompatible with '
521 'branch, all or merge'))
521 'branch, all or merge'))
522 return
522 return
523 if not (opts.get('source') or revs or
523 if not (opts.get('source') or revs or
524 opts.get('merge') or opts.get('branch')):
524 opts.get('merge') or opts.get('branch')):
525 raise util.Abort(_('no source URL, branch tag or revision '
525 raise util.Abort(_('no source URL, branch tag or revision '
526 'list provided'))
526 'list provided'))
527 if opts.get('all'):
527 if opts.get('all'):
528 if not opts.get('branch'):
528 if not opts.get('branch'):
529 raise util.Abort(_('--all requires a branch revision'))
529 raise util.Abort(_('--all requires a branch revision'))
530 if revs:
530 if revs:
531 raise util.Abort(_('--all is incompatible with a '
531 raise util.Abort(_('--all is incompatible with a '
532 'revision list'))
532 'revision list'))
533
533
534 checkopts(opts, revs)
534 checkopts(opts, revs)
535
535
536 if not opts.get('log'):
536 if not opts.get('log'):
537 opts['log'] = ui.config('transplant', 'log')
537 opts['log'] = ui.config('transplant', 'log')
538 if not opts.get('filter'):
538 if not opts.get('filter'):
539 opts['filter'] = ui.config('transplant', 'filter')
539 opts['filter'] = ui.config('transplant', 'filter')
540
540
541 tp = transplanter(ui, repo)
541 tp = transplanter(ui, repo)
542
542
543 p1, p2 = repo.dirstate.parents()
543 p1, p2 = repo.dirstate.parents()
544 if len(repo) > 0 and p1 == revlog.nullid:
544 if len(repo) > 0 and p1 == revlog.nullid:
545 raise util.Abort(_('no revision checked out'))
545 raise util.Abort(_('no revision checked out'))
546 if not opts.get('continue'):
546 if not opts.get('continue'):
547 if p2 != revlog.nullid:
547 if p2 != revlog.nullid:
548 raise util.Abort(_('outstanding uncommitted merges'))
548 raise util.Abort(_('outstanding uncommitted merges'))
549 m, a, r, d = repo.status()[:4]
549 m, a, r, d = repo.status()[:4]
550 if m or a or r or d:
550 if m or a or r or d:
551 raise util.Abort(_('outstanding local changes'))
551 raise util.Abort(_('outstanding local changes'))
552
552
553 bundle = None
553 bundle = None
554 source = opts.get('source')
554 source = opts.get('source')
555 if source:
555 if source:
556 (source, incoming, bundle) = getremotechanges(repo, source)
556 (source, incoming, bundle) = getremotechanges(repo, source)
557 else:
557 else:
558 source = repo
558 source = repo
559
559
560 try:
560 try:
561 if opts.get('continue'):
561 if opts.get('continue'):
562 tp.resume(repo, source, opts)
562 tp.resume(repo, source, opts)
563 return
563 return
564
564
565 tf = tp.transplantfilter(repo, source, p1)
565 tf = tp.transplantfilter(repo, source, p1)
566 if opts.get('prune'):
566 if opts.get('prune'):
567 prune = [source.lookup(r)
567 prune = [source.lookup(r)
568 for r in cmdutil.revrange(source, opts.get('prune'))]
568 for r in cmdutil.revrange(source, opts.get('prune'))]
569 matchfn = lambda x: tf(x) and x not in prune
569 matchfn = lambda x: tf(x) and x not in prune
570 else:
570 else:
571 matchfn = tf
571 matchfn = tf
572 branches = map(source.lookup, opts.get('branch', ()))
572 branches = map(source.lookup, opts.get('branch', ()))
573 merges = map(source.lookup, opts.get('merge', ()))
573 merges = map(source.lookup, opts.get('merge', ()))
574 revmap = {}
574 revmap = {}
575 if revs:
575 if revs:
576 for r in cmdutil.revrange(source, revs):
576 for r in cmdutil.revrange(source, revs):
577 revmap[int(r)] = source.lookup(r)
577 revmap[int(r)] = source.lookup(r)
578 elif opts.get('all') or not merges:
578 elif opts.get('all') or not merges:
579 if source != repo:
579 if source != repo:
580 alltransplants = incwalk(source, incoming, branches,
580 alltransplants = incwalk(source, incoming, branches,
581 match=matchfn)
581 match=matchfn)
582 else:
582 else:
583 alltransplants = transplantwalk(source, p1, branches,
583 alltransplants = transplantwalk(source, p1, branches,
584 match=matchfn)
584 match=matchfn)
585 if opts.get('all'):
585 if opts.get('all'):
586 revs = alltransplants
586 revs = alltransplants
587 else:
587 else:
588 revs, newmerges = browserevs(ui, source, alltransplants, opts)
588 revs, newmerges = browserevs(ui, source, alltransplants, opts)
589 merges.extend(newmerges)
589 merges.extend(newmerges)
590 for r in revs:
590 for r in revs:
591 revmap[source.changelog.rev(r)] = r
591 revmap[source.changelog.rev(r)] = r
592 for r in merges:
592 for r in merges:
593 revmap[source.changelog.rev(r)] = r
593 revmap[source.changelog.rev(r)] = r
594
594
595 tp.apply(repo, source, revmap, merges, opts)
595 tp.apply(repo, source, revmap, merges, opts)
596 finally:
596 finally:
597 if bundle:
597 if bundle:
598 source.close()
598 source.close()
599 os.unlink(bundle)
599 os.unlink(bundle)
600
600
601 cmdtable = {
601 cmdtable = {
602 "transplant":
602 "transplant":
603 (transplant,
603 (transplant,
604 [('s', 'source', '',
604 [('s', 'source', '',
605 _('pull patches from REPO'), _('REPO')),
605 _('pull patches from REPO'), _('REPO')),
606 ('b', 'branch', [],
606 ('b', 'branch', [],
607 _('pull patches from branch BRANCH'), _('BRANCH')),
607 _('pull patches from branch BRANCH'), _('BRANCH')),
608 ('a', 'all', None, _('pull all changesets up to BRANCH')),
608 ('a', 'all', None, _('pull all changesets up to BRANCH')),
609 ('p', 'prune', [],
609 ('p', 'prune', [],
610 _('skip over REV'), _('REV')),
610 _('skip over REV'), _('REV')),
611 ('m', 'merge', [],
611 ('m', 'merge', [],
612 _('merge at REV'), _('REV')),
612 _('merge at REV'), _('REV')),
613 ('', 'log', None, _('append transplant info to log message')),
613 ('', 'log', None, _('append transplant info to log message')),
614 ('c', 'continue', None, _('continue last transplant session '
614 ('c', 'continue', None, _('continue last transplant session '
615 'after repair')),
615 'after repair')),
616 ('', 'filter', '',
616 ('', 'filter', '',
617 _('filter changesets through command'), _('CMD'))],
617 _('filter changesets through command'), _('CMD'))],
618 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
618 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
619 '[-m REV] [REV]...'))
619 '[-m REV] [REV]...'))
620 }
620 }
@@ -1,4478 +1,4478 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw, discovery
13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset
16 import minirst, revset
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 bad = []
49 bad = []
50 names = []
50 names = []
51 m = cmdutil.match(repo, pats, opts)
51 m = cmdutil.match(repo, pats, opts)
52 oldbad = m.bad
52 oldbad = m.bad
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
54
54
55 for f in repo.walk(m):
55 for f in repo.walk(m):
56 exact = m.exact(f)
56 exact = m.exact(f)
57 if exact or f not in repo.dirstate:
57 if exact or f not in repo.dirstate:
58 names.append(f)
58 names.append(f)
59 if ui.verbose or not exact:
59 if ui.verbose or not exact:
60 ui.status(_('adding %s\n') % m.rel(f))
60 ui.status(_('adding %s\n') % m.rel(f))
61 if not opts.get('dry_run'):
61 if not opts.get('dry_run'):
62 bad += [f for f in repo[None].add(names) if f in m.files()]
62 bad += [f for f in repo[None].add(names) if f in m.files()]
63 return bad and 1 or 0
63 return bad and 1 or 0
64
64
65 def addremove(ui, repo, *pats, **opts):
65 def addremove(ui, repo, *pats, **opts):
66 """add all new files, delete all missing files
66 """add all new files, delete all missing files
67
67
68 Add all new files and remove all missing files from the
68 Add all new files and remove all missing files from the
69 repository.
69 repository.
70
70
71 New files are ignored if they match any of the patterns in
71 New files are ignored if they match any of the patterns in
72 .hgignore. As with add, these changes take effect at the next
72 .hgignore. As with add, these changes take effect at the next
73 commit.
73 commit.
74
74
75 Use the -s/--similarity option to detect renamed files. With a
75 Use the -s/--similarity option to detect renamed files. With a
76 parameter greater than 0, this compares every removed file with
76 parameter greater than 0, this compares every removed file with
77 every added file and records those similar enough as renames. This
77 every added file and records those similar enough as renames. This
78 option takes a percentage between 0 (disabled) and 100 (files must
78 option takes a percentage between 0 (disabled) and 100 (files must
79 be identical) as its parameter. Detecting renamed files this way
79 be identical) as its parameter. Detecting renamed files this way
80 can be expensive. After using this option, :hg:`status -C` can be
80 can be expensive. After using this option, :hg:`status -C` can be
81 used to check which files were identified as moved or renamed.
81 used to check which files were identified as moved or renamed.
82
82
83 Returns 0 if all files are successfully added.
83 Returns 0 if all files are successfully added.
84 """
84 """
85 try:
85 try:
86 sim = float(opts.get('similarity') or 0)
86 sim = float(opts.get('similarity') or 0)
87 except ValueError:
87 except ValueError:
88 raise util.Abort(_('similarity must be a number'))
88 raise util.Abort(_('similarity must be a number'))
89 if sim < 0 or sim > 100:
89 if sim < 0 or sim > 100:
90 raise util.Abort(_('similarity must be between 0 and 100'))
90 raise util.Abort(_('similarity must be between 0 and 100'))
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
92
92
93 def annotate(ui, repo, *pats, **opts):
93 def annotate(ui, repo, *pats, **opts):
94 """show changeset information by line for each file
94 """show changeset information by line for each file
95
95
96 List changes in files, showing the revision id responsible for
96 List changes in files, showing the revision id responsible for
97 each line
97 each line
98
98
99 This command is useful for discovering when a change was made and
99 This command is useful for discovering when a change was made and
100 by whom.
100 by whom.
101
101
102 Without the -a/--text option, annotate will avoid processing files
102 Without the -a/--text option, annotate will avoid processing files
103 it detects as binary. With -a, annotate will annotate the file
103 it detects as binary. With -a, annotate will annotate the file
104 anyway, although the results will probably be neither useful
104 anyway, although the results will probably be neither useful
105 nor desirable.
105 nor desirable.
106
106
107 Returns 0 on success.
107 Returns 0 on success.
108 """
108 """
109 if opts.get('follow'):
109 if opts.get('follow'):
110 # --follow is deprecated and now just an alias for -f/--file
110 # --follow is deprecated and now just an alias for -f/--file
111 # to mimic the behavior of Mercurial before version 1.5
111 # to mimic the behavior of Mercurial before version 1.5
112 opts['file'] = 1
112 opts['file'] = 1
113
113
114 datefunc = ui.quiet and util.shortdate or util.datestr
114 datefunc = ui.quiet and util.shortdate or util.datestr
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
116
116
117 if not pats:
117 if not pats:
118 raise util.Abort(_('at least one filename or pattern is required'))
118 raise util.Abort(_('at least one filename or pattern is required'))
119
119
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
121 ('number', lambda x: str(x[0].rev())),
121 ('number', lambda x: str(x[0].rev())),
122 ('changeset', lambda x: short(x[0].node())),
122 ('changeset', lambda x: short(x[0].node())),
123 ('date', getdate),
123 ('date', getdate),
124 ('file', lambda x: x[0].path()),
124 ('file', lambda x: x[0].path()),
125 ]
125 ]
126
126
127 if (not opts.get('user') and not opts.get('changeset')
127 if (not opts.get('user') and not opts.get('changeset')
128 and not opts.get('date') and not opts.get('file')):
128 and not opts.get('date') and not opts.get('file')):
129 opts['number'] = 1
129 opts['number'] = 1
130
130
131 linenumber = opts.get('line_number') is not None
131 linenumber = opts.get('line_number') is not None
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
134
134
135 funcmap = [func for op, func in opmap if opts.get(op)]
135 funcmap = [func for op, func in opmap if opts.get(op)]
136 if linenumber:
136 if linenumber:
137 lastfunc = funcmap[-1]
137 lastfunc = funcmap[-1]
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
139
139
140 ctx = repo[opts.get('rev')]
140 ctx = repo[opts.get('rev')]
141 m = cmdutil.match(repo, pats, opts)
141 m = cmdutil.match(repo, pats, opts)
142 follow = not opts.get('no_follow')
142 follow = not opts.get('no_follow')
143 for abs in ctx.walk(m):
143 for abs in ctx.walk(m):
144 fctx = ctx[abs]
144 fctx = ctx[abs]
145 if not opts.get('text') and util.binary(fctx.data()):
145 if not opts.get('text') and util.binary(fctx.data()):
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
147 continue
147 continue
148
148
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
150 pieces = []
150 pieces = []
151
151
152 for f in funcmap:
152 for f in funcmap:
153 l = [f(n) for n, dummy in lines]
153 l = [f(n) for n, dummy in lines]
154 if l:
154 if l:
155 sized = [(x, encoding.colwidth(x)) for x in l]
155 sized = [(x, encoding.colwidth(x)) for x in l]
156 ml = max([w for x, w in sized])
156 ml = max([w for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
158
158
159 if pieces:
159 if pieces:
160 for p, l in zip(zip(*pieces), lines):
160 for p, l in zip(zip(*pieces), lines):
161 ui.write("%s: %s" % (" ".join(p), l[1]))
161 ui.write("%s: %s" % (" ".join(p), l[1]))
162
162
163 def archive(ui, repo, dest, **opts):
163 def archive(ui, repo, dest, **opts):
164 '''create an unversioned archive of a repository revision
164 '''create an unversioned archive of a repository revision
165
165
166 By default, the revision used is the parent of the working
166 By default, the revision used is the parent of the working
167 directory; use -r/--rev to specify a different revision.
167 directory; use -r/--rev to specify a different revision.
168
168
169 The archive type is automatically detected based on file
169 The archive type is automatically detected based on file
170 extension (or override using -t/--type).
170 extension (or override using -t/--type).
171
171
172 Valid types are:
172 Valid types are:
173
173
174 :``files``: a directory full of files (default)
174 :``files``: a directory full of files (default)
175 :``tar``: tar archive, uncompressed
175 :``tar``: tar archive, uncompressed
176 :``tbz2``: tar archive, compressed using bzip2
176 :``tbz2``: tar archive, compressed using bzip2
177 :``tgz``: tar archive, compressed using gzip
177 :``tgz``: tar archive, compressed using gzip
178 :``uzip``: zip archive, uncompressed
178 :``uzip``: zip archive, uncompressed
179 :``zip``: zip archive, compressed using deflate
179 :``zip``: zip archive, compressed using deflate
180
180
181 The exact name of the destination archive or directory is given
181 The exact name of the destination archive or directory is given
182 using a format string; see :hg:`help export` for details.
182 using a format string; see :hg:`help export` for details.
183
183
184 Each member added to an archive file has a directory prefix
184 Each member added to an archive file has a directory prefix
185 prepended. Use -p/--prefix to specify a format string for the
185 prepended. Use -p/--prefix to specify a format string for the
186 prefix. The default is the basename of the archive, with suffixes
186 prefix. The default is the basename of the archive, with suffixes
187 removed.
187 removed.
188
188
189 Returns 0 on success.
189 Returns 0 on success.
190 '''
190 '''
191
191
192 ctx = repo[opts.get('rev')]
192 ctx = repo[opts.get('rev')]
193 if not ctx:
193 if not ctx:
194 raise util.Abort(_('no working directory: please specify a revision'))
194 raise util.Abort(_('no working directory: please specify a revision'))
195 node = ctx.node()
195 node = ctx.node()
196 dest = cmdutil.make_filename(repo, dest, node)
196 dest = cmdutil.make_filename(repo, dest, node)
197 if os.path.realpath(dest) == repo.root:
197 if os.path.realpath(dest) == repo.root:
198 raise util.Abort(_('repository root cannot be destination'))
198 raise util.Abort(_('repository root cannot be destination'))
199
199
200 def guess_type():
200 def guess_type():
201 exttypes = {
201 exttypes = {
202 'tar': ['.tar'],
202 'tar': ['.tar'],
203 'tbz2': ['.tbz2', '.tar.bz2'],
203 'tbz2': ['.tbz2', '.tar.bz2'],
204 'tgz': ['.tgz', '.tar.gz'],
204 'tgz': ['.tgz', '.tar.gz'],
205 'zip': ['.zip'],
205 'zip': ['.zip'],
206 }
206 }
207
207
208 for type, extensions in exttypes.items():
208 for type, extensions in exttypes.items():
209 if util.any(dest.endswith(ext) for ext in extensions):
209 if util.any(dest.endswith(ext) for ext in extensions):
210 return type
210 return type
211 return None
211 return None
212
212
213 kind = opts.get('type') or guess_type() or 'files'
213 kind = opts.get('type') or guess_type() or 'files'
214 prefix = opts.get('prefix')
214 prefix = opts.get('prefix')
215
215
216 if dest == '-':
216 if dest == '-':
217 if kind == 'files':
217 if kind == 'files':
218 raise util.Abort(_('cannot archive plain files to stdout'))
218 raise util.Abort(_('cannot archive plain files to stdout'))
219 dest = sys.stdout
219 dest = sys.stdout
220 if not prefix:
220 if not prefix:
221 prefix = os.path.basename(repo.root) + '-%h'
221 prefix = os.path.basename(repo.root) + '-%h'
222
222
223 prefix = cmdutil.make_filename(repo, prefix, node)
223 prefix = cmdutil.make_filename(repo, prefix, node)
224 matchfn = cmdutil.match(repo, [], opts)
224 matchfn = cmdutil.match(repo, [], opts)
225 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
225 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
226 matchfn, prefix)
226 matchfn, prefix)
227
227
228 def backout(ui, repo, node=None, rev=None, **opts):
228 def backout(ui, repo, node=None, rev=None, **opts):
229 '''reverse effect of earlier changeset
229 '''reverse effect of earlier changeset
230
230
231 Commit the backed out changes as a new changeset. The new
231 Commit the backed out changes as a new changeset. The new
232 changeset is a child of the backed out changeset.
232 changeset is a child of the backed out changeset.
233
233
234 If you backout a changeset other than the tip, a new head is
234 If you backout a changeset other than the tip, a new head is
235 created. This head will be the new tip and you should merge this
235 created. This head will be the new tip and you should merge this
236 backout changeset with another head.
236 backout changeset with another head.
237
237
238 The --merge option remembers the parent of the working directory
238 The --merge option remembers the parent of the working directory
239 before starting the backout, then merges the new head with that
239 before starting the backout, then merges the new head with that
240 changeset afterwards. This saves you from doing the merge by hand.
240 changeset afterwards. This saves you from doing the merge by hand.
241 The result of this merge is not committed, as with a normal merge.
241 The result of this merge is not committed, as with a normal merge.
242
242
243 See :hg:`help dates` for a list of formats valid for -d/--date.
243 See :hg:`help dates` for a list of formats valid for -d/--date.
244
244
245 Returns 0 on success.
245 Returns 0 on success.
246 '''
246 '''
247 if rev and node:
247 if rev and node:
248 raise util.Abort(_("please specify just one revision"))
248 raise util.Abort(_("please specify just one revision"))
249
249
250 if not rev:
250 if not rev:
251 rev = node
251 rev = node
252
252
253 if not rev:
253 if not rev:
254 raise util.Abort(_("please specify a revision to backout"))
254 raise util.Abort(_("please specify a revision to backout"))
255
255
256 date = opts.get('date')
256 date = opts.get('date')
257 if date:
257 if date:
258 opts['date'] = util.parsedate(date)
258 opts['date'] = util.parsedate(date)
259
259
260 cmdutil.bail_if_changed(repo)
260 cmdutil.bail_if_changed(repo)
261 node = repo.lookup(rev)
261 node = repo.lookup(rev)
262
262
263 op1, op2 = repo.dirstate.parents()
263 op1, op2 = repo.dirstate.parents()
264 a = repo.changelog.ancestor(op1, node)
264 a = repo.changelog.ancestor(op1, node)
265 if a != node:
265 if a != node:
266 raise util.Abort(_('cannot backout change on a different branch'))
266 raise util.Abort(_('cannot backout change on a different branch'))
267
267
268 p1, p2 = repo.changelog.parents(node)
268 p1, p2 = repo.changelog.parents(node)
269 if p1 == nullid:
269 if p1 == nullid:
270 raise util.Abort(_('cannot backout a change with no parents'))
270 raise util.Abort(_('cannot backout a change with no parents'))
271 if p2 != nullid:
271 if p2 != nullid:
272 if not opts.get('parent'):
272 if not opts.get('parent'):
273 raise util.Abort(_('cannot backout a merge changeset without '
273 raise util.Abort(_('cannot backout a merge changeset without '
274 '--parent'))
274 '--parent'))
275 p = repo.lookup(opts['parent'])
275 p = repo.lookup(opts['parent'])
276 if p not in (p1, p2):
276 if p not in (p1, p2):
277 raise util.Abort(_('%s is not a parent of %s') %
277 raise util.Abort(_('%s is not a parent of %s') %
278 (short(p), short(node)))
278 (short(p), short(node)))
279 parent = p
279 parent = p
280 else:
280 else:
281 if opts.get('parent'):
281 if opts.get('parent'):
282 raise util.Abort(_('cannot use --parent on non-merge changeset'))
282 raise util.Abort(_('cannot use --parent on non-merge changeset'))
283 parent = p1
283 parent = p1
284
284
285 # the backout should appear on the same branch
285 # the backout should appear on the same branch
286 branch = repo.dirstate.branch()
286 branch = repo.dirstate.branch()
287 hg.clean(repo, node, show_stats=False)
287 hg.clean(repo, node, show_stats=False)
288 repo.dirstate.setbranch(branch)
288 repo.dirstate.setbranch(branch)
289 revert_opts = opts.copy()
289 revert_opts = opts.copy()
290 revert_opts['date'] = None
290 revert_opts['date'] = None
291 revert_opts['all'] = True
291 revert_opts['all'] = True
292 revert_opts['rev'] = hex(parent)
292 revert_opts['rev'] = hex(parent)
293 revert_opts['no_backup'] = None
293 revert_opts['no_backup'] = None
294 revert(ui, repo, **revert_opts)
294 revert(ui, repo, **revert_opts)
295 commit_opts = opts.copy()
295 commit_opts = opts.copy()
296 commit_opts['addremove'] = False
296 commit_opts['addremove'] = False
297 if not commit_opts['message'] and not commit_opts['logfile']:
297 if not commit_opts['message'] and not commit_opts['logfile']:
298 # we don't translate commit messages
298 # we don't translate commit messages
299 commit_opts['message'] = "Backed out changeset %s" % short(node)
299 commit_opts['message'] = "Backed out changeset %s" % short(node)
300 commit_opts['force_editor'] = True
300 commit_opts['force_editor'] = True
301 commit(ui, repo, **commit_opts)
301 commit(ui, repo, **commit_opts)
302 def nice(node):
302 def nice(node):
303 return '%d:%s' % (repo.changelog.rev(node), short(node))
303 return '%d:%s' % (repo.changelog.rev(node), short(node))
304 ui.status(_('changeset %s backs out changeset %s\n') %
304 ui.status(_('changeset %s backs out changeset %s\n') %
305 (nice(repo.changelog.tip()), nice(node)))
305 (nice(repo.changelog.tip()), nice(node)))
306 if op1 != node:
306 if op1 != node:
307 hg.clean(repo, op1, show_stats=False)
307 hg.clean(repo, op1, show_stats=False)
308 if opts.get('merge'):
308 if opts.get('merge'):
309 ui.status(_('merging with changeset %s\n')
309 ui.status(_('merging with changeset %s\n')
310 % nice(repo.changelog.tip()))
310 % nice(repo.changelog.tip()))
311 hg.merge(repo, hex(repo.changelog.tip()))
311 hg.merge(repo, hex(repo.changelog.tip()))
312 else:
312 else:
313 ui.status(_('the backout changeset is a new head - '
313 ui.status(_('the backout changeset is a new head - '
314 'do not forget to merge\n'))
314 'do not forget to merge\n'))
315 ui.status(_('(use "backout --merge" '
315 ui.status(_('(use "backout --merge" '
316 'if you want to auto-merge)\n'))
316 'if you want to auto-merge)\n'))
317
317
318 def bisect(ui, repo, rev=None, extra=None, command=None,
318 def bisect(ui, repo, rev=None, extra=None, command=None,
319 reset=None, good=None, bad=None, skip=None, noupdate=None):
319 reset=None, good=None, bad=None, skip=None, noupdate=None):
320 """subdivision search of changesets
320 """subdivision search of changesets
321
321
322 This command helps to find changesets which introduce problems. To
322 This command helps to find changesets which introduce problems. To
323 use, mark the earliest changeset you know exhibits the problem as
323 use, mark the earliest changeset you know exhibits the problem as
324 bad, then mark the latest changeset which is free from the problem
324 bad, then mark the latest changeset which is free from the problem
325 as good. Bisect will update your working directory to a revision
325 as good. Bisect will update your working directory to a revision
326 for testing (unless the -U/--noupdate option is specified). Once
326 for testing (unless the -U/--noupdate option is specified). Once
327 you have performed tests, mark the working directory as good or
327 you have performed tests, mark the working directory as good or
328 bad, and bisect will either update to another candidate changeset
328 bad, and bisect will either update to another candidate changeset
329 or announce that it has found the bad revision.
329 or announce that it has found the bad revision.
330
330
331 As a shortcut, you can also use the revision argument to mark a
331 As a shortcut, you can also use the revision argument to mark a
332 revision as good or bad without checking it out first.
332 revision as good or bad without checking it out first.
333
333
334 If you supply a command, it will be used for automatic bisection.
334 If you supply a command, it will be used for automatic bisection.
335 Its exit status will be used to mark revisions as good or bad:
335 Its exit status will be used to mark revisions as good or bad:
336 status 0 means good, 125 means to skip the revision, 127
336 status 0 means good, 125 means to skip the revision, 127
337 (command not found) will abort the bisection, and any other
337 (command not found) will abort the bisection, and any other
338 non-zero exit status means the revision is bad.
338 non-zero exit status means the revision is bad.
339
339
340 Returns 0 on success.
340 Returns 0 on success.
341 """
341 """
342 def print_result(nodes, good):
342 def print_result(nodes, good):
343 displayer = cmdutil.show_changeset(ui, repo, {})
343 displayer = cmdutil.show_changeset(ui, repo, {})
344 if len(nodes) == 1:
344 if len(nodes) == 1:
345 # narrowed it down to a single revision
345 # narrowed it down to a single revision
346 if good:
346 if good:
347 ui.write(_("The first good revision is:\n"))
347 ui.write(_("The first good revision is:\n"))
348 else:
348 else:
349 ui.write(_("The first bad revision is:\n"))
349 ui.write(_("The first bad revision is:\n"))
350 displayer.show(repo[nodes[0]])
350 displayer.show(repo[nodes[0]])
351 else:
351 else:
352 # multiple possible revisions
352 # multiple possible revisions
353 if good:
353 if good:
354 ui.write(_("Due to skipped revisions, the first "
354 ui.write(_("Due to skipped revisions, the first "
355 "good revision could be any of:\n"))
355 "good revision could be any of:\n"))
356 else:
356 else:
357 ui.write(_("Due to skipped revisions, the first "
357 ui.write(_("Due to skipped revisions, the first "
358 "bad revision could be any of:\n"))
358 "bad revision could be any of:\n"))
359 for n in nodes:
359 for n in nodes:
360 displayer.show(repo[n])
360 displayer.show(repo[n])
361 displayer.close()
361 displayer.close()
362
362
363 def check_state(state, interactive=True):
363 def check_state(state, interactive=True):
364 if not state['good'] or not state['bad']:
364 if not state['good'] or not state['bad']:
365 if (good or bad or skip or reset) and interactive:
365 if (good or bad or skip or reset) and interactive:
366 return
366 return
367 if not state['good']:
367 if not state['good']:
368 raise util.Abort(_('cannot bisect (no known good revisions)'))
368 raise util.Abort(_('cannot bisect (no known good revisions)'))
369 else:
369 else:
370 raise util.Abort(_('cannot bisect (no known bad revisions)'))
370 raise util.Abort(_('cannot bisect (no known bad revisions)'))
371 return True
371 return True
372
372
373 # backward compatibility
373 # backward compatibility
374 if rev in "good bad reset init".split():
374 if rev in "good bad reset init".split():
375 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
375 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
376 cmd, rev, extra = rev, extra, None
376 cmd, rev, extra = rev, extra, None
377 if cmd == "good":
377 if cmd == "good":
378 good = True
378 good = True
379 elif cmd == "bad":
379 elif cmd == "bad":
380 bad = True
380 bad = True
381 else:
381 else:
382 reset = True
382 reset = True
383 elif extra or good + bad + skip + reset + bool(command) > 1:
383 elif extra or good + bad + skip + reset + bool(command) > 1:
384 raise util.Abort(_('incompatible arguments'))
384 raise util.Abort(_('incompatible arguments'))
385
385
386 if reset:
386 if reset:
387 p = repo.join("bisect.state")
387 p = repo.join("bisect.state")
388 if os.path.exists(p):
388 if os.path.exists(p):
389 os.unlink(p)
389 os.unlink(p)
390 return
390 return
391
391
392 state = hbisect.load_state(repo)
392 state = hbisect.load_state(repo)
393
393
394 if command:
394 if command:
395 changesets = 1
395 changesets = 1
396 try:
396 try:
397 while changesets:
397 while changesets:
398 # update state
398 # update state
399 status = util.system(command)
399 status = util.system(command)
400 if status == 125:
400 if status == 125:
401 transition = "skip"
401 transition = "skip"
402 elif status == 0:
402 elif status == 0:
403 transition = "good"
403 transition = "good"
404 # status < 0 means process was killed
404 # status < 0 means process was killed
405 elif status == 127:
405 elif status == 127:
406 raise util.Abort(_("failed to execute %s") % command)
406 raise util.Abort(_("failed to execute %s") % command)
407 elif status < 0:
407 elif status < 0:
408 raise util.Abort(_("%s killed") % command)
408 raise util.Abort(_("%s killed") % command)
409 else:
409 else:
410 transition = "bad"
410 transition = "bad"
411 ctx = repo[rev or '.']
411 ctx = repo[rev or '.']
412 state[transition].append(ctx.node())
412 state[transition].append(ctx.node())
413 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
413 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
414 check_state(state, interactive=False)
414 check_state(state, interactive=False)
415 # bisect
415 # bisect
416 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
416 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
417 # update to next check
417 # update to next check
418 cmdutil.bail_if_changed(repo)
418 cmdutil.bail_if_changed(repo)
419 hg.clean(repo, nodes[0], show_stats=False)
419 hg.clean(repo, nodes[0], show_stats=False)
420 finally:
420 finally:
421 hbisect.save_state(repo, state)
421 hbisect.save_state(repo, state)
422 print_result(nodes, good)
422 print_result(nodes, good)
423 return
423 return
424
424
425 # update state
425 # update state
426 node = repo.lookup(rev or '.')
426 node = repo.lookup(rev or '.')
427 if good or bad or skip:
427 if good or bad or skip:
428 if good:
428 if good:
429 state['good'].append(node)
429 state['good'].append(node)
430 elif bad:
430 elif bad:
431 state['bad'].append(node)
431 state['bad'].append(node)
432 elif skip:
432 elif skip:
433 state['skip'].append(node)
433 state['skip'].append(node)
434 hbisect.save_state(repo, state)
434 hbisect.save_state(repo, state)
435
435
436 if not check_state(state):
436 if not check_state(state):
437 return
437 return
438
438
439 # actually bisect
439 # actually bisect
440 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
440 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
441 if changesets == 0:
441 if changesets == 0:
442 print_result(nodes, good)
442 print_result(nodes, good)
443 else:
443 else:
444 assert len(nodes) == 1 # only a single node can be tested next
444 assert len(nodes) == 1 # only a single node can be tested next
445 node = nodes[0]
445 node = nodes[0]
446 # compute the approximate number of remaining tests
446 # compute the approximate number of remaining tests
447 tests, size = 0, 2
447 tests, size = 0, 2
448 while size <= changesets:
448 while size <= changesets:
449 tests, size = tests + 1, size * 2
449 tests, size = tests + 1, size * 2
450 rev = repo.changelog.rev(node)
450 rev = repo.changelog.rev(node)
451 ui.write(_("Testing changeset %d:%s "
451 ui.write(_("Testing changeset %d:%s "
452 "(%d changesets remaining, ~%d tests)\n")
452 "(%d changesets remaining, ~%d tests)\n")
453 % (rev, short(node), changesets, tests))
453 % (rev, short(node), changesets, tests))
454 if not noupdate:
454 if not noupdate:
455 cmdutil.bail_if_changed(repo)
455 cmdutil.bail_if_changed(repo)
456 return hg.clean(repo, node)
456 return hg.clean(repo, node)
457
457
458 def branch(ui, repo, label=None, **opts):
458 def branch(ui, repo, label=None, **opts):
459 """set or show the current branch name
459 """set or show the current branch name
460
460
461 With no argument, show the current branch name. With one argument,
461 With no argument, show the current branch name. With one argument,
462 set the working directory branch name (the branch will not exist
462 set the working directory branch name (the branch will not exist
463 in the repository until the next commit). Standard practice
463 in the repository until the next commit). Standard practice
464 recommends that primary development take place on the 'default'
464 recommends that primary development take place on the 'default'
465 branch.
465 branch.
466
466
467 Unless -f/--force is specified, branch will not let you set a
467 Unless -f/--force is specified, branch will not let you set a
468 branch name that already exists, even if it's inactive.
468 branch name that already exists, even if it's inactive.
469
469
470 Use -C/--clean to reset the working directory branch to that of
470 Use -C/--clean to reset the working directory branch to that of
471 the parent of the working directory, negating a previous branch
471 the parent of the working directory, negating a previous branch
472 change.
472 change.
473
473
474 Use the command :hg:`update` to switch to an existing branch. Use
474 Use the command :hg:`update` to switch to an existing branch. Use
475 :hg:`commit --close-branch` to mark this branch as closed.
475 :hg:`commit --close-branch` to mark this branch as closed.
476
476
477 Returns 0 on success.
477 Returns 0 on success.
478 """
478 """
479
479
480 if opts.get('clean'):
480 if opts.get('clean'):
481 label = repo[None].parents()[0].branch()
481 label = repo[None].parents()[0].branch()
482 repo.dirstate.setbranch(label)
482 repo.dirstate.setbranch(label)
483 ui.status(_('reset working directory to branch %s\n') % label)
483 ui.status(_('reset working directory to branch %s\n') % label)
484 elif label:
484 elif label:
485 utflabel = encoding.fromlocal(label)
485 utflabel = encoding.fromlocal(label)
486 if not opts.get('force') and utflabel in repo.branchtags():
486 if not opts.get('force') and utflabel in repo.branchtags():
487 if label not in [p.branch() for p in repo.parents()]:
487 if label not in [p.branch() for p in repo.parents()]:
488 raise util.Abort(_('a branch of the same name already exists'
488 raise util.Abort(_('a branch of the same name already exists'
489 " (use 'hg update' to switch to it)"))
489 " (use 'hg update' to switch to it)"))
490 repo.dirstate.setbranch(utflabel)
490 repo.dirstate.setbranch(utflabel)
491 ui.status(_('marked working directory as branch %s\n') % label)
491 ui.status(_('marked working directory as branch %s\n') % label)
492 else:
492 else:
493 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
493 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
494
494
495 def branches(ui, repo, active=False, closed=False):
495 def branches(ui, repo, active=False, closed=False):
496 """list repository named branches
496 """list repository named branches
497
497
498 List the repository's named branches, indicating which ones are
498 List the repository's named branches, indicating which ones are
499 inactive. If -c/--closed is specified, also list branches which have
499 inactive. If -c/--closed is specified, also list branches which have
500 been marked closed (see :hg:`commit --close-branch`).
500 been marked closed (see :hg:`commit --close-branch`).
501
501
502 If -a/--active is specified, only show active branches. A branch
502 If -a/--active is specified, only show active branches. A branch
503 is considered active if it contains repository heads.
503 is considered active if it contains repository heads.
504
504
505 Use the command :hg:`update` to switch to an existing branch.
505 Use the command :hg:`update` to switch to an existing branch.
506
506
507 Returns 0.
507 Returns 0.
508 """
508 """
509
509
510 hexfunc = ui.debugflag and hex or short
510 hexfunc = ui.debugflag and hex or short
511 activebranches = [repo[n].branch() for n in repo.heads()]
511 activebranches = [repo[n].branch() for n in repo.heads()]
512 def testactive(tag, node):
512 def testactive(tag, node):
513 realhead = tag in activebranches
513 realhead = tag in activebranches
514 open = node in repo.branchheads(tag, closed=False)
514 open = node in repo.branchheads(tag, closed=False)
515 return realhead and open
515 return realhead and open
516 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
516 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
517 for tag, node in repo.branchtags().items()],
517 for tag, node in repo.branchtags().items()],
518 reverse=True)
518 reverse=True)
519
519
520 for isactive, node, tag in branches:
520 for isactive, node, tag in branches:
521 if (not active) or isactive:
521 if (not active) or isactive:
522 encodedtag = encoding.tolocal(tag)
522 encodedtag = encoding.tolocal(tag)
523 if ui.quiet:
523 if ui.quiet:
524 ui.write("%s\n" % encodedtag)
524 ui.write("%s\n" % encodedtag)
525 else:
525 else:
526 hn = repo.lookup(node)
526 hn = repo.lookup(node)
527 if isactive:
527 if isactive:
528 notice = ''
528 notice = ''
529 elif hn not in repo.branchheads(tag, closed=False):
529 elif hn not in repo.branchheads(tag, closed=False):
530 if not closed:
530 if not closed:
531 continue
531 continue
532 notice = _(' (closed)')
532 notice = _(' (closed)')
533 else:
533 else:
534 notice = _(' (inactive)')
534 notice = _(' (inactive)')
535 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
535 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
536 data = encodedtag, rev, hexfunc(hn), notice
536 data = encodedtag, rev, hexfunc(hn), notice
537 ui.write("%s %s:%s%s\n" % data)
537 ui.write("%s %s:%s%s\n" % data)
538
538
539 def bundle(ui, repo, fname, dest=None, **opts):
539 def bundle(ui, repo, fname, dest=None, **opts):
540 """create a changegroup file
540 """create a changegroup file
541
541
542 Generate a compressed changegroup file collecting changesets not
542 Generate a compressed changegroup file collecting changesets not
543 known to be in another repository.
543 known to be in another repository.
544
544
545 If you omit the destination repository, then hg assumes the
545 If you omit the destination repository, then hg assumes the
546 destination will have all the nodes you specify with --base
546 destination will have all the nodes you specify with --base
547 parameters. To create a bundle containing all changesets, use
547 parameters. To create a bundle containing all changesets, use
548 -a/--all (or --base null).
548 -a/--all (or --base null).
549
549
550 You can change compression method with the -t/--type option.
550 You can change compression method with the -t/--type option.
551 The available compression methods are: none, bzip2, and
551 The available compression methods are: none, bzip2, and
552 gzip (by default, bundles are compressed using bzip2).
552 gzip (by default, bundles are compressed using bzip2).
553
553
554 The bundle file can then be transferred using conventional means
554 The bundle file can then be transferred using conventional means
555 and applied to another repository with the unbundle or pull
555 and applied to another repository with the unbundle or pull
556 command. This is useful when direct push and pull are not
556 command. This is useful when direct push and pull are not
557 available or when exporting an entire repository is undesirable.
557 available or when exporting an entire repository is undesirable.
558
558
559 Applying bundles preserves all changeset contents including
559 Applying bundles preserves all changeset contents including
560 permissions, copy/rename information, and revision history.
560 permissions, copy/rename information, and revision history.
561
561
562 Returns 0 on success, 1 if no changes found.
562 Returns 0 on success, 1 if no changes found.
563 """
563 """
564 revs = opts.get('rev') or None
564 revs = opts.get('rev') or None
565 if opts.get('all'):
565 if opts.get('all'):
566 base = ['null']
566 base = ['null']
567 else:
567 else:
568 base = opts.get('base')
568 base = opts.get('base')
569 if base:
569 if base:
570 if dest:
570 if dest:
571 raise util.Abort(_("--base is incompatible with specifying "
571 raise util.Abort(_("--base is incompatible with specifying "
572 "a destination"))
572 "a destination"))
573 base = [repo.lookup(rev) for rev in base]
573 base = [repo.lookup(rev) for rev in base]
574 # create the right base
574 # create the right base
575 # XXX: nodesbetween / changegroup* should be "fixed" instead
575 # XXX: nodesbetween / changegroup* should be "fixed" instead
576 o = []
576 o = []
577 has = set((nullid,))
577 has = set((nullid,))
578 for n in base:
578 for n in base:
579 has.update(repo.changelog.reachable(n))
579 has.update(repo.changelog.reachable(n))
580 if revs:
580 if revs:
581 revs = [repo.lookup(rev) for rev in revs]
581 revs = [repo.lookup(rev) for rev in revs]
582 visit = revs[:]
582 visit = revs[:]
583 has.difference_update(visit)
583 has.difference_update(visit)
584 else:
584 else:
585 visit = repo.changelog.heads()
585 visit = repo.changelog.heads()
586 seen = {}
586 seen = {}
587 while visit:
587 while visit:
588 n = visit.pop(0)
588 n = visit.pop(0)
589 parents = [p for p in repo.changelog.parents(n) if p not in has]
589 parents = [p for p in repo.changelog.parents(n) if p not in has]
590 if len(parents) == 0:
590 if len(parents) == 0:
591 if n not in has:
591 if n not in has:
592 o.append(n)
592 o.append(n)
593 else:
593 else:
594 for p in parents:
594 for p in parents:
595 if p not in seen:
595 if p not in seen:
596 seen[p] = 1
596 seen[p] = 1
597 visit.append(p)
597 visit.append(p)
598 else:
598 else:
599 dest = ui.expandpath(dest or 'default-push', dest or 'default')
599 dest = ui.expandpath(dest or 'default-push', dest or 'default')
600 dest, branches = hg.parseurl(dest, opts.get('branch'))
600 dest, branches = hg.parseurl(dest, opts.get('branch'))
601 other = hg.repository(hg.remoteui(repo, opts), dest)
601 other = hg.repository(hg.remoteui(repo, opts), dest)
602 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
602 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
603 if revs:
603 if revs:
604 revs = [repo.lookup(rev) for rev in revs]
604 revs = [repo.lookup(rev) for rev in revs]
605 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
605 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
606
606
607 if not o:
607 if not o:
608 ui.status(_("no changes found\n"))
608 ui.status(_("no changes found\n"))
609 return 1
609 return 1
610
610
611 if revs:
611 if revs:
612 cg = repo.changegroupsubset(o, revs, 'bundle')
612 cg = repo.changegroupsubset(o, revs, 'bundle')
613 else:
613 else:
614 cg = repo.changegroup(o, 'bundle')
614 cg = repo.changegroup(o, 'bundle')
615
615
616 bundletype = opts.get('type', 'bzip2').lower()
616 bundletype = opts.get('type', 'bzip2').lower()
617 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
617 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
618 bundletype = btypes.get(bundletype)
618 bundletype = btypes.get(bundletype)
619 if bundletype not in changegroup.bundletypes:
619 if bundletype not in changegroup.bundletypes:
620 raise util.Abort(_('unknown bundle type specified with --type'))
620 raise util.Abort(_('unknown bundle type specified with --type'))
621
621
622 changegroup.writebundle(cg, fname, bundletype)
622 changegroup.writebundle(cg, fname, bundletype)
623
623
624 def cat(ui, repo, file1, *pats, **opts):
624 def cat(ui, repo, file1, *pats, **opts):
625 """output the current or given revision of files
625 """output the current or given revision of files
626
626
627 Print the specified files as they were at the given revision. If
627 Print the specified files as they were at the given revision. If
628 no revision is given, the parent of the working directory is used,
628 no revision is given, the parent of the working directory is used,
629 or tip if no revision is checked out.
629 or tip if no revision is checked out.
630
630
631 Output may be to a file, in which case the name of the file is
631 Output may be to a file, in which case the name of the file is
632 given using a format string. The formatting rules are the same as
632 given using a format string. The formatting rules are the same as
633 for the export command, with the following additions:
633 for the export command, with the following additions:
634
634
635 :``%s``: basename of file being printed
635 :``%s``: basename of file being printed
636 :``%d``: dirname of file being printed, or '.' if in repository root
636 :``%d``: dirname of file being printed, or '.' if in repository root
637 :``%p``: root-relative path name of file being printed
637 :``%p``: root-relative path name of file being printed
638
638
639 Returns 0 on success.
639 Returns 0 on success.
640 """
640 """
641 ctx = repo[opts.get('rev')]
641 ctx = repo[opts.get('rev')]
642 err = 1
642 err = 1
643 m = cmdutil.match(repo, (file1,) + pats, opts)
643 m = cmdutil.match(repo, (file1,) + pats, opts)
644 for abs in ctx.walk(m):
644 for abs in ctx.walk(m):
645 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
645 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
646 data = ctx[abs].data()
646 data = ctx[abs].data()
647 if opts.get('decode'):
647 if opts.get('decode'):
648 data = repo.wwritedata(abs, data)
648 data = repo.wwritedata(abs, data)
649 fp.write(data)
649 fp.write(data)
650 err = 0
650 err = 0
651 return err
651 return err
652
652
653 def clone(ui, source, dest=None, **opts):
653 def clone(ui, source, dest=None, **opts):
654 """make a copy of an existing repository
654 """make a copy of an existing repository
655
655
656 Create a copy of an existing repository in a new directory.
656 Create a copy of an existing repository in a new directory.
657
657
658 If no destination directory name is specified, it defaults to the
658 If no destination directory name is specified, it defaults to the
659 basename of the source.
659 basename of the source.
660
660
661 The location of the source is added to the new repository's
661 The location of the source is added to the new repository's
662 .hg/hgrc file, as the default to be used for future pulls.
662 .hg/hgrc file, as the default to be used for future pulls.
663
663
664 See :hg:`help urls` for valid source format details.
664 See :hg:`help urls` for valid source format details.
665
665
666 It is possible to specify an ``ssh://`` URL as the destination, but no
666 It is possible to specify an ``ssh://`` URL as the destination, but no
667 .hg/hgrc and working directory will be created on the remote side.
667 .hg/hgrc and working directory will be created on the remote side.
668 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
668 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
669
669
670 A set of changesets (tags, or branch names) to pull may be specified
670 A set of changesets (tags, or branch names) to pull may be specified
671 by listing each changeset (tag, or branch name) with -r/--rev.
671 by listing each changeset (tag, or branch name) with -r/--rev.
672 If -r/--rev is used, the cloned repository will contain only a subset
672 If -r/--rev is used, the cloned repository will contain only a subset
673 of the changesets of the source repository. Only the set of changesets
673 of the changesets of the source repository. Only the set of changesets
674 defined by all -r/--rev options (including all their ancestors)
674 defined by all -r/--rev options (including all their ancestors)
675 will be pulled into the destination repository.
675 will be pulled into the destination repository.
676 No subsequent changesets (including subsequent tags) will be present
676 No subsequent changesets (including subsequent tags) will be present
677 in the destination.
677 in the destination.
678
678
679 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
679 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
680 local source repositories.
680 local source repositories.
681
681
682 For efficiency, hardlinks are used for cloning whenever the source
682 For efficiency, hardlinks are used for cloning whenever the source
683 and destination are on the same filesystem (note this applies only
683 and destination are on the same filesystem (note this applies only
684 to the repository data, not to the working directory). Some
684 to the repository data, not to the working directory). Some
685 filesystems, such as AFS, implement hardlinking incorrectly, but
685 filesystems, such as AFS, implement hardlinking incorrectly, but
686 do not report errors. In these cases, use the --pull option to
686 do not report errors. In these cases, use the --pull option to
687 avoid hardlinking.
687 avoid hardlinking.
688
688
689 In some cases, you can clone repositories and the working directory
689 In some cases, you can clone repositories and the working directory
690 using full hardlinks with ::
690 using full hardlinks with ::
691
691
692 $ cp -al REPO REPOCLONE
692 $ cp -al REPO REPOCLONE
693
693
694 This is the fastest way to clone, but it is not always safe. The
694 This is the fastest way to clone, but it is not always safe. The
695 operation is not atomic (making sure REPO is not modified during
695 operation is not atomic (making sure REPO is not modified during
696 the operation is up to you) and you have to make sure your editor
696 the operation is up to you) and you have to make sure your editor
697 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
697 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
698 this is not compatible with certain extensions that place their
698 this is not compatible with certain extensions that place their
699 metadata under the .hg directory, such as mq.
699 metadata under the .hg directory, such as mq.
700
700
701 Mercurial will update the working directory to the first applicable
701 Mercurial will update the working directory to the first applicable
702 revision from this list:
702 revision from this list:
703
703
704 a) null if -U or the source repository has no changesets
704 a) null if -U or the source repository has no changesets
705 b) if -u . and the source repository is local, the first parent of
705 b) if -u . and the source repository is local, the first parent of
706 the source repository's working directory
706 the source repository's working directory
707 c) the changeset specified with -u (if a branch name, this means the
707 c) the changeset specified with -u (if a branch name, this means the
708 latest head of that branch)
708 latest head of that branch)
709 d) the changeset specified with -r
709 d) the changeset specified with -r
710 e) the tipmost head specified with -b
710 e) the tipmost head specified with -b
711 f) the tipmost head specified with the url#branch source syntax
711 f) the tipmost head specified with the url#branch source syntax
712 g) the tipmost head of the default branch
712 g) the tipmost head of the default branch
713 h) tip
713 h) tip
714
714
715 Returns 0 on success.
715 Returns 0 on success.
716 """
716 """
717 if opts.get('noupdate') and opts.get('updaterev'):
717 if opts.get('noupdate') and opts.get('updaterev'):
718 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
718 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
719
719
720 r = hg.clone(hg.remoteui(ui, opts), source, dest,
720 r = hg.clone(hg.remoteui(ui, opts), source, dest,
721 pull=opts.get('pull'),
721 pull=opts.get('pull'),
722 stream=opts.get('uncompressed'),
722 stream=opts.get('uncompressed'),
723 rev=opts.get('rev'),
723 rev=opts.get('rev'),
724 update=opts.get('updaterev') or not opts.get('noupdate'),
724 update=opts.get('updaterev') or not opts.get('noupdate'),
725 branch=opts.get('branch'))
725 branch=opts.get('branch'))
726
726
727 return r is None
727 return r is None
728
728
729 def commit(ui, repo, *pats, **opts):
729 def commit(ui, repo, *pats, **opts):
730 """commit the specified files or all outstanding changes
730 """commit the specified files or all outstanding changes
731
731
732 Commit changes to the given files into the repository. Unlike a
732 Commit changes to the given files into the repository. Unlike a
733 centralized RCS, this operation is a local operation. See
733 centralized RCS, this operation is a local operation. See
734 :hg:`push` for a way to actively distribute your changes.
734 :hg:`push` for a way to actively distribute your changes.
735
735
736 If a list of files is omitted, all changes reported by :hg:`status`
736 If a list of files is omitted, all changes reported by :hg:`status`
737 will be committed.
737 will be committed.
738
738
739 If you are committing the result of a merge, do not provide any
739 If you are committing the result of a merge, do not provide any
740 filenames or -I/-X filters.
740 filenames or -I/-X filters.
741
741
742 If no commit message is specified, Mercurial starts your
742 If no commit message is specified, Mercurial starts your
743 configured editor where you can enter a message. In case your
743 configured editor where you can enter a message. In case your
744 commit fails, you will find a backup of your message in
744 commit fails, you will find a backup of your message in
745 ``.hg/last-message.txt``.
745 ``.hg/last-message.txt``.
746
746
747 See :hg:`help dates` for a list of formats valid for -d/--date.
747 See :hg:`help dates` for a list of formats valid for -d/--date.
748
748
749 Returns 0 on success, 1 if nothing changed.
749 Returns 0 on success, 1 if nothing changed.
750 """
750 """
751 extra = {}
751 extra = {}
752 if opts.get('close_branch'):
752 if opts.get('close_branch'):
753 if repo['.'].node() not in repo.branchheads():
753 if repo['.'].node() not in repo.branchheads():
754 # The topo heads set is included in the branch heads set of the
754 # The topo heads set is included in the branch heads set of the
755 # current branch, so it's sufficient to test branchheads
755 # current branch, so it's sufficient to test branchheads
756 raise util.Abort(_('can only close branch heads'))
756 raise util.Abort(_('can only close branch heads'))
757 extra['close'] = 1
757 extra['close'] = 1
758 e = cmdutil.commiteditor
758 e = cmdutil.commiteditor
759 if opts.get('force_editor'):
759 if opts.get('force_editor'):
760 e = cmdutil.commitforceeditor
760 e = cmdutil.commitforceeditor
761
761
762 def commitfunc(ui, repo, message, match, opts):
762 def commitfunc(ui, repo, message, match, opts):
763 return repo.commit(message, opts.get('user'), opts.get('date'), match,
763 return repo.commit(message, opts.get('user'), opts.get('date'), match,
764 editor=e, extra=extra)
764 editor=e, extra=extra)
765
765
766 branch = repo[None].branch()
766 branch = repo[None].branch()
767 bheads = repo.branchheads(branch)
767 bheads = repo.branchheads(branch)
768
768
769 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
769 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
770 if not node:
770 if not node:
771 ui.status(_("nothing changed\n"))
771 ui.status(_("nothing changed\n"))
772 return 1
772 return 1
773
773
774 ctx = repo[node]
774 ctx = repo[node]
775 parents = ctx.parents()
775 parents = ctx.parents()
776
776
777 if bheads and not [x for x in parents
777 if bheads and not [x for x in parents
778 if x.node() in bheads and x.branch() == branch]:
778 if x.node() in bheads and x.branch() == branch]:
779 ui.status(_('created new head\n'))
779 ui.status(_('created new head\n'))
780 # The message is not printed for initial roots. For the other
780 # The message is not printed for initial roots. For the other
781 # changesets, it is printed in the following situations:
781 # changesets, it is printed in the following situations:
782 #
782 #
783 # Par column: for the 2 parents with ...
783 # Par column: for the 2 parents with ...
784 # N: null or no parent
784 # N: null or no parent
785 # B: parent is on another named branch
785 # B: parent is on another named branch
786 # C: parent is a regular non head changeset
786 # C: parent is a regular non head changeset
787 # H: parent was a branch head of the current branch
787 # H: parent was a branch head of the current branch
788 # Msg column: whether we print "created new head" message
788 # Msg column: whether we print "created new head" message
789 # In the following, it is assumed that there already exists some
789 # In the following, it is assumed that there already exists some
790 # initial branch heads of the current branch, otherwise nothing is
790 # initial branch heads of the current branch, otherwise nothing is
791 # printed anyway.
791 # printed anyway.
792 #
792 #
793 # Par Msg Comment
793 # Par Msg Comment
794 # NN y additional topo root
794 # NN y additional topo root
795 #
795 #
796 # BN y additional branch root
796 # BN y additional branch root
797 # CN y additional topo head
797 # CN y additional topo head
798 # HN n usual case
798 # HN n usual case
799 #
799 #
800 # BB y weird additional branch root
800 # BB y weird additional branch root
801 # CB y branch merge
801 # CB y branch merge
802 # HB n merge with named branch
802 # HB n merge with named branch
803 #
803 #
804 # CC y additional head from merge
804 # CC y additional head from merge
805 # CH n merge with a head
805 # CH n merge with a head
806 #
806 #
807 # HH n head merge: head count decreases
807 # HH n head merge: head count decreases
808
808
809 if not opts.get('close_branch'):
809 if not opts.get('close_branch'):
810 for r in parents:
810 for r in parents:
811 if r.extra().get('close') and r.branch() == branch:
811 if r.extra().get('close') and r.branch() == branch:
812 ui.status(_('reopening closed branch head %d\n') % r)
812 ui.status(_('reopening closed branch head %d\n') % r)
813
813
814 if ui.debugflag:
814 if ui.debugflag:
815 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
815 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
816 elif ui.verbose:
816 elif ui.verbose:
817 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
817 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
818
818
819 def copy(ui, repo, *pats, **opts):
819 def copy(ui, repo, *pats, **opts):
820 """mark files as copied for the next commit
820 """mark files as copied for the next commit
821
821
822 Mark dest as having copies of source files. If dest is a
822 Mark dest as having copies of source files. If dest is a
823 directory, copies are put in that directory. If dest is a file,
823 directory, copies are put in that directory. If dest is a file,
824 the source must be a single file.
824 the source must be a single file.
825
825
826 By default, this command copies the contents of files as they
826 By default, this command copies the contents of files as they
827 exist in the working directory. If invoked with -A/--after, the
827 exist in the working directory. If invoked with -A/--after, the
828 operation is recorded, but no copying is performed.
828 operation is recorded, but no copying is performed.
829
829
830 This command takes effect with the next commit. To undo a copy
830 This command takes effect with the next commit. To undo a copy
831 before that, see :hg:`revert`.
831 before that, see :hg:`revert`.
832
832
833 Returns 0 on success, 1 if errors are encountered.
833 Returns 0 on success, 1 if errors are encountered.
834 """
834 """
835 wlock = repo.wlock(False)
835 wlock = repo.wlock(False)
836 try:
836 try:
837 return cmdutil.copy(ui, repo, pats, opts)
837 return cmdutil.copy(ui, repo, pats, opts)
838 finally:
838 finally:
839 wlock.release()
839 wlock.release()
840
840
841 def debugancestor(ui, repo, *args):
841 def debugancestor(ui, repo, *args):
842 """find the ancestor revision of two revisions in a given index"""
842 """find the ancestor revision of two revisions in a given index"""
843 if len(args) == 3:
843 if len(args) == 3:
844 index, rev1, rev2 = args
844 index, rev1, rev2 = args
845 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
845 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
846 lookup = r.lookup
846 lookup = r.lookup
847 elif len(args) == 2:
847 elif len(args) == 2:
848 if not repo:
848 if not repo:
849 raise util.Abort(_("There is no Mercurial repository here "
849 raise util.Abort(_("there is no Mercurial repository here "
850 "(.hg not found)"))
850 "(.hg not found)"))
851 rev1, rev2 = args
851 rev1, rev2 = args
852 r = repo.changelog
852 r = repo.changelog
853 lookup = repo.lookup
853 lookup = repo.lookup
854 else:
854 else:
855 raise util.Abort(_('either two or three arguments required'))
855 raise util.Abort(_('either two or three arguments required'))
856 a = r.ancestor(lookup(rev1), lookup(rev2))
856 a = r.ancestor(lookup(rev1), lookup(rev2))
857 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
857 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
858
858
859 def debugbuilddag(ui, repo, text,
859 def debugbuilddag(ui, repo, text,
860 mergeable_file=False,
860 mergeable_file=False,
861 appended_file=False,
861 appended_file=False,
862 overwritten_file=False,
862 overwritten_file=False,
863 new_file=False):
863 new_file=False):
864 """builds a repo with a given dag from scratch in the current empty repo
864 """builds a repo with a given dag from scratch in the current empty repo
865
865
866 Elements:
866 Elements:
867
867
868 - "+n" is a linear run of n nodes based on the current default parent
868 - "+n" is a linear run of n nodes based on the current default parent
869 - "." is a single node based on the current default parent
869 - "." is a single node based on the current default parent
870 - "$" resets the default parent to null (implied at the start);
870 - "$" resets the default parent to null (implied at the start);
871 otherwise the default parent is always the last node created
871 otherwise the default parent is always the last node created
872 - "<p" sets the default parent to the backref p
872 - "<p" sets the default parent to the backref p
873 - "*p" is a fork at parent p, which is a backref
873 - "*p" is a fork at parent p, which is a backref
874 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
874 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
875 - "/p2" is a merge of the preceding node and p2
875 - "/p2" is a merge of the preceding node and p2
876 - ":tag" defines a local tag for the preceding node
876 - ":tag" defines a local tag for the preceding node
877 - "@branch" sets the named branch for subsequent nodes
877 - "@branch" sets the named branch for subsequent nodes
878 - "!command" runs the command using your shell
878 - "!command" runs the command using your shell
879 - "!!my command\\n" is like "!", but to the end of the line
879 - "!!my command\\n" is like "!", but to the end of the line
880 - "#...\\n" is a comment up to the end of the line
880 - "#...\\n" is a comment up to the end of the line
881
881
882 Whitespace between the above elements is ignored.
882 Whitespace between the above elements is ignored.
883
883
884 A backref is either
884 A backref is either
885
885
886 - a number n, which references the node curr-n, where curr is the current
886 - a number n, which references the node curr-n, where curr is the current
887 node, or
887 node, or
888 - the name of a local tag you placed earlier using ":tag", or
888 - the name of a local tag you placed earlier using ":tag", or
889 - empty to denote the default parent.
889 - empty to denote the default parent.
890
890
891 All string valued-elements are either strictly alphanumeric, or must
891 All string valued-elements are either strictly alphanumeric, or must
892 be enclosed in double quotes ("..."), with "\\" as escape character.
892 be enclosed in double quotes ("..."), with "\\" as escape character.
893
893
894 Note that the --overwritten-file and --appended-file options imply the
894 Note that the --overwritten-file and --appended-file options imply the
895 use of "HGMERGE=internal:local" during DAG buildup.
895 use of "HGMERGE=internal:local" during DAG buildup.
896 """
896 """
897
897
898 if not (mergeable_file or appended_file or overwritten_file or new_file):
898 if not (mergeable_file or appended_file or overwritten_file or new_file):
899 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
899 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
900
900
901 if len(repo.changelog) > 0:
901 if len(repo.changelog) > 0:
902 raise util.Abort(_('repository is not empty'))
902 raise util.Abort(_('repository is not empty'))
903
903
904 if overwritten_file or appended_file:
904 if overwritten_file or appended_file:
905 # we don't want to fail in merges during buildup
905 # we don't want to fail in merges during buildup
906 os.environ['HGMERGE'] = 'internal:local'
906 os.environ['HGMERGE'] = 'internal:local'
907
907
908 def writefile(fname, text, fmode="w"):
908 def writefile(fname, text, fmode="w"):
909 f = open(fname, fmode)
909 f = open(fname, fmode)
910 try:
910 try:
911 f.write(text)
911 f.write(text)
912 finally:
912 finally:
913 f.close()
913 f.close()
914
914
915 if mergeable_file:
915 if mergeable_file:
916 linesperrev = 2
916 linesperrev = 2
917 # determine number of revs in DAG
917 # determine number of revs in DAG
918 n = 0
918 n = 0
919 for type, data in dagparser.parsedag(text):
919 for type, data in dagparser.parsedag(text):
920 if type == 'n':
920 if type == 'n':
921 n += 1
921 n += 1
922 # make a file with k lines per rev
922 # make a file with k lines per rev
923 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
923 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
924 + "\n")
924 + "\n")
925
925
926 at = -1
926 at = -1
927 atbranch = 'default'
927 atbranch = 'default'
928 for type, data in dagparser.parsedag(text):
928 for type, data in dagparser.parsedag(text):
929 if type == 'n':
929 if type == 'n':
930 ui.status('node %s\n' % str(data))
930 ui.status('node %s\n' % str(data))
931 id, ps = data
931 id, ps = data
932 p1 = ps[0]
932 p1 = ps[0]
933 if p1 != at:
933 if p1 != at:
934 update(ui, repo, node=p1, clean=True)
934 update(ui, repo, node=p1, clean=True)
935 at = p1
935 at = p1
936 if repo.dirstate.branch() != atbranch:
936 if repo.dirstate.branch() != atbranch:
937 branch(ui, repo, atbranch, force=True)
937 branch(ui, repo, atbranch, force=True)
938 if len(ps) > 1:
938 if len(ps) > 1:
939 p2 = ps[1]
939 p2 = ps[1]
940 merge(ui, repo, node=p2)
940 merge(ui, repo, node=p2)
941
941
942 if mergeable_file:
942 if mergeable_file:
943 f = open("mf", "r+")
943 f = open("mf", "r+")
944 try:
944 try:
945 lines = f.read().split("\n")
945 lines = f.read().split("\n")
946 lines[id * linesperrev] += " r%i" % id
946 lines[id * linesperrev] += " r%i" % id
947 f.seek(0)
947 f.seek(0)
948 f.write("\n".join(lines))
948 f.write("\n".join(lines))
949 finally:
949 finally:
950 f.close()
950 f.close()
951
951
952 if appended_file:
952 if appended_file:
953 writefile("af", "r%i\n" % id, "a")
953 writefile("af", "r%i\n" % id, "a")
954
954
955 if overwritten_file:
955 if overwritten_file:
956 writefile("of", "r%i\n" % id)
956 writefile("of", "r%i\n" % id)
957
957
958 if new_file:
958 if new_file:
959 writefile("nf%i" % id, "r%i\n" % id)
959 writefile("nf%i" % id, "r%i\n" % id)
960
960
961 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
961 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
962 at = id
962 at = id
963 elif type == 'l':
963 elif type == 'l':
964 id, name = data
964 id, name = data
965 ui.status('tag %s\n' % name)
965 ui.status('tag %s\n' % name)
966 tag(ui, repo, name, local=True)
966 tag(ui, repo, name, local=True)
967 elif type == 'a':
967 elif type == 'a':
968 ui.status('branch %s\n' % data)
968 ui.status('branch %s\n' % data)
969 atbranch = data
969 atbranch = data
970 elif type in 'cC':
970 elif type in 'cC':
971 r = util.system(data, cwd=repo.root)
971 r = util.system(data, cwd=repo.root)
972 if r:
972 if r:
973 desc, r = util.explain_exit(r)
973 desc, r = util.explain_exit(r)
974 raise util.Abort(_('%s command %s') % (data, desc))
974 raise util.Abort(_('%s command %s') % (data, desc))
975
975
976 def debugcommands(ui, cmd='', *args):
976 def debugcommands(ui, cmd='', *args):
977 """list all available commands and options"""
977 """list all available commands and options"""
978 for cmd, vals in sorted(table.iteritems()):
978 for cmd, vals in sorted(table.iteritems()):
979 cmd = cmd.split('|')[0].strip('^')
979 cmd = cmd.split('|')[0].strip('^')
980 opts = ', '.join([i[1] for i in vals[1]])
980 opts = ', '.join([i[1] for i in vals[1]])
981 ui.write('%s: %s\n' % (cmd, opts))
981 ui.write('%s: %s\n' % (cmd, opts))
982
982
983 def debugcomplete(ui, cmd='', **opts):
983 def debugcomplete(ui, cmd='', **opts):
984 """returns the completion list associated with the given command"""
984 """returns the completion list associated with the given command"""
985
985
986 if opts.get('options'):
986 if opts.get('options'):
987 options = []
987 options = []
988 otables = [globalopts]
988 otables = [globalopts]
989 if cmd:
989 if cmd:
990 aliases, entry = cmdutil.findcmd(cmd, table, False)
990 aliases, entry = cmdutil.findcmd(cmd, table, False)
991 otables.append(entry[1])
991 otables.append(entry[1])
992 for t in otables:
992 for t in otables:
993 for o in t:
993 for o in t:
994 if "(DEPRECATED)" in o[3]:
994 if "(DEPRECATED)" in o[3]:
995 continue
995 continue
996 if o[0]:
996 if o[0]:
997 options.append('-%s' % o[0])
997 options.append('-%s' % o[0])
998 options.append('--%s' % o[1])
998 options.append('--%s' % o[1])
999 ui.write("%s\n" % "\n".join(options))
999 ui.write("%s\n" % "\n".join(options))
1000 return
1000 return
1001
1001
1002 cmdlist = cmdutil.findpossible(cmd, table)
1002 cmdlist = cmdutil.findpossible(cmd, table)
1003 if ui.verbose:
1003 if ui.verbose:
1004 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1004 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1005 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1005 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1006
1006
1007 def debugfsinfo(ui, path = "."):
1007 def debugfsinfo(ui, path = "."):
1008 """show information detected about current filesystem"""
1008 """show information detected about current filesystem"""
1009 open('.debugfsinfo', 'w').write('')
1009 open('.debugfsinfo', 'w').write('')
1010 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1010 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1011 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1011 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1012 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1012 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1013 and 'yes' or 'no'))
1013 and 'yes' or 'no'))
1014 os.unlink('.debugfsinfo')
1014 os.unlink('.debugfsinfo')
1015
1015
1016 def debugrebuildstate(ui, repo, rev="tip"):
1016 def debugrebuildstate(ui, repo, rev="tip"):
1017 """rebuild the dirstate as it would look like for the given revision"""
1017 """rebuild the dirstate as it would look like for the given revision"""
1018 ctx = repo[rev]
1018 ctx = repo[rev]
1019 wlock = repo.wlock()
1019 wlock = repo.wlock()
1020 try:
1020 try:
1021 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1021 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1022 finally:
1022 finally:
1023 wlock.release()
1023 wlock.release()
1024
1024
1025 def debugcheckstate(ui, repo):
1025 def debugcheckstate(ui, repo):
1026 """validate the correctness of the current dirstate"""
1026 """validate the correctness of the current dirstate"""
1027 parent1, parent2 = repo.dirstate.parents()
1027 parent1, parent2 = repo.dirstate.parents()
1028 m1 = repo[parent1].manifest()
1028 m1 = repo[parent1].manifest()
1029 m2 = repo[parent2].manifest()
1029 m2 = repo[parent2].manifest()
1030 errors = 0
1030 errors = 0
1031 for f in repo.dirstate:
1031 for f in repo.dirstate:
1032 state = repo.dirstate[f]
1032 state = repo.dirstate[f]
1033 if state in "nr" and f not in m1:
1033 if state in "nr" and f not in m1:
1034 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1034 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1035 errors += 1
1035 errors += 1
1036 if state in "a" and f in m1:
1036 if state in "a" and f in m1:
1037 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1037 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1038 errors += 1
1038 errors += 1
1039 if state in "m" and f not in m1 and f not in m2:
1039 if state in "m" and f not in m1 and f not in m2:
1040 ui.warn(_("%s in state %s, but not in either manifest\n") %
1040 ui.warn(_("%s in state %s, but not in either manifest\n") %
1041 (f, state))
1041 (f, state))
1042 errors += 1
1042 errors += 1
1043 for f in m1:
1043 for f in m1:
1044 state = repo.dirstate[f]
1044 state = repo.dirstate[f]
1045 if state not in "nrm":
1045 if state not in "nrm":
1046 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1046 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1047 errors += 1
1047 errors += 1
1048 if errors:
1048 if errors:
1049 error = _(".hg/dirstate inconsistent with current parent's manifest")
1049 error = _(".hg/dirstate inconsistent with current parent's manifest")
1050 raise util.Abort(error)
1050 raise util.Abort(error)
1051
1051
1052 def showconfig(ui, repo, *values, **opts):
1052 def showconfig(ui, repo, *values, **opts):
1053 """show combined config settings from all hgrc files
1053 """show combined config settings from all hgrc files
1054
1054
1055 With no arguments, print names and values of all config items.
1055 With no arguments, print names and values of all config items.
1056
1056
1057 With one argument of the form section.name, print just the value
1057 With one argument of the form section.name, print just the value
1058 of that config item.
1058 of that config item.
1059
1059
1060 With multiple arguments, print names and values of all config
1060 With multiple arguments, print names and values of all config
1061 items with matching section names.
1061 items with matching section names.
1062
1062
1063 With --debug, the source (filename and line number) is printed
1063 With --debug, the source (filename and line number) is printed
1064 for each config item.
1064 for each config item.
1065
1065
1066 Returns 0 on success.
1066 Returns 0 on success.
1067 """
1067 """
1068
1068
1069 for f in util.rcpath():
1069 for f in util.rcpath():
1070 ui.debug(_('read config from: %s\n') % f)
1070 ui.debug(_('read config from: %s\n') % f)
1071 untrusted = bool(opts.get('untrusted'))
1071 untrusted = bool(opts.get('untrusted'))
1072 if values:
1072 if values:
1073 if len([v for v in values if '.' in v]) > 1:
1073 if len([v for v in values if '.' in v]) > 1:
1074 raise util.Abort(_('only one config item permitted'))
1074 raise util.Abort(_('only one config item permitted'))
1075 for section, name, value in ui.walkconfig(untrusted=untrusted):
1075 for section, name, value in ui.walkconfig(untrusted=untrusted):
1076 sectname = section + '.' + name
1076 sectname = section + '.' + name
1077 if values:
1077 if values:
1078 for v in values:
1078 for v in values:
1079 if v == section:
1079 if v == section:
1080 ui.debug('%s: ' %
1080 ui.debug('%s: ' %
1081 ui.configsource(section, name, untrusted))
1081 ui.configsource(section, name, untrusted))
1082 ui.write('%s=%s\n' % (sectname, value))
1082 ui.write('%s=%s\n' % (sectname, value))
1083 elif v == sectname:
1083 elif v == sectname:
1084 ui.debug('%s: ' %
1084 ui.debug('%s: ' %
1085 ui.configsource(section, name, untrusted))
1085 ui.configsource(section, name, untrusted))
1086 ui.write(value, '\n')
1086 ui.write(value, '\n')
1087 else:
1087 else:
1088 ui.debug('%s: ' %
1088 ui.debug('%s: ' %
1089 ui.configsource(section, name, untrusted))
1089 ui.configsource(section, name, untrusted))
1090 ui.write('%s=%s\n' % (sectname, value))
1090 ui.write('%s=%s\n' % (sectname, value))
1091
1091
1092 def debugpushkey(ui, repopath, namespace, *keyinfo):
1092 def debugpushkey(ui, repopath, namespace, *keyinfo):
1093 '''access the pushkey key/value protocol
1093 '''access the pushkey key/value protocol
1094
1094
1095 With two args, list the keys in the given namespace.
1095 With two args, list the keys in the given namespace.
1096
1096
1097 With five args, set a key to new if it currently is set to old.
1097 With five args, set a key to new if it currently is set to old.
1098 Reports success or failure.
1098 Reports success or failure.
1099 '''
1099 '''
1100
1100
1101 target = hg.repository(ui, repopath)
1101 target = hg.repository(ui, repopath)
1102 if keyinfo:
1102 if keyinfo:
1103 key, old, new = keyinfo
1103 key, old, new = keyinfo
1104 r = target.pushkey(namespace, key, old, new)
1104 r = target.pushkey(namespace, key, old, new)
1105 ui.status(str(r) + '\n')
1105 ui.status(str(r) + '\n')
1106 return not(r)
1106 return not(r)
1107 else:
1107 else:
1108 for k, v in target.listkeys(namespace).iteritems():
1108 for k, v in target.listkeys(namespace).iteritems():
1109 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1109 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1110 v.encode('string-escape')))
1110 v.encode('string-escape')))
1111
1111
1112 def debugrevspec(ui, repo, expr):
1112 def debugrevspec(ui, repo, expr):
1113 '''parse and apply a revision specification'''
1113 '''parse and apply a revision specification'''
1114 if ui.verbose:
1114 if ui.verbose:
1115 tree = revset.parse(expr)
1115 tree = revset.parse(expr)
1116 ui.note(tree, "\n")
1116 ui.note(tree, "\n")
1117 func = revset.match(expr)
1117 func = revset.match(expr)
1118 for c in func(repo, range(len(repo))):
1118 for c in func(repo, range(len(repo))):
1119 ui.write("%s\n" % c)
1119 ui.write("%s\n" % c)
1120
1120
1121 def debugsetparents(ui, repo, rev1, rev2=None):
1121 def debugsetparents(ui, repo, rev1, rev2=None):
1122 """manually set the parents of the current working directory
1122 """manually set the parents of the current working directory
1123
1123
1124 This is useful for writing repository conversion tools, but should
1124 This is useful for writing repository conversion tools, but should
1125 be used with care.
1125 be used with care.
1126
1126
1127 Returns 0 on success.
1127 Returns 0 on success.
1128 """
1128 """
1129
1129
1130 if not rev2:
1130 if not rev2:
1131 rev2 = hex(nullid)
1131 rev2 = hex(nullid)
1132
1132
1133 wlock = repo.wlock()
1133 wlock = repo.wlock()
1134 try:
1134 try:
1135 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1135 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1136 finally:
1136 finally:
1137 wlock.release()
1137 wlock.release()
1138
1138
1139 def debugstate(ui, repo, nodates=None):
1139 def debugstate(ui, repo, nodates=None):
1140 """show the contents of the current dirstate"""
1140 """show the contents of the current dirstate"""
1141 timestr = ""
1141 timestr = ""
1142 showdate = not nodates
1142 showdate = not nodates
1143 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1143 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1144 if showdate:
1144 if showdate:
1145 if ent[3] == -1:
1145 if ent[3] == -1:
1146 # Pad or slice to locale representation
1146 # Pad or slice to locale representation
1147 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1147 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1148 time.localtime(0)))
1148 time.localtime(0)))
1149 timestr = 'unset'
1149 timestr = 'unset'
1150 timestr = (timestr[:locale_len] +
1150 timestr = (timestr[:locale_len] +
1151 ' ' * (locale_len - len(timestr)))
1151 ' ' * (locale_len - len(timestr)))
1152 else:
1152 else:
1153 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1153 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1154 time.localtime(ent[3]))
1154 time.localtime(ent[3]))
1155 if ent[1] & 020000:
1155 if ent[1] & 020000:
1156 mode = 'lnk'
1156 mode = 'lnk'
1157 else:
1157 else:
1158 mode = '%3o' % (ent[1] & 0777)
1158 mode = '%3o' % (ent[1] & 0777)
1159 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1159 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1160 for f in repo.dirstate.copies():
1160 for f in repo.dirstate.copies():
1161 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1161 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1162
1162
1163 def debugsub(ui, repo, rev=None):
1163 def debugsub(ui, repo, rev=None):
1164 if rev == '':
1164 if rev == '':
1165 rev = None
1165 rev = None
1166 for k, v in sorted(repo[rev].substate.items()):
1166 for k, v in sorted(repo[rev].substate.items()):
1167 ui.write('path %s\n' % k)
1167 ui.write('path %s\n' % k)
1168 ui.write(' source %s\n' % v[0])
1168 ui.write(' source %s\n' % v[0])
1169 ui.write(' revision %s\n' % v[1])
1169 ui.write(' revision %s\n' % v[1])
1170
1170
1171 def debugdag(ui, repo, file_=None, *revs, **opts):
1171 def debugdag(ui, repo, file_=None, *revs, **opts):
1172 """format the changelog or an index DAG as a concise textual description
1172 """format the changelog or an index DAG as a concise textual description
1173
1173
1174 If you pass a revlog index, the revlog's DAG is emitted. If you list
1174 If you pass a revlog index, the revlog's DAG is emitted. If you list
1175 revision numbers, they get labelled in the output as rN.
1175 revision numbers, they get labelled in the output as rN.
1176
1176
1177 Otherwise, the changelog DAG of the current repo is emitted.
1177 Otherwise, the changelog DAG of the current repo is emitted.
1178 """
1178 """
1179 spaces = opts.get('spaces')
1179 spaces = opts.get('spaces')
1180 dots = opts.get('dots')
1180 dots = opts.get('dots')
1181 if file_:
1181 if file_:
1182 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1182 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1183 revs = set((int(r) for r in revs))
1183 revs = set((int(r) for r in revs))
1184 def events():
1184 def events():
1185 for r in rlog:
1185 for r in rlog:
1186 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1186 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1187 if r in revs:
1187 if r in revs:
1188 yield 'l', (r, "r%i" % r)
1188 yield 'l', (r, "r%i" % r)
1189 elif repo:
1189 elif repo:
1190 cl = repo.changelog
1190 cl = repo.changelog
1191 tags = opts.get('tags')
1191 tags = opts.get('tags')
1192 branches = opts.get('branches')
1192 branches = opts.get('branches')
1193 if tags:
1193 if tags:
1194 labels = {}
1194 labels = {}
1195 for l, n in repo.tags().items():
1195 for l, n in repo.tags().items():
1196 labels.setdefault(cl.rev(n), []).append(l)
1196 labels.setdefault(cl.rev(n), []).append(l)
1197 def events():
1197 def events():
1198 b = "default"
1198 b = "default"
1199 for r in cl:
1199 for r in cl:
1200 if branches:
1200 if branches:
1201 newb = cl.read(cl.node(r))[5]['branch']
1201 newb = cl.read(cl.node(r))[5]['branch']
1202 if newb != b:
1202 if newb != b:
1203 yield 'a', newb
1203 yield 'a', newb
1204 b = newb
1204 b = newb
1205 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1205 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1206 if tags:
1206 if tags:
1207 ls = labels.get(r)
1207 ls = labels.get(r)
1208 if ls:
1208 if ls:
1209 for l in ls:
1209 for l in ls:
1210 yield 'l', (r, l)
1210 yield 'l', (r, l)
1211 else:
1211 else:
1212 raise util.Abort(_('need repo for changelog dag'))
1212 raise util.Abort(_('need repo for changelog dag'))
1213
1213
1214 for line in dagparser.dagtextlines(events(),
1214 for line in dagparser.dagtextlines(events(),
1215 addspaces=spaces,
1215 addspaces=spaces,
1216 wraplabels=True,
1216 wraplabels=True,
1217 wrapannotations=True,
1217 wrapannotations=True,
1218 wrapnonlinear=dots,
1218 wrapnonlinear=dots,
1219 usedots=dots,
1219 usedots=dots,
1220 maxlinewidth=70):
1220 maxlinewidth=70):
1221 ui.write(line)
1221 ui.write(line)
1222 ui.write("\n")
1222 ui.write("\n")
1223
1223
1224 def debugdata(ui, file_, rev):
1224 def debugdata(ui, file_, rev):
1225 """dump the contents of a data file revision"""
1225 """dump the contents of a data file revision"""
1226 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1226 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1227 try:
1227 try:
1228 ui.write(r.revision(r.lookup(rev)))
1228 ui.write(r.revision(r.lookup(rev)))
1229 except KeyError:
1229 except KeyError:
1230 raise util.Abort(_('invalid revision identifier %s') % rev)
1230 raise util.Abort(_('invalid revision identifier %s') % rev)
1231
1231
1232 def debugdate(ui, date, range=None, **opts):
1232 def debugdate(ui, date, range=None, **opts):
1233 """parse and display a date"""
1233 """parse and display a date"""
1234 if opts["extended"]:
1234 if opts["extended"]:
1235 d = util.parsedate(date, util.extendeddateformats)
1235 d = util.parsedate(date, util.extendeddateformats)
1236 else:
1236 else:
1237 d = util.parsedate(date)
1237 d = util.parsedate(date)
1238 ui.write("internal: %s %s\n" % d)
1238 ui.write("internal: %s %s\n" % d)
1239 ui.write("standard: %s\n" % util.datestr(d))
1239 ui.write("standard: %s\n" % util.datestr(d))
1240 if range:
1240 if range:
1241 m = util.matchdate(range)
1241 m = util.matchdate(range)
1242 ui.write("match: %s\n" % m(d[0]))
1242 ui.write("match: %s\n" % m(d[0]))
1243
1243
1244 def debugindex(ui, file_):
1244 def debugindex(ui, file_):
1245 """dump the contents of an index file"""
1245 """dump the contents of an index file"""
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1247 ui.write(" rev offset length base linkrev"
1247 ui.write(" rev offset length base linkrev"
1248 " nodeid p1 p2\n")
1248 " nodeid p1 p2\n")
1249 for i in r:
1249 for i in r:
1250 node = r.node(i)
1250 node = r.node(i)
1251 try:
1251 try:
1252 pp = r.parents(node)
1252 pp = r.parents(node)
1253 except:
1253 except:
1254 pp = [nullid, nullid]
1254 pp = [nullid, nullid]
1255 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1255 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1256 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1256 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1257 short(node), short(pp[0]), short(pp[1])))
1257 short(node), short(pp[0]), short(pp[1])))
1258
1258
1259 def debugindexdot(ui, file_):
1259 def debugindexdot(ui, file_):
1260 """dump an index DAG as a graphviz dot file"""
1260 """dump an index DAG as a graphviz dot file"""
1261 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1261 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1262 ui.write("digraph G {\n")
1262 ui.write("digraph G {\n")
1263 for i in r:
1263 for i in r:
1264 node = r.node(i)
1264 node = r.node(i)
1265 pp = r.parents(node)
1265 pp = r.parents(node)
1266 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1266 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1267 if pp[1] != nullid:
1267 if pp[1] != nullid:
1268 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1268 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1269 ui.write("}\n")
1269 ui.write("}\n")
1270
1270
1271 def debuginstall(ui):
1271 def debuginstall(ui):
1272 '''test Mercurial installation
1272 '''test Mercurial installation
1273
1273
1274 Returns 0 on success.
1274 Returns 0 on success.
1275 '''
1275 '''
1276
1276
1277 def writetemp(contents):
1277 def writetemp(contents):
1278 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1278 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1279 f = os.fdopen(fd, "wb")
1279 f = os.fdopen(fd, "wb")
1280 f.write(contents)
1280 f.write(contents)
1281 f.close()
1281 f.close()
1282 return name
1282 return name
1283
1283
1284 problems = 0
1284 problems = 0
1285
1285
1286 # encoding
1286 # encoding
1287 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1287 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1288 try:
1288 try:
1289 encoding.fromlocal("test")
1289 encoding.fromlocal("test")
1290 except util.Abort, inst:
1290 except util.Abort, inst:
1291 ui.write(" %s\n" % inst)
1291 ui.write(" %s\n" % inst)
1292 ui.write(_(" (check that your locale is properly set)\n"))
1292 ui.write(_(" (check that your locale is properly set)\n"))
1293 problems += 1
1293 problems += 1
1294
1294
1295 # compiled modules
1295 # compiled modules
1296 ui.status(_("Checking extensions...\n"))
1296 ui.status(_("Checking extensions...\n"))
1297 try:
1297 try:
1298 import bdiff, mpatch, base85
1298 import bdiff, mpatch, base85
1299 except Exception, inst:
1299 except Exception, inst:
1300 ui.write(" %s\n" % inst)
1300 ui.write(" %s\n" % inst)
1301 ui.write(_(" One or more extensions could not be found"))
1301 ui.write(_(" One or more extensions could not be found"))
1302 ui.write(_(" (check that you compiled the extensions)\n"))
1302 ui.write(_(" (check that you compiled the extensions)\n"))
1303 problems += 1
1303 problems += 1
1304
1304
1305 # templates
1305 # templates
1306 ui.status(_("Checking templates...\n"))
1306 ui.status(_("Checking templates...\n"))
1307 try:
1307 try:
1308 import templater
1308 import templater
1309 templater.templater(templater.templatepath("map-cmdline.default"))
1309 templater.templater(templater.templatepath("map-cmdline.default"))
1310 except Exception, inst:
1310 except Exception, inst:
1311 ui.write(" %s\n" % inst)
1311 ui.write(" %s\n" % inst)
1312 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1312 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1313 problems += 1
1313 problems += 1
1314
1314
1315 # patch
1315 # patch
1316 ui.status(_("Checking patch...\n"))
1316 ui.status(_("Checking patch...\n"))
1317 patchproblems = 0
1317 patchproblems = 0
1318 a = "1\n2\n3\n4\n"
1318 a = "1\n2\n3\n4\n"
1319 b = "1\n2\n3\ninsert\n4\n"
1319 b = "1\n2\n3\ninsert\n4\n"
1320 fa = writetemp(a)
1320 fa = writetemp(a)
1321 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1321 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1322 os.path.basename(fa))
1322 os.path.basename(fa))
1323 fd = writetemp(d)
1323 fd = writetemp(d)
1324
1324
1325 files = {}
1325 files = {}
1326 try:
1326 try:
1327 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1327 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1328 except util.Abort, e:
1328 except util.Abort, e:
1329 ui.write(_(" patch call failed:\n"))
1329 ui.write(_(" patch call failed:\n"))
1330 ui.write(" " + str(e) + "\n")
1330 ui.write(" " + str(e) + "\n")
1331 patchproblems += 1
1331 patchproblems += 1
1332 else:
1332 else:
1333 if list(files) != [os.path.basename(fa)]:
1333 if list(files) != [os.path.basename(fa)]:
1334 ui.write(_(" unexpected patch output!\n"))
1334 ui.write(_(" unexpected patch output!\n"))
1335 patchproblems += 1
1335 patchproblems += 1
1336 a = open(fa).read()
1336 a = open(fa).read()
1337 if a != b:
1337 if a != b:
1338 ui.write(_(" patch test failed!\n"))
1338 ui.write(_(" patch test failed!\n"))
1339 patchproblems += 1
1339 patchproblems += 1
1340
1340
1341 if patchproblems:
1341 if patchproblems:
1342 if ui.config('ui', 'patch'):
1342 if ui.config('ui', 'patch'):
1343 ui.write(_(" (Current patch tool may be incompatible with patch,"
1343 ui.write(_(" (Current patch tool may be incompatible with patch,"
1344 " or misconfigured. Please check your .hgrc file)\n"))
1344 " or misconfigured. Please check your .hgrc file)\n"))
1345 else:
1345 else:
1346 ui.write(_(" Internal patcher failure, please report this error"
1346 ui.write(_(" Internal patcher failure, please report this error"
1347 " to http://mercurial.selenic.com/bts/\n"))
1347 " to http://mercurial.selenic.com/bts/\n"))
1348 problems += patchproblems
1348 problems += patchproblems
1349
1349
1350 os.unlink(fa)
1350 os.unlink(fa)
1351 os.unlink(fd)
1351 os.unlink(fd)
1352
1352
1353 # editor
1353 # editor
1354 ui.status(_("Checking commit editor...\n"))
1354 ui.status(_("Checking commit editor...\n"))
1355 editor = ui.geteditor()
1355 editor = ui.geteditor()
1356 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1356 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1357 if not cmdpath:
1357 if not cmdpath:
1358 if editor == 'vi':
1358 if editor == 'vi':
1359 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1359 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1360 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1360 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1361 else:
1361 else:
1362 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1362 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1363 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1363 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1364 problems += 1
1364 problems += 1
1365
1365
1366 # check username
1366 # check username
1367 ui.status(_("Checking username...\n"))
1367 ui.status(_("Checking username...\n"))
1368 try:
1368 try:
1369 user = ui.username()
1369 user = ui.username()
1370 except util.Abort, e:
1370 except util.Abort, e:
1371 ui.write(" %s\n" % e)
1371 ui.write(" %s\n" % e)
1372 ui.write(_(" (specify a username in your .hgrc file)\n"))
1372 ui.write(_(" (specify a username in your .hgrc file)\n"))
1373 problems += 1
1373 problems += 1
1374
1374
1375 if not problems:
1375 if not problems:
1376 ui.status(_("No problems detected\n"))
1376 ui.status(_("No problems detected\n"))
1377 else:
1377 else:
1378 ui.write(_("%s problems detected,"
1378 ui.write(_("%s problems detected,"
1379 " please check your install!\n") % problems)
1379 " please check your install!\n") % problems)
1380
1380
1381 return problems
1381 return problems
1382
1382
1383 def debugrename(ui, repo, file1, *pats, **opts):
1383 def debugrename(ui, repo, file1, *pats, **opts):
1384 """dump rename information"""
1384 """dump rename information"""
1385
1385
1386 ctx = repo[opts.get('rev')]
1386 ctx = repo[opts.get('rev')]
1387 m = cmdutil.match(repo, (file1,) + pats, opts)
1387 m = cmdutil.match(repo, (file1,) + pats, opts)
1388 for abs in ctx.walk(m):
1388 for abs in ctx.walk(m):
1389 fctx = ctx[abs]
1389 fctx = ctx[abs]
1390 o = fctx.filelog().renamed(fctx.filenode())
1390 o = fctx.filelog().renamed(fctx.filenode())
1391 rel = m.rel(abs)
1391 rel = m.rel(abs)
1392 if o:
1392 if o:
1393 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1393 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1394 else:
1394 else:
1395 ui.write(_("%s not renamed\n") % rel)
1395 ui.write(_("%s not renamed\n") % rel)
1396
1396
1397 def debugwalk(ui, repo, *pats, **opts):
1397 def debugwalk(ui, repo, *pats, **opts):
1398 """show how files match on given patterns"""
1398 """show how files match on given patterns"""
1399 m = cmdutil.match(repo, pats, opts)
1399 m = cmdutil.match(repo, pats, opts)
1400 items = list(repo.walk(m))
1400 items = list(repo.walk(m))
1401 if not items:
1401 if not items:
1402 return
1402 return
1403 fmt = 'f %%-%ds %%-%ds %%s' % (
1403 fmt = 'f %%-%ds %%-%ds %%s' % (
1404 max([len(abs) for abs in items]),
1404 max([len(abs) for abs in items]),
1405 max([len(m.rel(abs)) for abs in items]))
1405 max([len(m.rel(abs)) for abs in items]))
1406 for abs in items:
1406 for abs in items:
1407 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1407 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1408 ui.write("%s\n" % line.rstrip())
1408 ui.write("%s\n" % line.rstrip())
1409
1409
1410 def diff(ui, repo, *pats, **opts):
1410 def diff(ui, repo, *pats, **opts):
1411 """diff repository (or selected files)
1411 """diff repository (or selected files)
1412
1412
1413 Show differences between revisions for the specified files.
1413 Show differences between revisions for the specified files.
1414
1414
1415 Differences between files are shown using the unified diff format.
1415 Differences between files are shown using the unified diff format.
1416
1416
1417 NOTE: diff may generate unexpected results for merges, as it will
1417 NOTE: diff may generate unexpected results for merges, as it will
1418 default to comparing against the working directory's first parent
1418 default to comparing against the working directory's first parent
1419 changeset if no revisions are specified.
1419 changeset if no revisions are specified.
1420
1420
1421 When two revision arguments are given, then changes are shown
1421 When two revision arguments are given, then changes are shown
1422 between those revisions. If only one revision is specified then
1422 between those revisions. If only one revision is specified then
1423 that revision is compared to the working directory, and, when no
1423 that revision is compared to the working directory, and, when no
1424 revisions are specified, the working directory files are compared
1424 revisions are specified, the working directory files are compared
1425 to its parent.
1425 to its parent.
1426
1426
1427 Alternatively you can specify -c/--change with a revision to see
1427 Alternatively you can specify -c/--change with a revision to see
1428 the changes in that changeset relative to its first parent.
1428 the changes in that changeset relative to its first parent.
1429
1429
1430 Without the -a/--text option, diff will avoid generating diffs of
1430 Without the -a/--text option, diff will avoid generating diffs of
1431 files it detects as binary. With -a, diff will generate a diff
1431 files it detects as binary. With -a, diff will generate a diff
1432 anyway, probably with undesirable results.
1432 anyway, probably with undesirable results.
1433
1433
1434 Use the -g/--git option to generate diffs in the git extended diff
1434 Use the -g/--git option to generate diffs in the git extended diff
1435 format. For more information, read :hg:`help diffs`.
1435 format. For more information, read :hg:`help diffs`.
1436
1436
1437 Returns 0 on success.
1437 Returns 0 on success.
1438 """
1438 """
1439
1439
1440 revs = opts.get('rev')
1440 revs = opts.get('rev')
1441 change = opts.get('change')
1441 change = opts.get('change')
1442 stat = opts.get('stat')
1442 stat = opts.get('stat')
1443 reverse = opts.get('reverse')
1443 reverse = opts.get('reverse')
1444
1444
1445 if revs and change:
1445 if revs and change:
1446 msg = _('cannot specify --rev and --change at the same time')
1446 msg = _('cannot specify --rev and --change at the same time')
1447 raise util.Abort(msg)
1447 raise util.Abort(msg)
1448 elif change:
1448 elif change:
1449 node2 = repo.lookup(change)
1449 node2 = repo.lookup(change)
1450 node1 = repo[node2].parents()[0].node()
1450 node1 = repo[node2].parents()[0].node()
1451 else:
1451 else:
1452 node1, node2 = cmdutil.revpair(repo, revs)
1452 node1, node2 = cmdutil.revpair(repo, revs)
1453
1453
1454 if reverse:
1454 if reverse:
1455 node1, node2 = node2, node1
1455 node1, node2 = node2, node1
1456
1456
1457 diffopts = patch.diffopts(ui, opts)
1457 diffopts = patch.diffopts(ui, opts)
1458 m = cmdutil.match(repo, pats, opts)
1458 m = cmdutil.match(repo, pats, opts)
1459 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1459 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1460
1460
1461 def export(ui, repo, *changesets, **opts):
1461 def export(ui, repo, *changesets, **opts):
1462 """dump the header and diffs for one or more changesets
1462 """dump the header and diffs for one or more changesets
1463
1463
1464 Print the changeset header and diffs for one or more revisions.
1464 Print the changeset header and diffs for one or more revisions.
1465
1465
1466 The information shown in the changeset header is: author, date,
1466 The information shown in the changeset header is: author, date,
1467 branch name (if non-default), changeset hash, parent(s) and commit
1467 branch name (if non-default), changeset hash, parent(s) and commit
1468 comment.
1468 comment.
1469
1469
1470 NOTE: export may generate unexpected diff output for merge
1470 NOTE: export may generate unexpected diff output for merge
1471 changesets, as it will compare the merge changeset against its
1471 changesets, as it will compare the merge changeset against its
1472 first parent only.
1472 first parent only.
1473
1473
1474 Output may be to a file, in which case the name of the file is
1474 Output may be to a file, in which case the name of the file is
1475 given using a format string. The formatting rules are as follows:
1475 given using a format string. The formatting rules are as follows:
1476
1476
1477 :``%%``: literal "%" character
1477 :``%%``: literal "%" character
1478 :``%H``: changeset hash (40 bytes of hexadecimal)
1478 :``%H``: changeset hash (40 bytes of hexadecimal)
1479 :``%N``: number of patches being generated
1479 :``%N``: number of patches being generated
1480 :``%R``: changeset revision number
1480 :``%R``: changeset revision number
1481 :``%b``: basename of the exporting repository
1481 :``%b``: basename of the exporting repository
1482 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1482 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1483 :``%n``: zero-padded sequence number, starting at 1
1483 :``%n``: zero-padded sequence number, starting at 1
1484 :``%r``: zero-padded changeset revision number
1484 :``%r``: zero-padded changeset revision number
1485
1485
1486 Without the -a/--text option, export will avoid generating diffs
1486 Without the -a/--text option, export will avoid generating diffs
1487 of files it detects as binary. With -a, export will generate a
1487 of files it detects as binary. With -a, export will generate a
1488 diff anyway, probably with undesirable results.
1488 diff anyway, probably with undesirable results.
1489
1489
1490 Use the -g/--git option to generate diffs in the git extended diff
1490 Use the -g/--git option to generate diffs in the git extended diff
1491 format. See :hg:`help diffs` for more information.
1491 format. See :hg:`help diffs` for more information.
1492
1492
1493 With the --switch-parent option, the diff will be against the
1493 With the --switch-parent option, the diff will be against the
1494 second parent. It can be useful to review a merge.
1494 second parent. It can be useful to review a merge.
1495
1495
1496 Returns 0 on success.
1496 Returns 0 on success.
1497 """
1497 """
1498 changesets += tuple(opts.get('rev', []))
1498 changesets += tuple(opts.get('rev', []))
1499 if not changesets:
1499 if not changesets:
1500 raise util.Abort(_("export requires at least one changeset"))
1500 raise util.Abort(_("export requires at least one changeset"))
1501 revs = cmdutil.revrange(repo, changesets)
1501 revs = cmdutil.revrange(repo, changesets)
1502 if len(revs) > 1:
1502 if len(revs) > 1:
1503 ui.note(_('exporting patches:\n'))
1503 ui.note(_('exporting patches:\n'))
1504 else:
1504 else:
1505 ui.note(_('exporting patch:\n'))
1505 ui.note(_('exporting patch:\n'))
1506 cmdutil.export(repo, revs, template=opts.get('output'),
1506 cmdutil.export(repo, revs, template=opts.get('output'),
1507 switch_parent=opts.get('switch_parent'),
1507 switch_parent=opts.get('switch_parent'),
1508 opts=patch.diffopts(ui, opts))
1508 opts=patch.diffopts(ui, opts))
1509
1509
1510 def forget(ui, repo, *pats, **opts):
1510 def forget(ui, repo, *pats, **opts):
1511 """forget the specified files on the next commit
1511 """forget the specified files on the next commit
1512
1512
1513 Mark the specified files so they will no longer be tracked
1513 Mark the specified files so they will no longer be tracked
1514 after the next commit.
1514 after the next commit.
1515
1515
1516 This only removes files from the current branch, not from the
1516 This only removes files from the current branch, not from the
1517 entire project history, and it does not delete them from the
1517 entire project history, and it does not delete them from the
1518 working directory.
1518 working directory.
1519
1519
1520 To undo a forget before the next commit, see :hg:`add`.
1520 To undo a forget before the next commit, see :hg:`add`.
1521
1521
1522 Returns 0 on success.
1522 Returns 0 on success.
1523 """
1523 """
1524
1524
1525 if not pats:
1525 if not pats:
1526 raise util.Abort(_('no files specified'))
1526 raise util.Abort(_('no files specified'))
1527
1527
1528 m = cmdutil.match(repo, pats, opts)
1528 m = cmdutil.match(repo, pats, opts)
1529 s = repo.status(match=m, clean=True)
1529 s = repo.status(match=m, clean=True)
1530 forget = sorted(s[0] + s[1] + s[3] + s[6])
1530 forget = sorted(s[0] + s[1] + s[3] + s[6])
1531 errs = 0
1531 errs = 0
1532
1532
1533 for f in m.files():
1533 for f in m.files():
1534 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1534 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1535 ui.warn(_('not removing %s: file is already untracked\n')
1535 ui.warn(_('not removing %s: file is already untracked\n')
1536 % m.rel(f))
1536 % m.rel(f))
1537 errs = 1
1537 errs = 1
1538
1538
1539 for f in forget:
1539 for f in forget:
1540 if ui.verbose or not m.exact(f):
1540 if ui.verbose or not m.exact(f):
1541 ui.status(_('removing %s\n') % m.rel(f))
1541 ui.status(_('removing %s\n') % m.rel(f))
1542
1542
1543 repo[None].remove(forget, unlink=False)
1543 repo[None].remove(forget, unlink=False)
1544 return errs
1544 return errs
1545
1545
1546 def grep(ui, repo, pattern, *pats, **opts):
1546 def grep(ui, repo, pattern, *pats, **opts):
1547 """search for a pattern in specified files and revisions
1547 """search for a pattern in specified files and revisions
1548
1548
1549 Search revisions of files for a regular expression.
1549 Search revisions of files for a regular expression.
1550
1550
1551 This command behaves differently than Unix grep. It only accepts
1551 This command behaves differently than Unix grep. It only accepts
1552 Python/Perl regexps. It searches repository history, not the
1552 Python/Perl regexps. It searches repository history, not the
1553 working directory. It always prints the revision number in which a
1553 working directory. It always prints the revision number in which a
1554 match appears.
1554 match appears.
1555
1555
1556 By default, grep only prints output for the first revision of a
1556 By default, grep only prints output for the first revision of a
1557 file in which it finds a match. To get it to print every revision
1557 file in which it finds a match. To get it to print every revision
1558 that contains a change in match status ("-" for a match that
1558 that contains a change in match status ("-" for a match that
1559 becomes a non-match, or "+" for a non-match that becomes a match),
1559 becomes a non-match, or "+" for a non-match that becomes a match),
1560 use the --all flag.
1560 use the --all flag.
1561
1561
1562 Returns 0 if a match is found, 1 otherwise.
1562 Returns 0 if a match is found, 1 otherwise.
1563 """
1563 """
1564 reflags = 0
1564 reflags = 0
1565 if opts.get('ignore_case'):
1565 if opts.get('ignore_case'):
1566 reflags |= re.I
1566 reflags |= re.I
1567 try:
1567 try:
1568 regexp = re.compile(pattern, reflags)
1568 regexp = re.compile(pattern, reflags)
1569 except Exception, inst:
1569 except Exception, inst:
1570 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1570 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1571 return 1
1571 return 1
1572 sep, eol = ':', '\n'
1572 sep, eol = ':', '\n'
1573 if opts.get('print0'):
1573 if opts.get('print0'):
1574 sep = eol = '\0'
1574 sep = eol = '\0'
1575
1575
1576 getfile = util.lrucachefunc(repo.file)
1576 getfile = util.lrucachefunc(repo.file)
1577
1577
1578 def matchlines(body):
1578 def matchlines(body):
1579 begin = 0
1579 begin = 0
1580 linenum = 0
1580 linenum = 0
1581 while True:
1581 while True:
1582 match = regexp.search(body, begin)
1582 match = regexp.search(body, begin)
1583 if not match:
1583 if not match:
1584 break
1584 break
1585 mstart, mend = match.span()
1585 mstart, mend = match.span()
1586 linenum += body.count('\n', begin, mstart) + 1
1586 linenum += body.count('\n', begin, mstart) + 1
1587 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1587 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1588 begin = body.find('\n', mend) + 1 or len(body)
1588 begin = body.find('\n', mend) + 1 or len(body)
1589 lend = begin - 1
1589 lend = begin - 1
1590 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1590 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1591
1591
1592 class linestate(object):
1592 class linestate(object):
1593 def __init__(self, line, linenum, colstart, colend):
1593 def __init__(self, line, linenum, colstart, colend):
1594 self.line = line
1594 self.line = line
1595 self.linenum = linenum
1595 self.linenum = linenum
1596 self.colstart = colstart
1596 self.colstart = colstart
1597 self.colend = colend
1597 self.colend = colend
1598
1598
1599 def __hash__(self):
1599 def __hash__(self):
1600 return hash((self.linenum, self.line))
1600 return hash((self.linenum, self.line))
1601
1601
1602 def __eq__(self, other):
1602 def __eq__(self, other):
1603 return self.line == other.line
1603 return self.line == other.line
1604
1604
1605 matches = {}
1605 matches = {}
1606 copies = {}
1606 copies = {}
1607 def grepbody(fn, rev, body):
1607 def grepbody(fn, rev, body):
1608 matches[rev].setdefault(fn, [])
1608 matches[rev].setdefault(fn, [])
1609 m = matches[rev][fn]
1609 m = matches[rev][fn]
1610 for lnum, cstart, cend, line in matchlines(body):
1610 for lnum, cstart, cend, line in matchlines(body):
1611 s = linestate(line, lnum, cstart, cend)
1611 s = linestate(line, lnum, cstart, cend)
1612 m.append(s)
1612 m.append(s)
1613
1613
1614 def difflinestates(a, b):
1614 def difflinestates(a, b):
1615 sm = difflib.SequenceMatcher(None, a, b)
1615 sm = difflib.SequenceMatcher(None, a, b)
1616 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1616 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1617 if tag == 'insert':
1617 if tag == 'insert':
1618 for i in xrange(blo, bhi):
1618 for i in xrange(blo, bhi):
1619 yield ('+', b[i])
1619 yield ('+', b[i])
1620 elif tag == 'delete':
1620 elif tag == 'delete':
1621 for i in xrange(alo, ahi):
1621 for i in xrange(alo, ahi):
1622 yield ('-', a[i])
1622 yield ('-', a[i])
1623 elif tag == 'replace':
1623 elif tag == 'replace':
1624 for i in xrange(alo, ahi):
1624 for i in xrange(alo, ahi):
1625 yield ('-', a[i])
1625 yield ('-', a[i])
1626 for i in xrange(blo, bhi):
1626 for i in xrange(blo, bhi):
1627 yield ('+', b[i])
1627 yield ('+', b[i])
1628
1628
1629 def display(fn, ctx, pstates, states):
1629 def display(fn, ctx, pstates, states):
1630 rev = ctx.rev()
1630 rev = ctx.rev()
1631 datefunc = ui.quiet and util.shortdate or util.datestr
1631 datefunc = ui.quiet and util.shortdate or util.datestr
1632 found = False
1632 found = False
1633 filerevmatches = {}
1633 filerevmatches = {}
1634 if opts.get('all'):
1634 if opts.get('all'):
1635 iter = difflinestates(pstates, states)
1635 iter = difflinestates(pstates, states)
1636 else:
1636 else:
1637 iter = [('', l) for l in states]
1637 iter = [('', l) for l in states]
1638 for change, l in iter:
1638 for change, l in iter:
1639 cols = [fn, str(rev)]
1639 cols = [fn, str(rev)]
1640 before, match, after = None, None, None
1640 before, match, after = None, None, None
1641 if opts.get('line_number'):
1641 if opts.get('line_number'):
1642 cols.append(str(l.linenum))
1642 cols.append(str(l.linenum))
1643 if opts.get('all'):
1643 if opts.get('all'):
1644 cols.append(change)
1644 cols.append(change)
1645 if opts.get('user'):
1645 if opts.get('user'):
1646 cols.append(ui.shortuser(ctx.user()))
1646 cols.append(ui.shortuser(ctx.user()))
1647 if opts.get('date'):
1647 if opts.get('date'):
1648 cols.append(datefunc(ctx.date()))
1648 cols.append(datefunc(ctx.date()))
1649 if opts.get('files_with_matches'):
1649 if opts.get('files_with_matches'):
1650 c = (fn, rev)
1650 c = (fn, rev)
1651 if c in filerevmatches:
1651 if c in filerevmatches:
1652 continue
1652 continue
1653 filerevmatches[c] = 1
1653 filerevmatches[c] = 1
1654 else:
1654 else:
1655 before = l.line[:l.colstart]
1655 before = l.line[:l.colstart]
1656 match = l.line[l.colstart:l.colend]
1656 match = l.line[l.colstart:l.colend]
1657 after = l.line[l.colend:]
1657 after = l.line[l.colend:]
1658 ui.write(sep.join(cols))
1658 ui.write(sep.join(cols))
1659 if before is not None:
1659 if before is not None:
1660 ui.write(sep + before)
1660 ui.write(sep + before)
1661 ui.write(match, label='grep.match')
1661 ui.write(match, label='grep.match')
1662 ui.write(after)
1662 ui.write(after)
1663 ui.write(eol)
1663 ui.write(eol)
1664 found = True
1664 found = True
1665 return found
1665 return found
1666
1666
1667 skip = {}
1667 skip = {}
1668 revfiles = {}
1668 revfiles = {}
1669 matchfn = cmdutil.match(repo, pats, opts)
1669 matchfn = cmdutil.match(repo, pats, opts)
1670 found = False
1670 found = False
1671 follow = opts.get('follow')
1671 follow = opts.get('follow')
1672
1672
1673 def prep(ctx, fns):
1673 def prep(ctx, fns):
1674 rev = ctx.rev()
1674 rev = ctx.rev()
1675 pctx = ctx.parents()[0]
1675 pctx = ctx.parents()[0]
1676 parent = pctx.rev()
1676 parent = pctx.rev()
1677 matches.setdefault(rev, {})
1677 matches.setdefault(rev, {})
1678 matches.setdefault(parent, {})
1678 matches.setdefault(parent, {})
1679 files = revfiles.setdefault(rev, [])
1679 files = revfiles.setdefault(rev, [])
1680 for fn in fns:
1680 for fn in fns:
1681 flog = getfile(fn)
1681 flog = getfile(fn)
1682 try:
1682 try:
1683 fnode = ctx.filenode(fn)
1683 fnode = ctx.filenode(fn)
1684 except error.LookupError:
1684 except error.LookupError:
1685 continue
1685 continue
1686
1686
1687 copied = flog.renamed(fnode)
1687 copied = flog.renamed(fnode)
1688 copy = follow and copied and copied[0]
1688 copy = follow and copied and copied[0]
1689 if copy:
1689 if copy:
1690 copies.setdefault(rev, {})[fn] = copy
1690 copies.setdefault(rev, {})[fn] = copy
1691 if fn in skip:
1691 if fn in skip:
1692 if copy:
1692 if copy:
1693 skip[copy] = True
1693 skip[copy] = True
1694 continue
1694 continue
1695 files.append(fn)
1695 files.append(fn)
1696
1696
1697 if fn not in matches[rev]:
1697 if fn not in matches[rev]:
1698 grepbody(fn, rev, flog.read(fnode))
1698 grepbody(fn, rev, flog.read(fnode))
1699
1699
1700 pfn = copy or fn
1700 pfn = copy or fn
1701 if pfn not in matches[parent]:
1701 if pfn not in matches[parent]:
1702 try:
1702 try:
1703 fnode = pctx.filenode(pfn)
1703 fnode = pctx.filenode(pfn)
1704 grepbody(pfn, parent, flog.read(fnode))
1704 grepbody(pfn, parent, flog.read(fnode))
1705 except error.LookupError:
1705 except error.LookupError:
1706 pass
1706 pass
1707
1707
1708 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1708 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1709 rev = ctx.rev()
1709 rev = ctx.rev()
1710 parent = ctx.parents()[0].rev()
1710 parent = ctx.parents()[0].rev()
1711 for fn in sorted(revfiles.get(rev, [])):
1711 for fn in sorted(revfiles.get(rev, [])):
1712 states = matches[rev][fn]
1712 states = matches[rev][fn]
1713 copy = copies.get(rev, {}).get(fn)
1713 copy = copies.get(rev, {}).get(fn)
1714 if fn in skip:
1714 if fn in skip:
1715 if copy:
1715 if copy:
1716 skip[copy] = True
1716 skip[copy] = True
1717 continue
1717 continue
1718 pstates = matches.get(parent, {}).get(copy or fn, [])
1718 pstates = matches.get(parent, {}).get(copy or fn, [])
1719 if pstates or states:
1719 if pstates or states:
1720 r = display(fn, ctx, pstates, states)
1720 r = display(fn, ctx, pstates, states)
1721 found = found or r
1721 found = found or r
1722 if r and not opts.get('all'):
1722 if r and not opts.get('all'):
1723 skip[fn] = True
1723 skip[fn] = True
1724 if copy:
1724 if copy:
1725 skip[copy] = True
1725 skip[copy] = True
1726 del matches[rev]
1726 del matches[rev]
1727 del revfiles[rev]
1727 del revfiles[rev]
1728
1728
1729 return not found
1729 return not found
1730
1730
1731 def heads(ui, repo, *branchrevs, **opts):
1731 def heads(ui, repo, *branchrevs, **opts):
1732 """show current repository heads or show branch heads
1732 """show current repository heads or show branch heads
1733
1733
1734 With no arguments, show all repository branch heads.
1734 With no arguments, show all repository branch heads.
1735
1735
1736 Repository "heads" are changesets with no child changesets. They are
1736 Repository "heads" are changesets with no child changesets. They are
1737 where development generally takes place and are the usual targets
1737 where development generally takes place and are the usual targets
1738 for update and merge operations. Branch heads are changesets that have
1738 for update and merge operations. Branch heads are changesets that have
1739 no child changeset on the same branch.
1739 no child changeset on the same branch.
1740
1740
1741 If one or more REVs are given, only branch heads on the branches
1741 If one or more REVs are given, only branch heads on the branches
1742 associated with the specified changesets are shown.
1742 associated with the specified changesets are shown.
1743
1743
1744 If -c/--closed is specified, also show branch heads marked closed
1744 If -c/--closed is specified, also show branch heads marked closed
1745 (see :hg:`commit --close-branch`).
1745 (see :hg:`commit --close-branch`).
1746
1746
1747 If STARTREV is specified, only those heads that are descendants of
1747 If STARTREV is specified, only those heads that are descendants of
1748 STARTREV will be displayed.
1748 STARTREV will be displayed.
1749
1749
1750 If -t/--topo is specified, named branch mechanics will be ignored and only
1750 If -t/--topo is specified, named branch mechanics will be ignored and only
1751 changesets without children will be shown.
1751 changesets without children will be shown.
1752
1752
1753 Returns 0 if matching heads are found, 1 if not.
1753 Returns 0 if matching heads are found, 1 if not.
1754 """
1754 """
1755
1755
1756 if opts.get('rev'):
1756 if opts.get('rev'):
1757 start = repo.lookup(opts['rev'])
1757 start = repo.lookup(opts['rev'])
1758 else:
1758 else:
1759 start = None
1759 start = None
1760
1760
1761 if opts.get('topo'):
1761 if opts.get('topo'):
1762 heads = [repo[h] for h in repo.heads(start)]
1762 heads = [repo[h] for h in repo.heads(start)]
1763 else:
1763 else:
1764 heads = []
1764 heads = []
1765 for b, ls in repo.branchmap().iteritems():
1765 for b, ls in repo.branchmap().iteritems():
1766 if start is None:
1766 if start is None:
1767 heads += [repo[h] for h in ls]
1767 heads += [repo[h] for h in ls]
1768 continue
1768 continue
1769 startrev = repo.changelog.rev(start)
1769 startrev = repo.changelog.rev(start)
1770 descendants = set(repo.changelog.descendants(startrev))
1770 descendants = set(repo.changelog.descendants(startrev))
1771 descendants.add(startrev)
1771 descendants.add(startrev)
1772 rev = repo.changelog.rev
1772 rev = repo.changelog.rev
1773 heads += [repo[h] for h in ls if rev(h) in descendants]
1773 heads += [repo[h] for h in ls if rev(h) in descendants]
1774
1774
1775 if branchrevs:
1775 if branchrevs:
1776 decode, encode = encoding.fromlocal, encoding.tolocal
1776 decode, encode = encoding.fromlocal, encoding.tolocal
1777 branches = set(repo[decode(br)].branch() for br in branchrevs)
1777 branches = set(repo[decode(br)].branch() for br in branchrevs)
1778 heads = [h for h in heads if h.branch() in branches]
1778 heads = [h for h in heads if h.branch() in branches]
1779
1779
1780 if not opts.get('closed'):
1780 if not opts.get('closed'):
1781 heads = [h for h in heads if not h.extra().get('close')]
1781 heads = [h for h in heads if not h.extra().get('close')]
1782
1782
1783 if opts.get('active') and branchrevs:
1783 if opts.get('active') and branchrevs:
1784 dagheads = repo.heads(start)
1784 dagheads = repo.heads(start)
1785 heads = [h for h in heads if h.node() in dagheads]
1785 heads = [h for h in heads if h.node() in dagheads]
1786
1786
1787 if branchrevs:
1787 if branchrevs:
1788 haveheads = set(h.branch() for h in heads)
1788 haveheads = set(h.branch() for h in heads)
1789 if branches - haveheads:
1789 if branches - haveheads:
1790 headless = ', '.join(encode(b) for b in branches - haveheads)
1790 headless = ', '.join(encode(b) for b in branches - haveheads)
1791 msg = _('no open branch heads found on branches %s')
1791 msg = _('no open branch heads found on branches %s')
1792 if opts.get('rev'):
1792 if opts.get('rev'):
1793 msg += _(' (started at %s)' % opts['rev'])
1793 msg += _(' (started at %s)' % opts['rev'])
1794 ui.warn((msg + '\n') % headless)
1794 ui.warn((msg + '\n') % headless)
1795
1795
1796 if not heads:
1796 if not heads:
1797 return 1
1797 return 1
1798
1798
1799 heads = sorted(heads, key=lambda x: -x.rev())
1799 heads = sorted(heads, key=lambda x: -x.rev())
1800 displayer = cmdutil.show_changeset(ui, repo, opts)
1800 displayer = cmdutil.show_changeset(ui, repo, opts)
1801 for ctx in heads:
1801 for ctx in heads:
1802 displayer.show(ctx)
1802 displayer.show(ctx)
1803 displayer.close()
1803 displayer.close()
1804
1804
1805 def help_(ui, name=None, with_version=False, unknowncmd=False):
1805 def help_(ui, name=None, with_version=False, unknowncmd=False):
1806 """show help for a given topic or a help overview
1806 """show help for a given topic or a help overview
1807
1807
1808 With no arguments, print a list of commands with short help messages.
1808 With no arguments, print a list of commands with short help messages.
1809
1809
1810 Given a topic, extension, or command name, print help for that
1810 Given a topic, extension, or command name, print help for that
1811 topic.
1811 topic.
1812
1812
1813 Returns 0 if successful.
1813 Returns 0 if successful.
1814 """
1814 """
1815 option_lists = []
1815 option_lists = []
1816 textwidth = util.termwidth() - 2
1816 textwidth = util.termwidth() - 2
1817
1817
1818 def addglobalopts(aliases):
1818 def addglobalopts(aliases):
1819 if ui.verbose:
1819 if ui.verbose:
1820 option_lists.append((_("global options:"), globalopts))
1820 option_lists.append((_("global options:"), globalopts))
1821 if name == 'shortlist':
1821 if name == 'shortlist':
1822 option_lists.append((_('use "hg help" for the full list '
1822 option_lists.append((_('use "hg help" for the full list '
1823 'of commands'), ()))
1823 'of commands'), ()))
1824 else:
1824 else:
1825 if name == 'shortlist':
1825 if name == 'shortlist':
1826 msg = _('use "hg help" for the full list of commands '
1826 msg = _('use "hg help" for the full list of commands '
1827 'or "hg -v" for details')
1827 'or "hg -v" for details')
1828 elif aliases:
1828 elif aliases:
1829 msg = _('use "hg -v help%s" to show aliases and '
1829 msg = _('use "hg -v help%s" to show aliases and '
1830 'global options') % (name and " " + name or "")
1830 'global options') % (name and " " + name or "")
1831 else:
1831 else:
1832 msg = _('use "hg -v help %s" to show global options') % name
1832 msg = _('use "hg -v help %s" to show global options') % name
1833 option_lists.append((msg, ()))
1833 option_lists.append((msg, ()))
1834
1834
1835 def helpcmd(name):
1835 def helpcmd(name):
1836 if with_version:
1836 if with_version:
1837 version_(ui)
1837 version_(ui)
1838 ui.write('\n')
1838 ui.write('\n')
1839
1839
1840 try:
1840 try:
1841 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1841 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1842 except error.AmbiguousCommand, inst:
1842 except error.AmbiguousCommand, inst:
1843 # py3k fix: except vars can't be used outside the scope of the
1843 # py3k fix: except vars can't be used outside the scope of the
1844 # except block, nor can be used inside a lambda. python issue4617
1844 # except block, nor can be used inside a lambda. python issue4617
1845 prefix = inst.args[0]
1845 prefix = inst.args[0]
1846 select = lambda c: c.lstrip('^').startswith(prefix)
1846 select = lambda c: c.lstrip('^').startswith(prefix)
1847 helplist(_('list of commands:\n\n'), select)
1847 helplist(_('list of commands:\n\n'), select)
1848 return
1848 return
1849
1849
1850 # check if it's an invalid alias and display its error if it is
1850 # check if it's an invalid alias and display its error if it is
1851 if getattr(entry[0], 'badalias', False):
1851 if getattr(entry[0], 'badalias', False):
1852 if not unknowncmd:
1852 if not unknowncmd:
1853 entry[0](ui)
1853 entry[0](ui)
1854 return
1854 return
1855
1855
1856 # synopsis
1856 # synopsis
1857 if len(entry) > 2:
1857 if len(entry) > 2:
1858 if entry[2].startswith('hg'):
1858 if entry[2].startswith('hg'):
1859 ui.write("%s\n" % entry[2])
1859 ui.write("%s\n" % entry[2])
1860 else:
1860 else:
1861 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1861 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1862 else:
1862 else:
1863 ui.write('hg %s\n' % aliases[0])
1863 ui.write('hg %s\n' % aliases[0])
1864
1864
1865 # aliases
1865 # aliases
1866 if not ui.quiet and len(aliases) > 1:
1866 if not ui.quiet and len(aliases) > 1:
1867 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1867 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1868
1868
1869 # description
1869 # description
1870 doc = gettext(entry[0].__doc__)
1870 doc = gettext(entry[0].__doc__)
1871 if not doc:
1871 if not doc:
1872 doc = _("(no help text available)")
1872 doc = _("(no help text available)")
1873 if hasattr(entry[0], 'definition'): # aliased command
1873 if hasattr(entry[0], 'definition'): # aliased command
1874 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1874 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1875 if ui.quiet:
1875 if ui.quiet:
1876 doc = doc.splitlines()[0]
1876 doc = doc.splitlines()[0]
1877 keep = ui.verbose and ['verbose'] or []
1877 keep = ui.verbose and ['verbose'] or []
1878 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1878 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1879 ui.write("\n%s\n" % formatted)
1879 ui.write("\n%s\n" % formatted)
1880 if pruned:
1880 if pruned:
1881 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1881 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1882
1882
1883 if not ui.quiet:
1883 if not ui.quiet:
1884 # options
1884 # options
1885 if entry[1]:
1885 if entry[1]:
1886 option_lists.append((_("options:\n"), entry[1]))
1886 option_lists.append((_("options:\n"), entry[1]))
1887
1887
1888 addglobalopts(False)
1888 addglobalopts(False)
1889
1889
1890 def helplist(header, select=None):
1890 def helplist(header, select=None):
1891 h = {}
1891 h = {}
1892 cmds = {}
1892 cmds = {}
1893 for c, e in table.iteritems():
1893 for c, e in table.iteritems():
1894 f = c.split("|", 1)[0]
1894 f = c.split("|", 1)[0]
1895 if select and not select(f):
1895 if select and not select(f):
1896 continue
1896 continue
1897 if (not select and name != 'shortlist' and
1897 if (not select and name != 'shortlist' and
1898 e[0].__module__ != __name__):
1898 e[0].__module__ != __name__):
1899 continue
1899 continue
1900 if name == "shortlist" and not f.startswith("^"):
1900 if name == "shortlist" and not f.startswith("^"):
1901 continue
1901 continue
1902 f = f.lstrip("^")
1902 f = f.lstrip("^")
1903 if not ui.debugflag and f.startswith("debug"):
1903 if not ui.debugflag and f.startswith("debug"):
1904 continue
1904 continue
1905 doc = e[0].__doc__
1905 doc = e[0].__doc__
1906 if doc and 'DEPRECATED' in doc and not ui.verbose:
1906 if doc and 'DEPRECATED' in doc and not ui.verbose:
1907 continue
1907 continue
1908 doc = gettext(doc)
1908 doc = gettext(doc)
1909 if not doc:
1909 if not doc:
1910 doc = _("(no help text available)")
1910 doc = _("(no help text available)")
1911 h[f] = doc.splitlines()[0].rstrip()
1911 h[f] = doc.splitlines()[0].rstrip()
1912 cmds[f] = c.lstrip("^")
1912 cmds[f] = c.lstrip("^")
1913
1913
1914 if not h:
1914 if not h:
1915 ui.status(_('no commands defined\n'))
1915 ui.status(_('no commands defined\n'))
1916 return
1916 return
1917
1917
1918 ui.status(header)
1918 ui.status(header)
1919 fns = sorted(h)
1919 fns = sorted(h)
1920 m = max(map(len, fns))
1920 m = max(map(len, fns))
1921 for f in fns:
1921 for f in fns:
1922 if ui.verbose:
1922 if ui.verbose:
1923 commands = cmds[f].replace("|",", ")
1923 commands = cmds[f].replace("|",", ")
1924 ui.write(" %s:\n %s\n"%(commands, h[f]))
1924 ui.write(" %s:\n %s\n"%(commands, h[f]))
1925 else:
1925 else:
1926 ui.write('%s\n' % (util.wrap(h[f],
1926 ui.write('%s\n' % (util.wrap(h[f],
1927 initindent=' %-*s ' % (m, f),
1927 initindent=' %-*s ' % (m, f),
1928 hangindent=' ' * (m + 4))))
1928 hangindent=' ' * (m + 4))))
1929
1929
1930 if not ui.quiet:
1930 if not ui.quiet:
1931 addglobalopts(True)
1931 addglobalopts(True)
1932
1932
1933 def helptopic(name):
1933 def helptopic(name):
1934 for names, header, doc in help.helptable:
1934 for names, header, doc in help.helptable:
1935 if name in names:
1935 if name in names:
1936 break
1936 break
1937 else:
1937 else:
1938 raise error.UnknownCommand(name)
1938 raise error.UnknownCommand(name)
1939
1939
1940 # description
1940 # description
1941 if not doc:
1941 if not doc:
1942 doc = _("(no help text available)")
1942 doc = _("(no help text available)")
1943 if hasattr(doc, '__call__'):
1943 if hasattr(doc, '__call__'):
1944 doc = doc()
1944 doc = doc()
1945
1945
1946 ui.write("%s\n\n" % header)
1946 ui.write("%s\n\n" % header)
1947 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1947 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1948
1948
1949 def helpext(name):
1949 def helpext(name):
1950 try:
1950 try:
1951 mod = extensions.find(name)
1951 mod = extensions.find(name)
1952 doc = gettext(mod.__doc__) or _('no help text available')
1952 doc = gettext(mod.__doc__) or _('no help text available')
1953 except KeyError:
1953 except KeyError:
1954 mod = None
1954 mod = None
1955 doc = extensions.disabledext(name)
1955 doc = extensions.disabledext(name)
1956 if not doc:
1956 if not doc:
1957 raise error.UnknownCommand(name)
1957 raise error.UnknownCommand(name)
1958
1958
1959 if '\n' not in doc:
1959 if '\n' not in doc:
1960 head, tail = doc, ""
1960 head, tail = doc, ""
1961 else:
1961 else:
1962 head, tail = doc.split('\n', 1)
1962 head, tail = doc.split('\n', 1)
1963 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1963 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1964 if tail:
1964 if tail:
1965 ui.write(minirst.format(tail, textwidth))
1965 ui.write(minirst.format(tail, textwidth))
1966 ui.status('\n\n')
1966 ui.status('\n\n')
1967
1967
1968 if mod:
1968 if mod:
1969 try:
1969 try:
1970 ct = mod.cmdtable
1970 ct = mod.cmdtable
1971 except AttributeError:
1971 except AttributeError:
1972 ct = {}
1972 ct = {}
1973 modcmds = set([c.split('|', 1)[0] for c in ct])
1973 modcmds = set([c.split('|', 1)[0] for c in ct])
1974 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1974 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1975 else:
1975 else:
1976 ui.write(_('use "hg help extensions" for information on enabling '
1976 ui.write(_('use "hg help extensions" for information on enabling '
1977 'extensions\n'))
1977 'extensions\n'))
1978
1978
1979 def helpextcmd(name):
1979 def helpextcmd(name):
1980 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1980 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1981 doc = gettext(mod.__doc__).splitlines()[0]
1981 doc = gettext(mod.__doc__).splitlines()[0]
1982
1982
1983 msg = help.listexts(_("'%s' is provided by the following "
1983 msg = help.listexts(_("'%s' is provided by the following "
1984 "extension:") % cmd, {ext: doc}, len(ext),
1984 "extension:") % cmd, {ext: doc}, len(ext),
1985 indent=4)
1985 indent=4)
1986 ui.write(minirst.format(msg, textwidth))
1986 ui.write(minirst.format(msg, textwidth))
1987 ui.write('\n\n')
1987 ui.write('\n\n')
1988 ui.write(_('use "hg help extensions" for information on enabling '
1988 ui.write(_('use "hg help extensions" for information on enabling '
1989 'extensions\n'))
1989 'extensions\n'))
1990
1990
1991 if name and name != 'shortlist':
1991 if name and name != 'shortlist':
1992 i = None
1992 i = None
1993 if unknowncmd:
1993 if unknowncmd:
1994 queries = (helpextcmd,)
1994 queries = (helpextcmd,)
1995 else:
1995 else:
1996 queries = (helptopic, helpcmd, helpext, helpextcmd)
1996 queries = (helptopic, helpcmd, helpext, helpextcmd)
1997 for f in queries:
1997 for f in queries:
1998 try:
1998 try:
1999 f(name)
1999 f(name)
2000 i = None
2000 i = None
2001 break
2001 break
2002 except error.UnknownCommand, inst:
2002 except error.UnknownCommand, inst:
2003 i = inst
2003 i = inst
2004 if i:
2004 if i:
2005 raise i
2005 raise i
2006
2006
2007 else:
2007 else:
2008 # program name
2008 # program name
2009 if ui.verbose or with_version:
2009 if ui.verbose or with_version:
2010 version_(ui)
2010 version_(ui)
2011 else:
2011 else:
2012 ui.status(_("Mercurial Distributed SCM\n"))
2012 ui.status(_("Mercurial Distributed SCM\n"))
2013 ui.status('\n')
2013 ui.status('\n')
2014
2014
2015 # list of commands
2015 # list of commands
2016 if name == "shortlist":
2016 if name == "shortlist":
2017 header = _('basic commands:\n\n')
2017 header = _('basic commands:\n\n')
2018 else:
2018 else:
2019 header = _('list of commands:\n\n')
2019 header = _('list of commands:\n\n')
2020
2020
2021 helplist(header)
2021 helplist(header)
2022 if name != 'shortlist':
2022 if name != 'shortlist':
2023 exts, maxlength = extensions.enabled()
2023 exts, maxlength = extensions.enabled()
2024 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2024 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2025 if text:
2025 if text:
2026 ui.write("\n%s\n" % minirst.format(text, textwidth))
2026 ui.write("\n%s\n" % minirst.format(text, textwidth))
2027
2027
2028 # list all option lists
2028 # list all option lists
2029 opt_output = []
2029 opt_output = []
2030 multioccur = False
2030 multioccur = False
2031 for title, options in option_lists:
2031 for title, options in option_lists:
2032 opt_output.append(("\n%s" % title, None))
2032 opt_output.append(("\n%s" % title, None))
2033 for option in options:
2033 for option in options:
2034 if len(option) == 5:
2034 if len(option) == 5:
2035 shortopt, longopt, default, desc, optlabel = option
2035 shortopt, longopt, default, desc, optlabel = option
2036 else:
2036 else:
2037 shortopt, longopt, default, desc = option
2037 shortopt, longopt, default, desc = option
2038 optlabel = _("VALUE") # default label
2038 optlabel = _("VALUE") # default label
2039
2039
2040 if _("DEPRECATED") in desc and not ui.verbose:
2040 if _("DEPRECATED") in desc and not ui.verbose:
2041 continue
2041 continue
2042 if isinstance(default, list):
2042 if isinstance(default, list):
2043 numqualifier = " %s [+]" % optlabel
2043 numqualifier = " %s [+]" % optlabel
2044 multioccur = True
2044 multioccur = True
2045 elif (default is not None) and not isinstance(default, bool):
2045 elif (default is not None) and not isinstance(default, bool):
2046 numqualifier = " %s" % optlabel
2046 numqualifier = " %s" % optlabel
2047 else:
2047 else:
2048 numqualifier = ""
2048 numqualifier = ""
2049 opt_output.append(("%2s%s" %
2049 opt_output.append(("%2s%s" %
2050 (shortopt and "-%s" % shortopt,
2050 (shortopt and "-%s" % shortopt,
2051 longopt and " --%s%s" %
2051 longopt and " --%s%s" %
2052 (longopt, numqualifier)),
2052 (longopt, numqualifier)),
2053 "%s%s" % (desc,
2053 "%s%s" % (desc,
2054 default
2054 default
2055 and _(" (default: %s)") % default
2055 and _(" (default: %s)") % default
2056 or "")))
2056 or "")))
2057 if multioccur:
2057 if multioccur:
2058 msg = _("\n[+] marked option can be specified multiple times")
2058 msg = _("\n[+] marked option can be specified multiple times")
2059 if ui.verbose and name != 'shortlist':
2059 if ui.verbose and name != 'shortlist':
2060 opt_output.append((msg, None))
2060 opt_output.append((msg, None))
2061 else:
2061 else:
2062 opt_output.insert(-1, (msg, None))
2062 opt_output.insert(-1, (msg, None))
2063
2063
2064 if not name:
2064 if not name:
2065 ui.write(_("\nadditional help topics:\n\n"))
2065 ui.write(_("\nadditional help topics:\n\n"))
2066 topics = []
2066 topics = []
2067 for names, header, doc in help.helptable:
2067 for names, header, doc in help.helptable:
2068 topics.append((sorted(names, key=len, reverse=True)[0], header))
2068 topics.append((sorted(names, key=len, reverse=True)[0], header))
2069 topics_len = max([len(s[0]) for s in topics])
2069 topics_len = max([len(s[0]) for s in topics])
2070 for t, desc in topics:
2070 for t, desc in topics:
2071 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2071 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2072
2072
2073 if opt_output:
2073 if opt_output:
2074 colwidth = encoding.colwidth
2074 colwidth = encoding.colwidth
2075 # normalize: (opt or message, desc or None, width of opt)
2075 # normalize: (opt or message, desc or None, width of opt)
2076 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2076 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2077 for opt, desc in opt_output]
2077 for opt, desc in opt_output]
2078 hanging = max([e[2] for e in entries])
2078 hanging = max([e[2] for e in entries])
2079 for opt, desc, width in entries:
2079 for opt, desc, width in entries:
2080 if desc:
2080 if desc:
2081 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2081 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2082 hangindent = ' ' * (hanging + 3)
2082 hangindent = ' ' * (hanging + 3)
2083 ui.write('%s\n' % (util.wrap(desc,
2083 ui.write('%s\n' % (util.wrap(desc,
2084 initindent=initindent,
2084 initindent=initindent,
2085 hangindent=hangindent)))
2085 hangindent=hangindent)))
2086 else:
2086 else:
2087 ui.write("%s\n" % opt)
2087 ui.write("%s\n" % opt)
2088
2088
2089 def identify(ui, repo, source=None,
2089 def identify(ui, repo, source=None,
2090 rev=None, num=None, id=None, branch=None, tags=None):
2090 rev=None, num=None, id=None, branch=None, tags=None):
2091 """identify the working copy or specified revision
2091 """identify the working copy or specified revision
2092
2092
2093 With no revision, print a summary of the current state of the
2093 With no revision, print a summary of the current state of the
2094 repository.
2094 repository.
2095
2095
2096 Specifying a path to a repository root or Mercurial bundle will
2096 Specifying a path to a repository root or Mercurial bundle will
2097 cause lookup to operate on that repository/bundle.
2097 cause lookup to operate on that repository/bundle.
2098
2098
2099 This summary identifies the repository state using one or two
2099 This summary identifies the repository state using one or two
2100 parent hash identifiers, followed by a "+" if there are
2100 parent hash identifiers, followed by a "+" if there are
2101 uncommitted changes in the working directory, a list of tags for
2101 uncommitted changes in the working directory, a list of tags for
2102 this revision and a branch name for non-default branches.
2102 this revision and a branch name for non-default branches.
2103
2103
2104 Returns 0 if successful.
2104 Returns 0 if successful.
2105 """
2105 """
2106
2106
2107 if not repo and not source:
2107 if not repo and not source:
2108 raise util.Abort(_("There is no Mercurial repository here "
2108 raise util.Abort(_("there is no Mercurial repository here "
2109 "(.hg not found)"))
2109 "(.hg not found)"))
2110
2110
2111 hexfunc = ui.debugflag and hex or short
2111 hexfunc = ui.debugflag and hex or short
2112 default = not (num or id or branch or tags)
2112 default = not (num or id or branch or tags)
2113 output = []
2113 output = []
2114
2114
2115 revs = []
2115 revs = []
2116 if source:
2116 if source:
2117 source, branches = hg.parseurl(ui.expandpath(source))
2117 source, branches = hg.parseurl(ui.expandpath(source))
2118 repo = hg.repository(ui, source)
2118 repo = hg.repository(ui, source)
2119 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2119 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2120
2120
2121 if not repo.local():
2121 if not repo.local():
2122 if not rev and revs:
2122 if not rev and revs:
2123 rev = revs[0]
2123 rev = revs[0]
2124 if not rev:
2124 if not rev:
2125 rev = "tip"
2125 rev = "tip"
2126 if num or branch or tags:
2126 if num or branch or tags:
2127 raise util.Abort(
2127 raise util.Abort(
2128 "can't query remote revision number, branch, or tags")
2128 "can't query remote revision number, branch, or tags")
2129 output = [hexfunc(repo.lookup(rev))]
2129 output = [hexfunc(repo.lookup(rev))]
2130 elif not rev:
2130 elif not rev:
2131 ctx = repo[None]
2131 ctx = repo[None]
2132 parents = ctx.parents()
2132 parents = ctx.parents()
2133 changed = False
2133 changed = False
2134 if default or id or num:
2134 if default or id or num:
2135 changed = util.any(repo.status())
2135 changed = util.any(repo.status())
2136 if default or id:
2136 if default or id:
2137 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2137 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2138 (changed) and "+" or "")]
2138 (changed) and "+" or "")]
2139 if num:
2139 if num:
2140 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2140 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2141 (changed) and "+" or ""))
2141 (changed) and "+" or ""))
2142 else:
2142 else:
2143 ctx = repo[rev]
2143 ctx = repo[rev]
2144 if default or id:
2144 if default or id:
2145 output = [hexfunc(ctx.node())]
2145 output = [hexfunc(ctx.node())]
2146 if num:
2146 if num:
2147 output.append(str(ctx.rev()))
2147 output.append(str(ctx.rev()))
2148
2148
2149 if repo.local() and default and not ui.quiet:
2149 if repo.local() and default and not ui.quiet:
2150 b = encoding.tolocal(ctx.branch())
2150 b = encoding.tolocal(ctx.branch())
2151 if b != 'default':
2151 if b != 'default':
2152 output.append("(%s)" % b)
2152 output.append("(%s)" % b)
2153
2153
2154 # multiple tags for a single parent separated by '/'
2154 # multiple tags for a single parent separated by '/'
2155 t = "/".join(ctx.tags())
2155 t = "/".join(ctx.tags())
2156 if t:
2156 if t:
2157 output.append(t)
2157 output.append(t)
2158
2158
2159 if branch:
2159 if branch:
2160 output.append(encoding.tolocal(ctx.branch()))
2160 output.append(encoding.tolocal(ctx.branch()))
2161
2161
2162 if tags:
2162 if tags:
2163 output.extend(ctx.tags())
2163 output.extend(ctx.tags())
2164
2164
2165 ui.write("%s\n" % ' '.join(output))
2165 ui.write("%s\n" % ' '.join(output))
2166
2166
2167 def import_(ui, repo, patch1, *patches, **opts):
2167 def import_(ui, repo, patch1, *patches, **opts):
2168 """import an ordered set of patches
2168 """import an ordered set of patches
2169
2169
2170 Import a list of patches and commit them individually (unless
2170 Import a list of patches and commit them individually (unless
2171 --no-commit is specified).
2171 --no-commit is specified).
2172
2172
2173 If there are outstanding changes in the working directory, import
2173 If there are outstanding changes in the working directory, import
2174 will abort unless given the -f/--force flag.
2174 will abort unless given the -f/--force flag.
2175
2175
2176 You can import a patch straight from a mail message. Even patches
2176 You can import a patch straight from a mail message. Even patches
2177 as attachments work (to use the body part, it must have type
2177 as attachments work (to use the body part, it must have type
2178 text/plain or text/x-patch). From and Subject headers of email
2178 text/plain or text/x-patch). From and Subject headers of email
2179 message are used as default committer and commit message. All
2179 message are used as default committer and commit message. All
2180 text/plain body parts before first diff are added to commit
2180 text/plain body parts before first diff are added to commit
2181 message.
2181 message.
2182
2182
2183 If the imported patch was generated by :hg:`export`, user and
2183 If the imported patch was generated by :hg:`export`, user and
2184 description from patch override values from message headers and
2184 description from patch override values from message headers and
2185 body. Values given on command line with -m/--message and -u/--user
2185 body. Values given on command line with -m/--message and -u/--user
2186 override these.
2186 override these.
2187
2187
2188 If --exact is specified, import will set the working directory to
2188 If --exact is specified, import will set the working directory to
2189 the parent of each patch before applying it, and will abort if the
2189 the parent of each patch before applying it, and will abort if the
2190 resulting changeset has a different ID than the one recorded in
2190 resulting changeset has a different ID than the one recorded in
2191 the patch. This may happen due to character set problems or other
2191 the patch. This may happen due to character set problems or other
2192 deficiencies in the text patch format.
2192 deficiencies in the text patch format.
2193
2193
2194 With -s/--similarity, hg will attempt to discover renames and
2194 With -s/--similarity, hg will attempt to discover renames and
2195 copies in the patch in the same way as 'addremove'.
2195 copies in the patch in the same way as 'addremove'.
2196
2196
2197 To read a patch from standard input, use "-" as the patch name. If
2197 To read a patch from standard input, use "-" as the patch name. If
2198 a URL is specified, the patch will be downloaded from it.
2198 a URL is specified, the patch will be downloaded from it.
2199 See :hg:`help dates` for a list of formats valid for -d/--date.
2199 See :hg:`help dates` for a list of formats valid for -d/--date.
2200
2200
2201 Returns 0 on success.
2201 Returns 0 on success.
2202 """
2202 """
2203 patches = (patch1,) + patches
2203 patches = (patch1,) + patches
2204
2204
2205 date = opts.get('date')
2205 date = opts.get('date')
2206 if date:
2206 if date:
2207 opts['date'] = util.parsedate(date)
2207 opts['date'] = util.parsedate(date)
2208
2208
2209 try:
2209 try:
2210 sim = float(opts.get('similarity') or 0)
2210 sim = float(opts.get('similarity') or 0)
2211 except ValueError:
2211 except ValueError:
2212 raise util.Abort(_('similarity must be a number'))
2212 raise util.Abort(_('similarity must be a number'))
2213 if sim < 0 or sim > 100:
2213 if sim < 0 or sim > 100:
2214 raise util.Abort(_('similarity must be between 0 and 100'))
2214 raise util.Abort(_('similarity must be between 0 and 100'))
2215
2215
2216 if opts.get('exact') or not opts.get('force'):
2216 if opts.get('exact') or not opts.get('force'):
2217 cmdutil.bail_if_changed(repo)
2217 cmdutil.bail_if_changed(repo)
2218
2218
2219 d = opts["base"]
2219 d = opts["base"]
2220 strip = opts["strip"]
2220 strip = opts["strip"]
2221 wlock = lock = None
2221 wlock = lock = None
2222
2222
2223 def tryone(ui, hunk):
2223 def tryone(ui, hunk):
2224 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2224 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2225 patch.extract(ui, hunk)
2225 patch.extract(ui, hunk)
2226
2226
2227 if not tmpname:
2227 if not tmpname:
2228 return None
2228 return None
2229 commitid = _('to working directory')
2229 commitid = _('to working directory')
2230
2230
2231 try:
2231 try:
2232 cmdline_message = cmdutil.logmessage(opts)
2232 cmdline_message = cmdutil.logmessage(opts)
2233 if cmdline_message:
2233 if cmdline_message:
2234 # pickup the cmdline msg
2234 # pickup the cmdline msg
2235 message = cmdline_message
2235 message = cmdline_message
2236 elif message:
2236 elif message:
2237 # pickup the patch msg
2237 # pickup the patch msg
2238 message = message.strip()
2238 message = message.strip()
2239 else:
2239 else:
2240 # launch the editor
2240 # launch the editor
2241 message = None
2241 message = None
2242 ui.debug('message:\n%s\n' % message)
2242 ui.debug('message:\n%s\n' % message)
2243
2243
2244 wp = repo.parents()
2244 wp = repo.parents()
2245 if opts.get('exact'):
2245 if opts.get('exact'):
2246 if not nodeid or not p1:
2246 if not nodeid or not p1:
2247 raise util.Abort(_('not a Mercurial patch'))
2247 raise util.Abort(_('not a Mercurial patch'))
2248 p1 = repo.lookup(p1)
2248 p1 = repo.lookup(p1)
2249 p2 = repo.lookup(p2 or hex(nullid))
2249 p2 = repo.lookup(p2 or hex(nullid))
2250
2250
2251 if p1 != wp[0].node():
2251 if p1 != wp[0].node():
2252 hg.clean(repo, p1)
2252 hg.clean(repo, p1)
2253 repo.dirstate.setparents(p1, p2)
2253 repo.dirstate.setparents(p1, p2)
2254 elif p2:
2254 elif p2:
2255 try:
2255 try:
2256 p1 = repo.lookup(p1)
2256 p1 = repo.lookup(p1)
2257 p2 = repo.lookup(p2)
2257 p2 = repo.lookup(p2)
2258 if p1 == wp[0].node():
2258 if p1 == wp[0].node():
2259 repo.dirstate.setparents(p1, p2)
2259 repo.dirstate.setparents(p1, p2)
2260 except error.RepoError:
2260 except error.RepoError:
2261 pass
2261 pass
2262 if opts.get('exact') or opts.get('import_branch'):
2262 if opts.get('exact') or opts.get('import_branch'):
2263 repo.dirstate.setbranch(branch or 'default')
2263 repo.dirstate.setbranch(branch or 'default')
2264
2264
2265 files = {}
2265 files = {}
2266 try:
2266 try:
2267 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2267 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2268 files=files, eolmode=None)
2268 files=files, eolmode=None)
2269 finally:
2269 finally:
2270 files = patch.updatedir(ui, repo, files,
2270 files = patch.updatedir(ui, repo, files,
2271 similarity=sim / 100.0)
2271 similarity=sim / 100.0)
2272 if not opts.get('no_commit'):
2272 if not opts.get('no_commit'):
2273 if opts.get('exact'):
2273 if opts.get('exact'):
2274 m = None
2274 m = None
2275 else:
2275 else:
2276 m = cmdutil.matchfiles(repo, files or [])
2276 m = cmdutil.matchfiles(repo, files or [])
2277 n = repo.commit(message, opts.get('user') or user,
2277 n = repo.commit(message, opts.get('user') or user,
2278 opts.get('date') or date, match=m,
2278 opts.get('date') or date, match=m,
2279 editor=cmdutil.commiteditor)
2279 editor=cmdutil.commiteditor)
2280 if opts.get('exact'):
2280 if opts.get('exact'):
2281 if hex(n) != nodeid:
2281 if hex(n) != nodeid:
2282 repo.rollback()
2282 repo.rollback()
2283 raise util.Abort(_('patch is damaged'
2283 raise util.Abort(_('patch is damaged'
2284 ' or loses information'))
2284 ' or loses information'))
2285 # Force a dirstate write so that the next transaction
2285 # Force a dirstate write so that the next transaction
2286 # backups an up-do-date file.
2286 # backups an up-do-date file.
2287 repo.dirstate.write()
2287 repo.dirstate.write()
2288 if n:
2288 if n:
2289 commitid = short(n)
2289 commitid = short(n)
2290
2290
2291 return commitid
2291 return commitid
2292 finally:
2292 finally:
2293 os.unlink(tmpname)
2293 os.unlink(tmpname)
2294
2294
2295 try:
2295 try:
2296 wlock = repo.wlock()
2296 wlock = repo.wlock()
2297 lock = repo.lock()
2297 lock = repo.lock()
2298 lastcommit = None
2298 lastcommit = None
2299 for p in patches:
2299 for p in patches:
2300 pf = os.path.join(d, p)
2300 pf = os.path.join(d, p)
2301
2301
2302 if pf == '-':
2302 if pf == '-':
2303 ui.status(_("applying patch from stdin\n"))
2303 ui.status(_("applying patch from stdin\n"))
2304 pf = sys.stdin
2304 pf = sys.stdin
2305 else:
2305 else:
2306 ui.status(_("applying %s\n") % p)
2306 ui.status(_("applying %s\n") % p)
2307 pf = url.open(ui, pf)
2307 pf = url.open(ui, pf)
2308
2308
2309 haspatch = False
2309 haspatch = False
2310 for hunk in patch.split(pf):
2310 for hunk in patch.split(pf):
2311 commitid = tryone(ui, hunk)
2311 commitid = tryone(ui, hunk)
2312 if commitid:
2312 if commitid:
2313 haspatch = True
2313 haspatch = True
2314 if lastcommit:
2314 if lastcommit:
2315 ui.status(_('applied %s\n') % lastcommit)
2315 ui.status(_('applied %s\n') % lastcommit)
2316 lastcommit = commitid
2316 lastcommit = commitid
2317
2317
2318 if not haspatch:
2318 if not haspatch:
2319 raise util.Abort(_('no diffs found'))
2319 raise util.Abort(_('no diffs found'))
2320
2320
2321 finally:
2321 finally:
2322 release(lock, wlock)
2322 release(lock, wlock)
2323
2323
2324 def incoming(ui, repo, source="default", **opts):
2324 def incoming(ui, repo, source="default", **opts):
2325 """show new changesets found in source
2325 """show new changesets found in source
2326
2326
2327 Show new changesets found in the specified path/URL or the default
2327 Show new changesets found in the specified path/URL or the default
2328 pull location. These are the changesets that would have been pulled
2328 pull location. These are the changesets that would have been pulled
2329 if a pull at the time you issued this command.
2329 if a pull at the time you issued this command.
2330
2330
2331 For remote repository, using --bundle avoids downloading the
2331 For remote repository, using --bundle avoids downloading the
2332 changesets twice if the incoming is followed by a pull.
2332 changesets twice if the incoming is followed by a pull.
2333
2333
2334 See pull for valid source format details.
2334 See pull for valid source format details.
2335
2335
2336 Returns 0 if there are incoming changes, 1 otherwise.
2336 Returns 0 if there are incoming changes, 1 otherwise.
2337 """
2337 """
2338 limit = cmdutil.loglimit(opts)
2338 limit = cmdutil.loglimit(opts)
2339 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2339 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2340 other = hg.repository(hg.remoteui(repo, opts), source)
2340 other = hg.repository(hg.remoteui(repo, opts), source)
2341 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2341 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2342 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2342 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2343 if revs:
2343 if revs:
2344 revs = [other.lookup(rev) for rev in revs]
2344 revs = [other.lookup(rev) for rev in revs]
2345
2345
2346 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2346 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2347 force=opts.get('force'))
2347 force=opts.get('force'))
2348 common, incoming, rheads = tmp
2348 common, incoming, rheads = tmp
2349 if not incoming:
2349 if not incoming:
2350 try:
2350 try:
2351 os.unlink(opts["bundle"])
2351 os.unlink(opts["bundle"])
2352 except:
2352 except:
2353 pass
2353 pass
2354 ui.status(_("no changes found\n"))
2354 ui.status(_("no changes found\n"))
2355 return 1
2355 return 1
2356
2356
2357 cleanup = None
2357 cleanup = None
2358 try:
2358 try:
2359 fname = opts["bundle"]
2359 fname = opts["bundle"]
2360 if fname or not other.local():
2360 if fname or not other.local():
2361 # create a bundle (uncompressed if other repo is not local)
2361 # create a bundle (uncompressed if other repo is not local)
2362
2362
2363 if revs is None and other.capable('changegroupsubset'):
2363 if revs is None and other.capable('changegroupsubset'):
2364 revs = rheads
2364 revs = rheads
2365
2365
2366 if revs is None:
2366 if revs is None:
2367 cg = other.changegroup(incoming, "incoming")
2367 cg = other.changegroup(incoming, "incoming")
2368 else:
2368 else:
2369 cg = other.changegroupsubset(incoming, revs, 'incoming')
2369 cg = other.changegroupsubset(incoming, revs, 'incoming')
2370 bundletype = other.local() and "HG10BZ" or "HG10UN"
2370 bundletype = other.local() and "HG10BZ" or "HG10UN"
2371 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2371 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2372 # keep written bundle?
2372 # keep written bundle?
2373 if opts["bundle"]:
2373 if opts["bundle"]:
2374 cleanup = None
2374 cleanup = None
2375 if not other.local():
2375 if not other.local():
2376 # use the created uncompressed bundlerepo
2376 # use the created uncompressed bundlerepo
2377 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2377 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2378
2378
2379 o = other.changelog.nodesbetween(incoming, revs)[0]
2379 o = other.changelog.nodesbetween(incoming, revs)[0]
2380 if opts.get('newest_first'):
2380 if opts.get('newest_first'):
2381 o.reverse()
2381 o.reverse()
2382 displayer = cmdutil.show_changeset(ui, other, opts)
2382 displayer = cmdutil.show_changeset(ui, other, opts)
2383 count = 0
2383 count = 0
2384 for n in o:
2384 for n in o:
2385 if limit is not None and count >= limit:
2385 if limit is not None and count >= limit:
2386 break
2386 break
2387 parents = [p for p in other.changelog.parents(n) if p != nullid]
2387 parents = [p for p in other.changelog.parents(n) if p != nullid]
2388 if opts.get('no_merges') and len(parents) == 2:
2388 if opts.get('no_merges') and len(parents) == 2:
2389 continue
2389 continue
2390 count += 1
2390 count += 1
2391 displayer.show(other[n])
2391 displayer.show(other[n])
2392 displayer.close()
2392 displayer.close()
2393 finally:
2393 finally:
2394 if hasattr(other, 'close'):
2394 if hasattr(other, 'close'):
2395 other.close()
2395 other.close()
2396 if cleanup:
2396 if cleanup:
2397 os.unlink(cleanup)
2397 os.unlink(cleanup)
2398
2398
2399 def init(ui, dest=".", **opts):
2399 def init(ui, dest=".", **opts):
2400 """create a new repository in the given directory
2400 """create a new repository in the given directory
2401
2401
2402 Initialize a new repository in the given directory. If the given
2402 Initialize a new repository in the given directory. If the given
2403 directory does not exist, it will be created.
2403 directory does not exist, it will be created.
2404
2404
2405 If no directory is given, the current directory is used.
2405 If no directory is given, the current directory is used.
2406
2406
2407 It is possible to specify an ``ssh://`` URL as the destination.
2407 It is possible to specify an ``ssh://`` URL as the destination.
2408 See :hg:`help urls` for more information.
2408 See :hg:`help urls` for more information.
2409
2409
2410 Returns 0 on success.
2410 Returns 0 on success.
2411 """
2411 """
2412 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2412 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2413
2413
2414 def locate(ui, repo, *pats, **opts):
2414 def locate(ui, repo, *pats, **opts):
2415 """locate files matching specific patterns
2415 """locate files matching specific patterns
2416
2416
2417 Print files under Mercurial control in the working directory whose
2417 Print files under Mercurial control in the working directory whose
2418 names match the given patterns.
2418 names match the given patterns.
2419
2419
2420 By default, this command searches all directories in the working
2420 By default, this command searches all directories in the working
2421 directory. To search just the current directory and its
2421 directory. To search just the current directory and its
2422 subdirectories, use "--include .".
2422 subdirectories, use "--include .".
2423
2423
2424 If no patterns are given to match, this command prints the names
2424 If no patterns are given to match, this command prints the names
2425 of all files under Mercurial control in the working directory.
2425 of all files under Mercurial control in the working directory.
2426
2426
2427 If you want to feed the output of this command into the "xargs"
2427 If you want to feed the output of this command into the "xargs"
2428 command, use the -0 option to both this command and "xargs". This
2428 command, use the -0 option to both this command and "xargs". This
2429 will avoid the problem of "xargs" treating single filenames that
2429 will avoid the problem of "xargs" treating single filenames that
2430 contain whitespace as multiple filenames.
2430 contain whitespace as multiple filenames.
2431
2431
2432 Returns 0 if a match is found, 1 otherwise.
2432 Returns 0 if a match is found, 1 otherwise.
2433 """
2433 """
2434 end = opts.get('print0') and '\0' or '\n'
2434 end = opts.get('print0') and '\0' or '\n'
2435 rev = opts.get('rev') or None
2435 rev = opts.get('rev') or None
2436
2436
2437 ret = 1
2437 ret = 1
2438 m = cmdutil.match(repo, pats, opts, default='relglob')
2438 m = cmdutil.match(repo, pats, opts, default='relglob')
2439 m.bad = lambda x, y: False
2439 m.bad = lambda x, y: False
2440 for abs in repo[rev].walk(m):
2440 for abs in repo[rev].walk(m):
2441 if not rev and abs not in repo.dirstate:
2441 if not rev and abs not in repo.dirstate:
2442 continue
2442 continue
2443 if opts.get('fullpath'):
2443 if opts.get('fullpath'):
2444 ui.write(repo.wjoin(abs), end)
2444 ui.write(repo.wjoin(abs), end)
2445 else:
2445 else:
2446 ui.write(((pats and m.rel(abs)) or abs), end)
2446 ui.write(((pats and m.rel(abs)) or abs), end)
2447 ret = 0
2447 ret = 0
2448
2448
2449 return ret
2449 return ret
2450
2450
2451 def log(ui, repo, *pats, **opts):
2451 def log(ui, repo, *pats, **opts):
2452 """show revision history of entire repository or files
2452 """show revision history of entire repository or files
2453
2453
2454 Print the revision history of the specified files or the entire
2454 Print the revision history of the specified files or the entire
2455 project.
2455 project.
2456
2456
2457 File history is shown without following rename or copy history of
2457 File history is shown without following rename or copy history of
2458 files. Use -f/--follow with a filename to follow history across
2458 files. Use -f/--follow with a filename to follow history across
2459 renames and copies. --follow without a filename will only show
2459 renames and copies. --follow without a filename will only show
2460 ancestors or descendants of the starting revision. --follow-first
2460 ancestors or descendants of the starting revision. --follow-first
2461 only follows the first parent of merge revisions.
2461 only follows the first parent of merge revisions.
2462
2462
2463 If no revision range is specified, the default is tip:0 unless
2463 If no revision range is specified, the default is tip:0 unless
2464 --follow is set, in which case the working directory parent is
2464 --follow is set, in which case the working directory parent is
2465 used as the starting revision. You can specify a revision set for
2465 used as the starting revision. You can specify a revision set for
2466 log, see :hg:`help revsets` for more information.
2466 log, see :hg:`help revsets` for more information.
2467
2467
2468 See :hg:`help dates` for a list of formats valid for -d/--date.
2468 See :hg:`help dates` for a list of formats valid for -d/--date.
2469
2469
2470 By default this command prints revision number and changeset id,
2470 By default this command prints revision number and changeset id,
2471 tags, non-trivial parents, user, date and time, and a summary for
2471 tags, non-trivial parents, user, date and time, and a summary for
2472 each commit. When the -v/--verbose switch is used, the list of
2472 each commit. When the -v/--verbose switch is used, the list of
2473 changed files and full commit message are shown.
2473 changed files and full commit message are shown.
2474
2474
2475 NOTE: log -p/--patch may generate unexpected diff output for merge
2475 NOTE: log -p/--patch may generate unexpected diff output for merge
2476 changesets, as it will only compare the merge changeset against
2476 changesets, as it will only compare the merge changeset against
2477 its first parent. Also, only files different from BOTH parents
2477 its first parent. Also, only files different from BOTH parents
2478 will appear in files:.
2478 will appear in files:.
2479
2479
2480 Returns 0 on success.
2480 Returns 0 on success.
2481 """
2481 """
2482
2482
2483 matchfn = cmdutil.match(repo, pats, opts)
2483 matchfn = cmdutil.match(repo, pats, opts)
2484 limit = cmdutil.loglimit(opts)
2484 limit = cmdutil.loglimit(opts)
2485 count = 0
2485 count = 0
2486
2486
2487 endrev = None
2487 endrev = None
2488 if opts.get('copies') and opts.get('rev'):
2488 if opts.get('copies') and opts.get('rev'):
2489 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2489 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2490
2490
2491 df = False
2491 df = False
2492 if opts["date"]:
2492 if opts["date"]:
2493 df = util.matchdate(opts["date"])
2493 df = util.matchdate(opts["date"])
2494
2494
2495 branches = opts.get('branch', []) + opts.get('only_branch', [])
2495 branches = opts.get('branch', []) + opts.get('only_branch', [])
2496 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2496 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2497
2497
2498 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2498 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2499 def prep(ctx, fns):
2499 def prep(ctx, fns):
2500 rev = ctx.rev()
2500 rev = ctx.rev()
2501 parents = [p for p in repo.changelog.parentrevs(rev)
2501 parents = [p for p in repo.changelog.parentrevs(rev)
2502 if p != nullrev]
2502 if p != nullrev]
2503 if opts.get('no_merges') and len(parents) == 2:
2503 if opts.get('no_merges') and len(parents) == 2:
2504 return
2504 return
2505 if opts.get('only_merges') and len(parents) != 2:
2505 if opts.get('only_merges') and len(parents) != 2:
2506 return
2506 return
2507 if opts.get('branch') and ctx.branch() not in opts['branch']:
2507 if opts.get('branch') and ctx.branch() not in opts['branch']:
2508 return
2508 return
2509 if df and not df(ctx.date()[0]):
2509 if df and not df(ctx.date()[0]):
2510 return
2510 return
2511 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2511 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2512 return
2512 return
2513 if opts.get('keyword'):
2513 if opts.get('keyword'):
2514 for k in [kw.lower() for kw in opts['keyword']]:
2514 for k in [kw.lower() for kw in opts['keyword']]:
2515 if (k in ctx.user().lower() or
2515 if (k in ctx.user().lower() or
2516 k in ctx.description().lower() or
2516 k in ctx.description().lower() or
2517 k in " ".join(ctx.files()).lower()):
2517 k in " ".join(ctx.files()).lower()):
2518 break
2518 break
2519 else:
2519 else:
2520 return
2520 return
2521
2521
2522 copies = None
2522 copies = None
2523 if opts.get('copies') and rev:
2523 if opts.get('copies') and rev:
2524 copies = []
2524 copies = []
2525 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2525 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2526 for fn in ctx.files():
2526 for fn in ctx.files():
2527 rename = getrenamed(fn, rev)
2527 rename = getrenamed(fn, rev)
2528 if rename:
2528 if rename:
2529 copies.append((fn, rename[0]))
2529 copies.append((fn, rename[0]))
2530
2530
2531 revmatchfn = None
2531 revmatchfn = None
2532 if opts.get('patch') or opts.get('stat'):
2532 if opts.get('patch') or opts.get('stat'):
2533 revmatchfn = cmdutil.match(repo, fns, default='path')
2533 revmatchfn = cmdutil.match(repo, fns, default='path')
2534
2534
2535 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2535 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2536
2536
2537 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2537 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2538 if count == limit:
2538 if count == limit:
2539 break
2539 break
2540 if displayer.flush(ctx.rev()):
2540 if displayer.flush(ctx.rev()):
2541 count += 1
2541 count += 1
2542 displayer.close()
2542 displayer.close()
2543
2543
2544 def manifest(ui, repo, node=None, rev=None):
2544 def manifest(ui, repo, node=None, rev=None):
2545 """output the current or given revision of the project manifest
2545 """output the current or given revision of the project manifest
2546
2546
2547 Print a list of version controlled files for the given revision.
2547 Print a list of version controlled files for the given revision.
2548 If no revision is given, the first parent of the working directory
2548 If no revision is given, the first parent of the working directory
2549 is used, or the null revision if no revision is checked out.
2549 is used, or the null revision if no revision is checked out.
2550
2550
2551 With -v, print file permissions, symlink and executable bits.
2551 With -v, print file permissions, symlink and executable bits.
2552 With --debug, print file revision hashes.
2552 With --debug, print file revision hashes.
2553
2553
2554 Returns 0 on success.
2554 Returns 0 on success.
2555 """
2555 """
2556
2556
2557 if rev and node:
2557 if rev and node:
2558 raise util.Abort(_("please specify just one revision"))
2558 raise util.Abort(_("please specify just one revision"))
2559
2559
2560 if not node:
2560 if not node:
2561 node = rev
2561 node = rev
2562
2562
2563 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2563 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2564 ctx = repo[node]
2564 ctx = repo[node]
2565 for f in ctx:
2565 for f in ctx:
2566 if ui.debugflag:
2566 if ui.debugflag:
2567 ui.write("%40s " % hex(ctx.manifest()[f]))
2567 ui.write("%40s " % hex(ctx.manifest()[f]))
2568 if ui.verbose:
2568 if ui.verbose:
2569 ui.write(decor[ctx.flags(f)])
2569 ui.write(decor[ctx.flags(f)])
2570 ui.write("%s\n" % f)
2570 ui.write("%s\n" % f)
2571
2571
2572 def merge(ui, repo, node=None, **opts):
2572 def merge(ui, repo, node=None, **opts):
2573 """merge working directory with another revision
2573 """merge working directory with another revision
2574
2574
2575 The current working directory is updated with all changes made in
2575 The current working directory is updated with all changes made in
2576 the requested revision since the last common predecessor revision.
2576 the requested revision since the last common predecessor revision.
2577
2577
2578 Files that changed between either parent are marked as changed for
2578 Files that changed between either parent are marked as changed for
2579 the next commit and a commit must be performed before any further
2579 the next commit and a commit must be performed before any further
2580 updates to the repository are allowed. The next commit will have
2580 updates to the repository are allowed. The next commit will have
2581 two parents.
2581 two parents.
2582
2582
2583 If no revision is specified, the working directory's parent is a
2583 If no revision is specified, the working directory's parent is a
2584 head revision, and the current branch contains exactly one other
2584 head revision, and the current branch contains exactly one other
2585 head, the other head is merged with by default. Otherwise, an
2585 head, the other head is merged with by default. Otherwise, an
2586 explicit revision with which to merge with must be provided.
2586 explicit revision with which to merge with must be provided.
2587
2587
2588 To undo an uncommitted merge, use :hg:`update --clean .` which
2588 To undo an uncommitted merge, use :hg:`update --clean .` which
2589 will check out a clean copy of the original merge parent, losing
2589 will check out a clean copy of the original merge parent, losing
2590 all changes.
2590 all changes.
2591
2591
2592 Returns 0 on success, 1 if there are unresolved files.
2592 Returns 0 on success, 1 if there are unresolved files.
2593 """
2593 """
2594
2594
2595 if opts.get('rev') and node:
2595 if opts.get('rev') and node:
2596 raise util.Abort(_("please specify just one revision"))
2596 raise util.Abort(_("please specify just one revision"))
2597 if not node:
2597 if not node:
2598 node = opts.get('rev')
2598 node = opts.get('rev')
2599
2599
2600 if not node:
2600 if not node:
2601 branch = repo.changectx(None).branch()
2601 branch = repo.changectx(None).branch()
2602 bheads = repo.branchheads(branch)
2602 bheads = repo.branchheads(branch)
2603 if len(bheads) > 2:
2603 if len(bheads) > 2:
2604 raise util.Abort(_(
2604 raise util.Abort(_(
2605 'branch \'%s\' has %d heads - '
2605 'branch \'%s\' has %d heads - '
2606 'please merge with an explicit rev\n'
2606 'please merge with an explicit rev\n'
2607 '(run \'hg heads .\' to see heads)')
2607 '(run \'hg heads .\' to see heads)')
2608 % (branch, len(bheads)))
2608 % (branch, len(bheads)))
2609
2609
2610 parent = repo.dirstate.parents()[0]
2610 parent = repo.dirstate.parents()[0]
2611 if len(bheads) == 1:
2611 if len(bheads) == 1:
2612 if len(repo.heads()) > 1:
2612 if len(repo.heads()) > 1:
2613 raise util.Abort(_(
2613 raise util.Abort(_(
2614 'branch \'%s\' has one head - '
2614 'branch \'%s\' has one head - '
2615 'please merge with an explicit rev\n'
2615 'please merge with an explicit rev\n'
2616 '(run \'hg heads\' to see all heads)')
2616 '(run \'hg heads\' to see all heads)')
2617 % branch)
2617 % branch)
2618 msg = _('there is nothing to merge')
2618 msg = _('there is nothing to merge')
2619 if parent != repo.lookup(repo[None].branch()):
2619 if parent != repo.lookup(repo[None].branch()):
2620 msg = _('%s - use "hg update" instead') % msg
2620 msg = _('%s - use "hg update" instead') % msg
2621 raise util.Abort(msg)
2621 raise util.Abort(msg)
2622
2622
2623 if parent not in bheads:
2623 if parent not in bheads:
2624 raise util.Abort(_('working dir not at a head rev - '
2624 raise util.Abort(_('working dir not at a head rev - '
2625 'use "hg update" or merge with an explicit rev'))
2625 'use "hg update" or merge with an explicit rev'))
2626 node = parent == bheads[0] and bheads[-1] or bheads[0]
2626 node = parent == bheads[0] and bheads[-1] or bheads[0]
2627
2627
2628 if opts.get('preview'):
2628 if opts.get('preview'):
2629 # find nodes that are ancestors of p2 but not of p1
2629 # find nodes that are ancestors of p2 but not of p1
2630 p1 = repo.lookup('.')
2630 p1 = repo.lookup('.')
2631 p2 = repo.lookup(node)
2631 p2 = repo.lookup(node)
2632 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2632 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2633
2633
2634 displayer = cmdutil.show_changeset(ui, repo, opts)
2634 displayer = cmdutil.show_changeset(ui, repo, opts)
2635 for node in nodes:
2635 for node in nodes:
2636 displayer.show(repo[node])
2636 displayer.show(repo[node])
2637 displayer.close()
2637 displayer.close()
2638 return 0
2638 return 0
2639
2639
2640 return hg.merge(repo, node, force=opts.get('force'))
2640 return hg.merge(repo, node, force=opts.get('force'))
2641
2641
2642 def outgoing(ui, repo, dest=None, **opts):
2642 def outgoing(ui, repo, dest=None, **opts):
2643 """show changesets not found in the destination
2643 """show changesets not found in the destination
2644
2644
2645 Show changesets not found in the specified destination repository
2645 Show changesets not found in the specified destination repository
2646 or the default push location. These are the changesets that would
2646 or the default push location. These are the changesets that would
2647 be pushed if a push was requested.
2647 be pushed if a push was requested.
2648
2648
2649 See pull for details of valid destination formats.
2649 See pull for details of valid destination formats.
2650
2650
2651 Returns 0 if there are outgoing changes, 1 otherwise.
2651 Returns 0 if there are outgoing changes, 1 otherwise.
2652 """
2652 """
2653 limit = cmdutil.loglimit(opts)
2653 limit = cmdutil.loglimit(opts)
2654 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2654 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2655 dest, branches = hg.parseurl(dest, opts.get('branch'))
2655 dest, branches = hg.parseurl(dest, opts.get('branch'))
2656 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2656 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2657 if revs:
2657 if revs:
2658 revs = [repo.lookup(rev) for rev in revs]
2658 revs = [repo.lookup(rev) for rev in revs]
2659
2659
2660 other = hg.repository(hg.remoteui(repo, opts), dest)
2660 other = hg.repository(hg.remoteui(repo, opts), dest)
2661 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2661 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2662 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2662 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2663 if not o:
2663 if not o:
2664 ui.status(_("no changes found\n"))
2664 ui.status(_("no changes found\n"))
2665 return 1
2665 return 1
2666 o = repo.changelog.nodesbetween(o, revs)[0]
2666 o = repo.changelog.nodesbetween(o, revs)[0]
2667 if opts.get('newest_first'):
2667 if opts.get('newest_first'):
2668 o.reverse()
2668 o.reverse()
2669 displayer = cmdutil.show_changeset(ui, repo, opts)
2669 displayer = cmdutil.show_changeset(ui, repo, opts)
2670 count = 0
2670 count = 0
2671 for n in o:
2671 for n in o:
2672 if limit is not None and count >= limit:
2672 if limit is not None and count >= limit:
2673 break
2673 break
2674 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2674 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2675 if opts.get('no_merges') and len(parents) == 2:
2675 if opts.get('no_merges') and len(parents) == 2:
2676 continue
2676 continue
2677 count += 1
2677 count += 1
2678 displayer.show(repo[n])
2678 displayer.show(repo[n])
2679 displayer.close()
2679 displayer.close()
2680
2680
2681 def parents(ui, repo, file_=None, **opts):
2681 def parents(ui, repo, file_=None, **opts):
2682 """show the parents of the working directory or revision
2682 """show the parents of the working directory or revision
2683
2683
2684 Print the working directory's parent revisions. If a revision is
2684 Print the working directory's parent revisions. If a revision is
2685 given via -r/--rev, the parent of that revision will be printed.
2685 given via -r/--rev, the parent of that revision will be printed.
2686 If a file argument is given, the revision in which the file was
2686 If a file argument is given, the revision in which the file was
2687 last changed (before the working directory revision or the
2687 last changed (before the working directory revision or the
2688 argument to --rev if given) is printed.
2688 argument to --rev if given) is printed.
2689
2689
2690 Returns 0 on success.
2690 Returns 0 on success.
2691 """
2691 """
2692 rev = opts.get('rev')
2692 rev = opts.get('rev')
2693 if rev:
2693 if rev:
2694 ctx = repo[rev]
2694 ctx = repo[rev]
2695 else:
2695 else:
2696 ctx = repo[None]
2696 ctx = repo[None]
2697
2697
2698 if file_:
2698 if file_:
2699 m = cmdutil.match(repo, (file_,), opts)
2699 m = cmdutil.match(repo, (file_,), opts)
2700 if m.anypats() or len(m.files()) != 1:
2700 if m.anypats() or len(m.files()) != 1:
2701 raise util.Abort(_('can only specify an explicit filename'))
2701 raise util.Abort(_('can only specify an explicit filename'))
2702 file_ = m.files()[0]
2702 file_ = m.files()[0]
2703 filenodes = []
2703 filenodes = []
2704 for cp in ctx.parents():
2704 for cp in ctx.parents():
2705 if not cp:
2705 if not cp:
2706 continue
2706 continue
2707 try:
2707 try:
2708 filenodes.append(cp.filenode(file_))
2708 filenodes.append(cp.filenode(file_))
2709 except error.LookupError:
2709 except error.LookupError:
2710 pass
2710 pass
2711 if not filenodes:
2711 if not filenodes:
2712 raise util.Abort(_("'%s' not found in manifest!") % file_)
2712 raise util.Abort(_("'%s' not found in manifest!") % file_)
2713 fl = repo.file(file_)
2713 fl = repo.file(file_)
2714 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2714 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2715 else:
2715 else:
2716 p = [cp.node() for cp in ctx.parents()]
2716 p = [cp.node() for cp in ctx.parents()]
2717
2717
2718 displayer = cmdutil.show_changeset(ui, repo, opts)
2718 displayer = cmdutil.show_changeset(ui, repo, opts)
2719 for n in p:
2719 for n in p:
2720 if n != nullid:
2720 if n != nullid:
2721 displayer.show(repo[n])
2721 displayer.show(repo[n])
2722 displayer.close()
2722 displayer.close()
2723
2723
2724 def paths(ui, repo, search=None):
2724 def paths(ui, repo, search=None):
2725 """show aliases for remote repositories
2725 """show aliases for remote repositories
2726
2726
2727 Show definition of symbolic path name NAME. If no name is given,
2727 Show definition of symbolic path name NAME. If no name is given,
2728 show definition of all available names.
2728 show definition of all available names.
2729
2729
2730 Path names are defined in the [paths] section of
2730 Path names are defined in the [paths] section of
2731 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2731 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2732 repository, ``.hg/hgrc`` is used, too.
2732 repository, ``.hg/hgrc`` is used, too.
2733
2733
2734 The path names ``default`` and ``default-push`` have a special
2734 The path names ``default`` and ``default-push`` have a special
2735 meaning. When performing a push or pull operation, they are used
2735 meaning. When performing a push or pull operation, they are used
2736 as fallbacks if no location is specified on the command-line.
2736 as fallbacks if no location is specified on the command-line.
2737 When ``default-push`` is set, it will be used for push and
2737 When ``default-push`` is set, it will be used for push and
2738 ``default`` will be used for pull; otherwise ``default`` is used
2738 ``default`` will be used for pull; otherwise ``default`` is used
2739 as the fallback for both. When cloning a repository, the clone
2739 as the fallback for both. When cloning a repository, the clone
2740 source is written as ``default`` in ``.hg/hgrc``. Note that
2740 source is written as ``default`` in ``.hg/hgrc``. Note that
2741 ``default`` and ``default-push`` apply to all inbound (e.g.
2741 ``default`` and ``default-push`` apply to all inbound (e.g.
2742 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2742 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2743 :hg:`bundle`) operations.
2743 :hg:`bundle`) operations.
2744
2744
2745 See :hg:`help urls` for more information.
2745 See :hg:`help urls` for more information.
2746
2746
2747 Returns 0 on success.
2747 Returns 0 on success.
2748 """
2748 """
2749 if search:
2749 if search:
2750 for name, path in ui.configitems("paths"):
2750 for name, path in ui.configitems("paths"):
2751 if name == search:
2751 if name == search:
2752 ui.write("%s\n" % url.hidepassword(path))
2752 ui.write("%s\n" % url.hidepassword(path))
2753 return
2753 return
2754 ui.warn(_("not found!\n"))
2754 ui.warn(_("not found!\n"))
2755 return 1
2755 return 1
2756 else:
2756 else:
2757 for name, path in ui.configitems("paths"):
2757 for name, path in ui.configitems("paths"):
2758 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2758 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2759
2759
2760 def postincoming(ui, repo, modheads, optupdate, checkout):
2760 def postincoming(ui, repo, modheads, optupdate, checkout):
2761 if modheads == 0:
2761 if modheads == 0:
2762 return
2762 return
2763 if optupdate:
2763 if optupdate:
2764 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2764 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2765 return hg.update(repo, checkout)
2765 return hg.update(repo, checkout)
2766 else:
2766 else:
2767 ui.status(_("not updating, since new heads added\n"))
2767 ui.status(_("not updating, since new heads added\n"))
2768 if modheads > 1:
2768 if modheads > 1:
2769 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2769 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2770 else:
2770 else:
2771 ui.status(_("(run 'hg update' to get a working copy)\n"))
2771 ui.status(_("(run 'hg update' to get a working copy)\n"))
2772
2772
2773 def pull(ui, repo, source="default", **opts):
2773 def pull(ui, repo, source="default", **opts):
2774 """pull changes from the specified source
2774 """pull changes from the specified source
2775
2775
2776 Pull changes from a remote repository to a local one.
2776 Pull changes from a remote repository to a local one.
2777
2777
2778 This finds all changes from the repository at the specified path
2778 This finds all changes from the repository at the specified path
2779 or URL and adds them to a local repository (the current one unless
2779 or URL and adds them to a local repository (the current one unless
2780 -R is specified). By default, this does not update the copy of the
2780 -R is specified). By default, this does not update the copy of the
2781 project in the working directory.
2781 project in the working directory.
2782
2782
2783 Use :hg:`incoming` if you want to see what would have been added
2783 Use :hg:`incoming` if you want to see what would have been added
2784 by a pull at the time you issued this command. If you then decide
2784 by a pull at the time you issued this command. If you then decide
2785 to add those changes to the repository, you should use :hg:`pull
2785 to add those changes to the repository, you should use :hg:`pull
2786 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2786 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2787
2787
2788 If SOURCE is omitted, the 'default' path will be used.
2788 If SOURCE is omitted, the 'default' path will be used.
2789 See :hg:`help urls` for more information.
2789 See :hg:`help urls` for more information.
2790
2790
2791 Returns 0 on success, 1 if an update had unresolved files.
2791 Returns 0 on success, 1 if an update had unresolved files.
2792 """
2792 """
2793 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2793 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2794 other = hg.repository(hg.remoteui(repo, opts), source)
2794 other = hg.repository(hg.remoteui(repo, opts), source)
2795 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2795 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2796 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2796 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2797 if revs:
2797 if revs:
2798 try:
2798 try:
2799 revs = [other.lookup(rev) for rev in revs]
2799 revs = [other.lookup(rev) for rev in revs]
2800 except error.CapabilityError:
2800 except error.CapabilityError:
2801 err = _("Other repository doesn't support revision lookup, "
2801 err = _("Other repository doesn't support revision lookup, "
2802 "so a rev cannot be specified.")
2802 "so a rev cannot be specified.")
2803 raise util.Abort(err)
2803 raise util.Abort(err)
2804
2804
2805 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2805 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2806 if checkout:
2806 if checkout:
2807 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2807 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2808 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2808 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2809
2809
2810 def push(ui, repo, dest=None, **opts):
2810 def push(ui, repo, dest=None, **opts):
2811 """push changes to the specified destination
2811 """push changes to the specified destination
2812
2812
2813 Push changesets from the local repository to the specified
2813 Push changesets from the local repository to the specified
2814 destination.
2814 destination.
2815
2815
2816 This operation is symmetrical to pull: it is identical to a pull
2816 This operation is symmetrical to pull: it is identical to a pull
2817 in the destination repository from the current one.
2817 in the destination repository from the current one.
2818
2818
2819 By default, push will not allow creation of new heads at the
2819 By default, push will not allow creation of new heads at the
2820 destination, since multiple heads would make it unclear which head
2820 destination, since multiple heads would make it unclear which head
2821 to use. In this situation, it is recommended to pull and merge
2821 to use. In this situation, it is recommended to pull and merge
2822 before pushing.
2822 before pushing.
2823
2823
2824 Use --new-branch if you want to allow push to create a new named
2824 Use --new-branch if you want to allow push to create a new named
2825 branch that is not present at the destination. This allows you to
2825 branch that is not present at the destination. This allows you to
2826 only create a new branch without forcing other changes.
2826 only create a new branch without forcing other changes.
2827
2827
2828 Use -f/--force to override the default behavior and push all
2828 Use -f/--force to override the default behavior and push all
2829 changesets on all branches.
2829 changesets on all branches.
2830
2830
2831 If -r/--rev is used, the specified revision and all its ancestors
2831 If -r/--rev is used, the specified revision and all its ancestors
2832 will be pushed to the remote repository.
2832 will be pushed to the remote repository.
2833
2833
2834 Please see :hg:`help urls` for important details about ``ssh://``
2834 Please see :hg:`help urls` for important details about ``ssh://``
2835 URLs. If DESTINATION is omitted, a default path will be used.
2835 URLs. If DESTINATION is omitted, a default path will be used.
2836
2836
2837 Returns 0 if push was successful, 1 if nothing to push.
2837 Returns 0 if push was successful, 1 if nothing to push.
2838 """
2838 """
2839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2840 dest, branches = hg.parseurl(dest, opts.get('branch'))
2840 dest, branches = hg.parseurl(dest, opts.get('branch'))
2841 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2841 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2842 other = hg.repository(hg.remoteui(repo, opts), dest)
2842 other = hg.repository(hg.remoteui(repo, opts), dest)
2843 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2843 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2844 if revs:
2844 if revs:
2845 revs = [repo.lookup(rev) for rev in revs]
2845 revs = [repo.lookup(rev) for rev in revs]
2846
2846
2847 # push subrepos depth-first for coherent ordering
2847 # push subrepos depth-first for coherent ordering
2848 c = repo['']
2848 c = repo['']
2849 subs = c.substate # only repos that are committed
2849 subs = c.substate # only repos that are committed
2850 for s in sorted(subs):
2850 for s in sorted(subs):
2851 if not c.sub(s).push(opts.get('force')):
2851 if not c.sub(s).push(opts.get('force')):
2852 return False
2852 return False
2853
2853
2854 r = repo.push(other, opts.get('force'), revs=revs,
2854 r = repo.push(other, opts.get('force'), revs=revs,
2855 newbranch=opts.get('new_branch'))
2855 newbranch=opts.get('new_branch'))
2856 return r == 0
2856 return r == 0
2857
2857
2858 def recover(ui, repo):
2858 def recover(ui, repo):
2859 """roll back an interrupted transaction
2859 """roll back an interrupted transaction
2860
2860
2861 Recover from an interrupted commit or pull.
2861 Recover from an interrupted commit or pull.
2862
2862
2863 This command tries to fix the repository status after an
2863 This command tries to fix the repository status after an
2864 interrupted operation. It should only be necessary when Mercurial
2864 interrupted operation. It should only be necessary when Mercurial
2865 suggests it.
2865 suggests it.
2866
2866
2867 Returns 0 if successful, 1 if nothing to recover or verify fails.
2867 Returns 0 if successful, 1 if nothing to recover or verify fails.
2868 """
2868 """
2869 if repo.recover():
2869 if repo.recover():
2870 return hg.verify(repo)
2870 return hg.verify(repo)
2871 return 1
2871 return 1
2872
2872
2873 def remove(ui, repo, *pats, **opts):
2873 def remove(ui, repo, *pats, **opts):
2874 """remove the specified files on the next commit
2874 """remove the specified files on the next commit
2875
2875
2876 Schedule the indicated files for removal from the repository.
2876 Schedule the indicated files for removal from the repository.
2877
2877
2878 This only removes files from the current branch, not from the
2878 This only removes files from the current branch, not from the
2879 entire project history. -A/--after can be used to remove only
2879 entire project history. -A/--after can be used to remove only
2880 files that have already been deleted, -f/--force can be used to
2880 files that have already been deleted, -f/--force can be used to
2881 force deletion, and -Af can be used to remove files from the next
2881 force deletion, and -Af can be used to remove files from the next
2882 revision without deleting them from the working directory.
2882 revision without deleting them from the working directory.
2883
2883
2884 The following table details the behavior of remove for different
2884 The following table details the behavior of remove for different
2885 file states (columns) and option combinations (rows). The file
2885 file states (columns) and option combinations (rows). The file
2886 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2886 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2887 reported by :hg:`status`). The actions are Warn, Remove (from
2887 reported by :hg:`status`). The actions are Warn, Remove (from
2888 branch) and Delete (from disk)::
2888 branch) and Delete (from disk)::
2889
2889
2890 A C M !
2890 A C M !
2891 none W RD W R
2891 none W RD W R
2892 -f R RD RD R
2892 -f R RD RD R
2893 -A W W W R
2893 -A W W W R
2894 -Af R R R R
2894 -Af R R R R
2895
2895
2896 This command schedules the files to be removed at the next commit.
2896 This command schedules the files to be removed at the next commit.
2897 To undo a remove before that, see :hg:`revert`.
2897 To undo a remove before that, see :hg:`revert`.
2898
2898
2899 Returns 0 on success, 1 if any warnings encountered.
2899 Returns 0 on success, 1 if any warnings encountered.
2900 """
2900 """
2901
2901
2902 ret = 0
2902 ret = 0
2903 after, force = opts.get('after'), opts.get('force')
2903 after, force = opts.get('after'), opts.get('force')
2904 if not pats and not after:
2904 if not pats and not after:
2905 raise util.Abort(_('no files specified'))
2905 raise util.Abort(_('no files specified'))
2906
2906
2907 m = cmdutil.match(repo, pats, opts)
2907 m = cmdutil.match(repo, pats, opts)
2908 s = repo.status(match=m, clean=True)
2908 s = repo.status(match=m, clean=True)
2909 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2909 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2910
2910
2911 for f in m.files():
2911 for f in m.files():
2912 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2912 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2913 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2913 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2914 ret = 1
2914 ret = 1
2915
2915
2916 def warn(files, reason):
2916 def warn(files, reason):
2917 for f in files:
2917 for f in files:
2918 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2918 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2919 % (m.rel(f), reason))
2919 % (m.rel(f), reason))
2920 ret = 1
2920 ret = 1
2921
2921
2922 if force:
2922 if force:
2923 remove, forget = modified + deleted + clean, added
2923 remove, forget = modified + deleted + clean, added
2924 elif after:
2924 elif after:
2925 remove, forget = deleted, []
2925 remove, forget = deleted, []
2926 warn(modified + added + clean, _('still exists'))
2926 warn(modified + added + clean, _('still exists'))
2927 else:
2927 else:
2928 remove, forget = deleted + clean, []
2928 remove, forget = deleted + clean, []
2929 warn(modified, _('is modified'))
2929 warn(modified, _('is modified'))
2930 warn(added, _('has been marked for add'))
2930 warn(added, _('has been marked for add'))
2931
2931
2932 for f in sorted(remove + forget):
2932 for f in sorted(remove + forget):
2933 if ui.verbose or not m.exact(f):
2933 if ui.verbose or not m.exact(f):
2934 ui.status(_('removing %s\n') % m.rel(f))
2934 ui.status(_('removing %s\n') % m.rel(f))
2935
2935
2936 repo[None].forget(forget)
2936 repo[None].forget(forget)
2937 repo[None].remove(remove, unlink=not after)
2937 repo[None].remove(remove, unlink=not after)
2938 return ret
2938 return ret
2939
2939
2940 def rename(ui, repo, *pats, **opts):
2940 def rename(ui, repo, *pats, **opts):
2941 """rename files; equivalent of copy + remove
2941 """rename files; equivalent of copy + remove
2942
2942
2943 Mark dest as copies of sources; mark sources for deletion. If dest
2943 Mark dest as copies of sources; mark sources for deletion. If dest
2944 is a directory, copies are put in that directory. If dest is a
2944 is a directory, copies are put in that directory. If dest is a
2945 file, there can only be one source.
2945 file, there can only be one source.
2946
2946
2947 By default, this command copies the contents of files as they
2947 By default, this command copies the contents of files as they
2948 exist in the working directory. If invoked with -A/--after, the
2948 exist in the working directory. If invoked with -A/--after, the
2949 operation is recorded, but no copying is performed.
2949 operation is recorded, but no copying is performed.
2950
2950
2951 This command takes effect at the next commit. To undo a rename
2951 This command takes effect at the next commit. To undo a rename
2952 before that, see :hg:`revert`.
2952 before that, see :hg:`revert`.
2953
2953
2954 Returns 0 on success, 1 if errors are encountered.
2954 Returns 0 on success, 1 if errors are encountered.
2955 """
2955 """
2956 wlock = repo.wlock(False)
2956 wlock = repo.wlock(False)
2957 try:
2957 try:
2958 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2958 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2959 finally:
2959 finally:
2960 wlock.release()
2960 wlock.release()
2961
2961
2962 def resolve(ui, repo, *pats, **opts):
2962 def resolve(ui, repo, *pats, **opts):
2963 """redo merges or set/view the merge status of files
2963 """redo merges or set/view the merge status of files
2964
2964
2965 Merges with unresolved conflicts are often the result of
2965 Merges with unresolved conflicts are often the result of
2966 non-interactive merging using the ``internal:merge`` hgrc setting,
2966 non-interactive merging using the ``internal:merge`` hgrc setting,
2967 or a command-line merge tool like ``diff3``. The resolve command
2967 or a command-line merge tool like ``diff3``. The resolve command
2968 is used to manage the files involved in a merge, after :hg:`merge`
2968 is used to manage the files involved in a merge, after :hg:`merge`
2969 has been run, and before :hg:`commit` is run (i.e. the working
2969 has been run, and before :hg:`commit` is run (i.e. the working
2970 directory must have two parents).
2970 directory must have two parents).
2971
2971
2972 The resolve command can be used in the following ways:
2972 The resolve command can be used in the following ways:
2973
2973
2974 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2974 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2975 discarding any previous merge attempts. Re-merging is not
2975 discarding any previous merge attempts. Re-merging is not
2976 performed for files already marked as resolved. Use ``--all/-a``
2976 performed for files already marked as resolved. Use ``--all/-a``
2977 to selects all unresolved files.
2977 to selects all unresolved files.
2978
2978
2979 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2979 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2980 (e.g. after having manually fixed-up the files). The default is
2980 (e.g. after having manually fixed-up the files). The default is
2981 to mark all unresolved files.
2981 to mark all unresolved files.
2982
2982
2983 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2983 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2984 default is to mark all resolved files.
2984 default is to mark all resolved files.
2985
2985
2986 - :hg:`resolve -l`: list files which had or still have conflicts.
2986 - :hg:`resolve -l`: list files which had or still have conflicts.
2987 In the printed list, ``U`` = unresolved and ``R`` = resolved.
2987 In the printed list, ``U`` = unresolved and ``R`` = resolved.
2988
2988
2989 Note that Mercurial will not let you commit files with unresolved
2989 Note that Mercurial will not let you commit files with unresolved
2990 merge conflicts. You must use :hg:`resolve -m ...` before you can
2990 merge conflicts. You must use :hg:`resolve -m ...` before you can
2991 commit after a conflicting merge.
2991 commit after a conflicting merge.
2992
2992
2993 Returns 0 on success, 1 if any files fail a resolve attempt.
2993 Returns 0 on success, 1 if any files fail a resolve attempt.
2994 """
2994 """
2995
2995
2996 all, mark, unmark, show, nostatus = \
2996 all, mark, unmark, show, nostatus = \
2997 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2997 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2998
2998
2999 if (show and (mark or unmark)) or (mark and unmark):
2999 if (show and (mark or unmark)) or (mark and unmark):
3000 raise util.Abort(_("too many options specified"))
3000 raise util.Abort(_("too many options specified"))
3001 if pats and all:
3001 if pats and all:
3002 raise util.Abort(_("can't specify --all and patterns"))
3002 raise util.Abort(_("can't specify --all and patterns"))
3003 if not (all or pats or show or mark or unmark):
3003 if not (all or pats or show or mark or unmark):
3004 raise util.Abort(_('no files or directories specified; '
3004 raise util.Abort(_('no files or directories specified; '
3005 'use --all to remerge all files'))
3005 'use --all to remerge all files'))
3006
3006
3007 ms = mergemod.mergestate(repo)
3007 ms = mergemod.mergestate(repo)
3008 m = cmdutil.match(repo, pats, opts)
3008 m = cmdutil.match(repo, pats, opts)
3009 ret = 0
3009 ret = 0
3010
3010
3011 for f in ms:
3011 for f in ms:
3012 if m(f):
3012 if m(f):
3013 if show:
3013 if show:
3014 if nostatus:
3014 if nostatus:
3015 ui.write("%s\n" % f)
3015 ui.write("%s\n" % f)
3016 else:
3016 else:
3017 ui.write("%s %s\n" % (ms[f].upper(), f),
3017 ui.write("%s %s\n" % (ms[f].upper(), f),
3018 label='resolve.' +
3018 label='resolve.' +
3019 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3019 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3020 elif mark:
3020 elif mark:
3021 ms.mark(f, "r")
3021 ms.mark(f, "r")
3022 elif unmark:
3022 elif unmark:
3023 ms.mark(f, "u")
3023 ms.mark(f, "u")
3024 else:
3024 else:
3025 wctx = repo[None]
3025 wctx = repo[None]
3026 mctx = wctx.parents()[-1]
3026 mctx = wctx.parents()[-1]
3027
3027
3028 # backup pre-resolve (merge uses .orig for its own purposes)
3028 # backup pre-resolve (merge uses .orig for its own purposes)
3029 a = repo.wjoin(f)
3029 a = repo.wjoin(f)
3030 util.copyfile(a, a + ".resolve")
3030 util.copyfile(a, a + ".resolve")
3031
3031
3032 # resolve file
3032 # resolve file
3033 if ms.resolve(f, wctx, mctx):
3033 if ms.resolve(f, wctx, mctx):
3034 ret = 1
3034 ret = 1
3035
3035
3036 # replace filemerge's .orig file with our resolve file
3036 # replace filemerge's .orig file with our resolve file
3037 util.rename(a + ".resolve", a + ".orig")
3037 util.rename(a + ".resolve", a + ".orig")
3038 return ret
3038 return ret
3039
3039
3040 def revert(ui, repo, *pats, **opts):
3040 def revert(ui, repo, *pats, **opts):
3041 """restore individual files or directories to an earlier state
3041 """restore individual files or directories to an earlier state
3042
3042
3043 NOTE: This command is most likely not what you are looking for. revert
3043 NOTE: This command is most likely not what you are looking for. revert
3044 will partially overwrite content in the working directory without changing
3044 will partially overwrite content in the working directory without changing
3045 the working directory parents. Use :hg:`update -r rev` to check out earlier
3045 the working directory parents. Use :hg:`update -r rev` to check out earlier
3046 revisions, or :hg:`update --clean .` to undo a merge which has added
3046 revisions, or :hg:`update --clean .` to undo a merge which has added
3047 another parent.
3047 another parent.
3048
3048
3049 With no revision specified, revert the named files or directories
3049 With no revision specified, revert the named files or directories
3050 to the contents they had in the parent of the working directory.
3050 to the contents they had in the parent of the working directory.
3051 This restores the contents of the affected files to an unmodified
3051 This restores the contents of the affected files to an unmodified
3052 state and unschedules adds, removes, copies, and renames. If the
3052 state and unschedules adds, removes, copies, and renames. If the
3053 working directory has two parents, you must explicitly specify a
3053 working directory has two parents, you must explicitly specify a
3054 revision.
3054 revision.
3055
3055
3056 Using the -r/--rev option, revert the given files or directories
3056 Using the -r/--rev option, revert the given files or directories
3057 to their contents as of a specific revision. This can be helpful
3057 to their contents as of a specific revision. This can be helpful
3058 to "roll back" some or all of an earlier change. See :hg:`help
3058 to "roll back" some or all of an earlier change. See :hg:`help
3059 dates` for a list of formats valid for -d/--date.
3059 dates` for a list of formats valid for -d/--date.
3060
3060
3061 Revert modifies the working directory. It does not commit any
3061 Revert modifies the working directory. It does not commit any
3062 changes, or change the parent of the working directory. If you
3062 changes, or change the parent of the working directory. If you
3063 revert to a revision other than the parent of the working
3063 revert to a revision other than the parent of the working
3064 directory, the reverted files will thus appear modified
3064 directory, the reverted files will thus appear modified
3065 afterwards.
3065 afterwards.
3066
3066
3067 If a file has been deleted, it is restored. If the executable mode
3067 If a file has been deleted, it is restored. If the executable mode
3068 of a file was changed, it is reset.
3068 of a file was changed, it is reset.
3069
3069
3070 If names are given, all files matching the names are reverted.
3070 If names are given, all files matching the names are reverted.
3071 If no arguments are given, no files are reverted.
3071 If no arguments are given, no files are reverted.
3072
3072
3073 Modified files are saved with a .orig suffix before reverting.
3073 Modified files are saved with a .orig suffix before reverting.
3074 To disable these backups, use --no-backup.
3074 To disable these backups, use --no-backup.
3075
3075
3076 Returns 0 on success.
3076 Returns 0 on success.
3077 """
3077 """
3078
3078
3079 if opts["date"]:
3079 if opts["date"]:
3080 if opts["rev"]:
3080 if opts["rev"]:
3081 raise util.Abort(_("you can't specify a revision and a date"))
3081 raise util.Abort(_("you can't specify a revision and a date"))
3082 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3082 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3083
3083
3084 if not pats and not opts.get('all'):
3084 if not pats and not opts.get('all'):
3085 raise util.Abort(_('no files or directories specified; '
3085 raise util.Abort(_('no files or directories specified; '
3086 'use --all to revert the whole repo'))
3086 'use --all to revert the whole repo'))
3087
3087
3088 parent, p2 = repo.dirstate.parents()
3088 parent, p2 = repo.dirstate.parents()
3089 if not opts.get('rev') and p2 != nullid:
3089 if not opts.get('rev') and p2 != nullid:
3090 raise util.Abort(_('uncommitted merge - please provide a '
3090 raise util.Abort(_('uncommitted merge - please provide a '
3091 'specific revision'))
3091 'specific revision'))
3092 ctx = repo[opts.get('rev')]
3092 ctx = repo[opts.get('rev')]
3093 node = ctx.node()
3093 node = ctx.node()
3094 mf = ctx.manifest()
3094 mf = ctx.manifest()
3095 if node == parent:
3095 if node == parent:
3096 pmf = mf
3096 pmf = mf
3097 else:
3097 else:
3098 pmf = None
3098 pmf = None
3099
3099
3100 # need all matching names in dirstate and manifest of target rev,
3100 # need all matching names in dirstate and manifest of target rev,
3101 # so have to walk both. do not print errors if files exist in one
3101 # so have to walk both. do not print errors if files exist in one
3102 # but not other.
3102 # but not other.
3103
3103
3104 names = {}
3104 names = {}
3105
3105
3106 wlock = repo.wlock()
3106 wlock = repo.wlock()
3107 try:
3107 try:
3108 # walk dirstate.
3108 # walk dirstate.
3109
3109
3110 m = cmdutil.match(repo, pats, opts)
3110 m = cmdutil.match(repo, pats, opts)
3111 m.bad = lambda x, y: False
3111 m.bad = lambda x, y: False
3112 for abs in repo.walk(m):
3112 for abs in repo.walk(m):
3113 names[abs] = m.rel(abs), m.exact(abs)
3113 names[abs] = m.rel(abs), m.exact(abs)
3114
3114
3115 # walk target manifest.
3115 # walk target manifest.
3116
3116
3117 def badfn(path, msg):
3117 def badfn(path, msg):
3118 if path in names:
3118 if path in names:
3119 return
3119 return
3120 path_ = path + '/'
3120 path_ = path + '/'
3121 for f in names:
3121 for f in names:
3122 if f.startswith(path_):
3122 if f.startswith(path_):
3123 return
3123 return
3124 ui.warn("%s: %s\n" % (m.rel(path), msg))
3124 ui.warn("%s: %s\n" % (m.rel(path), msg))
3125
3125
3126 m = cmdutil.match(repo, pats, opts)
3126 m = cmdutil.match(repo, pats, opts)
3127 m.bad = badfn
3127 m.bad = badfn
3128 for abs in repo[node].walk(m):
3128 for abs in repo[node].walk(m):
3129 if abs not in names:
3129 if abs not in names:
3130 names[abs] = m.rel(abs), m.exact(abs)
3130 names[abs] = m.rel(abs), m.exact(abs)
3131
3131
3132 m = cmdutil.matchfiles(repo, names)
3132 m = cmdutil.matchfiles(repo, names)
3133 changes = repo.status(match=m)[:4]
3133 changes = repo.status(match=m)[:4]
3134 modified, added, removed, deleted = map(set, changes)
3134 modified, added, removed, deleted = map(set, changes)
3135
3135
3136 # if f is a rename, also revert the source
3136 # if f is a rename, also revert the source
3137 cwd = repo.getcwd()
3137 cwd = repo.getcwd()
3138 for f in added:
3138 for f in added:
3139 src = repo.dirstate.copied(f)
3139 src = repo.dirstate.copied(f)
3140 if src and src not in names and repo.dirstate[src] == 'r':
3140 if src and src not in names and repo.dirstate[src] == 'r':
3141 removed.add(src)
3141 removed.add(src)
3142 names[src] = (repo.pathto(src, cwd), True)
3142 names[src] = (repo.pathto(src, cwd), True)
3143
3143
3144 def removeforget(abs):
3144 def removeforget(abs):
3145 if repo.dirstate[abs] == 'a':
3145 if repo.dirstate[abs] == 'a':
3146 return _('forgetting %s\n')
3146 return _('forgetting %s\n')
3147 return _('removing %s\n')
3147 return _('removing %s\n')
3148
3148
3149 revert = ([], _('reverting %s\n'))
3149 revert = ([], _('reverting %s\n'))
3150 add = ([], _('adding %s\n'))
3150 add = ([], _('adding %s\n'))
3151 remove = ([], removeforget)
3151 remove = ([], removeforget)
3152 undelete = ([], _('undeleting %s\n'))
3152 undelete = ([], _('undeleting %s\n'))
3153
3153
3154 disptable = (
3154 disptable = (
3155 # dispatch table:
3155 # dispatch table:
3156 # file state
3156 # file state
3157 # action if in target manifest
3157 # action if in target manifest
3158 # action if not in target manifest
3158 # action if not in target manifest
3159 # make backup if in target manifest
3159 # make backup if in target manifest
3160 # make backup if not in target manifest
3160 # make backup if not in target manifest
3161 (modified, revert, remove, True, True),
3161 (modified, revert, remove, True, True),
3162 (added, revert, remove, True, False),
3162 (added, revert, remove, True, False),
3163 (removed, undelete, None, False, False),
3163 (removed, undelete, None, False, False),
3164 (deleted, revert, remove, False, False),
3164 (deleted, revert, remove, False, False),
3165 )
3165 )
3166
3166
3167 for abs, (rel, exact) in sorted(names.items()):
3167 for abs, (rel, exact) in sorted(names.items()):
3168 mfentry = mf.get(abs)
3168 mfentry = mf.get(abs)
3169 target = repo.wjoin(abs)
3169 target = repo.wjoin(abs)
3170 def handle(xlist, dobackup):
3170 def handle(xlist, dobackup):
3171 xlist[0].append(abs)
3171 xlist[0].append(abs)
3172 if dobackup and not opts.get('no_backup') and util.lexists(target):
3172 if dobackup and not opts.get('no_backup') and util.lexists(target):
3173 bakname = "%s.orig" % rel
3173 bakname = "%s.orig" % rel
3174 ui.note(_('saving current version of %s as %s\n') %
3174 ui.note(_('saving current version of %s as %s\n') %
3175 (rel, bakname))
3175 (rel, bakname))
3176 if not opts.get('dry_run'):
3176 if not opts.get('dry_run'):
3177 util.rename(target, bakname)
3177 util.rename(target, bakname)
3178 if ui.verbose or not exact:
3178 if ui.verbose or not exact:
3179 msg = xlist[1]
3179 msg = xlist[1]
3180 if not isinstance(msg, basestring):
3180 if not isinstance(msg, basestring):
3181 msg = msg(abs)
3181 msg = msg(abs)
3182 ui.status(msg % rel)
3182 ui.status(msg % rel)
3183 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3183 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3184 if abs not in table:
3184 if abs not in table:
3185 continue
3185 continue
3186 # file has changed in dirstate
3186 # file has changed in dirstate
3187 if mfentry:
3187 if mfentry:
3188 handle(hitlist, backuphit)
3188 handle(hitlist, backuphit)
3189 elif misslist is not None:
3189 elif misslist is not None:
3190 handle(misslist, backupmiss)
3190 handle(misslist, backupmiss)
3191 break
3191 break
3192 else:
3192 else:
3193 if abs not in repo.dirstate:
3193 if abs not in repo.dirstate:
3194 if mfentry:
3194 if mfentry:
3195 handle(add, True)
3195 handle(add, True)
3196 elif exact:
3196 elif exact:
3197 ui.warn(_('file not managed: %s\n') % rel)
3197 ui.warn(_('file not managed: %s\n') % rel)
3198 continue
3198 continue
3199 # file has not changed in dirstate
3199 # file has not changed in dirstate
3200 if node == parent:
3200 if node == parent:
3201 if exact:
3201 if exact:
3202 ui.warn(_('no changes needed to %s\n') % rel)
3202 ui.warn(_('no changes needed to %s\n') % rel)
3203 continue
3203 continue
3204 if pmf is None:
3204 if pmf is None:
3205 # only need parent manifest in this unlikely case,
3205 # only need parent manifest in this unlikely case,
3206 # so do not read by default
3206 # so do not read by default
3207 pmf = repo[parent].manifest()
3207 pmf = repo[parent].manifest()
3208 if abs in pmf:
3208 if abs in pmf:
3209 if mfentry:
3209 if mfentry:
3210 # if version of file is same in parent and target
3210 # if version of file is same in parent and target
3211 # manifests, do nothing
3211 # manifests, do nothing
3212 if (pmf[abs] != mfentry or
3212 if (pmf[abs] != mfentry or
3213 pmf.flags(abs) != mf.flags(abs)):
3213 pmf.flags(abs) != mf.flags(abs)):
3214 handle(revert, False)
3214 handle(revert, False)
3215 else:
3215 else:
3216 handle(remove, False)
3216 handle(remove, False)
3217
3217
3218 if not opts.get('dry_run'):
3218 if not opts.get('dry_run'):
3219 def checkout(f):
3219 def checkout(f):
3220 fc = ctx[f]
3220 fc = ctx[f]
3221 repo.wwrite(f, fc.data(), fc.flags())
3221 repo.wwrite(f, fc.data(), fc.flags())
3222
3222
3223 audit_path = util.path_auditor(repo.root)
3223 audit_path = util.path_auditor(repo.root)
3224 for f in remove[0]:
3224 for f in remove[0]:
3225 if repo.dirstate[f] == 'a':
3225 if repo.dirstate[f] == 'a':
3226 repo.dirstate.forget(f)
3226 repo.dirstate.forget(f)
3227 continue
3227 continue
3228 audit_path(f)
3228 audit_path(f)
3229 try:
3229 try:
3230 util.unlink(repo.wjoin(f))
3230 util.unlink(repo.wjoin(f))
3231 except OSError:
3231 except OSError:
3232 pass
3232 pass
3233 repo.dirstate.remove(f)
3233 repo.dirstate.remove(f)
3234
3234
3235 normal = None
3235 normal = None
3236 if node == parent:
3236 if node == parent:
3237 # We're reverting to our parent. If possible, we'd like status
3237 # We're reverting to our parent. If possible, we'd like status
3238 # to report the file as clean. We have to use normallookup for
3238 # to report the file as clean. We have to use normallookup for
3239 # merges to avoid losing information about merged/dirty files.
3239 # merges to avoid losing information about merged/dirty files.
3240 if p2 != nullid:
3240 if p2 != nullid:
3241 normal = repo.dirstate.normallookup
3241 normal = repo.dirstate.normallookup
3242 else:
3242 else:
3243 normal = repo.dirstate.normal
3243 normal = repo.dirstate.normal
3244 for f in revert[0]:
3244 for f in revert[0]:
3245 checkout(f)
3245 checkout(f)
3246 if normal:
3246 if normal:
3247 normal(f)
3247 normal(f)
3248
3248
3249 for f in add[0]:
3249 for f in add[0]:
3250 checkout(f)
3250 checkout(f)
3251 repo.dirstate.add(f)
3251 repo.dirstate.add(f)
3252
3252
3253 normal = repo.dirstate.normallookup
3253 normal = repo.dirstate.normallookup
3254 if node == parent and p2 == nullid:
3254 if node == parent and p2 == nullid:
3255 normal = repo.dirstate.normal
3255 normal = repo.dirstate.normal
3256 for f in undelete[0]:
3256 for f in undelete[0]:
3257 checkout(f)
3257 checkout(f)
3258 normal(f)
3258 normal(f)
3259
3259
3260 finally:
3260 finally:
3261 wlock.release()
3261 wlock.release()
3262
3262
3263 def rollback(ui, repo, **opts):
3263 def rollback(ui, repo, **opts):
3264 """roll back the last transaction (dangerous)
3264 """roll back the last transaction (dangerous)
3265
3265
3266 This command should be used with care. There is only one level of
3266 This command should be used with care. There is only one level of
3267 rollback, and there is no way to undo a rollback. It will also
3267 rollback, and there is no way to undo a rollback. It will also
3268 restore the dirstate at the time of the last transaction, losing
3268 restore the dirstate at the time of the last transaction, losing
3269 any dirstate changes since that time. This command does not alter
3269 any dirstate changes since that time. This command does not alter
3270 the working directory.
3270 the working directory.
3271
3271
3272 Transactions are used to encapsulate the effects of all commands
3272 Transactions are used to encapsulate the effects of all commands
3273 that create new changesets or propagate existing changesets into a
3273 that create new changesets or propagate existing changesets into a
3274 repository. For example, the following commands are transactional,
3274 repository. For example, the following commands are transactional,
3275 and their effects can be rolled back:
3275 and their effects can be rolled back:
3276
3276
3277 - commit
3277 - commit
3278 - import
3278 - import
3279 - pull
3279 - pull
3280 - push (with this repository as the destination)
3280 - push (with this repository as the destination)
3281 - unbundle
3281 - unbundle
3282
3282
3283 This command is not intended for use on public repositories. Once
3283 This command is not intended for use on public repositories. Once
3284 changes are visible for pull by other users, rolling a transaction
3284 changes are visible for pull by other users, rolling a transaction
3285 back locally is ineffective (someone else may already have pulled
3285 back locally is ineffective (someone else may already have pulled
3286 the changes). Furthermore, a race is possible with readers of the
3286 the changes). Furthermore, a race is possible with readers of the
3287 repository; for example an in-progress pull from the repository
3287 repository; for example an in-progress pull from the repository
3288 may fail if a rollback is performed.
3288 may fail if a rollback is performed.
3289
3289
3290 Returns 0 on success, 1 if no rollback data is available.
3290 Returns 0 on success, 1 if no rollback data is available.
3291 """
3291 """
3292 return repo.rollback(opts.get('dry_run'))
3292 return repo.rollback(opts.get('dry_run'))
3293
3293
3294 def root(ui, repo):
3294 def root(ui, repo):
3295 """print the root (top) of the current working directory
3295 """print the root (top) of the current working directory
3296
3296
3297 Print the root directory of the current repository.
3297 Print the root directory of the current repository.
3298
3298
3299 Returns 0 on success.
3299 Returns 0 on success.
3300 """
3300 """
3301 ui.write(repo.root + "\n")
3301 ui.write(repo.root + "\n")
3302
3302
3303 def serve(ui, repo, **opts):
3303 def serve(ui, repo, **opts):
3304 """start stand-alone webserver
3304 """start stand-alone webserver
3305
3305
3306 Start a local HTTP repository browser and pull server. You can use
3306 Start a local HTTP repository browser and pull server. You can use
3307 this for ad-hoc sharing and browing of repositories. It is
3307 this for ad-hoc sharing and browing of repositories. It is
3308 recommended to use a real web server to serve a repository for
3308 recommended to use a real web server to serve a repository for
3309 longer periods of time.
3309 longer periods of time.
3310
3310
3311 Please note that the server does not implement access control.
3311 Please note that the server does not implement access control.
3312 This means that, by default, anybody can read from the server and
3312 This means that, by default, anybody can read from the server and
3313 nobody can write to it by default. Set the ``web.allow_push``
3313 nobody can write to it by default. Set the ``web.allow_push``
3314 option to ``*`` to allow everybody to push to the server. You
3314 option to ``*`` to allow everybody to push to the server. You
3315 should use a real web server if you need to authenticate users.
3315 should use a real web server if you need to authenticate users.
3316
3316
3317 By default, the server logs accesses to stdout and errors to
3317 By default, the server logs accesses to stdout and errors to
3318 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3318 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3319 files.
3319 files.
3320
3320
3321 To have the server choose a free port number to listen on, specify
3321 To have the server choose a free port number to listen on, specify
3322 a port number of 0; in this case, the server will print the port
3322 a port number of 0; in this case, the server will print the port
3323 number it uses.
3323 number it uses.
3324
3324
3325 Returns 0 on success.
3325 Returns 0 on success.
3326 """
3326 """
3327
3327
3328 if opts["stdio"]:
3328 if opts["stdio"]:
3329 if repo is None:
3329 if repo is None:
3330 raise error.RepoError(_("There is no Mercurial repository here"
3330 raise error.RepoError(_("There is no Mercurial repository here"
3331 " (.hg not found)"))
3331 " (.hg not found)"))
3332 s = sshserver.sshserver(ui, repo)
3332 s = sshserver.sshserver(ui, repo)
3333 s.serve_forever()
3333 s.serve_forever()
3334
3334
3335 # this way we can check if something was given in the command-line
3335 # this way we can check if something was given in the command-line
3336 if opts.get('port'):
3336 if opts.get('port'):
3337 opts['port'] = int(opts.get('port'))
3337 opts['port'] = int(opts.get('port'))
3338
3338
3339 baseui = repo and repo.baseui or ui
3339 baseui = repo and repo.baseui or ui
3340 optlist = ("name templates style address port prefix ipv6"
3340 optlist = ("name templates style address port prefix ipv6"
3341 " accesslog errorlog certificate encoding")
3341 " accesslog errorlog certificate encoding")
3342 for o in optlist.split():
3342 for o in optlist.split():
3343 val = opts.get(o, '')
3343 val = opts.get(o, '')
3344 if val in (None, ''): # should check against default options instead
3344 if val in (None, ''): # should check against default options instead
3345 continue
3345 continue
3346 baseui.setconfig("web", o, val)
3346 baseui.setconfig("web", o, val)
3347 if repo and repo.ui != baseui:
3347 if repo and repo.ui != baseui:
3348 repo.ui.setconfig("web", o, val)
3348 repo.ui.setconfig("web", o, val)
3349
3349
3350 o = opts.get('web_conf') or opts.get('webdir_conf')
3350 o = opts.get('web_conf') or opts.get('webdir_conf')
3351 if not o:
3351 if not o:
3352 if not repo:
3352 if not repo:
3353 raise error.RepoError(_("There is no Mercurial repository"
3353 raise error.RepoError(_("There is no Mercurial repository"
3354 " here (.hg not found)"))
3354 " here (.hg not found)"))
3355 o = repo.root
3355 o = repo.root
3356
3356
3357 app = hgweb.hgweb(o, baseui=ui)
3357 app = hgweb.hgweb(o, baseui=ui)
3358
3358
3359 class service(object):
3359 class service(object):
3360 def init(self):
3360 def init(self):
3361 util.set_signal_handler()
3361 util.set_signal_handler()
3362 self.httpd = hgweb.server.create_server(ui, app)
3362 self.httpd = hgweb.server.create_server(ui, app)
3363
3363
3364 if opts['port'] and not ui.verbose:
3364 if opts['port'] and not ui.verbose:
3365 return
3365 return
3366
3366
3367 if self.httpd.prefix:
3367 if self.httpd.prefix:
3368 prefix = self.httpd.prefix.strip('/') + '/'
3368 prefix = self.httpd.prefix.strip('/') + '/'
3369 else:
3369 else:
3370 prefix = ''
3370 prefix = ''
3371
3371
3372 port = ':%d' % self.httpd.port
3372 port = ':%d' % self.httpd.port
3373 if port == ':80':
3373 if port == ':80':
3374 port = ''
3374 port = ''
3375
3375
3376 bindaddr = self.httpd.addr
3376 bindaddr = self.httpd.addr
3377 if bindaddr == '0.0.0.0':
3377 if bindaddr == '0.0.0.0':
3378 bindaddr = '*'
3378 bindaddr = '*'
3379 elif ':' in bindaddr: # IPv6
3379 elif ':' in bindaddr: # IPv6
3380 bindaddr = '[%s]' % bindaddr
3380 bindaddr = '[%s]' % bindaddr
3381
3381
3382 fqaddr = self.httpd.fqaddr
3382 fqaddr = self.httpd.fqaddr
3383 if ':' in fqaddr:
3383 if ':' in fqaddr:
3384 fqaddr = '[%s]' % fqaddr
3384 fqaddr = '[%s]' % fqaddr
3385 if opts['port']:
3385 if opts['port']:
3386 write = ui.status
3386 write = ui.status
3387 else:
3387 else:
3388 write = ui.write
3388 write = ui.write
3389 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3389 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3390 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3390 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3391
3391
3392 def run(self):
3392 def run(self):
3393 self.httpd.serve_forever()
3393 self.httpd.serve_forever()
3394
3394
3395 service = service()
3395 service = service()
3396
3396
3397 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3397 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3398
3398
3399 def status(ui, repo, *pats, **opts):
3399 def status(ui, repo, *pats, **opts):
3400 """show changed files in the working directory
3400 """show changed files in the working directory
3401
3401
3402 Show status of files in the repository. If names are given, only
3402 Show status of files in the repository. If names are given, only
3403 files that match are shown. Files that are clean or ignored or
3403 files that match are shown. Files that are clean or ignored or
3404 the source of a copy/move operation, are not listed unless
3404 the source of a copy/move operation, are not listed unless
3405 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3405 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3406 Unless options described with "show only ..." are given, the
3406 Unless options described with "show only ..." are given, the
3407 options -mardu are used.
3407 options -mardu are used.
3408
3408
3409 Option -q/--quiet hides untracked (unknown and ignored) files
3409 Option -q/--quiet hides untracked (unknown and ignored) files
3410 unless explicitly requested with -u/--unknown or -i/--ignored.
3410 unless explicitly requested with -u/--unknown or -i/--ignored.
3411
3411
3412 NOTE: status may appear to disagree with diff if permissions have
3412 NOTE: status may appear to disagree with diff if permissions have
3413 changed or a merge has occurred. The standard diff format does not
3413 changed or a merge has occurred. The standard diff format does not
3414 report permission changes and diff only reports changes relative
3414 report permission changes and diff only reports changes relative
3415 to one merge parent.
3415 to one merge parent.
3416
3416
3417 If one revision is given, it is used as the base revision.
3417 If one revision is given, it is used as the base revision.
3418 If two revisions are given, the differences between them are
3418 If two revisions are given, the differences between them are
3419 shown. The --change option can also be used as a shortcut to list
3419 shown. The --change option can also be used as a shortcut to list
3420 the changed files of a revision from its first parent.
3420 the changed files of a revision from its first parent.
3421
3421
3422 The codes used to show the status of files are::
3422 The codes used to show the status of files are::
3423
3423
3424 M = modified
3424 M = modified
3425 A = added
3425 A = added
3426 R = removed
3426 R = removed
3427 C = clean
3427 C = clean
3428 ! = missing (deleted by non-hg command, but still tracked)
3428 ! = missing (deleted by non-hg command, but still tracked)
3429 ? = not tracked
3429 ? = not tracked
3430 I = ignored
3430 I = ignored
3431 = origin of the previous file listed as A (added)
3431 = origin of the previous file listed as A (added)
3432
3432
3433 Returns 0 on success.
3433 Returns 0 on success.
3434 """
3434 """
3435
3435
3436 revs = opts.get('rev')
3436 revs = opts.get('rev')
3437 change = opts.get('change')
3437 change = opts.get('change')
3438
3438
3439 if revs and change:
3439 if revs and change:
3440 msg = _('cannot specify --rev and --change at the same time')
3440 msg = _('cannot specify --rev and --change at the same time')
3441 raise util.Abort(msg)
3441 raise util.Abort(msg)
3442 elif change:
3442 elif change:
3443 node2 = repo.lookup(change)
3443 node2 = repo.lookup(change)
3444 node1 = repo[node2].parents()[0].node()
3444 node1 = repo[node2].parents()[0].node()
3445 else:
3445 else:
3446 node1, node2 = cmdutil.revpair(repo, revs)
3446 node1, node2 = cmdutil.revpair(repo, revs)
3447
3447
3448 cwd = (pats and repo.getcwd()) or ''
3448 cwd = (pats and repo.getcwd()) or ''
3449 end = opts.get('print0') and '\0' or '\n'
3449 end = opts.get('print0') and '\0' or '\n'
3450 copy = {}
3450 copy = {}
3451 states = 'modified added removed deleted unknown ignored clean'.split()
3451 states = 'modified added removed deleted unknown ignored clean'.split()
3452 show = [k for k in states if opts.get(k)]
3452 show = [k for k in states if opts.get(k)]
3453 if opts.get('all'):
3453 if opts.get('all'):
3454 show += ui.quiet and (states[:4] + ['clean']) or states
3454 show += ui.quiet and (states[:4] + ['clean']) or states
3455 if not show:
3455 if not show:
3456 show = ui.quiet and states[:4] or states[:5]
3456 show = ui.quiet and states[:4] or states[:5]
3457
3457
3458 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3458 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3459 'ignored' in show, 'clean' in show, 'unknown' in show)
3459 'ignored' in show, 'clean' in show, 'unknown' in show)
3460 changestates = zip(states, 'MAR!?IC', stat)
3460 changestates = zip(states, 'MAR!?IC', stat)
3461
3461
3462 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3462 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3463 ctxn = repo[nullid]
3463 ctxn = repo[nullid]
3464 ctx1 = repo[node1]
3464 ctx1 = repo[node1]
3465 ctx2 = repo[node2]
3465 ctx2 = repo[node2]
3466 added = stat[1]
3466 added = stat[1]
3467 if node2 is None:
3467 if node2 is None:
3468 added = stat[0] + stat[1] # merged?
3468 added = stat[0] + stat[1] # merged?
3469
3469
3470 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3470 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3471 if k in added:
3471 if k in added:
3472 copy[k] = v
3472 copy[k] = v
3473 elif v in added:
3473 elif v in added:
3474 copy[v] = k
3474 copy[v] = k
3475
3475
3476 for state, char, files in changestates:
3476 for state, char, files in changestates:
3477 if state in show:
3477 if state in show:
3478 format = "%s %%s%s" % (char, end)
3478 format = "%s %%s%s" % (char, end)
3479 if opts.get('no_status'):
3479 if opts.get('no_status'):
3480 format = "%%s%s" % end
3480 format = "%%s%s" % end
3481
3481
3482 for f in files:
3482 for f in files:
3483 ui.write(format % repo.pathto(f, cwd),
3483 ui.write(format % repo.pathto(f, cwd),
3484 label='status.' + state)
3484 label='status.' + state)
3485 if f in copy:
3485 if f in copy:
3486 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3486 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3487 label='status.copied')
3487 label='status.copied')
3488
3488
3489 def summary(ui, repo, **opts):
3489 def summary(ui, repo, **opts):
3490 """summarize working directory state
3490 """summarize working directory state
3491
3491
3492 This generates a brief summary of the working directory state,
3492 This generates a brief summary of the working directory state,
3493 including parents, branch, commit status, and available updates.
3493 including parents, branch, commit status, and available updates.
3494
3494
3495 With the --remote option, this will check the default paths for
3495 With the --remote option, this will check the default paths for
3496 incoming and outgoing changes. This can be time-consuming.
3496 incoming and outgoing changes. This can be time-consuming.
3497
3497
3498 Returns 0 on success.
3498 Returns 0 on success.
3499 """
3499 """
3500
3500
3501 ctx = repo[None]
3501 ctx = repo[None]
3502 parents = ctx.parents()
3502 parents = ctx.parents()
3503 pnode = parents[0].node()
3503 pnode = parents[0].node()
3504
3504
3505 for p in parents:
3505 for p in parents:
3506 # label with log.changeset (instead of log.parent) since this
3506 # label with log.changeset (instead of log.parent) since this
3507 # shows a working directory parent *changeset*:
3507 # shows a working directory parent *changeset*:
3508 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3508 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3509 label='log.changeset')
3509 label='log.changeset')
3510 ui.write(' '.join(p.tags()), label='log.tag')
3510 ui.write(' '.join(p.tags()), label='log.tag')
3511 if p.rev() == -1:
3511 if p.rev() == -1:
3512 if not len(repo):
3512 if not len(repo):
3513 ui.write(_(' (empty repository)'))
3513 ui.write(_(' (empty repository)'))
3514 else:
3514 else:
3515 ui.write(_(' (no revision checked out)'))
3515 ui.write(_(' (no revision checked out)'))
3516 ui.write('\n')
3516 ui.write('\n')
3517 if p.description():
3517 if p.description():
3518 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3518 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3519 label='log.summary')
3519 label='log.summary')
3520
3520
3521 branch = ctx.branch()
3521 branch = ctx.branch()
3522 bheads = repo.branchheads(branch)
3522 bheads = repo.branchheads(branch)
3523 m = _('branch: %s\n') % branch
3523 m = _('branch: %s\n') % branch
3524 if branch != 'default':
3524 if branch != 'default':
3525 ui.write(m, label='log.branch')
3525 ui.write(m, label='log.branch')
3526 else:
3526 else:
3527 ui.status(m, label='log.branch')
3527 ui.status(m, label='log.branch')
3528
3528
3529 st = list(repo.status(unknown=True))[:6]
3529 st = list(repo.status(unknown=True))[:6]
3530
3530
3531 c = repo.dirstate.copies()
3531 c = repo.dirstate.copies()
3532 copied, renamed = [], []
3532 copied, renamed = [], []
3533 for d, s in c.iteritems():
3533 for d, s in c.iteritems():
3534 if s in st[2]:
3534 if s in st[2]:
3535 st[2].remove(s)
3535 st[2].remove(s)
3536 renamed.append(d)
3536 renamed.append(d)
3537 else:
3537 else:
3538 copied.append(d)
3538 copied.append(d)
3539 if d in st[1]:
3539 if d in st[1]:
3540 st[1].remove(d)
3540 st[1].remove(d)
3541 st.insert(3, renamed)
3541 st.insert(3, renamed)
3542 st.insert(4, copied)
3542 st.insert(4, copied)
3543
3543
3544 ms = mergemod.mergestate(repo)
3544 ms = mergemod.mergestate(repo)
3545 st.append([f for f in ms if ms[f] == 'u'])
3545 st.append([f for f in ms if ms[f] == 'u'])
3546
3546
3547 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3547 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3548 st.append(subs)
3548 st.append(subs)
3549
3549
3550 labels = [ui.label(_('%d modified'), 'status.modified'),
3550 labels = [ui.label(_('%d modified'), 'status.modified'),
3551 ui.label(_('%d added'), 'status.added'),
3551 ui.label(_('%d added'), 'status.added'),
3552 ui.label(_('%d removed'), 'status.removed'),
3552 ui.label(_('%d removed'), 'status.removed'),
3553 ui.label(_('%d renamed'), 'status.copied'),
3553 ui.label(_('%d renamed'), 'status.copied'),
3554 ui.label(_('%d copied'), 'status.copied'),
3554 ui.label(_('%d copied'), 'status.copied'),
3555 ui.label(_('%d deleted'), 'status.deleted'),
3555 ui.label(_('%d deleted'), 'status.deleted'),
3556 ui.label(_('%d unknown'), 'status.unknown'),
3556 ui.label(_('%d unknown'), 'status.unknown'),
3557 ui.label(_('%d ignored'), 'status.ignored'),
3557 ui.label(_('%d ignored'), 'status.ignored'),
3558 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3558 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3559 ui.label(_('%d subrepos'), 'status.modified')]
3559 ui.label(_('%d subrepos'), 'status.modified')]
3560 t = []
3560 t = []
3561 for s, l in zip(st, labels):
3561 for s, l in zip(st, labels):
3562 if s:
3562 if s:
3563 t.append(l % len(s))
3563 t.append(l % len(s))
3564
3564
3565 t = ', '.join(t)
3565 t = ', '.join(t)
3566 cleanworkdir = False
3566 cleanworkdir = False
3567
3567
3568 if len(parents) > 1:
3568 if len(parents) > 1:
3569 t += _(' (merge)')
3569 t += _(' (merge)')
3570 elif branch != parents[0].branch():
3570 elif branch != parents[0].branch():
3571 t += _(' (new branch)')
3571 t += _(' (new branch)')
3572 elif (parents[0].extra().get('close') and
3572 elif (parents[0].extra().get('close') and
3573 pnode in repo.branchheads(branch, closed=True)):
3573 pnode in repo.branchheads(branch, closed=True)):
3574 t += _(' (head closed)')
3574 t += _(' (head closed)')
3575 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3575 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3576 t += _(' (clean)')
3576 t += _(' (clean)')
3577 cleanworkdir = True
3577 cleanworkdir = True
3578 elif pnode not in bheads:
3578 elif pnode not in bheads:
3579 t += _(' (new branch head)')
3579 t += _(' (new branch head)')
3580
3580
3581 if cleanworkdir:
3581 if cleanworkdir:
3582 ui.status(_('commit: %s\n') % t.strip())
3582 ui.status(_('commit: %s\n') % t.strip())
3583 else:
3583 else:
3584 ui.write(_('commit: %s\n') % t.strip())
3584 ui.write(_('commit: %s\n') % t.strip())
3585
3585
3586 # all ancestors of branch heads - all ancestors of parent = new csets
3586 # all ancestors of branch heads - all ancestors of parent = new csets
3587 new = [0] * len(repo)
3587 new = [0] * len(repo)
3588 cl = repo.changelog
3588 cl = repo.changelog
3589 for a in [cl.rev(n) for n in bheads]:
3589 for a in [cl.rev(n) for n in bheads]:
3590 new[a] = 1
3590 new[a] = 1
3591 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3591 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3592 new[a] = 1
3592 new[a] = 1
3593 for a in [p.rev() for p in parents]:
3593 for a in [p.rev() for p in parents]:
3594 if a >= 0:
3594 if a >= 0:
3595 new[a] = 0
3595 new[a] = 0
3596 for a in cl.ancestors(*[p.rev() for p in parents]):
3596 for a in cl.ancestors(*[p.rev() for p in parents]):
3597 new[a] = 0
3597 new[a] = 0
3598 new = sum(new)
3598 new = sum(new)
3599
3599
3600 if new == 0:
3600 if new == 0:
3601 ui.status(_('update: (current)\n'))
3601 ui.status(_('update: (current)\n'))
3602 elif pnode not in bheads:
3602 elif pnode not in bheads:
3603 ui.write(_('update: %d new changesets (update)\n') % new)
3603 ui.write(_('update: %d new changesets (update)\n') % new)
3604 else:
3604 else:
3605 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3605 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3606 (new, len(bheads)))
3606 (new, len(bheads)))
3607
3607
3608 if opts.get('remote'):
3608 if opts.get('remote'):
3609 t = []
3609 t = []
3610 source, branches = hg.parseurl(ui.expandpath('default'))
3610 source, branches = hg.parseurl(ui.expandpath('default'))
3611 other = hg.repository(hg.remoteui(repo, {}), source)
3611 other = hg.repository(hg.remoteui(repo, {}), source)
3612 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3612 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3613 ui.debug('comparing with %s\n' % url.hidepassword(source))
3613 ui.debug('comparing with %s\n' % url.hidepassword(source))
3614 repo.ui.pushbuffer()
3614 repo.ui.pushbuffer()
3615 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3615 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3616 repo.ui.popbuffer()
3616 repo.ui.popbuffer()
3617 if incoming:
3617 if incoming:
3618 t.append(_('1 or more incoming'))
3618 t.append(_('1 or more incoming'))
3619
3619
3620 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3620 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3621 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3621 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3622 other = hg.repository(hg.remoteui(repo, {}), dest)
3622 other = hg.repository(hg.remoteui(repo, {}), dest)
3623 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3623 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3624 repo.ui.pushbuffer()
3624 repo.ui.pushbuffer()
3625 o = discovery.findoutgoing(repo, other)
3625 o = discovery.findoutgoing(repo, other)
3626 repo.ui.popbuffer()
3626 repo.ui.popbuffer()
3627 o = repo.changelog.nodesbetween(o, None)[0]
3627 o = repo.changelog.nodesbetween(o, None)[0]
3628 if o:
3628 if o:
3629 t.append(_('%d outgoing') % len(o))
3629 t.append(_('%d outgoing') % len(o))
3630
3630
3631 if t:
3631 if t:
3632 ui.write(_('remote: %s\n') % (', '.join(t)))
3632 ui.write(_('remote: %s\n') % (', '.join(t)))
3633 else:
3633 else:
3634 ui.status(_('remote: (synced)\n'))
3634 ui.status(_('remote: (synced)\n'))
3635
3635
3636 def tag(ui, repo, name1, *names, **opts):
3636 def tag(ui, repo, name1, *names, **opts):
3637 """add one or more tags for the current or given revision
3637 """add one or more tags for the current or given revision
3638
3638
3639 Name a particular revision using <name>.
3639 Name a particular revision using <name>.
3640
3640
3641 Tags are used to name particular revisions of the repository and are
3641 Tags are used to name particular revisions of the repository and are
3642 very useful to compare different revisions, to go back to significant
3642 very useful to compare different revisions, to go back to significant
3643 earlier versions or to mark branch points as releases, etc.
3643 earlier versions or to mark branch points as releases, etc.
3644
3644
3645 If no revision is given, the parent of the working directory is
3645 If no revision is given, the parent of the working directory is
3646 used, or tip if no revision is checked out.
3646 used, or tip if no revision is checked out.
3647
3647
3648 To facilitate version control, distribution, and merging of tags,
3648 To facilitate version control, distribution, and merging of tags,
3649 they are stored as a file named ".hgtags" which is managed
3649 they are stored as a file named ".hgtags" which is managed
3650 similarly to other project files and can be hand-edited if
3650 similarly to other project files and can be hand-edited if
3651 necessary. The file '.hg/localtags' is used for local tags (not
3651 necessary. The file '.hg/localtags' is used for local tags (not
3652 shared among repositories).
3652 shared among repositories).
3653
3653
3654 See :hg:`help dates` for a list of formats valid for -d/--date.
3654 See :hg:`help dates` for a list of formats valid for -d/--date.
3655
3655
3656 Since tag names have priority over branch names during revision
3656 Since tag names have priority over branch names during revision
3657 lookup, using an existing branch name as a tag name is discouraged.
3657 lookup, using an existing branch name as a tag name is discouraged.
3658
3658
3659 Returns 0 on success.
3659 Returns 0 on success.
3660 """
3660 """
3661
3661
3662 rev_ = "."
3662 rev_ = "."
3663 names = [t.strip() for t in (name1,) + names]
3663 names = [t.strip() for t in (name1,) + names]
3664 if len(names) != len(set(names)):
3664 if len(names) != len(set(names)):
3665 raise util.Abort(_('tag names must be unique'))
3665 raise util.Abort(_('tag names must be unique'))
3666 for n in names:
3666 for n in names:
3667 if n in ['tip', '.', 'null']:
3667 if n in ['tip', '.', 'null']:
3668 raise util.Abort(_('the name \'%s\' is reserved') % n)
3668 raise util.Abort(_('the name \'%s\' is reserved') % n)
3669 if not n:
3669 if not n:
3670 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3670 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3671 if opts.get('rev') and opts.get('remove'):
3671 if opts.get('rev') and opts.get('remove'):
3672 raise util.Abort(_("--rev and --remove are incompatible"))
3672 raise util.Abort(_("--rev and --remove are incompatible"))
3673 if opts.get('rev'):
3673 if opts.get('rev'):
3674 rev_ = opts['rev']
3674 rev_ = opts['rev']
3675 message = opts.get('message')
3675 message = opts.get('message')
3676 if opts.get('remove'):
3676 if opts.get('remove'):
3677 expectedtype = opts.get('local') and 'local' or 'global'
3677 expectedtype = opts.get('local') and 'local' or 'global'
3678 for n in names:
3678 for n in names:
3679 if not repo.tagtype(n):
3679 if not repo.tagtype(n):
3680 raise util.Abort(_('tag \'%s\' does not exist') % n)
3680 raise util.Abort(_('tag \'%s\' does not exist') % n)
3681 if repo.tagtype(n) != expectedtype:
3681 if repo.tagtype(n) != expectedtype:
3682 if expectedtype == 'global':
3682 if expectedtype == 'global':
3683 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3683 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3684 else:
3684 else:
3685 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3685 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3686 rev_ = nullid
3686 rev_ = nullid
3687 if not message:
3687 if not message:
3688 # we don't translate commit messages
3688 # we don't translate commit messages
3689 message = 'Removed tag %s' % ', '.join(names)
3689 message = 'Removed tag %s' % ', '.join(names)
3690 elif not opts.get('force'):
3690 elif not opts.get('force'):
3691 for n in names:
3691 for n in names:
3692 if n in repo.tags():
3692 if n in repo.tags():
3693 raise util.Abort(_('tag \'%s\' already exists '
3693 raise util.Abort(_('tag \'%s\' already exists '
3694 '(use -f to force)') % n)
3694 '(use -f to force)') % n)
3695 if not rev_ and repo.dirstate.parents()[1] != nullid:
3695 if not rev_ and repo.dirstate.parents()[1] != nullid:
3696 raise util.Abort(_('uncommitted merge - please provide a '
3696 raise util.Abort(_('uncommitted merge - please provide a '
3697 'specific revision'))
3697 'specific revision'))
3698 r = repo[rev_].node()
3698 r = repo[rev_].node()
3699
3699
3700 if not message:
3700 if not message:
3701 # we don't translate commit messages
3701 # we don't translate commit messages
3702 message = ('Added tag %s for changeset %s' %
3702 message = ('Added tag %s for changeset %s' %
3703 (', '.join(names), short(r)))
3703 (', '.join(names), short(r)))
3704
3704
3705 date = opts.get('date')
3705 date = opts.get('date')
3706 if date:
3706 if date:
3707 date = util.parsedate(date)
3707 date = util.parsedate(date)
3708
3708
3709 if opts.get('edit'):
3709 if opts.get('edit'):
3710 message = ui.edit(message, ui.username())
3710 message = ui.edit(message, ui.username())
3711
3711
3712 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3712 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3713
3713
3714 def tags(ui, repo):
3714 def tags(ui, repo):
3715 """list repository tags
3715 """list repository tags
3716
3716
3717 This lists both regular and local tags. When the -v/--verbose
3717 This lists both regular and local tags. When the -v/--verbose
3718 switch is used, a third column "local" is printed for local tags.
3718 switch is used, a third column "local" is printed for local tags.
3719
3719
3720 Returns 0 on success.
3720 Returns 0 on success.
3721 """
3721 """
3722
3722
3723 hexfunc = ui.debugflag and hex or short
3723 hexfunc = ui.debugflag and hex or short
3724 tagtype = ""
3724 tagtype = ""
3725
3725
3726 for t, n in reversed(repo.tagslist()):
3726 for t, n in reversed(repo.tagslist()):
3727 if ui.quiet:
3727 if ui.quiet:
3728 ui.write("%s\n" % t)
3728 ui.write("%s\n" % t)
3729 continue
3729 continue
3730
3730
3731 try:
3731 try:
3732 hn = hexfunc(n)
3732 hn = hexfunc(n)
3733 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3733 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3734 except error.LookupError:
3734 except error.LookupError:
3735 r = " ?:%s" % hn
3735 r = " ?:%s" % hn
3736 else:
3736 else:
3737 spaces = " " * (30 - encoding.colwidth(t))
3737 spaces = " " * (30 - encoding.colwidth(t))
3738 if ui.verbose:
3738 if ui.verbose:
3739 if repo.tagtype(t) == 'local':
3739 if repo.tagtype(t) == 'local':
3740 tagtype = " local"
3740 tagtype = " local"
3741 else:
3741 else:
3742 tagtype = ""
3742 tagtype = ""
3743 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3743 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3744
3744
3745 def tip(ui, repo, **opts):
3745 def tip(ui, repo, **opts):
3746 """show the tip revision
3746 """show the tip revision
3747
3747
3748 The tip revision (usually just called the tip) is the changeset
3748 The tip revision (usually just called the tip) is the changeset
3749 most recently added to the repository (and therefore the most
3749 most recently added to the repository (and therefore the most
3750 recently changed head).
3750 recently changed head).
3751
3751
3752 If you have just made a commit, that commit will be the tip. If
3752 If you have just made a commit, that commit will be the tip. If
3753 you have just pulled changes from another repository, the tip of
3753 you have just pulled changes from another repository, the tip of
3754 that repository becomes the current tip. The "tip" tag is special
3754 that repository becomes the current tip. The "tip" tag is special
3755 and cannot be renamed or assigned to a different changeset.
3755 and cannot be renamed or assigned to a different changeset.
3756
3756
3757 Returns 0 on success.
3757 Returns 0 on success.
3758 """
3758 """
3759 displayer = cmdutil.show_changeset(ui, repo, opts)
3759 displayer = cmdutil.show_changeset(ui, repo, opts)
3760 displayer.show(repo[len(repo) - 1])
3760 displayer.show(repo[len(repo) - 1])
3761 displayer.close()
3761 displayer.close()
3762
3762
3763 def unbundle(ui, repo, fname1, *fnames, **opts):
3763 def unbundle(ui, repo, fname1, *fnames, **opts):
3764 """apply one or more changegroup files
3764 """apply one or more changegroup files
3765
3765
3766 Apply one or more compressed changegroup files generated by the
3766 Apply one or more compressed changegroup files generated by the
3767 bundle command.
3767 bundle command.
3768
3768
3769 Returns 0 on success, 1 if an update has unresolved files.
3769 Returns 0 on success, 1 if an update has unresolved files.
3770 """
3770 """
3771 fnames = (fname1,) + fnames
3771 fnames = (fname1,) + fnames
3772
3772
3773 lock = repo.lock()
3773 lock = repo.lock()
3774 try:
3774 try:
3775 for fname in fnames:
3775 for fname in fnames:
3776 f = url.open(ui, fname)
3776 f = url.open(ui, fname)
3777 gen = changegroup.readbundle(f, fname)
3777 gen = changegroup.readbundle(f, fname)
3778 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3778 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3779 lock=lock)
3779 lock=lock)
3780 finally:
3780 finally:
3781 lock.release()
3781 lock.release()
3782
3782
3783 return postincoming(ui, repo, modheads, opts.get('update'), None)
3783 return postincoming(ui, repo, modheads, opts.get('update'), None)
3784
3784
3785 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3785 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3786 """update working directory (or switch revisions)
3786 """update working directory (or switch revisions)
3787
3787
3788 Update the repository's working directory to the specified
3788 Update the repository's working directory to the specified
3789 changeset.
3789 changeset.
3790
3790
3791 If no changeset is specified, attempt to update to the tip of the
3791 If no changeset is specified, attempt to update to the tip of the
3792 current branch. If this changeset is a descendant of the working
3792 current branch. If this changeset is a descendant of the working
3793 directory's parent, update to it, otherwise abort.
3793 directory's parent, update to it, otherwise abort.
3794
3794
3795 The following rules apply when the working directory contains
3795 The following rules apply when the working directory contains
3796 uncommitted changes:
3796 uncommitted changes:
3797
3797
3798 1. If neither -c/--check nor -C/--clean is specified, and if
3798 1. If neither -c/--check nor -C/--clean is specified, and if
3799 the requested changeset is an ancestor or descendant of
3799 the requested changeset is an ancestor or descendant of
3800 the working directory's parent, the uncommitted changes
3800 the working directory's parent, the uncommitted changes
3801 are merged into the requested changeset and the merged
3801 are merged into the requested changeset and the merged
3802 result is left uncommitted. If the requested changeset is
3802 result is left uncommitted. If the requested changeset is
3803 not an ancestor or descendant (that is, it is on another
3803 not an ancestor or descendant (that is, it is on another
3804 branch), the update is aborted and the uncommitted changes
3804 branch), the update is aborted and the uncommitted changes
3805 are preserved.
3805 are preserved.
3806
3806
3807 2. With the -c/--check option, the update is aborted and the
3807 2. With the -c/--check option, the update is aborted and the
3808 uncommitted changes are preserved.
3808 uncommitted changes are preserved.
3809
3809
3810 3. With the -C/--clean option, uncommitted changes are discarded and
3810 3. With the -C/--clean option, uncommitted changes are discarded and
3811 the working directory is updated to the requested changeset.
3811 the working directory is updated to the requested changeset.
3812
3812
3813 Use null as the changeset to remove the working directory (like
3813 Use null as the changeset to remove the working directory (like
3814 :hg:`clone -U`).
3814 :hg:`clone -U`).
3815
3815
3816 If you want to update just one file to an older changeset, use :hg:`revert`.
3816 If you want to update just one file to an older changeset, use :hg:`revert`.
3817
3817
3818 See :hg:`help dates` for a list of formats valid for -d/--date.
3818 See :hg:`help dates` for a list of formats valid for -d/--date.
3819
3819
3820 Returns 0 on success, 1 if there are unresolved files.
3820 Returns 0 on success, 1 if there are unresolved files.
3821 """
3821 """
3822 if rev and node:
3822 if rev and node:
3823 raise util.Abort(_("please specify just one revision"))
3823 raise util.Abort(_("please specify just one revision"))
3824
3824
3825 if not rev:
3825 if not rev:
3826 rev = node
3826 rev = node
3827
3827
3828 if check and clean:
3828 if check and clean:
3829 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3829 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3830
3830
3831 if check:
3831 if check:
3832 # we could use dirty() but we can ignore merge and branch trivia
3832 # we could use dirty() but we can ignore merge and branch trivia
3833 c = repo[None]
3833 c = repo[None]
3834 if c.modified() or c.added() or c.removed():
3834 if c.modified() or c.added() or c.removed():
3835 raise util.Abort(_("uncommitted local changes"))
3835 raise util.Abort(_("uncommitted local changes"))
3836
3836
3837 if date:
3837 if date:
3838 if rev:
3838 if rev:
3839 raise util.Abort(_("you can't specify a revision and a date"))
3839 raise util.Abort(_("you can't specify a revision and a date"))
3840 rev = cmdutil.finddate(ui, repo, date)
3840 rev = cmdutil.finddate(ui, repo, date)
3841
3841
3842 if clean or check:
3842 if clean or check:
3843 return hg.clean(repo, rev)
3843 return hg.clean(repo, rev)
3844 else:
3844 else:
3845 return hg.update(repo, rev)
3845 return hg.update(repo, rev)
3846
3846
3847 def verify(ui, repo):
3847 def verify(ui, repo):
3848 """verify the integrity of the repository
3848 """verify the integrity of the repository
3849
3849
3850 Verify the integrity of the current repository.
3850 Verify the integrity of the current repository.
3851
3851
3852 This will perform an extensive check of the repository's
3852 This will perform an extensive check of the repository's
3853 integrity, validating the hashes and checksums of each entry in
3853 integrity, validating the hashes and checksums of each entry in
3854 the changelog, manifest, and tracked files, as well as the
3854 the changelog, manifest, and tracked files, as well as the
3855 integrity of their crosslinks and indices.
3855 integrity of their crosslinks and indices.
3856
3856
3857 Returns 0 on success, 1 if errors are encountered.
3857 Returns 0 on success, 1 if errors are encountered.
3858 """
3858 """
3859 return hg.verify(repo)
3859 return hg.verify(repo)
3860
3860
3861 def version_(ui):
3861 def version_(ui):
3862 """output version and copyright information"""
3862 """output version and copyright information"""
3863 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3863 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3864 % util.version())
3864 % util.version())
3865 ui.status(_(
3865 ui.status(_(
3866 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3866 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3867 "This is free software; see the source for copying conditions. "
3867 "This is free software; see the source for copying conditions. "
3868 "There is NO\nwarranty; "
3868 "There is NO\nwarranty; "
3869 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3869 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3870 ))
3870 ))
3871
3871
3872 # Command options and aliases are listed here, alphabetically
3872 # Command options and aliases are listed here, alphabetically
3873
3873
3874 globalopts = [
3874 globalopts = [
3875 ('R', 'repository', '',
3875 ('R', 'repository', '',
3876 _('repository root directory or name of overlay bundle file'),
3876 _('repository root directory or name of overlay bundle file'),
3877 _('REPO')),
3877 _('REPO')),
3878 ('', 'cwd', '',
3878 ('', 'cwd', '',
3879 _('change working directory'), _('DIR')),
3879 _('change working directory'), _('DIR')),
3880 ('y', 'noninteractive', None,
3880 ('y', 'noninteractive', None,
3881 _('do not prompt, assume \'yes\' for any required answers')),
3881 _('do not prompt, assume \'yes\' for any required answers')),
3882 ('q', 'quiet', None, _('suppress output')),
3882 ('q', 'quiet', None, _('suppress output')),
3883 ('v', 'verbose', None, _('enable additional output')),
3883 ('v', 'verbose', None, _('enable additional output')),
3884 ('', 'config', [],
3884 ('', 'config', [],
3885 _('set/override config option (use \'section.name=value\')'),
3885 _('set/override config option (use \'section.name=value\')'),
3886 _('CONFIG')),
3886 _('CONFIG')),
3887 ('', 'debug', None, _('enable debugging output')),
3887 ('', 'debug', None, _('enable debugging output')),
3888 ('', 'debugger', None, _('start debugger')),
3888 ('', 'debugger', None, _('start debugger')),
3889 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3889 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3890 _('ENCODE')),
3890 _('ENCODE')),
3891 ('', 'encodingmode', encoding.encodingmode,
3891 ('', 'encodingmode', encoding.encodingmode,
3892 _('set the charset encoding mode'), _('MODE')),
3892 _('set the charset encoding mode'), _('MODE')),
3893 ('', 'traceback', None, _('always print a traceback on exception')),
3893 ('', 'traceback', None, _('always print a traceback on exception')),
3894 ('', 'time', None, _('time how long the command takes')),
3894 ('', 'time', None, _('time how long the command takes')),
3895 ('', 'profile', None, _('print command execution profile')),
3895 ('', 'profile', None, _('print command execution profile')),
3896 ('', 'version', None, _('output version information and exit')),
3896 ('', 'version', None, _('output version information and exit')),
3897 ('h', 'help', None, _('display help and exit')),
3897 ('h', 'help', None, _('display help and exit')),
3898 ]
3898 ]
3899
3899
3900 dryrunopts = [('n', 'dry-run', None,
3900 dryrunopts = [('n', 'dry-run', None,
3901 _('do not perform actions, just print output'))]
3901 _('do not perform actions, just print output'))]
3902
3902
3903 remoteopts = [
3903 remoteopts = [
3904 ('e', 'ssh', '',
3904 ('e', 'ssh', '',
3905 _('specify ssh command to use'), _('CMD')),
3905 _('specify ssh command to use'), _('CMD')),
3906 ('', 'remotecmd', '',
3906 ('', 'remotecmd', '',
3907 _('specify hg command to run on the remote side'), _('CMD')),
3907 _('specify hg command to run on the remote side'), _('CMD')),
3908 ]
3908 ]
3909
3909
3910 walkopts = [
3910 walkopts = [
3911 ('I', 'include', [],
3911 ('I', 'include', [],
3912 _('include names matching the given patterns'), _('PATTERN')),
3912 _('include names matching the given patterns'), _('PATTERN')),
3913 ('X', 'exclude', [],
3913 ('X', 'exclude', [],
3914 _('exclude names matching the given patterns'), _('PATTERN')),
3914 _('exclude names matching the given patterns'), _('PATTERN')),
3915 ]
3915 ]
3916
3916
3917 commitopts = [
3917 commitopts = [
3918 ('m', 'message', '',
3918 ('m', 'message', '',
3919 _('use text as commit message'), _('TEXT')),
3919 _('use text as commit message'), _('TEXT')),
3920 ('l', 'logfile', '',
3920 ('l', 'logfile', '',
3921 _('read commit message from file'), _('FILE')),
3921 _('read commit message from file'), _('FILE')),
3922 ]
3922 ]
3923
3923
3924 commitopts2 = [
3924 commitopts2 = [
3925 ('d', 'date', '',
3925 ('d', 'date', '',
3926 _('record datecode as commit date'), _('DATE')),
3926 _('record datecode as commit date'), _('DATE')),
3927 ('u', 'user', '',
3927 ('u', 'user', '',
3928 _('record the specified user as committer'), _('USER')),
3928 _('record the specified user as committer'), _('USER')),
3929 ]
3929 ]
3930
3930
3931 templateopts = [
3931 templateopts = [
3932 ('', 'style', '',
3932 ('', 'style', '',
3933 _('display using template map file'), _('STYLE')),
3933 _('display using template map file'), _('STYLE')),
3934 ('', 'template', '',
3934 ('', 'template', '',
3935 _('display with template'), _('TEMPLATE')),
3935 _('display with template'), _('TEMPLATE')),
3936 ]
3936 ]
3937
3937
3938 logopts = [
3938 logopts = [
3939 ('p', 'patch', None, _('show patch')),
3939 ('p', 'patch', None, _('show patch')),
3940 ('g', 'git', None, _('use git extended diff format')),
3940 ('g', 'git', None, _('use git extended diff format')),
3941 ('l', 'limit', '',
3941 ('l', 'limit', '',
3942 _('limit number of changes displayed'), _('NUM')),
3942 _('limit number of changes displayed'), _('NUM')),
3943 ('M', 'no-merges', None, _('do not show merges')),
3943 ('M', 'no-merges', None, _('do not show merges')),
3944 ('', 'stat', None, _('output diffstat-style summary of changes')),
3944 ('', 'stat', None, _('output diffstat-style summary of changes')),
3945 ] + templateopts
3945 ] + templateopts
3946
3946
3947 diffopts = [
3947 diffopts = [
3948 ('a', 'text', None, _('treat all files as text')),
3948 ('a', 'text', None, _('treat all files as text')),
3949 ('g', 'git', None, _('use git extended diff format')),
3949 ('g', 'git', None, _('use git extended diff format')),
3950 ('', 'nodates', None, _('omit dates from diff headers'))
3950 ('', 'nodates', None, _('omit dates from diff headers'))
3951 ]
3951 ]
3952
3952
3953 diffopts2 = [
3953 diffopts2 = [
3954 ('p', 'show-function', None, _('show which function each change is in')),
3954 ('p', 'show-function', None, _('show which function each change is in')),
3955 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3955 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3956 ('w', 'ignore-all-space', None,
3956 ('w', 'ignore-all-space', None,
3957 _('ignore white space when comparing lines')),
3957 _('ignore white space when comparing lines')),
3958 ('b', 'ignore-space-change', None,
3958 ('b', 'ignore-space-change', None,
3959 _('ignore changes in the amount of white space')),
3959 _('ignore changes in the amount of white space')),
3960 ('B', 'ignore-blank-lines', None,
3960 ('B', 'ignore-blank-lines', None,
3961 _('ignore changes whose lines are all blank')),
3961 _('ignore changes whose lines are all blank')),
3962 ('U', 'unified', '',
3962 ('U', 'unified', '',
3963 _('number of lines of context to show'), _('NUM')),
3963 _('number of lines of context to show'), _('NUM')),
3964 ('', 'stat', None, _('output diffstat-style summary of changes')),
3964 ('', 'stat', None, _('output diffstat-style summary of changes')),
3965 ]
3965 ]
3966
3966
3967 similarityopts = [
3967 similarityopts = [
3968 ('s', 'similarity', '',
3968 ('s', 'similarity', '',
3969 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3969 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3970 ]
3970 ]
3971
3971
3972 table = {
3972 table = {
3973 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3973 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3974 "addremove":
3974 "addremove":
3975 (addremove, similarityopts + walkopts + dryrunopts,
3975 (addremove, similarityopts + walkopts + dryrunopts,
3976 _('[OPTION]... [FILE]...')),
3976 _('[OPTION]... [FILE]...')),
3977 "^annotate|blame":
3977 "^annotate|blame":
3978 (annotate,
3978 (annotate,
3979 [('r', 'rev', '',
3979 [('r', 'rev', '',
3980 _('annotate the specified revision'), _('REV')),
3980 _('annotate the specified revision'), _('REV')),
3981 ('', 'follow', None,
3981 ('', 'follow', None,
3982 _('follow copies/renames and list the filename (DEPRECATED)')),
3982 _('follow copies/renames and list the filename (DEPRECATED)')),
3983 ('', 'no-follow', None, _("don't follow copies and renames")),
3983 ('', 'no-follow', None, _("don't follow copies and renames")),
3984 ('a', 'text', None, _('treat all files as text')),
3984 ('a', 'text', None, _('treat all files as text')),
3985 ('u', 'user', None, _('list the author (long with -v)')),
3985 ('u', 'user', None, _('list the author (long with -v)')),
3986 ('f', 'file', None, _('list the filename')),
3986 ('f', 'file', None, _('list the filename')),
3987 ('d', 'date', None, _('list the date (short with -q)')),
3987 ('d', 'date', None, _('list the date (short with -q)')),
3988 ('n', 'number', None, _('list the revision number (default)')),
3988 ('n', 'number', None, _('list the revision number (default)')),
3989 ('c', 'changeset', None, _('list the changeset')),
3989 ('c', 'changeset', None, _('list the changeset')),
3990 ('l', 'line-number', None,
3990 ('l', 'line-number', None,
3991 _('show line number at the first appearance'))
3991 _('show line number at the first appearance'))
3992 ] + walkopts,
3992 ] + walkopts,
3993 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3993 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3994 "archive":
3994 "archive":
3995 (archive,
3995 (archive,
3996 [('', 'no-decode', None, _('do not pass files through decoders')),
3996 [('', 'no-decode', None, _('do not pass files through decoders')),
3997 ('p', 'prefix', '',
3997 ('p', 'prefix', '',
3998 _('directory prefix for files in archive'), _('PREFIX')),
3998 _('directory prefix for files in archive'), _('PREFIX')),
3999 ('r', 'rev', '',
3999 ('r', 'rev', '',
4000 _('revision to distribute'), _('REV')),
4000 _('revision to distribute'), _('REV')),
4001 ('t', 'type', '',
4001 ('t', 'type', '',
4002 _('type of distribution to create'), _('TYPE')),
4002 _('type of distribution to create'), _('TYPE')),
4003 ] + walkopts,
4003 ] + walkopts,
4004 _('[OPTION]... DEST')),
4004 _('[OPTION]... DEST')),
4005 "backout":
4005 "backout":
4006 (backout,
4006 (backout,
4007 [('', 'merge', None,
4007 [('', 'merge', None,
4008 _('merge with old dirstate parent after backout')),
4008 _('merge with old dirstate parent after backout')),
4009 ('', 'parent', '',
4009 ('', 'parent', '',
4010 _('parent to choose when backing out merge'), _('REV')),
4010 _('parent to choose when backing out merge'), _('REV')),
4011 ('r', 'rev', '',
4011 ('r', 'rev', '',
4012 _('revision to backout'), _('REV')),
4012 _('revision to backout'), _('REV')),
4013 ] + walkopts + commitopts + commitopts2,
4013 ] + walkopts + commitopts + commitopts2,
4014 _('[OPTION]... [-r] REV')),
4014 _('[OPTION]... [-r] REV')),
4015 "bisect":
4015 "bisect":
4016 (bisect,
4016 (bisect,
4017 [('r', 'reset', False, _('reset bisect state')),
4017 [('r', 'reset', False, _('reset bisect state')),
4018 ('g', 'good', False, _('mark changeset good')),
4018 ('g', 'good', False, _('mark changeset good')),
4019 ('b', 'bad', False, _('mark changeset bad')),
4019 ('b', 'bad', False, _('mark changeset bad')),
4020 ('s', 'skip', False, _('skip testing changeset')),
4020 ('s', 'skip', False, _('skip testing changeset')),
4021 ('c', 'command', '',
4021 ('c', 'command', '',
4022 _('use command to check changeset state'), _('CMD')),
4022 _('use command to check changeset state'), _('CMD')),
4023 ('U', 'noupdate', False, _('do not update to target'))],
4023 ('U', 'noupdate', False, _('do not update to target'))],
4024 _("[-gbsr] [-U] [-c CMD] [REV]")),
4024 _("[-gbsr] [-U] [-c CMD] [REV]")),
4025 "branch":
4025 "branch":
4026 (branch,
4026 (branch,
4027 [('f', 'force', None,
4027 [('f', 'force', None,
4028 _('set branch name even if it shadows an existing branch')),
4028 _('set branch name even if it shadows an existing branch')),
4029 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4029 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4030 _('[-fC] [NAME]')),
4030 _('[-fC] [NAME]')),
4031 "branches":
4031 "branches":
4032 (branches,
4032 (branches,
4033 [('a', 'active', False,
4033 [('a', 'active', False,
4034 _('show only branches that have unmerged heads')),
4034 _('show only branches that have unmerged heads')),
4035 ('c', 'closed', False,
4035 ('c', 'closed', False,
4036 _('show normal and closed branches'))],
4036 _('show normal and closed branches'))],
4037 _('[-ac]')),
4037 _('[-ac]')),
4038 "bundle":
4038 "bundle":
4039 (bundle,
4039 (bundle,
4040 [('f', 'force', None,
4040 [('f', 'force', None,
4041 _('run even when the destination is unrelated')),
4041 _('run even when the destination is unrelated')),
4042 ('r', 'rev', [],
4042 ('r', 'rev', [],
4043 _('a changeset intended to be added to the destination'),
4043 _('a changeset intended to be added to the destination'),
4044 _('REV')),
4044 _('REV')),
4045 ('b', 'branch', [],
4045 ('b', 'branch', [],
4046 _('a specific branch you would like to bundle'),
4046 _('a specific branch you would like to bundle'),
4047 _('BRANCH')),
4047 _('BRANCH')),
4048 ('', 'base', [],
4048 ('', 'base', [],
4049 _('a base changeset assumed to be available at the destination'),
4049 _('a base changeset assumed to be available at the destination'),
4050 _('REV')),
4050 _('REV')),
4051 ('a', 'all', None, _('bundle all changesets in the repository')),
4051 ('a', 'all', None, _('bundle all changesets in the repository')),
4052 ('t', 'type', 'bzip2',
4052 ('t', 'type', 'bzip2',
4053 _('bundle compression type to use'), _('TYPE')),
4053 _('bundle compression type to use'), _('TYPE')),
4054 ] + remoteopts,
4054 ] + remoteopts,
4055 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4055 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4056 "cat":
4056 "cat":
4057 (cat,
4057 (cat,
4058 [('o', 'output', '',
4058 [('o', 'output', '',
4059 _('print output to file with formatted name'), _('FORMAT')),
4059 _('print output to file with formatted name'), _('FORMAT')),
4060 ('r', 'rev', '',
4060 ('r', 'rev', '',
4061 _('print the given revision'), _('REV')),
4061 _('print the given revision'), _('REV')),
4062 ('', 'decode', None, _('apply any matching decode filter')),
4062 ('', 'decode', None, _('apply any matching decode filter')),
4063 ] + walkopts,
4063 ] + walkopts,
4064 _('[OPTION]... FILE...')),
4064 _('[OPTION]... FILE...')),
4065 "^clone":
4065 "^clone":
4066 (clone,
4066 (clone,
4067 [('U', 'noupdate', None,
4067 [('U', 'noupdate', None,
4068 _('the clone will include an empty working copy (only a repository)')),
4068 _('the clone will include an empty working copy (only a repository)')),
4069 ('u', 'updaterev', '',
4069 ('u', 'updaterev', '',
4070 _('revision, tag or branch to check out'), _('REV')),
4070 _('revision, tag or branch to check out'), _('REV')),
4071 ('r', 'rev', [],
4071 ('r', 'rev', [],
4072 _('include the specified changeset'), _('REV')),
4072 _('include the specified changeset'), _('REV')),
4073 ('b', 'branch', [],
4073 ('b', 'branch', [],
4074 _('clone only the specified branch'), _('BRANCH')),
4074 _('clone only the specified branch'), _('BRANCH')),
4075 ('', 'pull', None, _('use pull protocol to copy metadata')),
4075 ('', 'pull', None, _('use pull protocol to copy metadata')),
4076 ('', 'uncompressed', None,
4076 ('', 'uncompressed', None,
4077 _('use uncompressed transfer (fast over LAN)')),
4077 _('use uncompressed transfer (fast over LAN)')),
4078 ] + remoteopts,
4078 ] + remoteopts,
4079 _('[OPTION]... SOURCE [DEST]')),
4079 _('[OPTION]... SOURCE [DEST]')),
4080 "^commit|ci":
4080 "^commit|ci":
4081 (commit,
4081 (commit,
4082 [('A', 'addremove', None,
4082 [('A', 'addremove', None,
4083 _('mark new/missing files as added/removed before committing')),
4083 _('mark new/missing files as added/removed before committing')),
4084 ('', 'close-branch', None,
4084 ('', 'close-branch', None,
4085 _('mark a branch as closed, hiding it from the branch list')),
4085 _('mark a branch as closed, hiding it from the branch list')),
4086 ] + walkopts + commitopts + commitopts2,
4086 ] + walkopts + commitopts + commitopts2,
4087 _('[OPTION]... [FILE]...')),
4087 _('[OPTION]... [FILE]...')),
4088 "copy|cp":
4088 "copy|cp":
4089 (copy,
4089 (copy,
4090 [('A', 'after', None, _('record a copy that has already occurred')),
4090 [('A', 'after', None, _('record a copy that has already occurred')),
4091 ('f', 'force', None,
4091 ('f', 'force', None,
4092 _('forcibly copy over an existing managed file')),
4092 _('forcibly copy over an existing managed file')),
4093 ] + walkopts + dryrunopts,
4093 ] + walkopts + dryrunopts,
4094 _('[OPTION]... [SOURCE]... DEST')),
4094 _('[OPTION]... [SOURCE]... DEST')),
4095 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4095 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4096 "debugbuilddag":
4096 "debugbuilddag":
4097 (debugbuilddag,
4097 (debugbuilddag,
4098 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4098 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4099 ('a', 'appended-file', None, _('add single file all revs append to')),
4099 ('a', 'appended-file', None, _('add single file all revs append to')),
4100 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4100 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4101 ('n', 'new-file', None, _('add new file at each rev')),
4101 ('n', 'new-file', None, _('add new file at each rev')),
4102 ],
4102 ],
4103 _('[OPTION]... TEXT')),
4103 _('[OPTION]... TEXT')),
4104 "debugcheckstate": (debugcheckstate, [], ''),
4104 "debugcheckstate": (debugcheckstate, [], ''),
4105 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4105 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4106 "debugcomplete":
4106 "debugcomplete":
4107 (debugcomplete,
4107 (debugcomplete,
4108 [('o', 'options', None, _('show the command options'))],
4108 [('o', 'options', None, _('show the command options'))],
4109 _('[-o] CMD')),
4109 _('[-o] CMD')),
4110 "debugdag":
4110 "debugdag":
4111 (debugdag,
4111 (debugdag,
4112 [('t', 'tags', None, _('use tags as labels')),
4112 [('t', 'tags', None, _('use tags as labels')),
4113 ('b', 'branches', None, _('annotate with branch names')),
4113 ('b', 'branches', None, _('annotate with branch names')),
4114 ('', 'dots', None, _('use dots for runs')),
4114 ('', 'dots', None, _('use dots for runs')),
4115 ('s', 'spaces', None, _('separate elements by spaces')),
4115 ('s', 'spaces', None, _('separate elements by spaces')),
4116 ],
4116 ],
4117 _('[OPTION]... [FILE [REV]...]')),
4117 _('[OPTION]... [FILE [REV]...]')),
4118 "debugdate":
4118 "debugdate":
4119 (debugdate,
4119 (debugdate,
4120 [('e', 'extended', None, _('try extended date formats'))],
4120 [('e', 'extended', None, _('try extended date formats'))],
4121 _('[-e] DATE [RANGE]')),
4121 _('[-e] DATE [RANGE]')),
4122 "debugdata": (debugdata, [], _('FILE REV')),
4122 "debugdata": (debugdata, [], _('FILE REV')),
4123 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4123 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4124 "debugindex": (debugindex, [], _('FILE')),
4124 "debugindex": (debugindex, [], _('FILE')),
4125 "debugindexdot": (debugindexdot, [], _('FILE')),
4125 "debugindexdot": (debugindexdot, [], _('FILE')),
4126 "debuginstall": (debuginstall, [], ''),
4126 "debuginstall": (debuginstall, [], ''),
4127 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4127 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4128 "debugrebuildstate":
4128 "debugrebuildstate":
4129 (debugrebuildstate,
4129 (debugrebuildstate,
4130 [('r', 'rev', '',
4130 [('r', 'rev', '',
4131 _('revision to rebuild to'), _('REV'))],
4131 _('revision to rebuild to'), _('REV'))],
4132 _('[-r REV] [REV]')),
4132 _('[-r REV] [REV]')),
4133 "debugrename":
4133 "debugrename":
4134 (debugrename,
4134 (debugrename,
4135 [('r', 'rev', '',
4135 [('r', 'rev', '',
4136 _('revision to debug'), _('REV'))],
4136 _('revision to debug'), _('REV'))],
4137 _('[-r REV] FILE')),
4137 _('[-r REV] FILE')),
4138 "debugrevspec":
4138 "debugrevspec":
4139 (debugrevspec, [], ('REVSPEC')),
4139 (debugrevspec, [], ('REVSPEC')),
4140 "debugsetparents":
4140 "debugsetparents":
4141 (debugsetparents, [], _('REV1 [REV2]')),
4141 (debugsetparents, [], _('REV1 [REV2]')),
4142 "debugstate":
4142 "debugstate":
4143 (debugstate,
4143 (debugstate,
4144 [('', 'nodates', None, _('do not display the saved mtime'))],
4144 [('', 'nodates', None, _('do not display the saved mtime'))],
4145 _('[OPTION]...')),
4145 _('[OPTION]...')),
4146 "debugsub":
4146 "debugsub":
4147 (debugsub,
4147 (debugsub,
4148 [('r', 'rev', '',
4148 [('r', 'rev', '',
4149 _('revision to check'), _('REV'))],
4149 _('revision to check'), _('REV'))],
4150 _('[-r REV] [REV]')),
4150 _('[-r REV] [REV]')),
4151 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4151 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4152 "^diff":
4152 "^diff":
4153 (diff,
4153 (diff,
4154 [('r', 'rev', [],
4154 [('r', 'rev', [],
4155 _('revision'), _('REV')),
4155 _('revision'), _('REV')),
4156 ('c', 'change', '',
4156 ('c', 'change', '',
4157 _('change made by revision'), _('REV'))
4157 _('change made by revision'), _('REV'))
4158 ] + diffopts + diffopts2 + walkopts,
4158 ] + diffopts + diffopts2 + walkopts,
4159 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4159 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4160 "^export":
4160 "^export":
4161 (export,
4161 (export,
4162 [('o', 'output', '',
4162 [('o', 'output', '',
4163 _('print output to file with formatted name'), _('FORMAT')),
4163 _('print output to file with formatted name'), _('FORMAT')),
4164 ('', 'switch-parent', None, _('diff against the second parent')),
4164 ('', 'switch-parent', None, _('diff against the second parent')),
4165 ('r', 'rev', [],
4165 ('r', 'rev', [],
4166 _('revisions to export'), _('REV')),
4166 _('revisions to export'), _('REV')),
4167 ] + diffopts,
4167 ] + diffopts,
4168 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4168 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4169 "^forget":
4169 "^forget":
4170 (forget,
4170 (forget,
4171 [] + walkopts,
4171 [] + walkopts,
4172 _('[OPTION]... FILE...')),
4172 _('[OPTION]... FILE...')),
4173 "grep":
4173 "grep":
4174 (grep,
4174 (grep,
4175 [('0', 'print0', None, _('end fields with NUL')),
4175 [('0', 'print0', None, _('end fields with NUL')),
4176 ('', 'all', None, _('print all revisions that match')),
4176 ('', 'all', None, _('print all revisions that match')),
4177 ('f', 'follow', None,
4177 ('f', 'follow', None,
4178 _('follow changeset history,'
4178 _('follow changeset history,'
4179 ' or file history across copies and renames')),
4179 ' or file history across copies and renames')),
4180 ('i', 'ignore-case', None, _('ignore case when matching')),
4180 ('i', 'ignore-case', None, _('ignore case when matching')),
4181 ('l', 'files-with-matches', None,
4181 ('l', 'files-with-matches', None,
4182 _('print only filenames and revisions that match')),
4182 _('print only filenames and revisions that match')),
4183 ('n', 'line-number', None, _('print matching line numbers')),
4183 ('n', 'line-number', None, _('print matching line numbers')),
4184 ('r', 'rev', [],
4184 ('r', 'rev', [],
4185 _('only search files changed within revision range'), _('REV')),
4185 _('only search files changed within revision range'), _('REV')),
4186 ('u', 'user', None, _('list the author (long with -v)')),
4186 ('u', 'user', None, _('list the author (long with -v)')),
4187 ('d', 'date', None, _('list the date (short with -q)')),
4187 ('d', 'date', None, _('list the date (short with -q)')),
4188 ] + walkopts,
4188 ] + walkopts,
4189 _('[OPTION]... PATTERN [FILE]...')),
4189 _('[OPTION]... PATTERN [FILE]...')),
4190 "heads":
4190 "heads":
4191 (heads,
4191 (heads,
4192 [('r', 'rev', '',
4192 [('r', 'rev', '',
4193 _('show only heads which are descendants of REV'), _('REV')),
4193 _('show only heads which are descendants of REV'), _('REV')),
4194 ('t', 'topo', False, _('show topological heads only')),
4194 ('t', 'topo', False, _('show topological heads only')),
4195 ('a', 'active', False,
4195 ('a', 'active', False,
4196 _('show active branchheads only (DEPRECATED)')),
4196 _('show active branchheads only (DEPRECATED)')),
4197 ('c', 'closed', False,
4197 ('c', 'closed', False,
4198 _('show normal and closed branch heads')),
4198 _('show normal and closed branch heads')),
4199 ] + templateopts,
4199 ] + templateopts,
4200 _('[-ac] [-r REV] [REV]...')),
4200 _('[-ac] [-r REV] [REV]...')),
4201 "help": (help_, [], _('[TOPIC]')),
4201 "help": (help_, [], _('[TOPIC]')),
4202 "identify|id":
4202 "identify|id":
4203 (identify,
4203 (identify,
4204 [('r', 'rev', '',
4204 [('r', 'rev', '',
4205 _('identify the specified revision'), _('REV')),
4205 _('identify the specified revision'), _('REV')),
4206 ('n', 'num', None, _('show local revision number')),
4206 ('n', 'num', None, _('show local revision number')),
4207 ('i', 'id', None, _('show global revision id')),
4207 ('i', 'id', None, _('show global revision id')),
4208 ('b', 'branch', None, _('show branch')),
4208 ('b', 'branch', None, _('show branch')),
4209 ('t', 'tags', None, _('show tags'))],
4209 ('t', 'tags', None, _('show tags'))],
4210 _('[-nibt] [-r REV] [SOURCE]')),
4210 _('[-nibt] [-r REV] [SOURCE]')),
4211 "import|patch":
4211 "import|patch":
4212 (import_,
4212 (import_,
4213 [('p', 'strip', 1,
4213 [('p', 'strip', 1,
4214 _('directory strip option for patch. This has the same '
4214 _('directory strip option for patch. This has the same '
4215 'meaning as the corresponding patch option'),
4215 'meaning as the corresponding patch option'),
4216 _('NUM')),
4216 _('NUM')),
4217 ('b', 'base', '',
4217 ('b', 'base', '',
4218 _('base path'), _('PATH')),
4218 _('base path'), _('PATH')),
4219 ('f', 'force', None,
4219 ('f', 'force', None,
4220 _('skip check for outstanding uncommitted changes')),
4220 _('skip check for outstanding uncommitted changes')),
4221 ('', 'no-commit', None,
4221 ('', 'no-commit', None,
4222 _("don't commit, just update the working directory")),
4222 _("don't commit, just update the working directory")),
4223 ('', 'exact', None,
4223 ('', 'exact', None,
4224 _('apply patch to the nodes from which it was generated')),
4224 _('apply patch to the nodes from which it was generated')),
4225 ('', 'import-branch', None,
4225 ('', 'import-branch', None,
4226 _('use any branch information in patch (implied by --exact)'))] +
4226 _('use any branch information in patch (implied by --exact)'))] +
4227 commitopts + commitopts2 + similarityopts,
4227 commitopts + commitopts2 + similarityopts,
4228 _('[OPTION]... PATCH...')),
4228 _('[OPTION]... PATCH...')),
4229 "incoming|in":
4229 "incoming|in":
4230 (incoming,
4230 (incoming,
4231 [('f', 'force', None,
4231 [('f', 'force', None,
4232 _('run even if remote repository is unrelated')),
4232 _('run even if remote repository is unrelated')),
4233 ('n', 'newest-first', None, _('show newest record first')),
4233 ('n', 'newest-first', None, _('show newest record first')),
4234 ('', 'bundle', '',
4234 ('', 'bundle', '',
4235 _('file to store the bundles into'), _('FILE')),
4235 _('file to store the bundles into'), _('FILE')),
4236 ('r', 'rev', [],
4236 ('r', 'rev', [],
4237 _('a remote changeset intended to be added'), _('REV')),
4237 _('a remote changeset intended to be added'), _('REV')),
4238 ('b', 'branch', [],
4238 ('b', 'branch', [],
4239 _('a specific branch you would like to pull'), _('BRANCH')),
4239 _('a specific branch you would like to pull'), _('BRANCH')),
4240 ] + logopts + remoteopts,
4240 ] + logopts + remoteopts,
4241 _('[-p] [-n] [-M] [-f] [-r REV]...'
4241 _('[-p] [-n] [-M] [-f] [-r REV]...'
4242 ' [--bundle FILENAME] [SOURCE]')),
4242 ' [--bundle FILENAME] [SOURCE]')),
4243 "^init":
4243 "^init":
4244 (init,
4244 (init,
4245 remoteopts,
4245 remoteopts,
4246 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4246 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4247 "locate":
4247 "locate":
4248 (locate,
4248 (locate,
4249 [('r', 'rev', '',
4249 [('r', 'rev', '',
4250 _('search the repository as it is in REV'), _('REV')),
4250 _('search the repository as it is in REV'), _('REV')),
4251 ('0', 'print0', None,
4251 ('0', 'print0', None,
4252 _('end filenames with NUL, for use with xargs')),
4252 _('end filenames with NUL, for use with xargs')),
4253 ('f', 'fullpath', None,
4253 ('f', 'fullpath', None,
4254 _('print complete paths from the filesystem root')),
4254 _('print complete paths from the filesystem root')),
4255 ] + walkopts,
4255 ] + walkopts,
4256 _('[OPTION]... [PATTERN]...')),
4256 _('[OPTION]... [PATTERN]...')),
4257 "^log|history":
4257 "^log|history":
4258 (log,
4258 (log,
4259 [('f', 'follow', None,
4259 [('f', 'follow', None,
4260 _('follow changeset history,'
4260 _('follow changeset history,'
4261 ' or file history across copies and renames')),
4261 ' or file history across copies and renames')),
4262 ('', 'follow-first', None,
4262 ('', 'follow-first', None,
4263 _('only follow the first parent of merge changesets')),
4263 _('only follow the first parent of merge changesets')),
4264 ('d', 'date', '',
4264 ('d', 'date', '',
4265 _('show revisions matching date spec'), _('DATE')),
4265 _('show revisions matching date spec'), _('DATE')),
4266 ('C', 'copies', None, _('show copied files')),
4266 ('C', 'copies', None, _('show copied files')),
4267 ('k', 'keyword', [],
4267 ('k', 'keyword', [],
4268 _('do case-insensitive search for a given text'), _('TEXT')),
4268 _('do case-insensitive search for a given text'), _('TEXT')),
4269 ('r', 'rev', [],
4269 ('r', 'rev', [],
4270 _('show the specified revision or range'), _('REV')),
4270 _('show the specified revision or range'), _('REV')),
4271 ('', 'removed', None, _('include revisions where files were removed')),
4271 ('', 'removed', None, _('include revisions where files were removed')),
4272 ('m', 'only-merges', None, _('show only merges')),
4272 ('m', 'only-merges', None, _('show only merges')),
4273 ('u', 'user', [],
4273 ('u', 'user', [],
4274 _('revisions committed by user'), _('USER')),
4274 _('revisions committed by user'), _('USER')),
4275 ('', 'only-branch', [],
4275 ('', 'only-branch', [],
4276 _('show only changesets within the given named branch (DEPRECATED)'),
4276 _('show only changesets within the given named branch (DEPRECATED)'),
4277 _('BRANCH')),
4277 _('BRANCH')),
4278 ('b', 'branch', [],
4278 ('b', 'branch', [],
4279 _('show changesets within the given named branch'), _('BRANCH')),
4279 _('show changesets within the given named branch'), _('BRANCH')),
4280 ('P', 'prune', [],
4280 ('P', 'prune', [],
4281 _('do not display revision or any of its ancestors'), _('REV')),
4281 _('do not display revision or any of its ancestors'), _('REV')),
4282 ] + logopts + walkopts,
4282 ] + logopts + walkopts,
4283 _('[OPTION]... [FILE]')),
4283 _('[OPTION]... [FILE]')),
4284 "manifest":
4284 "manifest":
4285 (manifest,
4285 (manifest,
4286 [('r', 'rev', '',
4286 [('r', 'rev', '',
4287 _('revision to display'), _('REV'))],
4287 _('revision to display'), _('REV'))],
4288 _('[-r REV]')),
4288 _('[-r REV]')),
4289 "^merge":
4289 "^merge":
4290 (merge,
4290 (merge,
4291 [('f', 'force', None, _('force a merge with outstanding changes')),
4291 [('f', 'force', None, _('force a merge with outstanding changes')),
4292 ('r', 'rev', '',
4292 ('r', 'rev', '',
4293 _('revision to merge'), _('REV')),
4293 _('revision to merge'), _('REV')),
4294 ('P', 'preview', None,
4294 ('P', 'preview', None,
4295 _('review revisions to merge (no merge is performed)'))],
4295 _('review revisions to merge (no merge is performed)'))],
4296 _('[-P] [-f] [[-r] REV]')),
4296 _('[-P] [-f] [[-r] REV]')),
4297 "outgoing|out":
4297 "outgoing|out":
4298 (outgoing,
4298 (outgoing,
4299 [('f', 'force', None,
4299 [('f', 'force', None,
4300 _('run even when the destination is unrelated')),
4300 _('run even when the destination is unrelated')),
4301 ('r', 'rev', [],
4301 ('r', 'rev', [],
4302 _('a changeset intended to be included in the destination'),
4302 _('a changeset intended to be included in the destination'),
4303 _('REV')),
4303 _('REV')),
4304 ('n', 'newest-first', None, _('show newest record first')),
4304 ('n', 'newest-first', None, _('show newest record first')),
4305 ('b', 'branch', [],
4305 ('b', 'branch', [],
4306 _('a specific branch you would like to push'), _('BRANCH')),
4306 _('a specific branch you would like to push'), _('BRANCH')),
4307 ] + logopts + remoteopts,
4307 ] + logopts + remoteopts,
4308 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4308 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4309 "parents":
4309 "parents":
4310 (parents,
4310 (parents,
4311 [('r', 'rev', '',
4311 [('r', 'rev', '',
4312 _('show parents of the specified revision'), _('REV')),
4312 _('show parents of the specified revision'), _('REV')),
4313 ] + templateopts,
4313 ] + templateopts,
4314 _('[-r REV] [FILE]')),
4314 _('[-r REV] [FILE]')),
4315 "paths": (paths, [], _('[NAME]')),
4315 "paths": (paths, [], _('[NAME]')),
4316 "^pull":
4316 "^pull":
4317 (pull,
4317 (pull,
4318 [('u', 'update', None,
4318 [('u', 'update', None,
4319 _('update to new branch head if changesets were pulled')),
4319 _('update to new branch head if changesets were pulled')),
4320 ('f', 'force', None,
4320 ('f', 'force', None,
4321 _('run even when remote repository is unrelated')),
4321 _('run even when remote repository is unrelated')),
4322 ('r', 'rev', [],
4322 ('r', 'rev', [],
4323 _('a remote changeset intended to be added'), _('REV')),
4323 _('a remote changeset intended to be added'), _('REV')),
4324 ('b', 'branch', [],
4324 ('b', 'branch', [],
4325 _('a specific branch you would like to pull'), _('BRANCH')),
4325 _('a specific branch you would like to pull'), _('BRANCH')),
4326 ] + remoteopts,
4326 ] + remoteopts,
4327 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4327 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4328 "^push":
4328 "^push":
4329 (push,
4329 (push,
4330 [('f', 'force', None, _('force push')),
4330 [('f', 'force', None, _('force push')),
4331 ('r', 'rev', [],
4331 ('r', 'rev', [],
4332 _('a changeset intended to be included in the destination'),
4332 _('a changeset intended to be included in the destination'),
4333 _('REV')),
4333 _('REV')),
4334 ('b', 'branch', [],
4334 ('b', 'branch', [],
4335 _('a specific branch you would like to push'), _('BRANCH')),
4335 _('a specific branch you would like to push'), _('BRANCH')),
4336 ('', 'new-branch', False, _('allow pushing a new branch')),
4336 ('', 'new-branch', False, _('allow pushing a new branch')),
4337 ] + remoteopts,
4337 ] + remoteopts,
4338 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4338 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4339 "recover": (recover, []),
4339 "recover": (recover, []),
4340 "^remove|rm":
4340 "^remove|rm":
4341 (remove,
4341 (remove,
4342 [('A', 'after', None, _('record delete for missing files')),
4342 [('A', 'after', None, _('record delete for missing files')),
4343 ('f', 'force', None,
4343 ('f', 'force', None,
4344 _('remove (and delete) file even if added or modified')),
4344 _('remove (and delete) file even if added or modified')),
4345 ] + walkopts,
4345 ] + walkopts,
4346 _('[OPTION]... FILE...')),
4346 _('[OPTION]... FILE...')),
4347 "rename|mv":
4347 "rename|mv":
4348 (rename,
4348 (rename,
4349 [('A', 'after', None, _('record a rename that has already occurred')),
4349 [('A', 'after', None, _('record a rename that has already occurred')),
4350 ('f', 'force', None,
4350 ('f', 'force', None,
4351 _('forcibly copy over an existing managed file')),
4351 _('forcibly copy over an existing managed file')),
4352 ] + walkopts + dryrunopts,
4352 ] + walkopts + dryrunopts,
4353 _('[OPTION]... SOURCE... DEST')),
4353 _('[OPTION]... SOURCE... DEST')),
4354 "resolve":
4354 "resolve":
4355 (resolve,
4355 (resolve,
4356 [('a', 'all', None, _('select all unresolved files')),
4356 [('a', 'all', None, _('select all unresolved files')),
4357 ('l', 'list', None, _('list state of files needing merge')),
4357 ('l', 'list', None, _('list state of files needing merge')),
4358 ('m', 'mark', None, _('mark files as resolved')),
4358 ('m', 'mark', None, _('mark files as resolved')),
4359 ('u', 'unmark', None, _('mark files as unresolved')),
4359 ('u', 'unmark', None, _('mark files as unresolved')),
4360 ('n', 'no-status', None, _('hide status prefix'))]
4360 ('n', 'no-status', None, _('hide status prefix'))]
4361 + walkopts,
4361 + walkopts,
4362 _('[OPTION]... [FILE]...')),
4362 _('[OPTION]... [FILE]...')),
4363 "revert":
4363 "revert":
4364 (revert,
4364 (revert,
4365 [('a', 'all', None, _('revert all changes when no arguments given')),
4365 [('a', 'all', None, _('revert all changes when no arguments given')),
4366 ('d', 'date', '',
4366 ('d', 'date', '',
4367 _('tipmost revision matching date'), _('DATE')),
4367 _('tipmost revision matching date'), _('DATE')),
4368 ('r', 'rev', '',
4368 ('r', 'rev', '',
4369 _('revert to the specified revision'), _('REV')),
4369 _('revert to the specified revision'), _('REV')),
4370 ('', 'no-backup', None, _('do not save backup copies of files')),
4370 ('', 'no-backup', None, _('do not save backup copies of files')),
4371 ] + walkopts + dryrunopts,
4371 ] + walkopts + dryrunopts,
4372 _('[OPTION]... [-r REV] [NAME]...')),
4372 _('[OPTION]... [-r REV] [NAME]...')),
4373 "rollback": (rollback, dryrunopts),
4373 "rollback": (rollback, dryrunopts),
4374 "root": (root, []),
4374 "root": (root, []),
4375 "^serve":
4375 "^serve":
4376 (serve,
4376 (serve,
4377 [('A', 'accesslog', '',
4377 [('A', 'accesslog', '',
4378 _('name of access log file to write to'), _('FILE')),
4378 _('name of access log file to write to'), _('FILE')),
4379 ('d', 'daemon', None, _('run server in background')),
4379 ('d', 'daemon', None, _('run server in background')),
4380 ('', 'daemon-pipefds', '',
4380 ('', 'daemon-pipefds', '',
4381 _('used internally by daemon mode'), _('NUM')),
4381 _('used internally by daemon mode'), _('NUM')),
4382 ('E', 'errorlog', '',
4382 ('E', 'errorlog', '',
4383 _('name of error log file to write to'), _('FILE')),
4383 _('name of error log file to write to'), _('FILE')),
4384 # use string type, then we can check if something was passed
4384 # use string type, then we can check if something was passed
4385 ('p', 'port', '',
4385 ('p', 'port', '',
4386 _('port to listen on (default: 8000)'), _('PORT')),
4386 _('port to listen on (default: 8000)'), _('PORT')),
4387 ('a', 'address', '',
4387 ('a', 'address', '',
4388 _('address to listen on (default: all interfaces)'), _('ADDR')),
4388 _('address to listen on (default: all interfaces)'), _('ADDR')),
4389 ('', 'prefix', '',
4389 ('', 'prefix', '',
4390 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4390 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4391 ('n', 'name', '',
4391 ('n', 'name', '',
4392 _('name to show in web pages (default: working directory)'),
4392 _('name to show in web pages (default: working directory)'),
4393 _('NAME')),
4393 _('NAME')),
4394 ('', 'web-conf', '',
4394 ('', 'web-conf', '',
4395 _('name of the hgweb config file (serve more than one repository)'),
4395 _('name of the hgweb config file (serve more than one repository)'),
4396 _('FILE')),
4396 _('FILE')),
4397 ('', 'webdir-conf', '',
4397 ('', 'webdir-conf', '',
4398 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4398 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4399 ('', 'pid-file', '',
4399 ('', 'pid-file', '',
4400 _('name of file to write process ID to'), _('FILE')),
4400 _('name of file to write process ID to'), _('FILE')),
4401 ('', 'stdio', None, _('for remote clients')),
4401 ('', 'stdio', None, _('for remote clients')),
4402 ('t', 'templates', '',
4402 ('t', 'templates', '',
4403 _('web templates to use'), _('TEMPLATE')),
4403 _('web templates to use'), _('TEMPLATE')),
4404 ('', 'style', '',
4404 ('', 'style', '',
4405 _('template style to use'), _('STYLE')),
4405 _('template style to use'), _('STYLE')),
4406 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4406 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4407 ('', 'certificate', '',
4407 ('', 'certificate', '',
4408 _('SSL certificate file'), _('FILE'))],
4408 _('SSL certificate file'), _('FILE'))],
4409 _('[OPTION]...')),
4409 _('[OPTION]...')),
4410 "showconfig|debugconfig":
4410 "showconfig|debugconfig":
4411 (showconfig,
4411 (showconfig,
4412 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4412 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4413 _('[-u] [NAME]...')),
4413 _('[-u] [NAME]...')),
4414 "^summary|sum":
4414 "^summary|sum":
4415 (summary,
4415 (summary,
4416 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4416 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4417 "^status|st":
4417 "^status|st":
4418 (status,
4418 (status,
4419 [('A', 'all', None, _('show status of all files')),
4419 [('A', 'all', None, _('show status of all files')),
4420 ('m', 'modified', None, _('show only modified files')),
4420 ('m', 'modified', None, _('show only modified files')),
4421 ('a', 'added', None, _('show only added files')),
4421 ('a', 'added', None, _('show only added files')),
4422 ('r', 'removed', None, _('show only removed files')),
4422 ('r', 'removed', None, _('show only removed files')),
4423 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4423 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4424 ('c', 'clean', None, _('show only files without changes')),
4424 ('c', 'clean', None, _('show only files without changes')),
4425 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4425 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4426 ('i', 'ignored', None, _('show only ignored files')),
4426 ('i', 'ignored', None, _('show only ignored files')),
4427 ('n', 'no-status', None, _('hide status prefix')),
4427 ('n', 'no-status', None, _('hide status prefix')),
4428 ('C', 'copies', None, _('show source of copied files')),
4428 ('C', 'copies', None, _('show source of copied files')),
4429 ('0', 'print0', None,
4429 ('0', 'print0', None,
4430 _('end filenames with NUL, for use with xargs')),
4430 _('end filenames with NUL, for use with xargs')),
4431 ('', 'rev', [],
4431 ('', 'rev', [],
4432 _('show difference from revision'), _('REV')),
4432 _('show difference from revision'), _('REV')),
4433 ('', 'change', '',
4433 ('', 'change', '',
4434 _('list the changed files of a revision'), _('REV')),
4434 _('list the changed files of a revision'), _('REV')),
4435 ] + walkopts,
4435 ] + walkopts,
4436 _('[OPTION]... [FILE]...')),
4436 _('[OPTION]... [FILE]...')),
4437 "tag":
4437 "tag":
4438 (tag,
4438 (tag,
4439 [('f', 'force', None, _('replace existing tag')),
4439 [('f', 'force', None, _('replace existing tag')),
4440 ('l', 'local', None, _('make the tag local')),
4440 ('l', 'local', None, _('make the tag local')),
4441 ('r', 'rev', '',
4441 ('r', 'rev', '',
4442 _('revision to tag'), _('REV')),
4442 _('revision to tag'), _('REV')),
4443 ('', 'remove', None, _('remove a tag')),
4443 ('', 'remove', None, _('remove a tag')),
4444 # -l/--local is already there, commitopts cannot be used
4444 # -l/--local is already there, commitopts cannot be used
4445 ('e', 'edit', None, _('edit commit message')),
4445 ('e', 'edit', None, _('edit commit message')),
4446 ('m', 'message', '',
4446 ('m', 'message', '',
4447 _('use <text> as commit message'), _('TEXT')),
4447 _('use <text> as commit message'), _('TEXT')),
4448 ] + commitopts2,
4448 ] + commitopts2,
4449 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4449 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4450 "tags": (tags, [], ''),
4450 "tags": (tags, [], ''),
4451 "tip":
4451 "tip":
4452 (tip,
4452 (tip,
4453 [('p', 'patch', None, _('show patch')),
4453 [('p', 'patch', None, _('show patch')),
4454 ('g', 'git', None, _('use git extended diff format')),
4454 ('g', 'git', None, _('use git extended diff format')),
4455 ] + templateopts,
4455 ] + templateopts,
4456 _('[-p] [-g]')),
4456 _('[-p] [-g]')),
4457 "unbundle":
4457 "unbundle":
4458 (unbundle,
4458 (unbundle,
4459 [('u', 'update', None,
4459 [('u', 'update', None,
4460 _('update to new branch head if changesets were unbundled'))],
4460 _('update to new branch head if changesets were unbundled'))],
4461 _('[-u] FILE...')),
4461 _('[-u] FILE...')),
4462 "^update|up|checkout|co":
4462 "^update|up|checkout|co":
4463 (update,
4463 (update,
4464 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4464 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4465 ('c', 'check', None, _('check for uncommitted changes')),
4465 ('c', 'check', None, _('check for uncommitted changes')),
4466 ('d', 'date', '',
4466 ('d', 'date', '',
4467 _('tipmost revision matching date'), _('DATE')),
4467 _('tipmost revision matching date'), _('DATE')),
4468 ('r', 'rev', '',
4468 ('r', 'rev', '',
4469 _('revision'), _('REV'))],
4469 _('revision'), _('REV'))],
4470 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4470 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4471 "verify": (verify, []),
4471 "verify": (verify, []),
4472 "version": (version_, []),
4472 "version": (version_, []),
4473 }
4473 }
4474
4474
4475 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4475 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4476 " debugindex debugindexdot debugdate debuginstall debugfsinfo"
4476 " debugindex debugindexdot debugdate debuginstall debugfsinfo"
4477 " debugpushkey")
4477 " debugpushkey")
4478 optionalrepo = ("identify paths serve showconfig debugancestor debugdag")
4478 optionalrepo = ("identify paths serve showconfig debugancestor debugdag")
@@ -1,1086 +1,1086 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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 nullid, nullrev, short, hex
8 from node import nullid, nullrev, short, hex
9 from i18n import _
9 from i18n import _
10 import ancestor, bdiff, error, util, subrepo, patch
10 import ancestor, bdiff, error, util, subrepo, patch
11 import os, errno, stat
11 import os, errno, stat
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 class changectx(object):
15 class changectx(object):
16 """A changecontext object makes access to data related to a particular
16 """A changecontext object makes access to data related to a particular
17 changeset convenient."""
17 changeset convenient."""
18 def __init__(self, repo, changeid=''):
18 def __init__(self, repo, changeid=''):
19 """changeid is a revision number, node, or tag"""
19 """changeid is a revision number, node, or tag"""
20 if changeid == '':
20 if changeid == '':
21 changeid = '.'
21 changeid = '.'
22 self._repo = repo
22 self._repo = repo
23 if isinstance(changeid, (long, int)):
23 if isinstance(changeid, (long, int)):
24 self._rev = changeid
24 self._rev = changeid
25 self._node = self._repo.changelog.node(changeid)
25 self._node = self._repo.changelog.node(changeid)
26 else:
26 else:
27 self._node = self._repo.lookup(changeid)
27 self._node = self._repo.lookup(changeid)
28 self._rev = self._repo.changelog.rev(self._node)
28 self._rev = self._repo.changelog.rev(self._node)
29
29
30 def __str__(self):
30 def __str__(self):
31 return short(self.node())
31 return short(self.node())
32
32
33 def __int__(self):
33 def __int__(self):
34 return self.rev()
34 return self.rev()
35
35
36 def __repr__(self):
36 def __repr__(self):
37 return "<changectx %s>" % str(self)
37 return "<changectx %s>" % str(self)
38
38
39 def __hash__(self):
39 def __hash__(self):
40 try:
40 try:
41 return hash(self._rev)
41 return hash(self._rev)
42 except AttributeError:
42 except AttributeError:
43 return id(self)
43 return id(self)
44
44
45 def __eq__(self, other):
45 def __eq__(self, other):
46 try:
46 try:
47 return self._rev == other._rev
47 return self._rev == other._rev
48 except AttributeError:
48 except AttributeError:
49 return False
49 return False
50
50
51 def __ne__(self, other):
51 def __ne__(self, other):
52 return not (self == other)
52 return not (self == other)
53
53
54 def __nonzero__(self):
54 def __nonzero__(self):
55 return self._rev != nullrev
55 return self._rev != nullrev
56
56
57 @propertycache
57 @propertycache
58 def _changeset(self):
58 def _changeset(self):
59 return self._repo.changelog.read(self.node())
59 return self._repo.changelog.read(self.node())
60
60
61 @propertycache
61 @propertycache
62 def _manifest(self):
62 def _manifest(self):
63 return self._repo.manifest.read(self._changeset[0])
63 return self._repo.manifest.read(self._changeset[0])
64
64
65 @propertycache
65 @propertycache
66 def _manifestdelta(self):
66 def _manifestdelta(self):
67 return self._repo.manifest.readdelta(self._changeset[0])
67 return self._repo.manifest.readdelta(self._changeset[0])
68
68
69 @propertycache
69 @propertycache
70 def _parents(self):
70 def _parents(self):
71 p = self._repo.changelog.parentrevs(self._rev)
71 p = self._repo.changelog.parentrevs(self._rev)
72 if p[1] == nullrev:
72 if p[1] == nullrev:
73 p = p[:-1]
73 p = p[:-1]
74 return [changectx(self._repo, x) for x in p]
74 return [changectx(self._repo, x) for x in p]
75
75
76 @propertycache
76 @propertycache
77 def substate(self):
77 def substate(self):
78 return subrepo.state(self)
78 return subrepo.state(self)
79
79
80 def __contains__(self, key):
80 def __contains__(self, key):
81 return key in self._manifest
81 return key in self._manifest
82
82
83 def __getitem__(self, key):
83 def __getitem__(self, key):
84 return self.filectx(key)
84 return self.filectx(key)
85
85
86 def __iter__(self):
86 def __iter__(self):
87 for f in sorted(self._manifest):
87 for f in sorted(self._manifest):
88 yield f
88 yield f
89
89
90 def changeset(self):
90 def changeset(self):
91 return self._changeset
91 return self._changeset
92 def manifest(self):
92 def manifest(self):
93 return self._manifest
93 return self._manifest
94 def manifestnode(self):
94 def manifestnode(self):
95 return self._changeset[0]
95 return self._changeset[0]
96
96
97 def rev(self):
97 def rev(self):
98 return self._rev
98 return self._rev
99 def node(self):
99 def node(self):
100 return self._node
100 return self._node
101 def hex(self):
101 def hex(self):
102 return hex(self._node)
102 return hex(self._node)
103 def user(self):
103 def user(self):
104 return self._changeset[1]
104 return self._changeset[1]
105 def date(self):
105 def date(self):
106 return self._changeset[2]
106 return self._changeset[2]
107 def files(self):
107 def files(self):
108 return self._changeset[3]
108 return self._changeset[3]
109 def description(self):
109 def description(self):
110 return self._changeset[4]
110 return self._changeset[4]
111 def branch(self):
111 def branch(self):
112 return self._changeset[5].get("branch")
112 return self._changeset[5].get("branch")
113 def extra(self):
113 def extra(self):
114 return self._changeset[5]
114 return self._changeset[5]
115 def tags(self):
115 def tags(self):
116 return self._repo.nodetags(self._node)
116 return self._repo.nodetags(self._node)
117
117
118 def parents(self):
118 def parents(self):
119 """return contexts for each parent changeset"""
119 """return contexts for each parent changeset"""
120 return self._parents
120 return self._parents
121
121
122 def p1(self):
122 def p1(self):
123 return self._parents[0]
123 return self._parents[0]
124
124
125 def p2(self):
125 def p2(self):
126 if len(self._parents) == 2:
126 if len(self._parents) == 2:
127 return self._parents[1]
127 return self._parents[1]
128 return changectx(self._repo, -1)
128 return changectx(self._repo, -1)
129
129
130 def children(self):
130 def children(self):
131 """return contexts for each child changeset"""
131 """return contexts for each child changeset"""
132 c = self._repo.changelog.children(self._node)
132 c = self._repo.changelog.children(self._node)
133 return [changectx(self._repo, x) for x in c]
133 return [changectx(self._repo, x) for x in c]
134
134
135 def ancestors(self):
135 def ancestors(self):
136 for a in self._repo.changelog.ancestors(self._rev):
136 for a in self._repo.changelog.ancestors(self._rev):
137 yield changectx(self._repo, a)
137 yield changectx(self._repo, a)
138
138
139 def descendants(self):
139 def descendants(self):
140 for d in self._repo.changelog.descendants(self._rev):
140 for d in self._repo.changelog.descendants(self._rev):
141 yield changectx(self._repo, d)
141 yield changectx(self._repo, d)
142
142
143 def _fileinfo(self, path):
143 def _fileinfo(self, path):
144 if '_manifest' in self.__dict__:
144 if '_manifest' in self.__dict__:
145 try:
145 try:
146 return self._manifest[path], self._manifest.flags(path)
146 return self._manifest[path], self._manifest.flags(path)
147 except KeyError:
147 except KeyError:
148 raise error.LookupError(self._node, path,
148 raise error.LookupError(self._node, path,
149 _('not found in manifest'))
149 _('not found in manifest'))
150 if '_manifestdelta' in self.__dict__ or path in self.files():
150 if '_manifestdelta' in self.__dict__ or path in self.files():
151 if path in self._manifestdelta:
151 if path in self._manifestdelta:
152 return self._manifestdelta[path], self._manifestdelta.flags(path)
152 return self._manifestdelta[path], self._manifestdelta.flags(path)
153 node, flag = self._repo.manifest.find(self._changeset[0], path)
153 node, flag = self._repo.manifest.find(self._changeset[0], path)
154 if not node:
154 if not node:
155 raise error.LookupError(self._node, path,
155 raise error.LookupError(self._node, path,
156 _('not found in manifest'))
156 _('not found in manifest'))
157
157
158 return node, flag
158 return node, flag
159
159
160 def filenode(self, path):
160 def filenode(self, path):
161 return self._fileinfo(path)[0]
161 return self._fileinfo(path)[0]
162
162
163 def flags(self, path):
163 def flags(self, path):
164 try:
164 try:
165 return self._fileinfo(path)[1]
165 return self._fileinfo(path)[1]
166 except error.LookupError:
166 except error.LookupError:
167 return ''
167 return ''
168
168
169 def filectx(self, path, fileid=None, filelog=None):
169 def filectx(self, path, fileid=None, filelog=None):
170 """get a file context from this changeset"""
170 """get a file context from this changeset"""
171 if fileid is None:
171 if fileid is None:
172 fileid = self.filenode(path)
172 fileid = self.filenode(path)
173 return filectx(self._repo, path, fileid=fileid,
173 return filectx(self._repo, path, fileid=fileid,
174 changectx=self, filelog=filelog)
174 changectx=self, filelog=filelog)
175
175
176 def ancestor(self, c2):
176 def ancestor(self, c2):
177 """
177 """
178 return the ancestor context of self and c2
178 return the ancestor context of self and c2
179 """
179 """
180 # deal with workingctxs
180 # deal with workingctxs
181 n2 = c2._node
181 n2 = c2._node
182 if n2 == None:
182 if n2 == None:
183 n2 = c2._parents[0]._node
183 n2 = c2._parents[0]._node
184 n = self._repo.changelog.ancestor(self._node, n2)
184 n = self._repo.changelog.ancestor(self._node, n2)
185 return changectx(self._repo, n)
185 return changectx(self._repo, n)
186
186
187 def walk(self, match):
187 def walk(self, match):
188 fset = set(match.files())
188 fset = set(match.files())
189 # for dirstate.walk, files=['.'] means "walk the whole tree".
189 # for dirstate.walk, files=['.'] means "walk the whole tree".
190 # follow that here, too
190 # follow that here, too
191 fset.discard('.')
191 fset.discard('.')
192 for fn in self:
192 for fn in self:
193 for ffn in fset:
193 for ffn in fset:
194 # match if the file is the exact name or a directory
194 # match if the file is the exact name or a directory
195 if ffn == fn or fn.startswith("%s/" % ffn):
195 if ffn == fn or fn.startswith("%s/" % ffn):
196 fset.remove(ffn)
196 fset.remove(ffn)
197 break
197 break
198 if match(fn):
198 if match(fn):
199 yield fn
199 yield fn
200 for fn in sorted(fset):
200 for fn in sorted(fset):
201 if match.bad(fn, _('No such file in rev %s') % self) and match(fn):
201 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
202 yield fn
202 yield fn
203
203
204 def sub(self, path):
204 def sub(self, path):
205 return subrepo.subrepo(self, path)
205 return subrepo.subrepo(self, path)
206
206
207 def diff(self, ctx2=None, match=None, **opts):
207 def diff(self, ctx2=None, match=None, **opts):
208 """Returns a diff generator for the given contexts and matcher"""
208 """Returns a diff generator for the given contexts and matcher"""
209 if ctx2 is None:
209 if ctx2 is None:
210 ctx2 = self.p1()
210 ctx2 = self.p1()
211 if ctx2 is not None and not isinstance(ctx2, changectx):
211 if ctx2 is not None and not isinstance(ctx2, changectx):
212 ctx2 = self._repo[ctx2]
212 ctx2 = self._repo[ctx2]
213 diffopts = patch.diffopts(self._repo.ui, opts)
213 diffopts = patch.diffopts(self._repo.ui, opts)
214 return patch.diff(self._repo, ctx2.node(), self.node(),
214 return patch.diff(self._repo, ctx2.node(), self.node(),
215 match=match, opts=diffopts)
215 match=match, opts=diffopts)
216
216
217 class filectx(object):
217 class filectx(object):
218 """A filecontext object makes access to data related to a particular
218 """A filecontext object makes access to data related to a particular
219 filerevision convenient."""
219 filerevision convenient."""
220 def __init__(self, repo, path, changeid=None, fileid=None,
220 def __init__(self, repo, path, changeid=None, fileid=None,
221 filelog=None, changectx=None):
221 filelog=None, changectx=None):
222 """changeid can be a changeset revision, node, or tag.
222 """changeid can be a changeset revision, node, or tag.
223 fileid can be a file revision or node."""
223 fileid can be a file revision or node."""
224 self._repo = repo
224 self._repo = repo
225 self._path = path
225 self._path = path
226
226
227 assert (changeid is not None
227 assert (changeid is not None
228 or fileid is not None
228 or fileid is not None
229 or changectx is not None), \
229 or changectx is not None), \
230 ("bad args: changeid=%r, fileid=%r, changectx=%r"
230 ("bad args: changeid=%r, fileid=%r, changectx=%r"
231 % (changeid, fileid, changectx))
231 % (changeid, fileid, changectx))
232
232
233 if filelog:
233 if filelog:
234 self._filelog = filelog
234 self._filelog = filelog
235
235
236 if changeid is not None:
236 if changeid is not None:
237 self._changeid = changeid
237 self._changeid = changeid
238 if changectx is not None:
238 if changectx is not None:
239 self._changectx = changectx
239 self._changectx = changectx
240 if fileid is not None:
240 if fileid is not None:
241 self._fileid = fileid
241 self._fileid = fileid
242
242
243 @propertycache
243 @propertycache
244 def _changectx(self):
244 def _changectx(self):
245 return changectx(self._repo, self._changeid)
245 return changectx(self._repo, self._changeid)
246
246
247 @propertycache
247 @propertycache
248 def _filelog(self):
248 def _filelog(self):
249 return self._repo.file(self._path)
249 return self._repo.file(self._path)
250
250
251 @propertycache
251 @propertycache
252 def _changeid(self):
252 def _changeid(self):
253 if '_changectx' in self.__dict__:
253 if '_changectx' in self.__dict__:
254 return self._changectx.rev()
254 return self._changectx.rev()
255 else:
255 else:
256 return self._filelog.linkrev(self._filerev)
256 return self._filelog.linkrev(self._filerev)
257
257
258 @propertycache
258 @propertycache
259 def _filenode(self):
259 def _filenode(self):
260 if '_fileid' in self.__dict__:
260 if '_fileid' in self.__dict__:
261 return self._filelog.lookup(self._fileid)
261 return self._filelog.lookup(self._fileid)
262 else:
262 else:
263 return self._changectx.filenode(self._path)
263 return self._changectx.filenode(self._path)
264
264
265 @propertycache
265 @propertycache
266 def _filerev(self):
266 def _filerev(self):
267 return self._filelog.rev(self._filenode)
267 return self._filelog.rev(self._filenode)
268
268
269 @propertycache
269 @propertycache
270 def _repopath(self):
270 def _repopath(self):
271 return self._path
271 return self._path
272
272
273 def __nonzero__(self):
273 def __nonzero__(self):
274 try:
274 try:
275 self._filenode
275 self._filenode
276 return True
276 return True
277 except error.LookupError:
277 except error.LookupError:
278 # file is missing
278 # file is missing
279 return False
279 return False
280
280
281 def __str__(self):
281 def __str__(self):
282 return "%s@%s" % (self.path(), short(self.node()))
282 return "%s@%s" % (self.path(), short(self.node()))
283
283
284 def __repr__(self):
284 def __repr__(self):
285 return "<filectx %s>" % str(self)
285 return "<filectx %s>" % str(self)
286
286
287 def __hash__(self):
287 def __hash__(self):
288 try:
288 try:
289 return hash((self._path, self._filenode))
289 return hash((self._path, self._filenode))
290 except AttributeError:
290 except AttributeError:
291 return id(self)
291 return id(self)
292
292
293 def __eq__(self, other):
293 def __eq__(self, other):
294 try:
294 try:
295 return (self._path == other._path
295 return (self._path == other._path
296 and self._filenode == other._filenode)
296 and self._filenode == other._filenode)
297 except AttributeError:
297 except AttributeError:
298 return False
298 return False
299
299
300 def __ne__(self, other):
300 def __ne__(self, other):
301 return not (self == other)
301 return not (self == other)
302
302
303 def filectx(self, fileid):
303 def filectx(self, fileid):
304 '''opens an arbitrary revision of the file without
304 '''opens an arbitrary revision of the file without
305 opening a new filelog'''
305 opening a new filelog'''
306 return filectx(self._repo, self._path, fileid=fileid,
306 return filectx(self._repo, self._path, fileid=fileid,
307 filelog=self._filelog)
307 filelog=self._filelog)
308
308
309 def filerev(self):
309 def filerev(self):
310 return self._filerev
310 return self._filerev
311 def filenode(self):
311 def filenode(self):
312 return self._filenode
312 return self._filenode
313 def flags(self):
313 def flags(self):
314 return self._changectx.flags(self._path)
314 return self._changectx.flags(self._path)
315 def filelog(self):
315 def filelog(self):
316 return self._filelog
316 return self._filelog
317
317
318 def rev(self):
318 def rev(self):
319 if '_changectx' in self.__dict__:
319 if '_changectx' in self.__dict__:
320 return self._changectx.rev()
320 return self._changectx.rev()
321 if '_changeid' in self.__dict__:
321 if '_changeid' in self.__dict__:
322 return self._changectx.rev()
322 return self._changectx.rev()
323 return self._filelog.linkrev(self._filerev)
323 return self._filelog.linkrev(self._filerev)
324
324
325 def linkrev(self):
325 def linkrev(self):
326 return self._filelog.linkrev(self._filerev)
326 return self._filelog.linkrev(self._filerev)
327 def node(self):
327 def node(self):
328 return self._changectx.node()
328 return self._changectx.node()
329 def hex(self):
329 def hex(self):
330 return hex(self.node())
330 return hex(self.node())
331 def user(self):
331 def user(self):
332 return self._changectx.user()
332 return self._changectx.user()
333 def date(self):
333 def date(self):
334 return self._changectx.date()
334 return self._changectx.date()
335 def files(self):
335 def files(self):
336 return self._changectx.files()
336 return self._changectx.files()
337 def description(self):
337 def description(self):
338 return self._changectx.description()
338 return self._changectx.description()
339 def branch(self):
339 def branch(self):
340 return self._changectx.branch()
340 return self._changectx.branch()
341 def extra(self):
341 def extra(self):
342 return self._changectx.extra()
342 return self._changectx.extra()
343 def manifest(self):
343 def manifest(self):
344 return self._changectx.manifest()
344 return self._changectx.manifest()
345 def changectx(self):
345 def changectx(self):
346 return self._changectx
346 return self._changectx
347
347
348 def data(self):
348 def data(self):
349 return self._filelog.read(self._filenode)
349 return self._filelog.read(self._filenode)
350 def path(self):
350 def path(self):
351 return self._path
351 return self._path
352 def size(self):
352 def size(self):
353 return self._filelog.size(self._filerev)
353 return self._filelog.size(self._filerev)
354
354
355 def cmp(self, text):
355 def cmp(self, text):
356 """compare text with stored file revision
356 """compare text with stored file revision
357
357
358 returns True if text is different than what is stored.
358 returns True if text is different than what is stored.
359 """
359 """
360 return self._filelog.cmp(self._filenode, text)
360 return self._filelog.cmp(self._filenode, text)
361
361
362 def renamed(self):
362 def renamed(self):
363 """check if file was actually renamed in this changeset revision
363 """check if file was actually renamed in this changeset revision
364
364
365 If rename logged in file revision, we report copy for changeset only
365 If rename logged in file revision, we report copy for changeset only
366 if file revisions linkrev points back to the changeset in question
366 if file revisions linkrev points back to the changeset in question
367 or both changeset parents contain different file revisions.
367 or both changeset parents contain different file revisions.
368 """
368 """
369
369
370 renamed = self._filelog.renamed(self._filenode)
370 renamed = self._filelog.renamed(self._filenode)
371 if not renamed:
371 if not renamed:
372 return renamed
372 return renamed
373
373
374 if self.rev() == self.linkrev():
374 if self.rev() == self.linkrev():
375 return renamed
375 return renamed
376
376
377 name = self.path()
377 name = self.path()
378 fnode = self._filenode
378 fnode = self._filenode
379 for p in self._changectx.parents():
379 for p in self._changectx.parents():
380 try:
380 try:
381 if fnode == p.filenode(name):
381 if fnode == p.filenode(name):
382 return None
382 return None
383 except error.LookupError:
383 except error.LookupError:
384 pass
384 pass
385 return renamed
385 return renamed
386
386
387 def parents(self):
387 def parents(self):
388 p = self._path
388 p = self._path
389 fl = self._filelog
389 fl = self._filelog
390 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
390 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
391
391
392 r = self._filelog.renamed(self._filenode)
392 r = self._filelog.renamed(self._filenode)
393 if r:
393 if r:
394 pl[0] = (r[0], r[1], None)
394 pl[0] = (r[0], r[1], None)
395
395
396 return [filectx(self._repo, p, fileid=n, filelog=l)
396 return [filectx(self._repo, p, fileid=n, filelog=l)
397 for p, n, l in pl if n != nullid]
397 for p, n, l in pl if n != nullid]
398
398
399 def children(self):
399 def children(self):
400 # hard for renames
400 # hard for renames
401 c = self._filelog.children(self._filenode)
401 c = self._filelog.children(self._filenode)
402 return [filectx(self._repo, self._path, fileid=x,
402 return [filectx(self._repo, self._path, fileid=x,
403 filelog=self._filelog) for x in c]
403 filelog=self._filelog) for x in c]
404
404
405 def annotate(self, follow=False, linenumber=None):
405 def annotate(self, follow=False, linenumber=None):
406 '''returns a list of tuples of (ctx, line) for each line
406 '''returns a list of tuples of (ctx, line) for each line
407 in the file, where ctx is the filectx of the node where
407 in the file, where ctx is the filectx of the node where
408 that line was last changed.
408 that line was last changed.
409 This returns tuples of ((ctx, linenumber), line) for each line,
409 This returns tuples of ((ctx, linenumber), line) for each line,
410 if "linenumber" parameter is NOT "None".
410 if "linenumber" parameter is NOT "None".
411 In such tuples, linenumber means one at the first appearance
411 In such tuples, linenumber means one at the first appearance
412 in the managed file.
412 in the managed file.
413 To reduce annotation cost,
413 To reduce annotation cost,
414 this returns fixed value(False is used) as linenumber,
414 this returns fixed value(False is used) as linenumber,
415 if "linenumber" parameter is "False".'''
415 if "linenumber" parameter is "False".'''
416
416
417 def decorate_compat(text, rev):
417 def decorate_compat(text, rev):
418 return ([rev] * len(text.splitlines()), text)
418 return ([rev] * len(text.splitlines()), text)
419
419
420 def without_linenumber(text, rev):
420 def without_linenumber(text, rev):
421 return ([(rev, False)] * len(text.splitlines()), text)
421 return ([(rev, False)] * len(text.splitlines()), text)
422
422
423 def with_linenumber(text, rev):
423 def with_linenumber(text, rev):
424 size = len(text.splitlines())
424 size = len(text.splitlines())
425 return ([(rev, i) for i in xrange(1, size + 1)], text)
425 return ([(rev, i) for i in xrange(1, size + 1)], text)
426
426
427 decorate = (((linenumber is None) and decorate_compat) or
427 decorate = (((linenumber is None) and decorate_compat) or
428 (linenumber and with_linenumber) or
428 (linenumber and with_linenumber) or
429 without_linenumber)
429 without_linenumber)
430
430
431 def pair(parent, child):
431 def pair(parent, child):
432 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
432 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
433 child[0][b1:b2] = parent[0][a1:a2]
433 child[0][b1:b2] = parent[0][a1:a2]
434 return child
434 return child
435
435
436 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
436 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
437 def getctx(path, fileid):
437 def getctx(path, fileid):
438 log = path == self._path and self._filelog or getlog(path)
438 log = path == self._path and self._filelog or getlog(path)
439 return filectx(self._repo, path, fileid=fileid, filelog=log)
439 return filectx(self._repo, path, fileid=fileid, filelog=log)
440 getctx = util.lrucachefunc(getctx)
440 getctx = util.lrucachefunc(getctx)
441
441
442 def parents(f):
442 def parents(f):
443 # we want to reuse filectx objects as much as possible
443 # we want to reuse filectx objects as much as possible
444 p = f._path
444 p = f._path
445 if f._filerev is None: # working dir
445 if f._filerev is None: # working dir
446 pl = [(n.path(), n.filerev()) for n in f.parents()]
446 pl = [(n.path(), n.filerev()) for n in f.parents()]
447 else:
447 else:
448 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
448 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
449
449
450 if follow:
450 if follow:
451 r = f.renamed()
451 r = f.renamed()
452 if r:
452 if r:
453 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
453 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
454
454
455 return [getctx(p, n) for p, n in pl if n != nullrev]
455 return [getctx(p, n) for p, n in pl if n != nullrev]
456
456
457 # use linkrev to find the first changeset where self appeared
457 # use linkrev to find the first changeset where self appeared
458 if self.rev() != self.linkrev():
458 if self.rev() != self.linkrev():
459 base = self.filectx(self.filerev())
459 base = self.filectx(self.filerev())
460 else:
460 else:
461 base = self
461 base = self
462
462
463 # find all ancestors
463 # find all ancestors
464 needed = {base: 1}
464 needed = {base: 1}
465 visit = [base]
465 visit = [base]
466 files = [base._path]
466 files = [base._path]
467 while visit:
467 while visit:
468 f = visit.pop(0)
468 f = visit.pop(0)
469 for p in parents(f):
469 for p in parents(f):
470 if p not in needed:
470 if p not in needed:
471 needed[p] = 1
471 needed[p] = 1
472 visit.append(p)
472 visit.append(p)
473 if p._path not in files:
473 if p._path not in files:
474 files.append(p._path)
474 files.append(p._path)
475 else:
475 else:
476 # count how many times we'll use this
476 # count how many times we'll use this
477 needed[p] += 1
477 needed[p] += 1
478
478
479 # sort by revision (per file) which is a topological order
479 # sort by revision (per file) which is a topological order
480 visit = []
480 visit = []
481 for f in files:
481 for f in files:
482 visit.extend(n for n in needed if n._path == f)
482 visit.extend(n for n in needed if n._path == f)
483
483
484 hist = {}
484 hist = {}
485 for f in sorted(visit, key=lambda x: x.rev()):
485 for f in sorted(visit, key=lambda x: x.rev()):
486 curr = decorate(f.data(), f)
486 curr = decorate(f.data(), f)
487 for p in parents(f):
487 for p in parents(f):
488 curr = pair(hist[p], curr)
488 curr = pair(hist[p], curr)
489 # trim the history of unneeded revs
489 # trim the history of unneeded revs
490 needed[p] -= 1
490 needed[p] -= 1
491 if not needed[p]:
491 if not needed[p]:
492 del hist[p]
492 del hist[p]
493 hist[f] = curr
493 hist[f] = curr
494
494
495 return zip(hist[f][0], hist[f][1].splitlines(True))
495 return zip(hist[f][0], hist[f][1].splitlines(True))
496
496
497 def ancestor(self, fc2, actx=None):
497 def ancestor(self, fc2, actx=None):
498 """
498 """
499 find the common ancestor file context, if any, of self, and fc2
499 find the common ancestor file context, if any, of self, and fc2
500
500
501 If actx is given, it must be the changectx of the common ancestor
501 If actx is given, it must be the changectx of the common ancestor
502 of self's and fc2's respective changesets.
502 of self's and fc2's respective changesets.
503 """
503 """
504
504
505 if actx is None:
505 if actx is None:
506 actx = self.changectx().ancestor(fc2.changectx())
506 actx = self.changectx().ancestor(fc2.changectx())
507
507
508 # the trivial case: changesets are unrelated, files must be too
508 # the trivial case: changesets are unrelated, files must be too
509 if not actx:
509 if not actx:
510 return None
510 return None
511
511
512 # the easy case: no (relevant) renames
512 # the easy case: no (relevant) renames
513 if fc2.path() == self.path() and self.path() in actx:
513 if fc2.path() == self.path() and self.path() in actx:
514 return actx[self.path()]
514 return actx[self.path()]
515 acache = {}
515 acache = {}
516
516
517 # prime the ancestor cache for the working directory
517 # prime the ancestor cache for the working directory
518 for c in (self, fc2):
518 for c in (self, fc2):
519 if c._filerev is None:
519 if c._filerev is None:
520 pl = [(n.path(), n.filenode()) for n in c.parents()]
520 pl = [(n.path(), n.filenode()) for n in c.parents()]
521 acache[(c._path, None)] = pl
521 acache[(c._path, None)] = pl
522
522
523 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
523 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
524 def parents(vertex):
524 def parents(vertex):
525 if vertex in acache:
525 if vertex in acache:
526 return acache[vertex]
526 return acache[vertex]
527 f, n = vertex
527 f, n = vertex
528 if f not in flcache:
528 if f not in flcache:
529 flcache[f] = self._repo.file(f)
529 flcache[f] = self._repo.file(f)
530 fl = flcache[f]
530 fl = flcache[f]
531 pl = [(f, p) for p in fl.parents(n) if p != nullid]
531 pl = [(f, p) for p in fl.parents(n) if p != nullid]
532 re = fl.renamed(n)
532 re = fl.renamed(n)
533 if re:
533 if re:
534 pl.append(re)
534 pl.append(re)
535 acache[vertex] = pl
535 acache[vertex] = pl
536 return pl
536 return pl
537
537
538 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
538 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
539 v = ancestor.ancestor(a, b, parents)
539 v = ancestor.ancestor(a, b, parents)
540 if v:
540 if v:
541 f, n = v
541 f, n = v
542 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
542 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
543
543
544 return None
544 return None
545
545
546 def ancestors(self):
546 def ancestors(self):
547 seen = set(str(self))
547 seen = set(str(self))
548 visit = [self]
548 visit = [self]
549 while visit:
549 while visit:
550 for parent in visit.pop(0).parents():
550 for parent in visit.pop(0).parents():
551 s = str(parent)
551 s = str(parent)
552 if s not in seen:
552 if s not in seen:
553 visit.append(parent)
553 visit.append(parent)
554 seen.add(s)
554 seen.add(s)
555 yield parent
555 yield parent
556
556
557 class workingctx(changectx):
557 class workingctx(changectx):
558 """A workingctx object makes access to data related to
558 """A workingctx object makes access to data related to
559 the current working directory convenient.
559 the current working directory convenient.
560 date - any valid date string or (unixtime, offset), or None.
560 date - any valid date string or (unixtime, offset), or None.
561 user - username string, or None.
561 user - username string, or None.
562 extra - a dictionary of extra values, or None.
562 extra - a dictionary of extra values, or None.
563 changes - a list of file lists as returned by localrepo.status()
563 changes - a list of file lists as returned by localrepo.status()
564 or None to use the repository status.
564 or None to use the repository status.
565 """
565 """
566 def __init__(self, repo, text="", user=None, date=None, extra=None,
566 def __init__(self, repo, text="", user=None, date=None, extra=None,
567 changes=None):
567 changes=None):
568 self._repo = repo
568 self._repo = repo
569 self._rev = None
569 self._rev = None
570 self._node = None
570 self._node = None
571 self._text = text
571 self._text = text
572 if date:
572 if date:
573 self._date = util.parsedate(date)
573 self._date = util.parsedate(date)
574 if user:
574 if user:
575 self._user = user
575 self._user = user
576 if changes:
576 if changes:
577 self._status = list(changes[:4])
577 self._status = list(changes[:4])
578 self._unknown = changes[4]
578 self._unknown = changes[4]
579 self._ignored = changes[5]
579 self._ignored = changes[5]
580 self._clean = changes[6]
580 self._clean = changes[6]
581 else:
581 else:
582 self._unknown = None
582 self._unknown = None
583 self._ignored = None
583 self._ignored = None
584 self._clean = None
584 self._clean = None
585
585
586 self._extra = {}
586 self._extra = {}
587 if extra:
587 if extra:
588 self._extra = extra.copy()
588 self._extra = extra.copy()
589 if 'branch' not in self._extra:
589 if 'branch' not in self._extra:
590 branch = self._repo.dirstate.branch()
590 branch = self._repo.dirstate.branch()
591 try:
591 try:
592 branch = branch.decode('UTF-8').encode('UTF-8')
592 branch = branch.decode('UTF-8').encode('UTF-8')
593 except UnicodeDecodeError:
593 except UnicodeDecodeError:
594 raise util.Abort(_('branch name not in UTF-8!'))
594 raise util.Abort(_('branch name not in UTF-8!'))
595 self._extra['branch'] = branch
595 self._extra['branch'] = branch
596 if self._extra['branch'] == '':
596 if self._extra['branch'] == '':
597 self._extra['branch'] = 'default'
597 self._extra['branch'] = 'default'
598
598
599 def __str__(self):
599 def __str__(self):
600 return str(self._parents[0]) + "+"
600 return str(self._parents[0]) + "+"
601
601
602 def __nonzero__(self):
602 def __nonzero__(self):
603 return True
603 return True
604
604
605 def __contains__(self, key):
605 def __contains__(self, key):
606 return self._repo.dirstate[key] not in "?r"
606 return self._repo.dirstate[key] not in "?r"
607
607
608 @propertycache
608 @propertycache
609 def _manifest(self):
609 def _manifest(self):
610 """generate a manifest corresponding to the working directory"""
610 """generate a manifest corresponding to the working directory"""
611
611
612 if self._unknown is None:
612 if self._unknown is None:
613 self.status(unknown=True)
613 self.status(unknown=True)
614
614
615 man = self._parents[0].manifest().copy()
615 man = self._parents[0].manifest().copy()
616 copied = self._repo.dirstate.copies()
616 copied = self._repo.dirstate.copies()
617 if len(self._parents) > 1:
617 if len(self._parents) > 1:
618 man2 = self.p2().manifest()
618 man2 = self.p2().manifest()
619 def getman(f):
619 def getman(f):
620 if f in man:
620 if f in man:
621 return man
621 return man
622 return man2
622 return man2
623 else:
623 else:
624 getman = lambda f: man
624 getman = lambda f: man
625 def cf(f):
625 def cf(f):
626 f = copied.get(f, f)
626 f = copied.get(f, f)
627 return getman(f).flags(f)
627 return getman(f).flags(f)
628 ff = self._repo.dirstate.flagfunc(cf)
628 ff = self._repo.dirstate.flagfunc(cf)
629 modified, added, removed, deleted = self._status
629 modified, added, removed, deleted = self._status
630 unknown = self._unknown
630 unknown = self._unknown
631 for i, l in (("a", added), ("m", modified), ("u", unknown)):
631 for i, l in (("a", added), ("m", modified), ("u", unknown)):
632 for f in l:
632 for f in l:
633 orig = copied.get(f, f)
633 orig = copied.get(f, f)
634 man[f] = getman(orig).get(orig, nullid) + i
634 man[f] = getman(orig).get(orig, nullid) + i
635 try:
635 try:
636 man.set(f, ff(f))
636 man.set(f, ff(f))
637 except OSError:
637 except OSError:
638 pass
638 pass
639
639
640 for f in deleted + removed:
640 for f in deleted + removed:
641 if f in man:
641 if f in man:
642 del man[f]
642 del man[f]
643
643
644 return man
644 return man
645
645
646 @propertycache
646 @propertycache
647 def _status(self):
647 def _status(self):
648 return self._repo.status()[:4]
648 return self._repo.status()[:4]
649
649
650 @propertycache
650 @propertycache
651 def _user(self):
651 def _user(self):
652 return self._repo.ui.username()
652 return self._repo.ui.username()
653
653
654 @propertycache
654 @propertycache
655 def _date(self):
655 def _date(self):
656 return util.makedate()
656 return util.makedate()
657
657
658 @propertycache
658 @propertycache
659 def _parents(self):
659 def _parents(self):
660 p = self._repo.dirstate.parents()
660 p = self._repo.dirstate.parents()
661 if p[1] == nullid:
661 if p[1] == nullid:
662 p = p[:-1]
662 p = p[:-1]
663 self._parents = [changectx(self._repo, x) for x in p]
663 self._parents = [changectx(self._repo, x) for x in p]
664 return self._parents
664 return self._parents
665
665
666 def status(self, ignored=False, clean=False, unknown=False):
666 def status(self, ignored=False, clean=False, unknown=False):
667 """Explicit status query
667 """Explicit status query
668 Unless this method is used to query the working copy status, the
668 Unless this method is used to query the working copy status, the
669 _status property will implicitly read the status using its default
669 _status property will implicitly read the status using its default
670 arguments."""
670 arguments."""
671 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
671 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
672 self._unknown = self._ignored = self._clean = None
672 self._unknown = self._ignored = self._clean = None
673 if unknown:
673 if unknown:
674 self._unknown = stat[4]
674 self._unknown = stat[4]
675 if ignored:
675 if ignored:
676 self._ignored = stat[5]
676 self._ignored = stat[5]
677 if clean:
677 if clean:
678 self._clean = stat[6]
678 self._clean = stat[6]
679 self._status = stat[:4]
679 self._status = stat[:4]
680 return stat
680 return stat
681
681
682 def manifest(self):
682 def manifest(self):
683 return self._manifest
683 return self._manifest
684 def user(self):
684 def user(self):
685 return self._user or self._repo.ui.username()
685 return self._user or self._repo.ui.username()
686 def date(self):
686 def date(self):
687 return self._date
687 return self._date
688 def description(self):
688 def description(self):
689 return self._text
689 return self._text
690 def files(self):
690 def files(self):
691 return sorted(self._status[0] + self._status[1] + self._status[2])
691 return sorted(self._status[0] + self._status[1] + self._status[2])
692
692
693 def modified(self):
693 def modified(self):
694 return self._status[0]
694 return self._status[0]
695 def added(self):
695 def added(self):
696 return self._status[1]
696 return self._status[1]
697 def removed(self):
697 def removed(self):
698 return self._status[2]
698 return self._status[2]
699 def deleted(self):
699 def deleted(self):
700 return self._status[3]
700 return self._status[3]
701 def unknown(self):
701 def unknown(self):
702 assert self._unknown is not None # must call status first
702 assert self._unknown is not None # must call status first
703 return self._unknown
703 return self._unknown
704 def ignored(self):
704 def ignored(self):
705 assert self._ignored is not None # must call status first
705 assert self._ignored is not None # must call status first
706 return self._ignored
706 return self._ignored
707 def clean(self):
707 def clean(self):
708 assert self._clean is not None # must call status first
708 assert self._clean is not None # must call status first
709 return self._clean
709 return self._clean
710 def branch(self):
710 def branch(self):
711 return self._extra['branch']
711 return self._extra['branch']
712 def extra(self):
712 def extra(self):
713 return self._extra
713 return self._extra
714
714
715 def tags(self):
715 def tags(self):
716 t = []
716 t = []
717 [t.extend(p.tags()) for p in self.parents()]
717 [t.extend(p.tags()) for p in self.parents()]
718 return t
718 return t
719
719
720 def children(self):
720 def children(self):
721 return []
721 return []
722
722
723 def flags(self, path):
723 def flags(self, path):
724 if '_manifest' in self.__dict__:
724 if '_manifest' in self.__dict__:
725 try:
725 try:
726 return self._manifest.flags(path)
726 return self._manifest.flags(path)
727 except KeyError:
727 except KeyError:
728 return ''
728 return ''
729
729
730 orig = self._repo.dirstate.copies().get(path, path)
730 orig = self._repo.dirstate.copies().get(path, path)
731
731
732 def findflag(ctx):
732 def findflag(ctx):
733 mnode = ctx.changeset()[0]
733 mnode = ctx.changeset()[0]
734 node, flag = self._repo.manifest.find(mnode, orig)
734 node, flag = self._repo.manifest.find(mnode, orig)
735 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
735 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
736 try:
736 try:
737 return ff(path)
737 return ff(path)
738 except OSError:
738 except OSError:
739 pass
739 pass
740
740
741 flag = findflag(self._parents[0])
741 flag = findflag(self._parents[0])
742 if flag is None and len(self.parents()) > 1:
742 if flag is None and len(self.parents()) > 1:
743 flag = findflag(self._parents[1])
743 flag = findflag(self._parents[1])
744 if flag is None or self._repo.dirstate[path] == 'r':
744 if flag is None or self._repo.dirstate[path] == 'r':
745 return ''
745 return ''
746 return flag
746 return flag
747
747
748 def filectx(self, path, filelog=None):
748 def filectx(self, path, filelog=None):
749 """get a file context from the working directory"""
749 """get a file context from the working directory"""
750 return workingfilectx(self._repo, path, workingctx=self,
750 return workingfilectx(self._repo, path, workingctx=self,
751 filelog=filelog)
751 filelog=filelog)
752
752
753 def ancestor(self, c2):
753 def ancestor(self, c2):
754 """return the ancestor context of self and c2"""
754 """return the ancestor context of self and c2"""
755 return self._parents[0].ancestor(c2) # punt on two parents for now
755 return self._parents[0].ancestor(c2) # punt on two parents for now
756
756
757 def walk(self, match):
757 def walk(self, match):
758 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
758 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
759 True, False))
759 True, False))
760
760
761 def dirty(self, missing=False):
761 def dirty(self, missing=False):
762 "check whether a working directory is modified"
762 "check whether a working directory is modified"
763 # check subrepos first
763 # check subrepos first
764 for s in self.substate:
764 for s in self.substate:
765 if self.sub(s).dirty():
765 if self.sub(s).dirty():
766 return True
766 return True
767 # check current working dir
767 # check current working dir
768 return (self.p2() or self.branch() != self.p1().branch() or
768 return (self.p2() or self.branch() != self.p1().branch() or
769 self.modified() or self.added() or self.removed() or
769 self.modified() or self.added() or self.removed() or
770 (missing and self.deleted()))
770 (missing and self.deleted()))
771
771
772 def add(self, list):
772 def add(self, list):
773 wlock = self._repo.wlock()
773 wlock = self._repo.wlock()
774 ui, ds = self._repo.ui, self._repo.dirstate
774 ui, ds = self._repo.ui, self._repo.dirstate
775 try:
775 try:
776 rejected = []
776 rejected = []
777 for f in list:
777 for f in list:
778 p = self._repo.wjoin(f)
778 p = self._repo.wjoin(f)
779 try:
779 try:
780 st = os.lstat(p)
780 st = os.lstat(p)
781 except:
781 except:
782 ui.warn(_("%s does not exist!\n") % f)
782 ui.warn(_("%s does not exist!\n") % f)
783 rejected.append(f)
783 rejected.append(f)
784 continue
784 continue
785 if st.st_size > 10000000:
785 if st.st_size > 10000000:
786 ui.warn(_("%s: up to %d MB of RAM may be required "
786 ui.warn(_("%s: up to %d MB of RAM may be required "
787 "to manage this file\n"
787 "to manage this file\n"
788 "(use 'hg revert %s' to cancel the "
788 "(use 'hg revert %s' to cancel the "
789 "pending addition)\n")
789 "pending addition)\n")
790 % (f, 3 * st.st_size // 1000000, f))
790 % (f, 3 * st.st_size // 1000000, f))
791 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
791 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
792 ui.warn(_("%s not added: only files and symlinks "
792 ui.warn(_("%s not added: only files and symlinks "
793 "supported currently\n") % f)
793 "supported currently\n") % f)
794 rejected.append(p)
794 rejected.append(p)
795 elif ds[f] in 'amn':
795 elif ds[f] in 'amn':
796 ui.warn(_("%s already tracked!\n") % f)
796 ui.warn(_("%s already tracked!\n") % f)
797 elif ds[f] == 'r':
797 elif ds[f] == 'r':
798 ds.normallookup(f)
798 ds.normallookup(f)
799 else:
799 else:
800 ds.add(f)
800 ds.add(f)
801 return rejected
801 return rejected
802 finally:
802 finally:
803 wlock.release()
803 wlock.release()
804
804
805 def forget(self, list):
805 def forget(self, list):
806 wlock = self._repo.wlock()
806 wlock = self._repo.wlock()
807 try:
807 try:
808 for f in list:
808 for f in list:
809 if self._repo.dirstate[f] != 'a':
809 if self._repo.dirstate[f] != 'a':
810 self._repo.ui.warn(_("%s not added!\n") % f)
810 self._repo.ui.warn(_("%s not added!\n") % f)
811 else:
811 else:
812 self._repo.dirstate.forget(f)
812 self._repo.dirstate.forget(f)
813 finally:
813 finally:
814 wlock.release()
814 wlock.release()
815
815
816 def remove(self, list, unlink=False):
816 def remove(self, list, unlink=False):
817 if unlink:
817 if unlink:
818 for f in list:
818 for f in list:
819 try:
819 try:
820 util.unlink(self._repo.wjoin(f))
820 util.unlink(self._repo.wjoin(f))
821 except OSError, inst:
821 except OSError, inst:
822 if inst.errno != errno.ENOENT:
822 if inst.errno != errno.ENOENT:
823 raise
823 raise
824 wlock = self._repo.wlock()
824 wlock = self._repo.wlock()
825 try:
825 try:
826 for f in list:
826 for f in list:
827 if unlink and os.path.exists(self._repo.wjoin(f)):
827 if unlink and os.path.exists(self._repo.wjoin(f)):
828 self._repo.ui.warn(_("%s still exists!\n") % f)
828 self._repo.ui.warn(_("%s still exists!\n") % f)
829 elif self._repo.dirstate[f] == 'a':
829 elif self._repo.dirstate[f] == 'a':
830 self._repo.dirstate.forget(f)
830 self._repo.dirstate.forget(f)
831 elif f not in self._repo.dirstate:
831 elif f not in self._repo.dirstate:
832 self._repo.ui.warn(_("%s not tracked!\n") % f)
832 self._repo.ui.warn(_("%s not tracked!\n") % f)
833 else:
833 else:
834 self._repo.dirstate.remove(f)
834 self._repo.dirstate.remove(f)
835 finally:
835 finally:
836 wlock.release()
836 wlock.release()
837
837
838 def undelete(self, list):
838 def undelete(self, list):
839 pctxs = self.parents()
839 pctxs = self.parents()
840 wlock = self._repo.wlock()
840 wlock = self._repo.wlock()
841 try:
841 try:
842 for f in list:
842 for f in list:
843 if self._repo.dirstate[f] != 'r':
843 if self._repo.dirstate[f] != 'r':
844 self._repo.ui.warn(_("%s not removed!\n") % f)
844 self._repo.ui.warn(_("%s not removed!\n") % f)
845 else:
845 else:
846 fctx = f in pctxs[0] and pctxs[0] or pctxs[1]
846 fctx = f in pctxs[0] and pctxs[0] or pctxs[1]
847 t = fctx.data()
847 t = fctx.data()
848 self._repo.wwrite(f, t, fctx.flags())
848 self._repo.wwrite(f, t, fctx.flags())
849 self._repo.dirstate.normal(f)
849 self._repo.dirstate.normal(f)
850 finally:
850 finally:
851 wlock.release()
851 wlock.release()
852
852
853 def copy(self, source, dest):
853 def copy(self, source, dest):
854 p = self._repo.wjoin(dest)
854 p = self._repo.wjoin(dest)
855 if not (os.path.exists(p) or os.path.islink(p)):
855 if not (os.path.exists(p) or os.path.islink(p)):
856 self._repo.ui.warn(_("%s does not exist!\n") % dest)
856 self._repo.ui.warn(_("%s does not exist!\n") % dest)
857 elif not (os.path.isfile(p) or os.path.islink(p)):
857 elif not (os.path.isfile(p) or os.path.islink(p)):
858 self._repo.ui.warn(_("copy failed: %s is not a file or a "
858 self._repo.ui.warn(_("copy failed: %s is not a file or a "
859 "symbolic link\n") % dest)
859 "symbolic link\n") % dest)
860 else:
860 else:
861 wlock = self._repo.wlock()
861 wlock = self._repo.wlock()
862 try:
862 try:
863 if self._repo.dirstate[dest] in '?r':
863 if self._repo.dirstate[dest] in '?r':
864 self._repo.dirstate.add(dest)
864 self._repo.dirstate.add(dest)
865 self._repo.dirstate.copy(source, dest)
865 self._repo.dirstate.copy(source, dest)
866 finally:
866 finally:
867 wlock.release()
867 wlock.release()
868
868
869 class workingfilectx(filectx):
869 class workingfilectx(filectx):
870 """A workingfilectx object makes access to data related to a particular
870 """A workingfilectx object makes access to data related to a particular
871 file in the working directory convenient."""
871 file in the working directory convenient."""
872 def __init__(self, repo, path, filelog=None, workingctx=None):
872 def __init__(self, repo, path, filelog=None, workingctx=None):
873 """changeid can be a changeset revision, node, or tag.
873 """changeid can be a changeset revision, node, or tag.
874 fileid can be a file revision or node."""
874 fileid can be a file revision or node."""
875 self._repo = repo
875 self._repo = repo
876 self._path = path
876 self._path = path
877 self._changeid = None
877 self._changeid = None
878 self._filerev = self._filenode = None
878 self._filerev = self._filenode = None
879
879
880 if filelog:
880 if filelog:
881 self._filelog = filelog
881 self._filelog = filelog
882 if workingctx:
882 if workingctx:
883 self._changectx = workingctx
883 self._changectx = workingctx
884
884
885 @propertycache
885 @propertycache
886 def _changectx(self):
886 def _changectx(self):
887 return workingctx(self._repo)
887 return workingctx(self._repo)
888
888
889 def __nonzero__(self):
889 def __nonzero__(self):
890 return True
890 return True
891
891
892 def __str__(self):
892 def __str__(self):
893 return "%s@%s" % (self.path(), self._changectx)
893 return "%s@%s" % (self.path(), self._changectx)
894
894
895 def data(self):
895 def data(self):
896 return self._repo.wread(self._path)
896 return self._repo.wread(self._path)
897 def renamed(self):
897 def renamed(self):
898 rp = self._repo.dirstate.copied(self._path)
898 rp = self._repo.dirstate.copied(self._path)
899 if not rp:
899 if not rp:
900 return None
900 return None
901 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
901 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
902
902
903 def parents(self):
903 def parents(self):
904 '''return parent filectxs, following copies if necessary'''
904 '''return parent filectxs, following copies if necessary'''
905 def filenode(ctx, path):
905 def filenode(ctx, path):
906 return ctx._manifest.get(path, nullid)
906 return ctx._manifest.get(path, nullid)
907
907
908 path = self._path
908 path = self._path
909 fl = self._filelog
909 fl = self._filelog
910 pcl = self._changectx._parents
910 pcl = self._changectx._parents
911 renamed = self.renamed()
911 renamed = self.renamed()
912
912
913 if renamed:
913 if renamed:
914 pl = [renamed + (None,)]
914 pl = [renamed + (None,)]
915 else:
915 else:
916 pl = [(path, filenode(pcl[0], path), fl)]
916 pl = [(path, filenode(pcl[0], path), fl)]
917
917
918 for pc in pcl[1:]:
918 for pc in pcl[1:]:
919 pl.append((path, filenode(pc, path), fl))
919 pl.append((path, filenode(pc, path), fl))
920
920
921 return [filectx(self._repo, p, fileid=n, filelog=l)
921 return [filectx(self._repo, p, fileid=n, filelog=l)
922 for p, n, l in pl if n != nullid]
922 for p, n, l in pl if n != nullid]
923
923
924 def children(self):
924 def children(self):
925 return []
925 return []
926
926
927 def size(self):
927 def size(self):
928 return os.lstat(self._repo.wjoin(self._path)).st_size
928 return os.lstat(self._repo.wjoin(self._path)).st_size
929 def date(self):
929 def date(self):
930 t, tz = self._changectx.date()
930 t, tz = self._changectx.date()
931 try:
931 try:
932 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
932 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
933 except OSError, err:
933 except OSError, err:
934 if err.errno != errno.ENOENT:
934 if err.errno != errno.ENOENT:
935 raise
935 raise
936 return (t, tz)
936 return (t, tz)
937
937
938 def cmp(self, text):
938 def cmp(self, text):
939 """compare text with disk content
939 """compare text with disk content
940
940
941 returns True if text is different than what is on disk.
941 returns True if text is different than what is on disk.
942 """
942 """
943 return self._repo.wread(self._path) != text
943 return self._repo.wread(self._path) != text
944
944
945 class memctx(object):
945 class memctx(object):
946 """Use memctx to perform in-memory commits via localrepo.commitctx().
946 """Use memctx to perform in-memory commits via localrepo.commitctx().
947
947
948 Revision information is supplied at initialization time while
948 Revision information is supplied at initialization time while
949 related files data and is made available through a callback
949 related files data and is made available through a callback
950 mechanism. 'repo' is the current localrepo, 'parents' is a
950 mechanism. 'repo' is the current localrepo, 'parents' is a
951 sequence of two parent revisions identifiers (pass None for every
951 sequence of two parent revisions identifiers (pass None for every
952 missing parent), 'text' is the commit message and 'files' lists
952 missing parent), 'text' is the commit message and 'files' lists
953 names of files touched by the revision (normalized and relative to
953 names of files touched by the revision (normalized and relative to
954 repository root).
954 repository root).
955
955
956 filectxfn(repo, memctx, path) is a callable receiving the
956 filectxfn(repo, memctx, path) is a callable receiving the
957 repository, the current memctx object and the normalized path of
957 repository, the current memctx object and the normalized path of
958 requested file, relative to repository root. It is fired by the
958 requested file, relative to repository root. It is fired by the
959 commit function for every file in 'files', but calls order is
959 commit function for every file in 'files', but calls order is
960 undefined. If the file is available in the revision being
960 undefined. If the file is available in the revision being
961 committed (updated or added), filectxfn returns a memfilectx
961 committed (updated or added), filectxfn returns a memfilectx
962 object. If the file was removed, filectxfn raises an
962 object. If the file was removed, filectxfn raises an
963 IOError. Moved files are represented by marking the source file
963 IOError. Moved files are represented by marking the source file
964 removed and the new file added with copy information (see
964 removed and the new file added with copy information (see
965 memfilectx).
965 memfilectx).
966
966
967 user receives the committer name and defaults to current
967 user receives the committer name and defaults to current
968 repository username, date is the commit date in any format
968 repository username, date is the commit date in any format
969 supported by util.parsedate() and defaults to current date, extra
969 supported by util.parsedate() and defaults to current date, extra
970 is a dictionary of metadata or is left empty.
970 is a dictionary of metadata or is left empty.
971 """
971 """
972 def __init__(self, repo, parents, text, files, filectxfn, user=None,
972 def __init__(self, repo, parents, text, files, filectxfn, user=None,
973 date=None, extra=None):
973 date=None, extra=None):
974 self._repo = repo
974 self._repo = repo
975 self._rev = None
975 self._rev = None
976 self._node = None
976 self._node = None
977 self._text = text
977 self._text = text
978 self._date = date and util.parsedate(date) or util.makedate()
978 self._date = date and util.parsedate(date) or util.makedate()
979 self._user = user
979 self._user = user
980 parents = [(p or nullid) for p in parents]
980 parents = [(p or nullid) for p in parents]
981 p1, p2 = parents
981 p1, p2 = parents
982 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
982 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
983 files = sorted(set(files))
983 files = sorted(set(files))
984 self._status = [files, [], [], [], []]
984 self._status = [files, [], [], [], []]
985 self._filectxfn = filectxfn
985 self._filectxfn = filectxfn
986
986
987 self._extra = extra and extra.copy() or {}
987 self._extra = extra and extra.copy() or {}
988 if 'branch' not in self._extra:
988 if 'branch' not in self._extra:
989 self._extra['branch'] = 'default'
989 self._extra['branch'] = 'default'
990 elif self._extra.get('branch') == '':
990 elif self._extra.get('branch') == '':
991 self._extra['branch'] = 'default'
991 self._extra['branch'] = 'default'
992
992
993 def __str__(self):
993 def __str__(self):
994 return str(self._parents[0]) + "+"
994 return str(self._parents[0]) + "+"
995
995
996 def __int__(self):
996 def __int__(self):
997 return self._rev
997 return self._rev
998
998
999 def __nonzero__(self):
999 def __nonzero__(self):
1000 return True
1000 return True
1001
1001
1002 def __getitem__(self, key):
1002 def __getitem__(self, key):
1003 return self.filectx(key)
1003 return self.filectx(key)
1004
1004
1005 def p1(self):
1005 def p1(self):
1006 return self._parents[0]
1006 return self._parents[0]
1007 def p2(self):
1007 def p2(self):
1008 return self._parents[1]
1008 return self._parents[1]
1009
1009
1010 def user(self):
1010 def user(self):
1011 return self._user or self._repo.ui.username()
1011 return self._user or self._repo.ui.username()
1012 def date(self):
1012 def date(self):
1013 return self._date
1013 return self._date
1014 def description(self):
1014 def description(self):
1015 return self._text
1015 return self._text
1016 def files(self):
1016 def files(self):
1017 return self.modified()
1017 return self.modified()
1018 def modified(self):
1018 def modified(self):
1019 return self._status[0]
1019 return self._status[0]
1020 def added(self):
1020 def added(self):
1021 return self._status[1]
1021 return self._status[1]
1022 def removed(self):
1022 def removed(self):
1023 return self._status[2]
1023 return self._status[2]
1024 def deleted(self):
1024 def deleted(self):
1025 return self._status[3]
1025 return self._status[3]
1026 def unknown(self):
1026 def unknown(self):
1027 return self._status[4]
1027 return self._status[4]
1028 def ignored(self):
1028 def ignored(self):
1029 return self._status[5]
1029 return self._status[5]
1030 def clean(self):
1030 def clean(self):
1031 return self._status[6]
1031 return self._status[6]
1032 def branch(self):
1032 def branch(self):
1033 return self._extra['branch']
1033 return self._extra['branch']
1034 def extra(self):
1034 def extra(self):
1035 return self._extra
1035 return self._extra
1036 def flags(self, f):
1036 def flags(self, f):
1037 return self[f].flags()
1037 return self[f].flags()
1038
1038
1039 def parents(self):
1039 def parents(self):
1040 """return contexts for each parent changeset"""
1040 """return contexts for each parent changeset"""
1041 return self._parents
1041 return self._parents
1042
1042
1043 def filectx(self, path, filelog=None):
1043 def filectx(self, path, filelog=None):
1044 """get a file context from the working directory"""
1044 """get a file context from the working directory"""
1045 return self._filectxfn(self._repo, self, path)
1045 return self._filectxfn(self._repo, self, path)
1046
1046
1047 def commit(self):
1047 def commit(self):
1048 """commit context to the repo"""
1048 """commit context to the repo"""
1049 return self._repo.commitctx(self)
1049 return self._repo.commitctx(self)
1050
1050
1051 class memfilectx(object):
1051 class memfilectx(object):
1052 """memfilectx represents an in-memory file to commit.
1052 """memfilectx represents an in-memory file to commit.
1053
1053
1054 See memctx for more details.
1054 See memctx for more details.
1055 """
1055 """
1056 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1056 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1057 """
1057 """
1058 path is the normalized file path relative to repository root.
1058 path is the normalized file path relative to repository root.
1059 data is the file content as a string.
1059 data is the file content as a string.
1060 islink is True if the file is a symbolic link.
1060 islink is True if the file is a symbolic link.
1061 isexec is True if the file is executable.
1061 isexec is True if the file is executable.
1062 copied is the source file path if current file was copied in the
1062 copied is the source file path if current file was copied in the
1063 revision being committed, or None."""
1063 revision being committed, or None."""
1064 self._path = path
1064 self._path = path
1065 self._data = data
1065 self._data = data
1066 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1066 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1067 self._copied = None
1067 self._copied = None
1068 if copied:
1068 if copied:
1069 self._copied = (copied, nullid)
1069 self._copied = (copied, nullid)
1070
1070
1071 def __nonzero__(self):
1071 def __nonzero__(self):
1072 return True
1072 return True
1073 def __str__(self):
1073 def __str__(self):
1074 return "%s@%s" % (self.path(), self._changectx)
1074 return "%s@%s" % (self.path(), self._changectx)
1075 def path(self):
1075 def path(self):
1076 return self._path
1076 return self._path
1077 def data(self):
1077 def data(self):
1078 return self._data
1078 return self._data
1079 def flags(self):
1079 def flags(self):
1080 return self._flags
1080 return self._flags
1081 def isexec(self):
1081 def isexec(self):
1082 return 'x' in self._flags
1082 return 'x' in self._flags
1083 def islink(self):
1083 def islink(self):
1084 return 'l' in self._flags
1084 return 'l' in self._flags
1085 def renamed(self):
1085 def renamed(self):
1086 return self._copied
1086 return self._copied
@@ -1,545 +1,545 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching 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 i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 def run():
14 def run():
15 "run the command in sys.argv"
15 "run the command in sys.argv"
16 sys.exit(dispatch(sys.argv[1:]))
16 sys.exit(dispatch(sys.argv[1:]))
17
17
18 def dispatch(args):
18 def dispatch(args):
19 "run the command specified in args"
19 "run the command specified in args"
20 try:
20 try:
21 u = uimod.ui()
21 u = uimod.ui()
22 if '--traceback' in args:
22 if '--traceback' in args:
23 u.setconfig('ui', 'traceback', 'on')
23 u.setconfig('ui', 'traceback', 'on')
24 except util.Abort, inst:
24 except util.Abort, inst:
25 sys.stderr.write(_("abort: %s\n") % inst)
25 sys.stderr.write(_("abort: %s\n") % inst)
26 return -1
26 return -1
27 except error.ParseError, inst:
27 except error.ParseError, inst:
28 if len(inst.args) > 1:
28 if len(inst.args) > 1:
29 sys.stderr.write(_("hg: parse error at %s: %s\n") %
29 sys.stderr.write(_("hg: parse error at %s: %s\n") %
30 (inst.args[1], inst.args[0]))
30 (inst.args[1], inst.args[0]))
31 else:
31 else:
32 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
32 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
33 return -1
33 return -1
34 return _runcatch(u, args)
34 return _runcatch(u, args)
35
35
36 def _runcatch(ui, args):
36 def _runcatch(ui, args):
37 def catchterm(*args):
37 def catchterm(*args):
38 raise error.SignalInterrupt
38 raise error.SignalInterrupt
39
39
40 try:
40 try:
41 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
41 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
42 num = getattr(signal, name, None)
42 num = getattr(signal, name, None)
43 if num:
43 if num:
44 signal.signal(num, catchterm)
44 signal.signal(num, catchterm)
45 except ValueError:
45 except ValueError:
46 pass # happens if called in a thread
46 pass # happens if called in a thread
47
47
48 try:
48 try:
49 try:
49 try:
50 # enter the debugger before command execution
50 # enter the debugger before command execution
51 if '--debugger' in args:
51 if '--debugger' in args:
52 pdb.set_trace()
52 pdb.set_trace()
53 try:
53 try:
54 return _dispatch(ui, args)
54 return _dispatch(ui, args)
55 finally:
55 finally:
56 ui.flush()
56 ui.flush()
57 except:
57 except:
58 # enter the debugger when we hit an exception
58 # enter the debugger when we hit an exception
59 if '--debugger' in args:
59 if '--debugger' in args:
60 pdb.post_mortem(sys.exc_info()[2])
60 pdb.post_mortem(sys.exc_info()[2])
61 ui.traceback()
61 ui.traceback()
62 raise
62 raise
63
63
64 # Global exception handling, alphabetically
64 # Global exception handling, alphabetically
65 # Mercurial-specific first, followed by built-in and library exceptions
65 # Mercurial-specific first, followed by built-in and library exceptions
66 except error.AmbiguousCommand, inst:
66 except error.AmbiguousCommand, inst:
67 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
67 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
68 (inst.args[0], " ".join(inst.args[1])))
68 (inst.args[0], " ".join(inst.args[1])))
69 except error.ParseError, inst:
69 except error.ParseError, inst:
70 if len(inst.args) > 1:
70 if len(inst.args) > 1:
71 ui.warn(_("hg: parse error at %s: %s\n") %
71 ui.warn(_("hg: parse error at %s: %s\n") %
72 (inst.args[1], inst.args[0]))
72 (inst.args[1], inst.args[0]))
73 else:
73 else:
74 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
74 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
75 return -1
75 return -1
76 except error.LockHeld, inst:
76 except error.LockHeld, inst:
77 if inst.errno == errno.ETIMEDOUT:
77 if inst.errno == errno.ETIMEDOUT:
78 reason = _('timed out waiting for lock held by %s') % inst.locker
78 reason = _('timed out waiting for lock held by %s') % inst.locker
79 else:
79 else:
80 reason = _('lock held by %s') % inst.locker
80 reason = _('lock held by %s') % inst.locker
81 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
81 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
82 except error.LockUnavailable, inst:
82 except error.LockUnavailable, inst:
83 ui.warn(_("abort: could not lock %s: %s\n") %
83 ui.warn(_("abort: could not lock %s: %s\n") %
84 (inst.desc or inst.filename, inst.strerror))
84 (inst.desc or inst.filename, inst.strerror))
85 except error.CommandError, inst:
85 except error.CommandError, inst:
86 if inst.args[0]:
86 if inst.args[0]:
87 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
87 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
88 commands.help_(ui, inst.args[0])
88 commands.help_(ui, inst.args[0])
89 else:
89 else:
90 ui.warn(_("hg: %s\n") % inst.args[1])
90 ui.warn(_("hg: %s\n") % inst.args[1])
91 commands.help_(ui, 'shortlist')
91 commands.help_(ui, 'shortlist')
92 except error.RepoError, inst:
92 except error.RepoError, inst:
93 ui.warn(_("abort: %s!\n") % inst)
93 ui.warn(_("abort: %s!\n") % inst)
94 except error.ResponseError, inst:
94 except error.ResponseError, inst:
95 ui.warn(_("abort: %s") % inst.args[0])
95 ui.warn(_("abort: %s") % inst.args[0])
96 if not isinstance(inst.args[1], basestring):
96 if not isinstance(inst.args[1], basestring):
97 ui.warn(" %r\n" % (inst.args[1],))
97 ui.warn(" %r\n" % (inst.args[1],))
98 elif not inst.args[1]:
98 elif not inst.args[1]:
99 ui.warn(_(" empty string\n"))
99 ui.warn(_(" empty string\n"))
100 else:
100 else:
101 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
101 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
102 except error.RevlogError, inst:
102 except error.RevlogError, inst:
103 ui.warn(_("abort: %s!\n") % inst)
103 ui.warn(_("abort: %s!\n") % inst)
104 except error.SignalInterrupt:
104 except error.SignalInterrupt:
105 ui.warn(_("killed!\n"))
105 ui.warn(_("killed!\n"))
106 except error.UnknownCommand, inst:
106 except error.UnknownCommand, inst:
107 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
107 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
108 try:
108 try:
109 # check if the command is in a disabled extension
109 # check if the command is in a disabled extension
110 # (but don't check for extensions themselves)
110 # (but don't check for extensions themselves)
111 commands.help_(ui, inst.args[0], unknowncmd=True)
111 commands.help_(ui, inst.args[0], unknowncmd=True)
112 except error.UnknownCommand:
112 except error.UnknownCommand:
113 commands.help_(ui, 'shortlist')
113 commands.help_(ui, 'shortlist')
114 except util.Abort, inst:
114 except util.Abort, inst:
115 ui.warn(_("abort: %s\n") % inst)
115 ui.warn(_("abort: %s\n") % inst)
116 except ImportError, inst:
116 except ImportError, inst:
117 ui.warn(_("abort: %s!\n") % inst)
117 ui.warn(_("abort: %s!\n") % inst)
118 m = str(inst).split()[-1]
118 m = str(inst).split()[-1]
119 if m in "mpatch bdiff".split():
119 if m in "mpatch bdiff".split():
120 ui.warn(_("(did you forget to compile extensions?)\n"))
120 ui.warn(_("(did you forget to compile extensions?)\n"))
121 elif m in "zlib".split():
121 elif m in "zlib".split():
122 ui.warn(_("(is your Python install correct?)\n"))
122 ui.warn(_("(is your Python install correct?)\n"))
123 except IOError, inst:
123 except IOError, inst:
124 if hasattr(inst, "code"):
124 if hasattr(inst, "code"):
125 ui.warn(_("abort: %s\n") % inst)
125 ui.warn(_("abort: %s\n") % inst)
126 elif hasattr(inst, "reason"):
126 elif hasattr(inst, "reason"):
127 try: # usually it is in the form (errno, strerror)
127 try: # usually it is in the form (errno, strerror)
128 reason = inst.reason.args[1]
128 reason = inst.reason.args[1]
129 except: # it might be anything, for example a string
129 except: # it might be anything, for example a string
130 reason = inst.reason
130 reason = inst.reason
131 ui.warn(_("abort: error: %s\n") % reason)
131 ui.warn(_("abort: error: %s\n") % reason)
132 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
132 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
133 if ui.debugflag:
133 if ui.debugflag:
134 ui.warn(_("broken pipe\n"))
134 ui.warn(_("broken pipe\n"))
135 elif getattr(inst, "strerror", None):
135 elif getattr(inst, "strerror", None):
136 if getattr(inst, "filename", None):
136 if getattr(inst, "filename", None):
137 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
137 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
138 else:
138 else:
139 ui.warn(_("abort: %s\n") % inst.strerror)
139 ui.warn(_("abort: %s\n") % inst.strerror)
140 else:
140 else:
141 raise
141 raise
142 except OSError, inst:
142 except OSError, inst:
143 if getattr(inst, "filename", None):
143 if getattr(inst, "filename", None):
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
145 else:
145 else:
146 ui.warn(_("abort: %s\n") % inst.strerror)
146 ui.warn(_("abort: %s\n") % inst.strerror)
147 except KeyboardInterrupt:
147 except KeyboardInterrupt:
148 try:
148 try:
149 ui.warn(_("interrupted!\n"))
149 ui.warn(_("interrupted!\n"))
150 except IOError, inst:
150 except IOError, inst:
151 if inst.errno == errno.EPIPE:
151 if inst.errno == errno.EPIPE:
152 if ui.debugflag:
152 if ui.debugflag:
153 ui.warn(_("\nbroken pipe\n"))
153 ui.warn(_("\nbroken pipe\n"))
154 else:
154 else:
155 raise
155 raise
156 except MemoryError:
156 except MemoryError:
157 ui.warn(_("abort: out of memory\n"))
157 ui.warn(_("abort: out of memory\n"))
158 except SystemExit, inst:
158 except SystemExit, inst:
159 # Commands shouldn't sys.exit directly, but give a return code.
159 # Commands shouldn't sys.exit directly, but give a return code.
160 # Just in case catch this and and pass exit code to caller.
160 # Just in case catch this and and pass exit code to caller.
161 return inst.code
161 return inst.code
162 except socket.error, inst:
162 except socket.error, inst:
163 ui.warn(_("abort: %s\n") % inst.args[-1])
163 ui.warn(_("abort: %s\n") % inst.args[-1])
164 except:
164 except:
165 ui.warn(_("** unknown exception encountered, details follow\n"))
165 ui.warn(_("** unknown exception encountered, details follow\n"))
166 ui.warn(_("** report bug details to "
166 ui.warn(_("** report bug details to "
167 "http://mercurial.selenic.com/bts/\n"))
167 "http://mercurial.selenic.com/bts/\n"))
168 ui.warn(_("** or mercurial@selenic.com\n"))
168 ui.warn(_("** or mercurial@selenic.com\n"))
169 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
169 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
170 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
170 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
171 % util.version())
171 % util.version())
172 ui.warn(_("** Extensions loaded: %s\n")
172 ui.warn(_("** Extensions loaded: %s\n")
173 % ", ".join([x[0] for x in extensions.extensions()]))
173 % ", ".join([x[0] for x in extensions.extensions()]))
174 raise
174 raise
175
175
176 return -1
176 return -1
177
177
178 def aliasargs(fn):
178 def aliasargs(fn):
179 if hasattr(fn, 'args'):
179 if hasattr(fn, 'args'):
180 return fn.args
180 return fn.args
181 return []
181 return []
182
182
183 class cmdalias(object):
183 class cmdalias(object):
184 def __init__(self, name, definition, cmdtable):
184 def __init__(self, name, definition, cmdtable):
185 self.name = self.cmd = name
185 self.name = self.cmd = name
186 self.definition = definition
186 self.definition = definition
187 self.args = []
187 self.args = []
188 self.opts = []
188 self.opts = []
189 self.help = ''
189 self.help = ''
190 self.norepo = True
190 self.norepo = True
191 self.badalias = False
191 self.badalias = False
192
192
193 try:
193 try:
194 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
194 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
195 for alias, e in cmdtable.iteritems():
195 for alias, e in cmdtable.iteritems():
196 if e is entry:
196 if e is entry:
197 self.cmd = alias
197 self.cmd = alias
198 break
198 break
199 self.shadows = True
199 self.shadows = True
200 except error.UnknownCommand:
200 except error.UnknownCommand:
201 self.shadows = False
201 self.shadows = False
202
202
203 if not self.definition:
203 if not self.definition:
204 def fn(ui, *args):
204 def fn(ui, *args):
205 ui.warn(_("no definition for alias '%s'\n") % self.name)
205 ui.warn(_("no definition for alias '%s'\n") % self.name)
206 return 1
206 return 1
207 self.fn = fn
207 self.fn = fn
208 self.badalias = True
208 self.badalias = True
209
209
210 return
210 return
211
211
212 args = shlex.split(self.definition)
212 args = shlex.split(self.definition)
213 cmd = args.pop(0)
213 cmd = args.pop(0)
214 args = map(util.expandpath, args)
214 args = map(util.expandpath, args)
215
215
216 try:
216 try:
217 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
217 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
218 if len(tableentry) > 2:
218 if len(tableentry) > 2:
219 self.fn, self.opts, self.help = tableentry
219 self.fn, self.opts, self.help = tableentry
220 else:
220 else:
221 self.fn, self.opts = tableentry
221 self.fn, self.opts = tableentry
222
222
223 self.args = aliasargs(self.fn) + args
223 self.args = aliasargs(self.fn) + args
224 if cmd not in commands.norepo.split(' '):
224 if cmd not in commands.norepo.split(' '):
225 self.norepo = False
225 self.norepo = False
226 if self.help.startswith("hg " + cmd):
226 if self.help.startswith("hg " + cmd):
227 # drop prefix in old-style help lines so hg shows the alias
227 # drop prefix in old-style help lines so hg shows the alias
228 self.help = self.help[4 + len(cmd):]
228 self.help = self.help[4 + len(cmd):]
229 self.__doc__ = self.fn.__doc__
229 self.__doc__ = self.fn.__doc__
230
230
231 except error.UnknownCommand:
231 except error.UnknownCommand:
232 def fn(ui, *args):
232 def fn(ui, *args):
233 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
233 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
234 % (self.name, cmd))
234 % (self.name, cmd))
235 try:
235 try:
236 # check if the command is in a disabled extension
236 # check if the command is in a disabled extension
237 commands.help_(ui, cmd, unknowncmd=True)
237 commands.help_(ui, cmd, unknowncmd=True)
238 except error.UnknownCommand:
238 except error.UnknownCommand:
239 pass
239 pass
240 return 1
240 return 1
241 self.fn = fn
241 self.fn = fn
242 self.badalias = True
242 self.badalias = True
243 except error.AmbiguousCommand:
243 except error.AmbiguousCommand:
244 def fn(ui, *args):
244 def fn(ui, *args):
245 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
245 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
246 % (self.name, cmd))
246 % (self.name, cmd))
247 return 1
247 return 1
248 self.fn = fn
248 self.fn = fn
249 self.badalias = True
249 self.badalias = True
250
250
251 def __call__(self, ui, *args, **opts):
251 def __call__(self, ui, *args, **opts):
252 if self.shadows:
252 if self.shadows:
253 ui.debug("alias '%s' shadows command\n" % self.name)
253 ui.debug("alias '%s' shadows command\n" % self.name)
254
254
255 return util.checksignature(self.fn)(ui, *args, **opts)
255 return util.checksignature(self.fn)(ui, *args, **opts)
256
256
257 def addaliases(ui, cmdtable):
257 def addaliases(ui, cmdtable):
258 # aliases are processed after extensions have been loaded, so they
258 # aliases are processed after extensions have been loaded, so they
259 # may use extension commands. Aliases can also use other alias definitions,
259 # may use extension commands. Aliases can also use other alias definitions,
260 # but only if they have been defined prior to the current definition.
260 # but only if they have been defined prior to the current definition.
261 for alias, definition in ui.configitems('alias'):
261 for alias, definition in ui.configitems('alias'):
262 aliasdef = cmdalias(alias, definition, cmdtable)
262 aliasdef = cmdalias(alias, definition, cmdtable)
263 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
263 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
264 if aliasdef.norepo:
264 if aliasdef.norepo:
265 commands.norepo += ' %s' % alias
265 commands.norepo += ' %s' % alias
266
266
267 def _parse(ui, args):
267 def _parse(ui, args):
268 options = {}
268 options = {}
269 cmdoptions = {}
269 cmdoptions = {}
270
270
271 try:
271 try:
272 args = fancyopts.fancyopts(args, commands.globalopts, options)
272 args = fancyopts.fancyopts(args, commands.globalopts, options)
273 except fancyopts.getopt.GetoptError, inst:
273 except fancyopts.getopt.GetoptError, inst:
274 raise error.CommandError(None, inst)
274 raise error.CommandError(None, inst)
275
275
276 if args:
276 if args:
277 cmd, args = args[0], args[1:]
277 cmd, args = args[0], args[1:]
278 aliases, entry = cmdutil.findcmd(cmd, commands.table,
278 aliases, entry = cmdutil.findcmd(cmd, commands.table,
279 ui.config("ui", "strict"))
279 ui.config("ui", "strict"))
280 cmd = aliases[0]
280 cmd = aliases[0]
281 args = aliasargs(entry[0]) + args
281 args = aliasargs(entry[0]) + args
282 defaults = ui.config("defaults", cmd)
282 defaults = ui.config("defaults", cmd)
283 if defaults:
283 if defaults:
284 args = map(util.expandpath, shlex.split(defaults)) + args
284 args = map(util.expandpath, shlex.split(defaults)) + args
285 c = list(entry[1])
285 c = list(entry[1])
286 else:
286 else:
287 cmd = None
287 cmd = None
288 c = []
288 c = []
289
289
290 # combine global options into local
290 # combine global options into local
291 for o in commands.globalopts:
291 for o in commands.globalopts:
292 c.append((o[0], o[1], options[o[1]], o[3]))
292 c.append((o[0], o[1], options[o[1]], o[3]))
293
293
294 try:
294 try:
295 args = fancyopts.fancyopts(args, c, cmdoptions, True)
295 args = fancyopts.fancyopts(args, c, cmdoptions, True)
296 except fancyopts.getopt.GetoptError, inst:
296 except fancyopts.getopt.GetoptError, inst:
297 raise error.CommandError(cmd, inst)
297 raise error.CommandError(cmd, inst)
298
298
299 # separate global options back out
299 # separate global options back out
300 for o in commands.globalopts:
300 for o in commands.globalopts:
301 n = o[1]
301 n = o[1]
302 options[n] = cmdoptions[n]
302 options[n] = cmdoptions[n]
303 del cmdoptions[n]
303 del cmdoptions[n]
304
304
305 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
305 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
306
306
307 def _parseconfig(ui, config):
307 def _parseconfig(ui, config):
308 """parse the --config options from the command line"""
308 """parse the --config options from the command line"""
309 for cfg in config:
309 for cfg in config:
310 try:
310 try:
311 name, value = cfg.split('=', 1)
311 name, value = cfg.split('=', 1)
312 section, name = name.split('.', 1)
312 section, name = name.split('.', 1)
313 if not section or not name:
313 if not section or not name:
314 raise IndexError
314 raise IndexError
315 ui.setconfig(section, name, value)
315 ui.setconfig(section, name, value)
316 except (IndexError, ValueError):
316 except (IndexError, ValueError):
317 raise util.Abort(_('malformed --config option: %r '
317 raise util.Abort(_('malformed --config option: %r '
318 '(use --config section.name=value)') % cfg)
318 '(use --config section.name=value)') % cfg)
319
319
320 def _earlygetopt(aliases, args):
320 def _earlygetopt(aliases, args):
321 """Return list of values for an option (or aliases).
321 """Return list of values for an option (or aliases).
322
322
323 The values are listed in the order they appear in args.
323 The values are listed in the order they appear in args.
324 The options and values are removed from args.
324 The options and values are removed from args.
325 """
325 """
326 try:
326 try:
327 argcount = args.index("--")
327 argcount = args.index("--")
328 except ValueError:
328 except ValueError:
329 argcount = len(args)
329 argcount = len(args)
330 shortopts = [opt for opt in aliases if len(opt) == 2]
330 shortopts = [opt for opt in aliases if len(opt) == 2]
331 values = []
331 values = []
332 pos = 0
332 pos = 0
333 while pos < argcount:
333 while pos < argcount:
334 if args[pos] in aliases:
334 if args[pos] in aliases:
335 if pos + 1 >= argcount:
335 if pos + 1 >= argcount:
336 # ignore and let getopt report an error if there is no value
336 # ignore and let getopt report an error if there is no value
337 break
337 break
338 del args[pos]
338 del args[pos]
339 values.append(args.pop(pos))
339 values.append(args.pop(pos))
340 argcount -= 2
340 argcount -= 2
341 elif args[pos][:2] in shortopts:
341 elif args[pos][:2] in shortopts:
342 # short option can have no following space, e.g. hg log -Rfoo
342 # short option can have no following space, e.g. hg log -Rfoo
343 values.append(args.pop(pos)[2:])
343 values.append(args.pop(pos)[2:])
344 argcount -= 1
344 argcount -= 1
345 else:
345 else:
346 pos += 1
346 pos += 1
347 return values
347 return values
348
348
349 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
349 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
350 # run pre-hook, and abort if it fails
350 # run pre-hook, and abort if it fails
351 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
351 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
352 pats=cmdpats, opts=cmdoptions)
352 pats=cmdpats, opts=cmdoptions)
353 if ret:
353 if ret:
354 return ret
354 return ret
355 ret = _runcommand(ui, options, cmd, d)
355 ret = _runcommand(ui, options, cmd, d)
356 # run post-hook, passing command result
356 # run post-hook, passing command result
357 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
357 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
358 result=ret, pats=cmdpats, opts=cmdoptions)
358 result=ret, pats=cmdpats, opts=cmdoptions)
359 return ret
359 return ret
360
360
361 _loaded = set()
361 _loaded = set()
362 def _dispatch(ui, args):
362 def _dispatch(ui, args):
363 # read --config before doing anything else
363 # read --config before doing anything else
364 # (e.g. to change trust settings for reading .hg/hgrc)
364 # (e.g. to change trust settings for reading .hg/hgrc)
365 _parseconfig(ui, _earlygetopt(['--config'], args))
365 _parseconfig(ui, _earlygetopt(['--config'], args))
366
366
367 # check for cwd
367 # check for cwd
368 cwd = _earlygetopt(['--cwd'], args)
368 cwd = _earlygetopt(['--cwd'], args)
369 if cwd:
369 if cwd:
370 os.chdir(cwd[-1])
370 os.chdir(cwd[-1])
371
371
372 # read the local repository .hgrc into a local ui object
372 # read the local repository .hgrc into a local ui object
373 try:
373 try:
374 wd = os.getcwd()
374 wd = os.getcwd()
375 except OSError, e:
375 except OSError, e:
376 raise util.Abort(_("error getting current working directory: %s") %
376 raise util.Abort(_("error getting current working directory: %s") %
377 e.strerror)
377 e.strerror)
378 path = cmdutil.findrepo(wd) or ""
378 path = cmdutil.findrepo(wd) or ""
379 if not path:
379 if not path:
380 lui = ui
380 lui = ui
381 else:
381 else:
382 try:
382 try:
383 lui = ui.copy()
383 lui = ui.copy()
384 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
384 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
385 except IOError:
385 except IOError:
386 pass
386 pass
387
387
388 # now we can expand paths, even ones in .hg/hgrc
388 # now we can expand paths, even ones in .hg/hgrc
389 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
389 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
390 if rpath:
390 if rpath:
391 path = lui.expandpath(rpath[-1])
391 path = lui.expandpath(rpath[-1])
392 lui = ui.copy()
392 lui = ui.copy()
393 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
393 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
394
394
395 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
395 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
396 # reposetup. Programs like TortoiseHg will call _dispatch several
396 # reposetup. Programs like TortoiseHg will call _dispatch several
397 # times so we keep track of configured extensions in _loaded.
397 # times so we keep track of configured extensions in _loaded.
398 extensions.loadall(lui)
398 extensions.loadall(lui)
399 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
399 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
400 # Propagate any changes to lui.__class__ by extensions
400 # Propagate any changes to lui.__class__ by extensions
401 ui.__class__ = lui.__class__
401 ui.__class__ = lui.__class__
402
402
403 # (uisetup and extsetup are handled in extensions.loadall)
403 # (uisetup and extsetup are handled in extensions.loadall)
404
404
405 for name, module in exts:
405 for name, module in exts:
406 cmdtable = getattr(module, 'cmdtable', {})
406 cmdtable = getattr(module, 'cmdtable', {})
407 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
407 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
408 if overrides:
408 if overrides:
409 ui.warn(_("extension '%s' overrides commands: %s\n")
409 ui.warn(_("extension '%s' overrides commands: %s\n")
410 % (name, " ".join(overrides)))
410 % (name, " ".join(overrides)))
411 commands.table.update(cmdtable)
411 commands.table.update(cmdtable)
412 _loaded.add(name)
412 _loaded.add(name)
413
413
414 # (reposetup is handled in hg.repository)
414 # (reposetup is handled in hg.repository)
415
415
416 addaliases(lui, commands.table)
416 addaliases(lui, commands.table)
417
417
418 # check for fallback encoding
418 # check for fallback encoding
419 fallback = lui.config('ui', 'fallbackencoding')
419 fallback = lui.config('ui', 'fallbackencoding')
420 if fallback:
420 if fallback:
421 encoding.fallbackencoding = fallback
421 encoding.fallbackencoding = fallback
422
422
423 fullargs = args
423 fullargs = args
424 cmd, func, args, options, cmdoptions = _parse(lui, args)
424 cmd, func, args, options, cmdoptions = _parse(lui, args)
425
425
426 if options["config"]:
426 if options["config"]:
427 raise util.Abort(_("Option --config may not be abbreviated!"))
427 raise util.Abort(_("option --config may not be abbreviated!"))
428 if options["cwd"]:
428 if options["cwd"]:
429 raise util.Abort(_("Option --cwd may not be abbreviated!"))
429 raise util.Abort(_("option --cwd may not be abbreviated!"))
430 if options["repository"]:
430 if options["repository"]:
431 raise util.Abort(_(
431 raise util.Abort(_(
432 "Option -R has to be separated from other options (e.g. not -qR) "
432 "Option -R has to be separated from other options (e.g. not -qR) "
433 "and --repository may only be abbreviated as --repo!"))
433 "and --repository may only be abbreviated as --repo!"))
434
434
435 if options["encoding"]:
435 if options["encoding"]:
436 encoding.encoding = options["encoding"]
436 encoding.encoding = options["encoding"]
437 if options["encodingmode"]:
437 if options["encodingmode"]:
438 encoding.encodingmode = options["encodingmode"]
438 encoding.encodingmode = options["encodingmode"]
439 if options["time"]:
439 if options["time"]:
440 def get_times():
440 def get_times():
441 t = os.times()
441 t = os.times()
442 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
442 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
443 t = (t[0], t[1], t[2], t[3], time.clock())
443 t = (t[0], t[1], t[2], t[3], time.clock())
444 return t
444 return t
445 s = get_times()
445 s = get_times()
446 def print_time():
446 def print_time():
447 t = get_times()
447 t = get_times()
448 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
448 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
449 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
449 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
450 atexit.register(print_time)
450 atexit.register(print_time)
451
451
452 if options['verbose'] or options['debug'] or options['quiet']:
452 if options['verbose'] or options['debug'] or options['quiet']:
453 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
453 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
454 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
454 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
455 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
455 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
456 if options['traceback']:
456 if options['traceback']:
457 ui.setconfig('ui', 'traceback', 'on')
457 ui.setconfig('ui', 'traceback', 'on')
458 if options['noninteractive']:
458 if options['noninteractive']:
459 ui.setconfig('ui', 'interactive', 'off')
459 ui.setconfig('ui', 'interactive', 'off')
460
460
461 if options['help']:
461 if options['help']:
462 return commands.help_(ui, cmd, options['version'])
462 return commands.help_(ui, cmd, options['version'])
463 elif options['version']:
463 elif options['version']:
464 return commands.version_(ui)
464 return commands.version_(ui)
465 elif not cmd:
465 elif not cmd:
466 return commands.help_(ui, 'shortlist')
466 return commands.help_(ui, 'shortlist')
467
467
468 repo = None
468 repo = None
469 cmdpats = args[:]
469 cmdpats = args[:]
470 if cmd not in commands.norepo.split():
470 if cmd not in commands.norepo.split():
471 try:
471 try:
472 repo = hg.repository(ui, path=path)
472 repo = hg.repository(ui, path=path)
473 ui = repo.ui
473 ui = repo.ui
474 if not repo.local():
474 if not repo.local():
475 raise util.Abort(_("repository '%s' is not local") % path)
475 raise util.Abort(_("repository '%s' is not local") % path)
476 ui.setconfig("bundle", "mainreporoot", repo.root)
476 ui.setconfig("bundle", "mainreporoot", repo.root)
477 except error.RepoError:
477 except error.RepoError:
478 if cmd not in commands.optionalrepo.split():
478 if cmd not in commands.optionalrepo.split():
479 if args and not path: # try to infer -R from command args
479 if args and not path: # try to infer -R from command args
480 repos = map(cmdutil.findrepo, args)
480 repos = map(cmdutil.findrepo, args)
481 guess = repos[0]
481 guess = repos[0]
482 if guess and repos.count(guess) == len(repos):
482 if guess and repos.count(guess) == len(repos):
483 return _dispatch(ui, ['--repository', guess] + fullargs)
483 return _dispatch(ui, ['--repository', guess] + fullargs)
484 if not path:
484 if not path:
485 raise error.RepoError(_("There is no Mercurial repository"
485 raise error.RepoError(_("There is no Mercurial repository"
486 " here (.hg not found)"))
486 " here (.hg not found)"))
487 raise
487 raise
488 args.insert(0, repo)
488 args.insert(0, repo)
489 elif rpath:
489 elif rpath:
490 ui.warn(_("warning: --repository ignored\n"))
490 ui.warn(_("warning: --repository ignored\n"))
491
491
492 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
492 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
493 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
493 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
494 cmdpats, cmdoptions)
494 cmdpats, cmdoptions)
495
495
496 def _runcommand(ui, options, cmd, cmdfunc):
496 def _runcommand(ui, options, cmd, cmdfunc):
497 def checkargs():
497 def checkargs():
498 try:
498 try:
499 return cmdfunc()
499 return cmdfunc()
500 except error.SignatureError:
500 except error.SignatureError:
501 raise error.CommandError(cmd, _("invalid arguments"))
501 raise error.CommandError(cmd, _("invalid arguments"))
502
502
503 if options['profile']:
503 if options['profile']:
504 format = ui.config('profiling', 'format', default='text')
504 format = ui.config('profiling', 'format', default='text')
505
505
506 if not format in ['text', 'kcachegrind']:
506 if not format in ['text', 'kcachegrind']:
507 ui.warn(_("unrecognized profiling format '%s'"
507 ui.warn(_("unrecognized profiling format '%s'"
508 " - Ignored\n") % format)
508 " - Ignored\n") % format)
509 format = 'text'
509 format = 'text'
510
510
511 output = ui.config('profiling', 'output')
511 output = ui.config('profiling', 'output')
512
512
513 if output:
513 if output:
514 path = ui.expandpath(output)
514 path = ui.expandpath(output)
515 ostream = open(path, 'wb')
515 ostream = open(path, 'wb')
516 else:
516 else:
517 ostream = sys.stderr
517 ostream = sys.stderr
518
518
519 try:
519 try:
520 from mercurial import lsprof
520 from mercurial import lsprof
521 except ImportError:
521 except ImportError:
522 raise util.Abort(_(
522 raise util.Abort(_(
523 'lsprof not available - install from '
523 'lsprof not available - install from '
524 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
524 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
525 p = lsprof.Profiler()
525 p = lsprof.Profiler()
526 p.enable(subcalls=True)
526 p.enable(subcalls=True)
527 try:
527 try:
528 return checkargs()
528 return checkargs()
529 finally:
529 finally:
530 p.disable()
530 p.disable()
531
531
532 if format == 'kcachegrind':
532 if format == 'kcachegrind':
533 import lsprofcalltree
533 import lsprofcalltree
534 calltree = lsprofcalltree.KCacheGrind(p)
534 calltree = lsprofcalltree.KCacheGrind(p)
535 calltree.output(ostream)
535 calltree.output(ostream)
536 else:
536 else:
537 # format == 'text'
537 # format == 'text'
538 stats = lsprof.Stats(p.getstats())
538 stats = lsprof.Stats(p.getstats())
539 stats.sort()
539 stats.sort()
540 stats.pprint(top=10, file=ostream, climit=5)
540 stats.pprint(top=10, file=ostream, climit=5)
541
541
542 if output:
542 if output:
543 ostream.close()
543 ostream.close()
544 else:
544 else:
545 return checkargs()
545 return checkargs()
@@ -1,155 +1,155 b''
1 # changelog bisection for mercurial
1 # changelog bisection for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall
3 # Copyright 2007 Matt Mackall
4 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
4 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
5 #
5 #
6 # Inspired by git bisect, extension skeleton taken from mq.py.
6 # Inspired by git bisect, extension skeleton taken from mq.py.
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2 or any later version.
9 # GNU General Public License version 2 or any later version.
10
10
11 import os
11 import os
12 from i18n import _
12 from i18n import _
13 from node import short, hex
13 from node import short, hex
14 import util
14 import util
15
15
16 def bisect(changelog, state):
16 def bisect(changelog, state):
17 """find the next node (if any) for testing during a bisect search.
17 """find the next node (if any) for testing during a bisect search.
18 returns a (nodes, number, good) tuple.
18 returns a (nodes, number, good) tuple.
19
19
20 'nodes' is the final result of the bisect if 'number' is 0.
20 'nodes' is the final result of the bisect if 'number' is 0.
21 Otherwise 'number' indicates the remaining possible candidates for
21 Otherwise 'number' indicates the remaining possible candidates for
22 the search and 'nodes' contains the next bisect target.
22 the search and 'nodes' contains the next bisect target.
23 'good' is True if bisect is searching for a first good changeset, False
23 'good' is True if bisect is searching for a first good changeset, False
24 if searching for a first bad one.
24 if searching for a first bad one.
25 """
25 """
26
26
27 clparents = changelog.parentrevs
27 clparents = changelog.parentrevs
28 skip = set([changelog.rev(n) for n in state['skip']])
28 skip = set([changelog.rev(n) for n in state['skip']])
29
29
30 def buildancestors(bad, good):
30 def buildancestors(bad, good):
31 # only the earliest bad revision matters
31 # only the earliest bad revision matters
32 badrev = min([changelog.rev(n) for n in bad])
32 badrev = min([changelog.rev(n) for n in bad])
33 goodrevs = [changelog.rev(n) for n in good]
33 goodrevs = [changelog.rev(n) for n in good]
34 goodrev = min(goodrevs)
34 goodrev = min(goodrevs)
35 # build visit array
35 # build visit array
36 ancestors = [None] * (len(changelog) + 1) # an extra for [-1]
36 ancestors = [None] * (len(changelog) + 1) # an extra for [-1]
37
37
38 # set nodes descended from goodrev
38 # set nodes descended from goodrev
39 ancestors[goodrev] = []
39 ancestors[goodrev] = []
40 for rev in xrange(goodrev + 1, len(changelog)):
40 for rev in xrange(goodrev + 1, len(changelog)):
41 for prev in clparents(rev):
41 for prev in clparents(rev):
42 if ancestors[prev] == []:
42 if ancestors[prev] == []:
43 ancestors[rev] = []
43 ancestors[rev] = []
44
44
45 # clear good revs from array
45 # clear good revs from array
46 for node in goodrevs:
46 for node in goodrevs:
47 ancestors[node] = None
47 ancestors[node] = None
48 for rev in xrange(len(changelog), -1, -1):
48 for rev in xrange(len(changelog), -1, -1):
49 if ancestors[rev] is None:
49 if ancestors[rev] is None:
50 for prev in clparents(rev):
50 for prev in clparents(rev):
51 ancestors[prev] = None
51 ancestors[prev] = None
52
52
53 if ancestors[badrev] is None:
53 if ancestors[badrev] is None:
54 return badrev, None
54 return badrev, None
55 return badrev, ancestors
55 return badrev, ancestors
56
56
57 good = 0
57 good = 0
58 badrev, ancestors = buildancestors(state['bad'], state['good'])
58 badrev, ancestors = buildancestors(state['bad'], state['good'])
59 if not ancestors: # looking for bad to good transition?
59 if not ancestors: # looking for bad to good transition?
60 good = 1
60 good = 1
61 badrev, ancestors = buildancestors(state['good'], state['bad'])
61 badrev, ancestors = buildancestors(state['good'], state['bad'])
62 bad = changelog.node(badrev)
62 bad = changelog.node(badrev)
63 if not ancestors: # now we're confused
63 if not ancestors: # now we're confused
64 if len(state['bad']) == 1 and len(state['good']) == 1:
64 if len(state['bad']) == 1 and len(state['good']) == 1:
65 raise util.Abort(_("starting revisions are not directly related"))
65 raise util.Abort(_("starting revisions are not directly related"))
66 raise util.Abort(_("Inconsistent state, %s:%s is good and bad")
66 raise util.Abort(_("inconsistent state, %s:%s is good and bad")
67 % (badrev, short(bad)))
67 % (badrev, short(bad)))
68
68
69 # build children dict
69 # build children dict
70 children = {}
70 children = {}
71 visit = [badrev]
71 visit = [badrev]
72 candidates = []
72 candidates = []
73 while visit:
73 while visit:
74 rev = visit.pop(0)
74 rev = visit.pop(0)
75 if ancestors[rev] == []:
75 if ancestors[rev] == []:
76 candidates.append(rev)
76 candidates.append(rev)
77 for prev in clparents(rev):
77 for prev in clparents(rev):
78 if prev != -1:
78 if prev != -1:
79 if prev in children:
79 if prev in children:
80 children[prev].append(rev)
80 children[prev].append(rev)
81 else:
81 else:
82 children[prev] = [rev]
82 children[prev] = [rev]
83 visit.append(prev)
83 visit.append(prev)
84
84
85 candidates.sort()
85 candidates.sort()
86 # have we narrowed it down to one entry?
86 # have we narrowed it down to one entry?
87 # or have all other possible candidates besides 'bad' have been skipped?
87 # or have all other possible candidates besides 'bad' have been skipped?
88 tot = len(candidates)
88 tot = len(candidates)
89 unskipped = [c for c in candidates if (c not in skip) and (c != badrev)]
89 unskipped = [c for c in candidates if (c not in skip) and (c != badrev)]
90 if tot == 1 or not unskipped:
90 if tot == 1 or not unskipped:
91 return ([changelog.node(rev) for rev in candidates], 0, good)
91 return ([changelog.node(rev) for rev in candidates], 0, good)
92 perfect = tot // 2
92 perfect = tot // 2
93
93
94 # find the best node to test
94 # find the best node to test
95 best_rev = None
95 best_rev = None
96 best_len = -1
96 best_len = -1
97 poison = set()
97 poison = set()
98 for rev in candidates:
98 for rev in candidates:
99 if rev in poison:
99 if rev in poison:
100 # poison children
100 # poison children
101 poison.update(children.get(rev, []))
101 poison.update(children.get(rev, []))
102 continue
102 continue
103
103
104 a = ancestors[rev] or [rev]
104 a = ancestors[rev] or [rev]
105 ancestors[rev] = None
105 ancestors[rev] = None
106
106
107 x = len(a) # number of ancestors
107 x = len(a) # number of ancestors
108 y = tot - x # number of non-ancestors
108 y = tot - x # number of non-ancestors
109 value = min(x, y) # how good is this test?
109 value = min(x, y) # how good is this test?
110 if value > best_len and rev not in skip:
110 if value > best_len and rev not in skip:
111 best_len = value
111 best_len = value
112 best_rev = rev
112 best_rev = rev
113 if value == perfect: # found a perfect candidate? quit early
113 if value == perfect: # found a perfect candidate? quit early
114 break
114 break
115
115
116 if y < perfect and rev not in skip: # all downhill from here?
116 if y < perfect and rev not in skip: # all downhill from here?
117 # poison children
117 # poison children
118 poison.update(children.get(rev, []))
118 poison.update(children.get(rev, []))
119 continue
119 continue
120
120
121 for c in children.get(rev, []):
121 for c in children.get(rev, []):
122 if ancestors[c]:
122 if ancestors[c]:
123 ancestors[c] = list(set(ancestors[c] + a))
123 ancestors[c] = list(set(ancestors[c] + a))
124 else:
124 else:
125 ancestors[c] = a + [c]
125 ancestors[c] = a + [c]
126
126
127 assert best_rev is not None
127 assert best_rev is not None
128 best_node = changelog.node(best_rev)
128 best_node = changelog.node(best_rev)
129
129
130 return ([best_node], tot, good)
130 return ([best_node], tot, good)
131
131
132
132
133 def load_state(repo):
133 def load_state(repo):
134 state = {'good': [], 'bad': [], 'skip': []}
134 state = {'good': [], 'bad': [], 'skip': []}
135 if os.path.exists(repo.join("bisect.state")):
135 if os.path.exists(repo.join("bisect.state")):
136 for l in repo.opener("bisect.state"):
136 for l in repo.opener("bisect.state"):
137 kind, node = l[:-1].split()
137 kind, node = l[:-1].split()
138 node = repo.lookup(node)
138 node = repo.lookup(node)
139 if kind not in state:
139 if kind not in state:
140 raise util.Abort(_("unknown bisect kind %s") % kind)
140 raise util.Abort(_("unknown bisect kind %s") % kind)
141 state[kind].append(node)
141 state[kind].append(node)
142 return state
142 return state
143
143
144
144
145 def save_state(repo, state):
145 def save_state(repo, state):
146 f = repo.opener("bisect.state", "w", atomictemp=True)
146 f = repo.opener("bisect.state", "w", atomictemp=True)
147 wlock = repo.wlock()
147 wlock = repo.wlock()
148 try:
148 try:
149 for kind in state:
149 for kind in state:
150 for node in state[kind]:
150 for node in state[kind]:
151 f.write("%s %s\n" % (kind, hex(node)))
151 f.write("%s %s\n" % (kind, hex(node)))
152 f.rename()
152 f.rename()
153 finally:
153 finally:
154 wlock.release()
154 wlock.release()
155
155
@@ -1,1873 +1,1873 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class 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 bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup, subrepo, discovery, pushkey
10 import repo, changegroup, subrepo, discovery, pushkey
11 import changelog, dirstate, filelog, manifest, context
11 import changelog, dirstate, filelog, manifest, context
12 import lock, transaction, store, encoding
12 import lock, transaction, store, encoding
13 import util, extensions, hook, error
13 import util, extensions, hook, error
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 import url as urlmod
17 import url as urlmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect
19 import weakref, errno, os, time, inspect
20 propertycache = util.propertycache
20 propertycache = util.propertycache
21
21
22 class localrepository(repo.repository):
22 class localrepository(repo.repository):
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
24 supported = set('revlogv1 store fncache shared'.split())
24 supported = set('revlogv1 store fncache shared'.split())
25
25
26 def __init__(self, baseui, path=None, create=0):
26 def __init__(self, baseui, path=None, create=0):
27 repo.repository.__init__(self)
27 repo.repository.__init__(self)
28 self.root = os.path.realpath(util.expandpath(path))
28 self.root = os.path.realpath(util.expandpath(path))
29 self.path = os.path.join(self.root, ".hg")
29 self.path = os.path.join(self.root, ".hg")
30 self.origroot = path
30 self.origroot = path
31 self.opener = util.opener(self.path)
31 self.opener = util.opener(self.path)
32 self.wopener = util.opener(self.root)
32 self.wopener = util.opener(self.root)
33 self.baseui = baseui
33 self.baseui = baseui
34 self.ui = baseui.copy()
34 self.ui = baseui.copy()
35
35
36 try:
36 try:
37 self.ui.readconfig(self.join("hgrc"), self.root)
37 self.ui.readconfig(self.join("hgrc"), self.root)
38 extensions.loadall(self.ui)
38 extensions.loadall(self.ui)
39 except IOError:
39 except IOError:
40 pass
40 pass
41
41
42 if not os.path.isdir(self.path):
42 if not os.path.isdir(self.path):
43 if create:
43 if create:
44 if not os.path.exists(path):
44 if not os.path.exists(path):
45 util.makedirs(path)
45 util.makedirs(path)
46 os.mkdir(self.path)
46 os.mkdir(self.path)
47 requirements = ["revlogv1"]
47 requirements = ["revlogv1"]
48 if self.ui.configbool('format', 'usestore', True):
48 if self.ui.configbool('format', 'usestore', True):
49 os.mkdir(os.path.join(self.path, "store"))
49 os.mkdir(os.path.join(self.path, "store"))
50 requirements.append("store")
50 requirements.append("store")
51 if self.ui.configbool('format', 'usefncache', True):
51 if self.ui.configbool('format', 'usefncache', True):
52 requirements.append("fncache")
52 requirements.append("fncache")
53 # create an invalid changelog
53 # create an invalid changelog
54 self.opener("00changelog.i", "a").write(
54 self.opener("00changelog.i", "a").write(
55 '\0\0\0\2' # represents revlogv2
55 '\0\0\0\2' # represents revlogv2
56 ' dummy changelog to prevent using the old repo layout'
56 ' dummy changelog to prevent using the old repo layout'
57 )
57 )
58 reqfile = self.opener("requires", "w")
58 reqfile = self.opener("requires", "w")
59 for r in requirements:
59 for r in requirements:
60 reqfile.write("%s\n" % r)
60 reqfile.write("%s\n" % r)
61 reqfile.close()
61 reqfile.close()
62 else:
62 else:
63 raise error.RepoError(_("repository %s not found") % path)
63 raise error.RepoError(_("repository %s not found") % path)
64 elif create:
64 elif create:
65 raise error.RepoError(_("repository %s already exists") % path)
65 raise error.RepoError(_("repository %s already exists") % path)
66 else:
66 else:
67 # find requirements
67 # find requirements
68 requirements = set()
68 requirements = set()
69 try:
69 try:
70 requirements = set(self.opener("requires").read().splitlines())
70 requirements = set(self.opener("requires").read().splitlines())
71 except IOError, inst:
71 except IOError, inst:
72 if inst.errno != errno.ENOENT:
72 if inst.errno != errno.ENOENT:
73 raise
73 raise
74 for r in requirements - self.supported:
74 for r in requirements - self.supported:
75 raise error.RepoError(_("requirement '%s' not supported") % r)
75 raise error.RepoError(_("requirement '%s' not supported") % r)
76
76
77 self.sharedpath = self.path
77 self.sharedpath = self.path
78 try:
78 try:
79 s = os.path.realpath(self.opener("sharedpath").read())
79 s = os.path.realpath(self.opener("sharedpath").read())
80 if not os.path.exists(s):
80 if not os.path.exists(s):
81 raise error.RepoError(
81 raise error.RepoError(
82 _('.hg/sharedpath points to nonexistent directory %s') % s)
82 _('.hg/sharedpath points to nonexistent directory %s') % s)
83 self.sharedpath = s
83 self.sharedpath = s
84 except IOError, inst:
84 except IOError, inst:
85 if inst.errno != errno.ENOENT:
85 if inst.errno != errno.ENOENT:
86 raise
86 raise
87
87
88 self.store = store.store(requirements, self.sharedpath, util.opener)
88 self.store = store.store(requirements, self.sharedpath, util.opener)
89 self.spath = self.store.path
89 self.spath = self.store.path
90 self.sopener = self.store.opener
90 self.sopener = self.store.opener
91 self.sjoin = self.store.join
91 self.sjoin = self.store.join
92 self.opener.createmode = self.store.createmode
92 self.opener.createmode = self.store.createmode
93 self.sopener.options = {}
93 self.sopener.options = {}
94
94
95 # These two define the set of tags for this repository. _tags
95 # These two define the set of tags for this repository. _tags
96 # maps tag name to node; _tagtypes maps tag name to 'global' or
96 # maps tag name to node; _tagtypes maps tag name to 'global' or
97 # 'local'. (Global tags are defined by .hgtags across all
97 # 'local'. (Global tags are defined by .hgtags across all
98 # heads, and local tags are defined in .hg/localtags.) They
98 # heads, and local tags are defined in .hg/localtags.) They
99 # constitute the in-memory cache of tags.
99 # constitute the in-memory cache of tags.
100 self._tags = None
100 self._tags = None
101 self._tagtypes = None
101 self._tagtypes = None
102
102
103 self._branchcache = None # in UTF-8
103 self._branchcache = None # in UTF-8
104 self._branchcachetip = None
104 self._branchcachetip = None
105 self.nodetagscache = None
105 self.nodetagscache = None
106 self.filterpats = {}
106 self.filterpats = {}
107 self._datafilters = {}
107 self._datafilters = {}
108 self._transref = self._lockref = self._wlockref = None
108 self._transref = self._lockref = self._wlockref = None
109
109
110 @propertycache
110 @propertycache
111 def changelog(self):
111 def changelog(self):
112 c = changelog.changelog(self.sopener)
112 c = changelog.changelog(self.sopener)
113 if 'HG_PENDING' in os.environ:
113 if 'HG_PENDING' in os.environ:
114 p = os.environ['HG_PENDING']
114 p = os.environ['HG_PENDING']
115 if p.startswith(self.root):
115 if p.startswith(self.root):
116 c.readpending('00changelog.i.a')
116 c.readpending('00changelog.i.a')
117 self.sopener.options['defversion'] = c.version
117 self.sopener.options['defversion'] = c.version
118 return c
118 return c
119
119
120 @propertycache
120 @propertycache
121 def manifest(self):
121 def manifest(self):
122 return manifest.manifest(self.sopener)
122 return manifest.manifest(self.sopener)
123
123
124 @propertycache
124 @propertycache
125 def dirstate(self):
125 def dirstate(self):
126 return dirstate.dirstate(self.opener, self.ui, self.root)
126 return dirstate.dirstate(self.opener, self.ui, self.root)
127
127
128 def __getitem__(self, changeid):
128 def __getitem__(self, changeid):
129 if changeid is None:
129 if changeid is None:
130 return context.workingctx(self)
130 return context.workingctx(self)
131 return context.changectx(self, changeid)
131 return context.changectx(self, changeid)
132
132
133 def __contains__(self, changeid):
133 def __contains__(self, changeid):
134 try:
134 try:
135 return bool(self.lookup(changeid))
135 return bool(self.lookup(changeid))
136 except error.RepoLookupError:
136 except error.RepoLookupError:
137 return False
137 return False
138
138
139 def __nonzero__(self):
139 def __nonzero__(self):
140 return True
140 return True
141
141
142 def __len__(self):
142 def __len__(self):
143 return len(self.changelog)
143 return len(self.changelog)
144
144
145 def __iter__(self):
145 def __iter__(self):
146 for i in xrange(len(self)):
146 for i in xrange(len(self)):
147 yield i
147 yield i
148
148
149 def url(self):
149 def url(self):
150 return 'file:' + self.root
150 return 'file:' + self.root
151
151
152 def hook(self, name, throw=False, **args):
152 def hook(self, name, throw=False, **args):
153 return hook.hook(self.ui, self, name, throw, **args)
153 return hook.hook(self.ui, self, name, throw, **args)
154
154
155 tag_disallowed = ':\r\n'
155 tag_disallowed = ':\r\n'
156
156
157 def _tag(self, names, node, message, local, user, date, extra={}):
157 def _tag(self, names, node, message, local, user, date, extra={}):
158 if isinstance(names, str):
158 if isinstance(names, str):
159 allchars = names
159 allchars = names
160 names = (names,)
160 names = (names,)
161 else:
161 else:
162 allchars = ''.join(names)
162 allchars = ''.join(names)
163 for c in self.tag_disallowed:
163 for c in self.tag_disallowed:
164 if c in allchars:
164 if c in allchars:
165 raise util.Abort(_('%r cannot be used in a tag name') % c)
165 raise util.Abort(_('%r cannot be used in a tag name') % c)
166
166
167 branches = self.branchmap()
167 branches = self.branchmap()
168 for name in names:
168 for name in names:
169 self.hook('pretag', throw=True, node=hex(node), tag=name,
169 self.hook('pretag', throw=True, node=hex(node), tag=name,
170 local=local)
170 local=local)
171 if name in branches:
171 if name in branches:
172 self.ui.warn(_("warning: tag %s conflicts with existing"
172 self.ui.warn(_("warning: tag %s conflicts with existing"
173 " branch name\n") % name)
173 " branch name\n") % name)
174
174
175 def writetags(fp, names, munge, prevtags):
175 def writetags(fp, names, munge, prevtags):
176 fp.seek(0, 2)
176 fp.seek(0, 2)
177 if prevtags and prevtags[-1] != '\n':
177 if prevtags and prevtags[-1] != '\n':
178 fp.write('\n')
178 fp.write('\n')
179 for name in names:
179 for name in names:
180 m = munge and munge(name) or name
180 m = munge and munge(name) or name
181 if self._tagtypes and name in self._tagtypes:
181 if self._tagtypes and name in self._tagtypes:
182 old = self._tags.get(name, nullid)
182 old = self._tags.get(name, nullid)
183 fp.write('%s %s\n' % (hex(old), m))
183 fp.write('%s %s\n' % (hex(old), m))
184 fp.write('%s %s\n' % (hex(node), m))
184 fp.write('%s %s\n' % (hex(node), m))
185 fp.close()
185 fp.close()
186
186
187 prevtags = ''
187 prevtags = ''
188 if local:
188 if local:
189 try:
189 try:
190 fp = self.opener('localtags', 'r+')
190 fp = self.opener('localtags', 'r+')
191 except IOError:
191 except IOError:
192 fp = self.opener('localtags', 'a')
192 fp = self.opener('localtags', 'a')
193 else:
193 else:
194 prevtags = fp.read()
194 prevtags = fp.read()
195
195
196 # local tags are stored in the current charset
196 # local tags are stored in the current charset
197 writetags(fp, names, None, prevtags)
197 writetags(fp, names, None, prevtags)
198 for name in names:
198 for name in names:
199 self.hook('tag', node=hex(node), tag=name, local=local)
199 self.hook('tag', node=hex(node), tag=name, local=local)
200 return
200 return
201
201
202 try:
202 try:
203 fp = self.wfile('.hgtags', 'rb+')
203 fp = self.wfile('.hgtags', 'rb+')
204 except IOError:
204 except IOError:
205 fp = self.wfile('.hgtags', 'ab')
205 fp = self.wfile('.hgtags', 'ab')
206 else:
206 else:
207 prevtags = fp.read()
207 prevtags = fp.read()
208
208
209 # committed tags are stored in UTF-8
209 # committed tags are stored in UTF-8
210 writetags(fp, names, encoding.fromlocal, prevtags)
210 writetags(fp, names, encoding.fromlocal, prevtags)
211
211
212 if '.hgtags' not in self.dirstate:
212 if '.hgtags' not in self.dirstate:
213 self[None].add(['.hgtags'])
213 self[None].add(['.hgtags'])
214
214
215 m = matchmod.exact(self.root, '', ['.hgtags'])
215 m = matchmod.exact(self.root, '', ['.hgtags'])
216 tagnode = self.commit(message, user, date, extra=extra, match=m)
216 tagnode = self.commit(message, user, date, extra=extra, match=m)
217
217
218 for name in names:
218 for name in names:
219 self.hook('tag', node=hex(node), tag=name, local=local)
219 self.hook('tag', node=hex(node), tag=name, local=local)
220
220
221 return tagnode
221 return tagnode
222
222
223 def tag(self, names, node, message, local, user, date):
223 def tag(self, names, node, message, local, user, date):
224 '''tag a revision with one or more symbolic names.
224 '''tag a revision with one or more symbolic names.
225
225
226 names is a list of strings or, when adding a single tag, names may be a
226 names is a list of strings or, when adding a single tag, names may be a
227 string.
227 string.
228
228
229 if local is True, the tags are stored in a per-repository file.
229 if local is True, the tags are stored in a per-repository file.
230 otherwise, they are stored in the .hgtags file, and a new
230 otherwise, they are stored in the .hgtags file, and a new
231 changeset is committed with the change.
231 changeset is committed with the change.
232
232
233 keyword arguments:
233 keyword arguments:
234
234
235 local: whether to store tags in non-version-controlled file
235 local: whether to store tags in non-version-controlled file
236 (default False)
236 (default False)
237
237
238 message: commit message to use if committing
238 message: commit message to use if committing
239
239
240 user: name of user to use if committing
240 user: name of user to use if committing
241
241
242 date: date tuple to use if committing'''
242 date: date tuple to use if committing'''
243
243
244 for x in self.status()[:5]:
244 for x in self.status()[:5]:
245 if '.hgtags' in x:
245 if '.hgtags' in x:
246 raise util.Abort(_('working copy of .hgtags is changed '
246 raise util.Abort(_('working copy of .hgtags is changed '
247 '(please commit .hgtags manually)'))
247 '(please commit .hgtags manually)'))
248
248
249 self.tags() # instantiate the cache
249 self.tags() # instantiate the cache
250 self._tag(names, node, message, local, user, date)
250 self._tag(names, node, message, local, user, date)
251
251
252 def tags(self):
252 def tags(self):
253 '''return a mapping of tag to node'''
253 '''return a mapping of tag to node'''
254 if self._tags is None:
254 if self._tags is None:
255 (self._tags, self._tagtypes) = self._findtags()
255 (self._tags, self._tagtypes) = self._findtags()
256
256
257 return self._tags
257 return self._tags
258
258
259 def _findtags(self):
259 def _findtags(self):
260 '''Do the hard work of finding tags. Return a pair of dicts
260 '''Do the hard work of finding tags. Return a pair of dicts
261 (tags, tagtypes) where tags maps tag name to node, and tagtypes
261 (tags, tagtypes) where tags maps tag name to node, and tagtypes
262 maps tag name to a string like \'global\' or \'local\'.
262 maps tag name to a string like \'global\' or \'local\'.
263 Subclasses or extensions are free to add their own tags, but
263 Subclasses or extensions are free to add their own tags, but
264 should be aware that the returned dicts will be retained for the
264 should be aware that the returned dicts will be retained for the
265 duration of the localrepo object.'''
265 duration of the localrepo object.'''
266
266
267 # XXX what tagtype should subclasses/extensions use? Currently
267 # XXX what tagtype should subclasses/extensions use? Currently
268 # mq and bookmarks add tags, but do not set the tagtype at all.
268 # mq and bookmarks add tags, but do not set the tagtype at all.
269 # Should each extension invent its own tag type? Should there
269 # Should each extension invent its own tag type? Should there
270 # be one tagtype for all such "virtual" tags? Or is the status
270 # be one tagtype for all such "virtual" tags? Or is the status
271 # quo fine?
271 # quo fine?
272
272
273 alltags = {} # map tag name to (node, hist)
273 alltags = {} # map tag name to (node, hist)
274 tagtypes = {}
274 tagtypes = {}
275
275
276 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
276 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
277 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
277 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
278
278
279 # Build the return dicts. Have to re-encode tag names because
279 # Build the return dicts. Have to re-encode tag names because
280 # the tags module always uses UTF-8 (in order not to lose info
280 # the tags module always uses UTF-8 (in order not to lose info
281 # writing to the cache), but the rest of Mercurial wants them in
281 # writing to the cache), but the rest of Mercurial wants them in
282 # local encoding.
282 # local encoding.
283 tags = {}
283 tags = {}
284 for (name, (node, hist)) in alltags.iteritems():
284 for (name, (node, hist)) in alltags.iteritems():
285 if node != nullid:
285 if node != nullid:
286 tags[encoding.tolocal(name)] = node
286 tags[encoding.tolocal(name)] = node
287 tags['tip'] = self.changelog.tip()
287 tags['tip'] = self.changelog.tip()
288 tagtypes = dict([(encoding.tolocal(name), value)
288 tagtypes = dict([(encoding.tolocal(name), value)
289 for (name, value) in tagtypes.iteritems()])
289 for (name, value) in tagtypes.iteritems()])
290 return (tags, tagtypes)
290 return (tags, tagtypes)
291
291
292 def tagtype(self, tagname):
292 def tagtype(self, tagname):
293 '''
293 '''
294 return the type of the given tag. result can be:
294 return the type of the given tag. result can be:
295
295
296 'local' : a local tag
296 'local' : a local tag
297 'global' : a global tag
297 'global' : a global tag
298 None : tag does not exist
298 None : tag does not exist
299 '''
299 '''
300
300
301 self.tags()
301 self.tags()
302
302
303 return self._tagtypes.get(tagname)
303 return self._tagtypes.get(tagname)
304
304
305 def tagslist(self):
305 def tagslist(self):
306 '''return a list of tags ordered by revision'''
306 '''return a list of tags ordered by revision'''
307 l = []
307 l = []
308 for t, n in self.tags().iteritems():
308 for t, n in self.tags().iteritems():
309 try:
309 try:
310 r = self.changelog.rev(n)
310 r = self.changelog.rev(n)
311 except:
311 except:
312 r = -2 # sort to the beginning of the list if unknown
312 r = -2 # sort to the beginning of the list if unknown
313 l.append((r, t, n))
313 l.append((r, t, n))
314 return [(t, n) for r, t, n in sorted(l)]
314 return [(t, n) for r, t, n in sorted(l)]
315
315
316 def nodetags(self, node):
316 def nodetags(self, node):
317 '''return the tags associated with a node'''
317 '''return the tags associated with a node'''
318 if not self.nodetagscache:
318 if not self.nodetagscache:
319 self.nodetagscache = {}
319 self.nodetagscache = {}
320 for t, n in self.tags().iteritems():
320 for t, n in self.tags().iteritems():
321 self.nodetagscache.setdefault(n, []).append(t)
321 self.nodetagscache.setdefault(n, []).append(t)
322 for tags in self.nodetagscache.itervalues():
322 for tags in self.nodetagscache.itervalues():
323 tags.sort()
323 tags.sort()
324 return self.nodetagscache.get(node, [])
324 return self.nodetagscache.get(node, [])
325
325
326 def _branchtags(self, partial, lrev):
326 def _branchtags(self, partial, lrev):
327 # TODO: rename this function?
327 # TODO: rename this function?
328 tiprev = len(self) - 1
328 tiprev = len(self) - 1
329 if lrev != tiprev:
329 if lrev != tiprev:
330 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
330 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
331 self._updatebranchcache(partial, ctxgen)
331 self._updatebranchcache(partial, ctxgen)
332 self._writebranchcache(partial, self.changelog.tip(), tiprev)
332 self._writebranchcache(partial, self.changelog.tip(), tiprev)
333
333
334 return partial
334 return partial
335
335
336 def branchmap(self):
336 def branchmap(self):
337 '''returns a dictionary {branch: [branchheads]}'''
337 '''returns a dictionary {branch: [branchheads]}'''
338 tip = self.changelog.tip()
338 tip = self.changelog.tip()
339 if self._branchcache is not None and self._branchcachetip == tip:
339 if self._branchcache is not None and self._branchcachetip == tip:
340 return self._branchcache
340 return self._branchcache
341
341
342 oldtip = self._branchcachetip
342 oldtip = self._branchcachetip
343 self._branchcachetip = tip
343 self._branchcachetip = tip
344 if oldtip is None or oldtip not in self.changelog.nodemap:
344 if oldtip is None or oldtip not in self.changelog.nodemap:
345 partial, last, lrev = self._readbranchcache()
345 partial, last, lrev = self._readbranchcache()
346 else:
346 else:
347 lrev = self.changelog.rev(oldtip)
347 lrev = self.changelog.rev(oldtip)
348 partial = self._branchcache
348 partial = self._branchcache
349
349
350 self._branchtags(partial, lrev)
350 self._branchtags(partial, lrev)
351 # this private cache holds all heads (not just tips)
351 # this private cache holds all heads (not just tips)
352 self._branchcache = partial
352 self._branchcache = partial
353
353
354 return self._branchcache
354 return self._branchcache
355
355
356 def branchtags(self):
356 def branchtags(self):
357 '''return a dict where branch names map to the tipmost head of
357 '''return a dict where branch names map to the tipmost head of
358 the branch, open heads come before closed'''
358 the branch, open heads come before closed'''
359 bt = {}
359 bt = {}
360 for bn, heads in self.branchmap().iteritems():
360 for bn, heads in self.branchmap().iteritems():
361 tip = heads[-1]
361 tip = heads[-1]
362 for h in reversed(heads):
362 for h in reversed(heads):
363 if 'close' not in self.changelog.read(h)[5]:
363 if 'close' not in self.changelog.read(h)[5]:
364 tip = h
364 tip = h
365 break
365 break
366 bt[bn] = tip
366 bt[bn] = tip
367 return bt
367 return bt
368
368
369
369
370 def _readbranchcache(self):
370 def _readbranchcache(self):
371 partial = {}
371 partial = {}
372 try:
372 try:
373 f = self.opener("branchheads.cache")
373 f = self.opener("branchheads.cache")
374 lines = f.read().split('\n')
374 lines = f.read().split('\n')
375 f.close()
375 f.close()
376 except (IOError, OSError):
376 except (IOError, OSError):
377 return {}, nullid, nullrev
377 return {}, nullid, nullrev
378
378
379 try:
379 try:
380 last, lrev = lines.pop(0).split(" ", 1)
380 last, lrev = lines.pop(0).split(" ", 1)
381 last, lrev = bin(last), int(lrev)
381 last, lrev = bin(last), int(lrev)
382 if lrev >= len(self) or self[lrev].node() != last:
382 if lrev >= len(self) or self[lrev].node() != last:
383 # invalidate the cache
383 # invalidate the cache
384 raise ValueError('invalidating branch cache (tip differs)')
384 raise ValueError('invalidating branch cache (tip differs)')
385 for l in lines:
385 for l in lines:
386 if not l:
386 if not l:
387 continue
387 continue
388 node, label = l.split(" ", 1)
388 node, label = l.split(" ", 1)
389 partial.setdefault(label.strip(), []).append(bin(node))
389 partial.setdefault(label.strip(), []).append(bin(node))
390 except KeyboardInterrupt:
390 except KeyboardInterrupt:
391 raise
391 raise
392 except Exception, inst:
392 except Exception, inst:
393 if self.ui.debugflag:
393 if self.ui.debugflag:
394 self.ui.warn(str(inst), '\n')
394 self.ui.warn(str(inst), '\n')
395 partial, last, lrev = {}, nullid, nullrev
395 partial, last, lrev = {}, nullid, nullrev
396 return partial, last, lrev
396 return partial, last, lrev
397
397
398 def _writebranchcache(self, branches, tip, tiprev):
398 def _writebranchcache(self, branches, tip, tiprev):
399 try:
399 try:
400 f = self.opener("branchheads.cache", "w", atomictemp=True)
400 f = self.opener("branchheads.cache", "w", atomictemp=True)
401 f.write("%s %s\n" % (hex(tip), tiprev))
401 f.write("%s %s\n" % (hex(tip), tiprev))
402 for label, nodes in branches.iteritems():
402 for label, nodes in branches.iteritems():
403 for node in nodes:
403 for node in nodes:
404 f.write("%s %s\n" % (hex(node), label))
404 f.write("%s %s\n" % (hex(node), label))
405 f.rename()
405 f.rename()
406 except (IOError, OSError):
406 except (IOError, OSError):
407 pass
407 pass
408
408
409 def _updatebranchcache(self, partial, ctxgen):
409 def _updatebranchcache(self, partial, ctxgen):
410 # collect new branch entries
410 # collect new branch entries
411 newbranches = {}
411 newbranches = {}
412 for c in ctxgen:
412 for c in ctxgen:
413 newbranches.setdefault(c.branch(), []).append(c.node())
413 newbranches.setdefault(c.branch(), []).append(c.node())
414 # if older branchheads are reachable from new ones, they aren't
414 # if older branchheads are reachable from new ones, they aren't
415 # really branchheads. Note checking parents is insufficient:
415 # really branchheads. Note checking parents is insufficient:
416 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
416 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
417 for branch, newnodes in newbranches.iteritems():
417 for branch, newnodes in newbranches.iteritems():
418 bheads = partial.setdefault(branch, [])
418 bheads = partial.setdefault(branch, [])
419 bheads.extend(newnodes)
419 bheads.extend(newnodes)
420 if len(bheads) <= 1:
420 if len(bheads) <= 1:
421 continue
421 continue
422 # starting from tip means fewer passes over reachable
422 # starting from tip means fewer passes over reachable
423 while newnodes:
423 while newnodes:
424 latest = newnodes.pop()
424 latest = newnodes.pop()
425 if latest not in bheads:
425 if latest not in bheads:
426 continue
426 continue
427 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
427 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
428 reachable = self.changelog.reachable(latest, minbhrev)
428 reachable = self.changelog.reachable(latest, minbhrev)
429 reachable.remove(latest)
429 reachable.remove(latest)
430 bheads = [b for b in bheads if b not in reachable]
430 bheads = [b for b in bheads if b not in reachable]
431 partial[branch] = bheads
431 partial[branch] = bheads
432
432
433 def lookup(self, key):
433 def lookup(self, key):
434 if isinstance(key, int):
434 if isinstance(key, int):
435 return self.changelog.node(key)
435 return self.changelog.node(key)
436 elif key == '.':
436 elif key == '.':
437 return self.dirstate.parents()[0]
437 return self.dirstate.parents()[0]
438 elif key == 'null':
438 elif key == 'null':
439 return nullid
439 return nullid
440 elif key == 'tip':
440 elif key == 'tip':
441 return self.changelog.tip()
441 return self.changelog.tip()
442 n = self.changelog._match(key)
442 n = self.changelog._match(key)
443 if n:
443 if n:
444 return n
444 return n
445 if key in self.tags():
445 if key in self.tags():
446 return self.tags()[key]
446 return self.tags()[key]
447 if key in self.branchtags():
447 if key in self.branchtags():
448 return self.branchtags()[key]
448 return self.branchtags()[key]
449 n = self.changelog._partialmatch(key)
449 n = self.changelog._partialmatch(key)
450 if n:
450 if n:
451 return n
451 return n
452
452
453 # can't find key, check if it might have come from damaged dirstate
453 # can't find key, check if it might have come from damaged dirstate
454 if key in self.dirstate.parents():
454 if key in self.dirstate.parents():
455 raise error.Abort(_("working directory has unknown parent '%s'!")
455 raise error.Abort(_("working directory has unknown parent '%s'!")
456 % short(key))
456 % short(key))
457 try:
457 try:
458 if len(key) == 20:
458 if len(key) == 20:
459 key = hex(key)
459 key = hex(key)
460 except:
460 except:
461 pass
461 pass
462 raise error.RepoLookupError(_("unknown revision '%s'") % key)
462 raise error.RepoLookupError(_("unknown revision '%s'") % key)
463
463
464 def lookupbranch(self, key, remote=None):
464 def lookupbranch(self, key, remote=None):
465 repo = remote or self
465 repo = remote or self
466 if key in repo.branchmap():
466 if key in repo.branchmap():
467 return key
467 return key
468
468
469 repo = (remote and remote.local()) and remote or self
469 repo = (remote and remote.local()) and remote or self
470 return repo[key].branch()
470 return repo[key].branch()
471
471
472 def local(self):
472 def local(self):
473 return True
473 return True
474
474
475 def join(self, f):
475 def join(self, f):
476 return os.path.join(self.path, f)
476 return os.path.join(self.path, f)
477
477
478 def wjoin(self, f):
478 def wjoin(self, f):
479 return os.path.join(self.root, f)
479 return os.path.join(self.root, f)
480
480
481 def rjoin(self, f):
481 def rjoin(self, f):
482 return os.path.join(self.root, util.pconvert(f))
482 return os.path.join(self.root, util.pconvert(f))
483
483
484 def file(self, f):
484 def file(self, f):
485 if f[0] == '/':
485 if f[0] == '/':
486 f = f[1:]
486 f = f[1:]
487 return filelog.filelog(self.sopener, f)
487 return filelog.filelog(self.sopener, f)
488
488
489 def changectx(self, changeid):
489 def changectx(self, changeid):
490 return self[changeid]
490 return self[changeid]
491
491
492 def parents(self, changeid=None):
492 def parents(self, changeid=None):
493 '''get list of changectxs for parents of changeid'''
493 '''get list of changectxs for parents of changeid'''
494 return self[changeid].parents()
494 return self[changeid].parents()
495
495
496 def filectx(self, path, changeid=None, fileid=None):
496 def filectx(self, path, changeid=None, fileid=None):
497 """changeid can be a changeset revision, node, or tag.
497 """changeid can be a changeset revision, node, or tag.
498 fileid can be a file revision or node."""
498 fileid can be a file revision or node."""
499 return context.filectx(self, path, changeid, fileid)
499 return context.filectx(self, path, changeid, fileid)
500
500
501 def getcwd(self):
501 def getcwd(self):
502 return self.dirstate.getcwd()
502 return self.dirstate.getcwd()
503
503
504 def pathto(self, f, cwd=None):
504 def pathto(self, f, cwd=None):
505 return self.dirstate.pathto(f, cwd)
505 return self.dirstate.pathto(f, cwd)
506
506
507 def wfile(self, f, mode='r'):
507 def wfile(self, f, mode='r'):
508 return self.wopener(f, mode)
508 return self.wopener(f, mode)
509
509
510 def _link(self, f):
510 def _link(self, f):
511 return os.path.islink(self.wjoin(f))
511 return os.path.islink(self.wjoin(f))
512
512
513 def _filter(self, filter, filename, data):
513 def _filter(self, filter, filename, data):
514 if filter not in self.filterpats:
514 if filter not in self.filterpats:
515 l = []
515 l = []
516 for pat, cmd in self.ui.configitems(filter):
516 for pat, cmd in self.ui.configitems(filter):
517 if cmd == '!':
517 if cmd == '!':
518 continue
518 continue
519 mf = matchmod.match(self.root, '', [pat])
519 mf = matchmod.match(self.root, '', [pat])
520 fn = None
520 fn = None
521 params = cmd
521 params = cmd
522 for name, filterfn in self._datafilters.iteritems():
522 for name, filterfn in self._datafilters.iteritems():
523 if cmd.startswith(name):
523 if cmd.startswith(name):
524 fn = filterfn
524 fn = filterfn
525 params = cmd[len(name):].lstrip()
525 params = cmd[len(name):].lstrip()
526 break
526 break
527 if not fn:
527 if not fn:
528 fn = lambda s, c, **kwargs: util.filter(s, c)
528 fn = lambda s, c, **kwargs: util.filter(s, c)
529 # Wrap old filters not supporting keyword arguments
529 # Wrap old filters not supporting keyword arguments
530 if not inspect.getargspec(fn)[2]:
530 if not inspect.getargspec(fn)[2]:
531 oldfn = fn
531 oldfn = fn
532 fn = lambda s, c, **kwargs: oldfn(s, c)
532 fn = lambda s, c, **kwargs: oldfn(s, c)
533 l.append((mf, fn, params))
533 l.append((mf, fn, params))
534 self.filterpats[filter] = l
534 self.filterpats[filter] = l
535
535
536 for mf, fn, cmd in self.filterpats[filter]:
536 for mf, fn, cmd in self.filterpats[filter]:
537 if mf(filename):
537 if mf(filename):
538 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
538 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
539 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
539 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
540 break
540 break
541
541
542 return data
542 return data
543
543
544 def adddatafilter(self, name, filter):
544 def adddatafilter(self, name, filter):
545 self._datafilters[name] = filter
545 self._datafilters[name] = filter
546
546
547 def wread(self, filename):
547 def wread(self, filename):
548 if self._link(filename):
548 if self._link(filename):
549 data = os.readlink(self.wjoin(filename))
549 data = os.readlink(self.wjoin(filename))
550 else:
550 else:
551 data = self.wopener(filename, 'r').read()
551 data = self.wopener(filename, 'r').read()
552 return self._filter("encode", filename, data)
552 return self._filter("encode", filename, data)
553
553
554 def wwrite(self, filename, data, flags):
554 def wwrite(self, filename, data, flags):
555 data = self._filter("decode", filename, data)
555 data = self._filter("decode", filename, data)
556 try:
556 try:
557 os.unlink(self.wjoin(filename))
557 os.unlink(self.wjoin(filename))
558 except OSError:
558 except OSError:
559 pass
559 pass
560 if 'l' in flags:
560 if 'l' in flags:
561 self.wopener.symlink(data, filename)
561 self.wopener.symlink(data, filename)
562 else:
562 else:
563 self.wopener(filename, 'w').write(data)
563 self.wopener(filename, 'w').write(data)
564 if 'x' in flags:
564 if 'x' in flags:
565 util.set_flags(self.wjoin(filename), False, True)
565 util.set_flags(self.wjoin(filename), False, True)
566
566
567 def wwritedata(self, filename, data):
567 def wwritedata(self, filename, data):
568 return self._filter("decode", filename, data)
568 return self._filter("decode", filename, data)
569
569
570 def transaction(self, desc):
570 def transaction(self, desc):
571 tr = self._transref and self._transref() or None
571 tr = self._transref and self._transref() or None
572 if tr and tr.running():
572 if tr and tr.running():
573 return tr.nest()
573 return tr.nest()
574
574
575 # abort here if the journal already exists
575 # abort here if the journal already exists
576 if os.path.exists(self.sjoin("journal")):
576 if os.path.exists(self.sjoin("journal")):
577 raise error.RepoError(
577 raise error.RepoError(
578 _("abandoned transaction found - run hg recover"))
578 _("abandoned transaction found - run hg recover"))
579
579
580 # save dirstate for rollback
580 # save dirstate for rollback
581 try:
581 try:
582 ds = self.opener("dirstate").read()
582 ds = self.opener("dirstate").read()
583 except IOError:
583 except IOError:
584 ds = ""
584 ds = ""
585 self.opener("journal.dirstate", "w").write(ds)
585 self.opener("journal.dirstate", "w").write(ds)
586 self.opener("journal.branch", "w").write(self.dirstate.branch())
586 self.opener("journal.branch", "w").write(self.dirstate.branch())
587 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
587 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
588
588
589 renames = [(self.sjoin("journal"), self.sjoin("undo")),
589 renames = [(self.sjoin("journal"), self.sjoin("undo")),
590 (self.join("journal.dirstate"), self.join("undo.dirstate")),
590 (self.join("journal.dirstate"), self.join("undo.dirstate")),
591 (self.join("journal.branch"), self.join("undo.branch")),
591 (self.join("journal.branch"), self.join("undo.branch")),
592 (self.join("journal.desc"), self.join("undo.desc"))]
592 (self.join("journal.desc"), self.join("undo.desc"))]
593 tr = transaction.transaction(self.ui.warn, self.sopener,
593 tr = transaction.transaction(self.ui.warn, self.sopener,
594 self.sjoin("journal"),
594 self.sjoin("journal"),
595 aftertrans(renames),
595 aftertrans(renames),
596 self.store.createmode)
596 self.store.createmode)
597 self._transref = weakref.ref(tr)
597 self._transref = weakref.ref(tr)
598 return tr
598 return tr
599
599
600 def recover(self):
600 def recover(self):
601 lock = self.lock()
601 lock = self.lock()
602 try:
602 try:
603 if os.path.exists(self.sjoin("journal")):
603 if os.path.exists(self.sjoin("journal")):
604 self.ui.status(_("rolling back interrupted transaction\n"))
604 self.ui.status(_("rolling back interrupted transaction\n"))
605 transaction.rollback(self.sopener, self.sjoin("journal"),
605 transaction.rollback(self.sopener, self.sjoin("journal"),
606 self.ui.warn)
606 self.ui.warn)
607 self.invalidate()
607 self.invalidate()
608 return True
608 return True
609 else:
609 else:
610 self.ui.warn(_("no interrupted transaction available\n"))
610 self.ui.warn(_("no interrupted transaction available\n"))
611 return False
611 return False
612 finally:
612 finally:
613 lock.release()
613 lock.release()
614
614
615 def rollback(self, dryrun=False):
615 def rollback(self, dryrun=False):
616 wlock = lock = None
616 wlock = lock = None
617 try:
617 try:
618 wlock = self.wlock()
618 wlock = self.wlock()
619 lock = self.lock()
619 lock = self.lock()
620 if os.path.exists(self.sjoin("undo")):
620 if os.path.exists(self.sjoin("undo")):
621 try:
621 try:
622 args = self.opener("undo.desc", "r").read().splitlines()
622 args = self.opener("undo.desc", "r").read().splitlines()
623 if len(args) >= 3 and self.ui.verbose:
623 if len(args) >= 3 and self.ui.verbose:
624 desc = _("rolling back to revision %s"
624 desc = _("rolling back to revision %s"
625 " (undo %s: %s)\n") % (
625 " (undo %s: %s)\n") % (
626 int(args[0]) - 1, args[1], args[2])
626 int(args[0]) - 1, args[1], args[2])
627 elif len(args) >= 2:
627 elif len(args) >= 2:
628 desc = _("rolling back to revision %s (undo %s)\n") % (
628 desc = _("rolling back to revision %s (undo %s)\n") % (
629 int(args[0]) - 1, args[1])
629 int(args[0]) - 1, args[1])
630 except IOError:
630 except IOError:
631 desc = _("rolling back unknown transaction\n")
631 desc = _("rolling back unknown transaction\n")
632 self.ui.status(desc)
632 self.ui.status(desc)
633 if dryrun:
633 if dryrun:
634 return
634 return
635 transaction.rollback(self.sopener, self.sjoin("undo"),
635 transaction.rollback(self.sopener, self.sjoin("undo"),
636 self.ui.warn)
636 self.ui.warn)
637 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
637 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
638 try:
638 try:
639 branch = self.opener("undo.branch").read()
639 branch = self.opener("undo.branch").read()
640 self.dirstate.setbranch(branch)
640 self.dirstate.setbranch(branch)
641 except IOError:
641 except IOError:
642 self.ui.warn(_("Named branch could not be reset, "
642 self.ui.warn(_("Named branch could not be reset, "
643 "current branch still is: %s\n")
643 "current branch still is: %s\n")
644 % encoding.tolocal(self.dirstate.branch()))
644 % encoding.tolocal(self.dirstate.branch()))
645 self.invalidate()
645 self.invalidate()
646 self.dirstate.invalidate()
646 self.dirstate.invalidate()
647 self.destroyed()
647 self.destroyed()
648 else:
648 else:
649 self.ui.warn(_("no rollback information available\n"))
649 self.ui.warn(_("no rollback information available\n"))
650 return 1
650 return 1
651 finally:
651 finally:
652 release(lock, wlock)
652 release(lock, wlock)
653
653
654 def invalidatecaches(self):
654 def invalidatecaches(self):
655 self._tags = None
655 self._tags = None
656 self._tagtypes = None
656 self._tagtypes = None
657 self.nodetagscache = None
657 self.nodetagscache = None
658 self._branchcache = None # in UTF-8
658 self._branchcache = None # in UTF-8
659 self._branchcachetip = None
659 self._branchcachetip = None
660
660
661 def invalidate(self):
661 def invalidate(self):
662 for a in "changelog manifest".split():
662 for a in "changelog manifest".split():
663 if a in self.__dict__:
663 if a in self.__dict__:
664 delattr(self, a)
664 delattr(self, a)
665 self.invalidatecaches()
665 self.invalidatecaches()
666
666
667 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
667 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
668 try:
668 try:
669 l = lock.lock(lockname, 0, releasefn, desc=desc)
669 l = lock.lock(lockname, 0, releasefn, desc=desc)
670 except error.LockHeld, inst:
670 except error.LockHeld, inst:
671 if not wait:
671 if not wait:
672 raise
672 raise
673 self.ui.warn(_("waiting for lock on %s held by %r\n") %
673 self.ui.warn(_("waiting for lock on %s held by %r\n") %
674 (desc, inst.locker))
674 (desc, inst.locker))
675 # default to 600 seconds timeout
675 # default to 600 seconds timeout
676 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
676 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
677 releasefn, desc=desc)
677 releasefn, desc=desc)
678 if acquirefn:
678 if acquirefn:
679 acquirefn()
679 acquirefn()
680 return l
680 return l
681
681
682 def lock(self, wait=True):
682 def lock(self, wait=True):
683 '''Lock the repository store (.hg/store) and return a weak reference
683 '''Lock the repository store (.hg/store) and return a weak reference
684 to the lock. Use this before modifying the store (e.g. committing or
684 to the lock. Use this before modifying the store (e.g. committing or
685 stripping). If you are opening a transaction, get a lock as well.)'''
685 stripping). If you are opening a transaction, get a lock as well.)'''
686 l = self._lockref and self._lockref()
686 l = self._lockref and self._lockref()
687 if l is not None and l.held:
687 if l is not None and l.held:
688 l.lock()
688 l.lock()
689 return l
689 return l
690
690
691 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
691 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
692 _('repository %s') % self.origroot)
692 _('repository %s') % self.origroot)
693 self._lockref = weakref.ref(l)
693 self._lockref = weakref.ref(l)
694 return l
694 return l
695
695
696 def wlock(self, wait=True):
696 def wlock(self, wait=True):
697 '''Lock the non-store parts of the repository (everything under
697 '''Lock the non-store parts of the repository (everything under
698 .hg except .hg/store) and return a weak reference to the lock.
698 .hg except .hg/store) and return a weak reference to the lock.
699 Use this before modifying files in .hg.'''
699 Use this before modifying files in .hg.'''
700 l = self._wlockref and self._wlockref()
700 l = self._wlockref and self._wlockref()
701 if l is not None and l.held:
701 if l is not None and l.held:
702 l.lock()
702 l.lock()
703 return l
703 return l
704
704
705 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
705 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
706 self.dirstate.invalidate, _('working directory of %s') %
706 self.dirstate.invalidate, _('working directory of %s') %
707 self.origroot)
707 self.origroot)
708 self._wlockref = weakref.ref(l)
708 self._wlockref = weakref.ref(l)
709 return l
709 return l
710
710
711 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
711 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
712 """
712 """
713 commit an individual file as part of a larger transaction
713 commit an individual file as part of a larger transaction
714 """
714 """
715
715
716 fname = fctx.path()
716 fname = fctx.path()
717 text = fctx.data()
717 text = fctx.data()
718 flog = self.file(fname)
718 flog = self.file(fname)
719 fparent1 = manifest1.get(fname, nullid)
719 fparent1 = manifest1.get(fname, nullid)
720 fparent2 = fparent2o = manifest2.get(fname, nullid)
720 fparent2 = fparent2o = manifest2.get(fname, nullid)
721
721
722 meta = {}
722 meta = {}
723 copy = fctx.renamed()
723 copy = fctx.renamed()
724 if copy and copy[0] != fname:
724 if copy and copy[0] != fname:
725 # Mark the new revision of this file as a copy of another
725 # Mark the new revision of this file as a copy of another
726 # file. This copy data will effectively act as a parent
726 # file. This copy data will effectively act as a parent
727 # of this new revision. If this is a merge, the first
727 # of this new revision. If this is a merge, the first
728 # parent will be the nullid (meaning "look up the copy data")
728 # parent will be the nullid (meaning "look up the copy data")
729 # and the second one will be the other parent. For example:
729 # and the second one will be the other parent. For example:
730 #
730 #
731 # 0 --- 1 --- 3 rev1 changes file foo
731 # 0 --- 1 --- 3 rev1 changes file foo
732 # \ / rev2 renames foo to bar and changes it
732 # \ / rev2 renames foo to bar and changes it
733 # \- 2 -/ rev3 should have bar with all changes and
733 # \- 2 -/ rev3 should have bar with all changes and
734 # should record that bar descends from
734 # should record that bar descends from
735 # bar in rev2 and foo in rev1
735 # bar in rev2 and foo in rev1
736 #
736 #
737 # this allows this merge to succeed:
737 # this allows this merge to succeed:
738 #
738 #
739 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
739 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
740 # \ / merging rev3 and rev4 should use bar@rev2
740 # \ / merging rev3 and rev4 should use bar@rev2
741 # \- 2 --- 4 as the merge base
741 # \- 2 --- 4 as the merge base
742 #
742 #
743
743
744 cfname = copy[0]
744 cfname = copy[0]
745 crev = manifest1.get(cfname)
745 crev = manifest1.get(cfname)
746 newfparent = fparent2
746 newfparent = fparent2
747
747
748 if manifest2: # branch merge
748 if manifest2: # branch merge
749 if fparent2 == nullid or crev is None: # copied on remote side
749 if fparent2 == nullid or crev is None: # copied on remote side
750 if cfname in manifest2:
750 if cfname in manifest2:
751 crev = manifest2[cfname]
751 crev = manifest2[cfname]
752 newfparent = fparent1
752 newfparent = fparent1
753
753
754 # find source in nearest ancestor if we've lost track
754 # find source in nearest ancestor if we've lost track
755 if not crev:
755 if not crev:
756 self.ui.debug(" %s: searching for copy revision for %s\n" %
756 self.ui.debug(" %s: searching for copy revision for %s\n" %
757 (fname, cfname))
757 (fname, cfname))
758 for ancestor in self['.'].ancestors():
758 for ancestor in self['.'].ancestors():
759 if cfname in ancestor:
759 if cfname in ancestor:
760 crev = ancestor[cfname].filenode()
760 crev = ancestor[cfname].filenode()
761 break
761 break
762
762
763 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
763 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
764 meta["copy"] = cfname
764 meta["copy"] = cfname
765 meta["copyrev"] = hex(crev)
765 meta["copyrev"] = hex(crev)
766 fparent1, fparent2 = nullid, newfparent
766 fparent1, fparent2 = nullid, newfparent
767 elif fparent2 != nullid:
767 elif fparent2 != nullid:
768 # is one parent an ancestor of the other?
768 # is one parent an ancestor of the other?
769 fparentancestor = flog.ancestor(fparent1, fparent2)
769 fparentancestor = flog.ancestor(fparent1, fparent2)
770 if fparentancestor == fparent1:
770 if fparentancestor == fparent1:
771 fparent1, fparent2 = fparent2, nullid
771 fparent1, fparent2 = fparent2, nullid
772 elif fparentancestor == fparent2:
772 elif fparentancestor == fparent2:
773 fparent2 = nullid
773 fparent2 = nullid
774
774
775 # is the file changed?
775 # is the file changed?
776 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
776 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
777 changelist.append(fname)
777 changelist.append(fname)
778 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
778 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
779
779
780 # are just the flags changed during merge?
780 # are just the flags changed during merge?
781 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
781 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
782 changelist.append(fname)
782 changelist.append(fname)
783
783
784 return fparent1
784 return fparent1
785
785
786 def commit(self, text="", user=None, date=None, match=None, force=False,
786 def commit(self, text="", user=None, date=None, match=None, force=False,
787 editor=False, extra={}):
787 editor=False, extra={}):
788 """Add a new revision to current repository.
788 """Add a new revision to current repository.
789
789
790 Revision information is gathered from the working directory,
790 Revision information is gathered from the working directory,
791 match can be used to filter the committed files. If editor is
791 match can be used to filter the committed files. If editor is
792 supplied, it is called to get a commit message.
792 supplied, it is called to get a commit message.
793 """
793 """
794
794
795 def fail(f, msg):
795 def fail(f, msg):
796 raise util.Abort('%s: %s' % (f, msg))
796 raise util.Abort('%s: %s' % (f, msg))
797
797
798 if not match:
798 if not match:
799 match = matchmod.always(self.root, '')
799 match = matchmod.always(self.root, '')
800
800
801 if not force:
801 if not force:
802 vdirs = []
802 vdirs = []
803 match.dir = vdirs.append
803 match.dir = vdirs.append
804 match.bad = fail
804 match.bad = fail
805
805
806 wlock = self.wlock()
806 wlock = self.wlock()
807 try:
807 try:
808 wctx = self[None]
808 wctx = self[None]
809 merge = len(wctx.parents()) > 1
809 merge = len(wctx.parents()) > 1
810
810
811 if (not force and merge and match and
811 if (not force and merge and match and
812 (match.files() or match.anypats())):
812 (match.files() or match.anypats())):
813 raise util.Abort(_('cannot partially commit a merge '
813 raise util.Abort(_('cannot partially commit a merge '
814 '(do not specify files or patterns)'))
814 '(do not specify files or patterns)'))
815
815
816 changes = self.status(match=match, clean=force)
816 changes = self.status(match=match, clean=force)
817 if force:
817 if force:
818 changes[0].extend(changes[6]) # mq may commit unchanged files
818 changes[0].extend(changes[6]) # mq may commit unchanged files
819
819
820 # check subrepos
820 # check subrepos
821 subs = []
821 subs = []
822 removedsubs = set()
822 removedsubs = set()
823 for p in wctx.parents():
823 for p in wctx.parents():
824 removedsubs.update(s for s in p.substate if match(s))
824 removedsubs.update(s for s in p.substate if match(s))
825 for s in wctx.substate:
825 for s in wctx.substate:
826 removedsubs.discard(s)
826 removedsubs.discard(s)
827 if match(s) and wctx.sub(s).dirty():
827 if match(s) and wctx.sub(s).dirty():
828 subs.append(s)
828 subs.append(s)
829 if (subs or removedsubs):
829 if (subs or removedsubs):
830 if (not match('.hgsub') and
830 if (not match('.hgsub') and
831 '.hgsub' in (wctx.modified() + wctx.added())):
831 '.hgsub' in (wctx.modified() + wctx.added())):
832 raise util.Abort(_("can't commit subrepos without .hgsub"))
832 raise util.Abort(_("can't commit subrepos without .hgsub"))
833 if '.hgsubstate' not in changes[0]:
833 if '.hgsubstate' not in changes[0]:
834 changes[0].insert(0, '.hgsubstate')
834 changes[0].insert(0, '.hgsubstate')
835
835
836 # make sure all explicit patterns are matched
836 # make sure all explicit patterns are matched
837 if not force and match.files():
837 if not force and match.files():
838 matched = set(changes[0] + changes[1] + changes[2])
838 matched = set(changes[0] + changes[1] + changes[2])
839
839
840 for f in match.files():
840 for f in match.files():
841 if f == '.' or f in matched or f in wctx.substate:
841 if f == '.' or f in matched or f in wctx.substate:
842 continue
842 continue
843 if f in changes[3]: # missing
843 if f in changes[3]: # missing
844 fail(f, _('file not found!'))
844 fail(f, _('file not found!'))
845 if f in vdirs: # visited directory
845 if f in vdirs: # visited directory
846 d = f + '/'
846 d = f + '/'
847 for mf in matched:
847 for mf in matched:
848 if mf.startswith(d):
848 if mf.startswith(d):
849 break
849 break
850 else:
850 else:
851 fail(f, _("no match under directory!"))
851 fail(f, _("no match under directory!"))
852 elif f not in self.dirstate:
852 elif f not in self.dirstate:
853 fail(f, _("file not tracked!"))
853 fail(f, _("file not tracked!"))
854
854
855 if (not force and not extra.get("close") and not merge
855 if (not force and not extra.get("close") and not merge
856 and not (changes[0] or changes[1] or changes[2])
856 and not (changes[0] or changes[1] or changes[2])
857 and wctx.branch() == wctx.p1().branch()):
857 and wctx.branch() == wctx.p1().branch()):
858 return None
858 return None
859
859
860 ms = mergemod.mergestate(self)
860 ms = mergemod.mergestate(self)
861 for f in changes[0]:
861 for f in changes[0]:
862 if f in ms and ms[f] == 'u':
862 if f in ms and ms[f] == 'u':
863 raise util.Abort(_("unresolved merge conflicts "
863 raise util.Abort(_("unresolved merge conflicts "
864 "(see hg resolve)"))
864 "(see hg resolve)"))
865
865
866 cctx = context.workingctx(self, text, user, date, extra, changes)
866 cctx = context.workingctx(self, text, user, date, extra, changes)
867 if editor:
867 if editor:
868 cctx._text = editor(self, cctx, subs)
868 cctx._text = editor(self, cctx, subs)
869 edited = (text != cctx._text)
869 edited = (text != cctx._text)
870
870
871 # commit subs
871 # commit subs
872 if subs or removedsubs:
872 if subs or removedsubs:
873 state = wctx.substate.copy()
873 state = wctx.substate.copy()
874 for s in subs:
874 for s in subs:
875 sub = wctx.sub(s)
875 sub = wctx.sub(s)
876 self.ui.status(_('committing subrepository %s\n') %
876 self.ui.status(_('committing subrepository %s\n') %
877 subrepo.relpath(sub))
877 subrepo.relpath(sub))
878 sr = sub.commit(cctx._text, user, date)
878 sr = sub.commit(cctx._text, user, date)
879 state[s] = (state[s][0], sr)
879 state[s] = (state[s][0], sr)
880 subrepo.writestate(self, state)
880 subrepo.writestate(self, state)
881
881
882 # Save commit message in case this transaction gets rolled back
882 # Save commit message in case this transaction gets rolled back
883 # (e.g. by a pretxncommit hook). Leave the content alone on
883 # (e.g. by a pretxncommit hook). Leave the content alone on
884 # the assumption that the user will use the same editor again.
884 # the assumption that the user will use the same editor again.
885 msgfile = self.opener('last-message.txt', 'wb')
885 msgfile = self.opener('last-message.txt', 'wb')
886 msgfile.write(cctx._text)
886 msgfile.write(cctx._text)
887 msgfile.close()
887 msgfile.close()
888
888
889 p1, p2 = self.dirstate.parents()
889 p1, p2 = self.dirstate.parents()
890 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
890 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
891 try:
891 try:
892 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
892 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
893 ret = self.commitctx(cctx, True)
893 ret = self.commitctx(cctx, True)
894 except:
894 except:
895 if edited:
895 if edited:
896 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
896 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
897 self.ui.write(
897 self.ui.write(
898 _('note: commit message saved in %s\n') % msgfn)
898 _('note: commit message saved in %s\n') % msgfn)
899 raise
899 raise
900
900
901 # update dirstate and mergestate
901 # update dirstate and mergestate
902 for f in changes[0] + changes[1]:
902 for f in changes[0] + changes[1]:
903 self.dirstate.normal(f)
903 self.dirstate.normal(f)
904 for f in changes[2]:
904 for f in changes[2]:
905 self.dirstate.forget(f)
905 self.dirstate.forget(f)
906 self.dirstate.setparents(ret)
906 self.dirstate.setparents(ret)
907 ms.reset()
907 ms.reset()
908 finally:
908 finally:
909 wlock.release()
909 wlock.release()
910
910
911 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
911 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
912 return ret
912 return ret
913
913
914 def commitctx(self, ctx, error=False):
914 def commitctx(self, ctx, error=False):
915 """Add a new revision to current repository.
915 """Add a new revision to current repository.
916 Revision information is passed via the context argument.
916 Revision information is passed via the context argument.
917 """
917 """
918
918
919 tr = lock = None
919 tr = lock = None
920 removed = ctx.removed()
920 removed = ctx.removed()
921 p1, p2 = ctx.p1(), ctx.p2()
921 p1, p2 = ctx.p1(), ctx.p2()
922 m1 = p1.manifest().copy()
922 m1 = p1.manifest().copy()
923 m2 = p2.manifest()
923 m2 = p2.manifest()
924 user = ctx.user()
924 user = ctx.user()
925
925
926 lock = self.lock()
926 lock = self.lock()
927 try:
927 try:
928 tr = self.transaction("commit")
928 tr = self.transaction("commit")
929 trp = weakref.proxy(tr)
929 trp = weakref.proxy(tr)
930
930
931 # check in files
931 # check in files
932 new = {}
932 new = {}
933 changed = []
933 changed = []
934 linkrev = len(self)
934 linkrev = len(self)
935 for f in sorted(ctx.modified() + ctx.added()):
935 for f in sorted(ctx.modified() + ctx.added()):
936 self.ui.note(f + "\n")
936 self.ui.note(f + "\n")
937 try:
937 try:
938 fctx = ctx[f]
938 fctx = ctx[f]
939 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
939 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
940 changed)
940 changed)
941 m1.set(f, fctx.flags())
941 m1.set(f, fctx.flags())
942 except OSError, inst:
942 except OSError, inst:
943 self.ui.warn(_("trouble committing %s!\n") % f)
943 self.ui.warn(_("trouble committing %s!\n") % f)
944 raise
944 raise
945 except IOError, inst:
945 except IOError, inst:
946 errcode = getattr(inst, 'errno', errno.ENOENT)
946 errcode = getattr(inst, 'errno', errno.ENOENT)
947 if error or errcode and errcode != errno.ENOENT:
947 if error or errcode and errcode != errno.ENOENT:
948 self.ui.warn(_("trouble committing %s!\n") % f)
948 self.ui.warn(_("trouble committing %s!\n") % f)
949 raise
949 raise
950 else:
950 else:
951 removed.append(f)
951 removed.append(f)
952
952
953 # update manifest
953 # update manifest
954 m1.update(new)
954 m1.update(new)
955 removed = [f for f in sorted(removed) if f in m1 or f in m2]
955 removed = [f for f in sorted(removed) if f in m1 or f in m2]
956 drop = [f for f in removed if f in m1]
956 drop = [f for f in removed if f in m1]
957 for f in drop:
957 for f in drop:
958 del m1[f]
958 del m1[f]
959 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
959 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
960 p2.manifestnode(), (new, drop))
960 p2.manifestnode(), (new, drop))
961
961
962 # update changelog
962 # update changelog
963 self.changelog.delayupdate()
963 self.changelog.delayupdate()
964 n = self.changelog.add(mn, changed + removed, ctx.description(),
964 n = self.changelog.add(mn, changed + removed, ctx.description(),
965 trp, p1.node(), p2.node(),
965 trp, p1.node(), p2.node(),
966 user, ctx.date(), ctx.extra().copy())
966 user, ctx.date(), ctx.extra().copy())
967 p = lambda: self.changelog.writepending() and self.root or ""
967 p = lambda: self.changelog.writepending() and self.root or ""
968 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
968 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
969 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
969 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
970 parent2=xp2, pending=p)
970 parent2=xp2, pending=p)
971 self.changelog.finalize(trp)
971 self.changelog.finalize(trp)
972 tr.close()
972 tr.close()
973
973
974 if self._branchcache:
974 if self._branchcache:
975 self.branchtags()
975 self.branchtags()
976 return n
976 return n
977 finally:
977 finally:
978 if tr:
978 if tr:
979 tr.release()
979 tr.release()
980 lock.release()
980 lock.release()
981
981
982 def destroyed(self):
982 def destroyed(self):
983 '''Inform the repository that nodes have been destroyed.
983 '''Inform the repository that nodes have been destroyed.
984 Intended for use by strip and rollback, so there's a common
984 Intended for use by strip and rollback, so there's a common
985 place for anything that has to be done after destroying history.'''
985 place for anything that has to be done after destroying history.'''
986 # XXX it might be nice if we could take the list of destroyed
986 # XXX it might be nice if we could take the list of destroyed
987 # nodes, but I don't see an easy way for rollback() to do that
987 # nodes, but I don't see an easy way for rollback() to do that
988
988
989 # Ensure the persistent tag cache is updated. Doing it now
989 # Ensure the persistent tag cache is updated. Doing it now
990 # means that the tag cache only has to worry about destroyed
990 # means that the tag cache only has to worry about destroyed
991 # heads immediately after a strip/rollback. That in turn
991 # heads immediately after a strip/rollback. That in turn
992 # guarantees that "cachetip == currenttip" (comparing both rev
992 # guarantees that "cachetip == currenttip" (comparing both rev
993 # and node) always means no nodes have been added or destroyed.
993 # and node) always means no nodes have been added or destroyed.
994
994
995 # XXX this is suboptimal when qrefresh'ing: we strip the current
995 # XXX this is suboptimal when qrefresh'ing: we strip the current
996 # head, refresh the tag cache, then immediately add a new head.
996 # head, refresh the tag cache, then immediately add a new head.
997 # But I think doing it this way is necessary for the "instant
997 # But I think doing it this way is necessary for the "instant
998 # tag cache retrieval" case to work.
998 # tag cache retrieval" case to work.
999 self.invalidatecaches()
999 self.invalidatecaches()
1000
1000
1001 def walk(self, match, node=None):
1001 def walk(self, match, node=None):
1002 '''
1002 '''
1003 walk recursively through the directory tree or a given
1003 walk recursively through the directory tree or a given
1004 changeset, finding all files matched by the match
1004 changeset, finding all files matched by the match
1005 function
1005 function
1006 '''
1006 '''
1007 return self[node].walk(match)
1007 return self[node].walk(match)
1008
1008
1009 def status(self, node1='.', node2=None, match=None,
1009 def status(self, node1='.', node2=None, match=None,
1010 ignored=False, clean=False, unknown=False):
1010 ignored=False, clean=False, unknown=False):
1011 """return status of files between two nodes or node and working directory
1011 """return status of files between two nodes or node and working directory
1012
1012
1013 If node1 is None, use the first dirstate parent instead.
1013 If node1 is None, use the first dirstate parent instead.
1014 If node2 is None, compare node1 with working directory.
1014 If node2 is None, compare node1 with working directory.
1015 """
1015 """
1016
1016
1017 def mfmatches(ctx):
1017 def mfmatches(ctx):
1018 mf = ctx.manifest().copy()
1018 mf = ctx.manifest().copy()
1019 for fn in mf.keys():
1019 for fn in mf.keys():
1020 if not match(fn):
1020 if not match(fn):
1021 del mf[fn]
1021 del mf[fn]
1022 return mf
1022 return mf
1023
1023
1024 if isinstance(node1, context.changectx):
1024 if isinstance(node1, context.changectx):
1025 ctx1 = node1
1025 ctx1 = node1
1026 else:
1026 else:
1027 ctx1 = self[node1]
1027 ctx1 = self[node1]
1028 if isinstance(node2, context.changectx):
1028 if isinstance(node2, context.changectx):
1029 ctx2 = node2
1029 ctx2 = node2
1030 else:
1030 else:
1031 ctx2 = self[node2]
1031 ctx2 = self[node2]
1032
1032
1033 working = ctx2.rev() is None
1033 working = ctx2.rev() is None
1034 parentworking = working and ctx1 == self['.']
1034 parentworking = working and ctx1 == self['.']
1035 match = match or matchmod.always(self.root, self.getcwd())
1035 match = match or matchmod.always(self.root, self.getcwd())
1036 listignored, listclean, listunknown = ignored, clean, unknown
1036 listignored, listclean, listunknown = ignored, clean, unknown
1037
1037
1038 # load earliest manifest first for caching reasons
1038 # load earliest manifest first for caching reasons
1039 if not working and ctx2.rev() < ctx1.rev():
1039 if not working and ctx2.rev() < ctx1.rev():
1040 ctx2.manifest()
1040 ctx2.manifest()
1041
1041
1042 if not parentworking:
1042 if not parentworking:
1043 def bad(f, msg):
1043 def bad(f, msg):
1044 if f not in ctx1:
1044 if f not in ctx1:
1045 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1045 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1046 match.bad = bad
1046 match.bad = bad
1047
1047
1048 if working: # we need to scan the working dir
1048 if working: # we need to scan the working dir
1049 subrepos = []
1049 subrepos = []
1050 if '.hgsub' in self.dirstate:
1050 if '.hgsub' in self.dirstate:
1051 subrepos = ctx1.substate.keys()
1051 subrepos = ctx1.substate.keys()
1052 s = self.dirstate.status(match, subrepos, listignored,
1052 s = self.dirstate.status(match, subrepos, listignored,
1053 listclean, listunknown)
1053 listclean, listunknown)
1054 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1054 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1055
1055
1056 # check for any possibly clean files
1056 # check for any possibly clean files
1057 if parentworking and cmp:
1057 if parentworking and cmp:
1058 fixup = []
1058 fixup = []
1059 # do a full compare of any files that might have changed
1059 # do a full compare of any files that might have changed
1060 for f in sorted(cmp):
1060 for f in sorted(cmp):
1061 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1061 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1062 or ctx1[f].cmp(ctx2[f].data())):
1062 or ctx1[f].cmp(ctx2[f].data())):
1063 modified.append(f)
1063 modified.append(f)
1064 else:
1064 else:
1065 fixup.append(f)
1065 fixup.append(f)
1066
1066
1067 if listclean:
1067 if listclean:
1068 clean += fixup
1068 clean += fixup
1069
1069
1070 # update dirstate for files that are actually clean
1070 # update dirstate for files that are actually clean
1071 if fixup:
1071 if fixup:
1072 try:
1072 try:
1073 # updating the dirstate is optional
1073 # updating the dirstate is optional
1074 # so we don't wait on the lock
1074 # so we don't wait on the lock
1075 wlock = self.wlock(False)
1075 wlock = self.wlock(False)
1076 try:
1076 try:
1077 for f in fixup:
1077 for f in fixup:
1078 self.dirstate.normal(f)
1078 self.dirstate.normal(f)
1079 finally:
1079 finally:
1080 wlock.release()
1080 wlock.release()
1081 except error.LockError:
1081 except error.LockError:
1082 pass
1082 pass
1083
1083
1084 if not parentworking:
1084 if not parentworking:
1085 mf1 = mfmatches(ctx1)
1085 mf1 = mfmatches(ctx1)
1086 if working:
1086 if working:
1087 # we are comparing working dir against non-parent
1087 # we are comparing working dir against non-parent
1088 # generate a pseudo-manifest for the working dir
1088 # generate a pseudo-manifest for the working dir
1089 mf2 = mfmatches(self['.'])
1089 mf2 = mfmatches(self['.'])
1090 for f in cmp + modified + added:
1090 for f in cmp + modified + added:
1091 mf2[f] = None
1091 mf2[f] = None
1092 mf2.set(f, ctx2.flags(f))
1092 mf2.set(f, ctx2.flags(f))
1093 for f in removed:
1093 for f in removed:
1094 if f in mf2:
1094 if f in mf2:
1095 del mf2[f]
1095 del mf2[f]
1096 else:
1096 else:
1097 # we are comparing two revisions
1097 # we are comparing two revisions
1098 deleted, unknown, ignored = [], [], []
1098 deleted, unknown, ignored = [], [], []
1099 mf2 = mfmatches(ctx2)
1099 mf2 = mfmatches(ctx2)
1100
1100
1101 modified, added, clean = [], [], []
1101 modified, added, clean = [], [], []
1102 for fn in mf2:
1102 for fn in mf2:
1103 if fn in mf1:
1103 if fn in mf1:
1104 if (mf1.flags(fn) != mf2.flags(fn) or
1104 if (mf1.flags(fn) != mf2.flags(fn) or
1105 (mf1[fn] != mf2[fn] and
1105 (mf1[fn] != mf2[fn] and
1106 (mf2[fn] or ctx1[fn].cmp(ctx2[fn].data())))):
1106 (mf2[fn] or ctx1[fn].cmp(ctx2[fn].data())))):
1107 modified.append(fn)
1107 modified.append(fn)
1108 elif listclean:
1108 elif listclean:
1109 clean.append(fn)
1109 clean.append(fn)
1110 del mf1[fn]
1110 del mf1[fn]
1111 else:
1111 else:
1112 added.append(fn)
1112 added.append(fn)
1113 removed = mf1.keys()
1113 removed = mf1.keys()
1114
1114
1115 r = modified, added, removed, deleted, unknown, ignored, clean
1115 r = modified, added, removed, deleted, unknown, ignored, clean
1116 [l.sort() for l in r]
1116 [l.sort() for l in r]
1117 return r
1117 return r
1118
1118
1119 def heads(self, start=None):
1119 def heads(self, start=None):
1120 heads = self.changelog.heads(start)
1120 heads = self.changelog.heads(start)
1121 # sort the output in rev descending order
1121 # sort the output in rev descending order
1122 heads = [(-self.changelog.rev(h), h) for h in heads]
1122 heads = [(-self.changelog.rev(h), h) for h in heads]
1123 return [n for (r, n) in sorted(heads)]
1123 return [n for (r, n) in sorted(heads)]
1124
1124
1125 def branchheads(self, branch=None, start=None, closed=False):
1125 def branchheads(self, branch=None, start=None, closed=False):
1126 '''return a (possibly filtered) list of heads for the given branch
1126 '''return a (possibly filtered) list of heads for the given branch
1127
1127
1128 Heads are returned in topological order, from newest to oldest.
1128 Heads are returned in topological order, from newest to oldest.
1129 If branch is None, use the dirstate branch.
1129 If branch is None, use the dirstate branch.
1130 If start is not None, return only heads reachable from start.
1130 If start is not None, return only heads reachable from start.
1131 If closed is True, return heads that are marked as closed as well.
1131 If closed is True, return heads that are marked as closed as well.
1132 '''
1132 '''
1133 if branch is None:
1133 if branch is None:
1134 branch = self[None].branch()
1134 branch = self[None].branch()
1135 branches = self.branchmap()
1135 branches = self.branchmap()
1136 if branch not in branches:
1136 if branch not in branches:
1137 return []
1137 return []
1138 # the cache returns heads ordered lowest to highest
1138 # the cache returns heads ordered lowest to highest
1139 bheads = list(reversed(branches[branch]))
1139 bheads = list(reversed(branches[branch]))
1140 if start is not None:
1140 if start is not None:
1141 # filter out the heads that cannot be reached from startrev
1141 # filter out the heads that cannot be reached from startrev
1142 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1142 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1143 bheads = [h for h in bheads if h in fbheads]
1143 bheads = [h for h in bheads if h in fbheads]
1144 if not closed:
1144 if not closed:
1145 bheads = [h for h in bheads if
1145 bheads = [h for h in bheads if
1146 ('close' not in self.changelog.read(h)[5])]
1146 ('close' not in self.changelog.read(h)[5])]
1147 return bheads
1147 return bheads
1148
1148
1149 def branches(self, nodes):
1149 def branches(self, nodes):
1150 if not nodes:
1150 if not nodes:
1151 nodes = [self.changelog.tip()]
1151 nodes = [self.changelog.tip()]
1152 b = []
1152 b = []
1153 for n in nodes:
1153 for n in nodes:
1154 t = n
1154 t = n
1155 while 1:
1155 while 1:
1156 p = self.changelog.parents(n)
1156 p = self.changelog.parents(n)
1157 if p[1] != nullid or p[0] == nullid:
1157 if p[1] != nullid or p[0] == nullid:
1158 b.append((t, n, p[0], p[1]))
1158 b.append((t, n, p[0], p[1]))
1159 break
1159 break
1160 n = p[0]
1160 n = p[0]
1161 return b
1161 return b
1162
1162
1163 def between(self, pairs):
1163 def between(self, pairs):
1164 r = []
1164 r = []
1165
1165
1166 for top, bottom in pairs:
1166 for top, bottom in pairs:
1167 n, l, i = top, [], 0
1167 n, l, i = top, [], 0
1168 f = 1
1168 f = 1
1169
1169
1170 while n != bottom and n != nullid:
1170 while n != bottom and n != nullid:
1171 p = self.changelog.parents(n)[0]
1171 p = self.changelog.parents(n)[0]
1172 if i == f:
1172 if i == f:
1173 l.append(n)
1173 l.append(n)
1174 f = f * 2
1174 f = f * 2
1175 n = p
1175 n = p
1176 i += 1
1176 i += 1
1177
1177
1178 r.append(l)
1178 r.append(l)
1179
1179
1180 return r
1180 return r
1181
1181
1182 def pull(self, remote, heads=None, force=False):
1182 def pull(self, remote, heads=None, force=False):
1183 lock = self.lock()
1183 lock = self.lock()
1184 try:
1184 try:
1185 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1185 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1186 force=force)
1186 force=force)
1187 common, fetch, rheads = tmp
1187 common, fetch, rheads = tmp
1188 if not fetch:
1188 if not fetch:
1189 self.ui.status(_("no changes found\n"))
1189 self.ui.status(_("no changes found\n"))
1190 return 0
1190 return 0
1191
1191
1192 if fetch == [nullid]:
1192 if fetch == [nullid]:
1193 self.ui.status(_("requesting all changes\n"))
1193 self.ui.status(_("requesting all changes\n"))
1194 elif heads is None and remote.capable('changegroupsubset'):
1194 elif heads is None and remote.capable('changegroupsubset'):
1195 # issue1320, avoid a race if remote changed after discovery
1195 # issue1320, avoid a race if remote changed after discovery
1196 heads = rheads
1196 heads = rheads
1197
1197
1198 if heads is None:
1198 if heads is None:
1199 cg = remote.changegroup(fetch, 'pull')
1199 cg = remote.changegroup(fetch, 'pull')
1200 else:
1200 else:
1201 if not remote.capable('changegroupsubset'):
1201 if not remote.capable('changegroupsubset'):
1202 raise util.Abort(_("Partial pull cannot be done because "
1202 raise util.Abort(_("partial pull cannot be done because "
1203 "other repository doesn't support "
1203 "other repository doesn't support "
1204 "changegroupsubset."))
1204 "changegroupsubset."))
1205 cg = remote.changegroupsubset(fetch, heads, 'pull')
1205 cg = remote.changegroupsubset(fetch, heads, 'pull')
1206 return self.addchangegroup(cg, 'pull', remote.url(), lock=lock)
1206 return self.addchangegroup(cg, 'pull', remote.url(), lock=lock)
1207 finally:
1207 finally:
1208 lock.release()
1208 lock.release()
1209
1209
1210 def push(self, remote, force=False, revs=None, newbranch=False):
1210 def push(self, remote, force=False, revs=None, newbranch=False):
1211 '''Push outgoing changesets (limited by revs) from the current
1211 '''Push outgoing changesets (limited by revs) from the current
1212 repository to remote. Return an integer:
1212 repository to remote. Return an integer:
1213 - 0 means HTTP error *or* nothing to push
1213 - 0 means HTTP error *or* nothing to push
1214 - 1 means we pushed and remote head count is unchanged *or*
1214 - 1 means we pushed and remote head count is unchanged *or*
1215 we have outgoing changesets but refused to push
1215 we have outgoing changesets but refused to push
1216 - other values as described by addchangegroup()
1216 - other values as described by addchangegroup()
1217 '''
1217 '''
1218 # there are two ways to push to remote repo:
1218 # there are two ways to push to remote repo:
1219 #
1219 #
1220 # addchangegroup assumes local user can lock remote
1220 # addchangegroup assumes local user can lock remote
1221 # repo (local filesystem, old ssh servers).
1221 # repo (local filesystem, old ssh servers).
1222 #
1222 #
1223 # unbundle assumes local user cannot lock remote repo (new ssh
1223 # unbundle assumes local user cannot lock remote repo (new ssh
1224 # servers, http servers).
1224 # servers, http servers).
1225
1225
1226 if remote.capable('unbundle'):
1226 if remote.capable('unbundle'):
1227 return self.push_unbundle(remote, force, revs, newbranch)
1227 return self.push_unbundle(remote, force, revs, newbranch)
1228 return self.push_addchangegroup(remote, force, revs, newbranch)
1228 return self.push_addchangegroup(remote, force, revs, newbranch)
1229
1229
1230 def push_addchangegroup(self, remote, force, revs, newbranch):
1230 def push_addchangegroup(self, remote, force, revs, newbranch):
1231 '''Push a changegroup by locking the remote and sending the
1231 '''Push a changegroup by locking the remote and sending the
1232 addchangegroup command to it. Used for local and old SSH repos.
1232 addchangegroup command to it. Used for local and old SSH repos.
1233 Return an integer: see push().
1233 Return an integer: see push().
1234 '''
1234 '''
1235 lock = remote.lock()
1235 lock = remote.lock()
1236 try:
1236 try:
1237 ret = discovery.prepush(self, remote, force, revs, newbranch)
1237 ret = discovery.prepush(self, remote, force, revs, newbranch)
1238 if ret[0] is not None:
1238 if ret[0] is not None:
1239 cg, remote_heads = ret
1239 cg, remote_heads = ret
1240 # we return an integer indicating remote head count change
1240 # we return an integer indicating remote head count change
1241 return remote.addchangegroup(cg, 'push', self.url(), lock=lock)
1241 return remote.addchangegroup(cg, 'push', self.url(), lock=lock)
1242 # and here we return 0 for "nothing to push" or 1 for
1242 # and here we return 0 for "nothing to push" or 1 for
1243 # "something to push but I refuse"
1243 # "something to push but I refuse"
1244 return ret[1]
1244 return ret[1]
1245 finally:
1245 finally:
1246 lock.release()
1246 lock.release()
1247
1247
1248 def push_unbundle(self, remote, force, revs, newbranch):
1248 def push_unbundle(self, remote, force, revs, newbranch):
1249 '''Push a changegroup by unbundling it on the remote. Used for new
1249 '''Push a changegroup by unbundling it on the remote. Used for new
1250 SSH and HTTP repos. Return an integer: see push().'''
1250 SSH and HTTP repos. Return an integer: see push().'''
1251 # local repo finds heads on server, finds out what revs it
1251 # local repo finds heads on server, finds out what revs it
1252 # must push. once revs transferred, if server finds it has
1252 # must push. once revs transferred, if server finds it has
1253 # different heads (someone else won commit/push race), server
1253 # different heads (someone else won commit/push race), server
1254 # aborts.
1254 # aborts.
1255
1255
1256 ret = discovery.prepush(self, remote, force, revs, newbranch)
1256 ret = discovery.prepush(self, remote, force, revs, newbranch)
1257 if ret[0] is not None:
1257 if ret[0] is not None:
1258 cg, remote_heads = ret
1258 cg, remote_heads = ret
1259 if force:
1259 if force:
1260 remote_heads = ['force']
1260 remote_heads = ['force']
1261 # ssh: return remote's addchangegroup()
1261 # ssh: return remote's addchangegroup()
1262 # http: return remote's addchangegroup() or 0 for error
1262 # http: return remote's addchangegroup() or 0 for error
1263 return remote.unbundle(cg, remote_heads, 'push')
1263 return remote.unbundle(cg, remote_heads, 'push')
1264 # as in push_addchangegroup()
1264 # as in push_addchangegroup()
1265 return ret[1]
1265 return ret[1]
1266
1266
1267 def changegroupinfo(self, nodes, source):
1267 def changegroupinfo(self, nodes, source):
1268 if self.ui.verbose or source == 'bundle':
1268 if self.ui.verbose or source == 'bundle':
1269 self.ui.status(_("%d changesets found\n") % len(nodes))
1269 self.ui.status(_("%d changesets found\n") % len(nodes))
1270 if self.ui.debugflag:
1270 if self.ui.debugflag:
1271 self.ui.debug("list of changesets:\n")
1271 self.ui.debug("list of changesets:\n")
1272 for node in nodes:
1272 for node in nodes:
1273 self.ui.debug("%s\n" % hex(node))
1273 self.ui.debug("%s\n" % hex(node))
1274
1274
1275 def changegroupsubset(self, bases, heads, source, extranodes=None):
1275 def changegroupsubset(self, bases, heads, source, extranodes=None):
1276 """Compute a changegroup consisting of all the nodes that are
1276 """Compute a changegroup consisting of all the nodes that are
1277 descendents of any of the bases and ancestors of any of the heads.
1277 descendents of any of the bases and ancestors of any of the heads.
1278 Return a chunkbuffer object whose read() method will return
1278 Return a chunkbuffer object whose read() method will return
1279 successive changegroup chunks.
1279 successive changegroup chunks.
1280
1280
1281 It is fairly complex as determining which filenodes and which
1281 It is fairly complex as determining which filenodes and which
1282 manifest nodes need to be included for the changeset to be complete
1282 manifest nodes need to be included for the changeset to be complete
1283 is non-trivial.
1283 is non-trivial.
1284
1284
1285 Another wrinkle is doing the reverse, figuring out which changeset in
1285 Another wrinkle is doing the reverse, figuring out which changeset in
1286 the changegroup a particular filenode or manifestnode belongs to.
1286 the changegroup a particular filenode or manifestnode belongs to.
1287
1287
1288 The caller can specify some nodes that must be included in the
1288 The caller can specify some nodes that must be included in the
1289 changegroup using the extranodes argument. It should be a dict
1289 changegroup using the extranodes argument. It should be a dict
1290 where the keys are the filenames (or 1 for the manifest), and the
1290 where the keys are the filenames (or 1 for the manifest), and the
1291 values are lists of (node, linknode) tuples, where node is a wanted
1291 values are lists of (node, linknode) tuples, where node is a wanted
1292 node and linknode is the changelog node that should be transmitted as
1292 node and linknode is the changelog node that should be transmitted as
1293 the linkrev.
1293 the linkrev.
1294 """
1294 """
1295
1295
1296 # Set up some initial variables
1296 # Set up some initial variables
1297 # Make it easy to refer to self.changelog
1297 # Make it easy to refer to self.changelog
1298 cl = self.changelog
1298 cl = self.changelog
1299 # msng is short for missing - compute the list of changesets in this
1299 # msng is short for missing - compute the list of changesets in this
1300 # changegroup.
1300 # changegroup.
1301 if not bases:
1301 if not bases:
1302 bases = [nullid]
1302 bases = [nullid]
1303 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1303 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1304
1304
1305 if extranodes is None:
1305 if extranodes is None:
1306 # can we go through the fast path ?
1306 # can we go through the fast path ?
1307 heads.sort()
1307 heads.sort()
1308 allheads = self.heads()
1308 allheads = self.heads()
1309 allheads.sort()
1309 allheads.sort()
1310 if heads == allheads:
1310 if heads == allheads:
1311 return self._changegroup(msng_cl_lst, source)
1311 return self._changegroup(msng_cl_lst, source)
1312
1312
1313 # slow path
1313 # slow path
1314 self.hook('preoutgoing', throw=True, source=source)
1314 self.hook('preoutgoing', throw=True, source=source)
1315
1315
1316 self.changegroupinfo(msng_cl_lst, source)
1316 self.changegroupinfo(msng_cl_lst, source)
1317 # Some bases may turn out to be superfluous, and some heads may be
1317 # Some bases may turn out to be superfluous, and some heads may be
1318 # too. nodesbetween will return the minimal set of bases and heads
1318 # too. nodesbetween will return the minimal set of bases and heads
1319 # necessary to re-create the changegroup.
1319 # necessary to re-create the changegroup.
1320
1320
1321 # Known heads are the list of heads that it is assumed the recipient
1321 # Known heads are the list of heads that it is assumed the recipient
1322 # of this changegroup will know about.
1322 # of this changegroup will know about.
1323 knownheads = set()
1323 knownheads = set()
1324 # We assume that all parents of bases are known heads.
1324 # We assume that all parents of bases are known heads.
1325 for n in bases:
1325 for n in bases:
1326 knownheads.update(cl.parents(n))
1326 knownheads.update(cl.parents(n))
1327 knownheads.discard(nullid)
1327 knownheads.discard(nullid)
1328 knownheads = list(knownheads)
1328 knownheads = list(knownheads)
1329 if knownheads:
1329 if knownheads:
1330 # Now that we know what heads are known, we can compute which
1330 # Now that we know what heads are known, we can compute which
1331 # changesets are known. The recipient must know about all
1331 # changesets are known. The recipient must know about all
1332 # changesets required to reach the known heads from the null
1332 # changesets required to reach the known heads from the null
1333 # changeset.
1333 # changeset.
1334 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1334 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1335 junk = None
1335 junk = None
1336 # Transform the list into a set.
1336 # Transform the list into a set.
1337 has_cl_set = set(has_cl_set)
1337 has_cl_set = set(has_cl_set)
1338 else:
1338 else:
1339 # If there were no known heads, the recipient cannot be assumed to
1339 # If there were no known heads, the recipient cannot be assumed to
1340 # know about any changesets.
1340 # know about any changesets.
1341 has_cl_set = set()
1341 has_cl_set = set()
1342
1342
1343 # Make it easy to refer to self.manifest
1343 # Make it easy to refer to self.manifest
1344 mnfst = self.manifest
1344 mnfst = self.manifest
1345 # We don't know which manifests are missing yet
1345 # We don't know which manifests are missing yet
1346 msng_mnfst_set = {}
1346 msng_mnfst_set = {}
1347 # Nor do we know which filenodes are missing.
1347 # Nor do we know which filenodes are missing.
1348 msng_filenode_set = {}
1348 msng_filenode_set = {}
1349
1349
1350 junk = mnfst.index[len(mnfst) - 1] # Get around a bug in lazyindex
1350 junk = mnfst.index[len(mnfst) - 1] # Get around a bug in lazyindex
1351 junk = None
1351 junk = None
1352
1352
1353 # A changeset always belongs to itself, so the changenode lookup
1353 # A changeset always belongs to itself, so the changenode lookup
1354 # function for a changenode is identity.
1354 # function for a changenode is identity.
1355 def identity(x):
1355 def identity(x):
1356 return x
1356 return x
1357
1357
1358 # If we determine that a particular file or manifest node must be a
1358 # If we determine that a particular file or manifest node must be a
1359 # node that the recipient of the changegroup will already have, we can
1359 # node that the recipient of the changegroup will already have, we can
1360 # also assume the recipient will have all the parents. This function
1360 # also assume the recipient will have all the parents. This function
1361 # prunes them from the set of missing nodes.
1361 # prunes them from the set of missing nodes.
1362 def prune_parents(revlog, hasset, msngset):
1362 def prune_parents(revlog, hasset, msngset):
1363 for r in revlog.ancestors(*[revlog.rev(n) for n in hasset]):
1363 for r in revlog.ancestors(*[revlog.rev(n) for n in hasset]):
1364 msngset.pop(revlog.node(r), None)
1364 msngset.pop(revlog.node(r), None)
1365
1365
1366 # Use the information collected in collect_manifests_and_files to say
1366 # Use the information collected in collect_manifests_and_files to say
1367 # which changenode any manifestnode belongs to.
1367 # which changenode any manifestnode belongs to.
1368 def lookup_manifest_link(mnfstnode):
1368 def lookup_manifest_link(mnfstnode):
1369 return msng_mnfst_set[mnfstnode]
1369 return msng_mnfst_set[mnfstnode]
1370
1370
1371 # A function generating function that sets up the initial environment
1371 # A function generating function that sets up the initial environment
1372 # the inner function.
1372 # the inner function.
1373 def filenode_collector(changedfiles):
1373 def filenode_collector(changedfiles):
1374 # This gathers information from each manifestnode included in the
1374 # This gathers information from each manifestnode included in the
1375 # changegroup about which filenodes the manifest node references
1375 # changegroup about which filenodes the manifest node references
1376 # so we can include those in the changegroup too.
1376 # so we can include those in the changegroup too.
1377 #
1377 #
1378 # It also remembers which changenode each filenode belongs to. It
1378 # It also remembers which changenode each filenode belongs to. It
1379 # does this by assuming the a filenode belongs to the changenode
1379 # does this by assuming the a filenode belongs to the changenode
1380 # the first manifest that references it belongs to.
1380 # the first manifest that references it belongs to.
1381 def collect_msng_filenodes(mnfstnode):
1381 def collect_msng_filenodes(mnfstnode):
1382 r = mnfst.rev(mnfstnode)
1382 r = mnfst.rev(mnfstnode)
1383 if r - 1 in mnfst.parentrevs(r):
1383 if r - 1 in mnfst.parentrevs(r):
1384 # If the previous rev is one of the parents,
1384 # If the previous rev is one of the parents,
1385 # we only need to see a diff.
1385 # we only need to see a diff.
1386 deltamf = mnfst.readdelta(mnfstnode)
1386 deltamf = mnfst.readdelta(mnfstnode)
1387 # For each line in the delta
1387 # For each line in the delta
1388 for f, fnode in deltamf.iteritems():
1388 for f, fnode in deltamf.iteritems():
1389 f = changedfiles.get(f, None)
1389 f = changedfiles.get(f, None)
1390 # And if the file is in the list of files we care
1390 # And if the file is in the list of files we care
1391 # about.
1391 # about.
1392 if f is not None:
1392 if f is not None:
1393 # Get the changenode this manifest belongs to
1393 # Get the changenode this manifest belongs to
1394 clnode = msng_mnfst_set[mnfstnode]
1394 clnode = msng_mnfst_set[mnfstnode]
1395 # Create the set of filenodes for the file if
1395 # Create the set of filenodes for the file if
1396 # there isn't one already.
1396 # there isn't one already.
1397 ndset = msng_filenode_set.setdefault(f, {})
1397 ndset = msng_filenode_set.setdefault(f, {})
1398 # And set the filenode's changelog node to the
1398 # And set the filenode's changelog node to the
1399 # manifest's if it hasn't been set already.
1399 # manifest's if it hasn't been set already.
1400 ndset.setdefault(fnode, clnode)
1400 ndset.setdefault(fnode, clnode)
1401 else:
1401 else:
1402 # Otherwise we need a full manifest.
1402 # Otherwise we need a full manifest.
1403 m = mnfst.read(mnfstnode)
1403 m = mnfst.read(mnfstnode)
1404 # For every file in we care about.
1404 # For every file in we care about.
1405 for f in changedfiles:
1405 for f in changedfiles:
1406 fnode = m.get(f, None)
1406 fnode = m.get(f, None)
1407 # If it's in the manifest
1407 # If it's in the manifest
1408 if fnode is not None:
1408 if fnode is not None:
1409 # See comments above.
1409 # See comments above.
1410 clnode = msng_mnfst_set[mnfstnode]
1410 clnode = msng_mnfst_set[mnfstnode]
1411 ndset = msng_filenode_set.setdefault(f, {})
1411 ndset = msng_filenode_set.setdefault(f, {})
1412 ndset.setdefault(fnode, clnode)
1412 ndset.setdefault(fnode, clnode)
1413 return collect_msng_filenodes
1413 return collect_msng_filenodes
1414
1414
1415 # We have a list of filenodes we think we need for a file, lets remove
1415 # We have a list of filenodes we think we need for a file, lets remove
1416 # all those we know the recipient must have.
1416 # all those we know the recipient must have.
1417 def prune_filenodes(f, filerevlog):
1417 def prune_filenodes(f, filerevlog):
1418 msngset = msng_filenode_set[f]
1418 msngset = msng_filenode_set[f]
1419 hasset = set()
1419 hasset = set()
1420 # If a 'missing' filenode thinks it belongs to a changenode we
1420 # If a 'missing' filenode thinks it belongs to a changenode we
1421 # assume the recipient must have, then the recipient must have
1421 # assume the recipient must have, then the recipient must have
1422 # that filenode.
1422 # that filenode.
1423 for n in msngset:
1423 for n in msngset:
1424 clnode = cl.node(filerevlog.linkrev(filerevlog.rev(n)))
1424 clnode = cl.node(filerevlog.linkrev(filerevlog.rev(n)))
1425 if clnode in has_cl_set:
1425 if clnode in has_cl_set:
1426 hasset.add(n)
1426 hasset.add(n)
1427 prune_parents(filerevlog, hasset, msngset)
1427 prune_parents(filerevlog, hasset, msngset)
1428
1428
1429 # A function generator function that sets up the a context for the
1429 # A function generator function that sets up the a context for the
1430 # inner function.
1430 # inner function.
1431 def lookup_filenode_link_func(fname):
1431 def lookup_filenode_link_func(fname):
1432 msngset = msng_filenode_set[fname]
1432 msngset = msng_filenode_set[fname]
1433 # Lookup the changenode the filenode belongs to.
1433 # Lookup the changenode the filenode belongs to.
1434 def lookup_filenode_link(fnode):
1434 def lookup_filenode_link(fnode):
1435 return msngset[fnode]
1435 return msngset[fnode]
1436 return lookup_filenode_link
1436 return lookup_filenode_link
1437
1437
1438 # Add the nodes that were explicitly requested.
1438 # Add the nodes that were explicitly requested.
1439 def add_extra_nodes(name, nodes):
1439 def add_extra_nodes(name, nodes):
1440 if not extranodes or name not in extranodes:
1440 if not extranodes or name not in extranodes:
1441 return
1441 return
1442
1442
1443 for node, linknode in extranodes[name]:
1443 for node, linknode in extranodes[name]:
1444 if node not in nodes:
1444 if node not in nodes:
1445 nodes[node] = linknode
1445 nodes[node] = linknode
1446
1446
1447 # Now that we have all theses utility functions to help out and
1447 # Now that we have all theses utility functions to help out and
1448 # logically divide up the task, generate the group.
1448 # logically divide up the task, generate the group.
1449 def gengroup():
1449 def gengroup():
1450 # The set of changed files starts empty.
1450 # The set of changed files starts empty.
1451 changedfiles = {}
1451 changedfiles = {}
1452 collect = changegroup.collector(cl, msng_mnfst_set, changedfiles)
1452 collect = changegroup.collector(cl, msng_mnfst_set, changedfiles)
1453
1453
1454 # Create a changenode group generator that will call our functions
1454 # Create a changenode group generator that will call our functions
1455 # back to lookup the owning changenode and collect information.
1455 # back to lookup the owning changenode and collect information.
1456 group = cl.group(msng_cl_lst, identity, collect)
1456 group = cl.group(msng_cl_lst, identity, collect)
1457 cnt = 0
1457 cnt = 0
1458 for chnk in group:
1458 for chnk in group:
1459 yield chnk
1459 yield chnk
1460 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
1460 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
1461 cnt += 1
1461 cnt += 1
1462 self.ui.progress(_('bundling changes'), None)
1462 self.ui.progress(_('bundling changes'), None)
1463
1463
1464
1464
1465 # Figure out which manifest nodes (of the ones we think might be
1465 # Figure out which manifest nodes (of the ones we think might be
1466 # part of the changegroup) the recipient must know about and
1466 # part of the changegroup) the recipient must know about and
1467 # remove them from the changegroup.
1467 # remove them from the changegroup.
1468 has_mnfst_set = set()
1468 has_mnfst_set = set()
1469 for n in msng_mnfst_set:
1469 for n in msng_mnfst_set:
1470 # If a 'missing' manifest thinks it belongs to a changenode
1470 # If a 'missing' manifest thinks it belongs to a changenode
1471 # the recipient is assumed to have, obviously the recipient
1471 # the recipient is assumed to have, obviously the recipient
1472 # must have that manifest.
1472 # must have that manifest.
1473 linknode = cl.node(mnfst.linkrev(mnfst.rev(n)))
1473 linknode = cl.node(mnfst.linkrev(mnfst.rev(n)))
1474 if linknode in has_cl_set:
1474 if linknode in has_cl_set:
1475 has_mnfst_set.add(n)
1475 has_mnfst_set.add(n)
1476 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1476 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1477 add_extra_nodes(1, msng_mnfst_set)
1477 add_extra_nodes(1, msng_mnfst_set)
1478 msng_mnfst_lst = msng_mnfst_set.keys()
1478 msng_mnfst_lst = msng_mnfst_set.keys()
1479 # Sort the manifestnodes by revision number.
1479 # Sort the manifestnodes by revision number.
1480 msng_mnfst_lst.sort(key=mnfst.rev)
1480 msng_mnfst_lst.sort(key=mnfst.rev)
1481 # Create a generator for the manifestnodes that calls our lookup
1481 # Create a generator for the manifestnodes that calls our lookup
1482 # and data collection functions back.
1482 # and data collection functions back.
1483 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1483 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1484 filenode_collector(changedfiles))
1484 filenode_collector(changedfiles))
1485 cnt = 0
1485 cnt = 0
1486 for chnk in group:
1486 for chnk in group:
1487 yield chnk
1487 yield chnk
1488 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
1488 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
1489 cnt += 1
1489 cnt += 1
1490 self.ui.progress(_('bundling manifests'), None)
1490 self.ui.progress(_('bundling manifests'), None)
1491
1491
1492 # These are no longer needed, dereference and toss the memory for
1492 # These are no longer needed, dereference and toss the memory for
1493 # them.
1493 # them.
1494 msng_mnfst_lst = None
1494 msng_mnfst_lst = None
1495 msng_mnfst_set.clear()
1495 msng_mnfst_set.clear()
1496
1496
1497 if extranodes:
1497 if extranodes:
1498 for fname in extranodes:
1498 for fname in extranodes:
1499 if isinstance(fname, int):
1499 if isinstance(fname, int):
1500 continue
1500 continue
1501 msng_filenode_set.setdefault(fname, {})
1501 msng_filenode_set.setdefault(fname, {})
1502 changedfiles[fname] = 1
1502 changedfiles[fname] = 1
1503 # Go through all our files in order sorted by name.
1503 # Go through all our files in order sorted by name.
1504 cnt = 0
1504 cnt = 0
1505 for fname in sorted(changedfiles):
1505 for fname in sorted(changedfiles):
1506 filerevlog = self.file(fname)
1506 filerevlog = self.file(fname)
1507 if not len(filerevlog):
1507 if not len(filerevlog):
1508 raise util.Abort(_("empty or missing revlog for %s") % fname)
1508 raise util.Abort(_("empty or missing revlog for %s") % fname)
1509 # Toss out the filenodes that the recipient isn't really
1509 # Toss out the filenodes that the recipient isn't really
1510 # missing.
1510 # missing.
1511 if fname in msng_filenode_set:
1511 if fname in msng_filenode_set:
1512 prune_filenodes(fname, filerevlog)
1512 prune_filenodes(fname, filerevlog)
1513 add_extra_nodes(fname, msng_filenode_set[fname])
1513 add_extra_nodes(fname, msng_filenode_set[fname])
1514 msng_filenode_lst = msng_filenode_set[fname].keys()
1514 msng_filenode_lst = msng_filenode_set[fname].keys()
1515 else:
1515 else:
1516 msng_filenode_lst = []
1516 msng_filenode_lst = []
1517 # If any filenodes are left, generate the group for them,
1517 # If any filenodes are left, generate the group for them,
1518 # otherwise don't bother.
1518 # otherwise don't bother.
1519 if len(msng_filenode_lst) > 0:
1519 if len(msng_filenode_lst) > 0:
1520 yield changegroup.chunkheader(len(fname))
1520 yield changegroup.chunkheader(len(fname))
1521 yield fname
1521 yield fname
1522 # Sort the filenodes by their revision #
1522 # Sort the filenodes by their revision #
1523 msng_filenode_lst.sort(key=filerevlog.rev)
1523 msng_filenode_lst.sort(key=filerevlog.rev)
1524 # Create a group generator and only pass in a changenode
1524 # Create a group generator and only pass in a changenode
1525 # lookup function as we need to collect no information
1525 # lookup function as we need to collect no information
1526 # from filenodes.
1526 # from filenodes.
1527 group = filerevlog.group(msng_filenode_lst,
1527 group = filerevlog.group(msng_filenode_lst,
1528 lookup_filenode_link_func(fname))
1528 lookup_filenode_link_func(fname))
1529 for chnk in group:
1529 for chnk in group:
1530 self.ui.progress(
1530 self.ui.progress(
1531 _('bundling files'), cnt, item=fname, unit=_('chunks'))
1531 _('bundling files'), cnt, item=fname, unit=_('chunks'))
1532 cnt += 1
1532 cnt += 1
1533 yield chnk
1533 yield chnk
1534 if fname in msng_filenode_set:
1534 if fname in msng_filenode_set:
1535 # Don't need this anymore, toss it to free memory.
1535 # Don't need this anymore, toss it to free memory.
1536 del msng_filenode_set[fname]
1536 del msng_filenode_set[fname]
1537 # Signal that no more groups are left.
1537 # Signal that no more groups are left.
1538 yield changegroup.closechunk()
1538 yield changegroup.closechunk()
1539 self.ui.progress(_('bundling files'), None)
1539 self.ui.progress(_('bundling files'), None)
1540
1540
1541 if msng_cl_lst:
1541 if msng_cl_lst:
1542 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1542 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1543
1543
1544 return util.chunkbuffer(gengroup())
1544 return util.chunkbuffer(gengroup())
1545
1545
1546 def changegroup(self, basenodes, source):
1546 def changegroup(self, basenodes, source):
1547 # to avoid a race we use changegroupsubset() (issue1320)
1547 # to avoid a race we use changegroupsubset() (issue1320)
1548 return self.changegroupsubset(basenodes, self.heads(), source)
1548 return self.changegroupsubset(basenodes, self.heads(), source)
1549
1549
1550 def _changegroup(self, nodes, source):
1550 def _changegroup(self, nodes, source):
1551 """Compute the changegroup of all nodes that we have that a recipient
1551 """Compute the changegroup of all nodes that we have that a recipient
1552 doesn't. Return a chunkbuffer object whose read() method will return
1552 doesn't. Return a chunkbuffer object whose read() method will return
1553 successive changegroup chunks.
1553 successive changegroup chunks.
1554
1554
1555 This is much easier than the previous function as we can assume that
1555 This is much easier than the previous function as we can assume that
1556 the recipient has any changenode we aren't sending them.
1556 the recipient has any changenode we aren't sending them.
1557
1557
1558 nodes is the set of nodes to send"""
1558 nodes is the set of nodes to send"""
1559
1559
1560 self.hook('preoutgoing', throw=True, source=source)
1560 self.hook('preoutgoing', throw=True, source=source)
1561
1561
1562 cl = self.changelog
1562 cl = self.changelog
1563 revset = set([cl.rev(n) for n in nodes])
1563 revset = set([cl.rev(n) for n in nodes])
1564 self.changegroupinfo(nodes, source)
1564 self.changegroupinfo(nodes, source)
1565
1565
1566 def identity(x):
1566 def identity(x):
1567 return x
1567 return x
1568
1568
1569 def gennodelst(log):
1569 def gennodelst(log):
1570 for r in log:
1570 for r in log:
1571 if log.linkrev(r) in revset:
1571 if log.linkrev(r) in revset:
1572 yield log.node(r)
1572 yield log.node(r)
1573
1573
1574 def lookuprevlink_func(revlog):
1574 def lookuprevlink_func(revlog):
1575 def lookuprevlink(n):
1575 def lookuprevlink(n):
1576 return cl.node(revlog.linkrev(revlog.rev(n)))
1576 return cl.node(revlog.linkrev(revlog.rev(n)))
1577 return lookuprevlink
1577 return lookuprevlink
1578
1578
1579 def gengroup():
1579 def gengroup():
1580 '''yield a sequence of changegroup chunks (strings)'''
1580 '''yield a sequence of changegroup chunks (strings)'''
1581 # construct a list of all changed files
1581 # construct a list of all changed files
1582 changedfiles = {}
1582 changedfiles = {}
1583 mmfs = {}
1583 mmfs = {}
1584 collect = changegroup.collector(cl, mmfs, changedfiles)
1584 collect = changegroup.collector(cl, mmfs, changedfiles)
1585
1585
1586 cnt = 0
1586 cnt = 0
1587 for chnk in cl.group(nodes, identity, collect):
1587 for chnk in cl.group(nodes, identity, collect):
1588 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
1588 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
1589 cnt += 1
1589 cnt += 1
1590 yield chnk
1590 yield chnk
1591 self.ui.progress(_('bundling changes'), None)
1591 self.ui.progress(_('bundling changes'), None)
1592
1592
1593 mnfst = self.manifest
1593 mnfst = self.manifest
1594 nodeiter = gennodelst(mnfst)
1594 nodeiter = gennodelst(mnfst)
1595 cnt = 0
1595 cnt = 0
1596 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1596 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1597 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
1597 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
1598 cnt += 1
1598 cnt += 1
1599 yield chnk
1599 yield chnk
1600 self.ui.progress(_('bundling manifests'), None)
1600 self.ui.progress(_('bundling manifests'), None)
1601
1601
1602 cnt = 0
1602 cnt = 0
1603 for fname in sorted(changedfiles):
1603 for fname in sorted(changedfiles):
1604 filerevlog = self.file(fname)
1604 filerevlog = self.file(fname)
1605 if not len(filerevlog):
1605 if not len(filerevlog):
1606 raise util.Abort(_("empty or missing revlog for %s") % fname)
1606 raise util.Abort(_("empty or missing revlog for %s") % fname)
1607 nodeiter = gennodelst(filerevlog)
1607 nodeiter = gennodelst(filerevlog)
1608 nodeiter = list(nodeiter)
1608 nodeiter = list(nodeiter)
1609 if nodeiter:
1609 if nodeiter:
1610 yield changegroup.chunkheader(len(fname))
1610 yield changegroup.chunkheader(len(fname))
1611 yield fname
1611 yield fname
1612 lookup = lookuprevlink_func(filerevlog)
1612 lookup = lookuprevlink_func(filerevlog)
1613 for chnk in filerevlog.group(nodeiter, lookup):
1613 for chnk in filerevlog.group(nodeiter, lookup):
1614 self.ui.progress(
1614 self.ui.progress(
1615 _('bundling files'), cnt, item=fname, unit=_('chunks'))
1615 _('bundling files'), cnt, item=fname, unit=_('chunks'))
1616 cnt += 1
1616 cnt += 1
1617 yield chnk
1617 yield chnk
1618 self.ui.progress(_('bundling files'), None)
1618 self.ui.progress(_('bundling files'), None)
1619
1619
1620 yield changegroup.closechunk()
1620 yield changegroup.closechunk()
1621
1621
1622 if nodes:
1622 if nodes:
1623 self.hook('outgoing', node=hex(nodes[0]), source=source)
1623 self.hook('outgoing', node=hex(nodes[0]), source=source)
1624
1624
1625 return util.chunkbuffer(gengroup())
1625 return util.chunkbuffer(gengroup())
1626
1626
1627 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1627 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1628 """Add the changegroup returned by source.read() to this repo.
1628 """Add the changegroup returned by source.read() to this repo.
1629 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1629 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1630 the URL of the repo where this changegroup is coming from.
1630 the URL of the repo where this changegroup is coming from.
1631
1631
1632 Return an integer summarizing the change to this repo:
1632 Return an integer summarizing the change to this repo:
1633 - nothing changed or no source: 0
1633 - nothing changed or no source: 0
1634 - more heads than before: 1+added heads (2..n)
1634 - more heads than before: 1+added heads (2..n)
1635 - fewer heads than before: -1-removed heads (-2..-n)
1635 - fewer heads than before: -1-removed heads (-2..-n)
1636 - number of heads stays the same: 1
1636 - number of heads stays the same: 1
1637 """
1637 """
1638 def csmap(x):
1638 def csmap(x):
1639 self.ui.debug("add changeset %s\n" % short(x))
1639 self.ui.debug("add changeset %s\n" % short(x))
1640 return len(cl)
1640 return len(cl)
1641
1641
1642 def revmap(x):
1642 def revmap(x):
1643 return cl.rev(x)
1643 return cl.rev(x)
1644
1644
1645 if not source:
1645 if not source:
1646 return 0
1646 return 0
1647
1647
1648 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1648 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1649
1649
1650 changesets = files = revisions = 0
1650 changesets = files = revisions = 0
1651 efiles = set()
1651 efiles = set()
1652
1652
1653 # write changelog data to temp files so concurrent readers will not see
1653 # write changelog data to temp files so concurrent readers will not see
1654 # inconsistent view
1654 # inconsistent view
1655 cl = self.changelog
1655 cl = self.changelog
1656 cl.delayupdate()
1656 cl.delayupdate()
1657 oldheads = len(cl.heads())
1657 oldheads = len(cl.heads())
1658
1658
1659 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1659 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1660 try:
1660 try:
1661 trp = weakref.proxy(tr)
1661 trp = weakref.proxy(tr)
1662 # pull off the changeset group
1662 # pull off the changeset group
1663 self.ui.status(_("adding changesets\n"))
1663 self.ui.status(_("adding changesets\n"))
1664 clstart = len(cl)
1664 clstart = len(cl)
1665 class prog(object):
1665 class prog(object):
1666 step = _('changesets')
1666 step = _('changesets')
1667 count = 1
1667 count = 1
1668 ui = self.ui
1668 ui = self.ui
1669 total = None
1669 total = None
1670 def __call__(self):
1670 def __call__(self):
1671 self.ui.progress(self.step, self.count, unit=_('chunks'),
1671 self.ui.progress(self.step, self.count, unit=_('chunks'),
1672 total=self.total)
1672 total=self.total)
1673 self.count += 1
1673 self.count += 1
1674 pr = prog()
1674 pr = prog()
1675 chunkiter = changegroup.chunkiter(source, progress=pr)
1675 chunkiter = changegroup.chunkiter(source, progress=pr)
1676 if cl.addgroup(chunkiter, csmap, trp) is None and not emptyok:
1676 if cl.addgroup(chunkiter, csmap, trp) is None and not emptyok:
1677 raise util.Abort(_("received changelog group is empty"))
1677 raise util.Abort(_("received changelog group is empty"))
1678 clend = len(cl)
1678 clend = len(cl)
1679 changesets = clend - clstart
1679 changesets = clend - clstart
1680 for c in xrange(clstart, clend):
1680 for c in xrange(clstart, clend):
1681 efiles.update(self[c].files())
1681 efiles.update(self[c].files())
1682 efiles = len(efiles)
1682 efiles = len(efiles)
1683 self.ui.progress(_('changesets'), None)
1683 self.ui.progress(_('changesets'), None)
1684
1684
1685 # pull off the manifest group
1685 # pull off the manifest group
1686 self.ui.status(_("adding manifests\n"))
1686 self.ui.status(_("adding manifests\n"))
1687 pr.step = _('manifests')
1687 pr.step = _('manifests')
1688 pr.count = 1
1688 pr.count = 1
1689 pr.total = changesets # manifests <= changesets
1689 pr.total = changesets # manifests <= changesets
1690 chunkiter = changegroup.chunkiter(source, progress=pr)
1690 chunkiter = changegroup.chunkiter(source, progress=pr)
1691 # no need to check for empty manifest group here:
1691 # no need to check for empty manifest group here:
1692 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1692 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1693 # no new manifest will be created and the manifest group will
1693 # no new manifest will be created and the manifest group will
1694 # be empty during the pull
1694 # be empty during the pull
1695 self.manifest.addgroup(chunkiter, revmap, trp)
1695 self.manifest.addgroup(chunkiter, revmap, trp)
1696 self.ui.progress(_('manifests'), None)
1696 self.ui.progress(_('manifests'), None)
1697
1697
1698 needfiles = {}
1698 needfiles = {}
1699 if self.ui.configbool('server', 'validate', default=False):
1699 if self.ui.configbool('server', 'validate', default=False):
1700 # validate incoming csets have their manifests
1700 # validate incoming csets have their manifests
1701 for cset in xrange(clstart, clend):
1701 for cset in xrange(clstart, clend):
1702 mfest = self.changelog.read(self.changelog.node(cset))[0]
1702 mfest = self.changelog.read(self.changelog.node(cset))[0]
1703 mfest = self.manifest.readdelta(mfest)
1703 mfest = self.manifest.readdelta(mfest)
1704 # store file nodes we must see
1704 # store file nodes we must see
1705 for f, n in mfest.iteritems():
1705 for f, n in mfest.iteritems():
1706 needfiles.setdefault(f, set()).add(n)
1706 needfiles.setdefault(f, set()).add(n)
1707
1707
1708 # process the files
1708 # process the files
1709 self.ui.status(_("adding file changes\n"))
1709 self.ui.status(_("adding file changes\n"))
1710 pr.step = 'files'
1710 pr.step = 'files'
1711 pr.count = 1
1711 pr.count = 1
1712 pr.total = efiles
1712 pr.total = efiles
1713 while 1:
1713 while 1:
1714 f = changegroup.getchunk(source)
1714 f = changegroup.getchunk(source)
1715 if not f:
1715 if not f:
1716 break
1716 break
1717 self.ui.debug("adding %s revisions\n" % f)
1717 self.ui.debug("adding %s revisions\n" % f)
1718 pr()
1718 pr()
1719 fl = self.file(f)
1719 fl = self.file(f)
1720 o = len(fl)
1720 o = len(fl)
1721 chunkiter = changegroup.chunkiter(source)
1721 chunkiter = changegroup.chunkiter(source)
1722 if fl.addgroup(chunkiter, revmap, trp) is None:
1722 if fl.addgroup(chunkiter, revmap, trp) is None:
1723 raise util.Abort(_("received file revlog group is empty"))
1723 raise util.Abort(_("received file revlog group is empty"))
1724 revisions += len(fl) - o
1724 revisions += len(fl) - o
1725 files += 1
1725 files += 1
1726 if f in needfiles:
1726 if f in needfiles:
1727 needs = needfiles[f]
1727 needs = needfiles[f]
1728 for new in xrange(o, len(fl)):
1728 for new in xrange(o, len(fl)):
1729 n = fl.node(new)
1729 n = fl.node(new)
1730 if n in needs:
1730 if n in needs:
1731 needs.remove(n)
1731 needs.remove(n)
1732 if not needs:
1732 if not needs:
1733 del needfiles[f]
1733 del needfiles[f]
1734 self.ui.progress(_('files'), None)
1734 self.ui.progress(_('files'), None)
1735
1735
1736 for f, needs in needfiles.iteritems():
1736 for f, needs in needfiles.iteritems():
1737 fl = self.file(f)
1737 fl = self.file(f)
1738 for n in needs:
1738 for n in needs:
1739 try:
1739 try:
1740 fl.rev(n)
1740 fl.rev(n)
1741 except error.LookupError:
1741 except error.LookupError:
1742 raise util.Abort(
1742 raise util.Abort(
1743 _('missing file data for %s:%s - run hg verify') %
1743 _('missing file data for %s:%s - run hg verify') %
1744 (f, hex(n)))
1744 (f, hex(n)))
1745
1745
1746 newheads = len(cl.heads())
1746 newheads = len(cl.heads())
1747 heads = ""
1747 heads = ""
1748 if oldheads and newheads != oldheads:
1748 if oldheads and newheads != oldheads:
1749 heads = _(" (%+d heads)") % (newheads - oldheads)
1749 heads = _(" (%+d heads)") % (newheads - oldheads)
1750
1750
1751 self.ui.status(_("added %d changesets"
1751 self.ui.status(_("added %d changesets"
1752 " with %d changes to %d files%s\n")
1752 " with %d changes to %d files%s\n")
1753 % (changesets, revisions, files, heads))
1753 % (changesets, revisions, files, heads))
1754
1754
1755 if changesets > 0:
1755 if changesets > 0:
1756 p = lambda: cl.writepending() and self.root or ""
1756 p = lambda: cl.writepending() and self.root or ""
1757 self.hook('pretxnchangegroup', throw=True,
1757 self.hook('pretxnchangegroup', throw=True,
1758 node=hex(cl.node(clstart)), source=srctype,
1758 node=hex(cl.node(clstart)), source=srctype,
1759 url=url, pending=p)
1759 url=url, pending=p)
1760
1760
1761 # make changelog see real files again
1761 # make changelog see real files again
1762 cl.finalize(trp)
1762 cl.finalize(trp)
1763
1763
1764 tr.close()
1764 tr.close()
1765 finally:
1765 finally:
1766 tr.release()
1766 tr.release()
1767 if lock:
1767 if lock:
1768 lock.release()
1768 lock.release()
1769
1769
1770 if changesets > 0:
1770 if changesets > 0:
1771 # forcefully update the on-disk branch cache
1771 # forcefully update the on-disk branch cache
1772 self.ui.debug("updating the branch cache\n")
1772 self.ui.debug("updating the branch cache\n")
1773 self.branchtags()
1773 self.branchtags()
1774 self.hook("changegroup", node=hex(cl.node(clstart)),
1774 self.hook("changegroup", node=hex(cl.node(clstart)),
1775 source=srctype, url=url)
1775 source=srctype, url=url)
1776
1776
1777 for i in xrange(clstart, clend):
1777 for i in xrange(clstart, clend):
1778 self.hook("incoming", node=hex(cl.node(i)),
1778 self.hook("incoming", node=hex(cl.node(i)),
1779 source=srctype, url=url)
1779 source=srctype, url=url)
1780
1780
1781 # never return 0 here:
1781 # never return 0 here:
1782 if newheads < oldheads:
1782 if newheads < oldheads:
1783 return newheads - oldheads - 1
1783 return newheads - oldheads - 1
1784 else:
1784 else:
1785 return newheads - oldheads + 1
1785 return newheads - oldheads + 1
1786
1786
1787
1787
1788 def stream_in(self, remote):
1788 def stream_in(self, remote):
1789 fp = remote.stream_out()
1789 fp = remote.stream_out()
1790 l = fp.readline()
1790 l = fp.readline()
1791 try:
1791 try:
1792 resp = int(l)
1792 resp = int(l)
1793 except ValueError:
1793 except ValueError:
1794 raise error.ResponseError(
1794 raise error.ResponseError(
1795 _('Unexpected response from remote server:'), l)
1795 _('Unexpected response from remote server:'), l)
1796 if resp == 1:
1796 if resp == 1:
1797 raise util.Abort(_('operation forbidden by server'))
1797 raise util.Abort(_('operation forbidden by server'))
1798 elif resp == 2:
1798 elif resp == 2:
1799 raise util.Abort(_('locking the remote repository failed'))
1799 raise util.Abort(_('locking the remote repository failed'))
1800 elif resp != 0:
1800 elif resp != 0:
1801 raise util.Abort(_('the server sent an unknown error code'))
1801 raise util.Abort(_('the server sent an unknown error code'))
1802 self.ui.status(_('streaming all changes\n'))
1802 self.ui.status(_('streaming all changes\n'))
1803 l = fp.readline()
1803 l = fp.readline()
1804 try:
1804 try:
1805 total_files, total_bytes = map(int, l.split(' ', 1))
1805 total_files, total_bytes = map(int, l.split(' ', 1))
1806 except (ValueError, TypeError):
1806 except (ValueError, TypeError):
1807 raise error.ResponseError(
1807 raise error.ResponseError(
1808 _('Unexpected response from remote server:'), l)
1808 _('Unexpected response from remote server:'), l)
1809 self.ui.status(_('%d files to transfer, %s of data\n') %
1809 self.ui.status(_('%d files to transfer, %s of data\n') %
1810 (total_files, util.bytecount(total_bytes)))
1810 (total_files, util.bytecount(total_bytes)))
1811 start = time.time()
1811 start = time.time()
1812 for i in xrange(total_files):
1812 for i in xrange(total_files):
1813 # XXX doesn't support '\n' or '\r' in filenames
1813 # XXX doesn't support '\n' or '\r' in filenames
1814 l = fp.readline()
1814 l = fp.readline()
1815 try:
1815 try:
1816 name, size = l.split('\0', 1)
1816 name, size = l.split('\0', 1)
1817 size = int(size)
1817 size = int(size)
1818 except (ValueError, TypeError):
1818 except (ValueError, TypeError):
1819 raise error.ResponseError(
1819 raise error.ResponseError(
1820 _('Unexpected response from remote server:'), l)
1820 _('Unexpected response from remote server:'), l)
1821 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1821 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1822 # for backwards compat, name was partially encoded
1822 # for backwards compat, name was partially encoded
1823 ofp = self.sopener(store.decodedir(name), 'w')
1823 ofp = self.sopener(store.decodedir(name), 'w')
1824 for chunk in util.filechunkiter(fp, limit=size):
1824 for chunk in util.filechunkiter(fp, limit=size):
1825 ofp.write(chunk)
1825 ofp.write(chunk)
1826 ofp.close()
1826 ofp.close()
1827 elapsed = time.time() - start
1827 elapsed = time.time() - start
1828 if elapsed <= 0:
1828 if elapsed <= 0:
1829 elapsed = 0.001
1829 elapsed = 0.001
1830 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1830 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1831 (util.bytecount(total_bytes), elapsed,
1831 (util.bytecount(total_bytes), elapsed,
1832 util.bytecount(total_bytes / elapsed)))
1832 util.bytecount(total_bytes / elapsed)))
1833 self.invalidate()
1833 self.invalidate()
1834 return len(self.heads()) + 1
1834 return len(self.heads()) + 1
1835
1835
1836 def clone(self, remote, heads=[], stream=False):
1836 def clone(self, remote, heads=[], stream=False):
1837 '''clone remote repository.
1837 '''clone remote repository.
1838
1838
1839 keyword arguments:
1839 keyword arguments:
1840 heads: list of revs to clone (forces use of pull)
1840 heads: list of revs to clone (forces use of pull)
1841 stream: use streaming clone if possible'''
1841 stream: use streaming clone if possible'''
1842
1842
1843 # now, all clients that can request uncompressed clones can
1843 # now, all clients that can request uncompressed clones can
1844 # read repo formats supported by all servers that can serve
1844 # read repo formats supported by all servers that can serve
1845 # them.
1845 # them.
1846
1846
1847 # if revlog format changes, client will have to check version
1847 # if revlog format changes, client will have to check version
1848 # and format flags on "stream" capability, and use
1848 # and format flags on "stream" capability, and use
1849 # uncompressed only if compatible.
1849 # uncompressed only if compatible.
1850
1850
1851 if stream and not heads and remote.capable('stream'):
1851 if stream and not heads and remote.capable('stream'):
1852 return self.stream_in(remote)
1852 return self.stream_in(remote)
1853 return self.pull(remote, heads)
1853 return self.pull(remote, heads)
1854
1854
1855 def pushkey(self, namespace, key, old, new):
1855 def pushkey(self, namespace, key, old, new):
1856 return pushkey.push(self, namespace, key, old, new)
1856 return pushkey.push(self, namespace, key, old, new)
1857
1857
1858 def listkeys(self, namespace):
1858 def listkeys(self, namespace):
1859 return pushkey.list(self, namespace)
1859 return pushkey.list(self, namespace)
1860
1860
1861 # used to avoid circular references so destructors work
1861 # used to avoid circular references so destructors work
1862 def aftertrans(files):
1862 def aftertrans(files):
1863 renamefiles = [tuple(t) for t in files]
1863 renamefiles = [tuple(t) for t in files]
1864 def a():
1864 def a():
1865 for src, dest in renamefiles:
1865 for src, dest in renamefiles:
1866 util.rename(src, dest)
1866 util.rename(src, dest)
1867 return a
1867 return a
1868
1868
1869 def instance(ui, path, create):
1869 def instance(ui, path, create):
1870 return localrepository(ui, util.drop_scheme('file', path), create)
1870 return localrepository(ui, util.drop_scheme('file', path), create)
1871
1871
1872 def islocal(path):
1872 def islocal(path):
1873 return True
1873 return True
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now