##// END OF EJS Templates
merge with crew-stable
Martin Geisler -
r9196:07d127e7 merge default
parent child Browse files
Show More
@@ -1,147 +1,148 b''
1 # fetch.py - pull and merge remote changes
1 # fetch.py - pull and merge remote changes
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''pull, update and merge in one command'''
8 '''pull, update and merge in one command'''
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial.node import nullid, short
11 from mercurial.node import nullid, short
12 from mercurial import commands, cmdutil, hg, util, url, error
12 from mercurial import commands, cmdutil, hg, util, url, error
13 from mercurial.lock import release
13 from mercurial.lock import release
14
14
15 def fetch(ui, repo, source='default', **opts):
15 def fetch(ui, repo, source='default', **opts):
16 '''pull changes from a remote repository, merge new changes if needed.
16 '''pull changes from a remote repository, merge new changes if needed.
17
17
18 This finds all changes from the repository at the specified path or URL
18 This finds all changes from the repository at the specified path or URL
19 and adds them to the local repository.
19 and adds them to the local repository.
20
20
21 If the pulled changes add a new branch head, the head is automatically
21 If the pulled changes add a new branch head, the head is automatically
22 merged, and the result of the merge is committed. Otherwise, the working
22 merged, and the result of the merge is committed. Otherwise, the working
23 directory is updated to include the new changes.
23 directory is updated to include the new changes.
24
24
25 When a merge occurs, the newly pulled changes are assumed to be
25 When a merge occurs, the newly pulled changes are assumed to be
26 "authoritative". The head of the new changes is used as the first parent,
26 "authoritative". The head of the new changes is used as the first parent,
27 with local changes as the second. To switch the merge order, use
27 with local changes as the second. To switch the merge order, use
28 --switch-parent.
28 --switch-parent.
29
29
30 See 'hg help dates' for a list of formats valid for -d/--date.
30 See 'hg help dates' for a list of formats valid for -d/--date.
31 '''
31 '''
32
32
33 date = opts.get('date')
33 date = opts.get('date')
34 if date:
34 if date:
35 opts['date'] = util.parsedate(date)
35 opts['date'] = util.parsedate(date)
36
36
37 parent, p2 = repo.dirstate.parents()
37 parent, p2 = repo.dirstate.parents()
38 branch = repo.dirstate.branch()
38 branch = repo.dirstate.branch()
39 branchnode = repo.branchtags().get(branch)
39 branchnode = repo.branchtags().get(branch)
40 if parent != branchnode:
40 if parent != branchnode:
41 raise util.Abort(_('working dir not at branch tip '
41 raise util.Abort(_('working dir not at branch tip '
42 '(use "hg update" to check out branch tip)'))
42 '(use "hg update" to check out branch tip)'))
43
43
44 if p2 != nullid:
44 if p2 != nullid:
45 raise util.Abort(_('outstanding uncommitted merge'))
45 raise util.Abort(_('outstanding uncommitted merge'))
46
46
47 wlock = lock = None
47 wlock = lock = None
48 try:
48 try:
49 wlock = repo.wlock()
49 wlock = repo.wlock()
50 lock = repo.lock()
50 lock = repo.lock()
51 mod, add, rem, del_ = repo.status()[:4]
51 mod, add, rem, del_ = repo.status()[:4]
52
52
53 if mod or add or rem:
53 if mod or add or rem:
54 raise util.Abort(_('outstanding uncommitted changes'))
54 raise util.Abort(_('outstanding uncommitted changes'))
55 if del_:
55 if del_:
56 raise util.Abort(_('working directory is missing some files'))
56 raise util.Abort(_('working directory is missing some files'))
57 bheads = repo.branchheads(branch)
57 bheads = repo.branchheads(branch)
58 bheads = [head for head in bheads if len(repo[head].children()) == 0]
58 bheads = [head for head in bheads if len(repo[head].children()) == 0]
59 if len(bheads) > 1:
59 if len(bheads) > 1:
60 raise util.Abort(_('multiple heads in this branch '
60 raise util.Abort(_('multiple heads in this branch '
61 '(use "hg heads ." and "hg merge" to merge)'))
61 '(use "hg heads ." and "hg merge" to merge)'))
62
62
63 other = hg.repository(cmdutil.remoteui(repo, opts),
63 other = hg.repository(cmdutil.remoteui(repo, opts),
64 ui.expandpath(source))
64 ui.expandpath(source))
65 ui.status(_('pulling from %s\n') %
65 ui.status(_('pulling from %s\n') %
66 url.hidepassword(ui.expandpath(source)))
66 url.hidepassword(ui.expandpath(source)))
67 revs = None
67 revs = None
68 if opts['rev']:
68 if opts['rev']:
69 try:
69 try:
70 revs = [other.lookup(rev) for rev in opts['rev']]
70 revs = [other.lookup(rev) for rev in opts['rev']]
71 except error.CapabilityError:
71 except error.CapabilityError:
72 err = _("Other repository doesn't support revision lookup, "
72 err = _("Other repository doesn't support revision lookup, "
73 "so a rev cannot be specified.")
73 "so a rev cannot be specified.")
74 raise util.Abort(err)
74 raise util.Abort(err)
75
75
76 # Are there any changes at all?
76 # Are there any changes at all?
77 modheads = repo.pull(other, heads=revs)
77 modheads = repo.pull(other, heads=revs)
78 if modheads == 0:
78 if modheads == 0:
79 return 0
79 return 0
80
80
81 # Is this a simple fast-forward along the current branch?
81 # Is this a simple fast-forward along the current branch?
82 newheads = repo.branchheads(branch)
82 newheads = repo.branchheads(branch)
83 newheads = [head for head in newheads if len(repo[head].children()) == 0]
83 newheads = [head for head in newheads if len(repo[head].children()) == 0]
84 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
84 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
85 if len(newheads) == 1:
85 if len(newheads) == 1:
86 if newchildren[0] != parent:
86 if newchildren[0] != parent:
87 return hg.clean(repo, newchildren[0])
87 return hg.clean(repo, newchildren[0])
88 else:
88 else:
89 return
89 return
90
90
91 # Are there more than one additional branch heads?
91 # Are there more than one additional branch heads?
92 newchildren = [n for n in newchildren if n != parent]
92 newchildren = [n for n in newchildren if n != parent]
93 newparent = parent
93 newparent = parent
94 if newchildren:
94 if newchildren:
95 newparent = newchildren[0]
95 newparent = newchildren[0]
96 hg.clean(repo, newparent)
96 hg.clean(repo, newparent)
97 newheads = [n for n in newheads if n != newparent]
97 newheads = [n for n in newheads if n != newparent]
98 if len(newheads) > 1:
98 if len(newheads) > 1:
99 ui.status(_('not merging with %d other new branch heads '
99 ui.status(_('not merging with %d other new branch heads '
100 '(use "hg heads ." and "hg merge" to merge them)\n') %
100 '(use "hg heads ." and "hg merge" to merge them)\n') %
101 (len(newheads) - 1))
101 (len(newheads) - 1))
102 return
102 return
103
103
104 # Otherwise, let's merge.
104 # Otherwise, let's merge.
105 err = False
105 err = False
106 if newheads:
106 if newheads:
107 # By default, we consider the repository we're pulling
107 # By default, we consider the repository we're pulling
108 # *from* as authoritative, so we merge our changes into
108 # *from* as authoritative, so we merge our changes into
109 # theirs.
109 # theirs.
110 if opts['switch_parent']:
110 if opts['switch_parent']:
111 firstparent, secondparent = newparent, newheads[0]
111 firstparent, secondparent = newparent, newheads[0]
112 else:
112 else:
113 firstparent, secondparent = newheads[0], newparent
113 firstparent, secondparent = newheads[0], newparent
114 ui.status(_('updating to %d:%s\n') %
114 ui.status(_('updating to %d:%s\n') %
115 (repo.changelog.rev(firstparent),
115 (repo.changelog.rev(firstparent),
116 short(firstparent)))
116 short(firstparent)))
117 hg.clean(repo, firstparent)
117 hg.clean(repo, firstparent)
118 ui.status(_('merging with %d:%s\n') %
118 ui.status(_('merging with %d:%s\n') %
119 (repo.changelog.rev(secondparent), short(secondparent)))
119 (repo.changelog.rev(secondparent), short(secondparent)))
120 err = hg.merge(repo, secondparent, remind=False)
120 err = hg.merge(repo, secondparent, remind=False)
121
121
122 if not err:
122 if not err:
123 # we don't translate commit messages
123 message = (cmdutil.logmessage(opts) or
124 message = (cmdutil.logmessage(opts) or
124 (_('Automated merge with %s') %
125 ('Automated merge with %s' %
125 url.removeauth(other.url())))
126 url.removeauth(other.url())))
126 editor = cmdutil.commiteditor
127 editor = cmdutil.commiteditor
127 if opts.get('force_editor') or opts.get('edit'):
128 if opts.get('force_editor') or opts.get('edit'):
128 editor = cmdutil.commitforceeditor
129 editor = cmdutil.commitforceeditor
129 n = repo.commit(message, opts['user'], opts['date'],
130 n = repo.commit(message, opts['user'], opts['date'],
130 force=True, editor=editor)
131 force=True, editor=editor)
131 ui.status(_('new changeset %d:%s merges remote changes '
132 ui.status(_('new changeset %d:%s merges remote changes '
132 'with local\n') % (repo.changelog.rev(n),
133 'with local\n') % (repo.changelog.rev(n),
133 short(n)))
134 short(n)))
134
135
135 finally:
136 finally:
136 release(lock, wlock)
137 release(lock, wlock)
137
138
138 cmdtable = {
139 cmdtable = {
139 'fetch':
140 'fetch':
140 (fetch,
141 (fetch,
141 [('r', 'rev', [], _('a specific revision you would like to pull')),
142 [('r', 'rev', [], _('a specific revision you would like to pull')),
142 ('e', 'edit', None, _('edit commit message')),
143 ('e', 'edit', None, _('edit commit message')),
143 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
144 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
144 ('', 'switch-parent', None, _('switch parents when merging')),
145 ('', 'switch-parent', None, _('switch parents when merging')),
145 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
146 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
146 _('hg fetch [SOURCE]')),
147 _('hg fetch [SOURCE]')),
147 }
148 }
@@ -1,283 +1,284 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, incorporated herein by reference.
4 # GNU General Public License version 2, incorporated herein by reference.
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: os.unlink(f)
41 if f: os.unlink(f)
42 except: pass
42 except: pass
43 keys = []
43 keys = []
44 key, fingerprint = None, None
44 key, fingerprint = None, None
45 err = ""
45 err = ""
46 for l in ret.splitlines():
46 for l in ret.splitlines():
47 # see DETAILS in the gnupg documentation
47 # see DETAILS in the gnupg documentation
48 # filter the logger output
48 # filter the logger output
49 if not l.startswith("[GNUPG:]"):
49 if not l.startswith("[GNUPG:]"):
50 continue
50 continue
51 l = l[9:]
51 l = l[9:]
52 if l.startswith("ERRSIG"):
52 if l.startswith("ERRSIG"):
53 err = _("error while verifying signature")
53 err = _("error while verifying signature")
54 break
54 break
55 elif l.startswith("VALIDSIG"):
55 elif l.startswith("VALIDSIG"):
56 # fingerprint of the primary key
56 # fingerprint of the primary key
57 fingerprint = l.split()[10]
57 fingerprint = l.split()[10]
58 elif (l.startswith("GOODSIG") or
58 elif (l.startswith("GOODSIG") or
59 l.startswith("EXPSIG") or
59 l.startswith("EXPSIG") or
60 l.startswith("EXPKEYSIG") or
60 l.startswith("EXPKEYSIG") or
61 l.startswith("BADSIG")):
61 l.startswith("BADSIG")):
62 if key is not None:
62 if key is not None:
63 keys.append(key + [fingerprint])
63 keys.append(key + [fingerprint])
64 key = l.split(" ", 2)
64 key = l.split(" ", 2)
65 fingerprint = None
65 fingerprint = None
66 if err:
66 if err:
67 return err, []
67 return err, []
68 if key is not None:
68 if key is not None:
69 keys.append(key + [fingerprint])
69 keys.append(key + [fingerprint])
70 return err, keys
70 return err, keys
71
71
72 def newgpg(ui, **opts):
72 def newgpg(ui, **opts):
73 """create a new gpg instance"""
73 """create a new gpg instance"""
74 gpgpath = ui.config("gpg", "cmd", "gpg")
74 gpgpath = ui.config("gpg", "cmd", "gpg")
75 gpgkey = opts.get('key')
75 gpgkey = opts.get('key')
76 if not gpgkey:
76 if not gpgkey:
77 gpgkey = ui.config("gpg", "key", None)
77 gpgkey = ui.config("gpg", "key", None)
78 return gpg(gpgpath, gpgkey)
78 return gpg(gpgpath, gpgkey)
79
79
80 def sigwalk(repo):
80 def sigwalk(repo):
81 """
81 """
82 walk over every sigs, yields a couple
82 walk over every sigs, yields a couple
83 ((node, version, sig), (filename, linenumber))
83 ((node, version, sig), (filename, linenumber))
84 """
84 """
85 def parsefile(fileiter, context):
85 def parsefile(fileiter, context):
86 ln = 1
86 ln = 1
87 for l in fileiter:
87 for l in fileiter:
88 if not l:
88 if not l:
89 continue
89 continue
90 yield (l.split(" ", 2), (context, ln))
90 yield (l.split(" ", 2), (context, ln))
91 ln +=1
91 ln +=1
92
92
93 # read the heads
93 # read the heads
94 fl = repo.file(".hgsigs")
94 fl = repo.file(".hgsigs")
95 for r in reversed(fl.heads()):
95 for r in reversed(fl.heads()):
96 fn = ".hgsigs|%s" % hgnode.short(r)
96 fn = ".hgsigs|%s" % hgnode.short(r)
97 for item in parsefile(fl.read(r).splitlines(), fn):
97 for item in parsefile(fl.read(r).splitlines(), fn):
98 yield item
98 yield item
99 try:
99 try:
100 # read local signatures
100 # read local signatures
101 fn = "localsigs"
101 fn = "localsigs"
102 for item in parsefile(repo.opener(fn), fn):
102 for item in parsefile(repo.opener(fn), fn):
103 yield item
103 yield item
104 except IOError:
104 except IOError:
105 pass
105 pass
106
106
107 def getkeys(ui, repo, mygpg, sigdata, context):
107 def getkeys(ui, repo, mygpg, sigdata, context):
108 """get the keys who signed a data"""
108 """get the keys who signed a data"""
109 fn, ln = context
109 fn, ln = context
110 node, version, sig = sigdata
110 node, version, sig = sigdata
111 prefix = "%s:%d" % (fn, ln)
111 prefix = "%s:%d" % (fn, ln)
112 node = hgnode.bin(node)
112 node = hgnode.bin(node)
113
113
114 data = node2txt(repo, node, version)
114 data = node2txt(repo, node, version)
115 sig = binascii.a2b_base64(sig)
115 sig = binascii.a2b_base64(sig)
116 err, keys = mygpg.verify(data, sig)
116 err, keys = mygpg.verify(data, sig)
117 if err:
117 if err:
118 ui.warn("%s:%d %s\n" % (fn, ln , err))
118 ui.warn("%s:%d %s\n" % (fn, ln , err))
119 return None
119 return None
120
120
121 validkeys = []
121 validkeys = []
122 # warn for expired key and/or sigs
122 # warn for expired key and/or sigs
123 for key in keys:
123 for key in keys:
124 if key[0] == "BADSIG":
124 if key[0] == "BADSIG":
125 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
125 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
126 continue
126 continue
127 if key[0] == "EXPSIG":
127 if key[0] == "EXPSIG":
128 ui.write(_("%s Note: Signature has expired"
128 ui.write(_("%s Note: Signature has expired"
129 " (signed by: \"%s\")\n") % (prefix, key[2]))
129 " (signed by: \"%s\")\n") % (prefix, key[2]))
130 elif key[0] == "EXPKEYSIG":
130 elif key[0] == "EXPKEYSIG":
131 ui.write(_("%s Note: This key has expired"
131 ui.write(_("%s Note: This key has expired"
132 " (signed by: \"%s\")\n") % (prefix, key[2]))
132 " (signed by: \"%s\")\n") % (prefix, key[2]))
133 validkeys.append((key[1], key[2], key[3]))
133 validkeys.append((key[1], key[2], key[3]))
134 return validkeys
134 return validkeys
135
135
136 def sigs(ui, repo):
136 def sigs(ui, repo):
137 """list signed changesets"""
137 """list signed changesets"""
138 mygpg = newgpg(ui)
138 mygpg = newgpg(ui)
139 revs = {}
139 revs = {}
140
140
141 for data, context in sigwalk(repo):
141 for data, context in sigwalk(repo):
142 node, version, sig = data
142 node, version, sig = data
143 fn, ln = context
143 fn, ln = context
144 try:
144 try:
145 n = repo.lookup(node)
145 n = repo.lookup(node)
146 except KeyError:
146 except KeyError:
147 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
147 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
148 continue
148 continue
149 r = repo.changelog.rev(n)
149 r = repo.changelog.rev(n)
150 keys = getkeys(ui, repo, mygpg, data, context)
150 keys = getkeys(ui, repo, mygpg, data, context)
151 if not keys:
151 if not keys:
152 continue
152 continue
153 revs.setdefault(r, [])
153 revs.setdefault(r, [])
154 revs[r].extend(keys)
154 revs[r].extend(keys)
155 for rev in sorted(revs, reverse=True):
155 for rev in sorted(revs, reverse=True):
156 for k in revs[rev]:
156 for k in revs[rev]:
157 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
157 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
158 ui.write("%-30s %s\n" % (keystr(ui, k), r))
158 ui.write("%-30s %s\n" % (keystr(ui, k), r))
159
159
160 def check(ui, repo, rev):
160 def check(ui, repo, rev):
161 """verify all the signatures there may be for a particular revision"""
161 """verify all the signatures there may be for a particular revision"""
162 mygpg = newgpg(ui)
162 mygpg = newgpg(ui)
163 rev = repo.lookup(rev)
163 rev = repo.lookup(rev)
164 hexrev = hgnode.hex(rev)
164 hexrev = hgnode.hex(rev)
165 keys = []
165 keys = []
166
166
167 for data, context in sigwalk(repo):
167 for data, context in sigwalk(repo):
168 node, version, sig = data
168 node, version, sig = data
169 if node == hexrev:
169 if node == hexrev:
170 k = getkeys(ui, repo, mygpg, data, context)
170 k = getkeys(ui, repo, mygpg, data, context)
171 if k:
171 if k:
172 keys.extend(k)
172 keys.extend(k)
173
173
174 if not keys:
174 if not keys:
175 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
175 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
176 return
176 return
177
177
178 # print summary
178 # print summary
179 ui.write("%s is signed by:\n" % hgnode.short(rev))
179 ui.write("%s is signed by:\n" % hgnode.short(rev))
180 for key in keys:
180 for key in keys:
181 ui.write(" %s\n" % keystr(ui, key))
181 ui.write(" %s\n" % keystr(ui, key))
182
182
183 def keystr(ui, key):
183 def keystr(ui, key):
184 """associate a string to a key (username, comment)"""
184 """associate a string to a key (username, comment)"""
185 keyid, user, fingerprint = key
185 keyid, user, fingerprint = key
186 comment = ui.config("gpg", fingerprint, None)
186 comment = ui.config("gpg", fingerprint, None)
187 if comment:
187 if comment:
188 return "%s (%s)" % (user, comment)
188 return "%s (%s)" % (user, comment)
189 else:
189 else:
190 return user
190 return user
191
191
192 def sign(ui, repo, *revs, **opts):
192 def sign(ui, repo, *revs, **opts):
193 """add a signature for the current or given revision
193 """add a signature for the current or given revision
194
194
195 If no revision is given, the parent of the working directory is used,
195 If no revision is given, the parent of the working directory is used,
196 or tip if no revision is checked out.
196 or tip if no revision is checked out.
197
197
198 See 'hg help dates' for a list of formats valid for -d/--date.
198 See 'hg help dates' for a list of formats valid for -d/--date.
199 """
199 """
200
200
201 mygpg = newgpg(ui, **opts)
201 mygpg = newgpg(ui, **opts)
202 sigver = "0"
202 sigver = "0"
203 sigmessage = ""
203 sigmessage = ""
204
204
205 date = opts.get('date')
205 date = opts.get('date')
206 if date:
206 if date:
207 opts['date'] = util.parsedate(date)
207 opts['date'] = util.parsedate(date)
208
208
209 if revs:
209 if revs:
210 nodes = [repo.lookup(n) for n in revs]
210 nodes = [repo.lookup(n) for n in revs]
211 else:
211 else:
212 nodes = [node for node in repo.dirstate.parents()
212 nodes = [node for node in repo.dirstate.parents()
213 if node != hgnode.nullid]
213 if node != hgnode.nullid]
214 if len(nodes) > 1:
214 if len(nodes) > 1:
215 raise util.Abort(_('uncommitted merge - please provide a '
215 raise util.Abort(_('uncommitted merge - please provide a '
216 'specific revision'))
216 'specific revision'))
217 if not nodes:
217 if not nodes:
218 nodes = [repo.changelog.tip()]
218 nodes = [repo.changelog.tip()]
219
219
220 for n in nodes:
220 for n in nodes:
221 hexnode = hgnode.hex(n)
221 hexnode = hgnode.hex(n)
222 ui.write("Signing %d:%s\n" % (repo.changelog.rev(n),
222 ui.write("Signing %d:%s\n" % (repo.changelog.rev(n),
223 hgnode.short(n)))
223 hgnode.short(n)))
224 # build data
224 # build data
225 data = node2txt(repo, n, sigver)
225 data = node2txt(repo, n, sigver)
226 sig = mygpg.sign(data)
226 sig = mygpg.sign(data)
227 if not sig:
227 if not sig:
228 raise util.Abort(_("Error while signing"))
228 raise util.Abort(_("Error while signing"))
229 sig = binascii.b2a_base64(sig)
229 sig = binascii.b2a_base64(sig)
230 sig = sig.replace("\n", "")
230 sig = sig.replace("\n", "")
231 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
231 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
232
232
233 # write it
233 # write it
234 if opts['local']:
234 if opts['local']:
235 repo.opener("localsigs", "ab").write(sigmessage)
235 repo.opener("localsigs", "ab").write(sigmessage)
236 return
236 return
237
237
238 for x in repo.status(unknown=True)[:5]:
238 for x in repo.status(unknown=True)[:5]:
239 if ".hgsigs" in x and not opts["force"]:
239 if ".hgsigs" in x and not opts["force"]:
240 raise util.Abort(_("working copy of .hgsigs is changed "
240 raise util.Abort(_("working copy of .hgsigs is changed "
241 "(please commit .hgsigs manually "
241 "(please commit .hgsigs manually "
242 "or use --force)"))
242 "or use --force)"))
243
243
244 repo.wfile(".hgsigs", "ab").write(sigmessage)
244 repo.wfile(".hgsigs", "ab").write(sigmessage)
245
245
246 if '.hgsigs' not in repo.dirstate:
246 if '.hgsigs' not in repo.dirstate:
247 repo.add([".hgsigs"])
247 repo.add([".hgsigs"])
248
248
249 if opts["no_commit"]:
249 if opts["no_commit"]:
250 return
250 return
251
251
252 message = opts['message']
252 message = opts['message']
253 if not message:
253 if not message:
254 message = "\n".join([_("Added signature for changeset %s")
254 # we don't translate commit messages
255 message = "\n".join(["Added signature for changeset %s"
255 % hgnode.short(n)
256 % hgnode.short(n)
256 for n in nodes])
257 for n in nodes])
257 try:
258 try:
258 m = match.exact(repo.root, '', ['.hgsigs'])
259 m = match.exact(repo.root, '', ['.hgsigs'])
259 repo.commit(message, opts['user'], opts['date'], match=m)
260 repo.commit(message, opts['user'], opts['date'], match=m)
260 except ValueError, inst:
261 except ValueError, inst:
261 raise util.Abort(str(inst))
262 raise util.Abort(str(inst))
262
263
263 def node2txt(repo, node, ver):
264 def node2txt(repo, node, ver):
264 """map a manifest into some text"""
265 """map a manifest into some text"""
265 if ver == "0":
266 if ver == "0":
266 return "%s\n" % hgnode.hex(node)
267 return "%s\n" % hgnode.hex(node)
267 else:
268 else:
268 raise util.Abort(_("unknown signature version"))
269 raise util.Abort(_("unknown signature version"))
269
270
270 cmdtable = {
271 cmdtable = {
271 "sign":
272 "sign":
272 (sign,
273 (sign,
273 [('l', 'local', None, _('make the signature local')),
274 [('l', 'local', None, _('make the signature local')),
274 ('f', 'force', None, _('sign even if the sigfile is modified')),
275 ('f', 'force', None, _('sign even if the sigfile is modified')),
275 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
276 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
276 ('k', 'key', '', _('the key id to sign with')),
277 ('k', 'key', '', _('the key id to sign with')),
277 ('m', 'message', '', _('commit message')),
278 ('m', 'message', '', _('commit message')),
278 ] + commands.commitopts2,
279 ] + commands.commitopts2,
279 _('hg sign [OPTION]... [REVISION]...')),
280 _('hg sign [OPTION]... [REVISION]...')),
280 "sigcheck": (check, [], _('hg sigcheck REVISION')),
281 "sigcheck": (check, [], _('hg sigcheck REVISION')),
281 "sigs": (sigs, [], _('hg sigs')),
282 "sigs": (sigs, [], _('hg sigs')),
282 }
283 }
283
284
@@ -1,469 +1,470 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial repository.
10 This extension lets you rebase changesets in an existing Mercurial repository.
11
11
12 For more information:
12 For more information:
13 http://mercurial.selenic.com/wiki/RebaseProject
13 http://mercurial.selenic.com/wiki/RebaseProject
14 '''
14 '''
15
15
16 from mercurial import util, repair, merge, cmdutil, commands, error
16 from mercurial import util, repair, merge, cmdutil, commands, error
17 from mercurial import extensions, ancestor, copies, patch
17 from mercurial import extensions, ancestor, copies, patch
18 from mercurial.commands import templateopts
18 from mercurial.commands import templateopts
19 from mercurial.node import nullrev
19 from mercurial.node import nullrev
20 from mercurial.lock import release
20 from mercurial.lock import release
21 from mercurial.i18n import _
21 from mercurial.i18n import _
22 import os, errno
22 import os, errno
23
23
24 def rebasemerge(repo, rev, first=False):
24 def rebasemerge(repo, rev, first=False):
25 'return the correct ancestor'
25 'return the correct ancestor'
26 oldancestor = ancestor.ancestor
26 oldancestor = ancestor.ancestor
27
27
28 def newancestor(a, b, pfunc):
28 def newancestor(a, b, pfunc):
29 ancestor.ancestor = oldancestor
29 ancestor.ancestor = oldancestor
30 if b == rev:
30 if b == rev:
31 return repo[rev].parents()[0].rev()
31 return repo[rev].parents()[0].rev()
32 return ancestor.ancestor(a, b, pfunc)
32 return ancestor.ancestor(a, b, pfunc)
33
33
34 if not first:
34 if not first:
35 ancestor.ancestor = newancestor
35 ancestor.ancestor = newancestor
36 else:
36 else:
37 repo.ui.debug(_("first revision, do not change ancestor\n"))
37 repo.ui.debug(_("first revision, do not change ancestor\n"))
38 stats = merge.update(repo, rev, True, True, False)
38 stats = merge.update(repo, rev, True, True, False)
39 return stats
39 return stats
40
40
41 def rebase(ui, repo, **opts):
41 def rebase(ui, repo, **opts):
42 """move changeset (and descendants) to a different branch
42 """move changeset (and descendants) to a different branch
43
43
44 Rebase uses repeated merging to graft changesets from one part of history
44 Rebase uses repeated merging to graft changesets from one part of history
45 onto another. This can be useful for linearizing local changes relative to
45 onto another. This can be useful for linearizing local changes relative to
46 a master development tree.
46 a master development tree.
47
47
48 If a rebase is interrupted to manually resolve a merge, it can be
48 If a rebase is interrupted to manually resolve a merge, it can be
49 continued with --continue/-c or aborted with --abort/-a.
49 continued with --continue/-c or aborted with --abort/-a.
50 """
50 """
51 originalwd = target = None
51 originalwd = target = None
52 external = nullrev
52 external = nullrev
53 state = {}
53 state = {}
54 skipped = set()
54 skipped = set()
55
55
56 lock = wlock = None
56 lock = wlock = None
57 try:
57 try:
58 lock = repo.lock()
58 lock = repo.lock()
59 wlock = repo.wlock()
59 wlock = repo.wlock()
60
60
61 # Validate input and define rebasing points
61 # Validate input and define rebasing points
62 destf = opts.get('dest', None)
62 destf = opts.get('dest', None)
63 srcf = opts.get('source', None)
63 srcf = opts.get('source', None)
64 basef = opts.get('base', None)
64 basef = opts.get('base', None)
65 contf = opts.get('continue')
65 contf = opts.get('continue')
66 abortf = opts.get('abort')
66 abortf = opts.get('abort')
67 collapsef = opts.get('collapse', False)
67 collapsef = opts.get('collapse', False)
68 extrafn = opts.get('extrafn')
68 extrafn = opts.get('extrafn')
69 keepf = opts.get('keep', False)
69 keepf = opts.get('keep', False)
70 keepbranchesf = opts.get('keepbranches', False)
70 keepbranchesf = opts.get('keepbranches', False)
71
71
72 if contf or abortf:
72 if contf or abortf:
73 if contf and abortf:
73 if contf and abortf:
74 raise error.ParseError('rebase',
74 raise error.ParseError('rebase',
75 _('cannot use both abort and continue'))
75 _('cannot use both abort and continue'))
76 if collapsef:
76 if collapsef:
77 raise error.ParseError(
77 raise error.ParseError(
78 'rebase', _('cannot use collapse with continue or abort'))
78 'rebase', _('cannot use collapse with continue or abort'))
79
79
80 if srcf or basef or destf:
80 if srcf or basef or destf:
81 raise error.ParseError('rebase',
81 raise error.ParseError('rebase',
82 _('abort and continue do not allow specifying revisions'))
82 _('abort and continue do not allow specifying revisions'))
83
83
84 (originalwd, target, state, collapsef, keepf,
84 (originalwd, target, state, collapsef, keepf,
85 keepbranchesf, external) = restorestatus(repo)
85 keepbranchesf, external) = restorestatus(repo)
86 if abortf:
86 if abortf:
87 abort(repo, originalwd, target, state)
87 abort(repo, originalwd, target, state)
88 return
88 return
89 else:
89 else:
90 if srcf and basef:
90 if srcf and basef:
91 raise error.ParseError('rebase', _('cannot specify both a '
91 raise error.ParseError('rebase', _('cannot specify both a '
92 'revision and a base'))
92 'revision and a base'))
93 cmdutil.bail_if_changed(repo)
93 cmdutil.bail_if_changed(repo)
94 result = buildstate(repo, destf, srcf, basef, collapsef)
94 result = buildstate(repo, destf, srcf, basef, collapsef)
95 if result:
95 if result:
96 originalwd, target, state, external = result
96 originalwd, target, state, external = result
97 else: # Empty state built, nothing to rebase
97 else: # Empty state built, nothing to rebase
98 ui.status(_('nothing to rebase\n'))
98 ui.status(_('nothing to rebase\n'))
99 return
99 return
100
100
101 if keepbranchesf:
101 if keepbranchesf:
102 if extrafn:
102 if extrafn:
103 raise error.ParseError(
103 raise error.ParseError(
104 'rebase', _('cannot use both keepbranches and extrafn'))
104 'rebase', _('cannot use both keepbranches and extrafn'))
105 def extrafn(ctx, extra):
105 def extrafn(ctx, extra):
106 extra['branch'] = ctx.branch()
106 extra['branch'] = ctx.branch()
107
107
108 # Rebase
108 # Rebase
109 targetancestors = list(repo.changelog.ancestors(target))
109 targetancestors = list(repo.changelog.ancestors(target))
110 targetancestors.append(target)
110 targetancestors.append(target)
111
111
112 for rev in sorted(state):
112 for rev in sorted(state):
113 if state[rev] == -1:
113 if state[rev] == -1:
114 storestatus(repo, originalwd, target, state, collapsef, keepf,
114 storestatus(repo, originalwd, target, state, collapsef, keepf,
115 keepbranchesf, external)
115 keepbranchesf, external)
116 rebasenode(repo, rev, target, state, skipped, targetancestors,
116 rebasenode(repo, rev, target, state, skipped, targetancestors,
117 collapsef, extrafn)
117 collapsef, extrafn)
118 ui.note(_('rebase merging completed\n'))
118 ui.note(_('rebase merging completed\n'))
119
119
120 if collapsef:
120 if collapsef:
121 p1, p2 = defineparents(repo, min(state), target,
121 p1, p2 = defineparents(repo, min(state), target,
122 state, targetancestors)
122 state, targetancestors)
123 concludenode(repo, rev, p1, external, state, collapsef,
123 concludenode(repo, rev, p1, external, state, collapsef,
124 last=True, skipped=skipped, extrafn=extrafn)
124 last=True, skipped=skipped, extrafn=extrafn)
125
125
126 if 'qtip' in repo.tags():
126 if 'qtip' in repo.tags():
127 updatemq(repo, state, skipped, **opts)
127 updatemq(repo, state, skipped, **opts)
128
128
129 if not keepf:
129 if not keepf:
130 # Remove no more useful revisions
130 # Remove no more useful revisions
131 if set(repo.changelog.descendants(min(state))) - set(state):
131 if set(repo.changelog.descendants(min(state))) - set(state):
132 ui.warn(_("warning: new changesets detected on source branch, "
132 ui.warn(_("warning: new changesets detected on source branch, "
133 "not stripping\n"))
133 "not stripping\n"))
134 else:
134 else:
135 repair.strip(ui, repo, repo[min(state)].node(), "strip")
135 repair.strip(ui, repo, repo[min(state)].node(), "strip")
136
136
137 clearstatus(repo)
137 clearstatus(repo)
138 ui.status(_("rebase completed\n"))
138 ui.status(_("rebase completed\n"))
139 if os.path.exists(repo.sjoin('undo')):
139 if os.path.exists(repo.sjoin('undo')):
140 util.unlink(repo.sjoin('undo'))
140 util.unlink(repo.sjoin('undo'))
141 if skipped:
141 if skipped:
142 ui.note(_("%d revisions have been skipped\n") % len(skipped))
142 ui.note(_("%d revisions have been skipped\n") % len(skipped))
143 finally:
143 finally:
144 release(lock, wlock)
144 release(lock, wlock)
145
145
146 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped=None,
146 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped=None,
147 extrafn=None):
147 extrafn=None):
148 """Skip commit if collapsing has been required and rev is not the last
148 """Skip commit if collapsing has been required and rev is not the last
149 revision, commit otherwise
149 revision, commit otherwise
150 """
150 """
151 repo.ui.debug(_(" set parents\n"))
151 repo.ui.debug(_(" set parents\n"))
152 if collapse and not last:
152 if collapse and not last:
153 repo.dirstate.setparents(repo[p1].node())
153 repo.dirstate.setparents(repo[p1].node())
154 return None
154 return None
155
155
156 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
156 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
157
157
158 if skipped is None:
158 if skipped is None:
159 skipped = set()
159 skipped = set()
160
160
161 # Commit, record the old nodeid
161 # Commit, record the old nodeid
162 newrev = nullrev
162 newrev = nullrev
163 try:
163 try:
164 if last:
164 if last:
165 # we don't translate commit messages
165 commitmsg = 'Collapsed revision'
166 commitmsg = 'Collapsed revision'
166 for rebased in state:
167 for rebased in state:
167 if rebased not in skipped:
168 if rebased not in skipped:
168 commitmsg += '\n* %s' % repo[rebased].description()
169 commitmsg += '\n* %s' % repo[rebased].description()
169 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
170 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
170 else:
171 else:
171 commitmsg = repo[rev].description()
172 commitmsg = repo[rev].description()
172 # Commit might fail if unresolved files exist
173 # Commit might fail if unresolved files exist
173 extra = {'rebase_source': repo[rev].hex()}
174 extra = {'rebase_source': repo[rev].hex()}
174 if extrafn:
175 if extrafn:
175 extrafn(repo[rev], extra)
176 extrafn(repo[rev], extra)
176 newrev = repo.commit(text=commitmsg, user=repo[rev].user(),
177 newrev = repo.commit(text=commitmsg, user=repo[rev].user(),
177 date=repo[rev].date(), extra=extra)
178 date=repo[rev].date(), extra=extra)
178 repo.dirstate.setbranch(repo[newrev].branch())
179 repo.dirstate.setbranch(repo[newrev].branch())
179 return newrev
180 return newrev
180 except util.Abort:
181 except util.Abort:
181 # Invalidate the previous setparents
182 # Invalidate the previous setparents
182 repo.dirstate.invalidate()
183 repo.dirstate.invalidate()
183 raise
184 raise
184
185
185 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse,
186 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse,
186 extrafn):
187 extrafn):
187 'Rebase a single revision'
188 'Rebase a single revision'
188 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
189 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
189
190
190 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
191 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
191
192
192 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
193 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
193 repo[p2].rev()))
194 repo[p2].rev()))
194
195
195 # Merge phase
196 # Merge phase
196 if len(repo.parents()) != 2:
197 if len(repo.parents()) != 2:
197 # Update to target and merge it with local
198 # Update to target and merge it with local
198 if repo['.'].rev() != repo[p1].rev():
199 if repo['.'].rev() != repo[p1].rev():
199 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
200 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
200 merge.update(repo, p1, False, True, False)
201 merge.update(repo, p1, False, True, False)
201 else:
202 else:
202 repo.ui.debug(_(" already in target\n"))
203 repo.ui.debug(_(" already in target\n"))
203 repo.dirstate.write()
204 repo.dirstate.write()
204 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
205 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
205 first = repo[rev].rev() == repo[min(state)].rev()
206 first = repo[rev].rev() == repo[min(state)].rev()
206 stats = rebasemerge(repo, rev, first)
207 stats = rebasemerge(repo, rev, first)
207
208
208 if stats[3] > 0:
209 if stats[3] > 0:
209 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
210 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
210 'run hg rebase --continue'))
211 'run hg rebase --continue'))
211 else: # we have an interrupted rebase
212 else: # we have an interrupted rebase
212 repo.ui.debug(_('resuming interrupted rebase\n'))
213 repo.ui.debug(_('resuming interrupted rebase\n'))
213
214
214 # Keep track of renamed files in the revision that is going to be rebased
215 # Keep track of renamed files in the revision that is going to be rebased
215 # Here we simulate the copies and renames in the source changeset
216 # Here we simulate the copies and renames in the source changeset
216 cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True)
217 cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True)
217 m1 = repo[rev].manifest()
218 m1 = repo[rev].manifest()
218 m2 = repo[target].manifest()
219 m2 = repo[target].manifest()
219 for k, v in cop.iteritems():
220 for k, v in cop.iteritems():
220 if k in m1:
221 if k in m1:
221 if v in m1 or v in m2:
222 if v in m1 or v in m2:
222 repo.dirstate.copy(v, k)
223 repo.dirstate.copy(v, k)
223 if v in m2 and v not in m1:
224 if v in m2 and v not in m1:
224 repo.dirstate.remove(v)
225 repo.dirstate.remove(v)
225
226
226 newrev = concludenode(repo, rev, p1, p2, state, collapse,
227 newrev = concludenode(repo, rev, p1, p2, state, collapse,
227 extrafn=extrafn)
228 extrafn=extrafn)
228
229
229 # Update the state
230 # Update the state
230 if newrev is not None:
231 if newrev is not None:
231 state[rev] = repo[newrev].rev()
232 state[rev] = repo[newrev].rev()
232 else:
233 else:
233 if not collapse:
234 if not collapse:
234 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
235 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
235 repo.ui.debug(_('next revision set to %s\n') % p1)
236 repo.ui.debug(_('next revision set to %s\n') % p1)
236 skipped.add(rev)
237 skipped.add(rev)
237 state[rev] = p1
238 state[rev] = p1
238
239
239 def defineparents(repo, rev, target, state, targetancestors):
240 def defineparents(repo, rev, target, state, targetancestors):
240 'Return the new parent relationship of the revision that will be rebased'
241 'Return the new parent relationship of the revision that will be rebased'
241 parents = repo[rev].parents()
242 parents = repo[rev].parents()
242 p1 = p2 = nullrev
243 p1 = p2 = nullrev
243
244
244 P1n = parents[0].rev()
245 P1n = parents[0].rev()
245 if P1n in targetancestors:
246 if P1n in targetancestors:
246 p1 = target
247 p1 = target
247 elif P1n in state:
248 elif P1n in state:
248 p1 = state[P1n]
249 p1 = state[P1n]
249 else: # P1n external
250 else: # P1n external
250 p1 = target
251 p1 = target
251 p2 = P1n
252 p2 = P1n
252
253
253 if len(parents) == 2 and parents[1].rev() not in targetancestors:
254 if len(parents) == 2 and parents[1].rev() not in targetancestors:
254 P2n = parents[1].rev()
255 P2n = parents[1].rev()
255 # interesting second parent
256 # interesting second parent
256 if P2n in state:
257 if P2n in state:
257 if p1 == target: # P1n in targetancestors or external
258 if p1 == target: # P1n in targetancestors or external
258 p1 = state[P2n]
259 p1 = state[P2n]
259 else:
260 else:
260 p2 = state[P2n]
261 p2 = state[P2n]
261 else: # P2n external
262 else: # P2n external
262 if p2 != nullrev: # P1n external too => rev is a merged revision
263 if p2 != nullrev: # P1n external too => rev is a merged revision
263 raise util.Abort(_('cannot use revision %d as base, result '
264 raise util.Abort(_('cannot use revision %d as base, result '
264 'would have 3 parents') % rev)
265 'would have 3 parents') % rev)
265 p2 = P2n
266 p2 = P2n
266 return p1, p2
267 return p1, p2
267
268
268 def isagitpatch(repo, patchname):
269 def isagitpatch(repo, patchname):
269 'Return true if the given patch is in git format'
270 'Return true if the given patch is in git format'
270 mqpatch = os.path.join(repo.mq.path, patchname)
271 mqpatch = os.path.join(repo.mq.path, patchname)
271 for line in patch.linereader(file(mqpatch, 'rb')):
272 for line in patch.linereader(file(mqpatch, 'rb')):
272 if line.startswith('diff --git'):
273 if line.startswith('diff --git'):
273 return True
274 return True
274 return False
275 return False
275
276
276 def updatemq(repo, state, skipped, **opts):
277 def updatemq(repo, state, skipped, **opts):
277 'Update rebased mq patches - finalize and then import them'
278 'Update rebased mq patches - finalize and then import them'
278 mqrebase = {}
279 mqrebase = {}
279 for p in repo.mq.applied:
280 for p in repo.mq.applied:
280 if repo[p.rev].rev() in state:
281 if repo[p.rev].rev() in state:
281 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
282 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
282 (repo[p.rev].rev(), p.name))
283 (repo[p.rev].rev(), p.name))
283 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name))
284 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name))
284
285
285 if mqrebase:
286 if mqrebase:
286 repo.mq.finish(repo, mqrebase.keys())
287 repo.mq.finish(repo, mqrebase.keys())
287
288
288 # We must start import from the newest revision
289 # We must start import from the newest revision
289 for rev in sorted(mqrebase, reverse=True):
290 for rev in sorted(mqrebase, reverse=True):
290 if rev not in skipped:
291 if rev not in skipped:
291 repo.ui.debug(_('import mq patch %d (%s)\n')
292 repo.ui.debug(_('import mq patch %d (%s)\n')
292 % (state[rev], mqrebase[rev][0]))
293 % (state[rev], mqrebase[rev][0]))
293 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
294 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
294 git=mqrebase[rev][1],rev=[str(state[rev])])
295 git=mqrebase[rev][1],rev=[str(state[rev])])
295 repo.mq.save_dirty()
296 repo.mq.save_dirty()
296
297
297 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
298 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
298 external):
299 external):
299 'Store the current status to allow recovery'
300 'Store the current status to allow recovery'
300 f = repo.opener("rebasestate", "w")
301 f = repo.opener("rebasestate", "w")
301 f.write(repo[originalwd].hex() + '\n')
302 f.write(repo[originalwd].hex() + '\n')
302 f.write(repo[target].hex() + '\n')
303 f.write(repo[target].hex() + '\n')
303 f.write(repo[external].hex() + '\n')
304 f.write(repo[external].hex() + '\n')
304 f.write('%d\n' % int(collapse))
305 f.write('%d\n' % int(collapse))
305 f.write('%d\n' % int(keep))
306 f.write('%d\n' % int(keep))
306 f.write('%d\n' % int(keepbranches))
307 f.write('%d\n' % int(keepbranches))
307 for d, v in state.iteritems():
308 for d, v in state.iteritems():
308 oldrev = repo[d].hex()
309 oldrev = repo[d].hex()
309 newrev = repo[v].hex()
310 newrev = repo[v].hex()
310 f.write("%s:%s\n" % (oldrev, newrev))
311 f.write("%s:%s\n" % (oldrev, newrev))
311 f.close()
312 f.close()
312 repo.ui.debug(_('rebase status stored\n'))
313 repo.ui.debug(_('rebase status stored\n'))
313
314
314 def clearstatus(repo):
315 def clearstatus(repo):
315 'Remove the status files'
316 'Remove the status files'
316 if os.path.exists(repo.join("rebasestate")):
317 if os.path.exists(repo.join("rebasestate")):
317 util.unlink(repo.join("rebasestate"))
318 util.unlink(repo.join("rebasestate"))
318
319
319 def restorestatus(repo):
320 def restorestatus(repo):
320 'Restore a previously stored status'
321 'Restore a previously stored status'
321 try:
322 try:
322 target = None
323 target = None
323 collapse = False
324 collapse = False
324 external = nullrev
325 external = nullrev
325 state = {}
326 state = {}
326 f = repo.opener("rebasestate")
327 f = repo.opener("rebasestate")
327 for i, l in enumerate(f.read().splitlines()):
328 for i, l in enumerate(f.read().splitlines()):
328 if i == 0:
329 if i == 0:
329 originalwd = repo[l].rev()
330 originalwd = repo[l].rev()
330 elif i == 1:
331 elif i == 1:
331 target = repo[l].rev()
332 target = repo[l].rev()
332 elif i == 2:
333 elif i == 2:
333 external = repo[l].rev()
334 external = repo[l].rev()
334 elif i == 3:
335 elif i == 3:
335 collapse = bool(int(l))
336 collapse = bool(int(l))
336 elif i == 4:
337 elif i == 4:
337 keep = bool(int(l))
338 keep = bool(int(l))
338 elif i == 5:
339 elif i == 5:
339 keepbranches = bool(int(l))
340 keepbranches = bool(int(l))
340 else:
341 else:
341 oldrev, newrev = l.split(':')
342 oldrev, newrev = l.split(':')
342 state[repo[oldrev].rev()] = repo[newrev].rev()
343 state[repo[oldrev].rev()] = repo[newrev].rev()
343 repo.ui.debug(_('rebase status resumed\n'))
344 repo.ui.debug(_('rebase status resumed\n'))
344 return originalwd, target, state, collapse, keep, keepbranches, external
345 return originalwd, target, state, collapse, keep, keepbranches, external
345 except IOError, err:
346 except IOError, err:
346 if err.errno != errno.ENOENT:
347 if err.errno != errno.ENOENT:
347 raise
348 raise
348 raise util.Abort(_('no rebase in progress'))
349 raise util.Abort(_('no rebase in progress'))
349
350
350 def abort(repo, originalwd, target, state):
351 def abort(repo, originalwd, target, state):
351 'Restore the repository to its original state'
352 'Restore the repository to its original state'
352 if set(repo.changelog.descendants(target)) - set(state.values()):
353 if set(repo.changelog.descendants(target)) - set(state.values()):
353 repo.ui.warn(_("warning: new changesets detected on target branch, "
354 repo.ui.warn(_("warning: new changesets detected on target branch, "
354 "not stripping\n"))
355 "not stripping\n"))
355 else:
356 else:
356 # Strip from the first rebased revision
357 # Strip from the first rebased revision
357 merge.update(repo, repo[originalwd].rev(), False, True, False)
358 merge.update(repo, repo[originalwd].rev(), False, True, False)
358 rebased = filter(lambda x: x > -1, state.values())
359 rebased = filter(lambda x: x > -1, state.values())
359 if rebased:
360 if rebased:
360 strippoint = min(rebased)
361 strippoint = min(rebased)
361 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
362 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
362 clearstatus(repo)
363 clearstatus(repo)
363 repo.ui.status(_('rebase aborted\n'))
364 repo.ui.status(_('rebase aborted\n'))
364
365
365 def buildstate(repo, dest, src, base, collapse):
366 def buildstate(repo, dest, src, base, collapse):
366 'Define which revisions are going to be rebased and where'
367 'Define which revisions are going to be rebased and where'
367 targetancestors = set()
368 targetancestors = set()
368
369
369 if not dest:
370 if not dest:
370 # Destination defaults to the latest revision in the current branch
371 # Destination defaults to the latest revision in the current branch
371 branch = repo[None].branch()
372 branch = repo[None].branch()
372 dest = repo[branch].rev()
373 dest = repo[branch].rev()
373 else:
374 else:
374 if 'qtip' in repo.tags() and (repo[dest].hex() in
375 if 'qtip' in repo.tags() and (repo[dest].hex() in
375 [s.rev for s in repo.mq.applied]):
376 [s.rev for s in repo.mq.applied]):
376 raise util.Abort(_('cannot rebase onto an applied mq patch'))
377 raise util.Abort(_('cannot rebase onto an applied mq patch'))
377 dest = repo[dest].rev()
378 dest = repo[dest].rev()
378
379
379 if src:
380 if src:
380 commonbase = repo[src].ancestor(repo[dest])
381 commonbase = repo[src].ancestor(repo[dest])
381 if commonbase == repo[src]:
382 if commonbase == repo[src]:
382 raise util.Abort(_('cannot rebase an ancestor'))
383 raise util.Abort(_('cannot rebase an ancestor'))
383 if commonbase == repo[dest]:
384 if commonbase == repo[dest]:
384 raise util.Abort(_('cannot rebase a descendant'))
385 raise util.Abort(_('cannot rebase a descendant'))
385 source = repo[src].rev()
386 source = repo[src].rev()
386 else:
387 else:
387 if base:
388 if base:
388 cwd = repo[base].rev()
389 cwd = repo[base].rev()
389 else:
390 else:
390 cwd = repo['.'].rev()
391 cwd = repo['.'].rev()
391
392
392 if cwd == dest:
393 if cwd == dest:
393 repo.ui.debug(_('already working on current\n'))
394 repo.ui.debug(_('already working on current\n'))
394 return None
395 return None
395
396
396 targetancestors = set(repo.changelog.ancestors(dest))
397 targetancestors = set(repo.changelog.ancestors(dest))
397 if cwd in targetancestors:
398 if cwd in targetancestors:
398 repo.ui.debug(_('already working on the current branch\n'))
399 repo.ui.debug(_('already working on the current branch\n'))
399 return None
400 return None
400
401
401 cwdancestors = set(repo.changelog.ancestors(cwd))
402 cwdancestors = set(repo.changelog.ancestors(cwd))
402 cwdancestors.add(cwd)
403 cwdancestors.add(cwd)
403 rebasingbranch = cwdancestors - targetancestors
404 rebasingbranch = cwdancestors - targetancestors
404 source = min(rebasingbranch)
405 source = min(rebasingbranch)
405
406
406 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
407 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
407 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
408 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
408 external = nullrev
409 external = nullrev
409 if collapse:
410 if collapse:
410 if not targetancestors:
411 if not targetancestors:
411 targetancestors = set(repo.changelog.ancestors(dest))
412 targetancestors = set(repo.changelog.ancestors(dest))
412 for rev in state:
413 for rev in state:
413 # Check externals and fail if there are more than one
414 # Check externals and fail if there are more than one
414 for p in repo[rev].parents():
415 for p in repo[rev].parents():
415 if (p.rev() not in state and p.rev() != source
416 if (p.rev() not in state and p.rev() != source
416 and p.rev() not in targetancestors):
417 and p.rev() not in targetancestors):
417 if external != nullrev:
418 if external != nullrev:
418 raise util.Abort(_('unable to collapse, there is more '
419 raise util.Abort(_('unable to collapse, there is more '
419 'than one external parent'))
420 'than one external parent'))
420 external = p.rev()
421 external = p.rev()
421
422
422 state[source] = nullrev
423 state[source] = nullrev
423 return repo['.'].rev(), repo[dest].rev(), state, external
424 return repo['.'].rev(), repo[dest].rev(), state, external
424
425
425 def pullrebase(orig, ui, repo, *args, **opts):
426 def pullrebase(orig, ui, repo, *args, **opts):
426 'Call rebase after pull if the latter has been invoked with --rebase'
427 'Call rebase after pull if the latter has been invoked with --rebase'
427 if opts.get('rebase'):
428 if opts.get('rebase'):
428 if opts.get('update'):
429 if opts.get('update'):
429 del opts['update']
430 del opts['update']
430 ui.debug(_('--update and --rebase are not compatible, ignoring '
431 ui.debug(_('--update and --rebase are not compatible, ignoring '
431 'the update flag\n'))
432 'the update flag\n'))
432
433
433 cmdutil.bail_if_changed(repo)
434 cmdutil.bail_if_changed(repo)
434 revsprepull = len(repo)
435 revsprepull = len(repo)
435 orig(ui, repo, *args, **opts)
436 orig(ui, repo, *args, **opts)
436 revspostpull = len(repo)
437 revspostpull = len(repo)
437 if revspostpull > revsprepull:
438 if revspostpull > revsprepull:
438 rebase(ui, repo, **opts)
439 rebase(ui, repo, **opts)
439 branch = repo[None].branch()
440 branch = repo[None].branch()
440 dest = repo[branch].rev()
441 dest = repo[branch].rev()
441 if dest != repo['.'].rev():
442 if dest != repo['.'].rev():
442 # there was nothing to rebase we force an update
443 # there was nothing to rebase we force an update
443 merge.update(repo, dest, False, False, False)
444 merge.update(repo, dest, False, False, False)
444 else:
445 else:
445 orig(ui, repo, *args, **opts)
446 orig(ui, repo, *args, **opts)
446
447
447 def uisetup(ui):
448 def uisetup(ui):
448 'Replace pull with a decorator to provide --rebase option'
449 'Replace pull with a decorator to provide --rebase option'
449 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
450 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
450 entry[1].append(('', 'rebase', None,
451 entry[1].append(('', 'rebase', None,
451 _("rebase working directory to branch head"))
452 _("rebase working directory to branch head"))
452 )
453 )
453
454
454 cmdtable = {
455 cmdtable = {
455 "rebase":
456 "rebase":
456 (rebase,
457 (rebase,
457 [
458 [
458 ('s', 'source', '', _('rebase from a given revision')),
459 ('s', 'source', '', _('rebase from a given revision')),
459 ('b', 'base', '', _('rebase from the base of a given revision')),
460 ('b', 'base', '', _('rebase from the base of a given revision')),
460 ('d', 'dest', '', _('rebase onto a given revision')),
461 ('d', 'dest', '', _('rebase onto a given revision')),
461 ('', 'collapse', False, _('collapse the rebased revisions')),
462 ('', 'collapse', False, _('collapse the rebased revisions')),
462 ('', 'keep', False, _('keep original revisions')),
463 ('', 'keep', False, _('keep original revisions')),
463 ('', 'keepbranches', False, _('keep original branches')),
464 ('', 'keepbranches', False, _('keep original branches')),
464 ('c', 'continue', False, _('continue an interrupted rebase')),
465 ('c', 'continue', False, _('continue an interrupted rebase')),
465 ('a', 'abort', False, _('abort an interrupted rebase')),] +
466 ('a', 'abort', False, _('abort an interrupted rebase')),] +
466 templateopts,
467 templateopts,
467 _('hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] '
468 _('hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] '
468 '[--keepbranches] | [-c] | [-a]')),
469 '[--keepbranches] | [-c] | [-a]')),
469 }
470 }
@@ -1,602 +1,603 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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 map from
12 Transplanted patches are recorded in .hg/transplant/transplants, as a map from
13 a changeset hash to its hash in the source repository.
13 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
19 from mercurial import patch, revlog, util, error
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(changelog[4])
185 fp.write(changelog[4])
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 message += '\n(transplanted from %s)' % revlog.hex(node)
210 message += '\n(transplanted from %s)' % revlog.hex(node)
210
211
211 self.ui.status(_('applying %s\n') % revlog.short(node))
212 self.ui.status(_('applying %s\n') % revlog.short(node))
212 self.ui.note('%s %s\n%s\n' % (user, date, message))
213 self.ui.note('%s %s\n%s\n' % (user, date, message))
213
214
214 if not patchfile and not merge:
215 if not patchfile and not merge:
215 raise util.Abort(_('can only omit patchfile if merging'))
216 raise util.Abort(_('can only omit patchfile if merging'))
216 if patchfile:
217 if patchfile:
217 try:
218 try:
218 files = {}
219 files = {}
219 try:
220 try:
220 patch.patch(patchfile, self.ui, cwd=repo.root,
221 patch.patch(patchfile, self.ui, cwd=repo.root,
221 files=files, eolmode=None)
222 files=files, eolmode=None)
222 if not files:
223 if not files:
223 self.ui.warn(_('%s: empty changeset')
224 self.ui.warn(_('%s: empty changeset')
224 % revlog.hex(node))
225 % revlog.hex(node))
225 return None
226 return None
226 finally:
227 finally:
227 files = patch.updatedir(self.ui, repo, files)
228 files = patch.updatedir(self.ui, repo, files)
228 except Exception, inst:
229 except Exception, inst:
229 if filter:
230 if filter:
230 os.unlink(patchfile)
231 os.unlink(patchfile)
231 seriespath = os.path.join(self.path, 'series')
232 seriespath = os.path.join(self.path, 'series')
232 if os.path.exists(seriespath):
233 if os.path.exists(seriespath):
233 os.unlink(seriespath)
234 os.unlink(seriespath)
234 p1 = repo.dirstate.parents()[0]
235 p1 = repo.dirstate.parents()[0]
235 p2 = node
236 p2 = node
236 self.log(user, date, message, p1, p2, merge=merge)
237 self.log(user, date, message, p1, p2, merge=merge)
237 self.ui.write(str(inst) + '\n')
238 self.ui.write(str(inst) + '\n')
238 raise util.Abort(_('Fix up the merge and run '
239 raise util.Abort(_('Fix up the merge and run '
239 'hg transplant --continue'))
240 'hg transplant --continue'))
240 else:
241 else:
241 files = None
242 files = None
242 if merge:
243 if merge:
243 p1, p2 = repo.dirstate.parents()
244 p1, p2 = repo.dirstate.parents()
244 repo.dirstate.setparents(p1, node)
245 repo.dirstate.setparents(p1, node)
245 m = match.always(repo.root, '')
246 m = match.always(repo.root, '')
246 else:
247 else:
247 m = match.exact(repo.root, '', files)
248 m = match.exact(repo.root, '', files)
248
249
249 n = repo.commit(message, user, date, extra=extra, match=m)
250 n = repo.commit(message, user, date, extra=extra, match=m)
250 if not merge:
251 if not merge:
251 self.transplants.set(n, node)
252 self.transplants.set(n, node)
252
253
253 return n
254 return n
254
255
255 def resume(self, repo, source, opts=None):
256 def resume(self, repo, source, opts=None):
256 '''recover last transaction and apply remaining changesets'''
257 '''recover last transaction and apply remaining changesets'''
257 if os.path.exists(os.path.join(self.path, 'journal')):
258 if os.path.exists(os.path.join(self.path, 'journal')):
258 n, node = self.recover(repo)
259 n, node = self.recover(repo)
259 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
260 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
260 revlog.short(n)))
261 revlog.short(n)))
261 seriespath = os.path.join(self.path, 'series')
262 seriespath = os.path.join(self.path, 'series')
262 if not os.path.exists(seriespath):
263 if not os.path.exists(seriespath):
263 self.transplants.write()
264 self.transplants.write()
264 return
265 return
265 nodes, merges = self.readseries()
266 nodes, merges = self.readseries()
266 revmap = {}
267 revmap = {}
267 for n in nodes:
268 for n in nodes:
268 revmap[source.changelog.rev(n)] = n
269 revmap[source.changelog.rev(n)] = n
269 os.unlink(seriespath)
270 os.unlink(seriespath)
270
271
271 self.apply(repo, source, revmap, merges, opts)
272 self.apply(repo, source, revmap, merges, opts)
272
273
273 def recover(self, repo):
274 def recover(self, repo):
274 '''commit working directory using journal metadata'''
275 '''commit working directory using journal metadata'''
275 node, user, date, message, parents = self.readlog()
276 node, user, date, message, parents = self.readlog()
276 merge = len(parents) == 2
277 merge = len(parents) == 2
277
278
278 if not user or not date or not message or not parents[0]:
279 if not user or not date or not message or not parents[0]:
279 raise util.Abort(_('transplant log file is corrupt'))
280 raise util.Abort(_('transplant log file is corrupt'))
280
281
281 extra = {'transplant_source': node}
282 extra = {'transplant_source': node}
282 wlock = repo.wlock()
283 wlock = repo.wlock()
283 try:
284 try:
284 p1, p2 = repo.dirstate.parents()
285 p1, p2 = repo.dirstate.parents()
285 if p1 != parents[0]:
286 if p1 != parents[0]:
286 raise util.Abort(
287 raise util.Abort(
287 _('working dir not at transplant parent %s') %
288 _('working dir not at transplant parent %s') %
288 revlog.hex(parents[0]))
289 revlog.hex(parents[0]))
289 if merge:
290 if merge:
290 repo.dirstate.setparents(p1, parents[1])
291 repo.dirstate.setparents(p1, parents[1])
291 n = repo.commit(message, user, date, extra=extra)
292 n = repo.commit(message, user, date, extra=extra)
292 if not n:
293 if not n:
293 raise util.Abort(_('commit failed'))
294 raise util.Abort(_('commit failed'))
294 if not merge:
295 if not merge:
295 self.transplants.set(n, node)
296 self.transplants.set(n, node)
296 self.unlog()
297 self.unlog()
297
298
298 return n, node
299 return n, node
299 finally:
300 finally:
300 wlock.release()
301 wlock.release()
301
302
302 def readseries(self):
303 def readseries(self):
303 nodes = []
304 nodes = []
304 merges = []
305 merges = []
305 cur = nodes
306 cur = nodes
306 for line in self.opener('series').read().splitlines():
307 for line in self.opener('series').read().splitlines():
307 if line.startswith('# Merges'):
308 if line.startswith('# Merges'):
308 cur = merges
309 cur = merges
309 continue
310 continue
310 cur.append(revlog.bin(line))
311 cur.append(revlog.bin(line))
311
312
312 return (nodes, merges)
313 return (nodes, merges)
313
314
314 def saveseries(self, revmap, merges):
315 def saveseries(self, revmap, merges):
315 if not revmap:
316 if not revmap:
316 return
317 return
317
318
318 if not os.path.isdir(self.path):
319 if not os.path.isdir(self.path):
319 os.mkdir(self.path)
320 os.mkdir(self.path)
320 series = self.opener('series', 'w')
321 series = self.opener('series', 'w')
321 for rev in sorted(revmap):
322 for rev in sorted(revmap):
322 series.write(revlog.hex(revmap[rev]) + '\n')
323 series.write(revlog.hex(revmap[rev]) + '\n')
323 if merges:
324 if merges:
324 series.write('# Merges\n')
325 series.write('# Merges\n')
325 for m in merges:
326 for m in merges:
326 series.write(revlog.hex(m) + '\n')
327 series.write(revlog.hex(m) + '\n')
327 series.close()
328 series.close()
328
329
329 def parselog(self, fp):
330 def parselog(self, fp):
330 parents = []
331 parents = []
331 message = []
332 message = []
332 node = revlog.nullid
333 node = revlog.nullid
333 inmsg = False
334 inmsg = False
334 for line in fp.read().splitlines():
335 for line in fp.read().splitlines():
335 if inmsg:
336 if inmsg:
336 message.append(line)
337 message.append(line)
337 elif line.startswith('# User '):
338 elif line.startswith('# User '):
338 user = line[7:]
339 user = line[7:]
339 elif line.startswith('# Date '):
340 elif line.startswith('# Date '):
340 date = line[7:]
341 date = line[7:]
341 elif line.startswith('# Node ID '):
342 elif line.startswith('# Node ID '):
342 node = revlog.bin(line[10:])
343 node = revlog.bin(line[10:])
343 elif line.startswith('# Parent '):
344 elif line.startswith('# Parent '):
344 parents.append(revlog.bin(line[9:]))
345 parents.append(revlog.bin(line[9:]))
345 elif not line.startswith('#'):
346 elif not line.startswith('#'):
346 inmsg = True
347 inmsg = True
347 message.append(line)
348 message.append(line)
348 return (node, user, date, '\n'.join(message), parents)
349 return (node, user, date, '\n'.join(message), parents)
349
350
350 def log(self, user, date, message, p1, p2, merge=False):
351 def log(self, user, date, message, p1, p2, merge=False):
351 '''journal changelog metadata for later recover'''
352 '''journal changelog metadata for later recover'''
352
353
353 if not os.path.isdir(self.path):
354 if not os.path.isdir(self.path):
354 os.mkdir(self.path)
355 os.mkdir(self.path)
355 fp = self.opener('journal', 'w')
356 fp = self.opener('journal', 'w')
356 fp.write('# User %s\n' % user)
357 fp.write('# User %s\n' % user)
357 fp.write('# Date %s\n' % date)
358 fp.write('# Date %s\n' % date)
358 fp.write('# Node ID %s\n' % revlog.hex(p2))
359 fp.write('# Node ID %s\n' % revlog.hex(p2))
359 fp.write('# Parent ' + revlog.hex(p1) + '\n')
360 fp.write('# Parent ' + revlog.hex(p1) + '\n')
360 if merge:
361 if merge:
361 fp.write('# Parent ' + revlog.hex(p2) + '\n')
362 fp.write('# Parent ' + revlog.hex(p2) + '\n')
362 fp.write(message.rstrip() + '\n')
363 fp.write(message.rstrip() + '\n')
363 fp.close()
364 fp.close()
364
365
365 def readlog(self):
366 def readlog(self):
366 return self.parselog(self.opener('journal'))
367 return self.parselog(self.opener('journal'))
367
368
368 def unlog(self):
369 def unlog(self):
369 '''remove changelog journal'''
370 '''remove changelog journal'''
370 absdst = os.path.join(self.path, 'journal')
371 absdst = os.path.join(self.path, 'journal')
371 if os.path.exists(absdst):
372 if os.path.exists(absdst):
372 os.unlink(absdst)
373 os.unlink(absdst)
373
374
374 def transplantfilter(self, repo, source, root):
375 def transplantfilter(self, repo, source, root):
375 def matchfn(node):
376 def matchfn(node):
376 if self.applied(repo, node, root):
377 if self.applied(repo, node, root):
377 return False
378 return False
378 if source.changelog.parents(node)[1] != revlog.nullid:
379 if source.changelog.parents(node)[1] != revlog.nullid:
379 return False
380 return False
380 extra = source.changelog.read(node)[5]
381 extra = source.changelog.read(node)[5]
381 cnode = extra.get('transplant_source')
382 cnode = extra.get('transplant_source')
382 if cnode and self.applied(repo, cnode, root):
383 if cnode and self.applied(repo, cnode, root):
383 return False
384 return False
384 return True
385 return True
385
386
386 return matchfn
387 return matchfn
387
388
388 def hasnode(repo, node):
389 def hasnode(repo, node):
389 try:
390 try:
390 return repo.changelog.rev(node) != None
391 return repo.changelog.rev(node) != None
391 except error.RevlogError:
392 except error.RevlogError:
392 return False
393 return False
393
394
394 def browserevs(ui, repo, nodes, opts):
395 def browserevs(ui, repo, nodes, opts):
395 '''interactively transplant changesets'''
396 '''interactively transplant changesets'''
396 def browsehelp(ui):
397 def browsehelp(ui):
397 ui.write('y: transplant this changeset\n'
398 ui.write('y: transplant this changeset\n'
398 'n: skip this changeset\n'
399 'n: skip this changeset\n'
399 'm: merge at this changeset\n'
400 'm: merge at this changeset\n'
400 'p: show patch\n'
401 'p: show patch\n'
401 'c: commit selected changesets\n'
402 'c: commit selected changesets\n'
402 'q: cancel transplant\n'
403 'q: cancel transplant\n'
403 '?: show this help\n')
404 '?: show this help\n')
404
405
405 displayer = cmdutil.show_changeset(ui, repo, opts)
406 displayer = cmdutil.show_changeset(ui, repo, opts)
406 transplants = []
407 transplants = []
407 merges = []
408 merges = []
408 for node in nodes:
409 for node in nodes:
409 displayer.show(repo[node])
410 displayer.show(repo[node])
410 action = None
411 action = None
411 while not action:
412 while not action:
412 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
413 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
413 if action == '?':
414 if action == '?':
414 browsehelp(ui)
415 browsehelp(ui)
415 action = None
416 action = None
416 elif action == 'p':
417 elif action == 'p':
417 parent = repo.changelog.parents(node)[0]
418 parent = repo.changelog.parents(node)[0]
418 for chunk in patch.diff(repo, parent, node):
419 for chunk in patch.diff(repo, parent, node):
419 ui.write(chunk)
420 ui.write(chunk)
420 action = None
421 action = None
421 elif action not in ('y', 'n', 'm', 'c', 'q'):
422 elif action not in ('y', 'n', 'm', 'c', 'q'):
422 ui.write('no such option\n')
423 ui.write('no such option\n')
423 action = None
424 action = None
424 if action == 'y':
425 if action == 'y':
425 transplants.append(node)
426 transplants.append(node)
426 elif action == 'm':
427 elif action == 'm':
427 merges.append(node)
428 merges.append(node)
428 elif action == 'c':
429 elif action == 'c':
429 break
430 break
430 elif action == 'q':
431 elif action == 'q':
431 transplants = ()
432 transplants = ()
432 merges = ()
433 merges = ()
433 break
434 break
434 return (transplants, merges)
435 return (transplants, merges)
435
436
436 def transplant(ui, repo, *revs, **opts):
437 def transplant(ui, repo, *revs, **opts):
437 '''transplant changesets from another branch
438 '''transplant changesets from another branch
438
439
439 Selected changesets will be applied on top of the current working
440 Selected changesets will be applied on top of the current working
440 directory with the log of the original changeset. If --log is specified,
441 directory with the log of the original changeset. If --log is specified,
441 log messages will have a comment appended of the form:
442 log messages will have a comment appended of the form:
442
443
443 (transplanted from CHANGESETHASH)
444 (transplanted from CHANGESETHASH)
444
445
445 You can rewrite the changelog message with the --filter option. Its
446 You can rewrite the changelog message with the --filter option. Its
446 argument will be invoked with the current changelog message as $1 and the
447 argument will be invoked with the current changelog message as $1 and the
447 patch as $2.
448 patch as $2.
448
449
449 If --source/-s is specified, selects changesets from the named repository.
450 If --source/-s is specified, selects changesets from the named repository.
450 If --branch/-b is specified, selects changesets from the branch holding
451 If --branch/-b is specified, selects changesets from the branch holding
451 the named revision, up to that revision. If --all/-a is specified, all
452 the named revision, up to that revision. If --all/-a is specified, all
452 changesets on the branch will be transplanted, otherwise you will be
453 changesets on the branch will be transplanted, otherwise you will be
453 prompted to select the changesets you want.
454 prompted to select the changesets you want.
454
455
455 hg transplant --branch REVISION --all will rebase the selected branch (up
456 hg transplant --branch REVISION --all will rebase the selected branch (up
456 to the named revision) onto your current working directory.
457 to the named revision) onto your current working directory.
457
458
458 You can optionally mark selected transplanted changesets as merge
459 You can optionally mark selected transplanted changesets as merge
459 changesets. You will not be prompted to transplant any ancestors of a
460 changesets. You will not be prompted to transplant any ancestors of a
460 merged transplant, and you can merge descendants of them normally instead
461 merged transplant, and you can merge descendants of them normally instead
461 of transplanting them.
462 of transplanting them.
462
463
463 If no merges or revisions are provided, hg transplant will start an
464 If no merges or revisions are provided, hg transplant will start an
464 interactive changeset browser.
465 interactive changeset browser.
465
466
466 If a changeset application fails, you can fix the merge by hand and then
467 If a changeset application fails, you can fix the merge by hand and then
467 resume where you left off by calling hg transplant --continue/-c.
468 resume where you left off by calling hg transplant --continue/-c.
468 '''
469 '''
469 def getremotechanges(repo, url):
470 def getremotechanges(repo, url):
470 sourcerepo = ui.expandpath(url)
471 sourcerepo = ui.expandpath(url)
471 source = hg.repository(ui, sourcerepo)
472 source = hg.repository(ui, sourcerepo)
472 common, incoming, rheads = repo.findcommonincoming(source, force=True)
473 common, incoming, rheads = repo.findcommonincoming(source, force=True)
473 if not incoming:
474 if not incoming:
474 return (source, None, None)
475 return (source, None, None)
475
476
476 bundle = None
477 bundle = None
477 if not source.local():
478 if not source.local():
478 if source.capable('changegroupsubset'):
479 if source.capable('changegroupsubset'):
479 cg = source.changegroupsubset(incoming, rheads, 'incoming')
480 cg = source.changegroupsubset(incoming, rheads, 'incoming')
480 else:
481 else:
481 cg = source.changegroup(incoming, 'incoming')
482 cg = source.changegroup(incoming, 'incoming')
482 bundle = changegroup.writebundle(cg, None, 'HG10UN')
483 bundle = changegroup.writebundle(cg, None, 'HG10UN')
483 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
484 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
484
485
485 return (source, incoming, bundle)
486 return (source, incoming, bundle)
486
487
487 def incwalk(repo, incoming, branches, match=util.always):
488 def incwalk(repo, incoming, branches, match=util.always):
488 if not branches:
489 if not branches:
489 branches=None
490 branches=None
490 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
491 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
491 if match(node):
492 if match(node):
492 yield node
493 yield node
493
494
494 def transplantwalk(repo, root, branches, match=util.always):
495 def transplantwalk(repo, root, branches, match=util.always):
495 if not branches:
496 if not branches:
496 branches = repo.heads()
497 branches = repo.heads()
497 ancestors = []
498 ancestors = []
498 for branch in branches:
499 for branch in branches:
499 ancestors.append(repo.changelog.ancestor(root, branch))
500 ancestors.append(repo.changelog.ancestor(root, branch))
500 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
501 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
501 if match(node):
502 if match(node):
502 yield node
503 yield node
503
504
504 def checkopts(opts, revs):
505 def checkopts(opts, revs):
505 if opts.get('continue'):
506 if opts.get('continue'):
506 if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
507 if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
507 raise util.Abort(_('--continue is incompatible with '
508 raise util.Abort(_('--continue is incompatible with '
508 'branch, all or merge'))
509 'branch, all or merge'))
509 return
510 return
510 if not (opts.get('source') or revs or
511 if not (opts.get('source') or revs or
511 opts.get('merge') or opts.get('branch')):
512 opts.get('merge') or opts.get('branch')):
512 raise util.Abort(_('no source URL, branch tag or revision '
513 raise util.Abort(_('no source URL, branch tag or revision '
513 'list provided'))
514 'list provided'))
514 if opts.get('all'):
515 if opts.get('all'):
515 if not opts.get('branch'):
516 if not opts.get('branch'):
516 raise util.Abort(_('--all requires a branch revision'))
517 raise util.Abort(_('--all requires a branch revision'))
517 if revs:
518 if revs:
518 raise util.Abort(_('--all is incompatible with a '
519 raise util.Abort(_('--all is incompatible with a '
519 'revision list'))
520 'revision list'))
520
521
521 checkopts(opts, revs)
522 checkopts(opts, revs)
522
523
523 if not opts.get('log'):
524 if not opts.get('log'):
524 opts['log'] = ui.config('transplant', 'log')
525 opts['log'] = ui.config('transplant', 'log')
525 if not opts.get('filter'):
526 if not opts.get('filter'):
526 opts['filter'] = ui.config('transplant', 'filter')
527 opts['filter'] = ui.config('transplant', 'filter')
527
528
528 tp = transplanter(ui, repo)
529 tp = transplanter(ui, repo)
529
530
530 p1, p2 = repo.dirstate.parents()
531 p1, p2 = repo.dirstate.parents()
531 if len(repo) > 0 and p1 == revlog.nullid:
532 if len(repo) > 0 and p1 == revlog.nullid:
532 raise util.Abort(_('no revision checked out'))
533 raise util.Abort(_('no revision checked out'))
533 if not opts.get('continue'):
534 if not opts.get('continue'):
534 if p2 != revlog.nullid:
535 if p2 != revlog.nullid:
535 raise util.Abort(_('outstanding uncommitted merges'))
536 raise util.Abort(_('outstanding uncommitted merges'))
536 m, a, r, d = repo.status()[:4]
537 m, a, r, d = repo.status()[:4]
537 if m or a or r or d:
538 if m or a or r or d:
538 raise util.Abort(_('outstanding local changes'))
539 raise util.Abort(_('outstanding local changes'))
539
540
540 bundle = None
541 bundle = None
541 source = opts.get('source')
542 source = opts.get('source')
542 if source:
543 if source:
543 (source, incoming, bundle) = getremotechanges(repo, source)
544 (source, incoming, bundle) = getremotechanges(repo, source)
544 else:
545 else:
545 source = repo
546 source = repo
546
547
547 try:
548 try:
548 if opts.get('continue'):
549 if opts.get('continue'):
549 tp.resume(repo, source, opts)
550 tp.resume(repo, source, opts)
550 return
551 return
551
552
552 tf=tp.transplantfilter(repo, source, p1)
553 tf=tp.transplantfilter(repo, source, p1)
553 if opts.get('prune'):
554 if opts.get('prune'):
554 prune = [source.lookup(r)
555 prune = [source.lookup(r)
555 for r in cmdutil.revrange(source, opts.get('prune'))]
556 for r in cmdutil.revrange(source, opts.get('prune'))]
556 matchfn = lambda x: tf(x) and x not in prune
557 matchfn = lambda x: tf(x) and x not in prune
557 else:
558 else:
558 matchfn = tf
559 matchfn = tf
559 branches = map(source.lookup, opts.get('branch', ()))
560 branches = map(source.lookup, opts.get('branch', ()))
560 merges = map(source.lookup, opts.get('merge', ()))
561 merges = map(source.lookup, opts.get('merge', ()))
561 revmap = {}
562 revmap = {}
562 if revs:
563 if revs:
563 for r in cmdutil.revrange(source, revs):
564 for r in cmdutil.revrange(source, revs):
564 revmap[int(r)] = source.lookup(r)
565 revmap[int(r)] = source.lookup(r)
565 elif opts.get('all') or not merges:
566 elif opts.get('all') or not merges:
566 if source != repo:
567 if source != repo:
567 alltransplants = incwalk(source, incoming, branches,
568 alltransplants = incwalk(source, incoming, branches,
568 match=matchfn)
569 match=matchfn)
569 else:
570 else:
570 alltransplants = transplantwalk(source, p1, branches,
571 alltransplants = transplantwalk(source, p1, branches,
571 match=matchfn)
572 match=matchfn)
572 if opts.get('all'):
573 if opts.get('all'):
573 revs = alltransplants
574 revs = alltransplants
574 else:
575 else:
575 revs, newmerges = browserevs(ui, source, alltransplants, opts)
576 revs, newmerges = browserevs(ui, source, alltransplants, opts)
576 merges.extend(newmerges)
577 merges.extend(newmerges)
577 for r in revs:
578 for r in revs:
578 revmap[source.changelog.rev(r)] = r
579 revmap[source.changelog.rev(r)] = r
579 for r in merges:
580 for r in merges:
580 revmap[source.changelog.rev(r)] = r
581 revmap[source.changelog.rev(r)] = r
581
582
582 tp.apply(repo, source, revmap, merges, opts)
583 tp.apply(repo, source, revmap, merges, opts)
583 finally:
584 finally:
584 if bundle:
585 if bundle:
585 source.close()
586 source.close()
586 os.unlink(bundle)
587 os.unlink(bundle)
587
588
588 cmdtable = {
589 cmdtable = {
589 "transplant":
590 "transplant":
590 (transplant,
591 (transplant,
591 [('s', 'source', '', _('pull patches from REPOSITORY')),
592 [('s', 'source', '', _('pull patches from REPOSITORY')),
592 ('b', 'branch', [], _('pull patches from branch BRANCH')),
593 ('b', 'branch', [], _('pull patches from branch BRANCH')),
593 ('a', 'all', None, _('pull all changesets up to BRANCH')),
594 ('a', 'all', None, _('pull all changesets up to BRANCH')),
594 ('p', 'prune', [], _('skip over REV')),
595 ('p', 'prune', [], _('skip over REV')),
595 ('m', 'merge', [], _('merge at REV')),
596 ('m', 'merge', [], _('merge at REV')),
596 ('', 'log', None, _('append transplant info to log message')),
597 ('', 'log', None, _('append transplant info to log message')),
597 ('c', 'continue', None, _('continue last transplant session '
598 ('c', 'continue', None, _('continue last transplant session '
598 'after repair')),
599 'after repair')),
599 ('', 'filter', '', _('filter changesets through FILTER'))],
600 ('', 'filter', '', _('filter changesets through FILTER'))],
600 _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] '
601 _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] '
601 '[-m REV] [REV]...'))
602 '[-m REV] [REV]...'))
602 }
603 }
@@ -1,3515 +1,3518 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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, subprocess, difflib, time, tempfile
11 import os, re, sys, subprocess, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, url, encoding
13 import patch, help, mdiff, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17 import minirst
17 import minirst
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 repository.
24 Schedule files to be version controlled and added to the repository.
25
25
26 The files will be added to the repository at the next commit. To undo an
26 The files will be added to the repository at the next commit. To undo an
27 add before that, see hg forget.
27 add before that, see hg forget.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30 """
30 """
31
31
32 bad = []
32 bad = []
33 exacts = {}
33 exacts = {}
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the repository.
52 Add all new files and remove all missing files from the repository.
53
53
54 New files are ignored if they match any of the patterns in .hgignore. As
54 New files are ignored if they match any of the patterns in .hgignore. As
55 with add, these changes take effect at the next commit.
55 with add, these changes take effect at the next commit.
56
56
57 Use the -s/--similarity option to detect renamed files. With a parameter
57 Use the -s/--similarity option to detect renamed files. With a parameter
58 greater than 0, this compares every removed file with every added file and
58 greater than 0, this compares every removed file with every added file and
59 records those similar enough as renames. This option takes a percentage
59 records those similar enough as renames. This option takes a percentage
60 between 0 (disabled) and 100 (files must be identical) as its parameter.
60 between 0 (disabled) and 100 (files must be identical) as its parameter.
61 Detecting renamed files this way can be expensive.
61 Detecting renamed files this way can be expensive.
62 """
62 """
63 try:
63 try:
64 sim = float(opts.get('similarity') or 0)
64 sim = float(opts.get('similarity') or 0)
65 except ValueError:
65 except ValueError:
66 raise util.Abort(_('similarity must be a number'))
66 raise util.Abort(_('similarity must be a number'))
67 if sim < 0 or sim > 100:
67 if sim < 0 or sim > 100:
68 raise util.Abort(_('similarity must be between 0 and 100'))
68 raise util.Abort(_('similarity must be between 0 and 100'))
69 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
69 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70
70
71 def annotate(ui, repo, *pats, **opts):
71 def annotate(ui, repo, *pats, **opts):
72 """show changeset information by line for each file
72 """show changeset information by line for each file
73
73
74 List changes in files, showing the revision id responsible for each line
74 List changes in files, showing the revision id responsible for each line
75
75
76 This command is useful for discovering when a change was made and by whom.
76 This command is useful for discovering when a change was made and by whom.
77
77
78 Without the -a/--text option, annotate will avoid processing files it
78 Without the -a/--text option, annotate will avoid processing files it
79 detects as binary. With -a, annotate will annotate the file anyway,
79 detects as binary. With -a, annotate will annotate the file anyway,
80 although the results will probably be neither useful nor desirable.
80 although the results will probably be neither useful nor desirable.
81 """
81 """
82 datefunc = ui.quiet and util.shortdate or util.datestr
82 datefunc = ui.quiet and util.shortdate or util.datestr
83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
84
84
85 if not pats:
85 if not pats:
86 raise util.Abort(_('at least one filename or pattern is required'))
86 raise util.Abort(_('at least one filename or pattern is required'))
87
87
88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
89 ('number', lambda x: str(x[0].rev())),
89 ('number', lambda x: str(x[0].rev())),
90 ('changeset', lambda x: short(x[0].node())),
90 ('changeset', lambda x: short(x[0].node())),
91 ('date', getdate),
91 ('date', getdate),
92 ('follow', lambda x: x[0].path()),
92 ('follow', lambda x: x[0].path()),
93 ]
93 ]
94
94
95 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
95 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
96 and not opts.get('follow')):
96 and not opts.get('follow')):
97 opts['number'] = 1
97 opts['number'] = 1
98
98
99 linenumber = opts.get('line_number') is not None
99 linenumber = opts.get('line_number') is not None
100 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
100 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
101 raise util.Abort(_('at least one of -n/-c is required for -l'))
101 raise util.Abort(_('at least one of -n/-c is required for -l'))
102
102
103 funcmap = [func for op, func in opmap if opts.get(op)]
103 funcmap = [func for op, func in opmap if opts.get(op)]
104 if linenumber:
104 if linenumber:
105 lastfunc = funcmap[-1]
105 lastfunc = funcmap[-1]
106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
107
107
108 ctx = repo[opts.get('rev')]
108 ctx = repo[opts.get('rev')]
109
109
110 m = cmdutil.match(repo, pats, opts)
110 m = cmdutil.match(repo, pats, opts)
111 for abs in ctx.walk(m):
111 for abs in ctx.walk(m):
112 fctx = ctx[abs]
112 fctx = ctx[abs]
113 if not opts.get('text') and util.binary(fctx.data()):
113 if not opts.get('text') and util.binary(fctx.data()):
114 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
114 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
115 continue
115 continue
116
116
117 lines = fctx.annotate(follow=opts.get('follow'),
117 lines = fctx.annotate(follow=opts.get('follow'),
118 linenumber=linenumber)
118 linenumber=linenumber)
119 pieces = []
119 pieces = []
120
120
121 for f in funcmap:
121 for f in funcmap:
122 l = [f(n) for n, dummy in lines]
122 l = [f(n) for n, dummy in lines]
123 if l:
123 if l:
124 ml = max(map(len, l))
124 ml = max(map(len, l))
125 pieces.append(["%*s" % (ml, x) for x in l])
125 pieces.append(["%*s" % (ml, x) for x in l])
126
126
127 if pieces:
127 if pieces:
128 for p, l in zip(zip(*pieces), lines):
128 for p, l in zip(zip(*pieces), lines):
129 ui.write("%s: %s" % (" ".join(p), l[1]))
129 ui.write("%s: %s" % (" ".join(p), l[1]))
130
130
131 def archive(ui, repo, dest, **opts):
131 def archive(ui, repo, dest, **opts):
132 '''create an unversioned archive of a repository revision
132 '''create an unversioned archive of a repository revision
133
133
134 By default, the revision used is the parent of the working directory; use
134 By default, the revision used is the parent of the working directory; use
135 -r/--rev to specify a different revision.
135 -r/--rev to specify a different revision.
136
136
137 To specify the type of archive to create, use -t/--type. Valid types are::
137 To specify the type of archive to create, use -t/--type. Valid types are::
138
138
139 "files" (default): a directory full of files
139 "files" (default): a directory full of files
140 "tar": tar archive, uncompressed
140 "tar": tar archive, uncompressed
141 "tbz2": tar archive, compressed using bzip2
141 "tbz2": tar archive, compressed using bzip2
142 "tgz": tar archive, compressed using gzip
142 "tgz": tar archive, compressed using gzip
143 "uzip": zip archive, uncompressed
143 "uzip": zip archive, uncompressed
144 "zip": zip archive, compressed using deflate
144 "zip": zip archive, compressed using deflate
145
145
146 The exact name of the destination archive or directory is given using a
146 The exact name of the destination archive or directory is given using a
147 format string; see 'hg help export' for details.
147 format string; see 'hg help export' for details.
148
148
149 Each member added to an archive file has a directory prefix prepended. Use
149 Each member added to an archive file has a directory prefix prepended. Use
150 -p/--prefix to specify a format string for the prefix. The default is the
150 -p/--prefix to specify a format string for the prefix. The default is the
151 basename of the archive, with suffixes removed.
151 basename of the archive, with suffixes removed.
152 '''
152 '''
153
153
154 ctx = repo[opts.get('rev')]
154 ctx = repo[opts.get('rev')]
155 if not ctx:
155 if not ctx:
156 raise util.Abort(_('no working directory: please specify a revision'))
156 raise util.Abort(_('no working directory: please specify a revision'))
157 node = ctx.node()
157 node = ctx.node()
158 dest = cmdutil.make_filename(repo, dest, node)
158 dest = cmdutil.make_filename(repo, dest, node)
159 if os.path.realpath(dest) == repo.root:
159 if os.path.realpath(dest) == repo.root:
160 raise util.Abort(_('repository root cannot be destination'))
160 raise util.Abort(_('repository root cannot be destination'))
161 matchfn = cmdutil.match(repo, [], opts)
161 matchfn = cmdutil.match(repo, [], opts)
162 kind = opts.get('type') or 'files'
162 kind = opts.get('type') or 'files'
163 prefix = opts.get('prefix')
163 prefix = opts.get('prefix')
164 if dest == '-':
164 if dest == '-':
165 if kind == 'files':
165 if kind == 'files':
166 raise util.Abort(_('cannot archive plain files to stdout'))
166 raise util.Abort(_('cannot archive plain files to stdout'))
167 dest = sys.stdout
167 dest = sys.stdout
168 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
168 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
169 prefix = cmdutil.make_filename(repo, prefix, node)
169 prefix = cmdutil.make_filename(repo, prefix, node)
170 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
170 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
171 matchfn, prefix)
171 matchfn, prefix)
172
172
173 def backout(ui, repo, node=None, rev=None, **opts):
173 def backout(ui, repo, node=None, rev=None, **opts):
174 '''reverse effect of earlier changeset
174 '''reverse effect of earlier changeset
175
175
176 Commit the backed out changes as a new changeset. The new changeset is a
176 Commit the backed out changes as a new changeset. The new changeset is a
177 child of the backed out changeset.
177 child of the backed out changeset.
178
178
179 If you backout a changeset other than the tip, a new head is created. This
179 If you backout a changeset other than the tip, a new head is created. This
180 head will be the new tip and you should merge this backout changeset with
180 head will be the new tip and you should merge this backout changeset with
181 another head.
181 another head.
182
182
183 The --merge option remembers the parent of the working directory before
183 The --merge option remembers the parent of the working directory before
184 starting the backout, then merges the new head with that changeset
184 starting the backout, then merges the new head with that changeset
185 afterwards. This saves you from doing the merge by hand. The result of
185 afterwards. This saves you from doing the merge by hand. The result of
186 this merge is not committed, as with a normal merge.
186 this merge is not committed, as with a normal merge.
187
187
188 See 'hg help dates' for a list of formats valid for -d/--date.
188 See 'hg help dates' for a list of formats valid for -d/--date.
189 '''
189 '''
190 if rev and node:
190 if rev and node:
191 raise util.Abort(_("please specify just one revision"))
191 raise util.Abort(_("please specify just one revision"))
192
192
193 if not rev:
193 if not rev:
194 rev = node
194 rev = node
195
195
196 if not rev:
196 if not rev:
197 raise util.Abort(_("please specify a revision to backout"))
197 raise util.Abort(_("please specify a revision to backout"))
198
198
199 date = opts.get('date')
199 date = opts.get('date')
200 if date:
200 if date:
201 opts['date'] = util.parsedate(date)
201 opts['date'] = util.parsedate(date)
202
202
203 cmdutil.bail_if_changed(repo)
203 cmdutil.bail_if_changed(repo)
204 node = repo.lookup(rev)
204 node = repo.lookup(rev)
205
205
206 op1, op2 = repo.dirstate.parents()
206 op1, op2 = repo.dirstate.parents()
207 a = repo.changelog.ancestor(op1, node)
207 a = repo.changelog.ancestor(op1, node)
208 if a != node:
208 if a != node:
209 raise util.Abort(_('cannot backout change on a different branch'))
209 raise util.Abort(_('cannot backout change on a different branch'))
210
210
211 p1, p2 = repo.changelog.parents(node)
211 p1, p2 = repo.changelog.parents(node)
212 if p1 == nullid:
212 if p1 == nullid:
213 raise util.Abort(_('cannot backout a change with no parents'))
213 raise util.Abort(_('cannot backout a change with no parents'))
214 if p2 != nullid:
214 if p2 != nullid:
215 if not opts.get('parent'):
215 if not opts.get('parent'):
216 raise util.Abort(_('cannot backout a merge changeset without '
216 raise util.Abort(_('cannot backout a merge changeset without '
217 '--parent'))
217 '--parent'))
218 p = repo.lookup(opts['parent'])
218 p = repo.lookup(opts['parent'])
219 if p not in (p1, p2):
219 if p not in (p1, p2):
220 raise util.Abort(_('%s is not a parent of %s') %
220 raise util.Abort(_('%s is not a parent of %s') %
221 (short(p), short(node)))
221 (short(p), short(node)))
222 parent = p
222 parent = p
223 else:
223 else:
224 if opts.get('parent'):
224 if opts.get('parent'):
225 raise util.Abort(_('cannot use --parent on non-merge changeset'))
225 raise util.Abort(_('cannot use --parent on non-merge changeset'))
226 parent = p1
226 parent = p1
227
227
228 # the backout should appear on the same branch
228 # the backout should appear on the same branch
229 branch = repo.dirstate.branch()
229 branch = repo.dirstate.branch()
230 hg.clean(repo, node, show_stats=False)
230 hg.clean(repo, node, show_stats=False)
231 repo.dirstate.setbranch(branch)
231 repo.dirstate.setbranch(branch)
232 revert_opts = opts.copy()
232 revert_opts = opts.copy()
233 revert_opts['date'] = None
233 revert_opts['date'] = None
234 revert_opts['all'] = True
234 revert_opts['all'] = True
235 revert_opts['rev'] = hex(parent)
235 revert_opts['rev'] = hex(parent)
236 revert_opts['no_backup'] = None
236 revert_opts['no_backup'] = None
237 revert(ui, repo, **revert_opts)
237 revert(ui, repo, **revert_opts)
238 commit_opts = opts.copy()
238 commit_opts = opts.copy()
239 commit_opts['addremove'] = False
239 commit_opts['addremove'] = False
240 if not commit_opts['message'] and not commit_opts['logfile']:
240 if not commit_opts['message'] and not commit_opts['logfile']:
241 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
241 # we don't translate commit messages
242 commit_opts['message'] = "Backed out changeset %s" % (short(node))
242 commit_opts['force_editor'] = True
243 commit_opts['force_editor'] = True
243 commit(ui, repo, **commit_opts)
244 commit(ui, repo, **commit_opts)
244 def nice(node):
245 def nice(node):
245 return '%d:%s' % (repo.changelog.rev(node), short(node))
246 return '%d:%s' % (repo.changelog.rev(node), short(node))
246 ui.status(_('changeset %s backs out changeset %s\n') %
247 ui.status(_('changeset %s backs out changeset %s\n') %
247 (nice(repo.changelog.tip()), nice(node)))
248 (nice(repo.changelog.tip()), nice(node)))
248 if op1 != node:
249 if op1 != node:
249 hg.clean(repo, op1, show_stats=False)
250 hg.clean(repo, op1, show_stats=False)
250 if opts.get('merge'):
251 if opts.get('merge'):
251 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
252 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
252 hg.merge(repo, hex(repo.changelog.tip()))
253 hg.merge(repo, hex(repo.changelog.tip()))
253 else:
254 else:
254 ui.status(_('the backout changeset is a new head - '
255 ui.status(_('the backout changeset is a new head - '
255 'do not forget to merge\n'))
256 'do not forget to merge\n'))
256 ui.status(_('(use "backout --merge" '
257 ui.status(_('(use "backout --merge" '
257 'if you want to auto-merge)\n'))
258 'if you want to auto-merge)\n'))
258
259
259 def bisect(ui, repo, rev=None, extra=None, command=None,
260 def bisect(ui, repo, rev=None, extra=None, command=None,
260 reset=None, good=None, bad=None, skip=None, noupdate=None):
261 reset=None, good=None, bad=None, skip=None, noupdate=None):
261 """subdivision search of changesets
262 """subdivision search of changesets
262
263
263 This command helps to find changesets which introduce problems. To use,
264 This command helps to find changesets which introduce problems. To use,
264 mark the earliest changeset you know exhibits the problem as bad, then
265 mark the earliest changeset you know exhibits the problem as bad, then
265 mark the latest changeset which is free from the problem as good. Bisect
266 mark the latest changeset which is free from the problem as good. Bisect
266 will update your working directory to a revision for testing (unless the
267 will update your working directory to a revision for testing (unless the
267 -U/--noupdate option is specified). Once you have performed tests, mark
268 -U/--noupdate option is specified). Once you have performed tests, mark
268 the working directory as good or bad, and bisect will either update to
269 the working directory as good or bad, and bisect will either update to
269 another candidate changeset or announce that it has found the bad
270 another candidate changeset or announce that it has found the bad
270 revision.
271 revision.
271
272
272 As a shortcut, you can also use the revision argument to mark a revision
273 As a shortcut, you can also use the revision argument to mark a revision
273 as good or bad without checking it out first.
274 as good or bad without checking it out first.
274
275
275 If you supply a command, it will be used for automatic bisection. Its exit
276 If you supply a command, it will be used for automatic bisection. Its exit
276 status will be used to mark revisions as good or bad: status 0 means good,
277 status will be used to mark revisions as good or bad: status 0 means good,
277 125 means to skip the revision, 127 (command not found) will abort the
278 125 means to skip the revision, 127 (command not found) will abort the
278 bisection, and any other non-zero exit status means the revision is bad.
279 bisection, and any other non-zero exit status means the revision is bad.
279 """
280 """
280 def print_result(nodes, good):
281 def print_result(nodes, good):
281 displayer = cmdutil.show_changeset(ui, repo, {})
282 displayer = cmdutil.show_changeset(ui, repo, {})
282 if len(nodes) == 1:
283 if len(nodes) == 1:
283 # narrowed it down to a single revision
284 # narrowed it down to a single revision
284 if good:
285 if good:
285 ui.write(_("The first good revision is:\n"))
286 ui.write(_("The first good revision is:\n"))
286 else:
287 else:
287 ui.write(_("The first bad revision is:\n"))
288 ui.write(_("The first bad revision is:\n"))
288 displayer.show(repo[nodes[0]])
289 displayer.show(repo[nodes[0]])
289 else:
290 else:
290 # multiple possible revisions
291 # multiple possible revisions
291 if good:
292 if good:
292 ui.write(_("Due to skipped revisions, the first "
293 ui.write(_("Due to skipped revisions, the first "
293 "good revision could be any of:\n"))
294 "good revision could be any of:\n"))
294 else:
295 else:
295 ui.write(_("Due to skipped revisions, the first "
296 ui.write(_("Due to skipped revisions, the first "
296 "bad revision could be any of:\n"))
297 "bad revision could be any of:\n"))
297 for n in nodes:
298 for n in nodes:
298 displayer.show(repo[n])
299 displayer.show(repo[n])
299
300
300 def check_state(state, interactive=True):
301 def check_state(state, interactive=True):
301 if not state['good'] or not state['bad']:
302 if not state['good'] or not state['bad']:
302 if (good or bad or skip or reset) and interactive:
303 if (good or bad or skip or reset) and interactive:
303 return
304 return
304 if not state['good']:
305 if not state['good']:
305 raise util.Abort(_('cannot bisect (no known good revisions)'))
306 raise util.Abort(_('cannot bisect (no known good revisions)'))
306 else:
307 else:
307 raise util.Abort(_('cannot bisect (no known bad revisions)'))
308 raise util.Abort(_('cannot bisect (no known bad revisions)'))
308 return True
309 return True
309
310
310 # backward compatibility
311 # backward compatibility
311 if rev in "good bad reset init".split():
312 if rev in "good bad reset init".split():
312 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
313 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
313 cmd, rev, extra = rev, extra, None
314 cmd, rev, extra = rev, extra, None
314 if cmd == "good":
315 if cmd == "good":
315 good = True
316 good = True
316 elif cmd == "bad":
317 elif cmd == "bad":
317 bad = True
318 bad = True
318 else:
319 else:
319 reset = True
320 reset = True
320 elif extra or good + bad + skip + reset + bool(command) > 1:
321 elif extra or good + bad + skip + reset + bool(command) > 1:
321 raise util.Abort(_('incompatible arguments'))
322 raise util.Abort(_('incompatible arguments'))
322
323
323 if reset:
324 if reset:
324 p = repo.join("bisect.state")
325 p = repo.join("bisect.state")
325 if os.path.exists(p):
326 if os.path.exists(p):
326 os.unlink(p)
327 os.unlink(p)
327 return
328 return
328
329
329 state = hbisect.load_state(repo)
330 state = hbisect.load_state(repo)
330
331
331 if command:
332 if command:
332 commandpath = util.find_exe(command)
333 commandpath = util.find_exe(command)
333 if commandpath is None:
334 if commandpath is None:
334 raise util.Abort(_("cannot find executable: %s") % command)
335 raise util.Abort(_("cannot find executable: %s") % command)
335 changesets = 1
336 changesets = 1
336 try:
337 try:
337 while changesets:
338 while changesets:
338 # update state
339 # update state
339 status = subprocess.call([commandpath])
340 status = subprocess.call([commandpath])
340 if status == 125:
341 if status == 125:
341 transition = "skip"
342 transition = "skip"
342 elif status == 0:
343 elif status == 0:
343 transition = "good"
344 transition = "good"
344 # status < 0 means process was killed
345 # status < 0 means process was killed
345 elif status == 127:
346 elif status == 127:
346 raise util.Abort(_("failed to execute %s") % command)
347 raise util.Abort(_("failed to execute %s") % command)
347 elif status < 0:
348 elif status < 0:
348 raise util.Abort(_("%s killed") % command)
349 raise util.Abort(_("%s killed") % command)
349 else:
350 else:
350 transition = "bad"
351 transition = "bad"
351 ctx = repo[rev or '.']
352 ctx = repo[rev or '.']
352 state[transition].append(ctx.node())
353 state[transition].append(ctx.node())
353 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
354 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
354 check_state(state, interactive=False)
355 check_state(state, interactive=False)
355 # bisect
356 # bisect
356 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
357 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
357 # update to next check
358 # update to next check
358 cmdutil.bail_if_changed(repo)
359 cmdutil.bail_if_changed(repo)
359 hg.clean(repo, nodes[0], show_stats=False)
360 hg.clean(repo, nodes[0], show_stats=False)
360 finally:
361 finally:
361 hbisect.save_state(repo, state)
362 hbisect.save_state(repo, state)
362 return print_result(nodes, not status)
363 return print_result(nodes, not status)
363
364
364 # update state
365 # update state
365 node = repo.lookup(rev or '.')
366 node = repo.lookup(rev or '.')
366 if good:
367 if good:
367 state['good'].append(node)
368 state['good'].append(node)
368 elif bad:
369 elif bad:
369 state['bad'].append(node)
370 state['bad'].append(node)
370 elif skip:
371 elif skip:
371 state['skip'].append(node)
372 state['skip'].append(node)
372
373
373 hbisect.save_state(repo, state)
374 hbisect.save_state(repo, state)
374
375
375 if not check_state(state):
376 if not check_state(state):
376 return
377 return
377
378
378 # actually bisect
379 # actually bisect
379 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
380 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
380 if changesets == 0:
381 if changesets == 0:
381 print_result(nodes, good)
382 print_result(nodes, good)
382 else:
383 else:
383 assert len(nodes) == 1 # only a single node can be tested next
384 assert len(nodes) == 1 # only a single node can be tested next
384 node = nodes[0]
385 node = nodes[0]
385 # compute the approximate number of remaining tests
386 # compute the approximate number of remaining tests
386 tests, size = 0, 2
387 tests, size = 0, 2
387 while size <= changesets:
388 while size <= changesets:
388 tests, size = tests + 1, size * 2
389 tests, size = tests + 1, size * 2
389 rev = repo.changelog.rev(node)
390 rev = repo.changelog.rev(node)
390 ui.write(_("Testing changeset %d:%s "
391 ui.write(_("Testing changeset %d:%s "
391 "(%d changesets remaining, ~%d tests)\n")
392 "(%d changesets remaining, ~%d tests)\n")
392 % (rev, short(node), changesets, tests))
393 % (rev, short(node), changesets, tests))
393 if not noupdate:
394 if not noupdate:
394 cmdutil.bail_if_changed(repo)
395 cmdutil.bail_if_changed(repo)
395 return hg.clean(repo, node)
396 return hg.clean(repo, node)
396
397
397 def branch(ui, repo, label=None, **opts):
398 def branch(ui, repo, label=None, **opts):
398 """set or show the current branch name
399 """set or show the current branch name
399
400
400 With no argument, show the current branch name. With one argument, set the
401 With no argument, show the current branch name. With one argument, set the
401 working directory branch name (the branch will not exist in the repository
402 working directory branch name (the branch will not exist in the repository
402 until the next commit). Standard practice recommends that primary
403 until the next commit). Standard practice recommends that primary
403 development take place on the 'default' branch.
404 development take place on the 'default' branch.
404
405
405 Unless -f/--force is specified, branch will not let you set a branch name
406 Unless -f/--force is specified, branch will not let you set a branch name
406 that already exists, even if it's inactive.
407 that already exists, even if it's inactive.
407
408
408 Use -C/--clean to reset the working directory branch to that of the parent
409 Use -C/--clean to reset the working directory branch to that of the parent
409 of the working directory, negating a previous branch change.
410 of the working directory, negating a previous branch change.
410
411
411 Use the command 'hg update' to switch to an existing branch. Use 'hg
412 Use the command 'hg update' to switch to an existing branch. Use 'hg
412 commit --close-branch' to mark this branch as closed.
413 commit --close-branch' to mark this branch as closed.
413 """
414 """
414
415
415 if opts.get('clean'):
416 if opts.get('clean'):
416 label = repo[None].parents()[0].branch()
417 label = repo[None].parents()[0].branch()
417 repo.dirstate.setbranch(label)
418 repo.dirstate.setbranch(label)
418 ui.status(_('reset working directory to branch %s\n') % label)
419 ui.status(_('reset working directory to branch %s\n') % label)
419 elif label:
420 elif label:
420 if not opts.get('force') and label in repo.branchtags():
421 if not opts.get('force') and label in repo.branchtags():
421 if label not in [p.branch() for p in repo.parents()]:
422 if label not in [p.branch() for p in repo.parents()]:
422 raise util.Abort(_('a branch of the same name already exists'
423 raise util.Abort(_('a branch of the same name already exists'
423 ' (use --force to override)'))
424 ' (use --force to override)'))
424 repo.dirstate.setbranch(encoding.fromlocal(label))
425 repo.dirstate.setbranch(encoding.fromlocal(label))
425 ui.status(_('marked working directory as branch %s\n') % label)
426 ui.status(_('marked working directory as branch %s\n') % label)
426 else:
427 else:
427 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
428 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
428
429
429 def branches(ui, repo, active=False, closed=False):
430 def branches(ui, repo, active=False, closed=False):
430 """list repository named branches
431 """list repository named branches
431
432
432 List the repository's named branches, indicating which ones are inactive.
433 List the repository's named branches, indicating which ones are inactive.
433 If -c/--closed is specified, also list branches which have been marked
434 If -c/--closed is specified, also list branches which have been marked
434 closed (see hg commit --close-branch).
435 closed (see hg commit --close-branch).
435
436
436 If -a/--active is specified, only show active branches. A branch is
437 If -a/--active is specified, only show active branches. A branch is
437 considered active if it contains repository heads.
438 considered active if it contains repository heads.
438
439
439 Use the command 'hg update' to switch to an existing branch.
440 Use the command 'hg update' to switch to an existing branch.
440 """
441 """
441
442
442 hexfunc = ui.debugflag and hex or short
443 hexfunc = ui.debugflag and hex or short
443 activebranches = [encoding.tolocal(repo[n].branch())
444 activebranches = [encoding.tolocal(repo[n].branch())
444 for n in repo.heads()]
445 for n in repo.heads()]
445 def testactive(tag, node):
446 def testactive(tag, node):
446 realhead = tag in activebranches
447 realhead = tag in activebranches
447 open = node in repo.branchheads(tag, closed=False)
448 open = node in repo.branchheads(tag, closed=False)
448 return realhead and open
449 return realhead and open
449 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
450 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
450 for tag, node in repo.branchtags().items()],
451 for tag, node in repo.branchtags().items()],
451 reverse=True)
452 reverse=True)
452
453
453 for isactive, node, tag in branches:
454 for isactive, node, tag in branches:
454 if (not active) or isactive:
455 if (not active) or isactive:
455 if ui.quiet:
456 if ui.quiet:
456 ui.write("%s\n" % tag)
457 ui.write("%s\n" % tag)
457 else:
458 else:
458 hn = repo.lookup(node)
459 hn = repo.lookup(node)
459 if isactive:
460 if isactive:
460 notice = ''
461 notice = ''
461 elif hn not in repo.branchheads(tag, closed=False):
462 elif hn not in repo.branchheads(tag, closed=False):
462 if not closed:
463 if not closed:
463 continue
464 continue
464 notice = ' (closed)'
465 notice = ' (closed)'
465 else:
466 else:
466 notice = ' (inactive)'
467 notice = ' (inactive)'
467 rev = str(node).rjust(31 - encoding.colwidth(tag))
468 rev = str(node).rjust(31 - encoding.colwidth(tag))
468 data = tag, rev, hexfunc(hn), notice
469 data = tag, rev, hexfunc(hn), notice
469 ui.write("%s %s:%s%s\n" % data)
470 ui.write("%s %s:%s%s\n" % data)
470
471
471 def bundle(ui, repo, fname, dest=None, **opts):
472 def bundle(ui, repo, fname, dest=None, **opts):
472 """create a changegroup file
473 """create a changegroup file
473
474
474 Generate a compressed changegroup file collecting changesets not known to
475 Generate a compressed changegroup file collecting changesets not known to
475 be in another repository.
476 be in another repository.
476
477
477 If no destination repository is specified the destination is assumed to
478 If no destination repository is specified the destination is assumed to
478 have all the nodes specified by one or more --base parameters. To create a
479 have all the nodes specified by one or more --base parameters. To create a
479 bundle containing all changesets, use -a/--all (or --base null).
480 bundle containing all changesets, use -a/--all (or --base null).
480
481
481 You can change compression method with the -t/--type option. The available
482 You can change compression method with the -t/--type option. The available
482 compression methods are: none, bzip2, and gzip (by default, bundles are
483 compression methods are: none, bzip2, and gzip (by default, bundles are
483 compressed using bzip2).
484 compressed using bzip2).
484
485
485 The bundle file can then be transferred using conventional means and
486 The bundle file can then be transferred using conventional means and
486 applied to another repository with the unbundle or pull command. This is
487 applied to another repository with the unbundle or pull command. This is
487 useful when direct push and pull are not available or when exporting an
488 useful when direct push and pull are not available or when exporting an
488 entire repository is undesirable.
489 entire repository is undesirable.
489
490
490 Applying bundles preserves all changeset contents including permissions,
491 Applying bundles preserves all changeset contents including permissions,
491 copy/rename information, and revision history.
492 copy/rename information, and revision history.
492 """
493 """
493 revs = opts.get('rev') or None
494 revs = opts.get('rev') or None
494 if revs:
495 if revs:
495 revs = [repo.lookup(rev) for rev in revs]
496 revs = [repo.lookup(rev) for rev in revs]
496 if opts.get('all'):
497 if opts.get('all'):
497 base = ['null']
498 base = ['null']
498 else:
499 else:
499 base = opts.get('base')
500 base = opts.get('base')
500 if base:
501 if base:
501 if dest:
502 if dest:
502 raise util.Abort(_("--base is incompatible with specifying "
503 raise util.Abort(_("--base is incompatible with specifying "
503 "a destination"))
504 "a destination"))
504 base = [repo.lookup(rev) for rev in base]
505 base = [repo.lookup(rev) for rev in base]
505 # create the right base
506 # create the right base
506 # XXX: nodesbetween / changegroup* should be "fixed" instead
507 # XXX: nodesbetween / changegroup* should be "fixed" instead
507 o = []
508 o = []
508 has = set((nullid,))
509 has = set((nullid,))
509 for n in base:
510 for n in base:
510 has.update(repo.changelog.reachable(n))
511 has.update(repo.changelog.reachable(n))
511 if revs:
512 if revs:
512 visit = list(revs)
513 visit = list(revs)
513 else:
514 else:
514 visit = repo.changelog.heads()
515 visit = repo.changelog.heads()
515 seen = {}
516 seen = {}
516 while visit:
517 while visit:
517 n = visit.pop(0)
518 n = visit.pop(0)
518 parents = [p for p in repo.changelog.parents(n) if p not in has]
519 parents = [p for p in repo.changelog.parents(n) if p not in has]
519 if len(parents) == 0:
520 if len(parents) == 0:
520 o.insert(0, n)
521 o.insert(0, n)
521 else:
522 else:
522 for p in parents:
523 for p in parents:
523 if p not in seen:
524 if p not in seen:
524 seen[p] = 1
525 seen[p] = 1
525 visit.append(p)
526 visit.append(p)
526 else:
527 else:
527 dest, revs, checkout = hg.parseurl(
528 dest, revs, checkout = hg.parseurl(
528 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
529 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
529 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
530 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
530 o = repo.findoutgoing(other, force=opts.get('force'))
531 o = repo.findoutgoing(other, force=opts.get('force'))
531
532
532 if revs:
533 if revs:
533 cg = repo.changegroupsubset(o, revs, 'bundle')
534 cg = repo.changegroupsubset(o, revs, 'bundle')
534 else:
535 else:
535 cg = repo.changegroup(o, 'bundle')
536 cg = repo.changegroup(o, 'bundle')
536
537
537 bundletype = opts.get('type', 'bzip2').lower()
538 bundletype = opts.get('type', 'bzip2').lower()
538 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
539 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
539 bundletype = btypes.get(bundletype)
540 bundletype = btypes.get(bundletype)
540 if bundletype not in changegroup.bundletypes:
541 if bundletype not in changegroup.bundletypes:
541 raise util.Abort(_('unknown bundle type specified with --type'))
542 raise util.Abort(_('unknown bundle type specified with --type'))
542
543
543 changegroup.writebundle(cg, fname, bundletype)
544 changegroup.writebundle(cg, fname, bundletype)
544
545
545 def cat(ui, repo, file1, *pats, **opts):
546 def cat(ui, repo, file1, *pats, **opts):
546 """output the current or given revision of files
547 """output the current or given revision of files
547
548
548 Print the specified files as they were at the given revision. If no
549 Print the specified files as they were at the given revision. If no
549 revision is given, the parent of the working directory is used, or tip if
550 revision is given, the parent of the working directory is used, or tip if
550 no revision is checked out.
551 no revision is checked out.
551
552
552 Output may be to a file, in which case the name of the file is given using
553 Output may be to a file, in which case the name of the file is given using
553 a format string. The formatting rules are the same as for the export
554 a format string. The formatting rules are the same as for the export
554 command, with the following additions::
555 command, with the following additions::
555
556
556 %s basename of file being printed
557 %s basename of file being printed
557 %d dirname of file being printed, or '.' if in repository root
558 %d dirname of file being printed, or '.' if in repository root
558 %p root-relative path name of file being printed
559 %p root-relative path name of file being printed
559 """
560 """
560 ctx = repo[opts.get('rev')]
561 ctx = repo[opts.get('rev')]
561 err = 1
562 err = 1
562 m = cmdutil.match(repo, (file1,) + pats, opts)
563 m = cmdutil.match(repo, (file1,) + pats, opts)
563 for abs in ctx.walk(m):
564 for abs in ctx.walk(m):
564 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
565 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
565 data = ctx[abs].data()
566 data = ctx[abs].data()
566 if opts.get('decode'):
567 if opts.get('decode'):
567 data = repo.wwritedata(abs, data)
568 data = repo.wwritedata(abs, data)
568 fp.write(data)
569 fp.write(data)
569 err = 0
570 err = 0
570 return err
571 return err
571
572
572 def clone(ui, source, dest=None, **opts):
573 def clone(ui, source, dest=None, **opts):
573 """make a copy of an existing repository
574 """make a copy of an existing repository
574
575
575 Create a copy of an existing repository in a new directory.
576 Create a copy of an existing repository in a new directory.
576
577
577 If no destination directory name is specified, it defaults to the basename
578 If no destination directory name is specified, it defaults to the basename
578 of the source.
579 of the source.
579
580
580 The location of the source is added to the new repository's .hg/hgrc file,
581 The location of the source is added to the new repository's .hg/hgrc file,
581 as the default to be used for future pulls.
582 as the default to be used for future pulls.
582
583
583 If you use the -r/--rev option to clone up to a specific revision, no
584 If you use the -r/--rev option to clone up to a specific revision, no
584 subsequent revisions (including subsequent tags) will be present in the
585 subsequent revisions (including subsequent tags) will be present in the
585 cloned repository. This option implies --pull, even on local repositories.
586 cloned repository. This option implies --pull, even on local repositories.
586
587
587 By default, clone will check out the head of the 'default' branch. If the
588 By default, clone will check out the head of the 'default' branch. If the
588 -U/--noupdate option is used, the new clone will contain only a repository
589 -U/--noupdate option is used, the new clone will contain only a repository
589 (.hg) and no working copy (the working copy parent is the null revision).
590 (.hg) and no working copy (the working copy parent is the null revision).
590
591
591 See 'hg help urls' for valid source format details.
592 See 'hg help urls' for valid source format details.
592
593
593 It is possible to specify an ssh:// URL as the destination, but no
594 It is possible to specify an ssh:// URL as the destination, but no
594 .hg/hgrc and working directory will be created on the remote side. Please
595 .hg/hgrc and working directory will be created on the remote side. Please
595 see 'hg help urls' for important details about ssh:// URLs.
596 see 'hg help urls' for important details about ssh:// URLs.
596
597
597 For efficiency, hardlinks are used for cloning whenever the source and
598 For efficiency, hardlinks are used for cloning whenever the source and
598 destination are on the same filesystem (note this applies only to the
599 destination are on the same filesystem (note this applies only to the
599 repository data, not to the checked out files). Some filesystems, such as
600 repository data, not to the checked out files). Some filesystems, such as
600 AFS, implement hardlinking incorrectly, but do not report errors. In these
601 AFS, implement hardlinking incorrectly, but do not report errors. In these
601 cases, use the --pull option to avoid hardlinking.
602 cases, use the --pull option to avoid hardlinking.
602
603
603 In some cases, you can clone repositories and checked out files using full
604 In some cases, you can clone repositories and checked out files using full
604 hardlinks with ::
605 hardlinks with ::
605
606
606 $ cp -al REPO REPOCLONE
607 $ cp -al REPO REPOCLONE
607
608
608 This is the fastest way to clone, but it is not always safe. The operation
609 This is the fastest way to clone, but it is not always safe. The operation
609 is not atomic (making sure REPO is not modified during the operation is up
610 is not atomic (making sure REPO is not modified during the operation is up
610 to you) and you have to make sure your editor breaks hardlinks (Emacs and
611 to you) and you have to make sure your editor breaks hardlinks (Emacs and
611 most Linux Kernel tools do so). Also, this is not compatible with certain
612 most Linux Kernel tools do so). Also, this is not compatible with certain
612 extensions that place their metadata under the .hg directory, such as mq.
613 extensions that place their metadata under the .hg directory, such as mq.
613 """
614 """
614 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
615 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
615 pull=opts.get('pull'),
616 pull=opts.get('pull'),
616 stream=opts.get('uncompressed'),
617 stream=opts.get('uncompressed'),
617 rev=opts.get('rev'),
618 rev=opts.get('rev'),
618 update=not opts.get('noupdate'))
619 update=not opts.get('noupdate'))
619
620
620 def commit(ui, repo, *pats, **opts):
621 def commit(ui, repo, *pats, **opts):
621 """commit the specified files or all outstanding changes
622 """commit the specified files or all outstanding changes
622
623
623 Commit changes to the given files into the repository. Unlike a
624 Commit changes to the given files into the repository. Unlike a
624 centralized RCS, this operation is a local operation. See hg push for a
625 centralized RCS, this operation is a local operation. See hg push for a
625 way to actively distribute your changes.
626 way to actively distribute your changes.
626
627
627 If a list of files is omitted, all changes reported by "hg status" will be
628 If a list of files is omitted, all changes reported by "hg status" will be
628 committed.
629 committed.
629
630
630 If you are committing the result of a merge, do not provide any filenames
631 If you are committing the result of a merge, do not provide any filenames
631 or -I/-X filters.
632 or -I/-X filters.
632
633
633 If no commit message is specified, the configured editor is started to
634 If no commit message is specified, the configured editor is started to
634 prompt you for a message.
635 prompt you for a message.
635
636
636 See 'hg help dates' for a list of formats valid for -d/--date.
637 See 'hg help dates' for a list of formats valid for -d/--date.
637 """
638 """
638 extra = {}
639 extra = {}
639 if opts.get('close_branch'):
640 if opts.get('close_branch'):
640 extra['close'] = 1
641 extra['close'] = 1
641 e = cmdutil.commiteditor
642 e = cmdutil.commiteditor
642 if opts.get('force_editor'):
643 if opts.get('force_editor'):
643 e = cmdutil.commitforceeditor
644 e = cmdutil.commitforceeditor
644
645
645 def commitfunc(ui, repo, message, match, opts):
646 def commitfunc(ui, repo, message, match, opts):
646 return repo.commit(message, opts.get('user'), opts.get('date'), match,
647 return repo.commit(message, opts.get('user'), opts.get('date'), match,
647 editor=e, extra=extra)
648 editor=e, extra=extra)
648
649
649 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
650 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
650 if not node:
651 if not node:
651 ui.status(_("nothing changed\n"))
652 ui.status(_("nothing changed\n"))
652 return
653 return
653 cl = repo.changelog
654 cl = repo.changelog
654 rev = cl.rev(node)
655 rev = cl.rev(node)
655 parents = cl.parentrevs(rev)
656 parents = cl.parentrevs(rev)
656 if rev - 1 in parents:
657 if rev - 1 in parents:
657 # one of the parents was the old tip
658 # one of the parents was the old tip
658 pass
659 pass
659 elif (parents == (nullrev, nullrev) or
660 elif (parents == (nullrev, nullrev) or
660 len(cl.heads(cl.node(parents[0]))) > 1 and
661 len(cl.heads(cl.node(parents[0]))) > 1 and
661 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
662 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
662 ui.status(_('created new head\n'))
663 ui.status(_('created new head\n'))
663
664
664 if ui.debugflag:
665 if ui.debugflag:
665 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
666 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
666 elif ui.verbose:
667 elif ui.verbose:
667 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
668 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
668
669
669 def copy(ui, repo, *pats, **opts):
670 def copy(ui, repo, *pats, **opts):
670 """mark files as copied for the next commit
671 """mark files as copied for the next commit
671
672
672 Mark dest as having copies of source files. If dest is a directory, copies
673 Mark dest as having copies of source files. If dest is a directory, copies
673 are put in that directory. If dest is a file, the source must be a single
674 are put in that directory. If dest is a file, the source must be a single
674 file.
675 file.
675
676
676 By default, this command copies the contents of files as they exist in the
677 By default, this command copies the contents of files as they exist in the
677 working directory. If invoked with -A/--after, the operation is recorded,
678 working directory. If invoked with -A/--after, the operation is recorded,
678 but no copying is performed.
679 but no copying is performed.
679
680
680 This command takes effect with the next commit. To undo a copy before
681 This command takes effect with the next commit. To undo a copy before
681 that, see hg revert.
682 that, see hg revert.
682 """
683 """
683 wlock = repo.wlock(False)
684 wlock = repo.wlock(False)
684 try:
685 try:
685 return cmdutil.copy(ui, repo, pats, opts)
686 return cmdutil.copy(ui, repo, pats, opts)
686 finally:
687 finally:
687 wlock.release()
688 wlock.release()
688
689
689 def debugancestor(ui, repo, *args):
690 def debugancestor(ui, repo, *args):
690 """find the ancestor revision of two revisions in a given index"""
691 """find the ancestor revision of two revisions in a given index"""
691 if len(args) == 3:
692 if len(args) == 3:
692 index, rev1, rev2 = args
693 index, rev1, rev2 = args
693 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
694 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
694 lookup = r.lookup
695 lookup = r.lookup
695 elif len(args) == 2:
696 elif len(args) == 2:
696 if not repo:
697 if not repo:
697 raise util.Abort(_("There is no Mercurial repository here "
698 raise util.Abort(_("There is no Mercurial repository here "
698 "(.hg not found)"))
699 "(.hg not found)"))
699 rev1, rev2 = args
700 rev1, rev2 = args
700 r = repo.changelog
701 r = repo.changelog
701 lookup = repo.lookup
702 lookup = repo.lookup
702 else:
703 else:
703 raise util.Abort(_('either two or three arguments required'))
704 raise util.Abort(_('either two or three arguments required'))
704 a = r.ancestor(lookup(rev1), lookup(rev2))
705 a = r.ancestor(lookup(rev1), lookup(rev2))
705 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
706 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
706
707
707 def debugcommands(ui, cmd='', *args):
708 def debugcommands(ui, cmd='', *args):
708 for cmd, vals in sorted(table.iteritems()):
709 for cmd, vals in sorted(table.iteritems()):
709 cmd = cmd.split('|')[0].strip('^')
710 cmd = cmd.split('|')[0].strip('^')
710 opts = ', '.join([i[1] for i in vals[1]])
711 opts = ', '.join([i[1] for i in vals[1]])
711 ui.write('%s: %s\n' % (cmd, opts))
712 ui.write('%s: %s\n' % (cmd, opts))
712
713
713 def debugcomplete(ui, cmd='', **opts):
714 def debugcomplete(ui, cmd='', **opts):
714 """returns the completion list associated with the given command"""
715 """returns the completion list associated with the given command"""
715
716
716 if opts.get('options'):
717 if opts.get('options'):
717 options = []
718 options = []
718 otables = [globalopts]
719 otables = [globalopts]
719 if cmd:
720 if cmd:
720 aliases, entry = cmdutil.findcmd(cmd, table, False)
721 aliases, entry = cmdutil.findcmd(cmd, table, False)
721 otables.append(entry[1])
722 otables.append(entry[1])
722 for t in otables:
723 for t in otables:
723 for o in t:
724 for o in t:
724 if o[0]:
725 if o[0]:
725 options.append('-%s' % o[0])
726 options.append('-%s' % o[0])
726 options.append('--%s' % o[1])
727 options.append('--%s' % o[1])
727 ui.write("%s\n" % "\n".join(options))
728 ui.write("%s\n" % "\n".join(options))
728 return
729 return
729
730
730 cmdlist = cmdutil.findpossible(cmd, table)
731 cmdlist = cmdutil.findpossible(cmd, table)
731 if ui.verbose:
732 if ui.verbose:
732 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
733 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
733 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
734 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
734
735
735 def debugfsinfo(ui, path = "."):
736 def debugfsinfo(ui, path = "."):
736 open('.debugfsinfo', 'w').write('')
737 open('.debugfsinfo', 'w').write('')
737 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
738 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
738 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
739 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
739 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
740 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
740 and 'yes' or 'no'))
741 and 'yes' or 'no'))
741 os.unlink('.debugfsinfo')
742 os.unlink('.debugfsinfo')
742
743
743 def debugrebuildstate(ui, repo, rev="tip"):
744 def debugrebuildstate(ui, repo, rev="tip"):
744 """rebuild the dirstate as it would look like for the given revision"""
745 """rebuild the dirstate as it would look like for the given revision"""
745 ctx = repo[rev]
746 ctx = repo[rev]
746 wlock = repo.wlock()
747 wlock = repo.wlock()
747 try:
748 try:
748 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
749 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
749 finally:
750 finally:
750 wlock.release()
751 wlock.release()
751
752
752 def debugcheckstate(ui, repo):
753 def debugcheckstate(ui, repo):
753 """validate the correctness of the current dirstate"""
754 """validate the correctness of the current dirstate"""
754 parent1, parent2 = repo.dirstate.parents()
755 parent1, parent2 = repo.dirstate.parents()
755 m1 = repo[parent1].manifest()
756 m1 = repo[parent1].manifest()
756 m2 = repo[parent2].manifest()
757 m2 = repo[parent2].manifest()
757 errors = 0
758 errors = 0
758 for f in repo.dirstate:
759 for f in repo.dirstate:
759 state = repo.dirstate[f]
760 state = repo.dirstate[f]
760 if state in "nr" and f not in m1:
761 if state in "nr" and f not in m1:
761 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
762 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
762 errors += 1
763 errors += 1
763 if state in "a" and f in m1:
764 if state in "a" and f in m1:
764 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
765 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
765 errors += 1
766 errors += 1
766 if state in "m" and f not in m1 and f not in m2:
767 if state in "m" and f not in m1 and f not in m2:
767 ui.warn(_("%s in state %s, but not in either manifest\n") %
768 ui.warn(_("%s in state %s, but not in either manifest\n") %
768 (f, state))
769 (f, state))
769 errors += 1
770 errors += 1
770 for f in m1:
771 for f in m1:
771 state = repo.dirstate[f]
772 state = repo.dirstate[f]
772 if state not in "nrm":
773 if state not in "nrm":
773 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
774 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
774 errors += 1
775 errors += 1
775 if errors:
776 if errors:
776 error = _(".hg/dirstate inconsistent with current parent's manifest")
777 error = _(".hg/dirstate inconsistent with current parent's manifest")
777 raise util.Abort(error)
778 raise util.Abort(error)
778
779
779 def showconfig(ui, repo, *values, **opts):
780 def showconfig(ui, repo, *values, **opts):
780 """show combined config settings from all hgrc files
781 """show combined config settings from all hgrc files
781
782
782 With no arguments, print names and values of all config items.
783 With no arguments, print names and values of all config items.
783
784
784 With one argument of the form section.name, print just the value of that
785 With one argument of the form section.name, print just the value of that
785 config item.
786 config item.
786
787
787 With multiple arguments, print names and values of all config items with
788 With multiple arguments, print names and values of all config items with
788 matching section names.
789 matching section names.
789
790
790 With --debug, the source (filename and line number) is printed for each
791 With --debug, the source (filename and line number) is printed for each
791 config item.
792 config item.
792 """
793 """
793
794
794 untrusted = bool(opts.get('untrusted'))
795 untrusted = bool(opts.get('untrusted'))
795 if values:
796 if values:
796 if len([v for v in values if '.' in v]) > 1:
797 if len([v for v in values if '.' in v]) > 1:
797 raise util.Abort(_('only one config item permitted'))
798 raise util.Abort(_('only one config item permitted'))
798 for section, name, value in ui.walkconfig(untrusted=untrusted):
799 for section, name, value in ui.walkconfig(untrusted=untrusted):
799 sectname = section + '.' + name
800 sectname = section + '.' + name
800 if values:
801 if values:
801 for v in values:
802 for v in values:
802 if v == section:
803 if v == section:
803 ui.debug('%s: ' %
804 ui.debug('%s: ' %
804 ui.configsource(section, name, untrusted))
805 ui.configsource(section, name, untrusted))
805 ui.write('%s=%s\n' % (sectname, value))
806 ui.write('%s=%s\n' % (sectname, value))
806 elif v == sectname:
807 elif v == sectname:
807 ui.debug('%s: ' %
808 ui.debug('%s: ' %
808 ui.configsource(section, name, untrusted))
809 ui.configsource(section, name, untrusted))
809 ui.write(value, '\n')
810 ui.write(value, '\n')
810 else:
811 else:
811 ui.debug('%s: ' %
812 ui.debug('%s: ' %
812 ui.configsource(section, name, untrusted))
813 ui.configsource(section, name, untrusted))
813 ui.write('%s=%s\n' % (sectname, value))
814 ui.write('%s=%s\n' % (sectname, value))
814
815
815 def debugsetparents(ui, repo, rev1, rev2=None):
816 def debugsetparents(ui, repo, rev1, rev2=None):
816 """manually set the parents of the current working directory
817 """manually set the parents of the current working directory
817
818
818 This is useful for writing repository conversion tools, but should be used
819 This is useful for writing repository conversion tools, but should be used
819 with care.
820 with care.
820 """
821 """
821
822
822 if not rev2:
823 if not rev2:
823 rev2 = hex(nullid)
824 rev2 = hex(nullid)
824
825
825 wlock = repo.wlock()
826 wlock = repo.wlock()
826 try:
827 try:
827 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
828 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
828 finally:
829 finally:
829 wlock.release()
830 wlock.release()
830
831
831 def debugstate(ui, repo, nodates=None):
832 def debugstate(ui, repo, nodates=None):
832 """show the contents of the current dirstate"""
833 """show the contents of the current dirstate"""
833 timestr = ""
834 timestr = ""
834 showdate = not nodates
835 showdate = not nodates
835 for file_, ent in sorted(repo.dirstate._map.iteritems()):
836 for file_, ent in sorted(repo.dirstate._map.iteritems()):
836 if showdate:
837 if showdate:
837 if ent[3] == -1:
838 if ent[3] == -1:
838 # Pad or slice to locale representation
839 # Pad or slice to locale representation
839 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
840 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
840 timestr = 'unset'
841 timestr = 'unset'
841 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
842 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
842 else:
843 else:
843 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
844 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
844 if ent[1] & 020000:
845 if ent[1] & 020000:
845 mode = 'lnk'
846 mode = 'lnk'
846 else:
847 else:
847 mode = '%3o' % (ent[1] & 0777)
848 mode = '%3o' % (ent[1] & 0777)
848 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
849 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
849 for f in repo.dirstate.copies():
850 for f in repo.dirstate.copies():
850 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
851 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
851
852
852 def debugsub(ui, repo, rev=None):
853 def debugsub(ui, repo, rev=None):
853 if rev == '':
854 if rev == '':
854 rev = None
855 rev = None
855 for k,v in sorted(repo[rev].substate.items()):
856 for k,v in sorted(repo[rev].substate.items()):
856 ui.write('path %s\n' % k)
857 ui.write('path %s\n' % k)
857 ui.write(' source %s\n' % v[0])
858 ui.write(' source %s\n' % v[0])
858 ui.write(' revision %s\n' % v[1])
859 ui.write(' revision %s\n' % v[1])
859
860
860 def debugdata(ui, file_, rev):
861 def debugdata(ui, file_, rev):
861 """dump the contents of a data file revision"""
862 """dump the contents of a data file revision"""
862 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
863 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
863 try:
864 try:
864 ui.write(r.revision(r.lookup(rev)))
865 ui.write(r.revision(r.lookup(rev)))
865 except KeyError:
866 except KeyError:
866 raise util.Abort(_('invalid revision identifier %s') % rev)
867 raise util.Abort(_('invalid revision identifier %s') % rev)
867
868
868 def debugdate(ui, date, range=None, **opts):
869 def debugdate(ui, date, range=None, **opts):
869 """parse and display a date"""
870 """parse and display a date"""
870 if opts["extended"]:
871 if opts["extended"]:
871 d = util.parsedate(date, util.extendeddateformats)
872 d = util.parsedate(date, util.extendeddateformats)
872 else:
873 else:
873 d = util.parsedate(date)
874 d = util.parsedate(date)
874 ui.write("internal: %s %s\n" % d)
875 ui.write("internal: %s %s\n" % d)
875 ui.write("standard: %s\n" % util.datestr(d))
876 ui.write("standard: %s\n" % util.datestr(d))
876 if range:
877 if range:
877 m = util.matchdate(range)
878 m = util.matchdate(range)
878 ui.write("match: %s\n" % m(d[0]))
879 ui.write("match: %s\n" % m(d[0]))
879
880
880 def debugindex(ui, file_):
881 def debugindex(ui, file_):
881 """dump the contents of an index file"""
882 """dump the contents of an index file"""
882 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
883 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
883 ui.write(" rev offset length base linkrev"
884 ui.write(" rev offset length base linkrev"
884 " nodeid p1 p2\n")
885 " nodeid p1 p2\n")
885 for i in r:
886 for i in r:
886 node = r.node(i)
887 node = r.node(i)
887 try:
888 try:
888 pp = r.parents(node)
889 pp = r.parents(node)
889 except:
890 except:
890 pp = [nullid, nullid]
891 pp = [nullid, nullid]
891 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
892 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
892 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
893 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
893 short(node), short(pp[0]), short(pp[1])))
894 short(node), short(pp[0]), short(pp[1])))
894
895
895 def debugindexdot(ui, file_):
896 def debugindexdot(ui, file_):
896 """dump an index DAG as a graphviz dot file"""
897 """dump an index DAG as a graphviz dot file"""
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
898 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
898 ui.write("digraph G {\n")
899 ui.write("digraph G {\n")
899 for i in r:
900 for i in r:
900 node = r.node(i)
901 node = r.node(i)
901 pp = r.parents(node)
902 pp = r.parents(node)
902 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
903 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
903 if pp[1] != nullid:
904 if pp[1] != nullid:
904 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
905 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
905 ui.write("}\n")
906 ui.write("}\n")
906
907
907 def debuginstall(ui):
908 def debuginstall(ui):
908 '''test Mercurial installation'''
909 '''test Mercurial installation'''
909
910
910 def writetemp(contents):
911 def writetemp(contents):
911 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
912 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
912 f = os.fdopen(fd, "wb")
913 f = os.fdopen(fd, "wb")
913 f.write(contents)
914 f.write(contents)
914 f.close()
915 f.close()
915 return name
916 return name
916
917
917 problems = 0
918 problems = 0
918
919
919 # encoding
920 # encoding
920 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
921 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
921 try:
922 try:
922 encoding.fromlocal("test")
923 encoding.fromlocal("test")
923 except util.Abort, inst:
924 except util.Abort, inst:
924 ui.write(" %s\n" % inst)
925 ui.write(" %s\n" % inst)
925 ui.write(_(" (check that your locale is properly set)\n"))
926 ui.write(_(" (check that your locale is properly set)\n"))
926 problems += 1
927 problems += 1
927
928
928 # compiled modules
929 # compiled modules
929 ui.status(_("Checking extensions...\n"))
930 ui.status(_("Checking extensions...\n"))
930 try:
931 try:
931 import bdiff, mpatch, base85
932 import bdiff, mpatch, base85
932 except Exception, inst:
933 except Exception, inst:
933 ui.write(" %s\n" % inst)
934 ui.write(" %s\n" % inst)
934 ui.write(_(" One or more extensions could not be found"))
935 ui.write(_(" One or more extensions could not be found"))
935 ui.write(_(" (check that you compiled the extensions)\n"))
936 ui.write(_(" (check that you compiled the extensions)\n"))
936 problems += 1
937 problems += 1
937
938
938 # templates
939 # templates
939 ui.status(_("Checking templates...\n"))
940 ui.status(_("Checking templates...\n"))
940 try:
941 try:
941 import templater
942 import templater
942 templater.templater(templater.templatepath("map-cmdline.default"))
943 templater.templater(templater.templatepath("map-cmdline.default"))
943 except Exception, inst:
944 except Exception, inst:
944 ui.write(" %s\n" % inst)
945 ui.write(" %s\n" % inst)
945 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
946 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
946 problems += 1
947 problems += 1
947
948
948 # patch
949 # patch
949 ui.status(_("Checking patch...\n"))
950 ui.status(_("Checking patch...\n"))
950 patchproblems = 0
951 patchproblems = 0
951 a = "1\n2\n3\n4\n"
952 a = "1\n2\n3\n4\n"
952 b = "1\n2\n3\ninsert\n4\n"
953 b = "1\n2\n3\ninsert\n4\n"
953 fa = writetemp(a)
954 fa = writetemp(a)
954 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
955 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
955 os.path.basename(fa))
956 os.path.basename(fa))
956 fd = writetemp(d)
957 fd = writetemp(d)
957
958
958 files = {}
959 files = {}
959 try:
960 try:
960 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
961 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
961 except util.Abort, e:
962 except util.Abort, e:
962 ui.write(_(" patch call failed:\n"))
963 ui.write(_(" patch call failed:\n"))
963 ui.write(" " + str(e) + "\n")
964 ui.write(" " + str(e) + "\n")
964 patchproblems += 1
965 patchproblems += 1
965 else:
966 else:
966 if list(files) != [os.path.basename(fa)]:
967 if list(files) != [os.path.basename(fa)]:
967 ui.write(_(" unexpected patch output!\n"))
968 ui.write(_(" unexpected patch output!\n"))
968 patchproblems += 1
969 patchproblems += 1
969 a = open(fa).read()
970 a = open(fa).read()
970 if a != b:
971 if a != b:
971 ui.write(_(" patch test failed!\n"))
972 ui.write(_(" patch test failed!\n"))
972 patchproblems += 1
973 patchproblems += 1
973
974
974 if patchproblems:
975 if patchproblems:
975 if ui.config('ui', 'patch'):
976 if ui.config('ui', 'patch'):
976 ui.write(_(" (Current patch tool may be incompatible with patch,"
977 ui.write(_(" (Current patch tool may be incompatible with patch,"
977 " or misconfigured. Please check your .hgrc file)\n"))
978 " or misconfigured. Please check your .hgrc file)\n"))
978 else:
979 else:
979 ui.write(_(" Internal patcher failure, please report this error"
980 ui.write(_(" Internal patcher failure, please report this error"
980 " to http://mercurial.selenic.com/bts/\n"))
981 " to http://mercurial.selenic.com/bts/\n"))
981 problems += patchproblems
982 problems += patchproblems
982
983
983 os.unlink(fa)
984 os.unlink(fa)
984 os.unlink(fd)
985 os.unlink(fd)
985
986
986 # editor
987 # editor
987 ui.status(_("Checking commit editor...\n"))
988 ui.status(_("Checking commit editor...\n"))
988 editor = ui.geteditor()
989 editor = ui.geteditor()
989 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
990 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
990 if not cmdpath:
991 if not cmdpath:
991 if editor == 'vi':
992 if editor == 'vi':
992 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
993 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
993 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
994 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
994 else:
995 else:
995 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
996 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
996 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
997 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
997 problems += 1
998 problems += 1
998
999
999 # check username
1000 # check username
1000 ui.status(_("Checking username...\n"))
1001 ui.status(_("Checking username...\n"))
1001 user = os.environ.get("HGUSER")
1002 user = os.environ.get("HGUSER")
1002 if user is None:
1003 if user is None:
1003 user = ui.config("ui", "username")
1004 user = ui.config("ui", "username")
1004 if user is None:
1005 if user is None:
1005 user = os.environ.get("EMAIL")
1006 user = os.environ.get("EMAIL")
1006 if not user:
1007 if not user:
1007 ui.warn(" ")
1008 ui.warn(" ")
1008 ui.username()
1009 ui.username()
1009 ui.write(_(" (specify a username in your .hgrc file)\n"))
1010 ui.write(_(" (specify a username in your .hgrc file)\n"))
1010
1011
1011 if not problems:
1012 if not problems:
1012 ui.status(_("No problems detected\n"))
1013 ui.status(_("No problems detected\n"))
1013 else:
1014 else:
1014 ui.write(_("%s problems detected,"
1015 ui.write(_("%s problems detected,"
1015 " please check your install!\n") % problems)
1016 " please check your install!\n") % problems)
1016
1017
1017 return problems
1018 return problems
1018
1019
1019 def debugrename(ui, repo, file1, *pats, **opts):
1020 def debugrename(ui, repo, file1, *pats, **opts):
1020 """dump rename information"""
1021 """dump rename information"""
1021
1022
1022 ctx = repo[opts.get('rev')]
1023 ctx = repo[opts.get('rev')]
1023 m = cmdutil.match(repo, (file1,) + pats, opts)
1024 m = cmdutil.match(repo, (file1,) + pats, opts)
1024 for abs in ctx.walk(m):
1025 for abs in ctx.walk(m):
1025 fctx = ctx[abs]
1026 fctx = ctx[abs]
1026 o = fctx.filelog().renamed(fctx.filenode())
1027 o = fctx.filelog().renamed(fctx.filenode())
1027 rel = m.rel(abs)
1028 rel = m.rel(abs)
1028 if o:
1029 if o:
1029 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1030 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1030 else:
1031 else:
1031 ui.write(_("%s not renamed\n") % rel)
1032 ui.write(_("%s not renamed\n") % rel)
1032
1033
1033 def debugwalk(ui, repo, *pats, **opts):
1034 def debugwalk(ui, repo, *pats, **opts):
1034 """show how files match on given patterns"""
1035 """show how files match on given patterns"""
1035 m = cmdutil.match(repo, pats, opts)
1036 m = cmdutil.match(repo, pats, opts)
1036 items = list(repo.walk(m))
1037 items = list(repo.walk(m))
1037 if not items:
1038 if not items:
1038 return
1039 return
1039 fmt = 'f %%-%ds %%-%ds %%s' % (
1040 fmt = 'f %%-%ds %%-%ds %%s' % (
1040 max([len(abs) for abs in items]),
1041 max([len(abs) for abs in items]),
1041 max([len(m.rel(abs)) for abs in items]))
1042 max([len(m.rel(abs)) for abs in items]))
1042 for abs in items:
1043 for abs in items:
1043 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1044 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1044 ui.write("%s\n" % line.rstrip())
1045 ui.write("%s\n" % line.rstrip())
1045
1046
1046 def diff(ui, repo, *pats, **opts):
1047 def diff(ui, repo, *pats, **opts):
1047 """diff repository (or selected files)
1048 """diff repository (or selected files)
1048
1049
1049 Show differences between revisions for the specified files.
1050 Show differences between revisions for the specified files.
1050
1051
1051 Differences between files are shown using the unified diff format.
1052 Differences between files are shown using the unified diff format.
1052
1053
1053 NOTE: diff may generate unexpected results for merges, as it will default
1054 NOTE: diff may generate unexpected results for merges, as it will default
1054 to comparing against the working directory's first parent changeset if no
1055 to comparing against the working directory's first parent changeset if no
1055 revisions are specified.
1056 revisions are specified.
1056
1057
1057 When two revision arguments are given, then changes are shown between
1058 When two revision arguments are given, then changes are shown between
1058 those revisions. If only one revision is specified then that revision is
1059 those revisions. If only one revision is specified then that revision is
1059 compared to the working directory, and, when no revisions are specified,
1060 compared to the working directory, and, when no revisions are specified,
1060 the working directory files are compared to its parent.
1061 the working directory files are compared to its parent.
1061
1062
1062 Without the -a/--text option, diff will avoid generating diffs of files it
1063 Without the -a/--text option, diff will avoid generating diffs of files it
1063 detects as binary. With -a, diff will generate a diff anyway, probably
1064 detects as binary. With -a, diff will generate a diff anyway, probably
1064 with undesirable results.
1065 with undesirable results.
1065
1066
1066 Use the -g/--git option to generate diffs in the git extended diff format.
1067 Use the -g/--git option to generate diffs in the git extended diff format.
1067 For more information, read 'hg help diffs'.
1068 For more information, read 'hg help diffs'.
1068 """
1069 """
1069
1070
1070 revs = opts.get('rev')
1071 revs = opts.get('rev')
1071 change = opts.get('change')
1072 change = opts.get('change')
1072
1073
1073 if revs and change:
1074 if revs and change:
1074 msg = _('cannot specify --rev and --change at the same time')
1075 msg = _('cannot specify --rev and --change at the same time')
1075 raise util.Abort(msg)
1076 raise util.Abort(msg)
1076 elif change:
1077 elif change:
1077 node2 = repo.lookup(change)
1078 node2 = repo.lookup(change)
1078 node1 = repo[node2].parents()[0].node()
1079 node1 = repo[node2].parents()[0].node()
1079 else:
1080 else:
1080 node1, node2 = cmdutil.revpair(repo, revs)
1081 node1, node2 = cmdutil.revpair(repo, revs)
1081
1082
1082 m = cmdutil.match(repo, pats, opts)
1083 m = cmdutil.match(repo, pats, opts)
1083 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1084 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1084 for chunk in it:
1085 for chunk in it:
1085 ui.write(chunk)
1086 ui.write(chunk)
1086
1087
1087 def export(ui, repo, *changesets, **opts):
1088 def export(ui, repo, *changesets, **opts):
1088 """dump the header and diffs for one or more changesets
1089 """dump the header and diffs for one or more changesets
1089
1090
1090 Print the changeset header and diffs for one or more revisions.
1091 Print the changeset header and diffs for one or more revisions.
1091
1092
1092 The information shown in the changeset header is: author, changeset hash,
1093 The information shown in the changeset header is: author, changeset hash,
1093 parent(s) and commit comment.
1094 parent(s) and commit comment.
1094
1095
1095 NOTE: export may generate unexpected diff output for merge changesets, as
1096 NOTE: export may generate unexpected diff output for merge changesets, as
1096 it will compare the merge changeset against its first parent only.
1097 it will compare the merge changeset against its first parent only.
1097
1098
1098 Output may be to a file, in which case the name of the file is given using
1099 Output may be to a file, in which case the name of the file is given using
1099 a format string. The formatting rules are as follows::
1100 a format string. The formatting rules are as follows::
1100
1101
1101 %% literal "%" character
1102 %% literal "%" character
1102 %H changeset hash (40 bytes of hexadecimal)
1103 %H changeset hash (40 bytes of hexadecimal)
1103 %N number of patches being generated
1104 %N number of patches being generated
1104 %R changeset revision number
1105 %R changeset revision number
1105 %b basename of the exporting repository
1106 %b basename of the exporting repository
1106 %h short-form changeset hash (12 bytes of hexadecimal)
1107 %h short-form changeset hash (12 bytes of hexadecimal)
1107 %n zero-padded sequence number, starting at 1
1108 %n zero-padded sequence number, starting at 1
1108 %r zero-padded changeset revision number
1109 %r zero-padded changeset revision number
1109
1110
1110 Without the -a/--text option, export will avoid generating diffs of files
1111 Without the -a/--text option, export will avoid generating diffs of files
1111 it detects as binary. With -a, export will generate a diff anyway,
1112 it detects as binary. With -a, export will generate a diff anyway,
1112 probably with undesirable results.
1113 probably with undesirable results.
1113
1114
1114 Use the -g/--git option to generate diffs in the git extended diff format.
1115 Use the -g/--git option to generate diffs in the git extended diff format.
1115 See 'hg help diffs' for more information.
1116 See 'hg help diffs' for more information.
1116
1117
1117 With the --switch-parent option, the diff will be against the second
1118 With the --switch-parent option, the diff will be against the second
1118 parent. It can be useful to review a merge.
1119 parent. It can be useful to review a merge.
1119 """
1120 """
1120 if not changesets:
1121 if not changesets:
1121 raise util.Abort(_("export requires at least one changeset"))
1122 raise util.Abort(_("export requires at least one changeset"))
1122 revs = cmdutil.revrange(repo, changesets)
1123 revs = cmdutil.revrange(repo, changesets)
1123 if len(revs) > 1:
1124 if len(revs) > 1:
1124 ui.note(_('exporting patches:\n'))
1125 ui.note(_('exporting patches:\n'))
1125 else:
1126 else:
1126 ui.note(_('exporting patch:\n'))
1127 ui.note(_('exporting patch:\n'))
1127 patch.export(repo, revs, template=opts.get('output'),
1128 patch.export(repo, revs, template=opts.get('output'),
1128 switch_parent=opts.get('switch_parent'),
1129 switch_parent=opts.get('switch_parent'),
1129 opts=patch.diffopts(ui, opts))
1130 opts=patch.diffopts(ui, opts))
1130
1131
1131 def forget(ui, repo, *pats, **opts):
1132 def forget(ui, repo, *pats, **opts):
1132 """forget the specified files on the next commit
1133 """forget the specified files on the next commit
1133
1134
1134 Mark the specified files so they will no longer be tracked after the next
1135 Mark the specified files so they will no longer be tracked after the next
1135 commit.
1136 commit.
1136
1137
1137 This only removes files from the current branch, not from the entire
1138 This only removes files from the current branch, not from the entire
1138 project history, and it does not delete them from the working directory.
1139 project history, and it does not delete them from the working directory.
1139
1140
1140 To undo a forget before the next commit, see hg add.
1141 To undo a forget before the next commit, see hg add.
1141 """
1142 """
1142
1143
1143 if not pats:
1144 if not pats:
1144 raise util.Abort(_('no files specified'))
1145 raise util.Abort(_('no files specified'))
1145
1146
1146 m = cmdutil.match(repo, pats, opts)
1147 m = cmdutil.match(repo, pats, opts)
1147 s = repo.status(match=m, clean=True)
1148 s = repo.status(match=m, clean=True)
1148 forget = sorted(s[0] + s[1] + s[3] + s[6])
1149 forget = sorted(s[0] + s[1] + s[3] + s[6])
1149
1150
1150 for f in m.files():
1151 for f in m.files():
1151 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1152 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1152 ui.warn(_('not removing %s: file is already untracked\n')
1153 ui.warn(_('not removing %s: file is already untracked\n')
1153 % m.rel(f))
1154 % m.rel(f))
1154
1155
1155 for f in forget:
1156 for f in forget:
1156 if ui.verbose or not m.exact(f):
1157 if ui.verbose or not m.exact(f):
1157 ui.status(_('removing %s\n') % m.rel(f))
1158 ui.status(_('removing %s\n') % m.rel(f))
1158
1159
1159 repo.remove(forget, unlink=False)
1160 repo.remove(forget, unlink=False)
1160
1161
1161 def grep(ui, repo, pattern, *pats, **opts):
1162 def grep(ui, repo, pattern, *pats, **opts):
1162 """search for a pattern in specified files and revisions
1163 """search for a pattern in specified files and revisions
1163
1164
1164 Search revisions of files for a regular expression.
1165 Search revisions of files for a regular expression.
1165
1166
1166 This command behaves differently than Unix grep. It only accepts
1167 This command behaves differently than Unix grep. It only accepts
1167 Python/Perl regexps. It searches repository history, not the working
1168 Python/Perl regexps. It searches repository history, not the working
1168 directory. It always prints the revision number in which a match appears.
1169 directory. It always prints the revision number in which a match appears.
1169
1170
1170 By default, grep only prints output for the first revision of a file in
1171 By default, grep only prints output for the first revision of a file in
1171 which it finds a match. To get it to print every revision that contains a
1172 which it finds a match. To get it to print every revision that contains a
1172 change in match status ("-" for a match that becomes a non-match, or "+"
1173 change in match status ("-" for a match that becomes a non-match, or "+"
1173 for a non-match that becomes a match), use the --all flag.
1174 for a non-match that becomes a match), use the --all flag.
1174 """
1175 """
1175 reflags = 0
1176 reflags = 0
1176 if opts.get('ignore_case'):
1177 if opts.get('ignore_case'):
1177 reflags |= re.I
1178 reflags |= re.I
1178 try:
1179 try:
1179 regexp = re.compile(pattern, reflags)
1180 regexp = re.compile(pattern, reflags)
1180 except Exception, inst:
1181 except Exception, inst:
1181 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1182 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1182 return None
1183 return None
1183 sep, eol = ':', '\n'
1184 sep, eol = ':', '\n'
1184 if opts.get('print0'):
1185 if opts.get('print0'):
1185 sep = eol = '\0'
1186 sep = eol = '\0'
1186
1187
1187 getfile = util.lrucachefunc(repo.file)
1188 getfile = util.lrucachefunc(repo.file)
1188
1189
1189 def matchlines(body):
1190 def matchlines(body):
1190 begin = 0
1191 begin = 0
1191 linenum = 0
1192 linenum = 0
1192 while True:
1193 while True:
1193 match = regexp.search(body, begin)
1194 match = regexp.search(body, begin)
1194 if not match:
1195 if not match:
1195 break
1196 break
1196 mstart, mend = match.span()
1197 mstart, mend = match.span()
1197 linenum += body.count('\n', begin, mstart) + 1
1198 linenum += body.count('\n', begin, mstart) + 1
1198 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1199 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1199 begin = body.find('\n', mend) + 1 or len(body)
1200 begin = body.find('\n', mend) + 1 or len(body)
1200 lend = begin - 1
1201 lend = begin - 1
1201 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1202 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1202
1203
1203 class linestate(object):
1204 class linestate(object):
1204 def __init__(self, line, linenum, colstart, colend):
1205 def __init__(self, line, linenum, colstart, colend):
1205 self.line = line
1206 self.line = line
1206 self.linenum = linenum
1207 self.linenum = linenum
1207 self.colstart = colstart
1208 self.colstart = colstart
1208 self.colend = colend
1209 self.colend = colend
1209
1210
1210 def __hash__(self):
1211 def __hash__(self):
1211 return hash((self.linenum, self.line))
1212 return hash((self.linenum, self.line))
1212
1213
1213 def __eq__(self, other):
1214 def __eq__(self, other):
1214 return self.line == other.line
1215 return self.line == other.line
1215
1216
1216 matches = {}
1217 matches = {}
1217 copies = {}
1218 copies = {}
1218 def grepbody(fn, rev, body):
1219 def grepbody(fn, rev, body):
1219 matches[rev].setdefault(fn, [])
1220 matches[rev].setdefault(fn, [])
1220 m = matches[rev][fn]
1221 m = matches[rev][fn]
1221 for lnum, cstart, cend, line in matchlines(body):
1222 for lnum, cstart, cend, line in matchlines(body):
1222 s = linestate(line, lnum, cstart, cend)
1223 s = linestate(line, lnum, cstart, cend)
1223 m.append(s)
1224 m.append(s)
1224
1225
1225 def difflinestates(a, b):
1226 def difflinestates(a, b):
1226 sm = difflib.SequenceMatcher(None, a, b)
1227 sm = difflib.SequenceMatcher(None, a, b)
1227 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1228 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1228 if tag == 'insert':
1229 if tag == 'insert':
1229 for i in xrange(blo, bhi):
1230 for i in xrange(blo, bhi):
1230 yield ('+', b[i])
1231 yield ('+', b[i])
1231 elif tag == 'delete':
1232 elif tag == 'delete':
1232 for i in xrange(alo, ahi):
1233 for i in xrange(alo, ahi):
1233 yield ('-', a[i])
1234 yield ('-', a[i])
1234 elif tag == 'replace':
1235 elif tag == 'replace':
1235 for i in xrange(alo, ahi):
1236 for i in xrange(alo, ahi):
1236 yield ('-', a[i])
1237 yield ('-', a[i])
1237 for i in xrange(blo, bhi):
1238 for i in xrange(blo, bhi):
1238 yield ('+', b[i])
1239 yield ('+', b[i])
1239
1240
1240 def display(fn, r, pstates, states):
1241 def display(fn, r, pstates, states):
1241 datefunc = ui.quiet and util.shortdate or util.datestr
1242 datefunc = ui.quiet and util.shortdate or util.datestr
1242 found = False
1243 found = False
1243 filerevmatches = {}
1244 filerevmatches = {}
1244 if opts.get('all'):
1245 if opts.get('all'):
1245 iter = difflinestates(pstates, states)
1246 iter = difflinestates(pstates, states)
1246 else:
1247 else:
1247 iter = [('', l) for l in states]
1248 iter = [('', l) for l in states]
1248 for change, l in iter:
1249 for change, l in iter:
1249 cols = [fn, str(r)]
1250 cols = [fn, str(r)]
1250 if opts.get('line_number'):
1251 if opts.get('line_number'):
1251 cols.append(str(l.linenum))
1252 cols.append(str(l.linenum))
1252 if opts.get('all'):
1253 if opts.get('all'):
1253 cols.append(change)
1254 cols.append(change)
1254 if opts.get('user'):
1255 if opts.get('user'):
1255 cols.append(ui.shortuser(get(r)[1]))
1256 cols.append(ui.shortuser(get(r)[1]))
1256 if opts.get('date'):
1257 if opts.get('date'):
1257 cols.append(datefunc(get(r)[2]))
1258 cols.append(datefunc(get(r)[2]))
1258 if opts.get('files_with_matches'):
1259 if opts.get('files_with_matches'):
1259 c = (fn, r)
1260 c = (fn, r)
1260 if c in filerevmatches:
1261 if c in filerevmatches:
1261 continue
1262 continue
1262 filerevmatches[c] = 1
1263 filerevmatches[c] = 1
1263 else:
1264 else:
1264 cols.append(l.line)
1265 cols.append(l.line)
1265 ui.write(sep.join(cols), eol)
1266 ui.write(sep.join(cols), eol)
1266 found = True
1267 found = True
1267 return found
1268 return found
1268
1269
1269 skip = {}
1270 skip = {}
1270 revfiles = {}
1271 revfiles = {}
1271 get = util.cachefunc(lambda r: repo[r].changeset())
1272 get = util.cachefunc(lambda r: repo[r].changeset())
1272 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1273 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1273 found = False
1274 found = False
1274 follow = opts.get('follow')
1275 follow = opts.get('follow')
1275 for st, rev, fns in changeiter:
1276 for st, rev, fns in changeiter:
1276 if st == 'window':
1277 if st == 'window':
1277 matches.clear()
1278 matches.clear()
1278 revfiles.clear()
1279 revfiles.clear()
1279 elif st == 'add':
1280 elif st == 'add':
1280 ctx = repo[rev]
1281 ctx = repo[rev]
1281 pctx = ctx.parents()[0]
1282 pctx = ctx.parents()[0]
1282 parent = pctx.rev()
1283 parent = pctx.rev()
1283 matches.setdefault(rev, {})
1284 matches.setdefault(rev, {})
1284 matches.setdefault(parent, {})
1285 matches.setdefault(parent, {})
1285 files = revfiles.setdefault(rev, [])
1286 files = revfiles.setdefault(rev, [])
1286 for fn in fns:
1287 for fn in fns:
1287 flog = getfile(fn)
1288 flog = getfile(fn)
1288 try:
1289 try:
1289 fnode = ctx.filenode(fn)
1290 fnode = ctx.filenode(fn)
1290 except error.LookupError:
1291 except error.LookupError:
1291 continue
1292 continue
1292
1293
1293 copied = flog.renamed(fnode)
1294 copied = flog.renamed(fnode)
1294 copy = follow and copied and copied[0]
1295 copy = follow and copied and copied[0]
1295 if copy:
1296 if copy:
1296 copies.setdefault(rev, {})[fn] = copy
1297 copies.setdefault(rev, {})[fn] = copy
1297 if fn in skip:
1298 if fn in skip:
1298 if copy:
1299 if copy:
1299 skip[copy] = True
1300 skip[copy] = True
1300 continue
1301 continue
1301 files.append(fn)
1302 files.append(fn)
1302
1303
1303 if not matches[rev].has_key(fn):
1304 if not matches[rev].has_key(fn):
1304 grepbody(fn, rev, flog.read(fnode))
1305 grepbody(fn, rev, flog.read(fnode))
1305
1306
1306 pfn = copy or fn
1307 pfn = copy or fn
1307 if not matches[parent].has_key(pfn):
1308 if not matches[parent].has_key(pfn):
1308 try:
1309 try:
1309 fnode = pctx.filenode(pfn)
1310 fnode = pctx.filenode(pfn)
1310 grepbody(pfn, parent, flog.read(fnode))
1311 grepbody(pfn, parent, flog.read(fnode))
1311 except error.LookupError:
1312 except error.LookupError:
1312 pass
1313 pass
1313 elif st == 'iter':
1314 elif st == 'iter':
1314 parent = repo[rev].parents()[0].rev()
1315 parent = repo[rev].parents()[0].rev()
1315 for fn in sorted(revfiles.get(rev, [])):
1316 for fn in sorted(revfiles.get(rev, [])):
1316 states = matches[rev][fn]
1317 states = matches[rev][fn]
1317 copy = copies.get(rev, {}).get(fn)
1318 copy = copies.get(rev, {}).get(fn)
1318 if fn in skip:
1319 if fn in skip:
1319 if copy:
1320 if copy:
1320 skip[copy] = True
1321 skip[copy] = True
1321 continue
1322 continue
1322 pstates = matches.get(parent, {}).get(copy or fn, [])
1323 pstates = matches.get(parent, {}).get(copy or fn, [])
1323 if pstates or states:
1324 if pstates or states:
1324 r = display(fn, rev, pstates, states)
1325 r = display(fn, rev, pstates, states)
1325 found = found or r
1326 found = found or r
1326 if r and not opts.get('all'):
1327 if r and not opts.get('all'):
1327 skip[fn] = True
1328 skip[fn] = True
1328 if copy:
1329 if copy:
1329 skip[copy] = True
1330 skip[copy] = True
1330
1331
1331 def heads(ui, repo, *branchrevs, **opts):
1332 def heads(ui, repo, *branchrevs, **opts):
1332 """show current repository heads or show branch heads
1333 """show current repository heads or show branch heads
1333
1334
1334 With no arguments, show all repository head changesets.
1335 With no arguments, show all repository head changesets.
1335
1336
1336 Repository "heads" are changesets that don't have child changesets. They
1337 Repository "heads" are changesets that don't have child changesets. They
1337 are where development generally takes place and are the usual targets for
1338 are where development generally takes place and are the usual targets for
1338 update and merge operations.
1339 update and merge operations.
1339
1340
1340 If one or more REV is given, the "branch heads" will be shown for the
1341 If one or more REV is given, the "branch heads" will be shown for the
1341 named branch associated with that revision. The name of the branch is
1342 named branch associated with that revision. The name of the branch is
1342 called the revision's branch tag.
1343 called the revision's branch tag.
1343
1344
1344 Branch heads are revisions on a given named branch that do not have any
1345 Branch heads are revisions on a given named branch that do not have any
1345 descendants on the same branch. A branch head could be a true head or it
1346 descendants on the same branch. A branch head could be a true head or it
1346 could be the last changeset on a branch before a new branch was created.
1347 could be the last changeset on a branch before a new branch was created.
1347 If none of the branch heads are true heads, the branch is considered
1348 If none of the branch heads are true heads, the branch is considered
1348 inactive. If -c/--closed is specified, also show branch heads marked
1349 inactive. If -c/--closed is specified, also show branch heads marked
1349 closed (see hg commit --close-branch).
1350 closed (see hg commit --close-branch).
1350
1351
1351 If STARTREV is specified only those heads (or branch heads) that are
1352 If STARTREV is specified only those heads (or branch heads) that are
1352 descendants of STARTREV will be displayed.
1353 descendants of STARTREV will be displayed.
1353 """
1354 """
1354 if opts.get('rev'):
1355 if opts.get('rev'):
1355 start = repo.lookup(opts['rev'])
1356 start = repo.lookup(opts['rev'])
1356 else:
1357 else:
1357 start = None
1358 start = None
1358 closed = opts.get('closed')
1359 closed = opts.get('closed')
1359 hideinactive, _heads = opts.get('active'), None
1360 hideinactive, _heads = opts.get('active'), None
1360 if not branchrevs:
1361 if not branchrevs:
1361 # Assume we're looking repo-wide heads if no revs were specified.
1362 # Assume we're looking repo-wide heads if no revs were specified.
1362 heads = repo.heads(start)
1363 heads = repo.heads(start)
1363 else:
1364 else:
1364 if hideinactive:
1365 if hideinactive:
1365 _heads = repo.heads(start)
1366 _heads = repo.heads(start)
1366 heads = []
1367 heads = []
1367 visitedset = set()
1368 visitedset = set()
1368 for branchrev in branchrevs:
1369 for branchrev in branchrevs:
1369 branch = repo[branchrev].branch()
1370 branch = repo[branchrev].branch()
1370 if branch in visitedset:
1371 if branch in visitedset:
1371 continue
1372 continue
1372 visitedset.add(branch)
1373 visitedset.add(branch)
1373 bheads = repo.branchheads(branch, start, closed=closed)
1374 bheads = repo.branchheads(branch, start, closed=closed)
1374 if not bheads:
1375 if not bheads:
1375 if not opts.get('rev'):
1376 if not opts.get('rev'):
1376 ui.warn(_("no open branch heads on branch %s\n") % branch)
1377 ui.warn(_("no open branch heads on branch %s\n") % branch)
1377 elif branch != branchrev:
1378 elif branch != branchrev:
1378 ui.warn(_("no changes on branch %s containing %s are "
1379 ui.warn(_("no changes on branch %s containing %s are "
1379 "reachable from %s\n")
1380 "reachable from %s\n")
1380 % (branch, branchrev, opts.get('rev')))
1381 % (branch, branchrev, opts.get('rev')))
1381 else:
1382 else:
1382 ui.warn(_("no changes on branch %s are reachable from %s\n")
1383 ui.warn(_("no changes on branch %s are reachable from %s\n")
1383 % (branch, opts.get('rev')))
1384 % (branch, opts.get('rev')))
1384 if hideinactive:
1385 if hideinactive:
1385 bheads = [bhead for bhead in bheads if bhead in _heads]
1386 bheads = [bhead for bhead in bheads if bhead in _heads]
1386 heads.extend(bheads)
1387 heads.extend(bheads)
1387 if not heads:
1388 if not heads:
1388 return 1
1389 return 1
1389 displayer = cmdutil.show_changeset(ui, repo, opts)
1390 displayer = cmdutil.show_changeset(ui, repo, opts)
1390 for n in heads:
1391 for n in heads:
1391 displayer.show(repo[n])
1392 displayer.show(repo[n])
1392
1393
1393 def help_(ui, name=None, with_version=False):
1394 def help_(ui, name=None, with_version=False):
1394 """show help for a given topic or a help overview
1395 """show help for a given topic or a help overview
1395
1396
1396 With no arguments, print a list of commands with short help messages.
1397 With no arguments, print a list of commands with short help messages.
1397
1398
1398 Given a topic, extension, or command name, print help for that topic.
1399 Given a topic, extension, or command name, print help for that topic.
1399 """
1400 """
1400 option_lists = []
1401 option_lists = []
1401 textwidth = util.termwidth() - 2
1402 textwidth = util.termwidth() - 2
1402
1403
1403 def addglobalopts(aliases):
1404 def addglobalopts(aliases):
1404 if ui.verbose:
1405 if ui.verbose:
1405 option_lists.append((_("global options:"), globalopts))
1406 option_lists.append((_("global options:"), globalopts))
1406 if name == 'shortlist':
1407 if name == 'shortlist':
1407 option_lists.append((_('use "hg help" for the full list '
1408 option_lists.append((_('use "hg help" for the full list '
1408 'of commands'), ()))
1409 'of commands'), ()))
1409 else:
1410 else:
1410 if name == 'shortlist':
1411 if name == 'shortlist':
1411 msg = _('use "hg help" for the full list of commands '
1412 msg = _('use "hg help" for the full list of commands '
1412 'or "hg -v" for details')
1413 'or "hg -v" for details')
1413 elif aliases:
1414 elif aliases:
1414 msg = _('use "hg -v help%s" to show aliases and '
1415 msg = _('use "hg -v help%s" to show aliases and '
1415 'global options') % (name and " " + name or "")
1416 'global options') % (name and " " + name or "")
1416 else:
1417 else:
1417 msg = _('use "hg -v help %s" to show global options') % name
1418 msg = _('use "hg -v help %s" to show global options') % name
1418 option_lists.append((msg, ()))
1419 option_lists.append((msg, ()))
1419
1420
1420 def helpcmd(name):
1421 def helpcmd(name):
1421 if with_version:
1422 if with_version:
1422 version_(ui)
1423 version_(ui)
1423 ui.write('\n')
1424 ui.write('\n')
1424
1425
1425 try:
1426 try:
1426 aliases, i = cmdutil.findcmd(name, table, False)
1427 aliases, i = cmdutil.findcmd(name, table, False)
1427 except error.AmbiguousCommand, inst:
1428 except error.AmbiguousCommand, inst:
1428 # py3k fix: except vars can't be used outside the scope of the
1429 # py3k fix: except vars can't be used outside the scope of the
1429 # except block, nor can be used inside a lambda. python issue4617
1430 # except block, nor can be used inside a lambda. python issue4617
1430 prefix = inst.args[0]
1431 prefix = inst.args[0]
1431 select = lambda c: c.lstrip('^').startswith(prefix)
1432 select = lambda c: c.lstrip('^').startswith(prefix)
1432 helplist(_('list of commands:\n\n'), select)
1433 helplist(_('list of commands:\n\n'), select)
1433 return
1434 return
1434
1435
1435 # synopsis
1436 # synopsis
1436 if len(i) > 2:
1437 if len(i) > 2:
1437 if i[2].startswith('hg'):
1438 if i[2].startswith('hg'):
1438 ui.write("%s\n" % i[2])
1439 ui.write("%s\n" % i[2])
1439 else:
1440 else:
1440 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1441 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1441 else:
1442 else:
1442 ui.write('hg %s\n' % aliases[0])
1443 ui.write('hg %s\n' % aliases[0])
1443
1444
1444 # aliases
1445 # aliases
1445 if not ui.quiet and len(aliases) > 1:
1446 if not ui.quiet and len(aliases) > 1:
1446 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1447 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1447
1448
1448 # description
1449 # description
1449 doc = gettext(i[0].__doc__)
1450 doc = gettext(i[0].__doc__)
1450 if not doc:
1451 if not doc:
1451 doc = _("(no help text available)")
1452 doc = _("(no help text available)")
1452 if ui.quiet:
1453 if ui.quiet:
1453 doc = doc.splitlines()[0]
1454 doc = doc.splitlines()[0]
1454 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1455 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1455
1456
1456 if not ui.quiet:
1457 if not ui.quiet:
1457 # options
1458 # options
1458 if i[1]:
1459 if i[1]:
1459 option_lists.append((_("options:\n"), i[1]))
1460 option_lists.append((_("options:\n"), i[1]))
1460
1461
1461 addglobalopts(False)
1462 addglobalopts(False)
1462
1463
1463 def helplist(header, select=None):
1464 def helplist(header, select=None):
1464 h = {}
1465 h = {}
1465 cmds = {}
1466 cmds = {}
1466 for c, e in table.iteritems():
1467 for c, e in table.iteritems():
1467 f = c.split("|", 1)[0]
1468 f = c.split("|", 1)[0]
1468 if select and not select(f):
1469 if select and not select(f):
1469 continue
1470 continue
1470 if (not select and name != 'shortlist' and
1471 if (not select and name != 'shortlist' and
1471 e[0].__module__ != __name__):
1472 e[0].__module__ != __name__):
1472 continue
1473 continue
1473 if name == "shortlist" and not f.startswith("^"):
1474 if name == "shortlist" and not f.startswith("^"):
1474 continue
1475 continue
1475 f = f.lstrip("^")
1476 f = f.lstrip("^")
1476 if not ui.debugflag and f.startswith("debug"):
1477 if not ui.debugflag and f.startswith("debug"):
1477 continue
1478 continue
1478 doc = e[0].__doc__
1479 doc = e[0].__doc__
1479 if doc and 'DEPRECATED' in doc and not ui.verbose:
1480 if doc and 'DEPRECATED' in doc and not ui.verbose:
1480 continue
1481 continue
1481 doc = gettext(doc)
1482 doc = gettext(doc)
1482 if not doc:
1483 if not doc:
1483 doc = _("(no help text available)")
1484 doc = _("(no help text available)")
1484 h[f] = doc.splitlines()[0].rstrip()
1485 h[f] = doc.splitlines()[0].rstrip()
1485 cmds[f] = c.lstrip("^")
1486 cmds[f] = c.lstrip("^")
1486
1487
1487 if not h:
1488 if not h:
1488 ui.status(_('no commands defined\n'))
1489 ui.status(_('no commands defined\n'))
1489 return
1490 return
1490
1491
1491 ui.status(header)
1492 ui.status(header)
1492 fns = sorted(h)
1493 fns = sorted(h)
1493 m = max(map(len, fns))
1494 m = max(map(len, fns))
1494 for f in fns:
1495 for f in fns:
1495 if ui.verbose:
1496 if ui.verbose:
1496 commands = cmds[f].replace("|",", ")
1497 commands = cmds[f].replace("|",", ")
1497 ui.write(" %s:\n %s\n"%(commands, h[f]))
1498 ui.write(" %s:\n %s\n"%(commands, h[f]))
1498 else:
1499 else:
1499 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1500 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1500
1501
1501 if name != 'shortlist':
1502 if name != 'shortlist':
1502 exts, maxlength = extensions.enabled()
1503 exts, maxlength = extensions.enabled()
1503 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1504 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1504 if text:
1505 if text:
1505 ui.write("\n%s\n" % minirst.format(text, textwidth))
1506 ui.write("\n%s\n" % minirst.format(text, textwidth))
1506
1507
1507 if not ui.quiet:
1508 if not ui.quiet:
1508 addglobalopts(True)
1509 addglobalopts(True)
1509
1510
1510 def helptopic(name):
1511 def helptopic(name):
1511 for names, header, doc in help.helptable:
1512 for names, header, doc in help.helptable:
1512 if name in names:
1513 if name in names:
1513 break
1514 break
1514 else:
1515 else:
1515 raise error.UnknownCommand(name)
1516 raise error.UnknownCommand(name)
1516
1517
1517 # description
1518 # description
1518 if not doc:
1519 if not doc:
1519 doc = _("(no help text available)")
1520 doc = _("(no help text available)")
1520 if hasattr(doc, '__call__'):
1521 if hasattr(doc, '__call__'):
1521 doc = doc()
1522 doc = doc()
1522
1523
1523 ui.write("%s\n\n" % header)
1524 ui.write("%s\n\n" % header)
1524 ui.write("%s\n" % minirst.format(doc, textwidth))
1525 ui.write("%s\n" % minirst.format(doc, textwidth))
1525
1526
1526 def helpext(name):
1527 def helpext(name):
1527 try:
1528 try:
1528 mod = extensions.find(name)
1529 mod = extensions.find(name)
1529 except KeyError:
1530 except KeyError:
1530 raise error.UnknownCommand(name)
1531 raise error.UnknownCommand(name)
1531
1532
1532 doc = gettext(mod.__doc__) or _('no help text available')
1533 doc = gettext(mod.__doc__) or _('no help text available')
1533 head, tail = doc.split('\n', 1)
1534 head, tail = doc.split('\n', 1)
1534 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1535 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1535 if tail:
1536 if tail:
1536 ui.write(minirst.format(tail, textwidth))
1537 ui.write(minirst.format(tail, textwidth))
1537 ui.status('\n\n')
1538 ui.status('\n\n')
1538
1539
1539 try:
1540 try:
1540 ct = mod.cmdtable
1541 ct = mod.cmdtable
1541 except AttributeError:
1542 except AttributeError:
1542 ct = {}
1543 ct = {}
1543
1544
1544 modcmds = set([c.split('|', 1)[0] for c in ct])
1545 modcmds = set([c.split('|', 1)[0] for c in ct])
1545 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1546 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1546
1547
1547 if name and name != 'shortlist':
1548 if name and name != 'shortlist':
1548 i = None
1549 i = None
1549 for f in (helptopic, helpcmd, helpext):
1550 for f in (helptopic, helpcmd, helpext):
1550 try:
1551 try:
1551 f(name)
1552 f(name)
1552 i = None
1553 i = None
1553 break
1554 break
1554 except error.UnknownCommand, inst:
1555 except error.UnknownCommand, inst:
1555 i = inst
1556 i = inst
1556 if i:
1557 if i:
1557 raise i
1558 raise i
1558
1559
1559 else:
1560 else:
1560 # program name
1561 # program name
1561 if ui.verbose or with_version:
1562 if ui.verbose or with_version:
1562 version_(ui)
1563 version_(ui)
1563 else:
1564 else:
1564 ui.status(_("Mercurial Distributed SCM\n"))
1565 ui.status(_("Mercurial Distributed SCM\n"))
1565 ui.status('\n')
1566 ui.status('\n')
1566
1567
1567 # list of commands
1568 # list of commands
1568 if name == "shortlist":
1569 if name == "shortlist":
1569 header = _('basic commands:\n\n')
1570 header = _('basic commands:\n\n')
1570 else:
1571 else:
1571 header = _('list of commands:\n\n')
1572 header = _('list of commands:\n\n')
1572
1573
1573 helplist(header)
1574 helplist(header)
1574
1575
1575 # list all option lists
1576 # list all option lists
1576 opt_output = []
1577 opt_output = []
1577 for title, options in option_lists:
1578 for title, options in option_lists:
1578 opt_output.append(("\n%s" % title, None))
1579 opt_output.append(("\n%s" % title, None))
1579 for shortopt, longopt, default, desc in options:
1580 for shortopt, longopt, default, desc in options:
1580 if "DEPRECATED" in desc and not ui.verbose: continue
1581 if "DEPRECATED" in desc and not ui.verbose: continue
1581 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1582 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1582 longopt and " --%s" % longopt),
1583 longopt and " --%s" % longopt),
1583 "%s%s" % (desc,
1584 "%s%s" % (desc,
1584 default
1585 default
1585 and _(" (default: %s)") % default
1586 and _(" (default: %s)") % default
1586 or "")))
1587 or "")))
1587
1588
1588 if not name:
1589 if not name:
1589 ui.write(_("\nadditional help topics:\n\n"))
1590 ui.write(_("\nadditional help topics:\n\n"))
1590 topics = []
1591 topics = []
1591 for names, header, doc in help.helptable:
1592 for names, header, doc in help.helptable:
1592 names = [(-len(name), name) for name in names]
1593 names = [(-len(name), name) for name in names]
1593 names.sort()
1594 names.sort()
1594 topics.append((names[0][1], header))
1595 topics.append((names[0][1], header))
1595 topics_len = max([len(s[0]) for s in topics])
1596 topics_len = max([len(s[0]) for s in topics])
1596 for t, desc in topics:
1597 for t, desc in topics:
1597 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1598 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1598
1599
1599 if opt_output:
1600 if opt_output:
1600 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1601 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1601 for first, second in opt_output:
1602 for first, second in opt_output:
1602 if second:
1603 if second:
1603 second = util.wrap(second, opts_len + 3)
1604 second = util.wrap(second, opts_len + 3)
1604 ui.write(" %-*s %s\n" % (opts_len, first, second))
1605 ui.write(" %-*s %s\n" % (opts_len, first, second))
1605 else:
1606 else:
1606 ui.write("%s\n" % first)
1607 ui.write("%s\n" % first)
1607
1608
1608 def identify(ui, repo, source=None,
1609 def identify(ui, repo, source=None,
1609 rev=None, num=None, id=None, branch=None, tags=None):
1610 rev=None, num=None, id=None, branch=None, tags=None):
1610 """identify the working copy or specified revision
1611 """identify the working copy or specified revision
1611
1612
1612 With no revision, print a summary of the current state of the repository.
1613 With no revision, print a summary of the current state of the repository.
1613
1614
1614 Specifying a path to a repository root or Mercurial bundle will cause
1615 Specifying a path to a repository root or Mercurial bundle will cause
1615 lookup to operate on that repository/bundle.
1616 lookup to operate on that repository/bundle.
1616
1617
1617 This summary identifies the repository state using one or two parent hash
1618 This summary identifies the repository state using one or two parent hash
1618 identifiers, followed by a "+" if there are uncommitted changes in the
1619 identifiers, followed by a "+" if there are uncommitted changes in the
1619 working directory, a list of tags for this revision and a branch name for
1620 working directory, a list of tags for this revision and a branch name for
1620 non-default branches.
1621 non-default branches.
1621 """
1622 """
1622
1623
1623 if not repo and not source:
1624 if not repo and not source:
1624 raise util.Abort(_("There is no Mercurial repository here "
1625 raise util.Abort(_("There is no Mercurial repository here "
1625 "(.hg not found)"))
1626 "(.hg not found)"))
1626
1627
1627 hexfunc = ui.debugflag and hex or short
1628 hexfunc = ui.debugflag and hex or short
1628 default = not (num or id or branch or tags)
1629 default = not (num or id or branch or tags)
1629 output = []
1630 output = []
1630
1631
1631 revs = []
1632 revs = []
1632 if source:
1633 if source:
1633 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1634 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1634 repo = hg.repository(ui, source)
1635 repo = hg.repository(ui, source)
1635
1636
1636 if not repo.local():
1637 if not repo.local():
1637 if not rev and revs:
1638 if not rev and revs:
1638 rev = revs[0]
1639 rev = revs[0]
1639 if not rev:
1640 if not rev:
1640 rev = "tip"
1641 rev = "tip"
1641 if num or branch or tags:
1642 if num or branch or tags:
1642 raise util.Abort(
1643 raise util.Abort(
1643 "can't query remote revision number, branch, or tags")
1644 "can't query remote revision number, branch, or tags")
1644 output = [hexfunc(repo.lookup(rev))]
1645 output = [hexfunc(repo.lookup(rev))]
1645 elif not rev:
1646 elif not rev:
1646 ctx = repo[None]
1647 ctx = repo[None]
1647 parents = ctx.parents()
1648 parents = ctx.parents()
1648 changed = False
1649 changed = False
1649 if default or id or num:
1650 if default or id or num:
1650 changed = ctx.files() + ctx.deleted()
1651 changed = ctx.files() + ctx.deleted()
1651 if default or id:
1652 if default or id:
1652 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1653 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1653 (changed) and "+" or "")]
1654 (changed) and "+" or "")]
1654 if num:
1655 if num:
1655 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1656 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1656 (changed) and "+" or ""))
1657 (changed) and "+" or ""))
1657 else:
1658 else:
1658 ctx = repo[rev]
1659 ctx = repo[rev]
1659 if default or id:
1660 if default or id:
1660 output = [hexfunc(ctx.node())]
1661 output = [hexfunc(ctx.node())]
1661 if num:
1662 if num:
1662 output.append(str(ctx.rev()))
1663 output.append(str(ctx.rev()))
1663
1664
1664 if repo.local() and default and not ui.quiet:
1665 if repo.local() and default and not ui.quiet:
1665 b = encoding.tolocal(ctx.branch())
1666 b = encoding.tolocal(ctx.branch())
1666 if b != 'default':
1667 if b != 'default':
1667 output.append("(%s)" % b)
1668 output.append("(%s)" % b)
1668
1669
1669 # multiple tags for a single parent separated by '/'
1670 # multiple tags for a single parent separated by '/'
1670 t = "/".join(ctx.tags())
1671 t = "/".join(ctx.tags())
1671 if t:
1672 if t:
1672 output.append(t)
1673 output.append(t)
1673
1674
1674 if branch:
1675 if branch:
1675 output.append(encoding.tolocal(ctx.branch()))
1676 output.append(encoding.tolocal(ctx.branch()))
1676
1677
1677 if tags:
1678 if tags:
1678 output.extend(ctx.tags())
1679 output.extend(ctx.tags())
1679
1680
1680 ui.write("%s\n" % ' '.join(output))
1681 ui.write("%s\n" % ' '.join(output))
1681
1682
1682 def import_(ui, repo, patch1, *patches, **opts):
1683 def import_(ui, repo, patch1, *patches, **opts):
1683 """import an ordered set of patches
1684 """import an ordered set of patches
1684
1685
1685 Import a list of patches and commit them individually.
1686 Import a list of patches and commit them individually.
1686
1687
1687 If there are outstanding changes in the working directory, import will
1688 If there are outstanding changes in the working directory, import will
1688 abort unless given the -f/--force flag.
1689 abort unless given the -f/--force flag.
1689
1690
1690 You can import a patch straight from a mail message. Even patches as
1691 You can import a patch straight from a mail message. Even patches as
1691 attachments work (to use the body part, it must have type text/plain or
1692 attachments work (to use the body part, it must have type text/plain or
1692 text/x-patch). From and Subject headers of email message are used as
1693 text/x-patch). From and Subject headers of email message are used as
1693 default committer and commit message. All text/plain body parts before
1694 default committer and commit message. All text/plain body parts before
1694 first diff are added to commit message.
1695 first diff are added to commit message.
1695
1696
1696 If the imported patch was generated by hg export, user and description
1697 If the imported patch was generated by hg export, user and description
1697 from patch override values from message headers and body. Values given on
1698 from patch override values from message headers and body. Values given on
1698 command line with -m/--message and -u/--user override these.
1699 command line with -m/--message and -u/--user override these.
1699
1700
1700 If --exact is specified, import will set the working directory to the
1701 If --exact is specified, import will set the working directory to the
1701 parent of each patch before applying it, and will abort if the resulting
1702 parent of each patch before applying it, and will abort if the resulting
1702 changeset has a different ID than the one recorded in the patch. This may
1703 changeset has a different ID than the one recorded in the patch. This may
1703 happen due to character set problems or other deficiencies in the text
1704 happen due to character set problems or other deficiencies in the text
1704 patch format.
1705 patch format.
1705
1706
1706 With -s/--similarity, hg will attempt to discover renames and copies in
1707 With -s/--similarity, hg will attempt to discover renames and copies in
1707 the patch in the same way as 'addremove'.
1708 the patch in the same way as 'addremove'.
1708
1709
1709 To read a patch from standard input, use "-" as the patch name. If a URL
1710 To read a patch from standard input, use "-" as the patch name. If a URL
1710 is specified, the patch will be downloaded from it. See 'hg help dates'
1711 is specified, the patch will be downloaded from it. See 'hg help dates'
1711 for a list of formats valid for -d/--date.
1712 for a list of formats valid for -d/--date.
1712 """
1713 """
1713 patches = (patch1,) + patches
1714 patches = (patch1,) + patches
1714
1715
1715 date = opts.get('date')
1716 date = opts.get('date')
1716 if date:
1717 if date:
1717 opts['date'] = util.parsedate(date)
1718 opts['date'] = util.parsedate(date)
1718
1719
1719 try:
1720 try:
1720 sim = float(opts.get('similarity') or 0)
1721 sim = float(opts.get('similarity') or 0)
1721 except ValueError:
1722 except ValueError:
1722 raise util.Abort(_('similarity must be a number'))
1723 raise util.Abort(_('similarity must be a number'))
1723 if sim < 0 or sim > 100:
1724 if sim < 0 or sim > 100:
1724 raise util.Abort(_('similarity must be between 0 and 100'))
1725 raise util.Abort(_('similarity must be between 0 and 100'))
1725
1726
1726 if opts.get('exact') or not opts.get('force'):
1727 if opts.get('exact') or not opts.get('force'):
1727 cmdutil.bail_if_changed(repo)
1728 cmdutil.bail_if_changed(repo)
1728
1729
1729 d = opts["base"]
1730 d = opts["base"]
1730 strip = opts["strip"]
1731 strip = opts["strip"]
1731 wlock = lock = None
1732 wlock = lock = None
1732 try:
1733 try:
1733 wlock = repo.wlock()
1734 wlock = repo.wlock()
1734 lock = repo.lock()
1735 lock = repo.lock()
1735 for p in patches:
1736 for p in patches:
1736 pf = os.path.join(d, p)
1737 pf = os.path.join(d, p)
1737
1738
1738 if pf == '-':
1739 if pf == '-':
1739 ui.status(_("applying patch from stdin\n"))
1740 ui.status(_("applying patch from stdin\n"))
1740 pf = sys.stdin
1741 pf = sys.stdin
1741 else:
1742 else:
1742 ui.status(_("applying %s\n") % p)
1743 ui.status(_("applying %s\n") % p)
1743 pf = url.open(ui, pf)
1744 pf = url.open(ui, pf)
1744 data = patch.extract(ui, pf)
1745 data = patch.extract(ui, pf)
1745 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1746 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1746
1747
1747 if tmpname is None:
1748 if tmpname is None:
1748 raise util.Abort(_('no diffs found'))
1749 raise util.Abort(_('no diffs found'))
1749
1750
1750 try:
1751 try:
1751 cmdline_message = cmdutil.logmessage(opts)
1752 cmdline_message = cmdutil.logmessage(opts)
1752 if cmdline_message:
1753 if cmdline_message:
1753 # pickup the cmdline msg
1754 # pickup the cmdline msg
1754 message = cmdline_message
1755 message = cmdline_message
1755 elif message:
1756 elif message:
1756 # pickup the patch msg
1757 # pickup the patch msg
1757 message = message.strip()
1758 message = message.strip()
1758 else:
1759 else:
1759 # launch the editor
1760 # launch the editor
1760 message = None
1761 message = None
1761 ui.debug(_('message:\n%s\n') % message)
1762 ui.debug(_('message:\n%s\n') % message)
1762
1763
1763 wp = repo.parents()
1764 wp = repo.parents()
1764 if opts.get('exact'):
1765 if opts.get('exact'):
1765 if not nodeid or not p1:
1766 if not nodeid or not p1:
1766 raise util.Abort(_('not a Mercurial patch'))
1767 raise util.Abort(_('not a Mercurial patch'))
1767 p1 = repo.lookup(p1)
1768 p1 = repo.lookup(p1)
1768 p2 = repo.lookup(p2 or hex(nullid))
1769 p2 = repo.lookup(p2 or hex(nullid))
1769
1770
1770 if p1 != wp[0].node():
1771 if p1 != wp[0].node():
1771 hg.clean(repo, p1)
1772 hg.clean(repo, p1)
1772 repo.dirstate.setparents(p1, p2)
1773 repo.dirstate.setparents(p1, p2)
1773 elif p2:
1774 elif p2:
1774 try:
1775 try:
1775 p1 = repo.lookup(p1)
1776 p1 = repo.lookup(p1)
1776 p2 = repo.lookup(p2)
1777 p2 = repo.lookup(p2)
1777 if p1 == wp[0].node():
1778 if p1 == wp[0].node():
1778 repo.dirstate.setparents(p1, p2)
1779 repo.dirstate.setparents(p1, p2)
1779 except error.RepoError:
1780 except error.RepoError:
1780 pass
1781 pass
1781 if opts.get('exact') or opts.get('import_branch'):
1782 if opts.get('exact') or opts.get('import_branch'):
1782 repo.dirstate.setbranch(branch or 'default')
1783 repo.dirstate.setbranch(branch or 'default')
1783
1784
1784 files = {}
1785 files = {}
1785 try:
1786 try:
1786 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1787 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1787 files=files, eolmode=None)
1788 files=files, eolmode=None)
1788 finally:
1789 finally:
1789 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1790 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1790 if not opts.get('no_commit'):
1791 if not opts.get('no_commit'):
1791 m = cmdutil.matchfiles(repo, files or [])
1792 m = cmdutil.matchfiles(repo, files or [])
1792 n = repo.commit(message, opts.get('user') or user,
1793 n = repo.commit(message, opts.get('user') or user,
1793 opts.get('date') or date, match=m,
1794 opts.get('date') or date, match=m,
1794 editor=cmdutil.commiteditor)
1795 editor=cmdutil.commiteditor)
1795 if opts.get('exact'):
1796 if opts.get('exact'):
1796 if hex(n) != nodeid:
1797 if hex(n) != nodeid:
1797 repo.rollback()
1798 repo.rollback()
1798 raise util.Abort(_('patch is damaged'
1799 raise util.Abort(_('patch is damaged'
1799 ' or loses information'))
1800 ' or loses information'))
1800 # Force a dirstate write so that the next transaction
1801 # Force a dirstate write so that the next transaction
1801 # backups an up-do-date file.
1802 # backups an up-do-date file.
1802 repo.dirstate.write()
1803 repo.dirstate.write()
1803 finally:
1804 finally:
1804 os.unlink(tmpname)
1805 os.unlink(tmpname)
1805 finally:
1806 finally:
1806 release(lock, wlock)
1807 release(lock, wlock)
1807
1808
1808 def incoming(ui, repo, source="default", **opts):
1809 def incoming(ui, repo, source="default", **opts):
1809 """show new changesets found in source
1810 """show new changesets found in source
1810
1811
1811 Show new changesets found in the specified path/URL or the default pull
1812 Show new changesets found in the specified path/URL or the default pull
1812 location. These are the changesets that would have been pulled if a pull
1813 location. These are the changesets that would have been pulled if a pull
1813 at the time you issued this command.
1814 at the time you issued this command.
1814
1815
1815 For remote repository, using --bundle avoids downloading the changesets
1816 For remote repository, using --bundle avoids downloading the changesets
1816 twice if the incoming is followed by a pull.
1817 twice if the incoming is followed by a pull.
1817
1818
1818 See pull for valid source format details.
1819 See pull for valid source format details.
1819 """
1820 """
1820 limit = cmdutil.loglimit(opts)
1821 limit = cmdutil.loglimit(opts)
1821 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1822 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1822 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1823 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1823 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1824 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1824 if revs:
1825 if revs:
1825 revs = [other.lookup(rev) for rev in revs]
1826 revs = [other.lookup(rev) for rev in revs]
1826 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1827 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1827 force=opts["force"])
1828 force=opts["force"])
1828 if not incoming:
1829 if not incoming:
1829 try:
1830 try:
1830 os.unlink(opts["bundle"])
1831 os.unlink(opts["bundle"])
1831 except:
1832 except:
1832 pass
1833 pass
1833 ui.status(_("no changes found\n"))
1834 ui.status(_("no changes found\n"))
1834 return 1
1835 return 1
1835
1836
1836 cleanup = None
1837 cleanup = None
1837 try:
1838 try:
1838 fname = opts["bundle"]
1839 fname = opts["bundle"]
1839 if fname or not other.local():
1840 if fname or not other.local():
1840 # create a bundle (uncompressed if other repo is not local)
1841 # create a bundle (uncompressed if other repo is not local)
1841
1842
1842 if revs is None and other.capable('changegroupsubset'):
1843 if revs is None and other.capable('changegroupsubset'):
1843 revs = rheads
1844 revs = rheads
1844
1845
1845 if revs is None:
1846 if revs is None:
1846 cg = other.changegroup(incoming, "incoming")
1847 cg = other.changegroup(incoming, "incoming")
1847 else:
1848 else:
1848 cg = other.changegroupsubset(incoming, revs, 'incoming')
1849 cg = other.changegroupsubset(incoming, revs, 'incoming')
1849 bundletype = other.local() and "HG10BZ" or "HG10UN"
1850 bundletype = other.local() and "HG10BZ" or "HG10UN"
1850 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1851 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1851 # keep written bundle?
1852 # keep written bundle?
1852 if opts["bundle"]:
1853 if opts["bundle"]:
1853 cleanup = None
1854 cleanup = None
1854 if not other.local():
1855 if not other.local():
1855 # use the created uncompressed bundlerepo
1856 # use the created uncompressed bundlerepo
1856 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1857 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1857
1858
1858 o = other.changelog.nodesbetween(incoming, revs)[0]
1859 o = other.changelog.nodesbetween(incoming, revs)[0]
1859 if opts.get('newest_first'):
1860 if opts.get('newest_first'):
1860 o.reverse()
1861 o.reverse()
1861 displayer = cmdutil.show_changeset(ui, other, opts)
1862 displayer = cmdutil.show_changeset(ui, other, opts)
1862 count = 0
1863 count = 0
1863 for n in o:
1864 for n in o:
1864 if count >= limit:
1865 if count >= limit:
1865 break
1866 break
1866 parents = [p for p in other.changelog.parents(n) if p != nullid]
1867 parents = [p for p in other.changelog.parents(n) if p != nullid]
1867 if opts.get('no_merges') and len(parents) == 2:
1868 if opts.get('no_merges') and len(parents) == 2:
1868 continue
1869 continue
1869 count += 1
1870 count += 1
1870 displayer.show(other[n])
1871 displayer.show(other[n])
1871 finally:
1872 finally:
1872 if hasattr(other, 'close'):
1873 if hasattr(other, 'close'):
1873 other.close()
1874 other.close()
1874 if cleanup:
1875 if cleanup:
1875 os.unlink(cleanup)
1876 os.unlink(cleanup)
1876
1877
1877 def init(ui, dest=".", **opts):
1878 def init(ui, dest=".", **opts):
1878 """create a new repository in the given directory
1879 """create a new repository in the given directory
1879
1880
1880 Initialize a new repository in the given directory. If the given directory
1881 Initialize a new repository in the given directory. If the given directory
1881 does not exist, it will be created.
1882 does not exist, it will be created.
1882
1883
1883 If no directory is given, the current directory is used.
1884 If no directory is given, the current directory is used.
1884
1885
1885 It is possible to specify an ssh:// URL as the destination. See 'hg help
1886 It is possible to specify an ssh:// URL as the destination. See 'hg help
1886 urls' for more information.
1887 urls' for more information.
1887 """
1888 """
1888 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1889 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1889
1890
1890 def locate(ui, repo, *pats, **opts):
1891 def locate(ui, repo, *pats, **opts):
1891 """locate files matching specific patterns
1892 """locate files matching specific patterns
1892
1893
1893 Print files under Mercurial control in the working directory whose names
1894 Print files under Mercurial control in the working directory whose names
1894 match the given patterns.
1895 match the given patterns.
1895
1896
1896 By default, this command searches all directories in the working
1897 By default, this command searches all directories in the working
1897 directory. To search just the current directory and its subdirectories,
1898 directory. To search just the current directory and its subdirectories,
1898 use "--include .".
1899 use "--include .".
1899
1900
1900 If no patterns are given to match, this command prints the names of all
1901 If no patterns are given to match, this command prints the names of all
1901 files under Mercurial control in the working directory.
1902 files under Mercurial control in the working directory.
1902
1903
1903 If you want to feed the output of this command into the "xargs" command,
1904 If you want to feed the output of this command into the "xargs" command,
1904 use the -0 option to both this command and "xargs". This will avoid the
1905 use the -0 option to both this command and "xargs". This will avoid the
1905 problem of "xargs" treating single filenames that contain whitespace as
1906 problem of "xargs" treating single filenames that contain whitespace as
1906 multiple filenames.
1907 multiple filenames.
1907 """
1908 """
1908 end = opts.get('print0') and '\0' or '\n'
1909 end = opts.get('print0') and '\0' or '\n'
1909 rev = opts.get('rev') or None
1910 rev = opts.get('rev') or None
1910
1911
1911 ret = 1
1912 ret = 1
1912 m = cmdutil.match(repo, pats, opts, default='relglob')
1913 m = cmdutil.match(repo, pats, opts, default='relglob')
1913 m.bad = lambda x,y: False
1914 m.bad = lambda x,y: False
1914 for abs in repo[rev].walk(m):
1915 for abs in repo[rev].walk(m):
1915 if not rev and abs not in repo.dirstate:
1916 if not rev and abs not in repo.dirstate:
1916 continue
1917 continue
1917 if opts.get('fullpath'):
1918 if opts.get('fullpath'):
1918 ui.write(repo.wjoin(abs), end)
1919 ui.write(repo.wjoin(abs), end)
1919 else:
1920 else:
1920 ui.write(((pats and m.rel(abs)) or abs), end)
1921 ui.write(((pats and m.rel(abs)) or abs), end)
1921 ret = 0
1922 ret = 0
1922
1923
1923 return ret
1924 return ret
1924
1925
1925 def log(ui, repo, *pats, **opts):
1926 def log(ui, repo, *pats, **opts):
1926 """show revision history of entire repository or files
1927 """show revision history of entire repository or files
1927
1928
1928 Print the revision history of the specified files or the entire project.
1929 Print the revision history of the specified files or the entire project.
1929
1930
1930 File history is shown without following rename or copy history of files.
1931 File history is shown without following rename or copy history of files.
1931 Use -f/--follow with a filename to follow history across renames and
1932 Use -f/--follow with a filename to follow history across renames and
1932 copies. --follow without a filename will only show ancestors or
1933 copies. --follow without a filename will only show ancestors or
1933 descendants of the starting revision. --follow-first only follows the
1934 descendants of the starting revision. --follow-first only follows the
1934 first parent of merge revisions.
1935 first parent of merge revisions.
1935
1936
1936 If no revision range is specified, the default is tip:0 unless --follow is
1937 If no revision range is specified, the default is tip:0 unless --follow is
1937 set, in which case the working directory parent is used as the starting
1938 set, in which case the working directory parent is used as the starting
1938 revision.
1939 revision.
1939
1940
1940 See 'hg help dates' for a list of formats valid for -d/--date.
1941 See 'hg help dates' for a list of formats valid for -d/--date.
1941
1942
1942 By default this command prints revision number and changeset id, tags,
1943 By default this command prints revision number and changeset id, tags,
1943 non-trivial parents, user, date and time, and a summary for each commit.
1944 non-trivial parents, user, date and time, and a summary for each commit.
1944 When the -v/--verbose switch is used, the list of changed files and full
1945 When the -v/--verbose switch is used, the list of changed files and full
1945 commit message are shown.
1946 commit message are shown.
1946
1947
1947 NOTE: log -p/--patch may generate unexpected diff output for merge
1948 NOTE: log -p/--patch may generate unexpected diff output for merge
1948 changesets, as it will only compare the merge changeset against its first
1949 changesets, as it will only compare the merge changeset against its first
1949 parent. Also, only files different from BOTH parents will appear in
1950 parent. Also, only files different from BOTH parents will appear in
1950 files:.
1951 files:.
1951 """
1952 """
1952
1953
1953 get = util.cachefunc(lambda r: repo[r].changeset())
1954 get = util.cachefunc(lambda r: repo[r].changeset())
1954 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1955 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1955
1956
1956 limit = cmdutil.loglimit(opts)
1957 limit = cmdutil.loglimit(opts)
1957 count = 0
1958 count = 0
1958
1959
1959 if opts.get('copies') and opts.get('rev'):
1960 if opts.get('copies') and opts.get('rev'):
1960 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1961 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1961 else:
1962 else:
1962 endrev = len(repo)
1963 endrev = len(repo)
1963 rcache = {}
1964 rcache = {}
1964 ncache = {}
1965 ncache = {}
1965 def getrenamed(fn, rev):
1966 def getrenamed(fn, rev):
1966 '''looks up all renames for a file (up to endrev) the first
1967 '''looks up all renames for a file (up to endrev) the first
1967 time the file is given. It indexes on the changerev and only
1968 time the file is given. It indexes on the changerev and only
1968 parses the manifest if linkrev != changerev.
1969 parses the manifest if linkrev != changerev.
1969 Returns rename info for fn at changerev rev.'''
1970 Returns rename info for fn at changerev rev.'''
1970 if fn not in rcache:
1971 if fn not in rcache:
1971 rcache[fn] = {}
1972 rcache[fn] = {}
1972 ncache[fn] = {}
1973 ncache[fn] = {}
1973 fl = repo.file(fn)
1974 fl = repo.file(fn)
1974 for i in fl:
1975 for i in fl:
1975 node = fl.node(i)
1976 node = fl.node(i)
1976 lr = fl.linkrev(i)
1977 lr = fl.linkrev(i)
1977 renamed = fl.renamed(node)
1978 renamed = fl.renamed(node)
1978 rcache[fn][lr] = renamed
1979 rcache[fn][lr] = renamed
1979 if renamed:
1980 if renamed:
1980 ncache[fn][node] = renamed
1981 ncache[fn][node] = renamed
1981 if lr >= endrev:
1982 if lr >= endrev:
1982 break
1983 break
1983 if rev in rcache[fn]:
1984 if rev in rcache[fn]:
1984 return rcache[fn][rev]
1985 return rcache[fn][rev]
1985
1986
1986 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1987 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1987 # filectx logic.
1988 # filectx logic.
1988
1989
1989 try:
1990 try:
1990 return repo[rev][fn].renamed()
1991 return repo[rev][fn].renamed()
1991 except error.LookupError:
1992 except error.LookupError:
1992 pass
1993 pass
1993 return None
1994 return None
1994
1995
1995 df = False
1996 df = False
1996 if opts["date"]:
1997 if opts["date"]:
1997 df = util.matchdate(opts["date"])
1998 df = util.matchdate(opts["date"])
1998
1999
1999 only_branches = opts.get('only_branch')
2000 only_branches = opts.get('only_branch')
2000
2001
2001 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2002 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2002 for st, rev, fns in changeiter:
2003 for st, rev, fns in changeiter:
2003 if st == 'add':
2004 if st == 'add':
2004 parents = [p for p in repo.changelog.parentrevs(rev)
2005 parents = [p for p in repo.changelog.parentrevs(rev)
2005 if p != nullrev]
2006 if p != nullrev]
2006 if opts.get('no_merges') and len(parents) == 2:
2007 if opts.get('no_merges') and len(parents) == 2:
2007 continue
2008 continue
2008 if opts.get('only_merges') and len(parents) != 2:
2009 if opts.get('only_merges') and len(parents) != 2:
2009 continue
2010 continue
2010
2011
2011 if only_branches:
2012 if only_branches:
2012 revbranch = get(rev)[5]['branch']
2013 revbranch = get(rev)[5]['branch']
2013 if revbranch not in only_branches:
2014 if revbranch not in only_branches:
2014 continue
2015 continue
2015
2016
2016 if df:
2017 if df:
2017 changes = get(rev)
2018 changes = get(rev)
2018 if not df(changes[2][0]):
2019 if not df(changes[2][0]):
2019 continue
2020 continue
2020
2021
2021 if opts.get('keyword'):
2022 if opts.get('keyword'):
2022 changes = get(rev)
2023 changes = get(rev)
2023 miss = 0
2024 miss = 0
2024 for k in [kw.lower() for kw in opts['keyword']]:
2025 for k in [kw.lower() for kw in opts['keyword']]:
2025 if not (k in changes[1].lower() or
2026 if not (k in changes[1].lower() or
2026 k in changes[4].lower() or
2027 k in changes[4].lower() or
2027 k in " ".join(changes[3]).lower()):
2028 k in " ".join(changes[3]).lower()):
2028 miss = 1
2029 miss = 1
2029 break
2030 break
2030 if miss:
2031 if miss:
2031 continue
2032 continue
2032
2033
2033 if opts['user']:
2034 if opts['user']:
2034 changes = get(rev)
2035 changes = get(rev)
2035 if not [k for k in opts['user'] if k in changes[1]]:
2036 if not [k for k in opts['user'] if k in changes[1]]:
2036 continue
2037 continue
2037
2038
2038 copies = []
2039 copies = []
2039 if opts.get('copies') and rev:
2040 if opts.get('copies') and rev:
2040 for fn in get(rev)[3]:
2041 for fn in get(rev)[3]:
2041 rename = getrenamed(fn, rev)
2042 rename = getrenamed(fn, rev)
2042 if rename:
2043 if rename:
2043 copies.append((fn, rename[0]))
2044 copies.append((fn, rename[0]))
2044 displayer.show(context.changectx(repo, rev), copies=copies)
2045 displayer.show(context.changectx(repo, rev), copies=copies)
2045 elif st == 'iter':
2046 elif st == 'iter':
2046 if count == limit: break
2047 if count == limit: break
2047 if displayer.flush(rev):
2048 if displayer.flush(rev):
2048 count += 1
2049 count += 1
2049
2050
2050 def manifest(ui, repo, node=None, rev=None):
2051 def manifest(ui, repo, node=None, rev=None):
2051 """output the current or given revision of the project manifest
2052 """output the current or given revision of the project manifest
2052
2053
2053 Print a list of version controlled files for the given revision. If no
2054 Print a list of version controlled files for the given revision. If no
2054 revision is given, the first parent of the working directory is used, or
2055 revision is given, the first parent of the working directory is used, or
2055 the null revision if no revision is checked out.
2056 the null revision if no revision is checked out.
2056
2057
2057 With -v, print file permissions, symlink and executable bits.
2058 With -v, print file permissions, symlink and executable bits.
2058 With --debug, print file revision hashes.
2059 With --debug, print file revision hashes.
2059 """
2060 """
2060
2061
2061 if rev and node:
2062 if rev and node:
2062 raise util.Abort(_("please specify just one revision"))
2063 raise util.Abort(_("please specify just one revision"))
2063
2064
2064 if not node:
2065 if not node:
2065 node = rev
2066 node = rev
2066
2067
2067 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2068 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2068 ctx = repo[node]
2069 ctx = repo[node]
2069 for f in ctx:
2070 for f in ctx:
2070 if ui.debugflag:
2071 if ui.debugflag:
2071 ui.write("%40s " % hex(ctx.manifest()[f]))
2072 ui.write("%40s " % hex(ctx.manifest()[f]))
2072 if ui.verbose:
2073 if ui.verbose:
2073 ui.write(decor[ctx.flags(f)])
2074 ui.write(decor[ctx.flags(f)])
2074 ui.write("%s\n" % f)
2075 ui.write("%s\n" % f)
2075
2076
2076 def merge(ui, repo, node=None, **opts):
2077 def merge(ui, repo, node=None, **opts):
2077 """merge working directory with another revision
2078 """merge working directory with another revision
2078
2079
2079 The current working directory is updated with all changes made in the
2080 The current working directory is updated with all changes made in the
2080 requested revision since the last common predecessor revision.
2081 requested revision since the last common predecessor revision.
2081
2082
2082 Files that changed between either parent are marked as changed for the
2083 Files that changed between either parent are marked as changed for the
2083 next commit and a commit must be performed before any further updates to
2084 next commit and a commit must be performed before any further updates to
2084 the repository are allowed. The next commit will have two parents.
2085 the repository are allowed. The next commit will have two parents.
2085
2086
2086 If no revision is specified, the working directory's parent is a head
2087 If no revision is specified, the working directory's parent is a head
2087 revision, and the current branch contains exactly one other head, the
2088 revision, and the current branch contains exactly one other head, the
2088 other head is merged with by default. Otherwise, an explicit revision with
2089 other head is merged with by default. Otherwise, an explicit revision with
2089 which to merge with must be provided.
2090 which to merge with must be provided.
2090 """
2091 """
2091
2092
2092 if opts.get('rev') and node:
2093 if opts.get('rev') and node:
2093 raise util.Abort(_("please specify just one revision"))
2094 raise util.Abort(_("please specify just one revision"))
2094 if not node:
2095 if not node:
2095 node = opts.get('rev')
2096 node = opts.get('rev')
2096
2097
2097 if not node:
2098 if not node:
2098 branch = repo.changectx(None).branch()
2099 branch = repo.changectx(None).branch()
2099 bheads = repo.branchheads(branch)
2100 bheads = repo.branchheads(branch)
2100 if len(bheads) > 2:
2101 if len(bheads) > 2:
2101 raise util.Abort(_("branch '%s' has %d heads - "
2102 raise util.Abort(_("branch '%s' has %d heads - "
2102 "please merge with an explicit rev") %
2103 "please merge with an explicit rev") %
2103 (branch, len(bheads)))
2104 (branch, len(bheads)))
2104
2105
2105 parent = repo.dirstate.parents()[0]
2106 parent = repo.dirstate.parents()[0]
2106 if len(bheads) == 1:
2107 if len(bheads) == 1:
2107 if len(repo.heads()) > 1:
2108 if len(repo.heads()) > 1:
2108 raise util.Abort(_("branch '%s' has one head - "
2109 raise util.Abort(_("branch '%s' has one head - "
2109 "please merge with an explicit rev") %
2110 "please merge with an explicit rev") %
2110 branch)
2111 branch)
2111 msg = _('there is nothing to merge')
2112 msg = _('there is nothing to merge')
2112 if parent != repo.lookup(repo[None].branch()):
2113 if parent != repo.lookup(repo[None].branch()):
2113 msg = _('%s - use "hg update" instead') % msg
2114 msg = _('%s - use "hg update" instead') % msg
2114 raise util.Abort(msg)
2115 raise util.Abort(msg)
2115
2116
2116 if parent not in bheads:
2117 if parent not in bheads:
2117 raise util.Abort(_('working dir not at a head rev - '
2118 raise util.Abort(_('working dir not at a head rev - '
2118 'use "hg update" or merge with an explicit rev'))
2119 'use "hg update" or merge with an explicit rev'))
2119 node = parent == bheads[0] and bheads[-1] or bheads[0]
2120 node = parent == bheads[0] and bheads[-1] or bheads[0]
2120
2121
2121 if opts.get('preview'):
2122 if opts.get('preview'):
2122 p1 = repo['.']
2123 p1 = repo['.']
2123 p2 = repo[node]
2124 p2 = repo[node]
2124 common = p1.ancestor(p2)
2125 common = p1.ancestor(p2)
2125 roots, heads = [common.node()], [p2.node()]
2126 roots, heads = [common.node()], [p2.node()]
2126 displayer = cmdutil.show_changeset(ui, repo, opts)
2127 displayer = cmdutil.show_changeset(ui, repo, opts)
2127 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2128 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2128 displayer.show(repo[node])
2129 displayer.show(repo[node])
2129 return 0
2130 return 0
2130
2131
2131 return hg.merge(repo, node, force=opts.get('force'))
2132 return hg.merge(repo, node, force=opts.get('force'))
2132
2133
2133 def outgoing(ui, repo, dest=None, **opts):
2134 def outgoing(ui, repo, dest=None, **opts):
2134 """show changesets not found in destination
2135 """show changesets not found in destination
2135
2136
2136 Show changesets not found in the specified destination repository or the
2137 Show changesets not found in the specified destination repository or the
2137 default push location. These are the changesets that would be pushed if a
2138 default push location. These are the changesets that would be pushed if a
2138 push was requested.
2139 push was requested.
2139
2140
2140 See pull for valid destination format details.
2141 See pull for valid destination format details.
2141 """
2142 """
2142 limit = cmdutil.loglimit(opts)
2143 limit = cmdutil.loglimit(opts)
2143 dest, revs, checkout = hg.parseurl(
2144 dest, revs, checkout = hg.parseurl(
2144 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2145 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2145 if revs:
2146 if revs:
2146 revs = [repo.lookup(rev) for rev in revs]
2147 revs = [repo.lookup(rev) for rev in revs]
2147
2148
2148 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2149 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2149 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2150 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2150 o = repo.findoutgoing(other, force=opts.get('force'))
2151 o = repo.findoutgoing(other, force=opts.get('force'))
2151 if not o:
2152 if not o:
2152 ui.status(_("no changes found\n"))
2153 ui.status(_("no changes found\n"))
2153 return 1
2154 return 1
2154 o = repo.changelog.nodesbetween(o, revs)[0]
2155 o = repo.changelog.nodesbetween(o, revs)[0]
2155 if opts.get('newest_first'):
2156 if opts.get('newest_first'):
2156 o.reverse()
2157 o.reverse()
2157 displayer = cmdutil.show_changeset(ui, repo, opts)
2158 displayer = cmdutil.show_changeset(ui, repo, opts)
2158 count = 0
2159 count = 0
2159 for n in o:
2160 for n in o:
2160 if count >= limit:
2161 if count >= limit:
2161 break
2162 break
2162 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2163 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2163 if opts.get('no_merges') and len(parents) == 2:
2164 if opts.get('no_merges') and len(parents) == 2:
2164 continue
2165 continue
2165 count += 1
2166 count += 1
2166 displayer.show(repo[n])
2167 displayer.show(repo[n])
2167
2168
2168 def parents(ui, repo, file_=None, **opts):
2169 def parents(ui, repo, file_=None, **opts):
2169 """show the parents of the working directory or revision
2170 """show the parents of the working directory or revision
2170
2171
2171 Print the working directory's parent revisions. If a revision is given via
2172 Print the working directory's parent revisions. If a revision is given via
2172 -r/--rev, the parent of that revision will be printed. If a file argument
2173 -r/--rev, the parent of that revision will be printed. If a file argument
2173 is given, the revision in which the file was last changed (before the
2174 is given, the revision in which the file was last changed (before the
2174 working directory revision or the argument to --rev if given) is printed.
2175 working directory revision or the argument to --rev if given) is printed.
2175 """
2176 """
2176 rev = opts.get('rev')
2177 rev = opts.get('rev')
2177 if rev:
2178 if rev:
2178 ctx = repo[rev]
2179 ctx = repo[rev]
2179 else:
2180 else:
2180 ctx = repo[None]
2181 ctx = repo[None]
2181
2182
2182 if file_:
2183 if file_:
2183 m = cmdutil.match(repo, (file_,), opts)
2184 m = cmdutil.match(repo, (file_,), opts)
2184 if m.anypats() or len(m.files()) != 1:
2185 if m.anypats() or len(m.files()) != 1:
2185 raise util.Abort(_('can only specify an explicit filename'))
2186 raise util.Abort(_('can only specify an explicit filename'))
2186 file_ = m.files()[0]
2187 file_ = m.files()[0]
2187 filenodes = []
2188 filenodes = []
2188 for cp in ctx.parents():
2189 for cp in ctx.parents():
2189 if not cp:
2190 if not cp:
2190 continue
2191 continue
2191 try:
2192 try:
2192 filenodes.append(cp.filenode(file_))
2193 filenodes.append(cp.filenode(file_))
2193 except error.LookupError:
2194 except error.LookupError:
2194 pass
2195 pass
2195 if not filenodes:
2196 if not filenodes:
2196 raise util.Abort(_("'%s' not found in manifest!") % file_)
2197 raise util.Abort(_("'%s' not found in manifest!") % file_)
2197 fl = repo.file(file_)
2198 fl = repo.file(file_)
2198 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2199 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2199 else:
2200 else:
2200 p = [cp.node() for cp in ctx.parents()]
2201 p = [cp.node() for cp in ctx.parents()]
2201
2202
2202 displayer = cmdutil.show_changeset(ui, repo, opts)
2203 displayer = cmdutil.show_changeset(ui, repo, opts)
2203 for n in p:
2204 for n in p:
2204 if n != nullid:
2205 if n != nullid:
2205 displayer.show(repo[n])
2206 displayer.show(repo[n])
2206
2207
2207 def paths(ui, repo, search=None):
2208 def paths(ui, repo, search=None):
2208 """show aliases for remote repositories
2209 """show aliases for remote repositories
2209
2210
2210 Show definition of symbolic path name NAME. If no name is given, show
2211 Show definition of symbolic path name NAME. If no name is given, show
2211 definition of all available names.
2212 definition of all available names.
2212
2213
2213 Path names are defined in the [paths] section of /etc/mercurial/hgrc and
2214 Path names are defined in the [paths] section of /etc/mercurial/hgrc and
2214 $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2215 $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2215
2216
2216 See 'hg help urls' for more information.
2217 See 'hg help urls' for more information.
2217 """
2218 """
2218 if search:
2219 if search:
2219 for name, path in ui.configitems("paths"):
2220 for name, path in ui.configitems("paths"):
2220 if name == search:
2221 if name == search:
2221 ui.write("%s\n" % url.hidepassword(path))
2222 ui.write("%s\n" % url.hidepassword(path))
2222 return
2223 return
2223 ui.warn(_("not found!\n"))
2224 ui.warn(_("not found!\n"))
2224 return 1
2225 return 1
2225 else:
2226 else:
2226 for name, path in ui.configitems("paths"):
2227 for name, path in ui.configitems("paths"):
2227 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2228 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2228
2229
2229 def postincoming(ui, repo, modheads, optupdate, checkout):
2230 def postincoming(ui, repo, modheads, optupdate, checkout):
2230 if modheads == 0:
2231 if modheads == 0:
2231 return
2232 return
2232 if optupdate:
2233 if optupdate:
2233 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2234 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2234 return hg.update(repo, checkout)
2235 return hg.update(repo, checkout)
2235 else:
2236 else:
2236 ui.status(_("not updating, since new heads added\n"))
2237 ui.status(_("not updating, since new heads added\n"))
2237 if modheads > 1:
2238 if modheads > 1:
2238 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2239 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2239 else:
2240 else:
2240 ui.status(_("(run 'hg update' to get a working copy)\n"))
2241 ui.status(_("(run 'hg update' to get a working copy)\n"))
2241
2242
2242 def pull(ui, repo, source="default", **opts):
2243 def pull(ui, repo, source="default", **opts):
2243 """pull changes from the specified source
2244 """pull changes from the specified source
2244
2245
2245 Pull changes from a remote repository to a local one.
2246 Pull changes from a remote repository to a local one.
2246
2247
2247 This finds all changes from the repository at the specified path or URL
2248 This finds all changes from the repository at the specified path or URL
2248 and adds them to a local repository (the current one unless -R is
2249 and adds them to a local repository (the current one unless -R is
2249 specified). By default, this does not update the copy of the project in
2250 specified). By default, this does not update the copy of the project in
2250 the working directory.
2251 the working directory.
2251
2252
2252 Use hg incoming if you want to see what would have been added by a pull at
2253 Use hg incoming if you want to see what would have been added by a pull at
2253 the time you issued this command. If you then decide to added those
2254 the time you issued this command. If you then decide to added those
2254 changes to the repository, you should use pull -r X where X is the last
2255 changes to the repository, you should use pull -r X where X is the last
2255 changeset listed by hg incoming.
2256 changeset listed by hg incoming.
2256
2257
2257 If SOURCE is omitted, the 'default' path will be used. See 'hg help urls'
2258 If SOURCE is omitted, the 'default' path will be used. See 'hg help urls'
2258 for more information.
2259 for more information.
2259 """
2260 """
2260 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2261 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2261 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2262 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2262 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2263 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2263 if revs:
2264 if revs:
2264 try:
2265 try:
2265 revs = [other.lookup(rev) for rev in revs]
2266 revs = [other.lookup(rev) for rev in revs]
2266 except error.CapabilityError:
2267 except error.CapabilityError:
2267 err = _("Other repository doesn't support revision lookup, "
2268 err = _("Other repository doesn't support revision lookup, "
2268 "so a rev cannot be specified.")
2269 "so a rev cannot be specified.")
2269 raise util.Abort(err)
2270 raise util.Abort(err)
2270
2271
2271 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2272 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2272 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2273 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2273
2274
2274 def push(ui, repo, dest=None, **opts):
2275 def push(ui, repo, dest=None, **opts):
2275 """push changes to the specified destination
2276 """push changes to the specified destination
2276
2277
2277 Push changes from the local repository to the given destination.
2278 Push changes from the local repository to the given destination.
2278
2279
2279 This is the symmetrical operation for pull. It moves changes from the
2280 This is the symmetrical operation for pull. It moves changes from the
2280 current repository to a different one. If the destination is local this is
2281 current repository to a different one. If the destination is local this is
2281 identical to a pull in that directory from the current one.
2282 identical to a pull in that directory from the current one.
2282
2283
2283 By default, push will refuse to run if it detects the result would
2284 By default, push will refuse to run if it detects the result would
2284 increase the number of remote heads. This generally indicates the user
2285 increase the number of remote heads. This generally indicates the user
2285 forgot to pull and merge before pushing.
2286 forgot to pull and merge before pushing.
2286
2287
2287 If -r/--rev is used, the named revision and all its ancestors will be
2288 If -r/--rev is used, the named revision and all its ancestors will be
2288 pushed to the remote repository.
2289 pushed to the remote repository.
2289
2290
2290 Please see 'hg help urls' for important details about ssh:// URLs. If
2291 Please see 'hg help urls' for important details about ssh:// URLs. If
2291 DESTINATION is omitted, a default path will be used.
2292 DESTINATION is omitted, a default path will be used.
2292 """
2293 """
2293 dest, revs, checkout = hg.parseurl(
2294 dest, revs, checkout = hg.parseurl(
2294 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2295 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2295 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2296 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2296 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2297 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2297 if revs:
2298 if revs:
2298 revs = [repo.lookup(rev) for rev in revs]
2299 revs = [repo.lookup(rev) for rev in revs]
2299
2300
2300 # push subrepos depth-first for coherent ordering
2301 # push subrepos depth-first for coherent ordering
2301 c = repo['']
2302 c = repo['']
2302 subs = c.substate # only repos that are committed
2303 subs = c.substate # only repos that are committed
2303 for s in sorted(subs):
2304 for s in sorted(subs):
2304 c.sub(s).push(opts.get('force'))
2305 c.sub(s).push(opts.get('force'))
2305
2306
2306 r = repo.push(other, opts.get('force'), revs=revs)
2307 r = repo.push(other, opts.get('force'), revs=revs)
2307 return r == 0
2308 return r == 0
2308
2309
2309 def recover(ui, repo):
2310 def recover(ui, repo):
2310 """roll back an interrupted transaction
2311 """roll back an interrupted transaction
2311
2312
2312 Recover from an interrupted commit or pull.
2313 Recover from an interrupted commit or pull.
2313
2314
2314 This command tries to fix the repository status after an interrupted
2315 This command tries to fix the repository status after an interrupted
2315 operation. It should only be necessary when Mercurial suggests it.
2316 operation. It should only be necessary when Mercurial suggests it.
2316 """
2317 """
2317 if repo.recover():
2318 if repo.recover():
2318 return hg.verify(repo)
2319 return hg.verify(repo)
2319 return 1
2320 return 1
2320
2321
2321 def remove(ui, repo, *pats, **opts):
2322 def remove(ui, repo, *pats, **opts):
2322 """remove the specified files on the next commit
2323 """remove the specified files on the next commit
2323
2324
2324 Schedule the indicated files for removal from the repository.
2325 Schedule the indicated files for removal from the repository.
2325
2326
2326 This only removes files from the current branch, not from the entire
2327 This only removes files from the current branch, not from the entire
2327 project history. -A/--after can be used to remove only files that have
2328 project history. -A/--after can be used to remove only files that have
2328 already been deleted, -f/--force can be used to force deletion, and -Af
2329 already been deleted, -f/--force can be used to force deletion, and -Af
2329 can be used to remove files from the next revision without deleting them
2330 can be used to remove files from the next revision without deleting them
2330 from the working directory.
2331 from the working directory.
2331
2332
2332 The following table details the behavior of remove for different file
2333 The following table details the behavior of remove for different file
2333 states (columns) and option combinations (rows). The file states are Added
2334 states (columns) and option combinations (rows). The file states are Added
2334 [A], Clean [C], Modified [M] and Missing [!] (as reported by hg status).
2335 [A], Clean [C], Modified [M] and Missing [!] (as reported by hg status).
2335 The actions are Warn, Remove (from branch) and Delete (from disk)::
2336 The actions are Warn, Remove (from branch) and Delete (from disk)::
2336
2337
2337 A C M !
2338 A C M !
2338 none W RD W R
2339 none W RD W R
2339 -f R RD RD R
2340 -f R RD RD R
2340 -A W W W R
2341 -A W W W R
2341 -Af R R R R
2342 -Af R R R R
2342
2343
2343 This command schedules the files to be removed at the next commit. To undo
2344 This command schedules the files to be removed at the next commit. To undo
2344 a remove before that, see hg revert.
2345 a remove before that, see hg revert.
2345 """
2346 """
2346
2347
2347 after, force = opts.get('after'), opts.get('force')
2348 after, force = opts.get('after'), opts.get('force')
2348 if not pats and not after:
2349 if not pats and not after:
2349 raise util.Abort(_('no files specified'))
2350 raise util.Abort(_('no files specified'))
2350
2351
2351 m = cmdutil.match(repo, pats, opts)
2352 m = cmdutil.match(repo, pats, opts)
2352 s = repo.status(match=m, clean=True)
2353 s = repo.status(match=m, clean=True)
2353 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2354 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2354
2355
2355 for f in m.files():
2356 for f in m.files():
2356 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2357 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2357 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2358 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2358
2359
2359 def warn(files, reason):
2360 def warn(files, reason):
2360 for f in files:
2361 for f in files:
2361 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2362 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2362 % (m.rel(f), reason))
2363 % (m.rel(f), reason))
2363
2364
2364 if force:
2365 if force:
2365 remove, forget = modified + deleted + clean, added
2366 remove, forget = modified + deleted + clean, added
2366 elif after:
2367 elif after:
2367 remove, forget = deleted, []
2368 remove, forget = deleted, []
2368 warn(modified + added + clean, _('still exists'))
2369 warn(modified + added + clean, _('still exists'))
2369 else:
2370 else:
2370 remove, forget = deleted + clean, []
2371 remove, forget = deleted + clean, []
2371 warn(modified, _('is modified'))
2372 warn(modified, _('is modified'))
2372 warn(added, _('has been marked for add'))
2373 warn(added, _('has been marked for add'))
2373
2374
2374 for f in sorted(remove + forget):
2375 for f in sorted(remove + forget):
2375 if ui.verbose or not m.exact(f):
2376 if ui.verbose or not m.exact(f):
2376 ui.status(_('removing %s\n') % m.rel(f))
2377 ui.status(_('removing %s\n') % m.rel(f))
2377
2378
2378 repo.forget(forget)
2379 repo.forget(forget)
2379 repo.remove(remove, unlink=not after)
2380 repo.remove(remove, unlink=not after)
2380
2381
2381 def rename(ui, repo, *pats, **opts):
2382 def rename(ui, repo, *pats, **opts):
2382 """rename files; equivalent of copy + remove
2383 """rename files; equivalent of copy + remove
2383
2384
2384 Mark dest as copies of sources; mark sources for deletion. If dest is a
2385 Mark dest as copies of sources; mark sources for deletion. If dest is a
2385 directory, copies are put in that directory. If dest is a file, there can
2386 directory, copies are put in that directory. If dest is a file, there can
2386 only be one source.
2387 only be one source.
2387
2388
2388 By default, this command copies the contents of files as they exist in the
2389 By default, this command copies the contents of files as they exist in the
2389 working directory. If invoked with -A/--after, the operation is recorded,
2390 working directory. If invoked with -A/--after, the operation is recorded,
2390 but no copying is performed.
2391 but no copying is performed.
2391
2392
2392 This command takes effect at the next commit. To undo a rename before
2393 This command takes effect at the next commit. To undo a rename before
2393 that, see hg revert.
2394 that, see hg revert.
2394 """
2395 """
2395 wlock = repo.wlock(False)
2396 wlock = repo.wlock(False)
2396 try:
2397 try:
2397 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2398 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2398 finally:
2399 finally:
2399 wlock.release()
2400 wlock.release()
2400
2401
2401 def resolve(ui, repo, *pats, **opts):
2402 def resolve(ui, repo, *pats, **opts):
2402 """retry file merges from a merge or update
2403 """retry file merges from a merge or update
2403
2404
2404 This command will cleanly retry unresolved file merges using file
2405 This command will cleanly retry unresolved file merges using file
2405 revisions preserved from the last update or merge. To attempt to resolve
2406 revisions preserved from the last update or merge. To attempt to resolve
2406 all unresolved files, use the -a/--all switch.
2407 all unresolved files, use the -a/--all switch.
2407
2408
2408 If a conflict is resolved manually, please note that the changes will be
2409 If a conflict is resolved manually, please note that the changes will be
2409 overwritten if the merge is retried with resolve. The -m/--mark switch
2410 overwritten if the merge is retried with resolve. The -m/--mark switch
2410 should be used to mark the file as resolved.
2411 should be used to mark the file as resolved.
2411
2412
2412 This command also allows listing resolved files and manually indicating
2413 This command also allows listing resolved files and manually indicating
2413 whether or not files are resolved. All files must be marked as resolved
2414 whether or not files are resolved. All files must be marked as resolved
2414 before a commit is permitted.
2415 before a commit is permitted.
2415
2416
2416 The codes used to show the status of files are::
2417 The codes used to show the status of files are::
2417
2418
2418 U = unresolved
2419 U = unresolved
2419 R = resolved
2420 R = resolved
2420 """
2421 """
2421
2422
2422 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2423 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2423
2424
2424 if (show and (mark or unmark)) or (mark and unmark):
2425 if (show and (mark or unmark)) or (mark and unmark):
2425 raise util.Abort(_("too many options specified"))
2426 raise util.Abort(_("too many options specified"))
2426 if pats and all:
2427 if pats and all:
2427 raise util.Abort(_("can't specify --all and patterns"))
2428 raise util.Abort(_("can't specify --all and patterns"))
2428 if not (all or pats or show or mark or unmark):
2429 if not (all or pats or show or mark or unmark):
2429 raise util.Abort(_('no files or directories specified; '
2430 raise util.Abort(_('no files or directories specified; '
2430 'use --all to remerge all files'))
2431 'use --all to remerge all files'))
2431
2432
2432 ms = merge_.mergestate(repo)
2433 ms = merge_.mergestate(repo)
2433 m = cmdutil.match(repo, pats, opts)
2434 m = cmdutil.match(repo, pats, opts)
2434
2435
2435 for f in ms:
2436 for f in ms:
2436 if m(f):
2437 if m(f):
2437 if show:
2438 if show:
2438 ui.write("%s %s\n" % (ms[f].upper(), f))
2439 ui.write("%s %s\n" % (ms[f].upper(), f))
2439 elif mark:
2440 elif mark:
2440 ms.mark(f, "r")
2441 ms.mark(f, "r")
2441 elif unmark:
2442 elif unmark:
2442 ms.mark(f, "u")
2443 ms.mark(f, "u")
2443 else:
2444 else:
2444 wctx = repo[None]
2445 wctx = repo[None]
2445 mctx = wctx.parents()[-1]
2446 mctx = wctx.parents()[-1]
2446
2447
2447 # backup pre-resolve (merge uses .orig for its own purposes)
2448 # backup pre-resolve (merge uses .orig for its own purposes)
2448 a = repo.wjoin(f)
2449 a = repo.wjoin(f)
2449 util.copyfile(a, a + ".resolve")
2450 util.copyfile(a, a + ".resolve")
2450
2451
2451 # resolve file
2452 # resolve file
2452 ms.resolve(f, wctx, mctx)
2453 ms.resolve(f, wctx, mctx)
2453
2454
2454 # replace filemerge's .orig file with our resolve file
2455 # replace filemerge's .orig file with our resolve file
2455 util.rename(a + ".resolve", a + ".orig")
2456 util.rename(a + ".resolve", a + ".orig")
2456
2457
2457 def revert(ui, repo, *pats, **opts):
2458 def revert(ui, repo, *pats, **opts):
2458 """restore individual files or directories to an earlier state
2459 """restore individual files or directories to an earlier state
2459
2460
2460 (Use update -r to check out earlier revisions, revert does not change the
2461 (Use update -r to check out earlier revisions, revert does not change the
2461 working directory parents.)
2462 working directory parents.)
2462
2463
2463 With no revision specified, revert the named files or directories to the
2464 With no revision specified, revert the named files or directories to the
2464 contents they had in the parent of the working directory. This restores
2465 contents they had in the parent of the working directory. This restores
2465 the contents of the affected files to an unmodified state and unschedules
2466 the contents of the affected files to an unmodified state and unschedules
2466 adds, removes, copies, and renames. If the working directory has two
2467 adds, removes, copies, and renames. If the working directory has two
2467 parents, you must explicitly specify the revision to revert to.
2468 parents, you must explicitly specify the revision to revert to.
2468
2469
2469 Using the -r/--rev option, revert the given files or directories to their
2470 Using the -r/--rev option, revert the given files or directories to their
2470 contents as of a specific revision. This can be helpful to "roll back"
2471 contents as of a specific revision. This can be helpful to "roll back"
2471 some or all of an earlier change. See 'hg help dates' for a list of
2472 some or all of an earlier change. See 'hg help dates' for a list of
2472 formats valid for -d/--date.
2473 formats valid for -d/--date.
2473
2474
2474 Revert modifies the working directory. It does not commit any changes, or
2475 Revert modifies the working directory. It does not commit any changes, or
2475 change the parent of the working directory. If you revert to a revision
2476 change the parent of the working directory. If you revert to a revision
2476 other than the parent of the working directory, the reverted files will
2477 other than the parent of the working directory, the reverted files will
2477 thus appear modified afterwards.
2478 thus appear modified afterwards.
2478
2479
2479 If a file has been deleted, it is restored. If the executable mode of a
2480 If a file has been deleted, it is restored. If the executable mode of a
2480 file was changed, it is reset.
2481 file was changed, it is reset.
2481
2482
2482 If names are given, all files matching the names are reverted. If no
2483 If names are given, all files matching the names are reverted. If no
2483 arguments are given, no files are reverted.
2484 arguments are given, no files are reverted.
2484
2485
2485 Modified files are saved with a .orig suffix before reverting. To disable
2486 Modified files are saved with a .orig suffix before reverting. To disable
2486 these backups, use --no-backup.
2487 these backups, use --no-backup.
2487 """
2488 """
2488
2489
2489 if opts["date"]:
2490 if opts["date"]:
2490 if opts["rev"]:
2491 if opts["rev"]:
2491 raise util.Abort(_("you can't specify a revision and a date"))
2492 raise util.Abort(_("you can't specify a revision and a date"))
2492 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2493 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2493
2494
2494 if not pats and not opts.get('all'):
2495 if not pats and not opts.get('all'):
2495 raise util.Abort(_('no files or directories specified; '
2496 raise util.Abort(_('no files or directories specified; '
2496 'use --all to revert the whole repo'))
2497 'use --all to revert the whole repo'))
2497
2498
2498 parent, p2 = repo.dirstate.parents()
2499 parent, p2 = repo.dirstate.parents()
2499 if not opts.get('rev') and p2 != nullid:
2500 if not opts.get('rev') and p2 != nullid:
2500 raise util.Abort(_('uncommitted merge - please provide a '
2501 raise util.Abort(_('uncommitted merge - please provide a '
2501 'specific revision'))
2502 'specific revision'))
2502 ctx = repo[opts.get('rev')]
2503 ctx = repo[opts.get('rev')]
2503 node = ctx.node()
2504 node = ctx.node()
2504 mf = ctx.manifest()
2505 mf = ctx.manifest()
2505 if node == parent:
2506 if node == parent:
2506 pmf = mf
2507 pmf = mf
2507 else:
2508 else:
2508 pmf = None
2509 pmf = None
2509
2510
2510 # need all matching names in dirstate and manifest of target rev,
2511 # need all matching names in dirstate and manifest of target rev,
2511 # so have to walk both. do not print errors if files exist in one
2512 # so have to walk both. do not print errors if files exist in one
2512 # but not other.
2513 # but not other.
2513
2514
2514 names = {}
2515 names = {}
2515
2516
2516 wlock = repo.wlock()
2517 wlock = repo.wlock()
2517 try:
2518 try:
2518 # walk dirstate.
2519 # walk dirstate.
2519
2520
2520 m = cmdutil.match(repo, pats, opts)
2521 m = cmdutil.match(repo, pats, opts)
2521 m.bad = lambda x,y: False
2522 m.bad = lambda x,y: False
2522 for abs in repo.walk(m):
2523 for abs in repo.walk(m):
2523 names[abs] = m.rel(abs), m.exact(abs)
2524 names[abs] = m.rel(abs), m.exact(abs)
2524
2525
2525 # walk target manifest.
2526 # walk target manifest.
2526
2527
2527 def badfn(path, msg):
2528 def badfn(path, msg):
2528 if path in names:
2529 if path in names:
2529 return
2530 return
2530 path_ = path + '/'
2531 path_ = path + '/'
2531 for f in names:
2532 for f in names:
2532 if f.startswith(path_):
2533 if f.startswith(path_):
2533 return
2534 return
2534 ui.warn("%s: %s\n" % (m.rel(path), msg))
2535 ui.warn("%s: %s\n" % (m.rel(path), msg))
2535
2536
2536 m = cmdutil.match(repo, pats, opts)
2537 m = cmdutil.match(repo, pats, opts)
2537 m.bad = badfn
2538 m.bad = badfn
2538 for abs in repo[node].walk(m):
2539 for abs in repo[node].walk(m):
2539 if abs not in names:
2540 if abs not in names:
2540 names[abs] = m.rel(abs), m.exact(abs)
2541 names[abs] = m.rel(abs), m.exact(abs)
2541
2542
2542 m = cmdutil.matchfiles(repo, names)
2543 m = cmdutil.matchfiles(repo, names)
2543 changes = repo.status(match=m)[:4]
2544 changes = repo.status(match=m)[:4]
2544 modified, added, removed, deleted = map(set, changes)
2545 modified, added, removed, deleted = map(set, changes)
2545
2546
2546 # if f is a rename, also revert the source
2547 # if f is a rename, also revert the source
2547 cwd = repo.getcwd()
2548 cwd = repo.getcwd()
2548 for f in added:
2549 for f in added:
2549 src = repo.dirstate.copied(f)
2550 src = repo.dirstate.copied(f)
2550 if src and src not in names and repo.dirstate[src] == 'r':
2551 if src and src not in names and repo.dirstate[src] == 'r':
2551 removed.add(src)
2552 removed.add(src)
2552 names[src] = (repo.pathto(src, cwd), True)
2553 names[src] = (repo.pathto(src, cwd), True)
2553
2554
2554 def removeforget(abs):
2555 def removeforget(abs):
2555 if repo.dirstate[abs] == 'a':
2556 if repo.dirstate[abs] == 'a':
2556 return _('forgetting %s\n')
2557 return _('forgetting %s\n')
2557 return _('removing %s\n')
2558 return _('removing %s\n')
2558
2559
2559 revert = ([], _('reverting %s\n'))
2560 revert = ([], _('reverting %s\n'))
2560 add = ([], _('adding %s\n'))
2561 add = ([], _('adding %s\n'))
2561 remove = ([], removeforget)
2562 remove = ([], removeforget)
2562 undelete = ([], _('undeleting %s\n'))
2563 undelete = ([], _('undeleting %s\n'))
2563
2564
2564 disptable = (
2565 disptable = (
2565 # dispatch table:
2566 # dispatch table:
2566 # file state
2567 # file state
2567 # action if in target manifest
2568 # action if in target manifest
2568 # action if not in target manifest
2569 # action if not in target manifest
2569 # make backup if in target manifest
2570 # make backup if in target manifest
2570 # make backup if not in target manifest
2571 # make backup if not in target manifest
2571 (modified, revert, remove, True, True),
2572 (modified, revert, remove, True, True),
2572 (added, revert, remove, True, False),
2573 (added, revert, remove, True, False),
2573 (removed, undelete, None, False, False),
2574 (removed, undelete, None, False, False),
2574 (deleted, revert, remove, False, False),
2575 (deleted, revert, remove, False, False),
2575 )
2576 )
2576
2577
2577 for abs, (rel, exact) in sorted(names.items()):
2578 for abs, (rel, exact) in sorted(names.items()):
2578 mfentry = mf.get(abs)
2579 mfentry = mf.get(abs)
2579 target = repo.wjoin(abs)
2580 target = repo.wjoin(abs)
2580 def handle(xlist, dobackup):
2581 def handle(xlist, dobackup):
2581 xlist[0].append(abs)
2582 xlist[0].append(abs)
2582 if dobackup and not opts.get('no_backup') and util.lexists(target):
2583 if dobackup and not opts.get('no_backup') and util.lexists(target):
2583 bakname = "%s.orig" % rel
2584 bakname = "%s.orig" % rel
2584 ui.note(_('saving current version of %s as %s\n') %
2585 ui.note(_('saving current version of %s as %s\n') %
2585 (rel, bakname))
2586 (rel, bakname))
2586 if not opts.get('dry_run'):
2587 if not opts.get('dry_run'):
2587 util.copyfile(target, bakname)
2588 util.copyfile(target, bakname)
2588 if ui.verbose or not exact:
2589 if ui.verbose or not exact:
2589 msg = xlist[1]
2590 msg = xlist[1]
2590 if not isinstance(msg, basestring):
2591 if not isinstance(msg, basestring):
2591 msg = msg(abs)
2592 msg = msg(abs)
2592 ui.status(msg % rel)
2593 ui.status(msg % rel)
2593 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2594 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2594 if abs not in table: continue
2595 if abs not in table: continue
2595 # file has changed in dirstate
2596 # file has changed in dirstate
2596 if mfentry:
2597 if mfentry:
2597 handle(hitlist, backuphit)
2598 handle(hitlist, backuphit)
2598 elif misslist is not None:
2599 elif misslist is not None:
2599 handle(misslist, backupmiss)
2600 handle(misslist, backupmiss)
2600 break
2601 break
2601 else:
2602 else:
2602 if abs not in repo.dirstate:
2603 if abs not in repo.dirstate:
2603 if mfentry:
2604 if mfentry:
2604 handle(add, True)
2605 handle(add, True)
2605 elif exact:
2606 elif exact:
2606 ui.warn(_('file not managed: %s\n') % rel)
2607 ui.warn(_('file not managed: %s\n') % rel)
2607 continue
2608 continue
2608 # file has not changed in dirstate
2609 # file has not changed in dirstate
2609 if node == parent:
2610 if node == parent:
2610 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2611 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2611 continue
2612 continue
2612 if pmf is None:
2613 if pmf is None:
2613 # only need parent manifest in this unlikely case,
2614 # only need parent manifest in this unlikely case,
2614 # so do not read by default
2615 # so do not read by default
2615 pmf = repo[parent].manifest()
2616 pmf = repo[parent].manifest()
2616 if abs in pmf:
2617 if abs in pmf:
2617 if mfentry:
2618 if mfentry:
2618 # if version of file is same in parent and target
2619 # if version of file is same in parent and target
2619 # manifests, do nothing
2620 # manifests, do nothing
2620 if (pmf[abs] != mfentry or
2621 if (pmf[abs] != mfentry or
2621 pmf.flags(abs) != mf.flags(abs)):
2622 pmf.flags(abs) != mf.flags(abs)):
2622 handle(revert, False)
2623 handle(revert, False)
2623 else:
2624 else:
2624 handle(remove, False)
2625 handle(remove, False)
2625
2626
2626 if not opts.get('dry_run'):
2627 if not opts.get('dry_run'):
2627 def checkout(f):
2628 def checkout(f):
2628 fc = ctx[f]
2629 fc = ctx[f]
2629 repo.wwrite(f, fc.data(), fc.flags())
2630 repo.wwrite(f, fc.data(), fc.flags())
2630
2631
2631 audit_path = util.path_auditor(repo.root)
2632 audit_path = util.path_auditor(repo.root)
2632 for f in remove[0]:
2633 for f in remove[0]:
2633 if repo.dirstate[f] == 'a':
2634 if repo.dirstate[f] == 'a':
2634 repo.dirstate.forget(f)
2635 repo.dirstate.forget(f)
2635 continue
2636 continue
2636 audit_path(f)
2637 audit_path(f)
2637 try:
2638 try:
2638 util.unlink(repo.wjoin(f))
2639 util.unlink(repo.wjoin(f))
2639 except OSError:
2640 except OSError:
2640 pass
2641 pass
2641 repo.dirstate.remove(f)
2642 repo.dirstate.remove(f)
2642
2643
2643 normal = None
2644 normal = None
2644 if node == parent:
2645 if node == parent:
2645 # We're reverting to our parent. If possible, we'd like status
2646 # We're reverting to our parent. If possible, we'd like status
2646 # to report the file as clean. We have to use normallookup for
2647 # to report the file as clean. We have to use normallookup for
2647 # merges to avoid losing information about merged/dirty files.
2648 # merges to avoid losing information about merged/dirty files.
2648 if p2 != nullid:
2649 if p2 != nullid:
2649 normal = repo.dirstate.normallookup
2650 normal = repo.dirstate.normallookup
2650 else:
2651 else:
2651 normal = repo.dirstate.normal
2652 normal = repo.dirstate.normal
2652 for f in revert[0]:
2653 for f in revert[0]:
2653 checkout(f)
2654 checkout(f)
2654 if normal:
2655 if normal:
2655 normal(f)
2656 normal(f)
2656
2657
2657 for f in add[0]:
2658 for f in add[0]:
2658 checkout(f)
2659 checkout(f)
2659 repo.dirstate.add(f)
2660 repo.dirstate.add(f)
2660
2661
2661 normal = repo.dirstate.normallookup
2662 normal = repo.dirstate.normallookup
2662 if node == parent and p2 == nullid:
2663 if node == parent and p2 == nullid:
2663 normal = repo.dirstate.normal
2664 normal = repo.dirstate.normal
2664 for f in undelete[0]:
2665 for f in undelete[0]:
2665 checkout(f)
2666 checkout(f)
2666 normal(f)
2667 normal(f)
2667
2668
2668 finally:
2669 finally:
2669 wlock.release()
2670 wlock.release()
2670
2671
2671 def rollback(ui, repo):
2672 def rollback(ui, repo):
2672 """roll back the last transaction
2673 """roll back the last transaction
2673
2674
2674 This command should be used with care. There is only one level of
2675 This command should be used with care. There is only one level of
2675 rollback, and there is no way to undo a rollback. It will also restore the
2676 rollback, and there is no way to undo a rollback. It will also restore the
2676 dirstate at the time of the last transaction, losing any dirstate changes
2677 dirstate at the time of the last transaction, losing any dirstate changes
2677 since that time. This command does not alter the working directory.
2678 since that time. This command does not alter the working directory.
2678
2679
2679 Transactions are used to encapsulate the effects of all commands that
2680 Transactions are used to encapsulate the effects of all commands that
2680 create new changesets or propagate existing changesets into a repository.
2681 create new changesets or propagate existing changesets into a repository.
2681 For example, the following commands are transactional, and their effects
2682 For example, the following commands are transactional, and their effects
2682 can be rolled back::
2683 can be rolled back::
2683
2684
2684 commit
2685 commit
2685 import
2686 import
2686 pull
2687 pull
2687 push (with this repository as destination)
2688 push (with this repository as destination)
2688 unbundle
2689 unbundle
2689
2690
2690 This command is not intended for use on public repositories. Once changes
2691 This command is not intended for use on public repositories. Once changes
2691 are visible for pull by other users, rolling a transaction back locally is
2692 are visible for pull by other users, rolling a transaction back locally is
2692 ineffective (someone else may already have pulled the changes).
2693 ineffective (someone else may already have pulled the changes).
2693 Furthermore, a race is possible with readers of the repository; for
2694 Furthermore, a race is possible with readers of the repository; for
2694 example an in-progress pull from the repository may fail if a rollback is
2695 example an in-progress pull from the repository may fail if a rollback is
2695 performed.
2696 performed.
2696 """
2697 """
2697 repo.rollback()
2698 repo.rollback()
2698
2699
2699 def root(ui, repo):
2700 def root(ui, repo):
2700 """print the root (top) of the current working directory
2701 """print the root (top) of the current working directory
2701
2702
2702 Print the root directory of the current repository.
2703 Print the root directory of the current repository.
2703 """
2704 """
2704 ui.write(repo.root + "\n")
2705 ui.write(repo.root + "\n")
2705
2706
2706 def serve(ui, repo, **opts):
2707 def serve(ui, repo, **opts):
2707 """export the repository via HTTP
2708 """export the repository via HTTP
2708
2709
2709 Start a local HTTP repository browser and pull server.
2710 Start a local HTTP repository browser and pull server.
2710
2711
2711 By default, the server logs accesses to stdout and errors to stderr. Use
2712 By default, the server logs accesses to stdout and errors to stderr. Use
2712 the -A/--accesslog and -E/--errorlog options to log to files.
2713 the -A/--accesslog and -E/--errorlog options to log to files.
2713 """
2714 """
2714
2715
2715 if opts["stdio"]:
2716 if opts["stdio"]:
2716 if repo is None:
2717 if repo is None:
2717 raise error.RepoError(_("There is no Mercurial repository here"
2718 raise error.RepoError(_("There is no Mercurial repository here"
2718 " (.hg not found)"))
2719 " (.hg not found)"))
2719 s = sshserver.sshserver(ui, repo)
2720 s = sshserver.sshserver(ui, repo)
2720 s.serve_forever()
2721 s.serve_forever()
2721
2722
2722 baseui = repo and repo.baseui or ui
2723 baseui = repo and repo.baseui or ui
2723 optlist = ("name templates style address port prefix ipv6"
2724 optlist = ("name templates style address port prefix ipv6"
2724 " accesslog errorlog webdir_conf certificate encoding")
2725 " accesslog errorlog webdir_conf certificate encoding")
2725 for o in optlist.split():
2726 for o in optlist.split():
2726 if opts.get(o, None):
2727 if opts.get(o, None):
2727 baseui.setconfig("web", o, str(opts[o]))
2728 baseui.setconfig("web", o, str(opts[o]))
2728 if (repo is not None) and (repo.ui != baseui):
2729 if (repo is not None) and (repo.ui != baseui):
2729 repo.ui.setconfig("web", o, str(opts[o]))
2730 repo.ui.setconfig("web", o, str(opts[o]))
2730
2731
2731 if repo is None and not ui.config("web", "webdir_conf"):
2732 if repo is None and not ui.config("web", "webdir_conf"):
2732 raise error.RepoError(_("There is no Mercurial repository here"
2733 raise error.RepoError(_("There is no Mercurial repository here"
2733 " (.hg not found)"))
2734 " (.hg not found)"))
2734
2735
2735 class service(object):
2736 class service(object):
2736 def init(self):
2737 def init(self):
2737 util.set_signal_handler()
2738 util.set_signal_handler()
2738 self.httpd = server.create_server(baseui, repo)
2739 self.httpd = server.create_server(baseui, repo)
2739
2740
2740 if not ui.verbose: return
2741 if not ui.verbose: return
2741
2742
2742 if self.httpd.prefix:
2743 if self.httpd.prefix:
2743 prefix = self.httpd.prefix.strip('/') + '/'
2744 prefix = self.httpd.prefix.strip('/') + '/'
2744 else:
2745 else:
2745 prefix = ''
2746 prefix = ''
2746
2747
2747 port = ':%d' % self.httpd.port
2748 port = ':%d' % self.httpd.port
2748 if port == ':80':
2749 if port == ':80':
2749 port = ''
2750 port = ''
2750
2751
2751 bindaddr = self.httpd.addr
2752 bindaddr = self.httpd.addr
2752 if bindaddr == '0.0.0.0':
2753 if bindaddr == '0.0.0.0':
2753 bindaddr = '*'
2754 bindaddr = '*'
2754 elif ':' in bindaddr: # IPv6
2755 elif ':' in bindaddr: # IPv6
2755 bindaddr = '[%s]' % bindaddr
2756 bindaddr = '[%s]' % bindaddr
2756
2757
2757 fqaddr = self.httpd.fqaddr
2758 fqaddr = self.httpd.fqaddr
2758 if ':' in fqaddr:
2759 if ':' in fqaddr:
2759 fqaddr = '[%s]' % fqaddr
2760 fqaddr = '[%s]' % fqaddr
2760 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2761 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2761 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2762 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2762
2763
2763 def run(self):
2764 def run(self):
2764 self.httpd.serve_forever()
2765 self.httpd.serve_forever()
2765
2766
2766 service = service()
2767 service = service()
2767
2768
2768 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2769 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2769
2770
2770 def status(ui, repo, *pats, **opts):
2771 def status(ui, repo, *pats, **opts):
2771 """show changed files in the working directory
2772 """show changed files in the working directory
2772
2773
2773 Show status of files in the repository. If names are given, only files
2774 Show status of files in the repository. If names are given, only files
2774 that match are shown. Files that are clean or ignored or the source of a
2775 that match are shown. Files that are clean or ignored or the source of a
2775 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
2776 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
2776 -C/--copies or -A/--all are given. Unless options described with "show
2777 -C/--copies or -A/--all are given. Unless options described with "show
2777 only ..." are given, the options -mardu are used.
2778 only ..." are given, the options -mardu are used.
2778
2779
2779 Option -q/--quiet hides untracked (unknown and ignored) files unless
2780 Option -q/--quiet hides untracked (unknown and ignored) files unless
2780 explicitly requested with -u/--unknown or -i/--ignored.
2781 explicitly requested with -u/--unknown or -i/--ignored.
2781
2782
2782 NOTE: status may appear to disagree with diff if permissions have changed
2783 NOTE: status may appear to disagree with diff if permissions have changed
2783 or a merge has occurred. The standard diff format does not report
2784 or a merge has occurred. The standard diff format does not report
2784 permission changes and diff only reports changes relative to one merge
2785 permission changes and diff only reports changes relative to one merge
2785 parent.
2786 parent.
2786
2787
2787 If one revision is given, it is used as the base revision. If two
2788 If one revision is given, it is used as the base revision. If two
2788 revisions are given, the differences between them are shown.
2789 revisions are given, the differences between them are shown.
2789
2790
2790 The codes used to show the status of files are::
2791 The codes used to show the status of files are::
2791
2792
2792 M = modified
2793 M = modified
2793 A = added
2794 A = added
2794 R = removed
2795 R = removed
2795 C = clean
2796 C = clean
2796 ! = missing (deleted by non-hg command, but still tracked)
2797 ! = missing (deleted by non-hg command, but still tracked)
2797 ? = not tracked
2798 ? = not tracked
2798 I = ignored
2799 I = ignored
2799 = origin of the previous file listed as A (added)
2800 = origin of the previous file listed as A (added)
2800 """
2801 """
2801
2802
2802 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2803 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2803 cwd = (pats and repo.getcwd()) or ''
2804 cwd = (pats and repo.getcwd()) or ''
2804 end = opts.get('print0') and '\0' or '\n'
2805 end = opts.get('print0') and '\0' or '\n'
2805 copy = {}
2806 copy = {}
2806 states = 'modified added removed deleted unknown ignored clean'.split()
2807 states = 'modified added removed deleted unknown ignored clean'.split()
2807 show = [k for k in states if opts.get(k)]
2808 show = [k for k in states if opts.get(k)]
2808 if opts.get('all'):
2809 if opts.get('all'):
2809 show += ui.quiet and (states[:4] + ['clean']) or states
2810 show += ui.quiet and (states[:4] + ['clean']) or states
2810 if not show:
2811 if not show:
2811 show = ui.quiet and states[:4] or states[:5]
2812 show = ui.quiet and states[:4] or states[:5]
2812
2813
2813 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2814 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2814 'ignored' in show, 'clean' in show, 'unknown' in show)
2815 'ignored' in show, 'clean' in show, 'unknown' in show)
2815 changestates = zip(states, 'MAR!?IC', stat)
2816 changestates = zip(states, 'MAR!?IC', stat)
2816
2817
2817 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2818 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2818 ctxn = repo[nullid]
2819 ctxn = repo[nullid]
2819 ctx1 = repo[node1]
2820 ctx1 = repo[node1]
2820 ctx2 = repo[node2]
2821 ctx2 = repo[node2]
2821 added = stat[1]
2822 added = stat[1]
2822 if node2 is None:
2823 if node2 is None:
2823 added = stat[0] + stat[1] # merged?
2824 added = stat[0] + stat[1] # merged?
2824
2825
2825 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2826 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2826 if k in added:
2827 if k in added:
2827 copy[k] = v
2828 copy[k] = v
2828 elif v in added:
2829 elif v in added:
2829 copy[v] = k
2830 copy[v] = k
2830
2831
2831 for state, char, files in changestates:
2832 for state, char, files in changestates:
2832 if state in show:
2833 if state in show:
2833 format = "%s %%s%s" % (char, end)
2834 format = "%s %%s%s" % (char, end)
2834 if opts.get('no_status'):
2835 if opts.get('no_status'):
2835 format = "%%s%s" % end
2836 format = "%%s%s" % end
2836
2837
2837 for f in files:
2838 for f in files:
2838 ui.write(format % repo.pathto(f, cwd))
2839 ui.write(format % repo.pathto(f, cwd))
2839 if f in copy:
2840 if f in copy:
2840 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2841 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2841
2842
2842 def tag(ui, repo, name1, *names, **opts):
2843 def tag(ui, repo, name1, *names, **opts):
2843 """add one or more tags for the current or given revision
2844 """add one or more tags for the current or given revision
2844
2845
2845 Name a particular revision using <name>.
2846 Name a particular revision using <name>.
2846
2847
2847 Tags are used to name particular revisions of the repository and are very
2848 Tags are used to name particular revisions of the repository and are very
2848 useful to compare different revisions, to go back to significant earlier
2849 useful to compare different revisions, to go back to significant earlier
2849 versions or to mark branch points as releases, etc.
2850 versions or to mark branch points as releases, etc.
2850
2851
2851 If no revision is given, the parent of the working directory is used, or
2852 If no revision is given, the parent of the working directory is used, or
2852 tip if no revision is checked out.
2853 tip if no revision is checked out.
2853
2854
2854 To facilitate version control, distribution, and merging of tags, they are
2855 To facilitate version control, distribution, and merging of tags, they are
2855 stored as a file named ".hgtags" which is managed similarly to other
2856 stored as a file named ".hgtags" which is managed similarly to other
2856 project files and can be hand-edited if necessary. The file
2857 project files and can be hand-edited if necessary. The file
2857 '.hg/localtags' is used for local tags (not shared among repositories).
2858 '.hg/localtags' is used for local tags (not shared among repositories).
2858
2859
2859 See 'hg help dates' for a list of formats valid for -d/--date.
2860 See 'hg help dates' for a list of formats valid for -d/--date.
2860 """
2861 """
2861
2862
2862 rev_ = "."
2863 rev_ = "."
2863 names = (name1,) + names
2864 names = (name1,) + names
2864 if len(names) != len(set(names)):
2865 if len(names) != len(set(names)):
2865 raise util.Abort(_('tag names must be unique'))
2866 raise util.Abort(_('tag names must be unique'))
2866 for n in names:
2867 for n in names:
2867 if n in ['tip', '.', 'null']:
2868 if n in ['tip', '.', 'null']:
2868 raise util.Abort(_('the name \'%s\' is reserved') % n)
2869 raise util.Abort(_('the name \'%s\' is reserved') % n)
2869 if opts.get('rev') and opts.get('remove'):
2870 if opts.get('rev') and opts.get('remove'):
2870 raise util.Abort(_("--rev and --remove are incompatible"))
2871 raise util.Abort(_("--rev and --remove are incompatible"))
2871 if opts.get('rev'):
2872 if opts.get('rev'):
2872 rev_ = opts['rev']
2873 rev_ = opts['rev']
2873 message = opts.get('message')
2874 message = opts.get('message')
2874 if opts.get('remove'):
2875 if opts.get('remove'):
2875 expectedtype = opts.get('local') and 'local' or 'global'
2876 expectedtype = opts.get('local') and 'local' or 'global'
2876 for n in names:
2877 for n in names:
2877 if not repo.tagtype(n):
2878 if not repo.tagtype(n):
2878 raise util.Abort(_('tag \'%s\' does not exist') % n)
2879 raise util.Abort(_('tag \'%s\' does not exist') % n)
2879 if repo.tagtype(n) != expectedtype:
2880 if repo.tagtype(n) != expectedtype:
2880 if expectedtype == 'global':
2881 if expectedtype == 'global':
2881 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2882 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2882 else:
2883 else:
2883 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2884 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2884 rev_ = nullid
2885 rev_ = nullid
2885 if not message:
2886 if not message:
2886 message = _('Removed tag %s') % ', '.join(names)
2887 # we don't translate commit messages
2888 message = 'Removed tag %s' % ', '.join(names)
2887 elif not opts.get('force'):
2889 elif not opts.get('force'):
2888 for n in names:
2890 for n in names:
2889 if n in repo.tags():
2891 if n in repo.tags():
2890 raise util.Abort(_('tag \'%s\' already exists '
2892 raise util.Abort(_('tag \'%s\' already exists '
2891 '(use -f to force)') % n)
2893 '(use -f to force)') % n)
2892 if not rev_ and repo.dirstate.parents()[1] != nullid:
2894 if not rev_ and repo.dirstate.parents()[1] != nullid:
2893 raise util.Abort(_('uncommitted merge - please provide a '
2895 raise util.Abort(_('uncommitted merge - please provide a '
2894 'specific revision'))
2896 'specific revision'))
2895 r = repo[rev_].node()
2897 r = repo[rev_].node()
2896
2898
2897 if not message:
2899 if not message:
2898 message = (_('Added tag %s for changeset %s') %
2900 # we don't translate commit messages
2901 message = ('Added tag %s for changeset %s' %
2899 (', '.join(names), short(r)))
2902 (', '.join(names), short(r)))
2900
2903
2901 date = opts.get('date')
2904 date = opts.get('date')
2902 if date:
2905 if date:
2903 date = util.parsedate(date)
2906 date = util.parsedate(date)
2904
2907
2905 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2908 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2906
2909
2907 def tags(ui, repo):
2910 def tags(ui, repo):
2908 """list repository tags
2911 """list repository tags
2909
2912
2910 This lists both regular and local tags. When the -v/--verbose switch is
2913 This lists both regular and local tags. When the -v/--verbose switch is
2911 used, a third column "local" is printed for local tags.
2914 used, a third column "local" is printed for local tags.
2912 """
2915 """
2913
2916
2914 hexfunc = ui.debugflag and hex or short
2917 hexfunc = ui.debugflag and hex or short
2915 tagtype = ""
2918 tagtype = ""
2916
2919
2917 for t, n in reversed(repo.tagslist()):
2920 for t, n in reversed(repo.tagslist()):
2918 if ui.quiet:
2921 if ui.quiet:
2919 ui.write("%s\n" % t)
2922 ui.write("%s\n" % t)
2920 continue
2923 continue
2921
2924
2922 try:
2925 try:
2923 hn = hexfunc(n)
2926 hn = hexfunc(n)
2924 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2927 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2925 except error.LookupError:
2928 except error.LookupError:
2926 r = " ?:%s" % hn
2929 r = " ?:%s" % hn
2927 else:
2930 else:
2928 spaces = " " * (30 - encoding.colwidth(t))
2931 spaces = " " * (30 - encoding.colwidth(t))
2929 if ui.verbose:
2932 if ui.verbose:
2930 if repo.tagtype(t) == 'local':
2933 if repo.tagtype(t) == 'local':
2931 tagtype = " local"
2934 tagtype = " local"
2932 else:
2935 else:
2933 tagtype = ""
2936 tagtype = ""
2934 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2937 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2935
2938
2936 def tip(ui, repo, **opts):
2939 def tip(ui, repo, **opts):
2937 """show the tip revision
2940 """show the tip revision
2938
2941
2939 The tip revision (usually just called the tip) is the changeset most
2942 The tip revision (usually just called the tip) is the changeset most
2940 recently added to the repository (and therefore the most recently changed
2943 recently added to the repository (and therefore the most recently changed
2941 head).
2944 head).
2942
2945
2943 If you have just made a commit, that commit will be the tip. If you have
2946 If you have just made a commit, that commit will be the tip. If you have
2944 just pulled changes from another repository, the tip of that repository
2947 just pulled changes from another repository, the tip of that repository
2945 becomes the current tip. The "tip" tag is special and cannot be renamed or
2948 becomes the current tip. The "tip" tag is special and cannot be renamed or
2946 assigned to a different changeset.
2949 assigned to a different changeset.
2947 """
2950 """
2948 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2951 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2949
2952
2950 def unbundle(ui, repo, fname1, *fnames, **opts):
2953 def unbundle(ui, repo, fname1, *fnames, **opts):
2951 """apply one or more changegroup files
2954 """apply one or more changegroup files
2952
2955
2953 Apply one or more compressed changegroup files generated by the bundle
2956 Apply one or more compressed changegroup files generated by the bundle
2954 command.
2957 command.
2955 """
2958 """
2956 fnames = (fname1,) + fnames
2959 fnames = (fname1,) + fnames
2957
2960
2958 lock = repo.lock()
2961 lock = repo.lock()
2959 try:
2962 try:
2960 for fname in fnames:
2963 for fname in fnames:
2961 f = url.open(ui, fname)
2964 f = url.open(ui, fname)
2962 gen = changegroup.readbundle(f, fname)
2965 gen = changegroup.readbundle(f, fname)
2963 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2966 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2964 finally:
2967 finally:
2965 lock.release()
2968 lock.release()
2966
2969
2967 return postincoming(ui, repo, modheads, opts.get('update'), None)
2970 return postincoming(ui, repo, modheads, opts.get('update'), None)
2968
2971
2969 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
2972 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
2970 """update working directory
2973 """update working directory
2971
2974
2972 Update the repository's working directory to the specified revision, or
2975 Update the repository's working directory to the specified revision, or
2973 the tip of the current branch if none is specified. Use null as the
2976 the tip of the current branch if none is specified. Use null as the
2974 revision to remove the working copy (like 'hg clone -U').
2977 revision to remove the working copy (like 'hg clone -U').
2975
2978
2976 When the working directory contains no uncommitted changes, it will be
2979 When the working directory contains no uncommitted changes, it will be
2977 replaced by the state of the requested revision from the repository. When
2980 replaced by the state of the requested revision from the repository. When
2978 the requested revision is on a different branch, the working directory
2981 the requested revision is on a different branch, the working directory
2979 will additionally be switched to that branch.
2982 will additionally be switched to that branch.
2980
2983
2981 When there are uncommitted changes, use option -C/--clean to discard them,
2984 When there are uncommitted changes, use option -C/--clean to discard them,
2982 forcibly replacing the state of the working directory with the requested
2985 forcibly replacing the state of the working directory with the requested
2983 revision. Alternately, use -c/--check to abort.
2986 revision. Alternately, use -c/--check to abort.
2984
2987
2985 When there are uncommitted changes and option -C/--clean is not used, and
2988 When there are uncommitted changes and option -C/--clean is not used, and
2986 the parent revision and requested revision are on the same branch, and one
2989 the parent revision and requested revision are on the same branch, and one
2987 of them is an ancestor of the other, then the new working directory will
2990 of them is an ancestor of the other, then the new working directory will
2988 contain the requested revision merged with the uncommitted changes.
2991 contain the requested revision merged with the uncommitted changes.
2989 Otherwise, the update will fail with a suggestion to use 'merge' or
2992 Otherwise, the update will fail with a suggestion to use 'merge' or
2990 'update -C' instead.
2993 'update -C' instead.
2991
2994
2992 If you want to update just one file to an older revision, use revert.
2995 If you want to update just one file to an older revision, use revert.
2993
2996
2994 See 'hg help dates' for a list of formats valid for -d/--date.
2997 See 'hg help dates' for a list of formats valid for -d/--date.
2995 """
2998 """
2996 if rev and node:
2999 if rev and node:
2997 raise util.Abort(_("please specify just one revision"))
3000 raise util.Abort(_("please specify just one revision"))
2998
3001
2999 if not rev:
3002 if not rev:
3000 rev = node
3003 rev = node
3001
3004
3002 if not clean and check:
3005 if not clean and check:
3003 # we could use dirty() but we can ignore merge and branch trivia
3006 # we could use dirty() but we can ignore merge and branch trivia
3004 c = repo[None]
3007 c = repo[None]
3005 if c.modified() or c.added() or c.removed():
3008 if c.modified() or c.added() or c.removed():
3006 raise util.Abort(_("uncommitted local changes"))
3009 raise util.Abort(_("uncommitted local changes"))
3007
3010
3008 if date:
3011 if date:
3009 if rev:
3012 if rev:
3010 raise util.Abort(_("you can't specify a revision and a date"))
3013 raise util.Abort(_("you can't specify a revision and a date"))
3011 rev = cmdutil.finddate(ui, repo, date)
3014 rev = cmdutil.finddate(ui, repo, date)
3012
3015
3013 if clean:
3016 if clean:
3014 return hg.clean(repo, rev)
3017 return hg.clean(repo, rev)
3015 else:
3018 else:
3016 return hg.update(repo, rev)
3019 return hg.update(repo, rev)
3017
3020
3018 def verify(ui, repo):
3021 def verify(ui, repo):
3019 """verify the integrity of the repository
3022 """verify the integrity of the repository
3020
3023
3021 Verify the integrity of the current repository.
3024 Verify the integrity of the current repository.
3022
3025
3023 This will perform an extensive check of the repository's integrity,
3026 This will perform an extensive check of the repository's integrity,
3024 validating the hashes and checksums of each entry in the changelog,
3027 validating the hashes and checksums of each entry in the changelog,
3025 manifest, and tracked files, as well as the integrity of their crosslinks
3028 manifest, and tracked files, as well as the integrity of their crosslinks
3026 and indices.
3029 and indices.
3027 """
3030 """
3028 return hg.verify(repo)
3031 return hg.verify(repo)
3029
3032
3030 def version_(ui):
3033 def version_(ui):
3031 """output version and copyright information"""
3034 """output version and copyright information"""
3032 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3035 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3033 % util.version())
3036 % util.version())
3034 ui.status(_(
3037 ui.status(_(
3035 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3038 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3036 "This is free software; see the source for copying conditions. "
3039 "This is free software; see the source for copying conditions. "
3037 "There is NO\nwarranty; "
3040 "There is NO\nwarranty; "
3038 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3041 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3039 ))
3042 ))
3040
3043
3041 # Command options and aliases are listed here, alphabetically
3044 # Command options and aliases are listed here, alphabetically
3042
3045
3043 globalopts = [
3046 globalopts = [
3044 ('R', 'repository', '',
3047 ('R', 'repository', '',
3045 _('repository root directory or symbolic path name')),
3048 _('repository root directory or symbolic path name')),
3046 ('', 'cwd', '', _('change working directory')),
3049 ('', 'cwd', '', _('change working directory')),
3047 ('y', 'noninteractive', None,
3050 ('y', 'noninteractive', None,
3048 _('do not prompt, assume \'yes\' for any required answers')),
3051 _('do not prompt, assume \'yes\' for any required answers')),
3049 ('q', 'quiet', None, _('suppress output')),
3052 ('q', 'quiet', None, _('suppress output')),
3050 ('v', 'verbose', None, _('enable additional output')),
3053 ('v', 'verbose', None, _('enable additional output')),
3051 ('', 'config', [], _('set/override config option')),
3054 ('', 'config', [], _('set/override config option')),
3052 ('', 'debug', None, _('enable debugging output')),
3055 ('', 'debug', None, _('enable debugging output')),
3053 ('', 'debugger', None, _('start debugger')),
3056 ('', 'debugger', None, _('start debugger')),
3054 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3057 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3055 ('', 'encodingmode', encoding.encodingmode,
3058 ('', 'encodingmode', encoding.encodingmode,
3056 _('set the charset encoding mode')),
3059 _('set the charset encoding mode')),
3057 ('', 'traceback', None, _('print traceback on exception')),
3060 ('', 'traceback', None, _('print traceback on exception')),
3058 ('', 'time', None, _('time how long the command takes')),
3061 ('', 'time', None, _('time how long the command takes')),
3059 ('', 'profile', None, _('print command execution profile')),
3062 ('', 'profile', None, _('print command execution profile')),
3060 ('', 'version', None, _('output version information and exit')),
3063 ('', 'version', None, _('output version information and exit')),
3061 ('h', 'help', None, _('display help and exit')),
3064 ('h', 'help', None, _('display help and exit')),
3062 ]
3065 ]
3063
3066
3064 dryrunopts = [('n', 'dry-run', None,
3067 dryrunopts = [('n', 'dry-run', None,
3065 _('do not perform actions, just print output'))]
3068 _('do not perform actions, just print output'))]
3066
3069
3067 remoteopts = [
3070 remoteopts = [
3068 ('e', 'ssh', '', _('specify ssh command to use')),
3071 ('e', 'ssh', '', _('specify ssh command to use')),
3069 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3072 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3070 ]
3073 ]
3071
3074
3072 walkopts = [
3075 walkopts = [
3073 ('I', 'include', [], _('include names matching the given patterns')),
3076 ('I', 'include', [], _('include names matching the given patterns')),
3074 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3077 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3075 ]
3078 ]
3076
3079
3077 commitopts = [
3080 commitopts = [
3078 ('m', 'message', '', _('use <text> as commit message')),
3081 ('m', 'message', '', _('use <text> as commit message')),
3079 ('l', 'logfile', '', _('read commit message from <file>')),
3082 ('l', 'logfile', '', _('read commit message from <file>')),
3080 ]
3083 ]
3081
3084
3082 commitopts2 = [
3085 commitopts2 = [
3083 ('d', 'date', '', _('record datecode as commit date')),
3086 ('d', 'date', '', _('record datecode as commit date')),
3084 ('u', 'user', '', _('record the specified user as committer')),
3087 ('u', 'user', '', _('record the specified user as committer')),
3085 ]
3088 ]
3086
3089
3087 templateopts = [
3090 templateopts = [
3088 ('', 'style', '', _('display using template map file')),
3091 ('', 'style', '', _('display using template map file')),
3089 ('', 'template', '', _('display with template')),
3092 ('', 'template', '', _('display with template')),
3090 ]
3093 ]
3091
3094
3092 logopts = [
3095 logopts = [
3093 ('p', 'patch', None, _('show patch')),
3096 ('p', 'patch', None, _('show patch')),
3094 ('g', 'git', None, _('use git extended diff format')),
3097 ('g', 'git', None, _('use git extended diff format')),
3095 ('l', 'limit', '', _('limit number of changes displayed')),
3098 ('l', 'limit', '', _('limit number of changes displayed')),
3096 ('M', 'no-merges', None, _('do not show merges')),
3099 ('M', 'no-merges', None, _('do not show merges')),
3097 ] + templateopts
3100 ] + templateopts
3098
3101
3099 diffopts = [
3102 diffopts = [
3100 ('a', 'text', None, _('treat all files as text')),
3103 ('a', 'text', None, _('treat all files as text')),
3101 ('g', 'git', None, _('use git extended diff format')),
3104 ('g', 'git', None, _('use git extended diff format')),
3102 ('', 'nodates', None, _("don't include dates in diff headers"))
3105 ('', 'nodates', None, _("don't include dates in diff headers"))
3103 ]
3106 ]
3104
3107
3105 diffopts2 = [
3108 diffopts2 = [
3106 ('p', 'show-function', None, _('show which function each change is in')),
3109 ('p', 'show-function', None, _('show which function each change is in')),
3107 ('w', 'ignore-all-space', None,
3110 ('w', 'ignore-all-space', None,
3108 _('ignore white space when comparing lines')),
3111 _('ignore white space when comparing lines')),
3109 ('b', 'ignore-space-change', None,
3112 ('b', 'ignore-space-change', None,
3110 _('ignore changes in the amount of white space')),
3113 _('ignore changes in the amount of white space')),
3111 ('B', 'ignore-blank-lines', None,
3114 ('B', 'ignore-blank-lines', None,
3112 _('ignore changes whose lines are all blank')),
3115 _('ignore changes whose lines are all blank')),
3113 ('U', 'unified', '', _('number of lines of context to show'))
3116 ('U', 'unified', '', _('number of lines of context to show'))
3114 ]
3117 ]
3115
3118
3116 similarityopts = [
3119 similarityopts = [
3117 ('s', 'similarity', '',
3120 ('s', 'similarity', '',
3118 _('guess renamed files by similarity (0<=s<=100)'))
3121 _('guess renamed files by similarity (0<=s<=100)'))
3119 ]
3122 ]
3120
3123
3121 table = {
3124 table = {
3122 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3125 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3123 "addremove":
3126 "addremove":
3124 (addremove, similarityopts + walkopts + dryrunopts,
3127 (addremove, similarityopts + walkopts + dryrunopts,
3125 _('[OPTION]... [FILE]...')),
3128 _('[OPTION]... [FILE]...')),
3126 "^annotate|blame":
3129 "^annotate|blame":
3127 (annotate,
3130 (annotate,
3128 [('r', 'rev', '', _('annotate the specified revision')),
3131 [('r', 'rev', '', _('annotate the specified revision')),
3129 ('f', 'follow', None, _('follow file copies and renames')),
3132 ('f', 'follow', None, _('follow file copies and renames')),
3130 ('a', 'text', None, _('treat all files as text')),
3133 ('a', 'text', None, _('treat all files as text')),
3131 ('u', 'user', None, _('list the author (long with -v)')),
3134 ('u', 'user', None, _('list the author (long with -v)')),
3132 ('d', 'date', None, _('list the date (short with -q)')),
3135 ('d', 'date', None, _('list the date (short with -q)')),
3133 ('n', 'number', None, _('list the revision number (default)')),
3136 ('n', 'number', None, _('list the revision number (default)')),
3134 ('c', 'changeset', None, _('list the changeset')),
3137 ('c', 'changeset', None, _('list the changeset')),
3135 ('l', 'line-number', None,
3138 ('l', 'line-number', None,
3136 _('show line number at the first appearance'))
3139 _('show line number at the first appearance'))
3137 ] + walkopts,
3140 ] + walkopts,
3138 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3141 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3139 "archive":
3142 "archive":
3140 (archive,
3143 (archive,
3141 [('', 'no-decode', None, _('do not pass files through decoders')),
3144 [('', 'no-decode', None, _('do not pass files through decoders')),
3142 ('p', 'prefix', '', _('directory prefix for files in archive')),
3145 ('p', 'prefix', '', _('directory prefix for files in archive')),
3143 ('r', 'rev', '', _('revision to distribute')),
3146 ('r', 'rev', '', _('revision to distribute')),
3144 ('t', 'type', '', _('type of distribution to create')),
3147 ('t', 'type', '', _('type of distribution to create')),
3145 ] + walkopts,
3148 ] + walkopts,
3146 _('[OPTION]... DEST')),
3149 _('[OPTION]... DEST')),
3147 "backout":
3150 "backout":
3148 (backout,
3151 (backout,
3149 [('', 'merge', None,
3152 [('', 'merge', None,
3150 _('merge with old dirstate parent after backout')),
3153 _('merge with old dirstate parent after backout')),
3151 ('', 'parent', '', _('parent to choose when backing out merge')),
3154 ('', 'parent', '', _('parent to choose when backing out merge')),
3152 ('r', 'rev', '', _('revision to backout')),
3155 ('r', 'rev', '', _('revision to backout')),
3153 ] + walkopts + commitopts + commitopts2,
3156 ] + walkopts + commitopts + commitopts2,
3154 _('[OPTION]... [-r] REV')),
3157 _('[OPTION]... [-r] REV')),
3155 "bisect":
3158 "bisect":
3156 (bisect,
3159 (bisect,
3157 [('r', 'reset', False, _('reset bisect state')),
3160 [('r', 'reset', False, _('reset bisect state')),
3158 ('g', 'good', False, _('mark changeset good')),
3161 ('g', 'good', False, _('mark changeset good')),
3159 ('b', 'bad', False, _('mark changeset bad')),
3162 ('b', 'bad', False, _('mark changeset bad')),
3160 ('s', 'skip', False, _('skip testing changeset')),
3163 ('s', 'skip', False, _('skip testing changeset')),
3161 ('c', 'command', '', _('use command to check changeset state')),
3164 ('c', 'command', '', _('use command to check changeset state')),
3162 ('U', 'noupdate', False, _('do not update to target'))],
3165 ('U', 'noupdate', False, _('do not update to target'))],
3163 _("[-gbsr] [-c CMD] [REV]")),
3166 _("[-gbsr] [-c CMD] [REV]")),
3164 "branch":
3167 "branch":
3165 (branch,
3168 (branch,
3166 [('f', 'force', None,
3169 [('f', 'force', None,
3167 _('set branch name even if it shadows an existing branch')),
3170 _('set branch name even if it shadows an existing branch')),
3168 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3171 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3169 _('[-fC] [NAME]')),
3172 _('[-fC] [NAME]')),
3170 "branches":
3173 "branches":
3171 (branches,
3174 (branches,
3172 [('a', 'active', False,
3175 [('a', 'active', False,
3173 _('show only branches that have unmerged heads')),
3176 _('show only branches that have unmerged heads')),
3174 ('c', 'closed', False,
3177 ('c', 'closed', False,
3175 _('show normal and closed heads'))],
3178 _('show normal and closed heads'))],
3176 _('[-a]')),
3179 _('[-a]')),
3177 "bundle":
3180 "bundle":
3178 (bundle,
3181 (bundle,
3179 [('f', 'force', None,
3182 [('f', 'force', None,
3180 _('run even when remote repository is unrelated')),
3183 _('run even when remote repository is unrelated')),
3181 ('r', 'rev', [],
3184 ('r', 'rev', [],
3182 _('a changeset up to which you would like to bundle')),
3185 _('a changeset up to which you would like to bundle')),
3183 ('', 'base', [],
3186 ('', 'base', [],
3184 _('a base changeset to specify instead of a destination')),
3187 _('a base changeset to specify instead of a destination')),
3185 ('a', 'all', None, _('bundle all changesets in the repository')),
3188 ('a', 'all', None, _('bundle all changesets in the repository')),
3186 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3189 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3187 ] + remoteopts,
3190 ] + remoteopts,
3188 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3191 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3189 "cat":
3192 "cat":
3190 (cat,
3193 (cat,
3191 [('o', 'output', '', _('print output to file with formatted name')),
3194 [('o', 'output', '', _('print output to file with formatted name')),
3192 ('r', 'rev', '', _('print the given revision')),
3195 ('r', 'rev', '', _('print the given revision')),
3193 ('', 'decode', None, _('apply any matching decode filter')),
3196 ('', 'decode', None, _('apply any matching decode filter')),
3194 ] + walkopts,
3197 ] + walkopts,
3195 _('[OPTION]... FILE...')),
3198 _('[OPTION]... FILE...')),
3196 "^clone":
3199 "^clone":
3197 (clone,
3200 (clone,
3198 [('U', 'noupdate', None,
3201 [('U', 'noupdate', None,
3199 _('the clone will only contain a repository (no working copy)')),
3202 _('the clone will only contain a repository (no working copy)')),
3200 ('r', 'rev', [],
3203 ('r', 'rev', [],
3201 _('a changeset you would like to have after cloning')),
3204 _('a changeset you would like to have after cloning')),
3202 ('', 'pull', None, _('use pull protocol to copy metadata')),
3205 ('', 'pull', None, _('use pull protocol to copy metadata')),
3203 ('', 'uncompressed', None,
3206 ('', 'uncompressed', None,
3204 _('use uncompressed transfer (fast over LAN)')),
3207 _('use uncompressed transfer (fast over LAN)')),
3205 ] + remoteopts,
3208 ] + remoteopts,
3206 _('[OPTION]... SOURCE [DEST]')),
3209 _('[OPTION]... SOURCE [DEST]')),
3207 "^commit|ci":
3210 "^commit|ci":
3208 (commit,
3211 (commit,
3209 [('A', 'addremove', None,
3212 [('A', 'addremove', None,
3210 _('mark new/missing files as added/removed before committing')),
3213 _('mark new/missing files as added/removed before committing')),
3211 ('', 'close-branch', None,
3214 ('', 'close-branch', None,
3212 _('mark a branch as closed, hiding it from the branch list')),
3215 _('mark a branch as closed, hiding it from the branch list')),
3213 ] + walkopts + commitopts + commitopts2,
3216 ] + walkopts + commitopts + commitopts2,
3214 _('[OPTION]... [FILE]...')),
3217 _('[OPTION]... [FILE]...')),
3215 "copy|cp":
3218 "copy|cp":
3216 (copy,
3219 (copy,
3217 [('A', 'after', None, _('record a copy that has already occurred')),
3220 [('A', 'after', None, _('record a copy that has already occurred')),
3218 ('f', 'force', None,
3221 ('f', 'force', None,
3219 _('forcibly copy over an existing managed file')),
3222 _('forcibly copy over an existing managed file')),
3220 ] + walkopts + dryrunopts,
3223 ] + walkopts + dryrunopts,
3221 _('[OPTION]... [SOURCE]... DEST')),
3224 _('[OPTION]... [SOURCE]... DEST')),
3222 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3225 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3223 "debugcheckstate": (debugcheckstate, []),
3226 "debugcheckstate": (debugcheckstate, []),
3224 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3227 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3225 "debugcomplete":
3228 "debugcomplete":
3226 (debugcomplete,
3229 (debugcomplete,
3227 [('o', 'options', None, _('show the command options'))],
3230 [('o', 'options', None, _('show the command options'))],
3228 _('[-o] CMD')),
3231 _('[-o] CMD')),
3229 "debugdate":
3232 "debugdate":
3230 (debugdate,
3233 (debugdate,
3231 [('e', 'extended', None, _('try extended date formats'))],
3234 [('e', 'extended', None, _('try extended date formats'))],
3232 _('[-e] DATE [RANGE]')),
3235 _('[-e] DATE [RANGE]')),
3233 "debugdata": (debugdata, [], _('FILE REV')),
3236 "debugdata": (debugdata, [], _('FILE REV')),
3234 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3237 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3235 "debugindex": (debugindex, [], _('FILE')),
3238 "debugindex": (debugindex, [], _('FILE')),
3236 "debugindexdot": (debugindexdot, [], _('FILE')),
3239 "debugindexdot": (debugindexdot, [], _('FILE')),
3237 "debuginstall": (debuginstall, []),
3240 "debuginstall": (debuginstall, []),
3238 "debugrebuildstate":
3241 "debugrebuildstate":
3239 (debugrebuildstate,
3242 (debugrebuildstate,
3240 [('r', 'rev', '', _('revision to rebuild to'))],
3243 [('r', 'rev', '', _('revision to rebuild to'))],
3241 _('[-r REV] [REV]')),
3244 _('[-r REV] [REV]')),
3242 "debugrename":
3245 "debugrename":
3243 (debugrename,
3246 (debugrename,
3244 [('r', 'rev', '', _('revision to debug'))],
3247 [('r', 'rev', '', _('revision to debug'))],
3245 _('[-r REV] FILE')),
3248 _('[-r REV] FILE')),
3246 "debugsetparents":
3249 "debugsetparents":
3247 (debugsetparents, [], _('REV1 [REV2]')),
3250 (debugsetparents, [], _('REV1 [REV2]')),
3248 "debugstate":
3251 "debugstate":
3249 (debugstate,
3252 (debugstate,
3250 [('', 'nodates', None, _('do not display the saved mtime'))],
3253 [('', 'nodates', None, _('do not display the saved mtime'))],
3251 _('[OPTION]...')),
3254 _('[OPTION]...')),
3252 "debugsub":
3255 "debugsub":
3253 (debugsub,
3256 (debugsub,
3254 [('r', 'rev', '', _('revision to check'))],
3257 [('r', 'rev', '', _('revision to check'))],
3255 _('[-r REV] [REV]')),
3258 _('[-r REV] [REV]')),
3256 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3259 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3257 "^diff":
3260 "^diff":
3258 (diff,
3261 (diff,
3259 [('r', 'rev', [], _('revision')),
3262 [('r', 'rev', [], _('revision')),
3260 ('c', 'change', '', _('change made by revision'))
3263 ('c', 'change', '', _('change made by revision'))
3261 ] + diffopts + diffopts2 + walkopts,
3264 ] + diffopts + diffopts2 + walkopts,
3262 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3265 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3263 "^export":
3266 "^export":
3264 (export,
3267 (export,
3265 [('o', 'output', '', _('print output to file with formatted name')),
3268 [('o', 'output', '', _('print output to file with formatted name')),
3266 ('', 'switch-parent', None, _('diff against the second parent'))
3269 ('', 'switch-parent', None, _('diff against the second parent'))
3267 ] + diffopts,
3270 ] + diffopts,
3268 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3271 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3269 "^forget":
3272 "^forget":
3270 (forget,
3273 (forget,
3271 [] + walkopts,
3274 [] + walkopts,
3272 _('[OPTION]... FILE...')),
3275 _('[OPTION]... FILE...')),
3273 "grep":
3276 "grep":
3274 (grep,
3277 (grep,
3275 [('0', 'print0', None, _('end fields with NUL')),
3278 [('0', 'print0', None, _('end fields with NUL')),
3276 ('', 'all', None, _('print all revisions that match')),
3279 ('', 'all', None, _('print all revisions that match')),
3277 ('f', 'follow', None,
3280 ('f', 'follow', None,
3278 _('follow changeset history, or file history across copies and renames')),
3281 _('follow changeset history, or file history across copies and renames')),
3279 ('i', 'ignore-case', None, _('ignore case when matching')),
3282 ('i', 'ignore-case', None, _('ignore case when matching')),
3280 ('l', 'files-with-matches', None,
3283 ('l', 'files-with-matches', None,
3281 _('print only filenames and revisions that match')),
3284 _('print only filenames and revisions that match')),
3282 ('n', 'line-number', None, _('print matching line numbers')),
3285 ('n', 'line-number', None, _('print matching line numbers')),
3283 ('r', 'rev', [], _('search in given revision range')),
3286 ('r', 'rev', [], _('search in given revision range')),
3284 ('u', 'user', None, _('list the author (long with -v)')),
3287 ('u', 'user', None, _('list the author (long with -v)')),
3285 ('d', 'date', None, _('list the date (short with -q)')),
3288 ('d', 'date', None, _('list the date (short with -q)')),
3286 ] + walkopts,
3289 ] + walkopts,
3287 _('[OPTION]... PATTERN [FILE]...')),
3290 _('[OPTION]... PATTERN [FILE]...')),
3288 "heads":
3291 "heads":
3289 (heads,
3292 (heads,
3290 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3293 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3291 ('a', 'active', False,
3294 ('a', 'active', False,
3292 _('show only the active heads from open branches')),
3295 _('show only the active heads from open branches')),
3293 ('c', 'closed', False,
3296 ('c', 'closed', False,
3294 _('show normal and closed heads')),
3297 _('show normal and closed heads')),
3295 ] + templateopts,
3298 ] + templateopts,
3296 _('[-r STARTREV] [REV]...')),
3299 _('[-r STARTREV] [REV]...')),
3297 "help": (help_, [], _('[TOPIC]')),
3300 "help": (help_, [], _('[TOPIC]')),
3298 "identify|id":
3301 "identify|id":
3299 (identify,
3302 (identify,
3300 [('r', 'rev', '', _('identify the specified revision')),
3303 [('r', 'rev', '', _('identify the specified revision')),
3301 ('n', 'num', None, _('show local revision number')),
3304 ('n', 'num', None, _('show local revision number')),
3302 ('i', 'id', None, _('show global revision id')),
3305 ('i', 'id', None, _('show global revision id')),
3303 ('b', 'branch', None, _('show branch')),
3306 ('b', 'branch', None, _('show branch')),
3304 ('t', 'tags', None, _('show tags'))],
3307 ('t', 'tags', None, _('show tags'))],
3305 _('[-nibt] [-r REV] [SOURCE]')),
3308 _('[-nibt] [-r REV] [SOURCE]')),
3306 "import|patch":
3309 "import|patch":
3307 (import_,
3310 (import_,
3308 [('p', 'strip', 1,
3311 [('p', 'strip', 1,
3309 _('directory strip option for patch. This has the same '
3312 _('directory strip option for patch. This has the same '
3310 'meaning as the corresponding patch option')),
3313 'meaning as the corresponding patch option')),
3311 ('b', 'base', '', _('base path')),
3314 ('b', 'base', '', _('base path')),
3312 ('f', 'force', None,
3315 ('f', 'force', None,
3313 _('skip check for outstanding uncommitted changes')),
3316 _('skip check for outstanding uncommitted changes')),
3314 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3317 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3315 ('', 'exact', None,
3318 ('', 'exact', None,
3316 _('apply patch to the nodes from which it was generated')),
3319 _('apply patch to the nodes from which it was generated')),
3317 ('', 'import-branch', None,
3320 ('', 'import-branch', None,
3318 _('use any branch information in patch (implied by --exact)'))] +
3321 _('use any branch information in patch (implied by --exact)'))] +
3319 commitopts + commitopts2 + similarityopts,
3322 commitopts + commitopts2 + similarityopts,
3320 _('[OPTION]... PATCH...')),
3323 _('[OPTION]... PATCH...')),
3321 "incoming|in":
3324 "incoming|in":
3322 (incoming,
3325 (incoming,
3323 [('f', 'force', None,
3326 [('f', 'force', None,
3324 _('run even when remote repository is unrelated')),
3327 _('run even when remote repository is unrelated')),
3325 ('n', 'newest-first', None, _('show newest record first')),
3328 ('n', 'newest-first', None, _('show newest record first')),
3326 ('', 'bundle', '', _('file to store the bundles into')),
3329 ('', 'bundle', '', _('file to store the bundles into')),
3327 ('r', 'rev', [],
3330 ('r', 'rev', [],
3328 _('a specific revision up to which you would like to pull')),
3331 _('a specific revision up to which you would like to pull')),
3329 ] + logopts + remoteopts,
3332 ] + logopts + remoteopts,
3330 _('[-p] [-n] [-M] [-f] [-r REV]...'
3333 _('[-p] [-n] [-M] [-f] [-r REV]...'
3331 ' [--bundle FILENAME] [SOURCE]')),
3334 ' [--bundle FILENAME] [SOURCE]')),
3332 "^init":
3335 "^init":
3333 (init,
3336 (init,
3334 remoteopts,
3337 remoteopts,
3335 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3338 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3336 "locate":
3339 "locate":
3337 (locate,
3340 (locate,
3338 [('r', 'rev', '', _('search the repository as it stood at REV')),
3341 [('r', 'rev', '', _('search the repository as it stood at REV')),
3339 ('0', 'print0', None,
3342 ('0', 'print0', None,
3340 _('end filenames with NUL, for use with xargs')),
3343 _('end filenames with NUL, for use with xargs')),
3341 ('f', 'fullpath', None,
3344 ('f', 'fullpath', None,
3342 _('print complete paths from the filesystem root')),
3345 _('print complete paths from the filesystem root')),
3343 ] + walkopts,
3346 ] + walkopts,
3344 _('[OPTION]... [PATTERN]...')),
3347 _('[OPTION]... [PATTERN]...')),
3345 "^log|history":
3348 "^log|history":
3346 (log,
3349 (log,
3347 [('f', 'follow', None,
3350 [('f', 'follow', None,
3348 _('follow changeset history, or file history across copies and renames')),
3351 _('follow changeset history, or file history across copies and renames')),
3349 ('', 'follow-first', None,
3352 ('', 'follow-first', None,
3350 _('only follow the first parent of merge changesets')),
3353 _('only follow the first parent of merge changesets')),
3351 ('d', 'date', '', _('show revisions matching date spec')),
3354 ('d', 'date', '', _('show revisions matching date spec')),
3352 ('C', 'copies', None, _('show copied files')),
3355 ('C', 'copies', None, _('show copied files')),
3353 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3356 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3354 ('r', 'rev', [], _('show the specified revision or range')),
3357 ('r', 'rev', [], _('show the specified revision or range')),
3355 ('', 'removed', None, _('include revisions where files were removed')),
3358 ('', 'removed', None, _('include revisions where files were removed')),
3356 ('m', 'only-merges', None, _('show only merges')),
3359 ('m', 'only-merges', None, _('show only merges')),
3357 ('u', 'user', [], _('revisions committed by user')),
3360 ('u', 'user', [], _('revisions committed by user')),
3358 ('b', 'only-branch', [],
3361 ('b', 'only-branch', [],
3359 _('show only changesets within the given named branch')),
3362 _('show only changesets within the given named branch')),
3360 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3363 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3361 ] + logopts + walkopts,
3364 ] + logopts + walkopts,
3362 _('[OPTION]... [FILE]')),
3365 _('[OPTION]... [FILE]')),
3363 "manifest":
3366 "manifest":
3364 (manifest,
3367 (manifest,
3365 [('r', 'rev', '', _('revision to display'))],
3368 [('r', 'rev', '', _('revision to display'))],
3366 _('[-r REV]')),
3369 _('[-r REV]')),
3367 "^merge":
3370 "^merge":
3368 (merge,
3371 (merge,
3369 [('f', 'force', None, _('force a merge with outstanding changes')),
3372 [('f', 'force', None, _('force a merge with outstanding changes')),
3370 ('r', 'rev', '', _('revision to merge')),
3373 ('r', 'rev', '', _('revision to merge')),
3371 ('P', 'preview', None,
3374 ('P', 'preview', None,
3372 _('review revisions to merge (no merge is performed)'))],
3375 _('review revisions to merge (no merge is performed)'))],
3373 _('[-f] [[-r] REV]')),
3376 _('[-f] [[-r] REV]')),
3374 "outgoing|out":
3377 "outgoing|out":
3375 (outgoing,
3378 (outgoing,
3376 [('f', 'force', None,
3379 [('f', 'force', None,
3377 _('run even when remote repository is unrelated')),
3380 _('run even when remote repository is unrelated')),
3378 ('r', 'rev', [],
3381 ('r', 'rev', [],
3379 _('a specific revision up to which you would like to push')),
3382 _('a specific revision up to which you would like to push')),
3380 ('n', 'newest-first', None, _('show newest record first')),
3383 ('n', 'newest-first', None, _('show newest record first')),
3381 ] + logopts + remoteopts,
3384 ] + logopts + remoteopts,
3382 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3385 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3383 "^parents":
3386 "^parents":
3384 (parents,
3387 (parents,
3385 [('r', 'rev', '', _('show parents from the specified revision')),
3388 [('r', 'rev', '', _('show parents from the specified revision')),
3386 ] + templateopts,
3389 ] + templateopts,
3387 _('[-r REV] [FILE]')),
3390 _('[-r REV] [FILE]')),
3388 "paths": (paths, [], _('[NAME]')),
3391 "paths": (paths, [], _('[NAME]')),
3389 "^pull":
3392 "^pull":
3390 (pull,
3393 (pull,
3391 [('u', 'update', None,
3394 [('u', 'update', None,
3392 _('update to new tip if changesets were pulled')),
3395 _('update to new tip if changesets were pulled')),
3393 ('f', 'force', None,
3396 ('f', 'force', None,
3394 _('run even when remote repository is unrelated')),
3397 _('run even when remote repository is unrelated')),
3395 ('r', 'rev', [],
3398 ('r', 'rev', [],
3396 _('a specific revision up to which you would like to pull')),
3399 _('a specific revision up to which you would like to pull')),
3397 ] + remoteopts,
3400 ] + remoteopts,
3398 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3401 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3399 "^push":
3402 "^push":
3400 (push,
3403 (push,
3401 [('f', 'force', None, _('force push')),
3404 [('f', 'force', None, _('force push')),
3402 ('r', 'rev', [],
3405 ('r', 'rev', [],
3403 _('a specific revision up to which you would like to push')),
3406 _('a specific revision up to which you would like to push')),
3404 ] + remoteopts,
3407 ] + remoteopts,
3405 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3408 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3406 "recover": (recover, []),
3409 "recover": (recover, []),
3407 "^remove|rm":
3410 "^remove|rm":
3408 (remove,
3411 (remove,
3409 [('A', 'after', None, _('record delete for missing files')),
3412 [('A', 'after', None, _('record delete for missing files')),
3410 ('f', 'force', None,
3413 ('f', 'force', None,
3411 _('remove (and delete) file even if added or modified')),
3414 _('remove (and delete) file even if added or modified')),
3412 ] + walkopts,
3415 ] + walkopts,
3413 _('[OPTION]... FILE...')),
3416 _('[OPTION]... FILE...')),
3414 "rename|mv":
3417 "rename|mv":
3415 (rename,
3418 (rename,
3416 [('A', 'after', None, _('record a rename that has already occurred')),
3419 [('A', 'after', None, _('record a rename that has already occurred')),
3417 ('f', 'force', None,
3420 ('f', 'force', None,
3418 _('forcibly copy over an existing managed file')),
3421 _('forcibly copy over an existing managed file')),
3419 ] + walkopts + dryrunopts,
3422 ] + walkopts + dryrunopts,
3420 _('[OPTION]... SOURCE... DEST')),
3423 _('[OPTION]... SOURCE... DEST')),
3421 "resolve":
3424 "resolve":
3422 (resolve,
3425 (resolve,
3423 [('a', 'all', None, _('remerge all unresolved files')),
3426 [('a', 'all', None, _('remerge all unresolved files')),
3424 ('l', 'list', None, _('list state of files needing merge')),
3427 ('l', 'list', None, _('list state of files needing merge')),
3425 ('m', 'mark', None, _('mark files as resolved')),
3428 ('m', 'mark', None, _('mark files as resolved')),
3426 ('u', 'unmark', None, _('unmark files as resolved'))]
3429 ('u', 'unmark', None, _('unmark files as resolved'))]
3427 + walkopts,
3430 + walkopts,
3428 _('[OPTION]... [FILE]...')),
3431 _('[OPTION]... [FILE]...')),
3429 "revert":
3432 "revert":
3430 (revert,
3433 (revert,
3431 [('a', 'all', None, _('revert all changes when no arguments given')),
3434 [('a', 'all', None, _('revert all changes when no arguments given')),
3432 ('d', 'date', '', _('tipmost revision matching date')),
3435 ('d', 'date', '', _('tipmost revision matching date')),
3433 ('r', 'rev', '', _('revision to revert to')),
3436 ('r', 'rev', '', _('revision to revert to')),
3434 ('', 'no-backup', None, _('do not save backup copies of files')),
3437 ('', 'no-backup', None, _('do not save backup copies of files')),
3435 ] + walkopts + dryrunopts,
3438 ] + walkopts + dryrunopts,
3436 _('[OPTION]... [-r REV] [NAME]...')),
3439 _('[OPTION]... [-r REV] [NAME]...')),
3437 "rollback": (rollback, []),
3440 "rollback": (rollback, []),
3438 "root": (root, []),
3441 "root": (root, []),
3439 "^serve":
3442 "^serve":
3440 (serve,
3443 (serve,
3441 [('A', 'accesslog', '', _('name of access log file to write to')),
3444 [('A', 'accesslog', '', _('name of access log file to write to')),
3442 ('d', 'daemon', None, _('run server in background')),
3445 ('d', 'daemon', None, _('run server in background')),
3443 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3446 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3444 ('E', 'errorlog', '', _('name of error log file to write to')),
3447 ('E', 'errorlog', '', _('name of error log file to write to')),
3445 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3448 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3446 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3449 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3447 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3450 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3448 ('n', 'name', '',
3451 ('n', 'name', '',
3449 _('name to show in web pages (default: working directory)')),
3452 _('name to show in web pages (default: working directory)')),
3450 ('', 'webdir-conf', '', _('name of the webdir config file'
3453 ('', 'webdir-conf', '', _('name of the webdir config file'
3451 ' (serve more than one repository)')),
3454 ' (serve more than one repository)')),
3452 ('', 'pid-file', '', _('name of file to write process ID to')),
3455 ('', 'pid-file', '', _('name of file to write process ID to')),
3453 ('', 'stdio', None, _('for remote clients')),
3456 ('', 'stdio', None, _('for remote clients')),
3454 ('t', 'templates', '', _('web templates to use')),
3457 ('t', 'templates', '', _('web templates to use')),
3455 ('', 'style', '', _('template style to use')),
3458 ('', 'style', '', _('template style to use')),
3456 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3459 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3457 ('', 'certificate', '', _('SSL certificate file'))],
3460 ('', 'certificate', '', _('SSL certificate file'))],
3458 _('[OPTION]...')),
3461 _('[OPTION]...')),
3459 "showconfig|debugconfig":
3462 "showconfig|debugconfig":
3460 (showconfig,
3463 (showconfig,
3461 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3464 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3462 _('[-u] [NAME]...')),
3465 _('[-u] [NAME]...')),
3463 "^status|st":
3466 "^status|st":
3464 (status,
3467 (status,
3465 [('A', 'all', None, _('show status of all files')),
3468 [('A', 'all', None, _('show status of all files')),
3466 ('m', 'modified', None, _('show only modified files')),
3469 ('m', 'modified', None, _('show only modified files')),
3467 ('a', 'added', None, _('show only added files')),
3470 ('a', 'added', None, _('show only added files')),
3468 ('r', 'removed', None, _('show only removed files')),
3471 ('r', 'removed', None, _('show only removed files')),
3469 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3472 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3470 ('c', 'clean', None, _('show only files without changes')),
3473 ('c', 'clean', None, _('show only files without changes')),
3471 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3474 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3472 ('i', 'ignored', None, _('show only ignored files')),
3475 ('i', 'ignored', None, _('show only ignored files')),
3473 ('n', 'no-status', None, _('hide status prefix')),
3476 ('n', 'no-status', None, _('hide status prefix')),
3474 ('C', 'copies', None, _('show source of copied files')),
3477 ('C', 'copies', None, _('show source of copied files')),
3475 ('0', 'print0', None,
3478 ('0', 'print0', None,
3476 _('end filenames with NUL, for use with xargs')),
3479 _('end filenames with NUL, for use with xargs')),
3477 ('', 'rev', [], _('show difference from revision')),
3480 ('', 'rev', [], _('show difference from revision')),
3478 ] + walkopts,
3481 ] + walkopts,
3479 _('[OPTION]... [FILE]...')),
3482 _('[OPTION]... [FILE]...')),
3480 "tag":
3483 "tag":
3481 (tag,
3484 (tag,
3482 [('f', 'force', None, _('replace existing tag')),
3485 [('f', 'force', None, _('replace existing tag')),
3483 ('l', 'local', None, _('make the tag local')),
3486 ('l', 'local', None, _('make the tag local')),
3484 ('r', 'rev', '', _('revision to tag')),
3487 ('r', 'rev', '', _('revision to tag')),
3485 ('', 'remove', None, _('remove a tag')),
3488 ('', 'remove', None, _('remove a tag')),
3486 # -l/--local is already there, commitopts cannot be used
3489 # -l/--local is already there, commitopts cannot be used
3487 ('m', 'message', '', _('use <text> as commit message')),
3490 ('m', 'message', '', _('use <text> as commit message')),
3488 ] + commitopts2,
3491 ] + commitopts2,
3489 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3492 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3490 "tags": (tags, []),
3493 "tags": (tags, []),
3491 "tip":
3494 "tip":
3492 (tip,
3495 (tip,
3493 [('p', 'patch', None, _('show patch')),
3496 [('p', 'patch', None, _('show patch')),
3494 ('g', 'git', None, _('use git extended diff format')),
3497 ('g', 'git', None, _('use git extended diff format')),
3495 ] + templateopts,
3498 ] + templateopts,
3496 _('[-p]')),
3499 _('[-p]')),
3497 "unbundle":
3500 "unbundle":
3498 (unbundle,
3501 (unbundle,
3499 [('u', 'update', None,
3502 [('u', 'update', None,
3500 _('update to new tip if changesets were unbundled'))],
3503 _('update to new tip if changesets were unbundled'))],
3501 _('[-u] FILE...')),
3504 _('[-u] FILE...')),
3502 "^update|up|checkout|co":
3505 "^update|up|checkout|co":
3503 (update,
3506 (update,
3504 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3507 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3505 ('c', 'check', None, _('check for uncommitted changes')),
3508 ('c', 'check', None, _('check for uncommitted changes')),
3506 ('d', 'date', '', _('tipmost revision matching date')),
3509 ('d', 'date', '', _('tipmost revision matching date')),
3507 ('r', 'rev', '', _('revision'))],
3510 ('r', 'rev', '', _('revision'))],
3508 _('[-C] [-d DATE] [[-r] REV]')),
3511 _('[-C] [-d DATE] [[-r] REV]')),
3509 "verify": (verify, []),
3512 "verify": (verify, []),
3510 "version": (version_, []),
3513 "version": (version_, []),
3511 }
3514 }
3512
3515
3513 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3516 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3514 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3517 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3515 optionalrepo = ("identify paths serve showconfig debugancestor")
3518 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now