##// END OF EJS Templates
add cmdutil.remoteui...
Matt Mackall -
r8188:f3abe032 default
parent child Browse files
Show More
@@ -1,147 +1,146 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 '''pulling, updating and merging in one command'''
7 '''pulling, updating and merging in one command'''
8
8
9 from mercurial.i18n import _
9 from mercurial.i18n import _
10 from mercurial.node import nullid, short
10 from mercurial.node import nullid, short
11 from mercurial import commands, cmdutil, hg, util, url
11 from mercurial import commands, cmdutil, hg, util, url
12 from mercurial.lock import release
12 from mercurial.lock import release
13
13
14 def fetch(ui, repo, source='default', **opts):
14 def fetch(ui, repo, source='default', **opts):
15 '''pull changes from a remote repository, merge new changes if needed.
15 '''pull changes from a remote repository, merge new changes if needed.
16
16
17 This finds all changes from the repository at the specified path
17 This finds all changes from the repository at the specified path
18 or URL and adds them to the local repository.
18 or URL and adds them to the local repository.
19
19
20 If the pulled changes add a new branch head, the head is
20 If the pulled changes add a new branch head, the head is
21 automatically merged, and the result of the merge is committed.
21 automatically merged, and the result of the merge is committed.
22 Otherwise, the working directory is updated to include the new
22 Otherwise, the working directory is updated to include the new
23 changes.
23 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
26 "authoritative". The head of the new changes is used as the first
27 parent, with local changes as the second. To switch the merge
27 parent, with local changes as the second. To switch the merge
28 order, use --switch-parent.
28 order, use --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 cmdutil.setremoteconfig(ui, opts)
63 other = hg.repository(cmdutil.remoteui(repo, opts),
64
64 ui.expandpath(source))
65 other = hg.repository(ui, ui.expandpath(source))
66 ui.status(_('pulling from %s\n') %
65 ui.status(_('pulling from %s\n') %
67 url.hidepassword(ui.expandpath(source)))
66 url.hidepassword(ui.expandpath(source)))
68 revs = None
67 revs = None
69 if opts['rev']:
68 if opts['rev']:
70 if not other.local():
69 if not other.local():
71 raise util.Abort(_("fetch -r doesn't work for remote "
70 raise util.Abort(_("fetch -r doesn't work for remote "
72 "repositories yet"))
71 "repositories yet"))
73 else:
72 else:
74 revs = [other.lookup(rev) for rev in opts['rev']]
73 revs = [other.lookup(rev) for rev in opts['rev']]
75
74
76 # Are there any changes at all?
75 # Are there any changes at all?
77 modheads = repo.pull(other, heads=revs)
76 modheads = repo.pull(other, heads=revs)
78 if modheads == 0:
77 if modheads == 0:
79 return 0
78 return 0
80
79
81 # Is this a simple fast-forward along the current branch?
80 # Is this a simple fast-forward along the current branch?
82 newheads = repo.branchheads(branch)
81 newheads = repo.branchheads(branch)
83 newheads = [head for head in newheads if len(repo[head].children()) == 0]
82 newheads = [head for head in newheads if len(repo[head].children()) == 0]
84 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
83 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
85 if len(newheads) == 1:
84 if len(newheads) == 1:
86 if newchildren[0] != parent:
85 if newchildren[0] != parent:
87 return hg.clean(repo, newchildren[0])
86 return hg.clean(repo, newchildren[0])
88 else:
87 else:
89 return
88 return
90
89
91 # Are there more than one additional branch heads?
90 # Are there more than one additional branch heads?
92 newchildren = [n for n in newchildren if n != parent]
91 newchildren = [n for n in newchildren if n != parent]
93 newparent = parent
92 newparent = parent
94 if newchildren:
93 if newchildren:
95 newparent = newchildren[0]
94 newparent = newchildren[0]
96 hg.clean(repo, newparent)
95 hg.clean(repo, newparent)
97 newheads = [n for n in newheads if n != newparent]
96 newheads = [n for n in newheads if n != newparent]
98 if len(newheads) > 1:
97 if len(newheads) > 1:
99 ui.status(_('not merging with %d other new branch heads '
98 ui.status(_('not merging with %d other new branch heads '
100 '(use "hg heads ." and "hg merge" to merge them)\n') %
99 '(use "hg heads ." and "hg merge" to merge them)\n') %
101 (len(newheads) - 1))
100 (len(newheads) - 1))
102 return
101 return
103
102
104 # Otherwise, let's merge.
103 # Otherwise, let's merge.
105 err = False
104 err = False
106 if newheads:
105 if newheads:
107 # By default, we consider the repository we're pulling
106 # By default, we consider the repository we're pulling
108 # *from* as authoritative, so we merge our changes into
107 # *from* as authoritative, so we merge our changes into
109 # theirs.
108 # theirs.
110 if opts['switch_parent']:
109 if opts['switch_parent']:
111 firstparent, secondparent = newparent, newheads[0]
110 firstparent, secondparent = newparent, newheads[0]
112 else:
111 else:
113 firstparent, secondparent = newheads[0], newparent
112 firstparent, secondparent = newheads[0], newparent
114 ui.status(_('updating to %d:%s\n') %
113 ui.status(_('updating to %d:%s\n') %
115 (repo.changelog.rev(firstparent),
114 (repo.changelog.rev(firstparent),
116 short(firstparent)))
115 short(firstparent)))
117 hg.clean(repo, firstparent)
116 hg.clean(repo, firstparent)
118 ui.status(_('merging with %d:%s\n') %
117 ui.status(_('merging with %d:%s\n') %
119 (repo.changelog.rev(secondparent), short(secondparent)))
118 (repo.changelog.rev(secondparent), short(secondparent)))
120 err = hg.merge(repo, secondparent, remind=False)
119 err = hg.merge(repo, secondparent, remind=False)
121
120
122 if not err:
121 if not err:
123 mod, add, rem = repo.status()[:3]
122 mod, add, rem = repo.status()[:3]
124 message = (cmdutil.logmessage(opts) or
123 message = (cmdutil.logmessage(opts) or
125 (_('Automated merge with %s') %
124 (_('Automated merge with %s') %
126 url.removeauth(other.url())))
125 url.removeauth(other.url())))
127 force_editor = opts.get('force_editor') or opts.get('edit')
126 force_editor = opts.get('force_editor') or opts.get('edit')
128 n = repo.commit(mod + add + rem, message,
127 n = repo.commit(mod + add + rem, message,
129 opts['user'], opts['date'], force=True,
128 opts['user'], opts['date'], force=True,
130 force_editor=force_editor)
129 force_editor=force_editor)
131 ui.status(_('new changeset %d:%s merges remote changes '
130 ui.status(_('new changeset %d:%s merges remote changes '
132 'with local\n') % (repo.changelog.rev(n),
131 'with local\n') % (repo.changelog.rev(n),
133 short(n)))
132 short(n)))
134
133
135 finally:
134 finally:
136 release(lock, wlock)
135 release(lock, wlock)
137
136
138 cmdtable = {
137 cmdtable = {
139 'fetch':
138 'fetch':
140 (fetch,
139 (fetch,
141 [('r', 'rev', [], _('a specific revision you would like to pull')),
140 [('r', 'rev', [], _('a specific revision you would like to pull')),
142 ('e', 'edit', None, _('edit commit message')),
141 ('e', 'edit', None, _('edit commit message')),
143 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
142 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
144 ('', 'switch-parent', None, _('switch parents when merging')),
143 ('', 'switch-parent', None, _('switch parents when merging')),
145 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
144 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
146 _('hg fetch [SOURCE]')),
145 _('hg fetch [SOURCE]')),
147 }
146 }
@@ -1,419 +1,416 b''
1 # ASCII graph log extension for Mercurial
1 # ASCII graph log extension for Mercurial
2 #
2 #
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License, incorporated herein by reference.
6 # the GNU General Public License, incorporated herein by reference.
7 '''show revision graphs in terminal windows
7 '''show revision graphs in terminal windows
8
8
9 This extension adds a --graph option to the incoming, outgoing and log
9 This extension adds a --graph option to the incoming, outgoing and log
10 commands. When this options is given, an ascii representation of the
10 commands. When this options is given, an ascii representation of the
11 revision graph is also shown.
11 revision graph is also shown.
12 '''
12 '''
13
13
14 import os
14 import os
15 from mercurial.cmdutil import revrange, show_changeset
15 from mercurial.cmdutil import revrange, show_changeset
16 from mercurial.commands import templateopts
16 from mercurial.commands import templateopts
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial.node import nullrev
18 from mercurial.node import nullrev
19 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
19 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
20 from mercurial import hg, url, util
20 from mercurial import hg, url, util
21
21
22 def revisions(repo, start, stop):
22 def revisions(repo, start, stop):
23 """cset DAG generator yielding (rev, node, [parents]) tuples
23 """cset DAG generator yielding (rev, node, [parents]) tuples
24
24
25 This generator function walks through the revision history from revision
25 This generator function walks through the revision history from revision
26 start to revision stop (which must be less than or equal to start).
26 start to revision stop (which must be less than or equal to start).
27 """
27 """
28 assert start >= stop
28 assert start >= stop
29 cur = start
29 cur = start
30 while cur >= stop:
30 while cur >= stop:
31 ctx = repo[cur]
31 ctx = repo[cur]
32 parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev]
32 parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev]
33 parents.sort()
33 parents.sort()
34 yield (ctx, parents)
34 yield (ctx, parents)
35 cur -= 1
35 cur -= 1
36
36
37 def filerevs(repo, path, start, stop):
37 def filerevs(repo, path, start, stop):
38 """file cset DAG generator yielding (rev, node, [parents]) tuples
38 """file cset DAG generator yielding (rev, node, [parents]) tuples
39
39
40 This generator function walks through the revision history of a single
40 This generator function walks through the revision history of a single
41 file from revision start to revision stop (which must be less than or
41 file from revision start to revision stop (which must be less than or
42 equal to start).
42 equal to start).
43 """
43 """
44 assert start >= stop
44 assert start >= stop
45 filerev = len(repo.file(path)) - 1
45 filerev = len(repo.file(path)) - 1
46 while filerev >= 0:
46 while filerev >= 0:
47 fctx = repo.filectx(path, fileid=filerev)
47 fctx = repo.filectx(path, fileid=filerev)
48 parents = [f.linkrev() for f in fctx.parents() if f.path() == path]
48 parents = [f.linkrev() for f in fctx.parents() if f.path() == path]
49 parents.sort()
49 parents.sort()
50 if fctx.rev() <= start:
50 if fctx.rev() <= start:
51 yield (fctx, parents)
51 yield (fctx, parents)
52 if fctx.rev() <= stop:
52 if fctx.rev() <= stop:
53 break
53 break
54 filerev -= 1
54 filerev -= 1
55
55
56 def grapher(nodes):
56 def grapher(nodes):
57 """grapher for asciigraph on a list of nodes and their parents
57 """grapher for asciigraph on a list of nodes and their parents
58
58
59 nodes must generate tuples (node, parents, char, lines) where
59 nodes must generate tuples (node, parents, char, lines) where
60 - parents must generate the parents of node, in sorted order,
60 - parents must generate the parents of node, in sorted order,
61 and max length 2,
61 and max length 2,
62 - char is the char to print as the node symbol, and
62 - char is the char to print as the node symbol, and
63 - lines are the lines to display next to the node.
63 - lines are the lines to display next to the node.
64 """
64 """
65 seen = []
65 seen = []
66 for node, parents, char, lines in nodes:
66 for node, parents, char, lines in nodes:
67 if node not in seen:
67 if node not in seen:
68 seen.append(node)
68 seen.append(node)
69 nodeidx = seen.index(node)
69 nodeidx = seen.index(node)
70
70
71 knownparents = []
71 knownparents = []
72 newparents = []
72 newparents = []
73 for parent in parents:
73 for parent in parents:
74 if parent in seen:
74 if parent in seen:
75 knownparents.append(parent)
75 knownparents.append(parent)
76 else:
76 else:
77 newparents.append(parent)
77 newparents.append(parent)
78
78
79 ncols = len(seen)
79 ncols = len(seen)
80 nextseen = seen[:]
80 nextseen = seen[:]
81 nextseen[nodeidx:nodeidx + 1] = newparents
81 nextseen[nodeidx:nodeidx + 1] = newparents
82 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
82 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
83
83
84 if len(newparents) > 0:
84 if len(newparents) > 0:
85 edges.append((nodeidx, nodeidx))
85 edges.append((nodeidx, nodeidx))
86 if len(newparents) > 1:
86 if len(newparents) > 1:
87 edges.append((nodeidx, nodeidx + 1))
87 edges.append((nodeidx, nodeidx + 1))
88 nmorecols = len(nextseen) - ncols
88 nmorecols = len(nextseen) - ncols
89 seen = nextseen
89 seen = nextseen
90 yield (char, lines, nodeidx, edges, ncols, nmorecols)
90 yield (char, lines, nodeidx, edges, ncols, nmorecols)
91
91
92 def fix_long_right_edges(edges):
92 def fix_long_right_edges(edges):
93 for (i, (start, end)) in enumerate(edges):
93 for (i, (start, end)) in enumerate(edges):
94 if end > start:
94 if end > start:
95 edges[i] = (start, end + 1)
95 edges[i] = (start, end + 1)
96
96
97 def get_nodeline_edges_tail(
97 def get_nodeline_edges_tail(
98 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
98 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
99 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
99 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
100 # Still going in the same non-vertical direction.
100 # Still going in the same non-vertical direction.
101 if n_columns_diff == -1:
101 if n_columns_diff == -1:
102 start = max(node_index + 1, p_node_index)
102 start = max(node_index + 1, p_node_index)
103 tail = ["|", " "] * (start - node_index - 1)
103 tail = ["|", " "] * (start - node_index - 1)
104 tail.extend(["/", " "] * (n_columns - start))
104 tail.extend(["/", " "] * (n_columns - start))
105 return tail
105 return tail
106 else:
106 else:
107 return ["\\", " "] * (n_columns - node_index - 1)
107 return ["\\", " "] * (n_columns - node_index - 1)
108 else:
108 else:
109 return ["|", " "] * (n_columns - node_index - 1)
109 return ["|", " "] * (n_columns - node_index - 1)
110
110
111 def draw_edges(edges, nodeline, interline):
111 def draw_edges(edges, nodeline, interline):
112 for (start, end) in edges:
112 for (start, end) in edges:
113 if start == end + 1:
113 if start == end + 1:
114 interline[2 * end + 1] = "/"
114 interline[2 * end + 1] = "/"
115 elif start == end - 1:
115 elif start == end - 1:
116 interline[2 * start + 1] = "\\"
116 interline[2 * start + 1] = "\\"
117 elif start == end:
117 elif start == end:
118 interline[2 * start] = "|"
118 interline[2 * start] = "|"
119 else:
119 else:
120 nodeline[2 * end] = "+"
120 nodeline[2 * end] = "+"
121 if start > end:
121 if start > end:
122 (start, end) = (end,start)
122 (start, end) = (end,start)
123 for i in range(2 * start + 1, 2 * end):
123 for i in range(2 * start + 1, 2 * end):
124 if nodeline[i] != "+":
124 if nodeline[i] != "+":
125 nodeline[i] = "-"
125 nodeline[i] = "-"
126
126
127 def get_padding_line(ni, n_columns, edges):
127 def get_padding_line(ni, n_columns, edges):
128 line = []
128 line = []
129 line.extend(["|", " "] * ni)
129 line.extend(["|", " "] * ni)
130 if (ni, ni - 1) in edges or (ni, ni) in edges:
130 if (ni, ni - 1) in edges or (ni, ni) in edges:
131 # (ni, ni - 1) (ni, ni)
131 # (ni, ni - 1) (ni, ni)
132 # | | | | | | | |
132 # | | | | | | | |
133 # +---o | | o---+
133 # +---o | | o---+
134 # | | c | | c | |
134 # | | c | | c | |
135 # | |/ / | |/ /
135 # | |/ / | |/ /
136 # | | | | | |
136 # | | | | | |
137 c = "|"
137 c = "|"
138 else:
138 else:
139 c = " "
139 c = " "
140 line.extend([c, " "])
140 line.extend([c, " "])
141 line.extend(["|", " "] * (n_columns - ni - 1))
141 line.extend(["|", " "] * (n_columns - ni - 1))
142 return line
142 return line
143
143
144 def ascii(ui, grapher):
144 def ascii(ui, grapher):
145 """prints an ASCII graph of the DAG returned by the grapher
145 """prints an ASCII graph of the DAG returned by the grapher
146
146
147 grapher is a generator that emits tuples with the following elements:
147 grapher is a generator that emits tuples with the following elements:
148
148
149 - Character to use as node's symbol.
149 - Character to use as node's symbol.
150 - List of lines to display as the node's text.
150 - List of lines to display as the node's text.
151 - Column of the current node in the set of ongoing edges.
151 - Column of the current node in the set of ongoing edges.
152 - Edges; a list of (col, next_col) indicating the edges between
152 - Edges; a list of (col, next_col) indicating the edges between
153 the current node and its parents.
153 the current node and its parents.
154 - Number of columns (ongoing edges) in the current revision.
154 - Number of columns (ongoing edges) in the current revision.
155 - The difference between the number of columns (ongoing edges)
155 - The difference between the number of columns (ongoing edges)
156 in the next revision and the number of columns (ongoing edges)
156 in the next revision and the number of columns (ongoing edges)
157 in the current revision. That is: -1 means one column removed;
157 in the current revision. That is: -1 means one column removed;
158 0 means no columns added or removed; 1 means one column added.
158 0 means no columns added or removed; 1 means one column added.
159 """
159 """
160 prev_n_columns_diff = 0
160 prev_n_columns_diff = 0
161 prev_node_index = 0
161 prev_node_index = 0
162 for (node_ch, node_lines, node_index, edges, n_columns, n_columns_diff) in grapher:
162 for (node_ch, node_lines, node_index, edges, n_columns, n_columns_diff) in grapher:
163
163
164 assert -2 < n_columns_diff < 2
164 assert -2 < n_columns_diff < 2
165 if n_columns_diff == -1:
165 if n_columns_diff == -1:
166 # Transform
166 # Transform
167 #
167 #
168 # | | | | | |
168 # | | | | | |
169 # o | | into o---+
169 # o | | into o---+
170 # |X / |/ /
170 # |X / |/ /
171 # | | | |
171 # | | | |
172 fix_long_right_edges(edges)
172 fix_long_right_edges(edges)
173
173
174 # add_padding_line says whether to rewrite
174 # add_padding_line says whether to rewrite
175 #
175 #
176 # | | | | | | | |
176 # | | | | | | | |
177 # | o---+ into | o---+
177 # | o---+ into | o---+
178 # | / / | | | # <--- padding line
178 # | / / | | | # <--- padding line
179 # o | | | / /
179 # o | | | / /
180 # o | |
180 # o | |
181 add_padding_line = (len(node_lines) > 2 and
181 add_padding_line = (len(node_lines) > 2 and
182 n_columns_diff == -1 and
182 n_columns_diff == -1 and
183 [x for (x, y) in edges if x + 1 < y])
183 [x for (x, y) in edges if x + 1 < y])
184
184
185 # fix_nodeline_tail says whether to rewrite
185 # fix_nodeline_tail says whether to rewrite
186 #
186 #
187 # | | o | | | | o | |
187 # | | o | | | | o | |
188 # | | |/ / | | |/ /
188 # | | |/ / | | |/ /
189 # | o | | into | o / / # <--- fixed nodeline tail
189 # | o | | into | o / / # <--- fixed nodeline tail
190 # | |/ / | |/ /
190 # | |/ / | |/ /
191 # o | | o | |
191 # o | | o | |
192 fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
192 fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
193
193
194 # nodeline is the line containing the node character (typically o)
194 # nodeline is the line containing the node character (typically o)
195 nodeline = ["|", " "] * node_index
195 nodeline = ["|", " "] * node_index
196 nodeline.extend([node_ch, " "])
196 nodeline.extend([node_ch, " "])
197
197
198 nodeline.extend(
198 nodeline.extend(
199 get_nodeline_edges_tail(
199 get_nodeline_edges_tail(
200 node_index, prev_node_index, n_columns, n_columns_diff,
200 node_index, prev_node_index, n_columns, n_columns_diff,
201 prev_n_columns_diff, fix_nodeline_tail))
201 prev_n_columns_diff, fix_nodeline_tail))
202
202
203 # shift_interline is the line containing the non-vertical
203 # shift_interline is the line containing the non-vertical
204 # edges between this entry and the next
204 # edges between this entry and the next
205 shift_interline = ["|", " "] * node_index
205 shift_interline = ["|", " "] * node_index
206 if n_columns_diff == -1:
206 if n_columns_diff == -1:
207 n_spaces = 1
207 n_spaces = 1
208 edge_ch = "/"
208 edge_ch = "/"
209 elif n_columns_diff == 0:
209 elif n_columns_diff == 0:
210 n_spaces = 2
210 n_spaces = 2
211 edge_ch = "|"
211 edge_ch = "|"
212 else:
212 else:
213 n_spaces = 3
213 n_spaces = 3
214 edge_ch = "\\"
214 edge_ch = "\\"
215 shift_interline.extend(n_spaces * [" "])
215 shift_interline.extend(n_spaces * [" "])
216 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
216 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
217
217
218 # draw edges from the current node to its parents
218 # draw edges from the current node to its parents
219 draw_edges(edges, nodeline, shift_interline)
219 draw_edges(edges, nodeline, shift_interline)
220
220
221 # lines is the list of all graph lines to print
221 # lines is the list of all graph lines to print
222 lines = [nodeline]
222 lines = [nodeline]
223 if add_padding_line:
223 if add_padding_line:
224 lines.append(get_padding_line(node_index, n_columns, edges))
224 lines.append(get_padding_line(node_index, n_columns, edges))
225 lines.append(shift_interline)
225 lines.append(shift_interline)
226
226
227 # make sure that there are as many graph lines as there are
227 # make sure that there are as many graph lines as there are
228 # log strings
228 # log strings
229 while len(node_lines) < len(lines):
229 while len(node_lines) < len(lines):
230 node_lines.append("")
230 node_lines.append("")
231 if len(lines) < len(node_lines):
231 if len(lines) < len(node_lines):
232 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
232 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
233 while len(lines) < len(node_lines):
233 while len(lines) < len(node_lines):
234 lines.append(extra_interline)
234 lines.append(extra_interline)
235
235
236 # print lines
236 # print lines
237 indentation_level = max(n_columns, n_columns + n_columns_diff)
237 indentation_level = max(n_columns, n_columns + n_columns_diff)
238 for (line, logstr) in zip(lines, node_lines):
238 for (line, logstr) in zip(lines, node_lines):
239 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
239 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
240 ui.write(ln.rstrip() + '\n')
240 ui.write(ln.rstrip() + '\n')
241
241
242 # ... and start over
242 # ... and start over
243 prev_node_index = node_index
243 prev_node_index = node_index
244 prev_n_columns_diff = n_columns_diff
244 prev_n_columns_diff = n_columns_diff
245
245
246 def get_revs(repo, rev_opt):
246 def get_revs(repo, rev_opt):
247 if rev_opt:
247 if rev_opt:
248 revs = revrange(repo, rev_opt)
248 revs = revrange(repo, rev_opt)
249 return (max(revs), min(revs))
249 return (max(revs), min(revs))
250 else:
250 else:
251 return (len(repo) - 1, 0)
251 return (len(repo) - 1, 0)
252
252
253 def check_unsupported_flags(opts):
253 def check_unsupported_flags(opts):
254 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
254 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
255 "only_merges", "user", "only_branch", "prune", "newest_first",
255 "only_merges", "user", "only_branch", "prune", "newest_first",
256 "no_merges", "include", "exclude"]:
256 "no_merges", "include", "exclude"]:
257 if op in opts and opts[op]:
257 if op in opts and opts[op]:
258 raise util.Abort(_("--graph option is incompatible with --%s") % op)
258 raise util.Abort(_("--graph option is incompatible with --%s") % op)
259
259
260 def graphlog(ui, repo, path=None, **opts):
260 def graphlog(ui, repo, path=None, **opts):
261 """show revision history alongside an ASCII revision graph
261 """show revision history alongside an ASCII revision graph
262
262
263 Print a revision history alongside a revision graph drawn with
263 Print a revision history alongside a revision graph drawn with
264 ASCII characters.
264 ASCII characters.
265
265
266 Nodes printed as an @ character are parents of the working
266 Nodes printed as an @ character are parents of the working
267 directory.
267 directory.
268 """
268 """
269
269
270 check_unsupported_flags(opts)
270 check_unsupported_flags(opts)
271 limit = cmdutil.loglimit(opts)
271 limit = cmdutil.loglimit(opts)
272 start, stop = get_revs(repo, opts["rev"])
272 start, stop = get_revs(repo, opts["rev"])
273 stop = max(stop, start - limit + 1)
273 stop = max(stop, start - limit + 1)
274 if start == nullrev:
274 if start == nullrev:
275 return
275 return
276
276
277 if path:
277 if path:
278 path = util.canonpath(repo.root, os.getcwd(), path)
278 path = util.canonpath(repo.root, os.getcwd(), path)
279 if path: # could be reset in canonpath
279 if path: # could be reset in canonpath
280 revdag = filerevs(repo, path, start, stop)
280 revdag = filerevs(repo, path, start, stop)
281 else:
281 else:
282 revdag = revisions(repo, start, stop)
282 revdag = revisions(repo, start, stop)
283
283
284 graphdag = graphabledag(ui, repo, revdag, opts)
284 graphdag = graphabledag(ui, repo, revdag, opts)
285 ascii(ui, grapher(graphdag))
285 ascii(ui, grapher(graphdag))
286
286
287 def graphrevs(repo, nodes, opts):
287 def graphrevs(repo, nodes, opts):
288 nodes.reverse()
288 nodes.reverse()
289 include = set(nodes)
289 include = set(nodes)
290 limit = cmdutil.loglimit(opts)
290 limit = cmdutil.loglimit(opts)
291 count = 0
291 count = 0
292 for node in nodes:
292 for node in nodes:
293 if count >= limit:
293 if count >= limit:
294 break
294 break
295 ctx = repo[node]
295 ctx = repo[node]
296 parents = [p.rev() for p in ctx.parents() if p.node() in include]
296 parents = [p.rev() for p in ctx.parents() if p.node() in include]
297 parents.sort()
297 parents.sort()
298 yield (ctx, parents)
298 yield (ctx, parents)
299 count += 1
299 count += 1
300
300
301 def graphabledag(ui, repo, revdag, opts):
301 def graphabledag(ui, repo, revdag, opts):
302 showparents = [ctx.node() for ctx in repo[None].parents()]
302 showparents = [ctx.node() for ctx in repo[None].parents()]
303 displayer = show_changeset(ui, repo, opts, buffered=True)
303 displayer = show_changeset(ui, repo, opts, buffered=True)
304 for (ctx, parents) in revdag:
304 for (ctx, parents) in revdag:
305 displayer.show(ctx)
305 displayer.show(ctx)
306 lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
306 lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
307 char = ctx.node() in showparents and '@' or 'o'
307 char = ctx.node() in showparents and '@' or 'o'
308 yield (ctx.rev(), parents, char, lines)
308 yield (ctx.rev(), parents, char, lines)
309
309
310 def goutgoing(ui, repo, dest=None, **opts):
310 def goutgoing(ui, repo, dest=None, **opts):
311 """show the outgoing changesets alongside an ASCII revision graph
311 """show the outgoing changesets alongside an ASCII revision graph
312
312
313 Print the outgoing changesets alongside a revision graph drawn with
313 Print the outgoing changesets alongside a revision graph drawn with
314 ASCII characters.
314 ASCII characters.
315
315
316 Nodes printed as an @ character are parents of the working
316 Nodes printed as an @ character are parents of the working
317 directory.
317 directory.
318 """
318 """
319
319
320 check_unsupported_flags(opts)
320 check_unsupported_flags(opts)
321 dest, revs, checkout = hg.parseurl(
321 dest, revs, checkout = hg.parseurl(
322 ui.expandpath(dest or 'default-push', dest or 'default'),
322 ui.expandpath(dest or 'default-push', dest or 'default'),
323 opts.get('rev'))
323 opts.get('rev'))
324 cmdutil.setremoteconfig(ui, opts)
325 if revs:
324 if revs:
326 revs = [repo.lookup(rev) for rev in revs]
325 revs = [repo.lookup(rev) for rev in revs]
327 other = hg.repository(ui, dest)
326 other = hg.repository(cmdutil.remoteui(ui, opts), dest)
328 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
327 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
329 o = repo.findoutgoing(other, force=opts.get('force'))
328 o = repo.findoutgoing(other, force=opts.get('force'))
330 if not o:
329 if not o:
331 ui.status(_("no changes found\n"))
330 ui.status(_("no changes found\n"))
332 return
331 return
333
332
334 o = repo.changelog.nodesbetween(o, revs)[0]
333 o = repo.changelog.nodesbetween(o, revs)[0]
335 revdag = graphrevs(repo, o, opts)
334 revdag = graphrevs(repo, o, opts)
336 graphdag = graphabledag(ui, repo, revdag, opts)
335 graphdag = graphabledag(ui, repo, revdag, opts)
337 ascii(ui, grapher(graphdag))
336 ascii(ui, grapher(graphdag))
338
337
339 def gincoming(ui, repo, source="default", **opts):
338 def gincoming(ui, repo, source="default", **opts):
340 """show the incoming changesets alongside an ASCII revision graph
339 """show the incoming changesets alongside an ASCII revision graph
341
340
342 Print the incoming changesets alongside a revision graph drawn with
341 Print the incoming changesets alongside a revision graph drawn with
343 ASCII characters.
342 ASCII characters.
344
343
345 Nodes printed as an @ character are parents of the working
344 Nodes printed as an @ character are parents of the working
346 directory.
345 directory.
347 """
346 """
348
347
349 check_unsupported_flags(opts)
348 check_unsupported_flags(opts)
350 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
349 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
351 cmdutil.setremoteconfig(ui, opts)
350 other = hg.repository(cmdutil.remoteui(repo, opts), source)
352
353 other = hg.repository(ui, source)
354 ui.status(_('comparing with %s\n') % url.hidepassword(source))
351 ui.status(_('comparing with %s\n') % url.hidepassword(source))
355 if revs:
352 if revs:
356 revs = [other.lookup(rev) for rev in revs]
353 revs = [other.lookup(rev) for rev in revs]
357 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
354 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
358 if not incoming:
355 if not incoming:
359 try:
356 try:
360 os.unlink(opts["bundle"])
357 os.unlink(opts["bundle"])
361 except:
358 except:
362 pass
359 pass
363 ui.status(_("no changes found\n"))
360 ui.status(_("no changes found\n"))
364 return
361 return
365
362
366 cleanup = None
363 cleanup = None
367 try:
364 try:
368
365
369 fname = opts["bundle"]
366 fname = opts["bundle"]
370 if fname or not other.local():
367 if fname or not other.local():
371 # create a bundle (uncompressed if other repo is not local)
368 # create a bundle (uncompressed if other repo is not local)
372 if revs is None:
369 if revs is None:
373 cg = other.changegroup(incoming, "incoming")
370 cg = other.changegroup(incoming, "incoming")
374 else:
371 else:
375 cg = other.changegroupsubset(incoming, revs, 'incoming')
372 cg = other.changegroupsubset(incoming, revs, 'incoming')
376 bundletype = other.local() and "HG10BZ" or "HG10UN"
373 bundletype = other.local() and "HG10BZ" or "HG10UN"
377 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
374 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
378 # keep written bundle?
375 # keep written bundle?
379 if opts["bundle"]:
376 if opts["bundle"]:
380 cleanup = None
377 cleanup = None
381 if not other.local():
378 if not other.local():
382 # use the created uncompressed bundlerepo
379 # use the created uncompressed bundlerepo
383 other = bundlerepo.bundlerepository(ui, repo.root, fname)
380 other = bundlerepo.bundlerepository(ui, repo.root, fname)
384
381
385 chlist = other.changelog.nodesbetween(incoming, revs)[0]
382 chlist = other.changelog.nodesbetween(incoming, revs)[0]
386 revdag = graphrevs(other, chlist, opts)
383 revdag = graphrevs(other, chlist, opts)
387 graphdag = graphabledag(ui, repo, revdag, opts)
384 graphdag = graphabledag(ui, repo, revdag, opts)
388 ascii(ui, grapher(graphdag))
385 ascii(ui, grapher(graphdag))
389
386
390 finally:
387 finally:
391 if hasattr(other, 'close'):
388 if hasattr(other, 'close'):
392 other.close()
389 other.close()
393 if cleanup:
390 if cleanup:
394 os.unlink(cleanup)
391 os.unlink(cleanup)
395
392
396 def uisetup(ui):
393 def uisetup(ui):
397 '''Initialize the extension.'''
394 '''Initialize the extension.'''
398 _wrapcmd(ui, 'log', commands.table, graphlog)
395 _wrapcmd(ui, 'log', commands.table, graphlog)
399 _wrapcmd(ui, 'incoming', commands.table, gincoming)
396 _wrapcmd(ui, 'incoming', commands.table, gincoming)
400 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
397 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
401
398
402 def _wrapcmd(ui, cmd, table, wrapfn):
399 def _wrapcmd(ui, cmd, table, wrapfn):
403 '''wrap the command'''
400 '''wrap the command'''
404 def graph(orig, *args, **kwargs):
401 def graph(orig, *args, **kwargs):
405 if kwargs['graph']:
402 if kwargs['graph']:
406 return wrapfn(*args, **kwargs)
403 return wrapfn(*args, **kwargs)
407 return orig(*args, **kwargs)
404 return orig(*args, **kwargs)
408 entry = extensions.wrapcommand(table, cmd, graph)
405 entry = extensions.wrapcommand(table, cmd, graph)
409 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
406 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
410
407
411 cmdtable = {
408 cmdtable = {
412 "glog":
409 "glog":
413 (graphlog,
410 (graphlog,
414 [('l', 'limit', '', _('limit number of changes displayed')),
411 [('l', 'limit', '', _('limit number of changes displayed')),
415 ('p', 'patch', False, _('show patch')),
412 ('p', 'patch', False, _('show patch')),
416 ('r', 'rev', [], _('show the specified revision or range')),
413 ('r', 'rev', [], _('show the specified revision or range')),
417 ] + templateopts,
414 ] + templateopts,
418 _('hg glog [OPTION]... [FILE]')),
415 _('hg glog [OPTION]... [FILE]')),
419 }
416 }
@@ -1,2611 +1,2610 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial.node import bin, hex, short, nullid, nullrev
33 from mercurial.node import bin, hex, short, nullid, nullrev
34 from mercurial.lock import release
34 from mercurial.lock import release
35 from mercurial import commands, cmdutil, hg, patch, util
35 from mercurial import commands, cmdutil, hg, patch, util
36 from mercurial import repair, extensions, url, error
36 from mercurial import repair, extensions, url, error
37 import os, sys, re, errno
37 import os, sys, re, errno
38
38
39 commands.norepo += " qclone"
39 commands.norepo += " qclone"
40
40
41 # Patch names looks like unix-file names.
41 # Patch names looks like unix-file names.
42 # They must be joinable with queue directory and result in the patch path.
42 # They must be joinable with queue directory and result in the patch path.
43 normname = util.normpath
43 normname = util.normpath
44
44
45 class statusentry:
45 class statusentry:
46 def __init__(self, rev, name=None):
46 def __init__(self, rev, name=None):
47 if not name:
47 if not name:
48 fields = rev.split(':', 1)
48 fields = rev.split(':', 1)
49 if len(fields) == 2:
49 if len(fields) == 2:
50 self.rev, self.name = fields
50 self.rev, self.name = fields
51 else:
51 else:
52 self.rev, self.name = None, None
52 self.rev, self.name = None, None
53 else:
53 else:
54 self.rev, self.name = rev, name
54 self.rev, self.name = rev, name
55
55
56 def __str__(self):
56 def __str__(self):
57 return self.rev + ':' + self.name
57 return self.rev + ':' + self.name
58
58
59 class patchheader(object):
59 class patchheader(object):
60 def __init__(self, message, comments, user, date, haspatch):
60 def __init__(self, message, comments, user, date, haspatch):
61 self.message = message
61 self.message = message
62 self.comments = comments
62 self.comments = comments
63 self.user = user
63 self.user = user
64 self.date = date
64 self.date = date
65 self.haspatch = haspatch
65 self.haspatch = haspatch
66
66
67 def setuser(self, user):
67 def setuser(self, user):
68 if not self.setheader(['From: ', '# User '], user):
68 if not self.setheader(['From: ', '# User '], user):
69 try:
69 try:
70 patchheaderat = self.comments.index('# HG changeset patch')
70 patchheaderat = self.comments.index('# HG changeset patch')
71 self.comments.insert(patchheaderat + 1,'# User ' + user)
71 self.comments.insert(patchheaderat + 1,'# User ' + user)
72 except ValueError:
72 except ValueError:
73 self.comments = ['From: ' + user, ''] + self.comments
73 self.comments = ['From: ' + user, ''] + self.comments
74 self.user = user
74 self.user = user
75
75
76 def setdate(self, date):
76 def setdate(self, date):
77 if self.setheader(['# Date '], date):
77 if self.setheader(['# Date '], date):
78 self.date = date
78 self.date = date
79
79
80 def setmessage(self, message):
80 def setmessage(self, message):
81 if self.comments:
81 if self.comments:
82 self._delmsg()
82 self._delmsg()
83 self.message = [message]
83 self.message = [message]
84 self.comments += self.message
84 self.comments += self.message
85
85
86 def setheader(self, prefixes, new):
86 def setheader(self, prefixes, new):
87 '''Update all references to a field in the patch header.
87 '''Update all references to a field in the patch header.
88 If none found, add it email style.'''
88 If none found, add it email style.'''
89 res = False
89 res = False
90 for prefix in prefixes:
90 for prefix in prefixes:
91 for i in xrange(len(self.comments)):
91 for i in xrange(len(self.comments)):
92 if self.comments[i].startswith(prefix):
92 if self.comments[i].startswith(prefix):
93 self.comments[i] = prefix + new
93 self.comments[i] = prefix + new
94 res = True
94 res = True
95 break
95 break
96 return res
96 return res
97
97
98 def __str__(self):
98 def __str__(self):
99 if not self.comments:
99 if not self.comments:
100 return ''
100 return ''
101 return '\n'.join(self.comments) + '\n\n'
101 return '\n'.join(self.comments) + '\n\n'
102
102
103 def _delmsg(self):
103 def _delmsg(self):
104 '''Remove existing message, keeping the rest of the comments fields.
104 '''Remove existing message, keeping the rest of the comments fields.
105 If comments contains 'subject: ', message will prepend
105 If comments contains 'subject: ', message will prepend
106 the field and a blank line.'''
106 the field and a blank line.'''
107 if self.message:
107 if self.message:
108 subj = 'subject: ' + self.message[0].lower()
108 subj = 'subject: ' + self.message[0].lower()
109 for i in xrange(len(self.comments)):
109 for i in xrange(len(self.comments)):
110 if subj == self.comments[i].lower():
110 if subj == self.comments[i].lower():
111 del self.comments[i]
111 del self.comments[i]
112 self.message = self.message[2:]
112 self.message = self.message[2:]
113 break
113 break
114 ci = 0
114 ci = 0
115 for mi in xrange(len(self.message)):
115 for mi in xrange(len(self.message)):
116 while self.message[mi] != self.comments[ci]:
116 while self.message[mi] != self.comments[ci]:
117 ci += 1
117 ci += 1
118 del self.comments[ci]
118 del self.comments[ci]
119
119
120 class queue:
120 class queue:
121 def __init__(self, ui, path, patchdir=None):
121 def __init__(self, ui, path, patchdir=None):
122 self.basepath = path
122 self.basepath = path
123 self.path = patchdir or os.path.join(path, "patches")
123 self.path = patchdir or os.path.join(path, "patches")
124 self.opener = util.opener(self.path)
124 self.opener = util.opener(self.path)
125 self.ui = ui
125 self.ui = ui
126 self.applied = []
126 self.applied = []
127 self.full_series = []
127 self.full_series = []
128 self.applied_dirty = 0
128 self.applied_dirty = 0
129 self.series_dirty = 0
129 self.series_dirty = 0
130 self.series_path = "series"
130 self.series_path = "series"
131 self.status_path = "status"
131 self.status_path = "status"
132 self.guards_path = "guards"
132 self.guards_path = "guards"
133 self.active_guards = None
133 self.active_guards = None
134 self.guards_dirty = False
134 self.guards_dirty = False
135 self._diffopts = None
135 self._diffopts = None
136
136
137 if os.path.exists(self.join(self.series_path)):
137 if os.path.exists(self.join(self.series_path)):
138 self.full_series = self.opener(self.series_path).read().splitlines()
138 self.full_series = self.opener(self.series_path).read().splitlines()
139 self.parse_series()
139 self.parse_series()
140
140
141 if os.path.exists(self.join(self.status_path)):
141 if os.path.exists(self.join(self.status_path)):
142 lines = self.opener(self.status_path).read().splitlines()
142 lines = self.opener(self.status_path).read().splitlines()
143 self.applied = [statusentry(l) for l in lines]
143 self.applied = [statusentry(l) for l in lines]
144
144
145 def diffopts(self):
145 def diffopts(self):
146 if self._diffopts is None:
146 if self._diffopts is None:
147 self._diffopts = patch.diffopts(self.ui)
147 self._diffopts = patch.diffopts(self.ui)
148 return self._diffopts
148 return self._diffopts
149
149
150 def join(self, *p):
150 def join(self, *p):
151 return os.path.join(self.path, *p)
151 return os.path.join(self.path, *p)
152
152
153 def find_series(self, patch):
153 def find_series(self, patch):
154 pre = re.compile("(\s*)([^#]+)")
154 pre = re.compile("(\s*)([^#]+)")
155 index = 0
155 index = 0
156 for l in self.full_series:
156 for l in self.full_series:
157 m = pre.match(l)
157 m = pre.match(l)
158 if m:
158 if m:
159 s = m.group(2)
159 s = m.group(2)
160 s = s.rstrip()
160 s = s.rstrip()
161 if s == patch:
161 if s == patch:
162 return index
162 return index
163 index += 1
163 index += 1
164 return None
164 return None
165
165
166 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
166 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
167
167
168 def parse_series(self):
168 def parse_series(self):
169 self.series = []
169 self.series = []
170 self.series_guards = []
170 self.series_guards = []
171 for l in self.full_series:
171 for l in self.full_series:
172 h = l.find('#')
172 h = l.find('#')
173 if h == -1:
173 if h == -1:
174 patch = l
174 patch = l
175 comment = ''
175 comment = ''
176 elif h == 0:
176 elif h == 0:
177 continue
177 continue
178 else:
178 else:
179 patch = l[:h]
179 patch = l[:h]
180 comment = l[h:]
180 comment = l[h:]
181 patch = patch.strip()
181 patch = patch.strip()
182 if patch:
182 if patch:
183 if patch in self.series:
183 if patch in self.series:
184 raise util.Abort(_('%s appears more than once in %s') %
184 raise util.Abort(_('%s appears more than once in %s') %
185 (patch, self.join(self.series_path)))
185 (patch, self.join(self.series_path)))
186 self.series.append(patch)
186 self.series.append(patch)
187 self.series_guards.append(self.guard_re.findall(comment))
187 self.series_guards.append(self.guard_re.findall(comment))
188
188
189 def check_guard(self, guard):
189 def check_guard(self, guard):
190 if not guard:
190 if not guard:
191 return _('guard cannot be an empty string')
191 return _('guard cannot be an empty string')
192 bad_chars = '# \t\r\n\f'
192 bad_chars = '# \t\r\n\f'
193 first = guard[0]
193 first = guard[0]
194 for c in '-+':
194 for c in '-+':
195 if first == c:
195 if first == c:
196 return (_('guard %r starts with invalid character: %r') %
196 return (_('guard %r starts with invalid character: %r') %
197 (guard, c))
197 (guard, c))
198 for c in bad_chars:
198 for c in bad_chars:
199 if c in guard:
199 if c in guard:
200 return _('invalid character in guard %r: %r') % (guard, c)
200 return _('invalid character in guard %r: %r') % (guard, c)
201
201
202 def set_active(self, guards):
202 def set_active(self, guards):
203 for guard in guards:
203 for guard in guards:
204 bad = self.check_guard(guard)
204 bad = self.check_guard(guard)
205 if bad:
205 if bad:
206 raise util.Abort(bad)
206 raise util.Abort(bad)
207 guards = util.sort(set(guards))
207 guards = util.sort(set(guards))
208 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
208 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
209 self.active_guards = guards
209 self.active_guards = guards
210 self.guards_dirty = True
210 self.guards_dirty = True
211
211
212 def active(self):
212 def active(self):
213 if self.active_guards is None:
213 if self.active_guards is None:
214 self.active_guards = []
214 self.active_guards = []
215 try:
215 try:
216 guards = self.opener(self.guards_path).read().split()
216 guards = self.opener(self.guards_path).read().split()
217 except IOError, err:
217 except IOError, err:
218 if err.errno != errno.ENOENT: raise
218 if err.errno != errno.ENOENT: raise
219 guards = []
219 guards = []
220 for i, guard in enumerate(guards):
220 for i, guard in enumerate(guards):
221 bad = self.check_guard(guard)
221 bad = self.check_guard(guard)
222 if bad:
222 if bad:
223 self.ui.warn('%s:%d: %s\n' %
223 self.ui.warn('%s:%d: %s\n' %
224 (self.join(self.guards_path), i + 1, bad))
224 (self.join(self.guards_path), i + 1, bad))
225 else:
225 else:
226 self.active_guards.append(guard)
226 self.active_guards.append(guard)
227 return self.active_guards
227 return self.active_guards
228
228
229 def set_guards(self, idx, guards):
229 def set_guards(self, idx, guards):
230 for g in guards:
230 for g in guards:
231 if len(g) < 2:
231 if len(g) < 2:
232 raise util.Abort(_('guard %r too short') % g)
232 raise util.Abort(_('guard %r too short') % g)
233 if g[0] not in '-+':
233 if g[0] not in '-+':
234 raise util.Abort(_('guard %r starts with invalid char') % g)
234 raise util.Abort(_('guard %r starts with invalid char') % g)
235 bad = self.check_guard(g[1:])
235 bad = self.check_guard(g[1:])
236 if bad:
236 if bad:
237 raise util.Abort(bad)
237 raise util.Abort(bad)
238 drop = self.guard_re.sub('', self.full_series[idx])
238 drop = self.guard_re.sub('', self.full_series[idx])
239 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
239 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
240 self.parse_series()
240 self.parse_series()
241 self.series_dirty = True
241 self.series_dirty = True
242
242
243 def pushable(self, idx):
243 def pushable(self, idx):
244 if isinstance(idx, str):
244 if isinstance(idx, str):
245 idx = self.series.index(idx)
245 idx = self.series.index(idx)
246 patchguards = self.series_guards[idx]
246 patchguards = self.series_guards[idx]
247 if not patchguards:
247 if not patchguards:
248 return True, None
248 return True, None
249 guards = self.active()
249 guards = self.active()
250 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
250 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
251 if exactneg:
251 if exactneg:
252 return False, exactneg[0]
252 return False, exactneg[0]
253 pos = [g for g in patchguards if g[0] == '+']
253 pos = [g for g in patchguards if g[0] == '+']
254 exactpos = [g for g in pos if g[1:] in guards]
254 exactpos = [g for g in pos if g[1:] in guards]
255 if pos:
255 if pos:
256 if exactpos:
256 if exactpos:
257 return True, exactpos[0]
257 return True, exactpos[0]
258 return False, pos
258 return False, pos
259 return True, ''
259 return True, ''
260
260
261 def explain_pushable(self, idx, all_patches=False):
261 def explain_pushable(self, idx, all_patches=False):
262 write = all_patches and self.ui.write or self.ui.warn
262 write = all_patches and self.ui.write or self.ui.warn
263 if all_patches or self.ui.verbose:
263 if all_patches or self.ui.verbose:
264 if isinstance(idx, str):
264 if isinstance(idx, str):
265 idx = self.series.index(idx)
265 idx = self.series.index(idx)
266 pushable, why = self.pushable(idx)
266 pushable, why = self.pushable(idx)
267 if all_patches and pushable:
267 if all_patches and pushable:
268 if why is None:
268 if why is None:
269 write(_('allowing %s - no guards in effect\n') %
269 write(_('allowing %s - no guards in effect\n') %
270 self.series[idx])
270 self.series[idx])
271 else:
271 else:
272 if not why:
272 if not why:
273 write(_('allowing %s - no matching negative guards\n') %
273 write(_('allowing %s - no matching negative guards\n') %
274 self.series[idx])
274 self.series[idx])
275 else:
275 else:
276 write(_('allowing %s - guarded by %r\n') %
276 write(_('allowing %s - guarded by %r\n') %
277 (self.series[idx], why))
277 (self.series[idx], why))
278 if not pushable:
278 if not pushable:
279 if why:
279 if why:
280 write(_('skipping %s - guarded by %r\n') %
280 write(_('skipping %s - guarded by %r\n') %
281 (self.series[idx], why))
281 (self.series[idx], why))
282 else:
282 else:
283 write(_('skipping %s - no matching guards\n') %
283 write(_('skipping %s - no matching guards\n') %
284 self.series[idx])
284 self.series[idx])
285
285
286 def save_dirty(self):
286 def save_dirty(self):
287 def write_list(items, path):
287 def write_list(items, path):
288 fp = self.opener(path, 'w')
288 fp = self.opener(path, 'w')
289 for i in items:
289 for i in items:
290 fp.write("%s\n" % i)
290 fp.write("%s\n" % i)
291 fp.close()
291 fp.close()
292 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
292 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
293 if self.series_dirty: write_list(self.full_series, self.series_path)
293 if self.series_dirty: write_list(self.full_series, self.series_path)
294 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
294 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
295
295
296 def readheaders(self, patch):
296 def readheaders(self, patch):
297 def eatdiff(lines):
297 def eatdiff(lines):
298 while lines:
298 while lines:
299 l = lines[-1]
299 l = lines[-1]
300 if (l.startswith("diff -") or
300 if (l.startswith("diff -") or
301 l.startswith("Index:") or
301 l.startswith("Index:") or
302 l.startswith("===========")):
302 l.startswith("===========")):
303 del lines[-1]
303 del lines[-1]
304 else:
304 else:
305 break
305 break
306 def eatempty(lines):
306 def eatempty(lines):
307 while lines:
307 while lines:
308 l = lines[-1]
308 l = lines[-1]
309 if re.match('\s*$', l):
309 if re.match('\s*$', l):
310 del lines[-1]
310 del lines[-1]
311 else:
311 else:
312 break
312 break
313
313
314 pf = self.join(patch)
314 pf = self.join(patch)
315 message = []
315 message = []
316 comments = []
316 comments = []
317 user = None
317 user = None
318 date = None
318 date = None
319 format = None
319 format = None
320 subject = None
320 subject = None
321 diffstart = 0
321 diffstart = 0
322
322
323 for line in file(pf):
323 for line in file(pf):
324 line = line.rstrip()
324 line = line.rstrip()
325 if line.startswith('diff --git'):
325 if line.startswith('diff --git'):
326 diffstart = 2
326 diffstart = 2
327 break
327 break
328 if diffstart:
328 if diffstart:
329 if line.startswith('+++ '):
329 if line.startswith('+++ '):
330 diffstart = 2
330 diffstart = 2
331 break
331 break
332 if line.startswith("--- "):
332 if line.startswith("--- "):
333 diffstart = 1
333 diffstart = 1
334 continue
334 continue
335 elif format == "hgpatch":
335 elif format == "hgpatch":
336 # parse values when importing the result of an hg export
336 # parse values when importing the result of an hg export
337 if line.startswith("# User "):
337 if line.startswith("# User "):
338 user = line[7:]
338 user = line[7:]
339 elif line.startswith("# Date "):
339 elif line.startswith("# Date "):
340 date = line[7:]
340 date = line[7:]
341 elif not line.startswith("# ") and line:
341 elif not line.startswith("# ") and line:
342 message.append(line)
342 message.append(line)
343 format = None
343 format = None
344 elif line == '# HG changeset patch':
344 elif line == '# HG changeset patch':
345 format = "hgpatch"
345 format = "hgpatch"
346 elif (format != "tagdone" and (line.startswith("Subject: ") or
346 elif (format != "tagdone" and (line.startswith("Subject: ") or
347 line.startswith("subject: "))):
347 line.startswith("subject: "))):
348 subject = line[9:]
348 subject = line[9:]
349 format = "tag"
349 format = "tag"
350 elif (format != "tagdone" and (line.startswith("From: ") or
350 elif (format != "tagdone" and (line.startswith("From: ") or
351 line.startswith("from: "))):
351 line.startswith("from: "))):
352 user = line[6:]
352 user = line[6:]
353 format = "tag"
353 format = "tag"
354 elif format == "tag" and line == "":
354 elif format == "tag" and line == "":
355 # when looking for tags (subject: from: etc) they
355 # when looking for tags (subject: from: etc) they
356 # end once you find a blank line in the source
356 # end once you find a blank line in the source
357 format = "tagdone"
357 format = "tagdone"
358 elif message or line:
358 elif message or line:
359 message.append(line)
359 message.append(line)
360 comments.append(line)
360 comments.append(line)
361
361
362 eatdiff(message)
362 eatdiff(message)
363 eatdiff(comments)
363 eatdiff(comments)
364 eatempty(message)
364 eatempty(message)
365 eatempty(comments)
365 eatempty(comments)
366
366
367 # make sure message isn't empty
367 # make sure message isn't empty
368 if format and format.startswith("tag") and subject:
368 if format and format.startswith("tag") and subject:
369 message.insert(0, "")
369 message.insert(0, "")
370 message.insert(0, subject)
370 message.insert(0, subject)
371 return patchheader(message, comments, user, date, diffstart > 1)
371 return patchheader(message, comments, user, date, diffstart > 1)
372
372
373 def removeundo(self, repo):
373 def removeundo(self, repo):
374 undo = repo.sjoin('undo')
374 undo = repo.sjoin('undo')
375 if not os.path.exists(undo):
375 if not os.path.exists(undo):
376 return
376 return
377 try:
377 try:
378 os.unlink(undo)
378 os.unlink(undo)
379 except OSError, inst:
379 except OSError, inst:
380 self.ui.warn(_('error removing undo: %s\n') % str(inst))
380 self.ui.warn(_('error removing undo: %s\n') % str(inst))
381
381
382 def printdiff(self, repo, node1, node2=None, files=None,
382 def printdiff(self, repo, node1, node2=None, files=None,
383 fp=None, changes=None, opts={}):
383 fp=None, changes=None, opts={}):
384 m = cmdutil.match(repo, files, opts)
384 m = cmdutil.match(repo, files, opts)
385 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
385 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
386 write = fp is None and repo.ui.write or fp.write
386 write = fp is None and repo.ui.write or fp.write
387 for chunk in chunks:
387 for chunk in chunks:
388 write(chunk)
388 write(chunk)
389
389
390 def mergeone(self, repo, mergeq, head, patch, rev):
390 def mergeone(self, repo, mergeq, head, patch, rev):
391 # first try just applying the patch
391 # first try just applying the patch
392 (err, n) = self.apply(repo, [ patch ], update_status=False,
392 (err, n) = self.apply(repo, [ patch ], update_status=False,
393 strict=True, merge=rev)
393 strict=True, merge=rev)
394
394
395 if err == 0:
395 if err == 0:
396 return (err, n)
396 return (err, n)
397
397
398 if n is None:
398 if n is None:
399 raise util.Abort(_("apply failed for patch %s") % patch)
399 raise util.Abort(_("apply failed for patch %s") % patch)
400
400
401 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
401 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
402
402
403 # apply failed, strip away that rev and merge.
403 # apply failed, strip away that rev and merge.
404 hg.clean(repo, head)
404 hg.clean(repo, head)
405 self.strip(repo, n, update=False, backup='strip')
405 self.strip(repo, n, update=False, backup='strip')
406
406
407 ctx = repo[rev]
407 ctx = repo[rev]
408 ret = hg.merge(repo, rev)
408 ret = hg.merge(repo, rev)
409 if ret:
409 if ret:
410 raise util.Abort(_("update returned %d") % ret)
410 raise util.Abort(_("update returned %d") % ret)
411 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
411 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
412 if n == None:
412 if n == None:
413 raise util.Abort(_("repo commit failed"))
413 raise util.Abort(_("repo commit failed"))
414 try:
414 try:
415 ph = mergeq.readheaders(patch)
415 ph = mergeq.readheaders(patch)
416 except:
416 except:
417 raise util.Abort(_("unable to read %s") % patch)
417 raise util.Abort(_("unable to read %s") % patch)
418
418
419 patchf = self.opener(patch, "w")
419 patchf = self.opener(patch, "w")
420 comments = str(ph)
420 comments = str(ph)
421 if comments:
421 if comments:
422 patchf.write(comments)
422 patchf.write(comments)
423 self.printdiff(repo, head, n, fp=patchf)
423 self.printdiff(repo, head, n, fp=patchf)
424 patchf.close()
424 patchf.close()
425 self.removeundo(repo)
425 self.removeundo(repo)
426 return (0, n)
426 return (0, n)
427
427
428 def qparents(self, repo, rev=None):
428 def qparents(self, repo, rev=None):
429 if rev is None:
429 if rev is None:
430 (p1, p2) = repo.dirstate.parents()
430 (p1, p2) = repo.dirstate.parents()
431 if p2 == nullid:
431 if p2 == nullid:
432 return p1
432 return p1
433 if len(self.applied) == 0:
433 if len(self.applied) == 0:
434 return None
434 return None
435 return bin(self.applied[-1].rev)
435 return bin(self.applied[-1].rev)
436 pp = repo.changelog.parents(rev)
436 pp = repo.changelog.parents(rev)
437 if pp[1] != nullid:
437 if pp[1] != nullid:
438 arevs = [ x.rev for x in self.applied ]
438 arevs = [ x.rev for x in self.applied ]
439 p0 = hex(pp[0])
439 p0 = hex(pp[0])
440 p1 = hex(pp[1])
440 p1 = hex(pp[1])
441 if p0 in arevs:
441 if p0 in arevs:
442 return pp[0]
442 return pp[0]
443 if p1 in arevs:
443 if p1 in arevs:
444 return pp[1]
444 return pp[1]
445 return pp[0]
445 return pp[0]
446
446
447 def mergepatch(self, repo, mergeq, series):
447 def mergepatch(self, repo, mergeq, series):
448 if len(self.applied) == 0:
448 if len(self.applied) == 0:
449 # each of the patches merged in will have two parents. This
449 # each of the patches merged in will have two parents. This
450 # can confuse the qrefresh, qdiff, and strip code because it
450 # can confuse the qrefresh, qdiff, and strip code because it
451 # needs to know which parent is actually in the patch queue.
451 # needs to know which parent is actually in the patch queue.
452 # so, we insert a merge marker with only one parent. This way
452 # so, we insert a merge marker with only one parent. This way
453 # the first patch in the queue is never a merge patch
453 # the first patch in the queue is never a merge patch
454 #
454 #
455 pname = ".hg.patches.merge.marker"
455 pname = ".hg.patches.merge.marker"
456 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
456 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
457 self.removeundo(repo)
457 self.removeundo(repo)
458 self.applied.append(statusentry(hex(n), pname))
458 self.applied.append(statusentry(hex(n), pname))
459 self.applied_dirty = 1
459 self.applied_dirty = 1
460
460
461 head = self.qparents(repo)
461 head = self.qparents(repo)
462
462
463 for patch in series:
463 for patch in series:
464 patch = mergeq.lookup(patch, strict=True)
464 patch = mergeq.lookup(patch, strict=True)
465 if not patch:
465 if not patch:
466 self.ui.warn(_("patch %s does not exist\n") % patch)
466 self.ui.warn(_("patch %s does not exist\n") % patch)
467 return (1, None)
467 return (1, None)
468 pushable, reason = self.pushable(patch)
468 pushable, reason = self.pushable(patch)
469 if not pushable:
469 if not pushable:
470 self.explain_pushable(patch, all_patches=True)
470 self.explain_pushable(patch, all_patches=True)
471 continue
471 continue
472 info = mergeq.isapplied(patch)
472 info = mergeq.isapplied(patch)
473 if not info:
473 if not info:
474 self.ui.warn(_("patch %s is not applied\n") % patch)
474 self.ui.warn(_("patch %s is not applied\n") % patch)
475 return (1, None)
475 return (1, None)
476 rev = bin(info[1])
476 rev = bin(info[1])
477 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
477 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
478 if head:
478 if head:
479 self.applied.append(statusentry(hex(head), patch))
479 self.applied.append(statusentry(hex(head), patch))
480 self.applied_dirty = 1
480 self.applied_dirty = 1
481 if err:
481 if err:
482 return (err, head)
482 return (err, head)
483 self.save_dirty()
483 self.save_dirty()
484 return (0, head)
484 return (0, head)
485
485
486 def patch(self, repo, patchfile):
486 def patch(self, repo, patchfile):
487 '''Apply patchfile to the working directory.
487 '''Apply patchfile to the working directory.
488 patchfile: file name of patch'''
488 patchfile: file name of patch'''
489 files = {}
489 files = {}
490 try:
490 try:
491 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
491 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
492 files=files)
492 files=files)
493 except Exception, inst:
493 except Exception, inst:
494 self.ui.note(str(inst) + '\n')
494 self.ui.note(str(inst) + '\n')
495 if not self.ui.verbose:
495 if not self.ui.verbose:
496 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
496 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
497 return (False, files, False)
497 return (False, files, False)
498
498
499 return (True, files, fuzz)
499 return (True, files, fuzz)
500
500
501 def apply(self, repo, series, list=False, update_status=True,
501 def apply(self, repo, series, list=False, update_status=True,
502 strict=False, patchdir=None, merge=None, all_files={}):
502 strict=False, patchdir=None, merge=None, all_files={}):
503 wlock = lock = tr = None
503 wlock = lock = tr = None
504 try:
504 try:
505 wlock = repo.wlock()
505 wlock = repo.wlock()
506 lock = repo.lock()
506 lock = repo.lock()
507 tr = repo.transaction()
507 tr = repo.transaction()
508 try:
508 try:
509 ret = self._apply(repo, series, list, update_status,
509 ret = self._apply(repo, series, list, update_status,
510 strict, patchdir, merge, all_files=all_files)
510 strict, patchdir, merge, all_files=all_files)
511 tr.close()
511 tr.close()
512 self.save_dirty()
512 self.save_dirty()
513 return ret
513 return ret
514 except:
514 except:
515 try:
515 try:
516 tr.abort()
516 tr.abort()
517 finally:
517 finally:
518 repo.invalidate()
518 repo.invalidate()
519 repo.dirstate.invalidate()
519 repo.dirstate.invalidate()
520 raise
520 raise
521 finally:
521 finally:
522 del tr
522 del tr
523 release(lock, wlock)
523 release(lock, wlock)
524 self.removeundo(repo)
524 self.removeundo(repo)
525
525
526 def _apply(self, repo, series, list=False, update_status=True,
526 def _apply(self, repo, series, list=False, update_status=True,
527 strict=False, patchdir=None, merge=None, all_files={}):
527 strict=False, patchdir=None, merge=None, all_files={}):
528 # TODO unify with commands.py
528 # TODO unify with commands.py
529 if not patchdir:
529 if not patchdir:
530 patchdir = self.path
530 patchdir = self.path
531 err = 0
531 err = 0
532 n = None
532 n = None
533 for patchname in series:
533 for patchname in series:
534 pushable, reason = self.pushable(patchname)
534 pushable, reason = self.pushable(patchname)
535 if not pushable:
535 if not pushable:
536 self.explain_pushable(patchname, all_patches=True)
536 self.explain_pushable(patchname, all_patches=True)
537 continue
537 continue
538 self.ui.warn(_("applying %s\n") % patchname)
538 self.ui.warn(_("applying %s\n") % patchname)
539 pf = os.path.join(patchdir, patchname)
539 pf = os.path.join(patchdir, patchname)
540
540
541 try:
541 try:
542 ph = self.readheaders(patchname)
542 ph = self.readheaders(patchname)
543 except:
543 except:
544 self.ui.warn(_("Unable to read %s\n") % patchname)
544 self.ui.warn(_("Unable to read %s\n") % patchname)
545 err = 1
545 err = 1
546 break
546 break
547
547
548 message = ph.message
548 message = ph.message
549 if not message:
549 if not message:
550 message = _("imported patch %s\n") % patchname
550 message = _("imported patch %s\n") % patchname
551 else:
551 else:
552 if list:
552 if list:
553 message.append(_("\nimported patch %s") % patchname)
553 message.append(_("\nimported patch %s") % patchname)
554 message = '\n'.join(message)
554 message = '\n'.join(message)
555
555
556 if ph.haspatch:
556 if ph.haspatch:
557 (patcherr, files, fuzz) = self.patch(repo, pf)
557 (patcherr, files, fuzz) = self.patch(repo, pf)
558 all_files.update(files)
558 all_files.update(files)
559 patcherr = not patcherr
559 patcherr = not patcherr
560 else:
560 else:
561 self.ui.warn(_("patch %s is empty\n") % patchname)
561 self.ui.warn(_("patch %s is empty\n") % patchname)
562 patcherr, files, fuzz = 0, [], 0
562 patcherr, files, fuzz = 0, [], 0
563
563
564 if merge and files:
564 if merge and files:
565 # Mark as removed/merged and update dirstate parent info
565 # Mark as removed/merged and update dirstate parent info
566 removed = []
566 removed = []
567 merged = []
567 merged = []
568 for f in files:
568 for f in files:
569 if os.path.exists(repo.wjoin(f)):
569 if os.path.exists(repo.wjoin(f)):
570 merged.append(f)
570 merged.append(f)
571 else:
571 else:
572 removed.append(f)
572 removed.append(f)
573 for f in removed:
573 for f in removed:
574 repo.dirstate.remove(f)
574 repo.dirstate.remove(f)
575 for f in merged:
575 for f in merged:
576 repo.dirstate.merge(f)
576 repo.dirstate.merge(f)
577 p1, p2 = repo.dirstate.parents()
577 p1, p2 = repo.dirstate.parents()
578 repo.dirstate.setparents(p1, merge)
578 repo.dirstate.setparents(p1, merge)
579
579
580 files = patch.updatedir(self.ui, repo, files)
580 files = patch.updatedir(self.ui, repo, files)
581 match = cmdutil.matchfiles(repo, files or [])
581 match = cmdutil.matchfiles(repo, files or [])
582 n = repo.commit(files, message, ph.user, ph.date, match=match,
582 n = repo.commit(files, message, ph.user, ph.date, match=match,
583 force=True)
583 force=True)
584
584
585 if n == None:
585 if n == None:
586 raise util.Abort(_("repo commit failed"))
586 raise util.Abort(_("repo commit failed"))
587
587
588 if update_status:
588 if update_status:
589 self.applied.append(statusentry(hex(n), patchname))
589 self.applied.append(statusentry(hex(n), patchname))
590
590
591 if patcherr:
591 if patcherr:
592 self.ui.warn(_("patch failed, rejects left in working dir\n"))
592 self.ui.warn(_("patch failed, rejects left in working dir\n"))
593 err = 1
593 err = 1
594 break
594 break
595
595
596 if fuzz and strict:
596 if fuzz and strict:
597 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
597 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
598 err = 1
598 err = 1
599 break
599 break
600 return (err, n)
600 return (err, n)
601
601
602 def _clean_series(self, patches):
602 def _clean_series(self, patches):
603 indices = util.sort([self.find_series(p) for p in patches])
603 indices = util.sort([self.find_series(p) for p in patches])
604 for i in indices[-1::-1]:
604 for i in indices[-1::-1]:
605 del self.full_series[i]
605 del self.full_series[i]
606 self.parse_series()
606 self.parse_series()
607 self.series_dirty = 1
607 self.series_dirty = 1
608
608
609 def finish(self, repo, revs):
609 def finish(self, repo, revs):
610 revs.sort()
610 revs.sort()
611 firstrev = repo[self.applied[0].rev].rev()
611 firstrev = repo[self.applied[0].rev].rev()
612 appliedbase = 0
612 appliedbase = 0
613 patches = []
613 patches = []
614 for rev in util.sort(revs):
614 for rev in util.sort(revs):
615 if rev < firstrev:
615 if rev < firstrev:
616 raise util.Abort(_('revision %d is not managed') % rev)
616 raise util.Abort(_('revision %d is not managed') % rev)
617 base = bin(self.applied[appliedbase].rev)
617 base = bin(self.applied[appliedbase].rev)
618 node = repo.changelog.node(rev)
618 node = repo.changelog.node(rev)
619 if node != base:
619 if node != base:
620 raise util.Abort(_('cannot delete revision %d above '
620 raise util.Abort(_('cannot delete revision %d above '
621 'applied patches') % rev)
621 'applied patches') % rev)
622 patches.append(self.applied[appliedbase].name)
622 patches.append(self.applied[appliedbase].name)
623 appliedbase += 1
623 appliedbase += 1
624
624
625 r = self.qrepo()
625 r = self.qrepo()
626 if r:
626 if r:
627 r.remove(patches, True)
627 r.remove(patches, True)
628 else:
628 else:
629 for p in patches:
629 for p in patches:
630 os.unlink(self.join(p))
630 os.unlink(self.join(p))
631
631
632 del self.applied[:appliedbase]
632 del self.applied[:appliedbase]
633 self.applied_dirty = 1
633 self.applied_dirty = 1
634 self._clean_series(patches)
634 self._clean_series(patches)
635
635
636 def delete(self, repo, patches, opts):
636 def delete(self, repo, patches, opts):
637 if not patches and not opts.get('rev'):
637 if not patches and not opts.get('rev'):
638 raise util.Abort(_('qdelete requires at least one revision or '
638 raise util.Abort(_('qdelete requires at least one revision or '
639 'patch name'))
639 'patch name'))
640
640
641 realpatches = []
641 realpatches = []
642 for patch in patches:
642 for patch in patches:
643 patch = self.lookup(patch, strict=True)
643 patch = self.lookup(patch, strict=True)
644 info = self.isapplied(patch)
644 info = self.isapplied(patch)
645 if info:
645 if info:
646 raise util.Abort(_("cannot delete applied patch %s") % patch)
646 raise util.Abort(_("cannot delete applied patch %s") % patch)
647 if patch not in self.series:
647 if patch not in self.series:
648 raise util.Abort(_("patch %s not in series file") % patch)
648 raise util.Abort(_("patch %s not in series file") % patch)
649 realpatches.append(patch)
649 realpatches.append(patch)
650
650
651 appliedbase = 0
651 appliedbase = 0
652 if opts.get('rev'):
652 if opts.get('rev'):
653 if not self.applied:
653 if not self.applied:
654 raise util.Abort(_('no patches applied'))
654 raise util.Abort(_('no patches applied'))
655 revs = cmdutil.revrange(repo, opts['rev'])
655 revs = cmdutil.revrange(repo, opts['rev'])
656 if len(revs) > 1 and revs[0] > revs[1]:
656 if len(revs) > 1 and revs[0] > revs[1]:
657 revs.reverse()
657 revs.reverse()
658 for rev in revs:
658 for rev in revs:
659 if appliedbase >= len(self.applied):
659 if appliedbase >= len(self.applied):
660 raise util.Abort(_("revision %d is not managed") % rev)
660 raise util.Abort(_("revision %d is not managed") % rev)
661
661
662 base = bin(self.applied[appliedbase].rev)
662 base = bin(self.applied[appliedbase].rev)
663 node = repo.changelog.node(rev)
663 node = repo.changelog.node(rev)
664 if node != base:
664 if node != base:
665 raise util.Abort(_("cannot delete revision %d above "
665 raise util.Abort(_("cannot delete revision %d above "
666 "applied patches") % rev)
666 "applied patches") % rev)
667 realpatches.append(self.applied[appliedbase].name)
667 realpatches.append(self.applied[appliedbase].name)
668 appliedbase += 1
668 appliedbase += 1
669
669
670 if not opts.get('keep'):
670 if not opts.get('keep'):
671 r = self.qrepo()
671 r = self.qrepo()
672 if r:
672 if r:
673 r.remove(realpatches, True)
673 r.remove(realpatches, True)
674 else:
674 else:
675 for p in realpatches:
675 for p in realpatches:
676 os.unlink(self.join(p))
676 os.unlink(self.join(p))
677
677
678 if appliedbase:
678 if appliedbase:
679 del self.applied[:appliedbase]
679 del self.applied[:appliedbase]
680 self.applied_dirty = 1
680 self.applied_dirty = 1
681 self._clean_series(realpatches)
681 self._clean_series(realpatches)
682
682
683 def check_toppatch(self, repo):
683 def check_toppatch(self, repo):
684 if len(self.applied) > 0:
684 if len(self.applied) > 0:
685 top = bin(self.applied[-1].rev)
685 top = bin(self.applied[-1].rev)
686 pp = repo.dirstate.parents()
686 pp = repo.dirstate.parents()
687 if top not in pp:
687 if top not in pp:
688 raise util.Abort(_("working directory revision is not qtip"))
688 raise util.Abort(_("working directory revision is not qtip"))
689 return top
689 return top
690 return None
690 return None
691 def check_localchanges(self, repo, force=False, refresh=True):
691 def check_localchanges(self, repo, force=False, refresh=True):
692 m, a, r, d = repo.status()[:4]
692 m, a, r, d = repo.status()[:4]
693 if m or a or r or d:
693 if m or a or r or d:
694 if not force:
694 if not force:
695 if refresh:
695 if refresh:
696 raise util.Abort(_("local changes found, refresh first"))
696 raise util.Abort(_("local changes found, refresh first"))
697 else:
697 else:
698 raise util.Abort(_("local changes found"))
698 raise util.Abort(_("local changes found"))
699 return m, a, r, d
699 return m, a, r, d
700
700
701 _reserved = ('series', 'status', 'guards')
701 _reserved = ('series', 'status', 'guards')
702 def check_reserved_name(self, name):
702 def check_reserved_name(self, name):
703 if (name in self._reserved or name.startswith('.hg')
703 if (name in self._reserved or name.startswith('.hg')
704 or name.startswith('.mq')):
704 or name.startswith('.mq')):
705 raise util.Abort(_('"%s" cannot be used as the name of a patch')
705 raise util.Abort(_('"%s" cannot be used as the name of a patch')
706 % name)
706 % name)
707
707
708 def new(self, repo, patchfn, *pats, **opts):
708 def new(self, repo, patchfn, *pats, **opts):
709 """options:
709 """options:
710 msg: a string or a no-argument function returning a string
710 msg: a string or a no-argument function returning a string
711 """
711 """
712 msg = opts.get('msg')
712 msg = opts.get('msg')
713 force = opts.get('force')
713 force = opts.get('force')
714 user = opts.get('user')
714 user = opts.get('user')
715 date = opts.get('date')
715 date = opts.get('date')
716 if date:
716 if date:
717 date = util.parsedate(date)
717 date = util.parsedate(date)
718 self.check_reserved_name(patchfn)
718 self.check_reserved_name(patchfn)
719 if os.path.exists(self.join(patchfn)):
719 if os.path.exists(self.join(patchfn)):
720 raise util.Abort(_('patch "%s" already exists') % patchfn)
720 raise util.Abort(_('patch "%s" already exists') % patchfn)
721 if opts.get('include') or opts.get('exclude') or pats:
721 if opts.get('include') or opts.get('exclude') or pats:
722 match = cmdutil.match(repo, pats, opts)
722 match = cmdutil.match(repo, pats, opts)
723 # detect missing files in pats
723 # detect missing files in pats
724 def badfn(f, msg):
724 def badfn(f, msg):
725 raise util.Abort('%s: %s' % (f, msg))
725 raise util.Abort('%s: %s' % (f, msg))
726 match.bad = badfn
726 match.bad = badfn
727 m, a, r, d = repo.status(match=match)[:4]
727 m, a, r, d = repo.status(match=match)[:4]
728 else:
728 else:
729 m, a, r, d = self.check_localchanges(repo, force)
729 m, a, r, d = self.check_localchanges(repo, force)
730 match = cmdutil.matchfiles(repo, m + a + r)
730 match = cmdutil.matchfiles(repo, m + a + r)
731 commitfiles = m + a + r
731 commitfiles = m + a + r
732 self.check_toppatch(repo)
732 self.check_toppatch(repo)
733 insert = self.full_series_end()
733 insert = self.full_series_end()
734 wlock = repo.wlock()
734 wlock = repo.wlock()
735 try:
735 try:
736 # if patch file write fails, abort early
736 # if patch file write fails, abort early
737 p = self.opener(patchfn, "w")
737 p = self.opener(patchfn, "w")
738 try:
738 try:
739 if date:
739 if date:
740 p.write("# HG changeset patch\n")
740 p.write("# HG changeset patch\n")
741 if user:
741 if user:
742 p.write("# User " + user + "\n")
742 p.write("# User " + user + "\n")
743 p.write("# Date %d %d\n\n" % date)
743 p.write("# Date %d %d\n\n" % date)
744 elif user:
744 elif user:
745 p.write("From: " + user + "\n\n")
745 p.write("From: " + user + "\n\n")
746
746
747 if callable(msg):
747 if callable(msg):
748 msg = msg()
748 msg = msg()
749 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
749 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
750 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
750 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
751 if n == None:
751 if n == None:
752 raise util.Abort(_("repo commit failed"))
752 raise util.Abort(_("repo commit failed"))
753 try:
753 try:
754 self.full_series[insert:insert] = [patchfn]
754 self.full_series[insert:insert] = [patchfn]
755 self.applied.append(statusentry(hex(n), patchfn))
755 self.applied.append(statusentry(hex(n), patchfn))
756 self.parse_series()
756 self.parse_series()
757 self.series_dirty = 1
757 self.series_dirty = 1
758 self.applied_dirty = 1
758 self.applied_dirty = 1
759 if msg:
759 if msg:
760 msg = msg + "\n\n"
760 msg = msg + "\n\n"
761 p.write(msg)
761 p.write(msg)
762 if commitfiles:
762 if commitfiles:
763 diffopts = self.diffopts()
763 diffopts = self.diffopts()
764 if opts.get('git'): diffopts.git = True
764 if opts.get('git'): diffopts.git = True
765 parent = self.qparents(repo, n)
765 parent = self.qparents(repo, n)
766 chunks = patch.diff(repo, node1=parent, node2=n,
766 chunks = patch.diff(repo, node1=parent, node2=n,
767 match=match, opts=diffopts)
767 match=match, opts=diffopts)
768 for chunk in chunks:
768 for chunk in chunks:
769 p.write(chunk)
769 p.write(chunk)
770 p.close()
770 p.close()
771 wlock.release()
771 wlock.release()
772 wlock = None
772 wlock = None
773 r = self.qrepo()
773 r = self.qrepo()
774 if r: r.add([patchfn])
774 if r: r.add([patchfn])
775 except:
775 except:
776 repo.rollback()
776 repo.rollback()
777 raise
777 raise
778 except Exception:
778 except Exception:
779 patchpath = self.join(patchfn)
779 patchpath = self.join(patchfn)
780 try:
780 try:
781 os.unlink(patchpath)
781 os.unlink(patchpath)
782 except:
782 except:
783 self.ui.warn(_('error unlinking %s\n') % patchpath)
783 self.ui.warn(_('error unlinking %s\n') % patchpath)
784 raise
784 raise
785 self.removeundo(repo)
785 self.removeundo(repo)
786 finally:
786 finally:
787 release(wlock)
787 release(wlock)
788
788
789 def strip(self, repo, rev, update=True, backup="all", force=None):
789 def strip(self, repo, rev, update=True, backup="all", force=None):
790 wlock = lock = None
790 wlock = lock = None
791 try:
791 try:
792 wlock = repo.wlock()
792 wlock = repo.wlock()
793 lock = repo.lock()
793 lock = repo.lock()
794
794
795 if update:
795 if update:
796 self.check_localchanges(repo, force=force, refresh=False)
796 self.check_localchanges(repo, force=force, refresh=False)
797 urev = self.qparents(repo, rev)
797 urev = self.qparents(repo, rev)
798 hg.clean(repo, urev)
798 hg.clean(repo, urev)
799 repo.dirstate.write()
799 repo.dirstate.write()
800
800
801 self.removeundo(repo)
801 self.removeundo(repo)
802 repair.strip(self.ui, repo, rev, backup)
802 repair.strip(self.ui, repo, rev, backup)
803 # strip may have unbundled a set of backed up revisions after
803 # strip may have unbundled a set of backed up revisions after
804 # the actual strip
804 # the actual strip
805 self.removeundo(repo)
805 self.removeundo(repo)
806 finally:
806 finally:
807 release(lock, wlock)
807 release(lock, wlock)
808
808
809 def isapplied(self, patch):
809 def isapplied(self, patch):
810 """returns (index, rev, patch)"""
810 """returns (index, rev, patch)"""
811 for i in xrange(len(self.applied)):
811 for i in xrange(len(self.applied)):
812 a = self.applied[i]
812 a = self.applied[i]
813 if a.name == patch:
813 if a.name == patch:
814 return (i, a.rev, a.name)
814 return (i, a.rev, a.name)
815 return None
815 return None
816
816
817 # if the exact patch name does not exist, we try a few
817 # if the exact patch name does not exist, we try a few
818 # variations. If strict is passed, we try only #1
818 # variations. If strict is passed, we try only #1
819 #
819 #
820 # 1) a number to indicate an offset in the series file
820 # 1) a number to indicate an offset in the series file
821 # 2) a unique substring of the patch name was given
821 # 2) a unique substring of the patch name was given
822 # 3) patchname[-+]num to indicate an offset in the series file
822 # 3) patchname[-+]num to indicate an offset in the series file
823 def lookup(self, patch, strict=False):
823 def lookup(self, patch, strict=False):
824 patch = patch and str(patch)
824 patch = patch and str(patch)
825
825
826 def partial_name(s):
826 def partial_name(s):
827 if s in self.series:
827 if s in self.series:
828 return s
828 return s
829 matches = [x for x in self.series if s in x]
829 matches = [x for x in self.series if s in x]
830 if len(matches) > 1:
830 if len(matches) > 1:
831 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
831 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
832 for m in matches:
832 for m in matches:
833 self.ui.warn(' %s\n' % m)
833 self.ui.warn(' %s\n' % m)
834 return None
834 return None
835 if matches:
835 if matches:
836 return matches[0]
836 return matches[0]
837 if len(self.series) > 0 and len(self.applied) > 0:
837 if len(self.series) > 0 and len(self.applied) > 0:
838 if s == 'qtip':
838 if s == 'qtip':
839 return self.series[self.series_end(True)-1]
839 return self.series[self.series_end(True)-1]
840 if s == 'qbase':
840 if s == 'qbase':
841 return self.series[0]
841 return self.series[0]
842 return None
842 return None
843
843
844 if patch == None:
844 if patch == None:
845 return None
845 return None
846 if patch in self.series:
846 if patch in self.series:
847 return patch
847 return patch
848
848
849 if not os.path.isfile(self.join(patch)):
849 if not os.path.isfile(self.join(patch)):
850 try:
850 try:
851 sno = int(patch)
851 sno = int(patch)
852 except(ValueError, OverflowError):
852 except(ValueError, OverflowError):
853 pass
853 pass
854 else:
854 else:
855 if -len(self.series) <= sno < len(self.series):
855 if -len(self.series) <= sno < len(self.series):
856 return self.series[sno]
856 return self.series[sno]
857
857
858 if not strict:
858 if not strict:
859 res = partial_name(patch)
859 res = partial_name(patch)
860 if res:
860 if res:
861 return res
861 return res
862 minus = patch.rfind('-')
862 minus = patch.rfind('-')
863 if minus >= 0:
863 if minus >= 0:
864 res = partial_name(patch[:minus])
864 res = partial_name(patch[:minus])
865 if res:
865 if res:
866 i = self.series.index(res)
866 i = self.series.index(res)
867 try:
867 try:
868 off = int(patch[minus+1:] or 1)
868 off = int(patch[minus+1:] or 1)
869 except(ValueError, OverflowError):
869 except(ValueError, OverflowError):
870 pass
870 pass
871 else:
871 else:
872 if i - off >= 0:
872 if i - off >= 0:
873 return self.series[i - off]
873 return self.series[i - off]
874 plus = patch.rfind('+')
874 plus = patch.rfind('+')
875 if plus >= 0:
875 if plus >= 0:
876 res = partial_name(patch[:plus])
876 res = partial_name(patch[:plus])
877 if res:
877 if res:
878 i = self.series.index(res)
878 i = self.series.index(res)
879 try:
879 try:
880 off = int(patch[plus+1:] or 1)
880 off = int(patch[plus+1:] or 1)
881 except(ValueError, OverflowError):
881 except(ValueError, OverflowError):
882 pass
882 pass
883 else:
883 else:
884 if i + off < len(self.series):
884 if i + off < len(self.series):
885 return self.series[i + off]
885 return self.series[i + off]
886 raise util.Abort(_("patch %s not in series") % patch)
886 raise util.Abort(_("patch %s not in series") % patch)
887
887
888 def push(self, repo, patch=None, force=False, list=False,
888 def push(self, repo, patch=None, force=False, list=False,
889 mergeq=None, all=False):
889 mergeq=None, all=False):
890 wlock = repo.wlock()
890 wlock = repo.wlock()
891 if repo.dirstate.parents()[0] != repo.changelog.tip():
891 if repo.dirstate.parents()[0] != repo.changelog.tip():
892 self.ui.status(_("(working directory not at tip)\n"))
892 self.ui.status(_("(working directory not at tip)\n"))
893
893
894 if not self.series:
894 if not self.series:
895 self.ui.warn(_('no patches in series\n'))
895 self.ui.warn(_('no patches in series\n'))
896 return 0
896 return 0
897
897
898 try:
898 try:
899 patch = self.lookup(patch)
899 patch = self.lookup(patch)
900 # Suppose our series file is: A B C and the current 'top'
900 # Suppose our series file is: A B C and the current 'top'
901 # patch is B. qpush C should be performed (moving forward)
901 # patch is B. qpush C should be performed (moving forward)
902 # qpush B is a NOP (no change) qpush A is an error (can't
902 # qpush B is a NOP (no change) qpush A is an error (can't
903 # go backwards with qpush)
903 # go backwards with qpush)
904 if patch:
904 if patch:
905 info = self.isapplied(patch)
905 info = self.isapplied(patch)
906 if info:
906 if info:
907 if info[0] < len(self.applied) - 1:
907 if info[0] < len(self.applied) - 1:
908 raise util.Abort(
908 raise util.Abort(
909 _("cannot push to a previous patch: %s") % patch)
909 _("cannot push to a previous patch: %s") % patch)
910 self.ui.warn(
910 self.ui.warn(
911 _('qpush: %s is already at the top\n') % patch)
911 _('qpush: %s is already at the top\n') % patch)
912 return
912 return
913 pushable, reason = self.pushable(patch)
913 pushable, reason = self.pushable(patch)
914 if not pushable:
914 if not pushable:
915 if reason:
915 if reason:
916 reason = _('guarded by %r') % reason
916 reason = _('guarded by %r') % reason
917 else:
917 else:
918 reason = _('no matching guards')
918 reason = _('no matching guards')
919 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
919 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
920 return 1
920 return 1
921 elif all:
921 elif all:
922 patch = self.series[-1]
922 patch = self.series[-1]
923 if self.isapplied(patch):
923 if self.isapplied(patch):
924 self.ui.warn(_('all patches are currently applied\n'))
924 self.ui.warn(_('all patches are currently applied\n'))
925 return 0
925 return 0
926
926
927 # Following the above example, starting at 'top' of B:
927 # Following the above example, starting at 'top' of B:
928 # qpush should be performed (pushes C), but a subsequent
928 # qpush should be performed (pushes C), but a subsequent
929 # qpush without an argument is an error (nothing to
929 # qpush without an argument is an error (nothing to
930 # apply). This allows a loop of "...while hg qpush..." to
930 # apply). This allows a loop of "...while hg qpush..." to
931 # work as it detects an error when done
931 # work as it detects an error when done
932 start = self.series_end()
932 start = self.series_end()
933 if start == len(self.series):
933 if start == len(self.series):
934 self.ui.warn(_('patch series already fully applied\n'))
934 self.ui.warn(_('patch series already fully applied\n'))
935 return 1
935 return 1
936 if not force:
936 if not force:
937 self.check_localchanges(repo)
937 self.check_localchanges(repo)
938
938
939 self.applied_dirty = 1
939 self.applied_dirty = 1
940 if start > 0:
940 if start > 0:
941 self.check_toppatch(repo)
941 self.check_toppatch(repo)
942 if not patch:
942 if not patch:
943 patch = self.series[start]
943 patch = self.series[start]
944 end = start + 1
944 end = start + 1
945 else:
945 else:
946 end = self.series.index(patch, start) + 1
946 end = self.series.index(patch, start) + 1
947 s = self.series[start:end]
947 s = self.series[start:end]
948 all_files = {}
948 all_files = {}
949 try:
949 try:
950 if mergeq:
950 if mergeq:
951 ret = self.mergepatch(repo, mergeq, s)
951 ret = self.mergepatch(repo, mergeq, s)
952 else:
952 else:
953 ret = self.apply(repo, s, list, all_files=all_files)
953 ret = self.apply(repo, s, list, all_files=all_files)
954 except:
954 except:
955 self.ui.warn(_('cleaning up working directory...'))
955 self.ui.warn(_('cleaning up working directory...'))
956 node = repo.dirstate.parents()[0]
956 node = repo.dirstate.parents()[0]
957 hg.revert(repo, node, None)
957 hg.revert(repo, node, None)
958 unknown = repo.status(unknown=True)[4]
958 unknown = repo.status(unknown=True)[4]
959 # only remove unknown files that we know we touched or
959 # only remove unknown files that we know we touched or
960 # created while patching
960 # created while patching
961 for f in unknown:
961 for f in unknown:
962 if f in all_files:
962 if f in all_files:
963 util.unlink(repo.wjoin(f))
963 util.unlink(repo.wjoin(f))
964 self.ui.warn(_('done\n'))
964 self.ui.warn(_('done\n'))
965 raise
965 raise
966 top = self.applied[-1].name
966 top = self.applied[-1].name
967 if ret[0]:
967 if ret[0]:
968 self.ui.write(_("errors during apply, please fix and "
968 self.ui.write(_("errors during apply, please fix and "
969 "refresh %s\n") % top)
969 "refresh %s\n") % top)
970 else:
970 else:
971 self.ui.write(_("now at: %s\n") % top)
971 self.ui.write(_("now at: %s\n") % top)
972 return ret[0]
972 return ret[0]
973 finally:
973 finally:
974 wlock.release()
974 wlock.release()
975
975
976 def pop(self, repo, patch=None, force=False, update=True, all=False):
976 def pop(self, repo, patch=None, force=False, update=True, all=False):
977 def getfile(f, rev, flags):
977 def getfile(f, rev, flags):
978 t = repo.file(f).read(rev)
978 t = repo.file(f).read(rev)
979 repo.wwrite(f, t, flags)
979 repo.wwrite(f, t, flags)
980
980
981 wlock = repo.wlock()
981 wlock = repo.wlock()
982 try:
982 try:
983 if patch:
983 if patch:
984 # index, rev, patch
984 # index, rev, patch
985 info = self.isapplied(patch)
985 info = self.isapplied(patch)
986 if not info:
986 if not info:
987 patch = self.lookup(patch)
987 patch = self.lookup(patch)
988 info = self.isapplied(patch)
988 info = self.isapplied(patch)
989 if not info:
989 if not info:
990 raise util.Abort(_("patch %s is not applied") % patch)
990 raise util.Abort(_("patch %s is not applied") % patch)
991
991
992 if len(self.applied) == 0:
992 if len(self.applied) == 0:
993 # Allow qpop -a to work repeatedly,
993 # Allow qpop -a to work repeatedly,
994 # but not qpop without an argument
994 # but not qpop without an argument
995 self.ui.warn(_("no patches applied\n"))
995 self.ui.warn(_("no patches applied\n"))
996 return not all
996 return not all
997
997
998 if all:
998 if all:
999 start = 0
999 start = 0
1000 elif patch:
1000 elif patch:
1001 start = info[0] + 1
1001 start = info[0] + 1
1002 else:
1002 else:
1003 start = len(self.applied) - 1
1003 start = len(self.applied) - 1
1004
1004
1005 if start >= len(self.applied):
1005 if start >= len(self.applied):
1006 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1006 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1007 return
1007 return
1008
1008
1009 if not update:
1009 if not update:
1010 parents = repo.dirstate.parents()
1010 parents = repo.dirstate.parents()
1011 rr = [ bin(x.rev) for x in self.applied ]
1011 rr = [ bin(x.rev) for x in self.applied ]
1012 for p in parents:
1012 for p in parents:
1013 if p in rr:
1013 if p in rr:
1014 self.ui.warn(_("qpop: forcing dirstate update\n"))
1014 self.ui.warn(_("qpop: forcing dirstate update\n"))
1015 update = True
1015 update = True
1016 else:
1016 else:
1017 parents = [p.hex() for p in repo[None].parents()]
1017 parents = [p.hex() for p in repo[None].parents()]
1018 needupdate = False
1018 needupdate = False
1019 for entry in self.applied[start:]:
1019 for entry in self.applied[start:]:
1020 if entry.rev in parents:
1020 if entry.rev in parents:
1021 needupdate = True
1021 needupdate = True
1022 break
1022 break
1023 update = needupdate
1023 update = needupdate
1024
1024
1025 if not force and update:
1025 if not force and update:
1026 self.check_localchanges(repo)
1026 self.check_localchanges(repo)
1027
1027
1028 self.applied_dirty = 1
1028 self.applied_dirty = 1
1029 end = len(self.applied)
1029 end = len(self.applied)
1030 rev = bin(self.applied[start].rev)
1030 rev = bin(self.applied[start].rev)
1031 if update:
1031 if update:
1032 top = self.check_toppatch(repo)
1032 top = self.check_toppatch(repo)
1033
1033
1034 try:
1034 try:
1035 heads = repo.changelog.heads(rev)
1035 heads = repo.changelog.heads(rev)
1036 except error.LookupError:
1036 except error.LookupError:
1037 node = short(rev)
1037 node = short(rev)
1038 raise util.Abort(_('trying to pop unknown node %s') % node)
1038 raise util.Abort(_('trying to pop unknown node %s') % node)
1039
1039
1040 if heads != [bin(self.applied[-1].rev)]:
1040 if heads != [bin(self.applied[-1].rev)]:
1041 raise util.Abort(_("popping would remove a revision not "
1041 raise util.Abort(_("popping would remove a revision not "
1042 "managed by this patch queue"))
1042 "managed by this patch queue"))
1043
1043
1044 # we know there are no local changes, so we can make a simplified
1044 # we know there are no local changes, so we can make a simplified
1045 # form of hg.update.
1045 # form of hg.update.
1046 if update:
1046 if update:
1047 qp = self.qparents(repo, rev)
1047 qp = self.qparents(repo, rev)
1048 changes = repo.changelog.read(qp)
1048 changes = repo.changelog.read(qp)
1049 mmap = repo.manifest.read(changes[0])
1049 mmap = repo.manifest.read(changes[0])
1050 m, a, r, d = repo.status(qp, top)[:4]
1050 m, a, r, d = repo.status(qp, top)[:4]
1051 if d:
1051 if d:
1052 raise util.Abort(_("deletions found between repo revs"))
1052 raise util.Abort(_("deletions found between repo revs"))
1053 for f in m:
1053 for f in m:
1054 getfile(f, mmap[f], mmap.flags(f))
1054 getfile(f, mmap[f], mmap.flags(f))
1055 for f in r:
1055 for f in r:
1056 getfile(f, mmap[f], mmap.flags(f))
1056 getfile(f, mmap[f], mmap.flags(f))
1057 for f in m + r:
1057 for f in m + r:
1058 repo.dirstate.normal(f)
1058 repo.dirstate.normal(f)
1059 for f in a:
1059 for f in a:
1060 try:
1060 try:
1061 os.unlink(repo.wjoin(f))
1061 os.unlink(repo.wjoin(f))
1062 except OSError, e:
1062 except OSError, e:
1063 if e.errno != errno.ENOENT:
1063 if e.errno != errno.ENOENT:
1064 raise
1064 raise
1065 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1065 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1066 except: pass
1066 except: pass
1067 repo.dirstate.forget(f)
1067 repo.dirstate.forget(f)
1068 repo.dirstate.setparents(qp, nullid)
1068 repo.dirstate.setparents(qp, nullid)
1069 del self.applied[start:end]
1069 del self.applied[start:end]
1070 self.strip(repo, rev, update=False, backup='strip')
1070 self.strip(repo, rev, update=False, backup='strip')
1071 if len(self.applied):
1071 if len(self.applied):
1072 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1072 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1073 else:
1073 else:
1074 self.ui.write(_("patch queue now empty\n"))
1074 self.ui.write(_("patch queue now empty\n"))
1075 finally:
1075 finally:
1076 wlock.release()
1076 wlock.release()
1077
1077
1078 def diff(self, repo, pats, opts):
1078 def diff(self, repo, pats, opts):
1079 top = self.check_toppatch(repo)
1079 top = self.check_toppatch(repo)
1080 if not top:
1080 if not top:
1081 self.ui.write(_("no patches applied\n"))
1081 self.ui.write(_("no patches applied\n"))
1082 return
1082 return
1083 qp = self.qparents(repo, top)
1083 qp = self.qparents(repo, top)
1084 self._diffopts = patch.diffopts(self.ui, opts)
1084 self._diffopts = patch.diffopts(self.ui, opts)
1085 self.printdiff(repo, qp, files=pats, opts=opts)
1085 self.printdiff(repo, qp, files=pats, opts=opts)
1086
1086
1087 def refresh(self, repo, pats=None, **opts):
1087 def refresh(self, repo, pats=None, **opts):
1088 if len(self.applied) == 0:
1088 if len(self.applied) == 0:
1089 self.ui.write(_("no patches applied\n"))
1089 self.ui.write(_("no patches applied\n"))
1090 return 1
1090 return 1
1091 msg = opts.get('msg', '').rstrip()
1091 msg = opts.get('msg', '').rstrip()
1092 newuser = opts.get('user')
1092 newuser = opts.get('user')
1093 newdate = opts.get('date')
1093 newdate = opts.get('date')
1094 if newdate:
1094 if newdate:
1095 newdate = '%d %d' % util.parsedate(newdate)
1095 newdate = '%d %d' % util.parsedate(newdate)
1096 wlock = repo.wlock()
1096 wlock = repo.wlock()
1097 try:
1097 try:
1098 self.check_toppatch(repo)
1098 self.check_toppatch(repo)
1099 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1099 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1100 top = bin(top)
1100 top = bin(top)
1101 if repo.changelog.heads(top) != [top]:
1101 if repo.changelog.heads(top) != [top]:
1102 raise util.Abort(_("cannot refresh a revision with children"))
1102 raise util.Abort(_("cannot refresh a revision with children"))
1103 cparents = repo.changelog.parents(top)
1103 cparents = repo.changelog.parents(top)
1104 patchparent = self.qparents(repo, top)
1104 patchparent = self.qparents(repo, top)
1105 ph = self.readheaders(patchfn)
1105 ph = self.readheaders(patchfn)
1106
1106
1107 patchf = self.opener(patchfn, 'r')
1107 patchf = self.opener(patchfn, 'r')
1108
1108
1109 # if the patch was a git patch, refresh it as a git patch
1109 # if the patch was a git patch, refresh it as a git patch
1110 for line in patchf:
1110 for line in patchf:
1111 if line.startswith('diff --git'):
1111 if line.startswith('diff --git'):
1112 self.diffopts().git = True
1112 self.diffopts().git = True
1113 break
1113 break
1114
1114
1115 if msg:
1115 if msg:
1116 ph.setmessage(msg)
1116 ph.setmessage(msg)
1117 if newuser:
1117 if newuser:
1118 ph.setuser(newuser)
1118 ph.setuser(newuser)
1119 if newdate:
1119 if newdate:
1120 ph.setdate(newdate)
1120 ph.setdate(newdate)
1121
1121
1122 # only commit new patch when write is complete
1122 # only commit new patch when write is complete
1123 patchf = self.opener(patchfn, 'w', atomictemp=True)
1123 patchf = self.opener(patchfn, 'w', atomictemp=True)
1124
1124
1125 patchf.seek(0)
1125 patchf.seek(0)
1126 patchf.truncate()
1126 patchf.truncate()
1127
1127
1128 comments = str(ph)
1128 comments = str(ph)
1129 if comments:
1129 if comments:
1130 patchf.write(comments)
1130 patchf.write(comments)
1131
1131
1132 if opts.get('git'):
1132 if opts.get('git'):
1133 self.diffopts().git = True
1133 self.diffopts().git = True
1134 tip = repo.changelog.tip()
1134 tip = repo.changelog.tip()
1135 if top == tip:
1135 if top == tip:
1136 # if the top of our patch queue is also the tip, there is an
1136 # if the top of our patch queue is also the tip, there is an
1137 # optimization here. We update the dirstate in place and strip
1137 # optimization here. We update the dirstate in place and strip
1138 # off the tip commit. Then just commit the current directory
1138 # off the tip commit. Then just commit the current directory
1139 # tree. We can also send repo.commit the list of files
1139 # tree. We can also send repo.commit the list of files
1140 # changed to speed up the diff
1140 # changed to speed up the diff
1141 #
1141 #
1142 # in short mode, we only diff the files included in the
1142 # in short mode, we only diff the files included in the
1143 # patch already plus specified files
1143 # patch already plus specified files
1144 #
1144 #
1145 # this should really read:
1145 # this should really read:
1146 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1146 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1147 # but we do it backwards to take advantage of manifest/chlog
1147 # but we do it backwards to take advantage of manifest/chlog
1148 # caching against the next repo.status call
1148 # caching against the next repo.status call
1149 #
1149 #
1150 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1150 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1151 changes = repo.changelog.read(tip)
1151 changes = repo.changelog.read(tip)
1152 man = repo.manifest.read(changes[0])
1152 man = repo.manifest.read(changes[0])
1153 aaa = aa[:]
1153 aaa = aa[:]
1154 matchfn = cmdutil.match(repo, pats, opts)
1154 matchfn = cmdutil.match(repo, pats, opts)
1155 if opts.get('short'):
1155 if opts.get('short'):
1156 # if amending a patch, we start with existing
1156 # if amending a patch, we start with existing
1157 # files plus specified files - unfiltered
1157 # files plus specified files - unfiltered
1158 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1158 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1159 # filter with inc/exl options
1159 # filter with inc/exl options
1160 matchfn = cmdutil.match(repo, opts=opts)
1160 matchfn = cmdutil.match(repo, opts=opts)
1161 else:
1161 else:
1162 match = cmdutil.matchall(repo)
1162 match = cmdutil.matchall(repo)
1163 m, a, r, d = repo.status(match=match)[:4]
1163 m, a, r, d = repo.status(match=match)[:4]
1164
1164
1165 # we might end up with files that were added between
1165 # we might end up with files that were added between
1166 # tip and the dirstate parent, but then changed in the
1166 # tip and the dirstate parent, but then changed in the
1167 # local dirstate. in this case, we want them to only
1167 # local dirstate. in this case, we want them to only
1168 # show up in the added section
1168 # show up in the added section
1169 for x in m:
1169 for x in m:
1170 if x not in aa:
1170 if x not in aa:
1171 mm.append(x)
1171 mm.append(x)
1172 # we might end up with files added by the local dirstate that
1172 # we might end up with files added by the local dirstate that
1173 # were deleted by the patch. In this case, they should only
1173 # were deleted by the patch. In this case, they should only
1174 # show up in the changed section.
1174 # show up in the changed section.
1175 for x in a:
1175 for x in a:
1176 if x in dd:
1176 if x in dd:
1177 del dd[dd.index(x)]
1177 del dd[dd.index(x)]
1178 mm.append(x)
1178 mm.append(x)
1179 else:
1179 else:
1180 aa.append(x)
1180 aa.append(x)
1181 # make sure any files deleted in the local dirstate
1181 # make sure any files deleted in the local dirstate
1182 # are not in the add or change column of the patch
1182 # are not in the add or change column of the patch
1183 forget = []
1183 forget = []
1184 for x in d + r:
1184 for x in d + r:
1185 if x in aa:
1185 if x in aa:
1186 del aa[aa.index(x)]
1186 del aa[aa.index(x)]
1187 forget.append(x)
1187 forget.append(x)
1188 continue
1188 continue
1189 elif x in mm:
1189 elif x in mm:
1190 del mm[mm.index(x)]
1190 del mm[mm.index(x)]
1191 dd.append(x)
1191 dd.append(x)
1192
1192
1193 m = list(set(mm))
1193 m = list(set(mm))
1194 r = list(set(dd))
1194 r = list(set(dd))
1195 a = list(set(aa))
1195 a = list(set(aa))
1196 c = [filter(matchfn, l) for l in (m, a, r)]
1196 c = [filter(matchfn, l) for l in (m, a, r)]
1197 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1197 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1198 chunks = patch.diff(repo, patchparent, match=match,
1198 chunks = patch.diff(repo, patchparent, match=match,
1199 changes=c, opts=self.diffopts())
1199 changes=c, opts=self.diffopts())
1200 for chunk in chunks:
1200 for chunk in chunks:
1201 patchf.write(chunk)
1201 patchf.write(chunk)
1202
1202
1203 try:
1203 try:
1204 if self.diffopts().git:
1204 if self.diffopts().git:
1205 copies = {}
1205 copies = {}
1206 for dst in a:
1206 for dst in a:
1207 src = repo.dirstate.copied(dst)
1207 src = repo.dirstate.copied(dst)
1208 # during qfold, the source file for copies may
1208 # during qfold, the source file for copies may
1209 # be removed. Treat this as a simple add.
1209 # be removed. Treat this as a simple add.
1210 if src is not None and src in repo.dirstate:
1210 if src is not None and src in repo.dirstate:
1211 copies.setdefault(src, []).append(dst)
1211 copies.setdefault(src, []).append(dst)
1212 repo.dirstate.add(dst)
1212 repo.dirstate.add(dst)
1213 # remember the copies between patchparent and tip
1213 # remember the copies between patchparent and tip
1214 for dst in aaa:
1214 for dst in aaa:
1215 f = repo.file(dst)
1215 f = repo.file(dst)
1216 src = f.renamed(man[dst])
1216 src = f.renamed(man[dst])
1217 if src:
1217 if src:
1218 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1218 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1219 if dst in a:
1219 if dst in a:
1220 copies[src[0]].append(dst)
1220 copies[src[0]].append(dst)
1221 # we can't copy a file created by the patch itself
1221 # we can't copy a file created by the patch itself
1222 if dst in copies:
1222 if dst in copies:
1223 del copies[dst]
1223 del copies[dst]
1224 for src, dsts in copies.iteritems():
1224 for src, dsts in copies.iteritems():
1225 for dst in dsts:
1225 for dst in dsts:
1226 repo.dirstate.copy(src, dst)
1226 repo.dirstate.copy(src, dst)
1227 else:
1227 else:
1228 for dst in a:
1228 for dst in a:
1229 repo.dirstate.add(dst)
1229 repo.dirstate.add(dst)
1230 # Drop useless copy information
1230 # Drop useless copy information
1231 for f in list(repo.dirstate.copies()):
1231 for f in list(repo.dirstate.copies()):
1232 repo.dirstate.copy(None, f)
1232 repo.dirstate.copy(None, f)
1233 for f in r:
1233 for f in r:
1234 repo.dirstate.remove(f)
1234 repo.dirstate.remove(f)
1235 # if the patch excludes a modified file, mark that
1235 # if the patch excludes a modified file, mark that
1236 # file with mtime=0 so status can see it.
1236 # file with mtime=0 so status can see it.
1237 mm = []
1237 mm = []
1238 for i in xrange(len(m)-1, -1, -1):
1238 for i in xrange(len(m)-1, -1, -1):
1239 if not matchfn(m[i]):
1239 if not matchfn(m[i]):
1240 mm.append(m[i])
1240 mm.append(m[i])
1241 del m[i]
1241 del m[i]
1242 for f in m:
1242 for f in m:
1243 repo.dirstate.normal(f)
1243 repo.dirstate.normal(f)
1244 for f in mm:
1244 for f in mm:
1245 repo.dirstate.normallookup(f)
1245 repo.dirstate.normallookup(f)
1246 for f in forget:
1246 for f in forget:
1247 repo.dirstate.forget(f)
1247 repo.dirstate.forget(f)
1248
1248
1249 if not msg:
1249 if not msg:
1250 if not ph.message:
1250 if not ph.message:
1251 message = "[mq]: %s\n" % patchfn
1251 message = "[mq]: %s\n" % patchfn
1252 else:
1252 else:
1253 message = "\n".join(ph.message)
1253 message = "\n".join(ph.message)
1254 else:
1254 else:
1255 message = msg
1255 message = msg
1256
1256
1257 user = ph.user or changes[1]
1257 user = ph.user or changes[1]
1258
1258
1259 # assumes strip can roll itself back if interrupted
1259 # assumes strip can roll itself back if interrupted
1260 repo.dirstate.setparents(*cparents)
1260 repo.dirstate.setparents(*cparents)
1261 self.applied.pop()
1261 self.applied.pop()
1262 self.applied_dirty = 1
1262 self.applied_dirty = 1
1263 self.strip(repo, top, update=False,
1263 self.strip(repo, top, update=False,
1264 backup='strip')
1264 backup='strip')
1265 except:
1265 except:
1266 repo.dirstate.invalidate()
1266 repo.dirstate.invalidate()
1267 raise
1267 raise
1268
1268
1269 try:
1269 try:
1270 # might be nice to attempt to roll back strip after this
1270 # might be nice to attempt to roll back strip after this
1271 patchf.rename()
1271 patchf.rename()
1272 n = repo.commit(match.files(), message, user, ph.date,
1272 n = repo.commit(match.files(), message, user, ph.date,
1273 match=match, force=1)
1273 match=match, force=1)
1274 self.applied.append(statusentry(hex(n), patchfn))
1274 self.applied.append(statusentry(hex(n), patchfn))
1275 except:
1275 except:
1276 ctx = repo[cparents[0]]
1276 ctx = repo[cparents[0]]
1277 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1277 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1278 self.save_dirty()
1278 self.save_dirty()
1279 self.ui.warn(_('refresh interrupted while patch was popped! '
1279 self.ui.warn(_('refresh interrupted while patch was popped! '
1280 '(revert --all, qpush to recover)\n'))
1280 '(revert --all, qpush to recover)\n'))
1281 raise
1281 raise
1282 else:
1282 else:
1283 self.printdiff(repo, patchparent, fp=patchf)
1283 self.printdiff(repo, patchparent, fp=patchf)
1284 patchf.rename()
1284 patchf.rename()
1285 added = repo.status()[1]
1285 added = repo.status()[1]
1286 for a in added:
1286 for a in added:
1287 f = repo.wjoin(a)
1287 f = repo.wjoin(a)
1288 try:
1288 try:
1289 os.unlink(f)
1289 os.unlink(f)
1290 except OSError, e:
1290 except OSError, e:
1291 if e.errno != errno.ENOENT:
1291 if e.errno != errno.ENOENT:
1292 raise
1292 raise
1293 try: os.removedirs(os.path.dirname(f))
1293 try: os.removedirs(os.path.dirname(f))
1294 except: pass
1294 except: pass
1295 # forget the file copies in the dirstate
1295 # forget the file copies in the dirstate
1296 # push should readd the files later on
1296 # push should readd the files later on
1297 repo.dirstate.forget(a)
1297 repo.dirstate.forget(a)
1298 self.pop(repo, force=True)
1298 self.pop(repo, force=True)
1299 self.push(repo, force=True)
1299 self.push(repo, force=True)
1300 finally:
1300 finally:
1301 wlock.release()
1301 wlock.release()
1302 self.removeundo(repo)
1302 self.removeundo(repo)
1303
1303
1304 def init(self, repo, create=False):
1304 def init(self, repo, create=False):
1305 if not create and os.path.isdir(self.path):
1305 if not create and os.path.isdir(self.path):
1306 raise util.Abort(_("patch queue directory already exists"))
1306 raise util.Abort(_("patch queue directory already exists"))
1307 try:
1307 try:
1308 os.mkdir(self.path)
1308 os.mkdir(self.path)
1309 except OSError, inst:
1309 except OSError, inst:
1310 if inst.errno != errno.EEXIST or not create:
1310 if inst.errno != errno.EEXIST or not create:
1311 raise
1311 raise
1312 if create:
1312 if create:
1313 return self.qrepo(create=True)
1313 return self.qrepo(create=True)
1314
1314
1315 def unapplied(self, repo, patch=None):
1315 def unapplied(self, repo, patch=None):
1316 if patch and patch not in self.series:
1316 if patch and patch not in self.series:
1317 raise util.Abort(_("patch %s is not in series file") % patch)
1317 raise util.Abort(_("patch %s is not in series file") % patch)
1318 if not patch:
1318 if not patch:
1319 start = self.series_end()
1319 start = self.series_end()
1320 else:
1320 else:
1321 start = self.series.index(patch) + 1
1321 start = self.series.index(patch) + 1
1322 unapplied = []
1322 unapplied = []
1323 for i in xrange(start, len(self.series)):
1323 for i in xrange(start, len(self.series)):
1324 pushable, reason = self.pushable(i)
1324 pushable, reason = self.pushable(i)
1325 if pushable:
1325 if pushable:
1326 unapplied.append((i, self.series[i]))
1326 unapplied.append((i, self.series[i]))
1327 self.explain_pushable(i)
1327 self.explain_pushable(i)
1328 return unapplied
1328 return unapplied
1329
1329
1330 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1330 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1331 summary=False):
1331 summary=False):
1332 def displayname(patchname):
1332 def displayname(patchname):
1333 if summary:
1333 if summary:
1334 ph = self.readheaders(patchname)
1334 ph = self.readheaders(patchname)
1335 msg = ph.message
1335 msg = ph.message
1336 msg = msg and ': ' + msg[0] or ': '
1336 msg = msg and ': ' + msg[0] or ': '
1337 else:
1337 else:
1338 msg = ''
1338 msg = ''
1339 return '%s%s' % (patchname, msg)
1339 return '%s%s' % (patchname, msg)
1340
1340
1341 applied = set([p.name for p in self.applied])
1341 applied = set([p.name for p in self.applied])
1342 if length is None:
1342 if length is None:
1343 length = len(self.series) - start
1343 length = len(self.series) - start
1344 if not missing:
1344 if not missing:
1345 for i in xrange(start, start+length):
1345 for i in xrange(start, start+length):
1346 patch = self.series[i]
1346 patch = self.series[i]
1347 if patch in applied:
1347 if patch in applied:
1348 stat = 'A'
1348 stat = 'A'
1349 elif self.pushable(i)[0]:
1349 elif self.pushable(i)[0]:
1350 stat = 'U'
1350 stat = 'U'
1351 else:
1351 else:
1352 stat = 'G'
1352 stat = 'G'
1353 pfx = ''
1353 pfx = ''
1354 if self.ui.verbose:
1354 if self.ui.verbose:
1355 pfx = '%d %s ' % (i, stat)
1355 pfx = '%d %s ' % (i, stat)
1356 elif status and status != stat:
1356 elif status and status != stat:
1357 continue
1357 continue
1358 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1358 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1359 else:
1359 else:
1360 msng_list = []
1360 msng_list = []
1361 for root, dirs, files in os.walk(self.path):
1361 for root, dirs, files in os.walk(self.path):
1362 d = root[len(self.path) + 1:]
1362 d = root[len(self.path) + 1:]
1363 for f in files:
1363 for f in files:
1364 fl = os.path.join(d, f)
1364 fl = os.path.join(d, f)
1365 if (fl not in self.series and
1365 if (fl not in self.series and
1366 fl not in (self.status_path, self.series_path,
1366 fl not in (self.status_path, self.series_path,
1367 self.guards_path)
1367 self.guards_path)
1368 and not fl.startswith('.')):
1368 and not fl.startswith('.')):
1369 msng_list.append(fl)
1369 msng_list.append(fl)
1370 for x in util.sort(msng_list):
1370 for x in util.sort(msng_list):
1371 pfx = self.ui.verbose and ('D ') or ''
1371 pfx = self.ui.verbose and ('D ') or ''
1372 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1372 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1373
1373
1374 def issaveline(self, l):
1374 def issaveline(self, l):
1375 if l.name == '.hg.patches.save.line':
1375 if l.name == '.hg.patches.save.line':
1376 return True
1376 return True
1377
1377
1378 def qrepo(self, create=False):
1378 def qrepo(self, create=False):
1379 if create or os.path.isdir(self.join(".hg")):
1379 if create or os.path.isdir(self.join(".hg")):
1380 return hg.repository(self.ui, path=self.path, create=create)
1380 return hg.repository(self.ui, path=self.path, create=create)
1381
1381
1382 def restore(self, repo, rev, delete=None, qupdate=None):
1382 def restore(self, repo, rev, delete=None, qupdate=None):
1383 c = repo.changelog.read(rev)
1383 c = repo.changelog.read(rev)
1384 desc = c[4].strip()
1384 desc = c[4].strip()
1385 lines = desc.splitlines()
1385 lines = desc.splitlines()
1386 i = 0
1386 i = 0
1387 datastart = None
1387 datastart = None
1388 series = []
1388 series = []
1389 applied = []
1389 applied = []
1390 qpp = None
1390 qpp = None
1391 for i in xrange(0, len(lines)):
1391 for i in xrange(0, len(lines)):
1392 if lines[i] == 'Patch Data:':
1392 if lines[i] == 'Patch Data:':
1393 datastart = i + 1
1393 datastart = i + 1
1394 elif lines[i].startswith('Dirstate:'):
1394 elif lines[i].startswith('Dirstate:'):
1395 l = lines[i].rstrip()
1395 l = lines[i].rstrip()
1396 l = l[10:].split(' ')
1396 l = l[10:].split(' ')
1397 qpp = [ bin(x) for x in l ]
1397 qpp = [ bin(x) for x in l ]
1398 elif datastart != None:
1398 elif datastart != None:
1399 l = lines[i].rstrip()
1399 l = lines[i].rstrip()
1400 se = statusentry(l)
1400 se = statusentry(l)
1401 file_ = se.name
1401 file_ = se.name
1402 if se.rev:
1402 if se.rev:
1403 applied.append(se)
1403 applied.append(se)
1404 else:
1404 else:
1405 series.append(file_)
1405 series.append(file_)
1406 if datastart == None:
1406 if datastart == None:
1407 self.ui.warn(_("No saved patch data found\n"))
1407 self.ui.warn(_("No saved patch data found\n"))
1408 return 1
1408 return 1
1409 self.ui.warn(_("restoring status: %s\n") % lines[0])
1409 self.ui.warn(_("restoring status: %s\n") % lines[0])
1410 self.full_series = series
1410 self.full_series = series
1411 self.applied = applied
1411 self.applied = applied
1412 self.parse_series()
1412 self.parse_series()
1413 self.series_dirty = 1
1413 self.series_dirty = 1
1414 self.applied_dirty = 1
1414 self.applied_dirty = 1
1415 heads = repo.changelog.heads()
1415 heads = repo.changelog.heads()
1416 if delete:
1416 if delete:
1417 if rev not in heads:
1417 if rev not in heads:
1418 self.ui.warn(_("save entry has children, leaving it alone\n"))
1418 self.ui.warn(_("save entry has children, leaving it alone\n"))
1419 else:
1419 else:
1420 self.ui.warn(_("removing save entry %s\n") % short(rev))
1420 self.ui.warn(_("removing save entry %s\n") % short(rev))
1421 pp = repo.dirstate.parents()
1421 pp = repo.dirstate.parents()
1422 if rev in pp:
1422 if rev in pp:
1423 update = True
1423 update = True
1424 else:
1424 else:
1425 update = False
1425 update = False
1426 self.strip(repo, rev, update=update, backup='strip')
1426 self.strip(repo, rev, update=update, backup='strip')
1427 if qpp:
1427 if qpp:
1428 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1428 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1429 (short(qpp[0]), short(qpp[1])))
1429 (short(qpp[0]), short(qpp[1])))
1430 if qupdate:
1430 if qupdate:
1431 self.ui.status(_("queue directory updating\n"))
1431 self.ui.status(_("queue directory updating\n"))
1432 r = self.qrepo()
1432 r = self.qrepo()
1433 if not r:
1433 if not r:
1434 self.ui.warn(_("Unable to load queue repository\n"))
1434 self.ui.warn(_("Unable to load queue repository\n"))
1435 return 1
1435 return 1
1436 hg.clean(r, qpp[0])
1436 hg.clean(r, qpp[0])
1437
1437
1438 def save(self, repo, msg=None):
1438 def save(self, repo, msg=None):
1439 if len(self.applied) == 0:
1439 if len(self.applied) == 0:
1440 self.ui.warn(_("save: no patches applied, exiting\n"))
1440 self.ui.warn(_("save: no patches applied, exiting\n"))
1441 return 1
1441 return 1
1442 if self.issaveline(self.applied[-1]):
1442 if self.issaveline(self.applied[-1]):
1443 self.ui.warn(_("status is already saved\n"))
1443 self.ui.warn(_("status is already saved\n"))
1444 return 1
1444 return 1
1445
1445
1446 ar = [ ':' + x for x in self.full_series ]
1446 ar = [ ':' + x for x in self.full_series ]
1447 if not msg:
1447 if not msg:
1448 msg = _("hg patches saved state")
1448 msg = _("hg patches saved state")
1449 else:
1449 else:
1450 msg = "hg patches: " + msg.rstrip('\r\n')
1450 msg = "hg patches: " + msg.rstrip('\r\n')
1451 r = self.qrepo()
1451 r = self.qrepo()
1452 if r:
1452 if r:
1453 pp = r.dirstate.parents()
1453 pp = r.dirstate.parents()
1454 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1454 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1455 msg += "\n\nPatch Data:\n"
1455 msg += "\n\nPatch Data:\n"
1456 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1456 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1457 "\n".join(ar) + '\n' or "")
1457 "\n".join(ar) + '\n' or "")
1458 n = repo.commit(None, text, user=None, force=1)
1458 n = repo.commit(None, text, user=None, force=1)
1459 if not n:
1459 if not n:
1460 self.ui.warn(_("repo commit failed\n"))
1460 self.ui.warn(_("repo commit failed\n"))
1461 return 1
1461 return 1
1462 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1462 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1463 self.applied_dirty = 1
1463 self.applied_dirty = 1
1464 self.removeundo(repo)
1464 self.removeundo(repo)
1465
1465
1466 def full_series_end(self):
1466 def full_series_end(self):
1467 if len(self.applied) > 0:
1467 if len(self.applied) > 0:
1468 p = self.applied[-1].name
1468 p = self.applied[-1].name
1469 end = self.find_series(p)
1469 end = self.find_series(p)
1470 if end == None:
1470 if end == None:
1471 return len(self.full_series)
1471 return len(self.full_series)
1472 return end + 1
1472 return end + 1
1473 return 0
1473 return 0
1474
1474
1475 def series_end(self, all_patches=False):
1475 def series_end(self, all_patches=False):
1476 """If all_patches is False, return the index of the next pushable patch
1476 """If all_patches is False, return the index of the next pushable patch
1477 in the series, or the series length. If all_patches is True, return the
1477 in the series, or the series length. If all_patches is True, return the
1478 index of the first patch past the last applied one.
1478 index of the first patch past the last applied one.
1479 """
1479 """
1480 end = 0
1480 end = 0
1481 def next(start):
1481 def next(start):
1482 if all_patches:
1482 if all_patches:
1483 return start
1483 return start
1484 i = start
1484 i = start
1485 while i < len(self.series):
1485 while i < len(self.series):
1486 p, reason = self.pushable(i)
1486 p, reason = self.pushable(i)
1487 if p:
1487 if p:
1488 break
1488 break
1489 self.explain_pushable(i)
1489 self.explain_pushable(i)
1490 i += 1
1490 i += 1
1491 return i
1491 return i
1492 if len(self.applied) > 0:
1492 if len(self.applied) > 0:
1493 p = self.applied[-1].name
1493 p = self.applied[-1].name
1494 try:
1494 try:
1495 end = self.series.index(p)
1495 end = self.series.index(p)
1496 except ValueError:
1496 except ValueError:
1497 return 0
1497 return 0
1498 return next(end + 1)
1498 return next(end + 1)
1499 return next(end)
1499 return next(end)
1500
1500
1501 def appliedname(self, index):
1501 def appliedname(self, index):
1502 pname = self.applied[index].name
1502 pname = self.applied[index].name
1503 if not self.ui.verbose:
1503 if not self.ui.verbose:
1504 p = pname
1504 p = pname
1505 else:
1505 else:
1506 p = str(self.series.index(pname)) + " " + pname
1506 p = str(self.series.index(pname)) + " " + pname
1507 return p
1507 return p
1508
1508
1509 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1509 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1510 force=None, git=False):
1510 force=None, git=False):
1511 def checkseries(patchname):
1511 def checkseries(patchname):
1512 if patchname in self.series:
1512 if patchname in self.series:
1513 raise util.Abort(_('patch %s is already in the series file')
1513 raise util.Abort(_('patch %s is already in the series file')
1514 % patchname)
1514 % patchname)
1515 def checkfile(patchname):
1515 def checkfile(patchname):
1516 if not force and os.path.exists(self.join(patchname)):
1516 if not force and os.path.exists(self.join(patchname)):
1517 raise util.Abort(_('patch "%s" already exists')
1517 raise util.Abort(_('patch "%s" already exists')
1518 % patchname)
1518 % patchname)
1519
1519
1520 if rev:
1520 if rev:
1521 if files:
1521 if files:
1522 raise util.Abort(_('option "-r" not valid when importing '
1522 raise util.Abort(_('option "-r" not valid when importing '
1523 'files'))
1523 'files'))
1524 rev = cmdutil.revrange(repo, rev)
1524 rev = cmdutil.revrange(repo, rev)
1525 rev.sort(lambda x, y: cmp(y, x))
1525 rev.sort(lambda x, y: cmp(y, x))
1526 if (len(files) > 1 or len(rev) > 1) and patchname:
1526 if (len(files) > 1 or len(rev) > 1) and patchname:
1527 raise util.Abort(_('option "-n" not valid when importing multiple '
1527 raise util.Abort(_('option "-n" not valid when importing multiple '
1528 'patches'))
1528 'patches'))
1529 i = 0
1529 i = 0
1530 added = []
1530 added = []
1531 if rev:
1531 if rev:
1532 # If mq patches are applied, we can only import revisions
1532 # If mq patches are applied, we can only import revisions
1533 # that form a linear path to qbase.
1533 # that form a linear path to qbase.
1534 # Otherwise, they should form a linear path to a head.
1534 # Otherwise, they should form a linear path to a head.
1535 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1535 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1536 if len(heads) > 1:
1536 if len(heads) > 1:
1537 raise util.Abort(_('revision %d is the root of more than one '
1537 raise util.Abort(_('revision %d is the root of more than one '
1538 'branch') % rev[-1])
1538 'branch') % rev[-1])
1539 if self.applied:
1539 if self.applied:
1540 base = hex(repo.changelog.node(rev[0]))
1540 base = hex(repo.changelog.node(rev[0]))
1541 if base in [n.rev for n in self.applied]:
1541 if base in [n.rev for n in self.applied]:
1542 raise util.Abort(_('revision %d is already managed')
1542 raise util.Abort(_('revision %d is already managed')
1543 % rev[0])
1543 % rev[0])
1544 if heads != [bin(self.applied[-1].rev)]:
1544 if heads != [bin(self.applied[-1].rev)]:
1545 raise util.Abort(_('revision %d is not the parent of '
1545 raise util.Abort(_('revision %d is not the parent of '
1546 'the queue') % rev[0])
1546 'the queue') % rev[0])
1547 base = repo.changelog.rev(bin(self.applied[0].rev))
1547 base = repo.changelog.rev(bin(self.applied[0].rev))
1548 lastparent = repo.changelog.parentrevs(base)[0]
1548 lastparent = repo.changelog.parentrevs(base)[0]
1549 else:
1549 else:
1550 if heads != [repo.changelog.node(rev[0])]:
1550 if heads != [repo.changelog.node(rev[0])]:
1551 raise util.Abort(_('revision %d has unmanaged children')
1551 raise util.Abort(_('revision %d has unmanaged children')
1552 % rev[0])
1552 % rev[0])
1553 lastparent = None
1553 lastparent = None
1554
1554
1555 if git:
1555 if git:
1556 self.diffopts().git = True
1556 self.diffopts().git = True
1557
1557
1558 for r in rev:
1558 for r in rev:
1559 p1, p2 = repo.changelog.parentrevs(r)
1559 p1, p2 = repo.changelog.parentrevs(r)
1560 n = repo.changelog.node(r)
1560 n = repo.changelog.node(r)
1561 if p2 != nullrev:
1561 if p2 != nullrev:
1562 raise util.Abort(_('cannot import merge revision %d') % r)
1562 raise util.Abort(_('cannot import merge revision %d') % r)
1563 if lastparent and lastparent != r:
1563 if lastparent and lastparent != r:
1564 raise util.Abort(_('revision %d is not the parent of %d')
1564 raise util.Abort(_('revision %d is not the parent of %d')
1565 % (r, lastparent))
1565 % (r, lastparent))
1566 lastparent = p1
1566 lastparent = p1
1567
1567
1568 if not patchname:
1568 if not patchname:
1569 patchname = normname('%d.diff' % r)
1569 patchname = normname('%d.diff' % r)
1570 self.check_reserved_name(patchname)
1570 self.check_reserved_name(patchname)
1571 checkseries(patchname)
1571 checkseries(patchname)
1572 checkfile(patchname)
1572 checkfile(patchname)
1573 self.full_series.insert(0, patchname)
1573 self.full_series.insert(0, patchname)
1574
1574
1575 patchf = self.opener(patchname, "w")
1575 patchf = self.opener(patchname, "w")
1576 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1576 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1577 patchf.close()
1577 patchf.close()
1578
1578
1579 se = statusentry(hex(n), patchname)
1579 se = statusentry(hex(n), patchname)
1580 self.applied.insert(0, se)
1580 self.applied.insert(0, se)
1581
1581
1582 added.append(patchname)
1582 added.append(patchname)
1583 patchname = None
1583 patchname = None
1584 self.parse_series()
1584 self.parse_series()
1585 self.applied_dirty = 1
1585 self.applied_dirty = 1
1586
1586
1587 for filename in files:
1587 for filename in files:
1588 if existing:
1588 if existing:
1589 if filename == '-':
1589 if filename == '-':
1590 raise util.Abort(_('-e is incompatible with import from -'))
1590 raise util.Abort(_('-e is incompatible with import from -'))
1591 if not patchname:
1591 if not patchname:
1592 patchname = normname(filename)
1592 patchname = normname(filename)
1593 self.check_reserved_name(patchname)
1593 self.check_reserved_name(patchname)
1594 if not os.path.isfile(self.join(patchname)):
1594 if not os.path.isfile(self.join(patchname)):
1595 raise util.Abort(_("patch %s does not exist") % patchname)
1595 raise util.Abort(_("patch %s does not exist") % patchname)
1596 else:
1596 else:
1597 try:
1597 try:
1598 if filename == '-':
1598 if filename == '-':
1599 if not patchname:
1599 if not patchname:
1600 raise util.Abort(_('need --name to import a patch from -'))
1600 raise util.Abort(_('need --name to import a patch from -'))
1601 text = sys.stdin.read()
1601 text = sys.stdin.read()
1602 else:
1602 else:
1603 text = url.open(self.ui, filename).read()
1603 text = url.open(self.ui, filename).read()
1604 except (OSError, IOError):
1604 except (OSError, IOError):
1605 raise util.Abort(_("unable to read %s") % filename)
1605 raise util.Abort(_("unable to read %s") % filename)
1606 if not patchname:
1606 if not patchname:
1607 patchname = normname(os.path.basename(filename))
1607 patchname = normname(os.path.basename(filename))
1608 self.check_reserved_name(patchname)
1608 self.check_reserved_name(patchname)
1609 checkfile(patchname)
1609 checkfile(patchname)
1610 patchf = self.opener(patchname, "w")
1610 patchf = self.opener(patchname, "w")
1611 patchf.write(text)
1611 patchf.write(text)
1612 if not force:
1612 if not force:
1613 checkseries(patchname)
1613 checkseries(patchname)
1614 if patchname not in self.series:
1614 if patchname not in self.series:
1615 index = self.full_series_end() + i
1615 index = self.full_series_end() + i
1616 self.full_series[index:index] = [patchname]
1616 self.full_series[index:index] = [patchname]
1617 self.parse_series()
1617 self.parse_series()
1618 self.ui.warn(_("adding %s to series file\n") % patchname)
1618 self.ui.warn(_("adding %s to series file\n") % patchname)
1619 i += 1
1619 i += 1
1620 added.append(patchname)
1620 added.append(patchname)
1621 patchname = None
1621 patchname = None
1622 self.series_dirty = 1
1622 self.series_dirty = 1
1623 qrepo = self.qrepo()
1623 qrepo = self.qrepo()
1624 if qrepo:
1624 if qrepo:
1625 qrepo.add(added)
1625 qrepo.add(added)
1626
1626
1627 def delete(ui, repo, *patches, **opts):
1627 def delete(ui, repo, *patches, **opts):
1628 """remove patches from queue
1628 """remove patches from queue
1629
1629
1630 The patches must not be applied, unless they are arguments to the
1630 The patches must not be applied, unless they are arguments to the
1631 -r/--rev parameter. At least one patch or revision is required.
1631 -r/--rev parameter. At least one patch or revision is required.
1632
1632
1633 With --rev, mq will stop managing the named revisions (converting
1633 With --rev, mq will stop managing the named revisions (converting
1634 them to regular mercurial changesets). The qfinish command should
1634 them to regular mercurial changesets). The qfinish command should
1635 be used as an alternative for qdelete -r, as the latter option is
1635 be used as an alternative for qdelete -r, as the latter option is
1636 deprecated.
1636 deprecated.
1637
1637
1638 With -k/--keep, the patch files are preserved in the patch
1638 With -k/--keep, the patch files are preserved in the patch
1639 directory."""
1639 directory."""
1640 q = repo.mq
1640 q = repo.mq
1641 q.delete(repo, patches, opts)
1641 q.delete(repo, patches, opts)
1642 q.save_dirty()
1642 q.save_dirty()
1643 return 0
1643 return 0
1644
1644
1645 def applied(ui, repo, patch=None, **opts):
1645 def applied(ui, repo, patch=None, **opts):
1646 """print the patches already applied"""
1646 """print the patches already applied"""
1647 q = repo.mq
1647 q = repo.mq
1648 if patch:
1648 if patch:
1649 if patch not in q.series:
1649 if patch not in q.series:
1650 raise util.Abort(_("patch %s is not in series file") % patch)
1650 raise util.Abort(_("patch %s is not in series file") % patch)
1651 end = q.series.index(patch) + 1
1651 end = q.series.index(patch) + 1
1652 else:
1652 else:
1653 end = q.series_end(True)
1653 end = q.series_end(True)
1654 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1654 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1655
1655
1656 def unapplied(ui, repo, patch=None, **opts):
1656 def unapplied(ui, repo, patch=None, **opts):
1657 """print the patches not yet applied"""
1657 """print the patches not yet applied"""
1658 q = repo.mq
1658 q = repo.mq
1659 if patch:
1659 if patch:
1660 if patch not in q.series:
1660 if patch not in q.series:
1661 raise util.Abort(_("patch %s is not in series file") % patch)
1661 raise util.Abort(_("patch %s is not in series file") % patch)
1662 start = q.series.index(patch) + 1
1662 start = q.series.index(patch) + 1
1663 else:
1663 else:
1664 start = q.series_end(True)
1664 start = q.series_end(True)
1665 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1665 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1666
1666
1667 def qimport(ui, repo, *filename, **opts):
1667 def qimport(ui, repo, *filename, **opts):
1668 """import a patch
1668 """import a patch
1669
1669
1670 The patch is inserted into the series after the last applied
1670 The patch is inserted into the series after the last applied
1671 patch. If no patches have been applied, qimport prepends the patch
1671 patch. If no patches have been applied, qimport prepends the patch
1672 to the series.
1672 to the series.
1673
1673
1674 The patch will have the same name as its source file unless you
1674 The patch will have the same name as its source file unless you
1675 give it a new one with -n/--name.
1675 give it a new one with -n/--name.
1676
1676
1677 You can register an existing patch inside the patch directory with
1677 You can register an existing patch inside the patch directory with
1678 the -e/--existing flag.
1678 the -e/--existing flag.
1679
1679
1680 With -f/--force, an existing patch of the same name will be
1680 With -f/--force, an existing patch of the same name will be
1681 overwritten.
1681 overwritten.
1682
1682
1683 An existing changeset may be placed under mq control with -r/--rev
1683 An existing changeset may be placed under mq control with -r/--rev
1684 (e.g. qimport --rev tip -n patch will place tip under mq control).
1684 (e.g. qimport --rev tip -n patch will place tip under mq control).
1685 With -g/--git, patches imported with --rev will use the git diff
1685 With -g/--git, patches imported with --rev will use the git diff
1686 format. See the diffs help topic for information on why this is
1686 format. See the diffs help topic for information on why this is
1687 important for preserving rename/copy information and permission
1687 important for preserving rename/copy information and permission
1688 changes.
1688 changes.
1689
1689
1690 To import a patch from standard input, pass - as the patch file.
1690 To import a patch from standard input, pass - as the patch file.
1691 When importing from standard input, a patch name must be specified
1691 When importing from standard input, a patch name must be specified
1692 using the --name flag.
1692 using the --name flag.
1693 """
1693 """
1694 q = repo.mq
1694 q = repo.mq
1695 q.qimport(repo, filename, patchname=opts['name'],
1695 q.qimport(repo, filename, patchname=opts['name'],
1696 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1696 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1697 git=opts['git'])
1697 git=opts['git'])
1698 q.save_dirty()
1698 q.save_dirty()
1699 return 0
1699 return 0
1700
1700
1701 def init(ui, repo, **opts):
1701 def init(ui, repo, **opts):
1702 """init a new queue repository
1702 """init a new queue repository
1703
1703
1704 The queue repository is unversioned by default. If
1704 The queue repository is unversioned by default. If
1705 -c/--create-repo is specified, qinit will create a separate nested
1705 -c/--create-repo is specified, qinit will create a separate nested
1706 repository for patches (qinit -c may also be run later to convert
1706 repository for patches (qinit -c may also be run later to convert
1707 an unversioned patch repository into a versioned one). You can use
1707 an unversioned patch repository into a versioned one). You can use
1708 qcommit to commit changes to this queue repository."""
1708 qcommit to commit changes to this queue repository."""
1709 q = repo.mq
1709 q = repo.mq
1710 r = q.init(repo, create=opts['create_repo'])
1710 r = q.init(repo, create=opts['create_repo'])
1711 q.save_dirty()
1711 q.save_dirty()
1712 if r:
1712 if r:
1713 if not os.path.exists(r.wjoin('.hgignore')):
1713 if not os.path.exists(r.wjoin('.hgignore')):
1714 fp = r.wopener('.hgignore', 'w')
1714 fp = r.wopener('.hgignore', 'w')
1715 fp.write('^\\.hg\n')
1715 fp.write('^\\.hg\n')
1716 fp.write('^\\.mq\n')
1716 fp.write('^\\.mq\n')
1717 fp.write('syntax: glob\n')
1717 fp.write('syntax: glob\n')
1718 fp.write('status\n')
1718 fp.write('status\n')
1719 fp.write('guards\n')
1719 fp.write('guards\n')
1720 fp.close()
1720 fp.close()
1721 if not os.path.exists(r.wjoin('series')):
1721 if not os.path.exists(r.wjoin('series')):
1722 r.wopener('series', 'w').close()
1722 r.wopener('series', 'w').close()
1723 r.add(['.hgignore', 'series'])
1723 r.add(['.hgignore', 'series'])
1724 commands.add(ui, r)
1724 commands.add(ui, r)
1725 return 0
1725 return 0
1726
1726
1727 def clone(ui, source, dest=None, **opts):
1727 def clone(ui, source, dest=None, **opts):
1728 '''clone main and patch repository at same time
1728 '''clone main and patch repository at same time
1729
1729
1730 If source is local, destination will have no patches applied. If
1730 If source is local, destination will have no patches applied. If
1731 source is remote, this command can not check if patches are
1731 source is remote, this command can not check if patches are
1732 applied in source, so cannot guarantee that patches are not
1732 applied in source, so cannot guarantee that patches are not
1733 applied in destination. If you clone remote repository, be sure
1733 applied in destination. If you clone remote repository, be sure
1734 before that it has no patches applied.
1734 before that it has no patches applied.
1735
1735
1736 Source patch repository is looked for in <src>/.hg/patches by
1736 Source patch repository is looked for in <src>/.hg/patches by
1737 default. Use -p <url> to change.
1737 default. Use -p <url> to change.
1738
1738
1739 The patch directory must be a nested mercurial repository, as
1739 The patch directory must be a nested mercurial repository, as
1740 would be created by qinit -c.
1740 would be created by qinit -c.
1741 '''
1741 '''
1742 def patchdir(repo):
1742 def patchdir(repo):
1743 url = repo.url()
1743 url = repo.url()
1744 if url.endswith('/'):
1744 if url.endswith('/'):
1745 url = url[:-1]
1745 url = url[:-1]
1746 return url + '/.hg/patches'
1746 return url + '/.hg/patches'
1747 cmdutil.setremoteconfig(ui, opts)
1748 if dest is None:
1747 if dest is None:
1749 dest = hg.defaultdest(source)
1748 dest = hg.defaultdest(source)
1750 sr = hg.repository(ui, ui.expandpath(source))
1749 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1751 if opts['patches']:
1750 if opts['patches']:
1752 patchespath = ui.expandpath(opts['patches'])
1751 patchespath = ui.expandpath(opts['patches'])
1753 else:
1752 else:
1754 patchespath = patchdir(sr)
1753 patchespath = patchdir(sr)
1755 try:
1754 try:
1756 hg.repository(ui, patchespath)
1755 hg.repository(ui, patchespath)
1757 except error.RepoError:
1756 except error.RepoError:
1758 raise util.Abort(_('versioned patch repository not found'
1757 raise util.Abort(_('versioned patch repository not found'
1759 ' (see qinit -c)'))
1758 ' (see qinit -c)'))
1760 qbase, destrev = None, None
1759 qbase, destrev = None, None
1761 if sr.local():
1760 if sr.local():
1762 if sr.mq.applied:
1761 if sr.mq.applied:
1763 qbase = bin(sr.mq.applied[0].rev)
1762 qbase = bin(sr.mq.applied[0].rev)
1764 if not hg.islocal(dest):
1763 if not hg.islocal(dest):
1765 heads = set(sr.heads())
1764 heads = set(sr.heads())
1766 destrev = list(heads.difference(sr.heads(qbase)))
1765 destrev = list(heads.difference(sr.heads(qbase)))
1767 destrev.append(sr.changelog.parents(qbase)[0])
1766 destrev.append(sr.changelog.parents(qbase)[0])
1768 elif sr.capable('lookup'):
1767 elif sr.capable('lookup'):
1769 try:
1768 try:
1770 qbase = sr.lookup('qbase')
1769 qbase = sr.lookup('qbase')
1771 except error.RepoError:
1770 except error.RepoError:
1772 pass
1771 pass
1773 ui.note(_('cloning main repository\n'))
1772 ui.note(_('cloning main repository\n'))
1774 sr, dr = hg.clone(ui, sr.url(), dest,
1773 sr, dr = hg.clone(ui, sr.url(), dest,
1775 pull=opts['pull'],
1774 pull=opts['pull'],
1776 rev=destrev,
1775 rev=destrev,
1777 update=False,
1776 update=False,
1778 stream=opts['uncompressed'])
1777 stream=opts['uncompressed'])
1779 ui.note(_('cloning patch repository\n'))
1778 ui.note(_('cloning patch repository\n'))
1780 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1779 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1781 pull=opts['pull'], update=not opts['noupdate'],
1780 pull=opts['pull'], update=not opts['noupdate'],
1782 stream=opts['uncompressed'])
1781 stream=opts['uncompressed'])
1783 if dr.local():
1782 if dr.local():
1784 if qbase:
1783 if qbase:
1785 ui.note(_('stripping applied patches from destination '
1784 ui.note(_('stripping applied patches from destination '
1786 'repository\n'))
1785 'repository\n'))
1787 dr.mq.strip(dr, qbase, update=False, backup=None)
1786 dr.mq.strip(dr, qbase, update=False, backup=None)
1788 if not opts['noupdate']:
1787 if not opts['noupdate']:
1789 ui.note(_('updating destination repository\n'))
1788 ui.note(_('updating destination repository\n'))
1790 hg.update(dr, dr.changelog.tip())
1789 hg.update(dr, dr.changelog.tip())
1791
1790
1792 def commit(ui, repo, *pats, **opts):
1791 def commit(ui, repo, *pats, **opts):
1793 """commit changes in the queue repository"""
1792 """commit changes in the queue repository"""
1794 q = repo.mq
1793 q = repo.mq
1795 r = q.qrepo()
1794 r = q.qrepo()
1796 if not r: raise util.Abort('no queue repository')
1795 if not r: raise util.Abort('no queue repository')
1797 commands.commit(r.ui, r, *pats, **opts)
1796 commands.commit(r.ui, r, *pats, **opts)
1798
1797
1799 def series(ui, repo, **opts):
1798 def series(ui, repo, **opts):
1800 """print the entire series file"""
1799 """print the entire series file"""
1801 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1800 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1802 return 0
1801 return 0
1803
1802
1804 def top(ui, repo, **opts):
1803 def top(ui, repo, **opts):
1805 """print the name of the current patch"""
1804 """print the name of the current patch"""
1806 q = repo.mq
1805 q = repo.mq
1807 t = q.applied and q.series_end(True) or 0
1806 t = q.applied and q.series_end(True) or 0
1808 if t:
1807 if t:
1809 return q.qseries(repo, start=t-1, length=1, status='A',
1808 return q.qseries(repo, start=t-1, length=1, status='A',
1810 summary=opts.get('summary'))
1809 summary=opts.get('summary'))
1811 else:
1810 else:
1812 ui.write(_("no patches applied\n"))
1811 ui.write(_("no patches applied\n"))
1813 return 1
1812 return 1
1814
1813
1815 def next(ui, repo, **opts):
1814 def next(ui, repo, **opts):
1816 """print the name of the next patch"""
1815 """print the name of the next patch"""
1817 q = repo.mq
1816 q = repo.mq
1818 end = q.series_end()
1817 end = q.series_end()
1819 if end == len(q.series):
1818 if end == len(q.series):
1820 ui.write(_("all patches applied\n"))
1819 ui.write(_("all patches applied\n"))
1821 return 1
1820 return 1
1822 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1821 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1823
1822
1824 def prev(ui, repo, **opts):
1823 def prev(ui, repo, **opts):
1825 """print the name of the previous patch"""
1824 """print the name of the previous patch"""
1826 q = repo.mq
1825 q = repo.mq
1827 l = len(q.applied)
1826 l = len(q.applied)
1828 if l == 1:
1827 if l == 1:
1829 ui.write(_("only one patch applied\n"))
1828 ui.write(_("only one patch applied\n"))
1830 return 1
1829 return 1
1831 if not l:
1830 if not l:
1832 ui.write(_("no patches applied\n"))
1831 ui.write(_("no patches applied\n"))
1833 return 1
1832 return 1
1834 return q.qseries(repo, start=l-2, length=1, status='A',
1833 return q.qseries(repo, start=l-2, length=1, status='A',
1835 summary=opts.get('summary'))
1834 summary=opts.get('summary'))
1836
1835
1837 def setupheaderopts(ui, opts):
1836 def setupheaderopts(ui, opts):
1838 def do(opt,val):
1837 def do(opt,val):
1839 if not opts[opt] and opts['current' + opt]:
1838 if not opts[opt] and opts['current' + opt]:
1840 opts[opt] = val
1839 opts[opt] = val
1841 do('user', ui.username())
1840 do('user', ui.username())
1842 do('date', "%d %d" % util.makedate())
1841 do('date', "%d %d" % util.makedate())
1843
1842
1844 def new(ui, repo, patch, *args, **opts):
1843 def new(ui, repo, patch, *args, **opts):
1845 """create a new patch
1844 """create a new patch
1846
1845
1847 qnew creates a new patch on top of the currently-applied patch (if
1846 qnew creates a new patch on top of the currently-applied patch (if
1848 any). It will refuse to run if there are any outstanding changes
1847 any). It will refuse to run if there are any outstanding changes
1849 unless -f/--force is specified, in which case the patch will be
1848 unless -f/--force is specified, in which case the patch will be
1850 initialized with them. You may also use -I/--include,
1849 initialized with them. You may also use -I/--include,
1851 -X/--exclude, and/or a list of files after the patch name to add
1850 -X/--exclude, and/or a list of files after the patch name to add
1852 only changes to matching files to the new patch, leaving the rest
1851 only changes to matching files to the new patch, leaving the rest
1853 as uncommitted modifications.
1852 as uncommitted modifications.
1854
1853
1855 -u/--user and -d/--date can be used to set the (given) user and
1854 -u/--user and -d/--date can be used to set the (given) user and
1856 date, respectively. -U/--currentuser and -D/--currentdate set user
1855 date, respectively. -U/--currentuser and -D/--currentdate set user
1857 to current user and date to current date.
1856 to current user and date to current date.
1858
1857
1859 -e/--edit, -m/--message or -l/--logfile set the patch header as
1858 -e/--edit, -m/--message or -l/--logfile set the patch header as
1860 well as the commit message. If none is specified, the header is
1859 well as the commit message. If none is specified, the header is
1861 empty and the commit message is '[mq]: PATCH'.
1860 empty and the commit message is '[mq]: PATCH'.
1862
1861
1863 Use the -g/--git option to keep the patch in the git extended diff
1862 Use the -g/--git option to keep the patch in the git extended diff
1864 format. Read the diffs help topic for more information on why this
1863 format. Read the diffs help topic for more information on why this
1865 is important for preserving permission changes and copy/rename
1864 is important for preserving permission changes and copy/rename
1866 information.
1865 information.
1867 """
1866 """
1868 msg = cmdutil.logmessage(opts)
1867 msg = cmdutil.logmessage(opts)
1869 def getmsg(): return ui.edit(msg, ui.username())
1868 def getmsg(): return ui.edit(msg, ui.username())
1870 q = repo.mq
1869 q = repo.mq
1871 opts['msg'] = msg
1870 opts['msg'] = msg
1872 if opts.get('edit'):
1871 if opts.get('edit'):
1873 opts['msg'] = getmsg
1872 opts['msg'] = getmsg
1874 else:
1873 else:
1875 opts['msg'] = msg
1874 opts['msg'] = msg
1876 setupheaderopts(ui, opts)
1875 setupheaderopts(ui, opts)
1877 q.new(repo, patch, *args, **opts)
1876 q.new(repo, patch, *args, **opts)
1878 q.save_dirty()
1877 q.save_dirty()
1879 return 0
1878 return 0
1880
1879
1881 def refresh(ui, repo, *pats, **opts):
1880 def refresh(ui, repo, *pats, **opts):
1882 """update the current patch
1881 """update the current patch
1883
1882
1884 If any file patterns are provided, the refreshed patch will
1883 If any file patterns are provided, the refreshed patch will
1885 contain only the modifications that match those patterns; the
1884 contain only the modifications that match those patterns; the
1886 remaining modifications will remain in the working directory.
1885 remaining modifications will remain in the working directory.
1887
1886
1888 If -s/--short is specified, files currently included in the patch
1887 If -s/--short is specified, files currently included in the patch
1889 will be refreshed just like matched files and remain in the patch.
1888 will be refreshed just like matched files and remain in the patch.
1890
1889
1891 hg add/remove/copy/rename work as usual, though you might want to
1890 hg add/remove/copy/rename work as usual, though you might want to
1892 use git-style patches (-g/--git or [diff] git=1) to track copies
1891 use git-style patches (-g/--git or [diff] git=1) to track copies
1893 and renames. See the diffs help topic for more information on the
1892 and renames. See the diffs help topic for more information on the
1894 git diff format.
1893 git diff format.
1895 """
1894 """
1896 q = repo.mq
1895 q = repo.mq
1897 message = cmdutil.logmessage(opts)
1896 message = cmdutil.logmessage(opts)
1898 if opts['edit']:
1897 if opts['edit']:
1899 if not q.applied:
1898 if not q.applied:
1900 ui.write(_("no patches applied\n"))
1899 ui.write(_("no patches applied\n"))
1901 return 1
1900 return 1
1902 if message:
1901 if message:
1903 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1902 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1904 patch = q.applied[-1].name
1903 patch = q.applied[-1].name
1905 ph = q.readheaders(patch)
1904 ph = q.readheaders(patch)
1906 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1905 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1907 setupheaderopts(ui, opts)
1906 setupheaderopts(ui, opts)
1908 ret = q.refresh(repo, pats, msg=message, **opts)
1907 ret = q.refresh(repo, pats, msg=message, **opts)
1909 q.save_dirty()
1908 q.save_dirty()
1910 return ret
1909 return ret
1911
1910
1912 def diff(ui, repo, *pats, **opts):
1911 def diff(ui, repo, *pats, **opts):
1913 """diff of the current patch and subsequent modifications
1912 """diff of the current patch and subsequent modifications
1914
1913
1915 Shows a diff which includes the current patch as well as any
1914 Shows a diff which includes the current patch as well as any
1916 changes which have been made in the working directory since the
1915 changes which have been made in the working directory since the
1917 last refresh (thus showing what the current patch would become
1916 last refresh (thus showing what the current patch would become
1918 after a qrefresh).
1917 after a qrefresh).
1919
1918
1920 Use 'hg diff' if you only want to see the changes made since the
1919 Use 'hg diff' if you only want to see the changes made since the
1921 last qrefresh, or 'hg export qtip' if you want to see changes made
1920 last qrefresh, or 'hg export qtip' if you want to see changes made
1922 by the current patch without including changes made since the
1921 by the current patch without including changes made since the
1923 qrefresh.
1922 qrefresh.
1924 """
1923 """
1925 repo.mq.diff(repo, pats, opts)
1924 repo.mq.diff(repo, pats, opts)
1926 return 0
1925 return 0
1927
1926
1928 def fold(ui, repo, *files, **opts):
1927 def fold(ui, repo, *files, **opts):
1929 """fold the named patches into the current patch
1928 """fold the named patches into the current patch
1930
1929
1931 Patches must not yet be applied. Each patch will be successively
1930 Patches must not yet be applied. Each patch will be successively
1932 applied to the current patch in the order given. If all the
1931 applied to the current patch in the order given. If all the
1933 patches apply successfully, the current patch will be refreshed
1932 patches apply successfully, the current patch will be refreshed
1934 with the new cumulative patch, and the folded patches will be
1933 with the new cumulative patch, and the folded patches will be
1935 deleted. With -k/--keep, the folded patch files will not be
1934 deleted. With -k/--keep, the folded patch files will not be
1936 removed afterwards.
1935 removed afterwards.
1937
1936
1938 The header for each folded patch will be concatenated with the
1937 The header for each folded patch will be concatenated with the
1939 current patch header, separated by a line of '* * *'."""
1938 current patch header, separated by a line of '* * *'."""
1940
1939
1941 q = repo.mq
1940 q = repo.mq
1942
1941
1943 if not files:
1942 if not files:
1944 raise util.Abort(_('qfold requires at least one patch name'))
1943 raise util.Abort(_('qfold requires at least one patch name'))
1945 if not q.check_toppatch(repo):
1944 if not q.check_toppatch(repo):
1946 raise util.Abort(_('No patches applied'))
1945 raise util.Abort(_('No patches applied'))
1947
1946
1948 message = cmdutil.logmessage(opts)
1947 message = cmdutil.logmessage(opts)
1949 if opts['edit']:
1948 if opts['edit']:
1950 if message:
1949 if message:
1951 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1950 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1952
1951
1953 parent = q.lookup('qtip')
1952 parent = q.lookup('qtip')
1954 patches = []
1953 patches = []
1955 messages = []
1954 messages = []
1956 for f in files:
1955 for f in files:
1957 p = q.lookup(f)
1956 p = q.lookup(f)
1958 if p in patches or p == parent:
1957 if p in patches or p == parent:
1959 ui.warn(_('Skipping already folded patch %s') % p)
1958 ui.warn(_('Skipping already folded patch %s') % p)
1960 if q.isapplied(p):
1959 if q.isapplied(p):
1961 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1960 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1962 patches.append(p)
1961 patches.append(p)
1963
1962
1964 for p in patches:
1963 for p in patches:
1965 if not message:
1964 if not message:
1966 ph = q.readheaders(p)
1965 ph = q.readheaders(p)
1967 if ph.message:
1966 if ph.message:
1968 messages.append(ph.message)
1967 messages.append(ph.message)
1969 pf = q.join(p)
1968 pf = q.join(p)
1970 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1969 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1971 if not patchsuccess:
1970 if not patchsuccess:
1972 raise util.Abort(_('Error folding patch %s') % p)
1971 raise util.Abort(_('Error folding patch %s') % p)
1973 patch.updatedir(ui, repo, files)
1972 patch.updatedir(ui, repo, files)
1974
1973
1975 if not message:
1974 if not message:
1976 ph = q.readheaders(parent)
1975 ph = q.readheaders(parent)
1977 message, user = ph.message, ph.user
1976 message, user = ph.message, ph.user
1978 for msg in messages:
1977 for msg in messages:
1979 message.append('* * *')
1978 message.append('* * *')
1980 message.extend(msg)
1979 message.extend(msg)
1981 message = '\n'.join(message)
1980 message = '\n'.join(message)
1982
1981
1983 if opts['edit']:
1982 if opts['edit']:
1984 message = ui.edit(message, user or ui.username())
1983 message = ui.edit(message, user or ui.username())
1985
1984
1986 q.refresh(repo, msg=message)
1985 q.refresh(repo, msg=message)
1987 q.delete(repo, patches, opts)
1986 q.delete(repo, patches, opts)
1988 q.save_dirty()
1987 q.save_dirty()
1989
1988
1990 def goto(ui, repo, patch, **opts):
1989 def goto(ui, repo, patch, **opts):
1991 '''push or pop patches until named patch is at top of stack'''
1990 '''push or pop patches until named patch is at top of stack'''
1992 q = repo.mq
1991 q = repo.mq
1993 patch = q.lookup(patch)
1992 patch = q.lookup(patch)
1994 if q.isapplied(patch):
1993 if q.isapplied(patch):
1995 ret = q.pop(repo, patch, force=opts['force'])
1994 ret = q.pop(repo, patch, force=opts['force'])
1996 else:
1995 else:
1997 ret = q.push(repo, patch, force=opts['force'])
1996 ret = q.push(repo, patch, force=opts['force'])
1998 q.save_dirty()
1997 q.save_dirty()
1999 return ret
1998 return ret
2000
1999
2001 def guard(ui, repo, *args, **opts):
2000 def guard(ui, repo, *args, **opts):
2002 '''set or print guards for a patch
2001 '''set or print guards for a patch
2003
2002
2004 Guards control whether a patch can be pushed. A patch with no
2003 Guards control whether a patch can be pushed. A patch with no
2005 guards is always pushed. A patch with a positive guard ("+foo") is
2004 guards is always pushed. A patch with a positive guard ("+foo") is
2006 pushed only if the qselect command has activated it. A patch with
2005 pushed only if the qselect command has activated it. A patch with
2007 a negative guard ("-foo") is never pushed if the qselect command
2006 a negative guard ("-foo") is never pushed if the qselect command
2008 has activated it.
2007 has activated it.
2009
2008
2010 With no arguments, print the currently active guards.
2009 With no arguments, print the currently active guards.
2011 With arguments, set guards for the named patch.
2010 With arguments, set guards for the named patch.
2012 NOTE: Specifying negative guards now requires '--'.
2011 NOTE: Specifying negative guards now requires '--'.
2013
2012
2014 To set guards on another patch:
2013 To set guards on another patch:
2015 hg qguard -- other.patch +2.6.17 -stable
2014 hg qguard -- other.patch +2.6.17 -stable
2016 '''
2015 '''
2017 def status(idx):
2016 def status(idx):
2018 guards = q.series_guards[idx] or ['unguarded']
2017 guards = q.series_guards[idx] or ['unguarded']
2019 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2018 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2020 q = repo.mq
2019 q = repo.mq
2021 patch = None
2020 patch = None
2022 args = list(args)
2021 args = list(args)
2023 if opts['list']:
2022 if opts['list']:
2024 if args or opts['none']:
2023 if args or opts['none']:
2025 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2024 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2026 for i in xrange(len(q.series)):
2025 for i in xrange(len(q.series)):
2027 status(i)
2026 status(i)
2028 return
2027 return
2029 if not args or args[0][0:1] in '-+':
2028 if not args or args[0][0:1] in '-+':
2030 if not q.applied:
2029 if not q.applied:
2031 raise util.Abort(_('no patches applied'))
2030 raise util.Abort(_('no patches applied'))
2032 patch = q.applied[-1].name
2031 patch = q.applied[-1].name
2033 if patch is None and args[0][0:1] not in '-+':
2032 if patch is None and args[0][0:1] not in '-+':
2034 patch = args.pop(0)
2033 patch = args.pop(0)
2035 if patch is None:
2034 if patch is None:
2036 raise util.Abort(_('no patch to work with'))
2035 raise util.Abort(_('no patch to work with'))
2037 if args or opts['none']:
2036 if args or opts['none']:
2038 idx = q.find_series(patch)
2037 idx = q.find_series(patch)
2039 if idx is None:
2038 if idx is None:
2040 raise util.Abort(_('no patch named %s') % patch)
2039 raise util.Abort(_('no patch named %s') % patch)
2041 q.set_guards(idx, args)
2040 q.set_guards(idx, args)
2042 q.save_dirty()
2041 q.save_dirty()
2043 else:
2042 else:
2044 status(q.series.index(q.lookup(patch)))
2043 status(q.series.index(q.lookup(patch)))
2045
2044
2046 def header(ui, repo, patch=None):
2045 def header(ui, repo, patch=None):
2047 """print the header of the topmost or specified patch"""
2046 """print the header of the topmost or specified patch"""
2048 q = repo.mq
2047 q = repo.mq
2049
2048
2050 if patch:
2049 if patch:
2051 patch = q.lookup(patch)
2050 patch = q.lookup(patch)
2052 else:
2051 else:
2053 if not q.applied:
2052 if not q.applied:
2054 ui.write('no patches applied\n')
2053 ui.write('no patches applied\n')
2055 return 1
2054 return 1
2056 patch = q.lookup('qtip')
2055 patch = q.lookup('qtip')
2057 ph = repo.mq.readheaders(patch)
2056 ph = repo.mq.readheaders(patch)
2058
2057
2059 ui.write('\n'.join(ph.message) + '\n')
2058 ui.write('\n'.join(ph.message) + '\n')
2060
2059
2061 def lastsavename(path):
2060 def lastsavename(path):
2062 (directory, base) = os.path.split(path)
2061 (directory, base) = os.path.split(path)
2063 names = os.listdir(directory)
2062 names = os.listdir(directory)
2064 namere = re.compile("%s.([0-9]+)" % base)
2063 namere = re.compile("%s.([0-9]+)" % base)
2065 maxindex = None
2064 maxindex = None
2066 maxname = None
2065 maxname = None
2067 for f in names:
2066 for f in names:
2068 m = namere.match(f)
2067 m = namere.match(f)
2069 if m:
2068 if m:
2070 index = int(m.group(1))
2069 index = int(m.group(1))
2071 if maxindex == None or index > maxindex:
2070 if maxindex == None or index > maxindex:
2072 maxindex = index
2071 maxindex = index
2073 maxname = f
2072 maxname = f
2074 if maxname:
2073 if maxname:
2075 return (os.path.join(directory, maxname), maxindex)
2074 return (os.path.join(directory, maxname), maxindex)
2076 return (None, None)
2075 return (None, None)
2077
2076
2078 def savename(path):
2077 def savename(path):
2079 (last, index) = lastsavename(path)
2078 (last, index) = lastsavename(path)
2080 if last is None:
2079 if last is None:
2081 index = 0
2080 index = 0
2082 newpath = path + ".%d" % (index + 1)
2081 newpath = path + ".%d" % (index + 1)
2083 return newpath
2082 return newpath
2084
2083
2085 def push(ui, repo, patch=None, **opts):
2084 def push(ui, repo, patch=None, **opts):
2086 """push the next patch onto the stack
2085 """push the next patch onto the stack
2087
2086
2088 When -f/--force is applied, all local changes in patched files
2087 When -f/--force is applied, all local changes in patched files
2089 will be lost.
2088 will be lost.
2090 """
2089 """
2091 q = repo.mq
2090 q = repo.mq
2092 mergeq = None
2091 mergeq = None
2093
2092
2094 if opts['merge']:
2093 if opts['merge']:
2095 if opts['name']:
2094 if opts['name']:
2096 newpath = repo.join(opts['name'])
2095 newpath = repo.join(opts['name'])
2097 else:
2096 else:
2098 newpath, i = lastsavename(q.path)
2097 newpath, i = lastsavename(q.path)
2099 if not newpath:
2098 if not newpath:
2100 ui.warn(_("no saved queues found, please use -n\n"))
2099 ui.warn(_("no saved queues found, please use -n\n"))
2101 return 1
2100 return 1
2102 mergeq = queue(ui, repo.join(""), newpath)
2101 mergeq = queue(ui, repo.join(""), newpath)
2103 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2102 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2104 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2103 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2105 mergeq=mergeq, all=opts.get('all'))
2104 mergeq=mergeq, all=opts.get('all'))
2106 return ret
2105 return ret
2107
2106
2108 def pop(ui, repo, patch=None, **opts):
2107 def pop(ui, repo, patch=None, **opts):
2109 """pop the current patch off the stack
2108 """pop the current patch off the stack
2110
2109
2111 By default, pops off the top of the patch stack. If given a patch
2110 By default, pops off the top of the patch stack. If given a patch
2112 name, keeps popping off patches until the named patch is at the
2111 name, keeps popping off patches until the named patch is at the
2113 top of the stack.
2112 top of the stack.
2114 """
2113 """
2115 localupdate = True
2114 localupdate = True
2116 if opts['name']:
2115 if opts['name']:
2117 q = queue(ui, repo.join(""), repo.join(opts['name']))
2116 q = queue(ui, repo.join(""), repo.join(opts['name']))
2118 ui.warn(_('using patch queue: %s\n') % q.path)
2117 ui.warn(_('using patch queue: %s\n') % q.path)
2119 localupdate = False
2118 localupdate = False
2120 else:
2119 else:
2121 q = repo.mq
2120 q = repo.mq
2122 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2121 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2123 all=opts['all'])
2122 all=opts['all'])
2124 q.save_dirty()
2123 q.save_dirty()
2125 return ret
2124 return ret
2126
2125
2127 def rename(ui, repo, patch, name=None, **opts):
2126 def rename(ui, repo, patch, name=None, **opts):
2128 """rename a patch
2127 """rename a patch
2129
2128
2130 With one argument, renames the current patch to PATCH1.
2129 With one argument, renames the current patch to PATCH1.
2131 With two arguments, renames PATCH1 to PATCH2."""
2130 With two arguments, renames PATCH1 to PATCH2."""
2132
2131
2133 q = repo.mq
2132 q = repo.mq
2134
2133
2135 if not name:
2134 if not name:
2136 name = patch
2135 name = patch
2137 patch = None
2136 patch = None
2138
2137
2139 if patch:
2138 if patch:
2140 patch = q.lookup(patch)
2139 patch = q.lookup(patch)
2141 else:
2140 else:
2142 if not q.applied:
2141 if not q.applied:
2143 ui.write(_('no patches applied\n'))
2142 ui.write(_('no patches applied\n'))
2144 return
2143 return
2145 patch = q.lookup('qtip')
2144 patch = q.lookup('qtip')
2146 absdest = q.join(name)
2145 absdest = q.join(name)
2147 if os.path.isdir(absdest):
2146 if os.path.isdir(absdest):
2148 name = normname(os.path.join(name, os.path.basename(patch)))
2147 name = normname(os.path.join(name, os.path.basename(patch)))
2149 absdest = q.join(name)
2148 absdest = q.join(name)
2150 if os.path.exists(absdest):
2149 if os.path.exists(absdest):
2151 raise util.Abort(_('%s already exists') % absdest)
2150 raise util.Abort(_('%s already exists') % absdest)
2152
2151
2153 if name in q.series:
2152 if name in q.series:
2154 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2153 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2155
2154
2156 if ui.verbose:
2155 if ui.verbose:
2157 ui.write('renaming %s to %s\n' % (patch, name))
2156 ui.write('renaming %s to %s\n' % (patch, name))
2158 i = q.find_series(patch)
2157 i = q.find_series(patch)
2159 guards = q.guard_re.findall(q.full_series[i])
2158 guards = q.guard_re.findall(q.full_series[i])
2160 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2159 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2161 q.parse_series()
2160 q.parse_series()
2162 q.series_dirty = 1
2161 q.series_dirty = 1
2163
2162
2164 info = q.isapplied(patch)
2163 info = q.isapplied(patch)
2165 if info:
2164 if info:
2166 q.applied[info[0]] = statusentry(info[1], name)
2165 q.applied[info[0]] = statusentry(info[1], name)
2167 q.applied_dirty = 1
2166 q.applied_dirty = 1
2168
2167
2169 util.rename(q.join(patch), absdest)
2168 util.rename(q.join(patch), absdest)
2170 r = q.qrepo()
2169 r = q.qrepo()
2171 if r:
2170 if r:
2172 wlock = r.wlock()
2171 wlock = r.wlock()
2173 try:
2172 try:
2174 if r.dirstate[patch] == 'a':
2173 if r.dirstate[patch] == 'a':
2175 r.dirstate.forget(patch)
2174 r.dirstate.forget(patch)
2176 r.dirstate.add(name)
2175 r.dirstate.add(name)
2177 else:
2176 else:
2178 if r.dirstate[name] == 'r':
2177 if r.dirstate[name] == 'r':
2179 r.undelete([name])
2178 r.undelete([name])
2180 r.copy(patch, name)
2179 r.copy(patch, name)
2181 r.remove([patch], False)
2180 r.remove([patch], False)
2182 finally:
2181 finally:
2183 wlock.release()
2182 wlock.release()
2184
2183
2185 q.save_dirty()
2184 q.save_dirty()
2186
2185
2187 def restore(ui, repo, rev, **opts):
2186 def restore(ui, repo, rev, **opts):
2188 """restore the queue state saved by a revision"""
2187 """restore the queue state saved by a revision"""
2189 rev = repo.lookup(rev)
2188 rev = repo.lookup(rev)
2190 q = repo.mq
2189 q = repo.mq
2191 q.restore(repo, rev, delete=opts['delete'],
2190 q.restore(repo, rev, delete=opts['delete'],
2192 qupdate=opts['update'])
2191 qupdate=opts['update'])
2193 q.save_dirty()
2192 q.save_dirty()
2194 return 0
2193 return 0
2195
2194
2196 def save(ui, repo, **opts):
2195 def save(ui, repo, **opts):
2197 """save current queue state"""
2196 """save current queue state"""
2198 q = repo.mq
2197 q = repo.mq
2199 message = cmdutil.logmessage(opts)
2198 message = cmdutil.logmessage(opts)
2200 ret = q.save(repo, msg=message)
2199 ret = q.save(repo, msg=message)
2201 if ret:
2200 if ret:
2202 return ret
2201 return ret
2203 q.save_dirty()
2202 q.save_dirty()
2204 if opts['copy']:
2203 if opts['copy']:
2205 path = q.path
2204 path = q.path
2206 if opts['name']:
2205 if opts['name']:
2207 newpath = os.path.join(q.basepath, opts['name'])
2206 newpath = os.path.join(q.basepath, opts['name'])
2208 if os.path.exists(newpath):
2207 if os.path.exists(newpath):
2209 if not os.path.isdir(newpath):
2208 if not os.path.isdir(newpath):
2210 raise util.Abort(_('destination %s exists and is not '
2209 raise util.Abort(_('destination %s exists and is not '
2211 'a directory') % newpath)
2210 'a directory') % newpath)
2212 if not opts['force']:
2211 if not opts['force']:
2213 raise util.Abort(_('destination %s exists, '
2212 raise util.Abort(_('destination %s exists, '
2214 'use -f to force') % newpath)
2213 'use -f to force') % newpath)
2215 else:
2214 else:
2216 newpath = savename(path)
2215 newpath = savename(path)
2217 ui.warn(_("copy %s to %s\n") % (path, newpath))
2216 ui.warn(_("copy %s to %s\n") % (path, newpath))
2218 util.copyfiles(path, newpath)
2217 util.copyfiles(path, newpath)
2219 if opts['empty']:
2218 if opts['empty']:
2220 try:
2219 try:
2221 os.unlink(q.join(q.status_path))
2220 os.unlink(q.join(q.status_path))
2222 except:
2221 except:
2223 pass
2222 pass
2224 return 0
2223 return 0
2225
2224
2226 def strip(ui, repo, rev, **opts):
2225 def strip(ui, repo, rev, **opts):
2227 """strip a revision and all its descendants from the repository
2226 """strip a revision and all its descendants from the repository
2228
2227
2229 If one of the working directory's parent revisions is stripped, the
2228 If one of the working directory's parent revisions is stripped, the
2230 working directory will be updated to the parent of the stripped
2229 working directory will be updated to the parent of the stripped
2231 revision.
2230 revision.
2232 """
2231 """
2233 backup = 'all'
2232 backup = 'all'
2234 if opts['backup']:
2233 if opts['backup']:
2235 backup = 'strip'
2234 backup = 'strip'
2236 elif opts['nobackup']:
2235 elif opts['nobackup']:
2237 backup = 'none'
2236 backup = 'none'
2238
2237
2239 rev = repo.lookup(rev)
2238 rev = repo.lookup(rev)
2240 p = repo.dirstate.parents()
2239 p = repo.dirstate.parents()
2241 cl = repo.changelog
2240 cl = repo.changelog
2242 update = True
2241 update = True
2243 if p[0] == nullid:
2242 if p[0] == nullid:
2244 update = False
2243 update = False
2245 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2244 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2246 update = False
2245 update = False
2247 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2246 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2248 update = False
2247 update = False
2249
2248
2250 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2249 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2251 return 0
2250 return 0
2252
2251
2253 def select(ui, repo, *args, **opts):
2252 def select(ui, repo, *args, **opts):
2254 '''set or print guarded patches to push
2253 '''set or print guarded patches to push
2255
2254
2256 Use the qguard command to set or print guards on patch, then use
2255 Use the qguard command to set or print guards on patch, then use
2257 qselect to tell mq which guards to use. A patch will be pushed if
2256 qselect to tell mq which guards to use. A patch will be pushed if
2258 it has no guards or any positive guards match the currently
2257 it has no guards or any positive guards match the currently
2259 selected guard, but will not be pushed if any negative guards
2258 selected guard, but will not be pushed if any negative guards
2260 match the current guard. For example:
2259 match the current guard. For example:
2261
2260
2262 qguard foo.patch -stable (negative guard)
2261 qguard foo.patch -stable (negative guard)
2263 qguard bar.patch +stable (positive guard)
2262 qguard bar.patch +stable (positive guard)
2264 qselect stable
2263 qselect stable
2265
2264
2266 This activates the "stable" guard. mq will skip foo.patch (because
2265 This activates the "stable" guard. mq will skip foo.patch (because
2267 it has a negative match) but push bar.patch (because it has a
2266 it has a negative match) but push bar.patch (because it has a
2268 positive match).
2267 positive match).
2269
2268
2270 With no arguments, prints the currently active guards.
2269 With no arguments, prints the currently active guards.
2271 With one argument, sets the active guard.
2270 With one argument, sets the active guard.
2272
2271
2273 Use -n/--none to deactivate guards (no other arguments needed).
2272 Use -n/--none to deactivate guards (no other arguments needed).
2274 When no guards are active, patches with positive guards are
2273 When no guards are active, patches with positive guards are
2275 skipped and patches with negative guards are pushed.
2274 skipped and patches with negative guards are pushed.
2276
2275
2277 qselect can change the guards on applied patches. It does not pop
2276 qselect can change the guards on applied patches. It does not pop
2278 guarded patches by default. Use --pop to pop back to the last
2277 guarded patches by default. Use --pop to pop back to the last
2279 applied patch that is not guarded. Use --reapply (which implies
2278 applied patch that is not guarded. Use --reapply (which implies
2280 --pop) to push back to the current patch afterwards, but skip
2279 --pop) to push back to the current patch afterwards, but skip
2281 guarded patches.
2280 guarded patches.
2282
2281
2283 Use -s/--series to print a list of all guards in the series file
2282 Use -s/--series to print a list of all guards in the series file
2284 (no other arguments needed). Use -v for more information.'''
2283 (no other arguments needed). Use -v for more information.'''
2285
2284
2286 q = repo.mq
2285 q = repo.mq
2287 guards = q.active()
2286 guards = q.active()
2288 if args or opts['none']:
2287 if args or opts['none']:
2289 old_unapplied = q.unapplied(repo)
2288 old_unapplied = q.unapplied(repo)
2290 old_guarded = [i for i in xrange(len(q.applied)) if
2289 old_guarded = [i for i in xrange(len(q.applied)) if
2291 not q.pushable(i)[0]]
2290 not q.pushable(i)[0]]
2292 q.set_active(args)
2291 q.set_active(args)
2293 q.save_dirty()
2292 q.save_dirty()
2294 if not args:
2293 if not args:
2295 ui.status(_('guards deactivated\n'))
2294 ui.status(_('guards deactivated\n'))
2296 if not opts['pop'] and not opts['reapply']:
2295 if not opts['pop'] and not opts['reapply']:
2297 unapplied = q.unapplied(repo)
2296 unapplied = q.unapplied(repo)
2298 guarded = [i for i in xrange(len(q.applied))
2297 guarded = [i for i in xrange(len(q.applied))
2299 if not q.pushable(i)[0]]
2298 if not q.pushable(i)[0]]
2300 if len(unapplied) != len(old_unapplied):
2299 if len(unapplied) != len(old_unapplied):
2301 ui.status(_('number of unguarded, unapplied patches has '
2300 ui.status(_('number of unguarded, unapplied patches has '
2302 'changed from %d to %d\n') %
2301 'changed from %d to %d\n') %
2303 (len(old_unapplied), len(unapplied)))
2302 (len(old_unapplied), len(unapplied)))
2304 if len(guarded) != len(old_guarded):
2303 if len(guarded) != len(old_guarded):
2305 ui.status(_('number of guarded, applied patches has changed '
2304 ui.status(_('number of guarded, applied patches has changed '
2306 'from %d to %d\n') %
2305 'from %d to %d\n') %
2307 (len(old_guarded), len(guarded)))
2306 (len(old_guarded), len(guarded)))
2308 elif opts['series']:
2307 elif opts['series']:
2309 guards = {}
2308 guards = {}
2310 noguards = 0
2309 noguards = 0
2311 for gs in q.series_guards:
2310 for gs in q.series_guards:
2312 if not gs:
2311 if not gs:
2313 noguards += 1
2312 noguards += 1
2314 for g in gs:
2313 for g in gs:
2315 guards.setdefault(g, 0)
2314 guards.setdefault(g, 0)
2316 guards[g] += 1
2315 guards[g] += 1
2317 if ui.verbose:
2316 if ui.verbose:
2318 guards['NONE'] = noguards
2317 guards['NONE'] = noguards
2319 guards = guards.items()
2318 guards = guards.items()
2320 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2319 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2321 if guards:
2320 if guards:
2322 ui.note(_('guards in series file:\n'))
2321 ui.note(_('guards in series file:\n'))
2323 for guard, count in guards:
2322 for guard, count in guards:
2324 ui.note('%2d ' % count)
2323 ui.note('%2d ' % count)
2325 ui.write(guard, '\n')
2324 ui.write(guard, '\n')
2326 else:
2325 else:
2327 ui.note(_('no guards in series file\n'))
2326 ui.note(_('no guards in series file\n'))
2328 else:
2327 else:
2329 if guards:
2328 if guards:
2330 ui.note(_('active guards:\n'))
2329 ui.note(_('active guards:\n'))
2331 for g in guards:
2330 for g in guards:
2332 ui.write(g, '\n')
2331 ui.write(g, '\n')
2333 else:
2332 else:
2334 ui.write(_('no active guards\n'))
2333 ui.write(_('no active guards\n'))
2335 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2334 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2336 popped = False
2335 popped = False
2337 if opts['pop'] or opts['reapply']:
2336 if opts['pop'] or opts['reapply']:
2338 for i in xrange(len(q.applied)):
2337 for i in xrange(len(q.applied)):
2339 pushable, reason = q.pushable(i)
2338 pushable, reason = q.pushable(i)
2340 if not pushable:
2339 if not pushable:
2341 ui.status(_('popping guarded patches\n'))
2340 ui.status(_('popping guarded patches\n'))
2342 popped = True
2341 popped = True
2343 if i == 0:
2342 if i == 0:
2344 q.pop(repo, all=True)
2343 q.pop(repo, all=True)
2345 else:
2344 else:
2346 q.pop(repo, i-1)
2345 q.pop(repo, i-1)
2347 break
2346 break
2348 if popped:
2347 if popped:
2349 try:
2348 try:
2350 if reapply:
2349 if reapply:
2351 ui.status(_('reapplying unguarded patches\n'))
2350 ui.status(_('reapplying unguarded patches\n'))
2352 q.push(repo, reapply)
2351 q.push(repo, reapply)
2353 finally:
2352 finally:
2354 q.save_dirty()
2353 q.save_dirty()
2355
2354
2356 def finish(ui, repo, *revrange, **opts):
2355 def finish(ui, repo, *revrange, **opts):
2357 """move applied patches into repository history
2356 """move applied patches into repository history
2358
2357
2359 Finishes the specified revisions (corresponding to applied
2358 Finishes the specified revisions (corresponding to applied
2360 patches) by moving them out of mq control into regular repository
2359 patches) by moving them out of mq control into regular repository
2361 history.
2360 history.
2362
2361
2363 Accepts a revision range or the -a/--applied option. If --applied
2362 Accepts a revision range or the -a/--applied option. If --applied
2364 is specified, all applied mq revisions are removed from mq
2363 is specified, all applied mq revisions are removed from mq
2365 control. Otherwise, the given revisions must be at the base of the
2364 control. Otherwise, the given revisions must be at the base of the
2366 stack of applied patches.
2365 stack of applied patches.
2367
2366
2368 This can be especially useful if your changes have been applied to
2367 This can be especially useful if your changes have been applied to
2369 an upstream repository, or if you are about to push your changes
2368 an upstream repository, or if you are about to push your changes
2370 to upstream.
2369 to upstream.
2371 """
2370 """
2372 if not opts['applied'] and not revrange:
2371 if not opts['applied'] and not revrange:
2373 raise util.Abort(_('no revisions specified'))
2372 raise util.Abort(_('no revisions specified'))
2374 elif opts['applied']:
2373 elif opts['applied']:
2375 revrange = ('qbase:qtip',) + revrange
2374 revrange = ('qbase:qtip',) + revrange
2376
2375
2377 q = repo.mq
2376 q = repo.mq
2378 if not q.applied:
2377 if not q.applied:
2379 ui.status(_('no patches applied\n'))
2378 ui.status(_('no patches applied\n'))
2380 return 0
2379 return 0
2381
2380
2382 revs = cmdutil.revrange(repo, revrange)
2381 revs = cmdutil.revrange(repo, revrange)
2383 q.finish(repo, revs)
2382 q.finish(repo, revs)
2384 q.save_dirty()
2383 q.save_dirty()
2385 return 0
2384 return 0
2386
2385
2387 def reposetup(ui, repo):
2386 def reposetup(ui, repo):
2388 class mqrepo(repo.__class__):
2387 class mqrepo(repo.__class__):
2389 def abort_if_wdir_patched(self, errmsg, force=False):
2388 def abort_if_wdir_patched(self, errmsg, force=False):
2390 if self.mq.applied and not force:
2389 if self.mq.applied and not force:
2391 parent = hex(self.dirstate.parents()[0])
2390 parent = hex(self.dirstate.parents()[0])
2392 if parent in [s.rev for s in self.mq.applied]:
2391 if parent in [s.rev for s in self.mq.applied]:
2393 raise util.Abort(errmsg)
2392 raise util.Abort(errmsg)
2394
2393
2395 def commit(self, *args, **opts):
2394 def commit(self, *args, **opts):
2396 if len(args) >= 6:
2395 if len(args) >= 6:
2397 force = args[5]
2396 force = args[5]
2398 else:
2397 else:
2399 force = opts.get('force')
2398 force = opts.get('force')
2400 self.abort_if_wdir_patched(
2399 self.abort_if_wdir_patched(
2401 _('cannot commit over an applied mq patch'),
2400 _('cannot commit over an applied mq patch'),
2402 force)
2401 force)
2403
2402
2404 return super(mqrepo, self).commit(*args, **opts)
2403 return super(mqrepo, self).commit(*args, **opts)
2405
2404
2406 def push(self, remote, force=False, revs=None):
2405 def push(self, remote, force=False, revs=None):
2407 if self.mq.applied and not force and not revs:
2406 if self.mq.applied and not force and not revs:
2408 raise util.Abort(_('source has mq patches applied'))
2407 raise util.Abort(_('source has mq patches applied'))
2409 return super(mqrepo, self).push(remote, force, revs)
2408 return super(mqrepo, self).push(remote, force, revs)
2410
2409
2411 def tags(self):
2410 def tags(self):
2412 if self.tagscache:
2411 if self.tagscache:
2413 return self.tagscache
2412 return self.tagscache
2414
2413
2415 tagscache = super(mqrepo, self).tags()
2414 tagscache = super(mqrepo, self).tags()
2416
2415
2417 q = self.mq
2416 q = self.mq
2418 if not q.applied:
2417 if not q.applied:
2419 return tagscache
2418 return tagscache
2420
2419
2421 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2420 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2422
2421
2423 if mqtags[-1][0] not in self.changelog.nodemap:
2422 if mqtags[-1][0] not in self.changelog.nodemap:
2424 self.ui.warn(_('mq status file refers to unknown node %s\n')
2423 self.ui.warn(_('mq status file refers to unknown node %s\n')
2425 % short(mqtags[-1][0]))
2424 % short(mqtags[-1][0]))
2426 return tagscache
2425 return tagscache
2427
2426
2428 mqtags.append((mqtags[-1][0], 'qtip'))
2427 mqtags.append((mqtags[-1][0], 'qtip'))
2429 mqtags.append((mqtags[0][0], 'qbase'))
2428 mqtags.append((mqtags[0][0], 'qbase'))
2430 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2429 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2431 for patch in mqtags:
2430 for patch in mqtags:
2432 if patch[1] in tagscache:
2431 if patch[1] in tagscache:
2433 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2432 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2434 % patch[1])
2433 % patch[1])
2435 else:
2434 else:
2436 tagscache[patch[1]] = patch[0]
2435 tagscache[patch[1]] = patch[0]
2437
2436
2438 return tagscache
2437 return tagscache
2439
2438
2440 def _branchtags(self, partial, lrev):
2439 def _branchtags(self, partial, lrev):
2441 q = self.mq
2440 q = self.mq
2442 if not q.applied:
2441 if not q.applied:
2443 return super(mqrepo, self)._branchtags(partial, lrev)
2442 return super(mqrepo, self)._branchtags(partial, lrev)
2444
2443
2445 cl = self.changelog
2444 cl = self.changelog
2446 qbasenode = bin(q.applied[0].rev)
2445 qbasenode = bin(q.applied[0].rev)
2447 if qbasenode not in cl.nodemap:
2446 if qbasenode not in cl.nodemap:
2448 self.ui.warn(_('mq status file refers to unknown node %s\n')
2447 self.ui.warn(_('mq status file refers to unknown node %s\n')
2449 % short(qbasenode))
2448 % short(qbasenode))
2450 return super(mqrepo, self)._branchtags(partial, lrev)
2449 return super(mqrepo, self)._branchtags(partial, lrev)
2451
2450
2452 qbase = cl.rev(qbasenode)
2451 qbase = cl.rev(qbasenode)
2453 start = lrev + 1
2452 start = lrev + 1
2454 if start < qbase:
2453 if start < qbase:
2455 # update the cache (excluding the patches) and save it
2454 # update the cache (excluding the patches) and save it
2456 self._updatebranchcache(partial, lrev+1, qbase)
2455 self._updatebranchcache(partial, lrev+1, qbase)
2457 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2456 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2458 start = qbase
2457 start = qbase
2459 # if start = qbase, the cache is as updated as it should be.
2458 # if start = qbase, the cache is as updated as it should be.
2460 # if start > qbase, the cache includes (part of) the patches.
2459 # if start > qbase, the cache includes (part of) the patches.
2461 # we might as well use it, but we won't save it.
2460 # we might as well use it, but we won't save it.
2462
2461
2463 # update the cache up to the tip
2462 # update the cache up to the tip
2464 self._updatebranchcache(partial, start, len(cl))
2463 self._updatebranchcache(partial, start, len(cl))
2465
2464
2466 return partial
2465 return partial
2467
2466
2468 if repo.local():
2467 if repo.local():
2469 repo.__class__ = mqrepo
2468 repo.__class__ = mqrepo
2470 repo.mq = queue(ui, repo.join(""))
2469 repo.mq = queue(ui, repo.join(""))
2471
2470
2472 def mqimport(orig, ui, repo, *args, **kwargs):
2471 def mqimport(orig, ui, repo, *args, **kwargs):
2473 if hasattr(repo, 'abort_if_wdir_patched'):
2472 if hasattr(repo, 'abort_if_wdir_patched'):
2474 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2473 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2475 kwargs.get('force'))
2474 kwargs.get('force'))
2476 return orig(ui, repo, *args, **kwargs)
2475 return orig(ui, repo, *args, **kwargs)
2477
2476
2478 def uisetup(ui):
2477 def uisetup(ui):
2479 extensions.wrapcommand(commands.table, 'import', mqimport)
2478 extensions.wrapcommand(commands.table, 'import', mqimport)
2480
2479
2481 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2480 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2482
2481
2483 cmdtable = {
2482 cmdtable = {
2484 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2483 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2485 "qclone":
2484 "qclone":
2486 (clone,
2485 (clone,
2487 [('', 'pull', None, _('use pull protocol to copy metadata')),
2486 [('', 'pull', None, _('use pull protocol to copy metadata')),
2488 ('U', 'noupdate', None, _('do not update the new working directories')),
2487 ('U', 'noupdate', None, _('do not update the new working directories')),
2489 ('', 'uncompressed', None,
2488 ('', 'uncompressed', None,
2490 _('use uncompressed transfer (fast over LAN)')),
2489 _('use uncompressed transfer (fast over LAN)')),
2491 ('p', 'patches', '', _('location of source patch repository')),
2490 ('p', 'patches', '', _('location of source patch repository')),
2492 ] + commands.remoteopts,
2491 ] + commands.remoteopts,
2493 _('hg qclone [OPTION]... SOURCE [DEST]')),
2492 _('hg qclone [OPTION]... SOURCE [DEST]')),
2494 "qcommit|qci":
2493 "qcommit|qci":
2495 (commit,
2494 (commit,
2496 commands.table["^commit|ci"][1],
2495 commands.table["^commit|ci"][1],
2497 _('hg qcommit [OPTION]... [FILE]...')),
2496 _('hg qcommit [OPTION]... [FILE]...')),
2498 "^qdiff":
2497 "^qdiff":
2499 (diff,
2498 (diff,
2500 commands.diffopts + commands.diffopts2 + commands.walkopts,
2499 commands.diffopts + commands.diffopts2 + commands.walkopts,
2501 _('hg qdiff [OPTION]... [FILE]...')),
2500 _('hg qdiff [OPTION]... [FILE]...')),
2502 "qdelete|qremove|qrm":
2501 "qdelete|qremove|qrm":
2503 (delete,
2502 (delete,
2504 [('k', 'keep', None, _('keep patch file')),
2503 [('k', 'keep', None, _('keep patch file')),
2505 ('r', 'rev', [], _('stop managing a revision'))],
2504 ('r', 'rev', [], _('stop managing a revision'))],
2506 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2505 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2507 'qfold':
2506 'qfold':
2508 (fold,
2507 (fold,
2509 [('e', 'edit', None, _('edit patch header')),
2508 [('e', 'edit', None, _('edit patch header')),
2510 ('k', 'keep', None, _('keep folded patch files')),
2509 ('k', 'keep', None, _('keep folded patch files')),
2511 ] + commands.commitopts,
2510 ] + commands.commitopts,
2512 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2511 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2513 'qgoto':
2512 'qgoto':
2514 (goto,
2513 (goto,
2515 [('f', 'force', None, _('overwrite any local changes'))],
2514 [('f', 'force', None, _('overwrite any local changes'))],
2516 _('hg qgoto [OPTION]... PATCH')),
2515 _('hg qgoto [OPTION]... PATCH')),
2517 'qguard':
2516 'qguard':
2518 (guard,
2517 (guard,
2519 [('l', 'list', None, _('list all patches and guards')),
2518 [('l', 'list', None, _('list all patches and guards')),
2520 ('n', 'none', None, _('drop all guards'))],
2519 ('n', 'none', None, _('drop all guards'))],
2521 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2520 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2522 'qheader': (header, [], _('hg qheader [PATCH]')),
2521 'qheader': (header, [], _('hg qheader [PATCH]')),
2523 "^qimport":
2522 "^qimport":
2524 (qimport,
2523 (qimport,
2525 [('e', 'existing', None, _('import file in patch directory')),
2524 [('e', 'existing', None, _('import file in patch directory')),
2526 ('n', 'name', '', _('patch file name')),
2525 ('n', 'name', '', _('patch file name')),
2527 ('f', 'force', None, _('overwrite existing files')),
2526 ('f', 'force', None, _('overwrite existing files')),
2528 ('r', 'rev', [], _('place existing revisions under mq control')),
2527 ('r', 'rev', [], _('place existing revisions under mq control')),
2529 ('g', 'git', None, _('use git extended diff format'))],
2528 ('g', 'git', None, _('use git extended diff format'))],
2530 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2529 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2531 "^qinit":
2530 "^qinit":
2532 (init,
2531 (init,
2533 [('c', 'create-repo', None, _('create queue repository'))],
2532 [('c', 'create-repo', None, _('create queue repository'))],
2534 _('hg qinit [-c]')),
2533 _('hg qinit [-c]')),
2535 "qnew":
2534 "qnew":
2536 (new,
2535 (new,
2537 [('e', 'edit', None, _('edit commit message')),
2536 [('e', 'edit', None, _('edit commit message')),
2538 ('f', 'force', None, _('import uncommitted changes into patch')),
2537 ('f', 'force', None, _('import uncommitted changes into patch')),
2539 ('g', 'git', None, _('use git extended diff format')),
2538 ('g', 'git', None, _('use git extended diff format')),
2540 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2539 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2541 ('u', 'user', '', _('add "From: <given user>" to patch')),
2540 ('u', 'user', '', _('add "From: <given user>" to patch')),
2542 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2541 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2543 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2542 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2544 ] + commands.walkopts + commands.commitopts,
2543 ] + commands.walkopts + commands.commitopts,
2545 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2544 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2546 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2545 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2547 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2546 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2548 "^qpop":
2547 "^qpop":
2549 (pop,
2548 (pop,
2550 [('a', 'all', None, _('pop all patches')),
2549 [('a', 'all', None, _('pop all patches')),
2551 ('n', 'name', '', _('queue name to pop')),
2550 ('n', 'name', '', _('queue name to pop')),
2552 ('f', 'force', None, _('forget any local changes'))],
2551 ('f', 'force', None, _('forget any local changes'))],
2553 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2552 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2554 "^qpush":
2553 "^qpush":
2555 (push,
2554 (push,
2556 [('f', 'force', None, _('apply if the patch has rejects')),
2555 [('f', 'force', None, _('apply if the patch has rejects')),
2557 ('l', 'list', None, _('list patch name in commit text')),
2556 ('l', 'list', None, _('list patch name in commit text')),
2558 ('a', 'all', None, _('apply all patches')),
2557 ('a', 'all', None, _('apply all patches')),
2559 ('m', 'merge', None, _('merge from another queue')),
2558 ('m', 'merge', None, _('merge from another queue')),
2560 ('n', 'name', '', _('merge queue name'))],
2559 ('n', 'name', '', _('merge queue name'))],
2561 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2560 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2562 "^qrefresh":
2561 "^qrefresh":
2563 (refresh,
2562 (refresh,
2564 [('e', 'edit', None, _('edit commit message')),
2563 [('e', 'edit', None, _('edit commit message')),
2565 ('g', 'git', None, _('use git extended diff format')),
2564 ('g', 'git', None, _('use git extended diff format')),
2566 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2565 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2567 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2566 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2568 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2567 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2569 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2568 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2570 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2569 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2571 ] + commands.walkopts + commands.commitopts,
2570 ] + commands.walkopts + commands.commitopts,
2572 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2571 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2573 'qrename|qmv':
2572 'qrename|qmv':
2574 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2573 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2575 "qrestore":
2574 "qrestore":
2576 (restore,
2575 (restore,
2577 [('d', 'delete', None, _('delete save entry')),
2576 [('d', 'delete', None, _('delete save entry')),
2578 ('u', 'update', None, _('update queue working directory'))],
2577 ('u', 'update', None, _('update queue working directory'))],
2579 _('hg qrestore [-d] [-u] REV')),
2578 _('hg qrestore [-d] [-u] REV')),
2580 "qsave":
2579 "qsave":
2581 (save,
2580 (save,
2582 [('c', 'copy', None, _('copy patch directory')),
2581 [('c', 'copy', None, _('copy patch directory')),
2583 ('n', 'name', '', _('copy directory name')),
2582 ('n', 'name', '', _('copy directory name')),
2584 ('e', 'empty', None, _('clear queue status file')),
2583 ('e', 'empty', None, _('clear queue status file')),
2585 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2584 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2586 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2585 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2587 "qselect":
2586 "qselect":
2588 (select,
2587 (select,
2589 [('n', 'none', None, _('disable all guards')),
2588 [('n', 'none', None, _('disable all guards')),
2590 ('s', 'series', None, _('list all guards in series file')),
2589 ('s', 'series', None, _('list all guards in series file')),
2591 ('', 'pop', None, _('pop to before first guarded applied patch')),
2590 ('', 'pop', None, _('pop to before first guarded applied patch')),
2592 ('', 'reapply', None, _('pop, then reapply patches'))],
2591 ('', 'reapply', None, _('pop, then reapply patches'))],
2593 _('hg qselect [OPTION]... [GUARD]...')),
2592 _('hg qselect [OPTION]... [GUARD]...')),
2594 "qseries":
2593 "qseries":
2595 (series,
2594 (series,
2596 [('m', 'missing', None, _('print patches not in series')),
2595 [('m', 'missing', None, _('print patches not in series')),
2597 ] + seriesopts,
2596 ] + seriesopts,
2598 _('hg qseries [-ms]')),
2597 _('hg qseries [-ms]')),
2599 "^strip":
2598 "^strip":
2600 (strip,
2599 (strip,
2601 [('f', 'force', None, _('force removal with local changes')),
2600 [('f', 'force', None, _('force removal with local changes')),
2602 ('b', 'backup', None, _('bundle unrelated changesets')),
2601 ('b', 'backup', None, _('bundle unrelated changesets')),
2603 ('n', 'nobackup', None, _('no backups'))],
2602 ('n', 'nobackup', None, _('no backups'))],
2604 _('hg strip [-f] [-b] [-n] REV')),
2603 _('hg strip [-f] [-b] [-n] REV')),
2605 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2604 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2606 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2605 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2607 "qfinish":
2606 "qfinish":
2608 (finish,
2607 (finish,
2609 [('a', 'applied', None, _('finish all applied changesets'))],
2608 [('a', 'applied', None, _('finish all applied changesets'))],
2610 _('hg qfinish [-a] [REV...]')),
2609 _('hg qfinish [-a] [REV...]')),
2611 }
2610 }
@@ -1,490 +1,489 b''
1 '''sending Mercurial changesets as a series of patch emails
1 '''sending Mercurial changesets as a series of patch emails
2
2
3 The series is started off with a "[PATCH 0 of N]" introduction, which
3 The series is started off with a "[PATCH 0 of N]" introduction, which
4 describes the series as a whole.
4 describes the series as a whole.
5
5
6 Each patch email has a Subject line of "[PATCH M of N] ...", using the
6 Each patch email has a Subject line of "[PATCH M of N] ...", using the
7 first line of the changeset description as the subject text. The
7 first line of the changeset description as the subject text. The
8 message contains two or three body parts:
8 message contains two or three body parts:
9
9
10 The remainder of the changeset description.
10 The remainder of the changeset description.
11
11
12 [Optional] The result of running diffstat on the patch.
12 [Optional] The result of running diffstat on the patch.
13
13
14 The patch itself, as generated by "hg export".
14 The patch itself, as generated by "hg export".
15
15
16 Each message refers to all of its predecessors using the In-Reply-To
16 Each message refers to all of its predecessors using the In-Reply-To
17 and References headers, so they will show up as a sequence in threaded
17 and References headers, so they will show up as a sequence in threaded
18 mail and news readers, and in mail archives.
18 mail and news readers, and in mail archives.
19
19
20 For each changeset, you will be prompted with a diffstat summary and
20 For each changeset, you will be prompted with a diffstat summary and
21 the changeset summary, so you can be sure you are sending the right
21 the changeset summary, so you can be sure you are sending the right
22 changes.
22 changes.
23
23
24 To enable this extension:
24 To enable this extension:
25
25
26 [extensions]
26 [extensions]
27 hgext.patchbomb =
27 hgext.patchbomb =
28
28
29 To configure other defaults, add a section like this to your hgrc
29 To configure other defaults, add a section like this to your hgrc
30 file:
30 file:
31
31
32 [email]
32 [email]
33 from = My Name <my@email>
33 from = My Name <my@email>
34 to = recipient1, recipient2, ...
34 to = recipient1, recipient2, ...
35 cc = cc1, cc2, ...
35 cc = cc1, cc2, ...
36 bcc = bcc1, bcc2, ...
36 bcc = bcc1, bcc2, ...
37
37
38 Then you can use the "hg email" command to mail a series of changesets
38 Then you can use the "hg email" command to mail a series of changesets
39 as a patchbomb.
39 as a patchbomb.
40
40
41 To avoid sending patches prematurely, it is a good idea to first run
41 To avoid sending patches prematurely, it is a good idea to first run
42 the "email" command with the "-n" option (test only). You will be
42 the "email" command with the "-n" option (test only). You will be
43 prompted for an email recipient address, a subject an an introductory
43 prompted for an email recipient address, a subject an an introductory
44 message describing the patches of your patchbomb. Then when all is
44 message describing the patches of your patchbomb. Then when all is
45 done, patchbomb messages are displayed. If PAGER environment variable
45 done, patchbomb messages are displayed. If PAGER environment variable
46 is set, your pager will be fired up once for each patchbomb message,
46 is set, your pager will be fired up once for each patchbomb message,
47 so you can verify everything is alright.
47 so you can verify everything is alright.
48
48
49 The -m/--mbox option is also very useful. Instead of previewing each
49 The -m/--mbox option is also very useful. Instead of previewing each
50 patchbomb message in a pager or sending the messages directly, it will
50 patchbomb message in a pager or sending the messages directly, it will
51 create a UNIX mailbox file with the patch emails. This mailbox file
51 create a UNIX mailbox file with the patch emails. This mailbox file
52 can be previewed with any mail user agent which supports UNIX mbox
52 can be previewed with any mail user agent which supports UNIX mbox
53 files, e.g. with mutt:
53 files, e.g. with mutt:
54
54
55 % mutt -R -f mbox
55 % mutt -R -f mbox
56
56
57 When you are previewing the patchbomb messages, you can use `formail'
57 When you are previewing the patchbomb messages, you can use `formail'
58 (a utility that is commonly installed as part of the procmail
58 (a utility that is commonly installed as part of the procmail
59 package), to send each message out:
59 package), to send each message out:
60
60
61 % formail -s sendmail -bm -t < mbox
61 % formail -s sendmail -bm -t < mbox
62
62
63 That should be all. Now your patchbomb is on its way out.
63 That should be all. Now your patchbomb is on its way out.
64
64
65 You can also either configure the method option in the email section
65 You can also either configure the method option in the email section
66 to be a sendmail compatable mailer or fill out the [smtp] section so
66 to be a sendmail compatable mailer or fill out the [smtp] section so
67 that the patchbomb extension can automatically send patchbombs
67 that the patchbomb extension can automatically send patchbombs
68 directly from the commandline. See the [email] and [smtp] sections in
68 directly from the commandline. See the [email] and [smtp] sections in
69 hgrc(5) for details.'''
69 hgrc(5) for details.'''
70
70
71 import os, errno, socket, tempfile, cStringIO
71 import os, errno, socket, tempfile, cStringIO
72 import email.MIMEMultipart, email.MIMEBase
72 import email.MIMEMultipart, email.MIMEBase
73 import email.Utils, email.Encoders, email.Generator
73 import email.Utils, email.Encoders, email.Generator
74 from mercurial import cmdutil, commands, hg, mail, patch, util
74 from mercurial import cmdutil, commands, hg, mail, patch, util
75 from mercurial.i18n import _
75 from mercurial.i18n import _
76 from mercurial.node import bin
76 from mercurial.node import bin
77
77
78 def prompt(ui, prompt, default=None, rest=': ', empty_ok=False):
78 def prompt(ui, prompt, default=None, rest=': ', empty_ok=False):
79 if not ui.interactive:
79 if not ui.interactive:
80 return default
80 return default
81 if default:
81 if default:
82 prompt += ' [%s]' % default
82 prompt += ' [%s]' % default
83 prompt += rest
83 prompt += rest
84 while True:
84 while True:
85 r = ui.prompt(prompt, default=default)
85 r = ui.prompt(prompt, default=default)
86 if r:
86 if r:
87 return r
87 return r
88 if default is not None:
88 if default is not None:
89 return default
89 return default
90 if empty_ok:
90 if empty_ok:
91 return r
91 return r
92 ui.warn(_('Please enter a valid value.\n'))
92 ui.warn(_('Please enter a valid value.\n'))
93
93
94 def cdiffstat(ui, summary, patchlines):
94 def cdiffstat(ui, summary, patchlines):
95 s = patch.diffstat(patchlines)
95 s = patch.diffstat(patchlines)
96 if summary:
96 if summary:
97 ui.write(summary, '\n')
97 ui.write(summary, '\n')
98 ui.write(s, '\n')
98 ui.write(s, '\n')
99 ans = prompt(ui, _('does the diffstat above look okay? '), 'y')
99 ans = prompt(ui, _('does the diffstat above look okay? '), 'y')
100 if not ans.lower().startswith('y'):
100 if not ans.lower().startswith('y'):
101 raise util.Abort(_('diffstat rejected'))
101 raise util.Abort(_('diffstat rejected'))
102 return s
102 return s
103
103
104 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
104 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
105
105
106 desc = []
106 desc = []
107 node = None
107 node = None
108 body = ''
108 body = ''
109
109
110 for line in patch:
110 for line in patch:
111 if line.startswith('#'):
111 if line.startswith('#'):
112 if line.startswith('# Node ID'):
112 if line.startswith('# Node ID'):
113 node = line.split()[-1]
113 node = line.split()[-1]
114 continue
114 continue
115 if line.startswith('diff -r') or line.startswith('diff --git'):
115 if line.startswith('diff -r') or line.startswith('diff --git'):
116 break
116 break
117 desc.append(line)
117 desc.append(line)
118
118
119 if not patchname and not node:
119 if not patchname and not node:
120 raise ValueError
120 raise ValueError
121
121
122 if opts.get('attach'):
122 if opts.get('attach'):
123 body = ('\n'.join(desc[1:]).strip() or
123 body = ('\n'.join(desc[1:]).strip() or
124 'Patch subject is complete summary.')
124 'Patch subject is complete summary.')
125 body += '\n\n\n'
125 body += '\n\n\n'
126
126
127 if opts.get('plain'):
127 if opts.get('plain'):
128 while patch and patch[0].startswith('# '):
128 while patch and patch[0].startswith('# '):
129 patch.pop(0)
129 patch.pop(0)
130 if patch:
130 if patch:
131 patch.pop(0)
131 patch.pop(0)
132 while patch and not patch[0].strip():
132 while patch and not patch[0].strip():
133 patch.pop(0)
133 patch.pop(0)
134
134
135 if opts.get('diffstat'):
135 if opts.get('diffstat'):
136 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n'
136 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n'
137
137
138 if opts.get('attach') or opts.get('inline'):
138 if opts.get('attach') or opts.get('inline'):
139 msg = email.MIMEMultipart.MIMEMultipart()
139 msg = email.MIMEMultipart.MIMEMultipart()
140 if body:
140 if body:
141 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
141 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
142 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test'))
142 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test'))
143 binnode = bin(node)
143 binnode = bin(node)
144 # if node is mq patch, it will have patch file name as tag
144 # if node is mq patch, it will have patch file name as tag
145 if not patchname:
145 if not patchname:
146 patchtags = [t for t in repo.nodetags(binnode)
146 patchtags = [t for t in repo.nodetags(binnode)
147 if t.endswith('.patch') or t.endswith('.diff')]
147 if t.endswith('.patch') or t.endswith('.diff')]
148 if patchtags:
148 if patchtags:
149 patchname = patchtags[0]
149 patchname = patchtags[0]
150 elif total > 1:
150 elif total > 1:
151 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
151 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
152 binnode, seqno=idx, total=total)
152 binnode, seqno=idx, total=total)
153 else:
153 else:
154 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
154 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
155 disposition = 'inline'
155 disposition = 'inline'
156 if opts.get('attach'):
156 if opts.get('attach'):
157 disposition = 'attachment'
157 disposition = 'attachment'
158 p['Content-Disposition'] = disposition + '; filename=' + patchname
158 p['Content-Disposition'] = disposition + '; filename=' + patchname
159 msg.attach(p)
159 msg.attach(p)
160 else:
160 else:
161 body += '\n'.join(patch)
161 body += '\n'.join(patch)
162 msg = mail.mimetextpatch(body, display=opts.get('test'))
162 msg = mail.mimetextpatch(body, display=opts.get('test'))
163
163
164 subj = desc[0].strip().rstrip('. ')
164 subj = desc[0].strip().rstrip('. ')
165 if total == 1 and not opts.get('intro'):
165 if total == 1 and not opts.get('intro'):
166 subj = '[PATCH] ' + (opts.get('subject') or subj)
166 subj = '[PATCH] ' + (opts.get('subject') or subj)
167 else:
167 else:
168 tlen = len(str(total))
168 tlen = len(str(total))
169 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
169 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
170 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
170 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
171 msg['X-Mercurial-Node'] = node
171 msg['X-Mercurial-Node'] = node
172 return msg, subj
172 return msg, subj
173
173
174 def patchbomb(ui, repo, *revs, **opts):
174 def patchbomb(ui, repo, *revs, **opts):
175 '''send changesets by email
175 '''send changesets by email
176
176
177 By default, diffs are sent in the format generated by hg export,
177 By default, diffs are sent in the format generated by hg export,
178 one per message. The series starts with a "[PATCH 0 of N]"
178 one per message. The series starts with a "[PATCH 0 of N]"
179 introduction, which describes the series as a whole.
179 introduction, which describes the series as a whole.
180
180
181 Each patch email has a Subject line of "[PATCH M of N] ...", using
181 Each patch email has a Subject line of "[PATCH M of N] ...", using
182 the first line of the changeset description as the subject text.
182 the first line of the changeset description as the subject text.
183 The message contains two or three body parts. First, the rest of
183 The message contains two or three body parts. First, the rest of
184 the changeset description. Next, (optionally) if the diffstat
184 the changeset description. Next, (optionally) if the diffstat
185 program is installed, the result of running diffstat on the patch.
185 program is installed, the result of running diffstat on the patch.
186 Finally, the patch itself, as generated by "hg export".
186 Finally, the patch itself, as generated by "hg export".
187
187
188 With -o/--outgoing, emails will be generated for patches not found
188 With -o/--outgoing, emails will be generated for patches not found
189 in the destination repository (or only those which are ancestors
189 in the destination repository (or only those which are ancestors
190 of the specified revisions if any are provided)
190 of the specified revisions if any are provided)
191
191
192 With -b/--bundle, changesets are selected as for --outgoing, but a
192 With -b/--bundle, changesets are selected as for --outgoing, but a
193 single email containing a binary Mercurial bundle as an attachment
193 single email containing a binary Mercurial bundle as an attachment
194 will be sent.
194 will be sent.
195
195
196 Examples:
196 Examples:
197
197
198 hg email -r 3000 # send patch 3000 only
198 hg email -r 3000 # send patch 3000 only
199 hg email -r 3000 -r 3001 # send patches 3000 and 3001
199 hg email -r 3000 -r 3001 # send patches 3000 and 3001
200 hg email -r 3000:3005 # send patches 3000 through 3005
200 hg email -r 3000:3005 # send patches 3000 through 3005
201 hg email 3000 # send patch 3000 (deprecated)
201 hg email 3000 # send patch 3000 (deprecated)
202
202
203 hg email -o # send all patches not in default
203 hg email -o # send all patches not in default
204 hg email -o DEST # send all patches not in DEST
204 hg email -o DEST # send all patches not in DEST
205 hg email -o -r 3000 # send all ancestors of 3000 not in default
205 hg email -o -r 3000 # send all ancestors of 3000 not in default
206 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
206 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
207
207
208 hg email -b # send bundle of all patches not in default
208 hg email -b # send bundle of all patches not in default
209 hg email -b DEST # send bundle of all patches not in DEST
209 hg email -b DEST # send bundle of all patches not in DEST
210 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
210 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
211 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
211 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
212
212
213 Before using this command, you will need to enable email in your
213 Before using this command, you will need to enable email in your
214 hgrc. See the [email] section in hgrc(5) for details.
214 hgrc. See the [email] section in hgrc(5) for details.
215 '''
215 '''
216
216
217 _charsets = mail._charsets(ui)
217 _charsets = mail._charsets(ui)
218
218
219 def outgoing(dest, revs):
219 def outgoing(dest, revs):
220 '''Return the revisions present locally but not in dest'''
220 '''Return the revisions present locally but not in dest'''
221 dest = ui.expandpath(dest or 'default-push', dest or 'default')
221 dest = ui.expandpath(dest or 'default-push', dest or 'default')
222 revs = [repo.lookup(rev) for rev in revs]
222 revs = [repo.lookup(rev) for rev in revs]
223 other = hg.repository(ui, dest)
223 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
224 ui.status(_('comparing with %s\n') % dest)
224 ui.status(_('comparing with %s\n') % dest)
225 o = repo.findoutgoing(other)
225 o = repo.findoutgoing(other)
226 if not o:
226 if not o:
227 ui.status(_("no changes found\n"))
227 ui.status(_("no changes found\n"))
228 return []
228 return []
229 o = repo.changelog.nodesbetween(o, revs or None)[0]
229 o = repo.changelog.nodesbetween(o, revs or None)[0]
230 return [str(repo.changelog.rev(r)) for r in o]
230 return [str(repo.changelog.rev(r)) for r in o]
231
231
232 def getpatches(revs):
232 def getpatches(revs):
233 for r in cmdutil.revrange(repo, revs):
233 for r in cmdutil.revrange(repo, revs):
234 output = cStringIO.StringIO()
234 output = cStringIO.StringIO()
235 patch.export(repo, [r], fp=output,
235 patch.export(repo, [r], fp=output,
236 opts=patch.diffopts(ui, opts))
236 opts=patch.diffopts(ui, opts))
237 yield output.getvalue().split('\n')
237 yield output.getvalue().split('\n')
238
238
239 def getbundle(dest):
239 def getbundle(dest):
240 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
240 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
241 tmpfn = os.path.join(tmpdir, 'bundle')
241 tmpfn = os.path.join(tmpdir, 'bundle')
242 try:
242 try:
243 commands.bundle(ui, repo, tmpfn, dest, **opts)
243 commands.bundle(ui, repo, tmpfn, dest, **opts)
244 return open(tmpfn, 'rb').read()
244 return open(tmpfn, 'rb').read()
245 finally:
245 finally:
246 try:
246 try:
247 os.unlink(tmpfn)
247 os.unlink(tmpfn)
248 except:
248 except:
249 pass
249 pass
250 os.rmdir(tmpdir)
250 os.rmdir(tmpdir)
251
251
252 if not (opts.get('test') or opts.get('mbox')):
252 if not (opts.get('test') or opts.get('mbox')):
253 # really sending
253 # really sending
254 mail.validateconfig(ui)
254 mail.validateconfig(ui)
255
255
256 if not (revs or opts.get('rev')
256 if not (revs or opts.get('rev')
257 or opts.get('outgoing') or opts.get('bundle')
257 or opts.get('outgoing') or opts.get('bundle')
258 or opts.get('patches')):
258 or opts.get('patches')):
259 raise util.Abort(_('specify at least one changeset with -r or -o'))
259 raise util.Abort(_('specify at least one changeset with -r or -o'))
260
260
261 cmdutil.setremoteconfig(ui, opts)
262 if opts.get('outgoing') and opts.get('bundle'):
261 if opts.get('outgoing') and opts.get('bundle'):
263 raise util.Abort(_("--outgoing mode always on with --bundle;"
262 raise util.Abort(_("--outgoing mode always on with --bundle;"
264 " do not re-specify --outgoing"))
263 " do not re-specify --outgoing"))
265
264
266 if opts.get('outgoing') or opts.get('bundle'):
265 if opts.get('outgoing') or opts.get('bundle'):
267 if len(revs) > 1:
266 if len(revs) > 1:
268 raise util.Abort(_("too many destinations"))
267 raise util.Abort(_("too many destinations"))
269 dest = revs and revs[0] or None
268 dest = revs and revs[0] or None
270 revs = []
269 revs = []
271
270
272 if opts.get('rev'):
271 if opts.get('rev'):
273 if revs:
272 if revs:
274 raise util.Abort(_('use only one form to specify the revision'))
273 raise util.Abort(_('use only one form to specify the revision'))
275 revs = opts.get('rev')
274 revs = opts.get('rev')
276
275
277 if opts.get('outgoing'):
276 if opts.get('outgoing'):
278 revs = outgoing(dest, opts.get('rev'))
277 revs = outgoing(dest, opts.get('rev'))
279 if opts.get('bundle'):
278 if opts.get('bundle'):
280 opts['revs'] = revs
279 opts['revs'] = revs
281
280
282 # start
281 # start
283 if opts.get('date'):
282 if opts.get('date'):
284 start_time = util.parsedate(opts.get('date'))
283 start_time = util.parsedate(opts.get('date'))
285 else:
284 else:
286 start_time = util.makedate()
285 start_time = util.makedate()
287
286
288 def genmsgid(id):
287 def genmsgid(id):
289 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
288 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
290
289
291 def getdescription(body, sender):
290 def getdescription(body, sender):
292 if opts.get('desc'):
291 if opts.get('desc'):
293 body = open(opts.get('desc')).read()
292 body = open(opts.get('desc')).read()
294 else:
293 else:
295 ui.write(_('\nWrite the introductory message for the '
294 ui.write(_('\nWrite the introductory message for the '
296 'patch series.\n\n'))
295 'patch series.\n\n'))
297 body = ui.edit(body, sender)
296 body = ui.edit(body, sender)
298 return body
297 return body
299
298
300 def getpatchmsgs(patches, patchnames=None):
299 def getpatchmsgs(patches, patchnames=None):
301 jumbo = []
300 jumbo = []
302 msgs = []
301 msgs = []
303
302
304 ui.write(_('This patch series consists of %d patches.\n\n')
303 ui.write(_('This patch series consists of %d patches.\n\n')
305 % len(patches))
304 % len(patches))
306
305
307 name = None
306 name = None
308 for i, p in enumerate(patches):
307 for i, p in enumerate(patches):
309 jumbo.extend(p)
308 jumbo.extend(p)
310 if patchnames:
309 if patchnames:
311 name = patchnames[i]
310 name = patchnames[i]
312 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
311 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
313 len(patches), name)
312 len(patches), name)
314 msgs.append(msg)
313 msgs.append(msg)
315
314
316 if len(patches) > 1 or opts.get('intro'):
315 if len(patches) > 1 or opts.get('intro'):
317 tlen = len(str(len(patches)))
316 tlen = len(str(len(patches)))
318
317
319 subj = '[PATCH %0*d of %d] %s' % (
318 subj = '[PATCH %0*d of %d] %s' % (
320 tlen, 0, len(patches),
319 tlen, 0, len(patches),
321 opts.get('subject') or
320 opts.get('subject') or
322 prompt(ui, 'Subject:',
321 prompt(ui, 'Subject:',
323 rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches))))
322 rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches))))
324
323
325 body = ''
324 body = ''
326 if opts.get('diffstat'):
325 if opts.get('diffstat'):
327 d = cdiffstat(ui, _('Final summary:\n'), jumbo)
326 d = cdiffstat(ui, _('Final summary:\n'), jumbo)
328 if d:
327 if d:
329 body = '\n' + d
328 body = '\n' + d
330
329
331 body = getdescription(body, sender)
330 body = getdescription(body, sender)
332 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
331 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
333 msg['Subject'] = mail.headencode(ui, subj, _charsets,
332 msg['Subject'] = mail.headencode(ui, subj, _charsets,
334 opts.get('test'))
333 opts.get('test'))
335
334
336 msgs.insert(0, (msg, subj))
335 msgs.insert(0, (msg, subj))
337 return msgs
336 return msgs
338
337
339 def getbundlemsgs(bundle):
338 def getbundlemsgs(bundle):
340 subj = (opts.get('subject')
339 subj = (opts.get('subject')
341 or prompt(ui, 'Subject:', 'A bundle for your repository'))
340 or prompt(ui, 'Subject:', 'A bundle for your repository'))
342
341
343 body = getdescription('', sender)
342 body = getdescription('', sender)
344 msg = email.MIMEMultipart.MIMEMultipart()
343 msg = email.MIMEMultipart.MIMEMultipart()
345 if body:
344 if body:
346 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
345 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
347 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
346 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
348 datapart.set_payload(bundle)
347 datapart.set_payload(bundle)
349 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
348 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
350 datapart.add_header('Content-Disposition', 'attachment',
349 datapart.add_header('Content-Disposition', 'attachment',
351 filename=bundlename)
350 filename=bundlename)
352 email.Encoders.encode_base64(datapart)
351 email.Encoders.encode_base64(datapart)
353 msg.attach(datapart)
352 msg.attach(datapart)
354 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
353 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
355 return [(msg, subj)]
354 return [(msg, subj)]
356
355
357 sender = (opts.get('from') or ui.config('email', 'from') or
356 sender = (opts.get('from') or ui.config('email', 'from') or
358 ui.config('patchbomb', 'from') or
357 ui.config('patchbomb', 'from') or
359 prompt(ui, 'From', ui.username()))
358 prompt(ui, 'From', ui.username()))
360
359
361 # internal option used by pbranches
360 # internal option used by pbranches
362 patches = opts.get('patches')
361 patches = opts.get('patches')
363 if patches:
362 if patches:
364 msgs = getpatchmsgs(patches, opts.get('patchnames'))
363 msgs = getpatchmsgs(patches, opts.get('patchnames'))
365 elif opts.get('bundle'):
364 elif opts.get('bundle'):
366 msgs = getbundlemsgs(getbundle(dest))
365 msgs = getbundlemsgs(getbundle(dest))
367 else:
366 else:
368 msgs = getpatchmsgs(list(getpatches(revs)))
367 msgs = getpatchmsgs(list(getpatches(revs)))
369
368
370 def getaddrs(opt, prpt, default = None):
369 def getaddrs(opt, prpt, default = None):
371 addrs = opts.get(opt) or (ui.config('email', opt) or
370 addrs = opts.get(opt) or (ui.config('email', opt) or
372 ui.config('patchbomb', opt) or
371 ui.config('patchbomb', opt) or
373 prompt(ui, prpt, default)).split(',')
372 prompt(ui, prpt, default)).split(',')
374 return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
373 return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
375 for a in addrs if a.strip()]
374 for a in addrs if a.strip()]
376
375
377 to = getaddrs('to', 'To')
376 to = getaddrs('to', 'To')
378 cc = getaddrs('cc', 'Cc', '')
377 cc = getaddrs('cc', 'Cc', '')
379
378
380 bcc = opts.get('bcc') or (ui.config('email', 'bcc') or
379 bcc = opts.get('bcc') or (ui.config('email', 'bcc') or
381 ui.config('patchbomb', 'bcc') or '').split(',')
380 ui.config('patchbomb', 'bcc') or '').split(',')
382 bcc = [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
381 bcc = [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
383 for a in bcc if a.strip()]
382 for a in bcc if a.strip()]
384
383
385 ui.write('\n')
384 ui.write('\n')
386
385
387 parent = opts.get('in_reply_to') or None
386 parent = opts.get('in_reply_to') or None
388
387
389 sender_addr = email.Utils.parseaddr(sender)[1]
388 sender_addr = email.Utils.parseaddr(sender)[1]
390 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
389 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
391 sendmail = None
390 sendmail = None
392 for m, subj in msgs:
391 for m, subj in msgs:
393 try:
392 try:
394 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
393 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
395 except TypeError:
394 except TypeError:
396 m['Message-Id'] = genmsgid('patchbomb')
395 m['Message-Id'] = genmsgid('patchbomb')
397 if parent:
396 if parent:
398 m['In-Reply-To'] = parent
397 m['In-Reply-To'] = parent
399 m['References'] = parent
398 m['References'] = parent
400 else:
399 else:
401 parent = m['Message-Id']
400 parent = m['Message-Id']
402 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
401 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
403 m['Date'] = util.datestr(start_time, "%a, %d %b %Y %H:%M:%S %1%2")
402 m['Date'] = util.datestr(start_time, "%a, %d %b %Y %H:%M:%S %1%2")
404
403
405 start_time = (start_time[0] + 1, start_time[1])
404 start_time = (start_time[0] + 1, start_time[1])
406 m['From'] = sender
405 m['From'] = sender
407 m['To'] = ', '.join(to)
406 m['To'] = ', '.join(to)
408 if cc:
407 if cc:
409 m['Cc'] = ', '.join(cc)
408 m['Cc'] = ', '.join(cc)
410 if bcc:
409 if bcc:
411 m['Bcc'] = ', '.join(bcc)
410 m['Bcc'] = ', '.join(bcc)
412 if opts.get('test'):
411 if opts.get('test'):
413 ui.status(_('Displaying '), subj, ' ...\n')
412 ui.status(_('Displaying '), subj, ' ...\n')
414 ui.flush()
413 ui.flush()
415 if 'PAGER' in os.environ:
414 if 'PAGER' in os.environ:
416 fp = util.popen(os.environ['PAGER'], 'w')
415 fp = util.popen(os.environ['PAGER'], 'w')
417 else:
416 else:
418 fp = ui
417 fp = ui
419 generator = email.Generator.Generator(fp, mangle_from_=False)
418 generator = email.Generator.Generator(fp, mangle_from_=False)
420 try:
419 try:
421 generator.flatten(m, 0)
420 generator.flatten(m, 0)
422 fp.write('\n')
421 fp.write('\n')
423 except IOError, inst:
422 except IOError, inst:
424 if inst.errno != errno.EPIPE:
423 if inst.errno != errno.EPIPE:
425 raise
424 raise
426 if fp is not ui:
425 if fp is not ui:
427 fp.close()
426 fp.close()
428 elif opts.get('mbox'):
427 elif opts.get('mbox'):
429 ui.status(_('Writing '), subj, ' ...\n')
428 ui.status(_('Writing '), subj, ' ...\n')
430 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
429 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
431 generator = email.Generator.Generator(fp, mangle_from_=True)
430 generator = email.Generator.Generator(fp, mangle_from_=True)
432 date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
431 date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
433 fp.write('From %s %s\n' % (sender_addr, date))
432 fp.write('From %s %s\n' % (sender_addr, date))
434 generator.flatten(m, 0)
433 generator.flatten(m, 0)
435 fp.write('\n\n')
434 fp.write('\n\n')
436 fp.close()
435 fp.close()
437 else:
436 else:
438 if not sendmail:
437 if not sendmail:
439 sendmail = mail.connect(ui)
438 sendmail = mail.connect(ui)
440 ui.status(_('Sending '), subj, ' ...\n')
439 ui.status(_('Sending '), subj, ' ...\n')
441 # Exim does not remove the Bcc field
440 # Exim does not remove the Bcc field
442 del m['Bcc']
441 del m['Bcc']
443 fp = cStringIO.StringIO()
442 fp = cStringIO.StringIO()
444 generator = email.Generator.Generator(fp, mangle_from_=False)
443 generator = email.Generator.Generator(fp, mangle_from_=False)
445 generator.flatten(m, 0)
444 generator.flatten(m, 0)
446 sendmail(sender, to + bcc + cc, fp.getvalue())
445 sendmail(sender, to + bcc + cc, fp.getvalue())
447
446
448 emailopts = [
447 emailopts = [
449 ('a', 'attach', None, _('send patches as attachments')),
448 ('a', 'attach', None, _('send patches as attachments')),
450 ('i', 'inline', None, _('send patches as inline attachments')),
449 ('i', 'inline', None, _('send patches as inline attachments')),
451 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
450 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
452 ('c', 'cc', [], _('email addresses of copy recipients')),
451 ('c', 'cc', [], _('email addresses of copy recipients')),
453 ('d', 'diffstat', None, _('add diffstat output to messages')),
452 ('d', 'diffstat', None, _('add diffstat output to messages')),
454 ('', 'date', '', _('use the given date as the sending date')),
453 ('', 'date', '', _('use the given date as the sending date')),
455 ('', 'desc', '', _('use the given file as the series description')),
454 ('', 'desc', '', _('use the given file as the series description')),
456 ('f', 'from', '', _('email address of sender')),
455 ('f', 'from', '', _('email address of sender')),
457 ('n', 'test', None, _('print messages that would be sent')),
456 ('n', 'test', None, _('print messages that would be sent')),
458 ('m', 'mbox', '',
457 ('m', 'mbox', '',
459 _('write messages to mbox file instead of sending them')),
458 _('write messages to mbox file instead of sending them')),
460 ('s', 'subject', '',
459 ('s', 'subject', '',
461 _('subject of first message (intro or single patch)')),
460 _('subject of first message (intro or single patch)')),
462 ('', 'in-reply-to', '',
461 ('', 'in-reply-to', '',
463 _('"message identifier to reply to"')),
462 _('"message identifier to reply to"')),
464 ('t', 'to', [], _('email addresses of recipients')),
463 ('t', 'to', [], _('email addresses of recipients')),
465 ]
464 ]
466
465
467
466
468 cmdtable = {
467 cmdtable = {
469 "email":
468 "email":
470 (patchbomb,
469 (patchbomb,
471 [('g', 'git', None, _('use git extended diff format')),
470 [('g', 'git', None, _('use git extended diff format')),
472 ('', 'plain', None, _('omit hg patch header')),
471 ('', 'plain', None, _('omit hg patch header')),
473 ('o', 'outgoing', None,
472 ('o', 'outgoing', None,
474 _('send changes not found in the target repository')),
473 _('send changes not found in the target repository')),
475 ('b', 'bundle', None,
474 ('b', 'bundle', None,
476 _('send changes not in target as a binary bundle')),
475 _('send changes not in target as a binary bundle')),
477 ('', 'bundlename', 'bundle',
476 ('', 'bundlename', 'bundle',
478 _('file name of the bundle attachment')),
477 _('file name of the bundle attachment')),
479 ('r', 'rev', [], _('a revision to send')),
478 ('r', 'rev', [], _('a revision to send')),
480 ('', 'force', None,
479 ('', 'force', None,
481 _('run even when remote repository is unrelated '
480 _('run even when remote repository is unrelated '
482 '(with -b/--bundle)')),
481 '(with -b/--bundle)')),
483 ('', 'base', [],
482 ('', 'base', [],
484 _('a base changeset to specify instead of a destination '
483 _('a base changeset to specify instead of a destination '
485 '(with -b/--bundle)')),
484 '(with -b/--bundle)')),
486 ('', 'intro', None,
485 ('', 'intro', None,
487 _('send an introduction email for a single patch')),
486 _('send an introduction email for a single patch')),
488 ] + emailopts + commands.remoteopts,
487 ] + emailopts + commands.remoteopts,
489 _('hg email [OPTION]... [DEST]...'))
488 _('hg email [OPTION]... [DEST]...'))
490 }
489 }
@@ -1,1213 +1,1226 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat, encoding
10 import os, sys, bisect, stat, encoding
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno, error
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno, error
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def findpossible(cmd, table, strict=False):
16 def findpossible(cmd, table, strict=False):
17 """
17 """
18 Return cmd -> (aliases, command table entry)
18 Return cmd -> (aliases, command table entry)
19 for each matching command.
19 for each matching command.
20 Return debug commands (or their aliases) only if no normal command matches.
20 Return debug commands (or their aliases) only if no normal command matches.
21 """
21 """
22 choice = {}
22 choice = {}
23 debugchoice = {}
23 debugchoice = {}
24 for e in table.keys():
24 for e in table.keys():
25 aliases = e.lstrip("^").split("|")
25 aliases = e.lstrip("^").split("|")
26 found = None
26 found = None
27 if cmd in aliases:
27 if cmd in aliases:
28 found = cmd
28 found = cmd
29 elif not strict:
29 elif not strict:
30 for a in aliases:
30 for a in aliases:
31 if a.startswith(cmd):
31 if a.startswith(cmd):
32 found = a
32 found = a
33 break
33 break
34 if found is not None:
34 if found is not None:
35 if aliases[0].startswith("debug") or found.startswith("debug"):
35 if aliases[0].startswith("debug") or found.startswith("debug"):
36 debugchoice[found] = (aliases, table[e])
36 debugchoice[found] = (aliases, table[e])
37 else:
37 else:
38 choice[found] = (aliases, table[e])
38 choice[found] = (aliases, table[e])
39
39
40 if not choice and debugchoice:
40 if not choice and debugchoice:
41 choice = debugchoice
41 choice = debugchoice
42
42
43 return choice
43 return choice
44
44
45 def findcmd(cmd, table, strict=True):
45 def findcmd(cmd, table, strict=True):
46 """Return (aliases, command table entry) for command string."""
46 """Return (aliases, command table entry) for command string."""
47 choice = findpossible(cmd, table, strict)
47 choice = findpossible(cmd, table, strict)
48
48
49 if cmd in choice:
49 if cmd in choice:
50 return choice[cmd]
50 return choice[cmd]
51
51
52 if len(choice) > 1:
52 if len(choice) > 1:
53 clist = choice.keys()
53 clist = choice.keys()
54 clist.sort()
54 clist.sort()
55 raise error.AmbiguousCommand(cmd, clist)
55 raise error.AmbiguousCommand(cmd, clist)
56
56
57 if choice:
57 if choice:
58 return choice.values()[0]
58 return choice.values()[0]
59
59
60 raise error.UnknownCommand(cmd)
60 raise error.UnknownCommand(cmd)
61
61
62 def bail_if_changed(repo):
62 def bail_if_changed(repo):
63 if repo.dirstate.parents()[1] != nullid:
63 if repo.dirstate.parents()[1] != nullid:
64 raise util.Abort(_('outstanding uncommitted merge'))
64 raise util.Abort(_('outstanding uncommitted merge'))
65 modified, added, removed, deleted = repo.status()[:4]
65 modified, added, removed, deleted = repo.status()[:4]
66 if modified or added or removed or deleted:
66 if modified or added or removed or deleted:
67 raise util.Abort(_("outstanding uncommitted changes"))
67 raise util.Abort(_("outstanding uncommitted changes"))
68
68
69 def logmessage(opts):
69 def logmessage(opts):
70 """ get the log message according to -m and -l option """
70 """ get the log message according to -m and -l option """
71 message = opts.get('message')
71 message = opts.get('message')
72 logfile = opts.get('logfile')
72 logfile = opts.get('logfile')
73
73
74 if message and logfile:
74 if message and logfile:
75 raise util.Abort(_('options --message and --logfile are mutually '
75 raise util.Abort(_('options --message and --logfile are mutually '
76 'exclusive'))
76 'exclusive'))
77 if not message and logfile:
77 if not message and logfile:
78 try:
78 try:
79 if logfile == '-':
79 if logfile == '-':
80 message = sys.stdin.read()
80 message = sys.stdin.read()
81 else:
81 else:
82 message = open(logfile).read()
82 message = open(logfile).read()
83 except IOError, inst:
83 except IOError, inst:
84 raise util.Abort(_("can't read commit message '%s': %s") %
84 raise util.Abort(_("can't read commit message '%s': %s") %
85 (logfile, inst.strerror))
85 (logfile, inst.strerror))
86 return message
86 return message
87
87
88 def loglimit(opts):
88 def loglimit(opts):
89 """get the log limit according to option -l/--limit"""
89 """get the log limit according to option -l/--limit"""
90 limit = opts.get('limit')
90 limit = opts.get('limit')
91 if limit:
91 if limit:
92 try:
92 try:
93 limit = int(limit)
93 limit = int(limit)
94 except ValueError:
94 except ValueError:
95 raise util.Abort(_('limit must be a positive integer'))
95 raise util.Abort(_('limit must be a positive integer'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
97 else:
97 else:
98 limit = sys.maxint
98 limit = sys.maxint
99 return limit
99 return limit
100
100
101 def setremoteconfig(ui, opts):
101 def remoteui(src, opts):
102 "copy remote options to ui tree"
102 'build a remote ui from ui or repo and opts'
103 if opts.get('ssh'):
103 if hasattr(src, 'ui'): # looks like a repository
104 ui.setconfig("ui", "ssh", opts['ssh'])
104 dst = src.ui.parentui # drop repo-specific config
105 if opts.get('remotecmd'):
105 src = src.ui # copy target options from repo
106 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
106 else: # assume it's a ui object
107 dst = src # keep all global options
108
109 # copy ssh-specific options
110 for o in 'ssh', 'remotecmd':
111 v = opts.get(o) or src.config('ui', o)
112 if v:
113 dst.setconfig("ui", o, v)
114 # copy bundle-specific options
115 r = src.config('bundle', 'mainreporoot')
116 if r:
117 dst.setconfig('bundle', 'mainreporoot', r)
118
119 return dst
107
120
108 def revpair(repo, revs):
121 def revpair(repo, revs):
109 '''return pair of nodes, given list of revisions. second item can
122 '''return pair of nodes, given list of revisions. second item can
110 be None, meaning use working dir.'''
123 be None, meaning use working dir.'''
111
124
112 def revfix(repo, val, defval):
125 def revfix(repo, val, defval):
113 if not val and val != 0 and defval is not None:
126 if not val and val != 0 and defval is not None:
114 val = defval
127 val = defval
115 return repo.lookup(val)
128 return repo.lookup(val)
116
129
117 if not revs:
130 if not revs:
118 return repo.dirstate.parents()[0], None
131 return repo.dirstate.parents()[0], None
119 end = None
132 end = None
120 if len(revs) == 1:
133 if len(revs) == 1:
121 if revrangesep in revs[0]:
134 if revrangesep in revs[0]:
122 start, end = revs[0].split(revrangesep, 1)
135 start, end = revs[0].split(revrangesep, 1)
123 start = revfix(repo, start, 0)
136 start = revfix(repo, start, 0)
124 end = revfix(repo, end, len(repo) - 1)
137 end = revfix(repo, end, len(repo) - 1)
125 else:
138 else:
126 start = revfix(repo, revs[0], None)
139 start = revfix(repo, revs[0], None)
127 elif len(revs) == 2:
140 elif len(revs) == 2:
128 if revrangesep in revs[0] or revrangesep in revs[1]:
141 if revrangesep in revs[0] or revrangesep in revs[1]:
129 raise util.Abort(_('too many revisions specified'))
142 raise util.Abort(_('too many revisions specified'))
130 start = revfix(repo, revs[0], None)
143 start = revfix(repo, revs[0], None)
131 end = revfix(repo, revs[1], None)
144 end = revfix(repo, revs[1], None)
132 else:
145 else:
133 raise util.Abort(_('too many revisions specified'))
146 raise util.Abort(_('too many revisions specified'))
134 return start, end
147 return start, end
135
148
136 def revrange(repo, revs):
149 def revrange(repo, revs):
137 """Yield revision as strings from a list of revision specifications."""
150 """Yield revision as strings from a list of revision specifications."""
138
151
139 def revfix(repo, val, defval):
152 def revfix(repo, val, defval):
140 if not val and val != 0 and defval is not None:
153 if not val and val != 0 and defval is not None:
141 return defval
154 return defval
142 return repo.changelog.rev(repo.lookup(val))
155 return repo.changelog.rev(repo.lookup(val))
143
156
144 seen, l = {}, []
157 seen, l = {}, []
145 for spec in revs:
158 for spec in revs:
146 if revrangesep in spec:
159 if revrangesep in spec:
147 start, end = spec.split(revrangesep, 1)
160 start, end = spec.split(revrangesep, 1)
148 start = revfix(repo, start, 0)
161 start = revfix(repo, start, 0)
149 end = revfix(repo, end, len(repo) - 1)
162 end = revfix(repo, end, len(repo) - 1)
150 step = start > end and -1 or 1
163 step = start > end and -1 or 1
151 for rev in xrange(start, end+step, step):
164 for rev in xrange(start, end+step, step):
152 if rev in seen:
165 if rev in seen:
153 continue
166 continue
154 seen[rev] = 1
167 seen[rev] = 1
155 l.append(rev)
168 l.append(rev)
156 else:
169 else:
157 rev = revfix(repo, spec, None)
170 rev = revfix(repo, spec, None)
158 if rev in seen:
171 if rev in seen:
159 continue
172 continue
160 seen[rev] = 1
173 seen[rev] = 1
161 l.append(rev)
174 l.append(rev)
162
175
163 return l
176 return l
164
177
165 def make_filename(repo, pat, node,
178 def make_filename(repo, pat, node,
166 total=None, seqno=None, revwidth=None, pathname=None):
179 total=None, seqno=None, revwidth=None, pathname=None):
167 node_expander = {
180 node_expander = {
168 'H': lambda: hex(node),
181 'H': lambda: hex(node),
169 'R': lambda: str(repo.changelog.rev(node)),
182 'R': lambda: str(repo.changelog.rev(node)),
170 'h': lambda: short(node),
183 'h': lambda: short(node),
171 }
184 }
172 expander = {
185 expander = {
173 '%': lambda: '%',
186 '%': lambda: '%',
174 'b': lambda: os.path.basename(repo.root),
187 'b': lambda: os.path.basename(repo.root),
175 }
188 }
176
189
177 try:
190 try:
178 if node:
191 if node:
179 expander.update(node_expander)
192 expander.update(node_expander)
180 if node:
193 if node:
181 expander['r'] = (lambda:
194 expander['r'] = (lambda:
182 str(repo.changelog.rev(node)).zfill(revwidth or 0))
195 str(repo.changelog.rev(node)).zfill(revwidth or 0))
183 if total is not None:
196 if total is not None:
184 expander['N'] = lambda: str(total)
197 expander['N'] = lambda: str(total)
185 if seqno is not None:
198 if seqno is not None:
186 expander['n'] = lambda: str(seqno)
199 expander['n'] = lambda: str(seqno)
187 if total is not None and seqno is not None:
200 if total is not None and seqno is not None:
188 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
201 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
189 if pathname is not None:
202 if pathname is not None:
190 expander['s'] = lambda: os.path.basename(pathname)
203 expander['s'] = lambda: os.path.basename(pathname)
191 expander['d'] = lambda: os.path.dirname(pathname) or '.'
204 expander['d'] = lambda: os.path.dirname(pathname) or '.'
192 expander['p'] = lambda: pathname
205 expander['p'] = lambda: pathname
193
206
194 newname = []
207 newname = []
195 patlen = len(pat)
208 patlen = len(pat)
196 i = 0
209 i = 0
197 while i < patlen:
210 while i < patlen:
198 c = pat[i]
211 c = pat[i]
199 if c == '%':
212 if c == '%':
200 i += 1
213 i += 1
201 c = pat[i]
214 c = pat[i]
202 c = expander[c]()
215 c = expander[c]()
203 newname.append(c)
216 newname.append(c)
204 i += 1
217 i += 1
205 return ''.join(newname)
218 return ''.join(newname)
206 except KeyError, inst:
219 except KeyError, inst:
207 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
220 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
208 inst.args[0])
221 inst.args[0])
209
222
210 def make_file(repo, pat, node=None,
223 def make_file(repo, pat, node=None,
211 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
224 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
212
225
213 writable = 'w' in mode or 'a' in mode
226 writable = 'w' in mode or 'a' in mode
214
227
215 if not pat or pat == '-':
228 if not pat or pat == '-':
216 return writable and sys.stdout or sys.stdin
229 return writable and sys.stdout or sys.stdin
217 if hasattr(pat, 'write') and writable:
230 if hasattr(pat, 'write') and writable:
218 return pat
231 return pat
219 if hasattr(pat, 'read') and 'r' in mode:
232 if hasattr(pat, 'read') and 'r' in mode:
220 return pat
233 return pat
221 return open(make_filename(repo, pat, node, total, seqno, revwidth,
234 return open(make_filename(repo, pat, node, total, seqno, revwidth,
222 pathname),
235 pathname),
223 mode)
236 mode)
224
237
225 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
238 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
226 if not globbed and default == 'relpath':
239 if not globbed and default == 'relpath':
227 pats = util.expand_glob(pats or [])
240 pats = util.expand_glob(pats or [])
228 m = _match.match(repo.root, repo.getcwd(), pats,
241 m = _match.match(repo.root, repo.getcwd(), pats,
229 opts.get('include'), opts.get('exclude'), default)
242 opts.get('include'), opts.get('exclude'), default)
230 def badfn(f, msg):
243 def badfn(f, msg):
231 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
244 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
232 return False
245 return False
233 m.bad = badfn
246 m.bad = badfn
234 return m
247 return m
235
248
236 def matchall(repo):
249 def matchall(repo):
237 return _match.always(repo.root, repo.getcwd())
250 return _match.always(repo.root, repo.getcwd())
238
251
239 def matchfiles(repo, files):
252 def matchfiles(repo, files):
240 return _match.exact(repo.root, repo.getcwd(), files)
253 return _match.exact(repo.root, repo.getcwd(), files)
241
254
242 def findrenames(repo, added=None, removed=None, threshold=0.5):
255 def findrenames(repo, added=None, removed=None, threshold=0.5):
243 '''find renamed files -- yields (before, after, score) tuples'''
256 '''find renamed files -- yields (before, after, score) tuples'''
244 if added is None or removed is None:
257 if added is None or removed is None:
245 added, removed = repo.status()[1:3]
258 added, removed = repo.status()[1:3]
246 ctx = repo['.']
259 ctx = repo['.']
247 for a in added:
260 for a in added:
248 aa = repo.wread(a)
261 aa = repo.wread(a)
249 bestname, bestscore = None, threshold
262 bestname, bestscore = None, threshold
250 for r in removed:
263 for r in removed:
251 rr = ctx.filectx(r).data()
264 rr = ctx.filectx(r).data()
252
265
253 # bdiff.blocks() returns blocks of matching lines
266 # bdiff.blocks() returns blocks of matching lines
254 # count the number of bytes in each
267 # count the number of bytes in each
255 equal = 0
268 equal = 0
256 alines = mdiff.splitnewlines(aa)
269 alines = mdiff.splitnewlines(aa)
257 matches = bdiff.blocks(aa, rr)
270 matches = bdiff.blocks(aa, rr)
258 for x1,x2,y1,y2 in matches:
271 for x1,x2,y1,y2 in matches:
259 for line in alines[x1:x2]:
272 for line in alines[x1:x2]:
260 equal += len(line)
273 equal += len(line)
261
274
262 lengths = len(aa) + len(rr)
275 lengths = len(aa) + len(rr)
263 if lengths:
276 if lengths:
264 myscore = equal*2.0 / lengths
277 myscore = equal*2.0 / lengths
265 if myscore >= bestscore:
278 if myscore >= bestscore:
266 bestname, bestscore = r, myscore
279 bestname, bestscore = r, myscore
267 if bestname:
280 if bestname:
268 yield bestname, a, bestscore
281 yield bestname, a, bestscore
269
282
270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
283 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
271 if dry_run is None:
284 if dry_run is None:
272 dry_run = opts.get('dry_run')
285 dry_run = opts.get('dry_run')
273 if similarity is None:
286 if similarity is None:
274 similarity = float(opts.get('similarity') or 0)
287 similarity = float(opts.get('similarity') or 0)
275 add, remove = [], []
288 add, remove = [], []
276 mapping = {}
289 mapping = {}
277 audit_path = util.path_auditor(repo.root)
290 audit_path = util.path_auditor(repo.root)
278 m = match(repo, pats, opts)
291 m = match(repo, pats, opts)
279 for abs in repo.walk(m):
292 for abs in repo.walk(m):
280 target = repo.wjoin(abs)
293 target = repo.wjoin(abs)
281 good = True
294 good = True
282 try:
295 try:
283 audit_path(abs)
296 audit_path(abs)
284 except:
297 except:
285 good = False
298 good = False
286 rel = m.rel(abs)
299 rel = m.rel(abs)
287 exact = m.exact(abs)
300 exact = m.exact(abs)
288 if good and abs not in repo.dirstate:
301 if good and abs not in repo.dirstate:
289 add.append(abs)
302 add.append(abs)
290 mapping[abs] = rel, m.exact(abs)
303 mapping[abs] = rel, m.exact(abs)
291 if repo.ui.verbose or not exact:
304 if repo.ui.verbose or not exact:
292 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
305 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
293 if repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
306 if repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
294 or (os.path.isdir(target) and not os.path.islink(target))):
307 or (os.path.isdir(target) and not os.path.islink(target))):
295 remove.append(abs)
308 remove.append(abs)
296 mapping[abs] = rel, exact
309 mapping[abs] = rel, exact
297 if repo.ui.verbose or not exact:
310 if repo.ui.verbose or not exact:
298 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
311 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
299 if not dry_run:
312 if not dry_run:
300 repo.remove(remove)
313 repo.remove(remove)
301 repo.add(add)
314 repo.add(add)
302 if similarity > 0:
315 if similarity > 0:
303 for old, new, score in findrenames(repo, add, remove, similarity):
316 for old, new, score in findrenames(repo, add, remove, similarity):
304 oldrel, oldexact = mapping[old]
317 oldrel, oldexact = mapping[old]
305 newrel, newexact = mapping[new]
318 newrel, newexact = mapping[new]
306 if repo.ui.verbose or not oldexact or not newexact:
319 if repo.ui.verbose or not oldexact or not newexact:
307 repo.ui.status(_('recording removal of %s as rename to %s '
320 repo.ui.status(_('recording removal of %s as rename to %s '
308 '(%d%% similar)\n') %
321 '(%d%% similar)\n') %
309 (oldrel, newrel, score * 100))
322 (oldrel, newrel, score * 100))
310 if not dry_run:
323 if not dry_run:
311 repo.copy(old, new)
324 repo.copy(old, new)
312
325
313 def copy(ui, repo, pats, opts, rename=False):
326 def copy(ui, repo, pats, opts, rename=False):
314 # called with the repo lock held
327 # called with the repo lock held
315 #
328 #
316 # hgsep => pathname that uses "/" to separate directories
329 # hgsep => pathname that uses "/" to separate directories
317 # ossep => pathname that uses os.sep to separate directories
330 # ossep => pathname that uses os.sep to separate directories
318 cwd = repo.getcwd()
331 cwd = repo.getcwd()
319 targets = {}
332 targets = {}
320 after = opts.get("after")
333 after = opts.get("after")
321 dryrun = opts.get("dry_run")
334 dryrun = opts.get("dry_run")
322
335
323 def walkpat(pat):
336 def walkpat(pat):
324 srcs = []
337 srcs = []
325 m = match(repo, [pat], opts, globbed=True)
338 m = match(repo, [pat], opts, globbed=True)
326 for abs in repo.walk(m):
339 for abs in repo.walk(m):
327 state = repo.dirstate[abs]
340 state = repo.dirstate[abs]
328 rel = m.rel(abs)
341 rel = m.rel(abs)
329 exact = m.exact(abs)
342 exact = m.exact(abs)
330 if state in '?r':
343 if state in '?r':
331 if exact and state == '?':
344 if exact and state == '?':
332 ui.warn(_('%s: not copying - file is not managed\n') % rel)
345 ui.warn(_('%s: not copying - file is not managed\n') % rel)
333 if exact and state == 'r':
346 if exact and state == 'r':
334 ui.warn(_('%s: not copying - file has been marked for'
347 ui.warn(_('%s: not copying - file has been marked for'
335 ' remove\n') % rel)
348 ' remove\n') % rel)
336 continue
349 continue
337 # abs: hgsep
350 # abs: hgsep
338 # rel: ossep
351 # rel: ossep
339 srcs.append((abs, rel, exact))
352 srcs.append((abs, rel, exact))
340 return srcs
353 return srcs
341
354
342 # abssrc: hgsep
355 # abssrc: hgsep
343 # relsrc: ossep
356 # relsrc: ossep
344 # otarget: ossep
357 # otarget: ossep
345 def copyfile(abssrc, relsrc, otarget, exact):
358 def copyfile(abssrc, relsrc, otarget, exact):
346 abstarget = util.canonpath(repo.root, cwd, otarget)
359 abstarget = util.canonpath(repo.root, cwd, otarget)
347 reltarget = repo.pathto(abstarget, cwd)
360 reltarget = repo.pathto(abstarget, cwd)
348 target = repo.wjoin(abstarget)
361 target = repo.wjoin(abstarget)
349 src = repo.wjoin(abssrc)
362 src = repo.wjoin(abssrc)
350 state = repo.dirstate[abstarget]
363 state = repo.dirstate[abstarget]
351
364
352 # check for collisions
365 # check for collisions
353 prevsrc = targets.get(abstarget)
366 prevsrc = targets.get(abstarget)
354 if prevsrc is not None:
367 if prevsrc is not None:
355 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
368 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
356 (reltarget, repo.pathto(abssrc, cwd),
369 (reltarget, repo.pathto(abssrc, cwd),
357 repo.pathto(prevsrc, cwd)))
370 repo.pathto(prevsrc, cwd)))
358 return
371 return
359
372
360 # check for overwrites
373 # check for overwrites
361 exists = os.path.exists(target)
374 exists = os.path.exists(target)
362 if not after and exists or after and state in 'mn':
375 if not after and exists or after and state in 'mn':
363 if not opts['force']:
376 if not opts['force']:
364 ui.warn(_('%s: not overwriting - file exists\n') %
377 ui.warn(_('%s: not overwriting - file exists\n') %
365 reltarget)
378 reltarget)
366 return
379 return
367
380
368 if after:
381 if after:
369 if not exists:
382 if not exists:
370 return
383 return
371 elif not dryrun:
384 elif not dryrun:
372 try:
385 try:
373 if exists:
386 if exists:
374 os.unlink(target)
387 os.unlink(target)
375 targetdir = os.path.dirname(target) or '.'
388 targetdir = os.path.dirname(target) or '.'
376 if not os.path.isdir(targetdir):
389 if not os.path.isdir(targetdir):
377 os.makedirs(targetdir)
390 os.makedirs(targetdir)
378 util.copyfile(src, target)
391 util.copyfile(src, target)
379 except IOError, inst:
392 except IOError, inst:
380 if inst.errno == errno.ENOENT:
393 if inst.errno == errno.ENOENT:
381 ui.warn(_('%s: deleted in working copy\n') % relsrc)
394 ui.warn(_('%s: deleted in working copy\n') % relsrc)
382 else:
395 else:
383 ui.warn(_('%s: cannot copy - %s\n') %
396 ui.warn(_('%s: cannot copy - %s\n') %
384 (relsrc, inst.strerror))
397 (relsrc, inst.strerror))
385 return True # report a failure
398 return True # report a failure
386
399
387 if ui.verbose or not exact:
400 if ui.verbose or not exact:
388 if rename:
401 if rename:
389 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
402 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
390 else:
403 else:
391 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
404 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
392
405
393 targets[abstarget] = abssrc
406 targets[abstarget] = abssrc
394
407
395 # fix up dirstate
408 # fix up dirstate
396 origsrc = repo.dirstate.copied(abssrc) or abssrc
409 origsrc = repo.dirstate.copied(abssrc) or abssrc
397 if abstarget == origsrc: # copying back a copy?
410 if abstarget == origsrc: # copying back a copy?
398 if state not in 'mn' and not dryrun:
411 if state not in 'mn' and not dryrun:
399 repo.dirstate.normallookup(abstarget)
412 repo.dirstate.normallookup(abstarget)
400 else:
413 else:
401 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
414 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
402 if not ui.quiet:
415 if not ui.quiet:
403 ui.warn(_("%s has not been committed yet, so no copy "
416 ui.warn(_("%s has not been committed yet, so no copy "
404 "data will be stored for %s.\n")
417 "data will be stored for %s.\n")
405 % (repo.pathto(origsrc, cwd), reltarget))
418 % (repo.pathto(origsrc, cwd), reltarget))
406 if repo.dirstate[abstarget] in '?r' and not dryrun:
419 if repo.dirstate[abstarget] in '?r' and not dryrun:
407 repo.add([abstarget])
420 repo.add([abstarget])
408 elif not dryrun:
421 elif not dryrun:
409 repo.copy(origsrc, abstarget)
422 repo.copy(origsrc, abstarget)
410
423
411 if rename and not dryrun:
424 if rename and not dryrun:
412 repo.remove([abssrc], not after)
425 repo.remove([abssrc], not after)
413
426
414 # pat: ossep
427 # pat: ossep
415 # dest ossep
428 # dest ossep
416 # srcs: list of (hgsep, hgsep, ossep, bool)
429 # srcs: list of (hgsep, hgsep, ossep, bool)
417 # return: function that takes hgsep and returns ossep
430 # return: function that takes hgsep and returns ossep
418 def targetpathfn(pat, dest, srcs):
431 def targetpathfn(pat, dest, srcs):
419 if os.path.isdir(pat):
432 if os.path.isdir(pat):
420 abspfx = util.canonpath(repo.root, cwd, pat)
433 abspfx = util.canonpath(repo.root, cwd, pat)
421 abspfx = util.localpath(abspfx)
434 abspfx = util.localpath(abspfx)
422 if destdirexists:
435 if destdirexists:
423 striplen = len(os.path.split(abspfx)[0])
436 striplen = len(os.path.split(abspfx)[0])
424 else:
437 else:
425 striplen = len(abspfx)
438 striplen = len(abspfx)
426 if striplen:
439 if striplen:
427 striplen += len(os.sep)
440 striplen += len(os.sep)
428 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
441 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
429 elif destdirexists:
442 elif destdirexists:
430 res = lambda p: os.path.join(dest,
443 res = lambda p: os.path.join(dest,
431 os.path.basename(util.localpath(p)))
444 os.path.basename(util.localpath(p)))
432 else:
445 else:
433 res = lambda p: dest
446 res = lambda p: dest
434 return res
447 return res
435
448
436 # pat: ossep
449 # pat: ossep
437 # dest ossep
450 # dest ossep
438 # srcs: list of (hgsep, hgsep, ossep, bool)
451 # srcs: list of (hgsep, hgsep, ossep, bool)
439 # return: function that takes hgsep and returns ossep
452 # return: function that takes hgsep and returns ossep
440 def targetpathafterfn(pat, dest, srcs):
453 def targetpathafterfn(pat, dest, srcs):
441 if util.patkind(pat, None)[0]:
454 if util.patkind(pat, None)[0]:
442 # a mercurial pattern
455 # a mercurial pattern
443 res = lambda p: os.path.join(dest,
456 res = lambda p: os.path.join(dest,
444 os.path.basename(util.localpath(p)))
457 os.path.basename(util.localpath(p)))
445 else:
458 else:
446 abspfx = util.canonpath(repo.root, cwd, pat)
459 abspfx = util.canonpath(repo.root, cwd, pat)
447 if len(abspfx) < len(srcs[0][0]):
460 if len(abspfx) < len(srcs[0][0]):
448 # A directory. Either the target path contains the last
461 # A directory. Either the target path contains the last
449 # component of the source path or it does not.
462 # component of the source path or it does not.
450 def evalpath(striplen):
463 def evalpath(striplen):
451 score = 0
464 score = 0
452 for s in srcs:
465 for s in srcs:
453 t = os.path.join(dest, util.localpath(s[0])[striplen:])
466 t = os.path.join(dest, util.localpath(s[0])[striplen:])
454 if os.path.exists(t):
467 if os.path.exists(t):
455 score += 1
468 score += 1
456 return score
469 return score
457
470
458 abspfx = util.localpath(abspfx)
471 abspfx = util.localpath(abspfx)
459 striplen = len(abspfx)
472 striplen = len(abspfx)
460 if striplen:
473 if striplen:
461 striplen += len(os.sep)
474 striplen += len(os.sep)
462 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
475 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
463 score = evalpath(striplen)
476 score = evalpath(striplen)
464 striplen1 = len(os.path.split(abspfx)[0])
477 striplen1 = len(os.path.split(abspfx)[0])
465 if striplen1:
478 if striplen1:
466 striplen1 += len(os.sep)
479 striplen1 += len(os.sep)
467 if evalpath(striplen1) > score:
480 if evalpath(striplen1) > score:
468 striplen = striplen1
481 striplen = striplen1
469 res = lambda p: os.path.join(dest,
482 res = lambda p: os.path.join(dest,
470 util.localpath(p)[striplen:])
483 util.localpath(p)[striplen:])
471 else:
484 else:
472 # a file
485 # a file
473 if destdirexists:
486 if destdirexists:
474 res = lambda p: os.path.join(dest,
487 res = lambda p: os.path.join(dest,
475 os.path.basename(util.localpath(p)))
488 os.path.basename(util.localpath(p)))
476 else:
489 else:
477 res = lambda p: dest
490 res = lambda p: dest
478 return res
491 return res
479
492
480
493
481 pats = util.expand_glob(pats)
494 pats = util.expand_glob(pats)
482 if not pats:
495 if not pats:
483 raise util.Abort(_('no source or destination specified'))
496 raise util.Abort(_('no source or destination specified'))
484 if len(pats) == 1:
497 if len(pats) == 1:
485 raise util.Abort(_('no destination specified'))
498 raise util.Abort(_('no destination specified'))
486 dest = pats.pop()
499 dest = pats.pop()
487 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
500 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
488 if not destdirexists:
501 if not destdirexists:
489 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
502 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
490 raise util.Abort(_('with multiple sources, destination must be an '
503 raise util.Abort(_('with multiple sources, destination must be an '
491 'existing directory'))
504 'existing directory'))
492 if util.endswithsep(dest):
505 if util.endswithsep(dest):
493 raise util.Abort(_('destination %s is not a directory') % dest)
506 raise util.Abort(_('destination %s is not a directory') % dest)
494
507
495 tfn = targetpathfn
508 tfn = targetpathfn
496 if after:
509 if after:
497 tfn = targetpathafterfn
510 tfn = targetpathafterfn
498 copylist = []
511 copylist = []
499 for pat in pats:
512 for pat in pats:
500 srcs = walkpat(pat)
513 srcs = walkpat(pat)
501 if not srcs:
514 if not srcs:
502 continue
515 continue
503 copylist.append((tfn(pat, dest, srcs), srcs))
516 copylist.append((tfn(pat, dest, srcs), srcs))
504 if not copylist:
517 if not copylist:
505 raise util.Abort(_('no files to copy'))
518 raise util.Abort(_('no files to copy'))
506
519
507 errors = 0
520 errors = 0
508 for targetpath, srcs in copylist:
521 for targetpath, srcs in copylist:
509 for abssrc, relsrc, exact in srcs:
522 for abssrc, relsrc, exact in srcs:
510 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
523 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
511 errors += 1
524 errors += 1
512
525
513 if errors:
526 if errors:
514 ui.warn(_('(consider using --after)\n'))
527 ui.warn(_('(consider using --after)\n'))
515
528
516 return errors
529 return errors
517
530
518 def service(opts, parentfn=None, initfn=None, runfn=None):
531 def service(opts, parentfn=None, initfn=None, runfn=None):
519 '''Run a command as a service.'''
532 '''Run a command as a service.'''
520
533
521 if opts['daemon'] and not opts['daemon_pipefds']:
534 if opts['daemon'] and not opts['daemon_pipefds']:
522 rfd, wfd = os.pipe()
535 rfd, wfd = os.pipe()
523 args = sys.argv[:]
536 args = sys.argv[:]
524 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
537 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
525 # Don't pass --cwd to the child process, because we've already
538 # Don't pass --cwd to the child process, because we've already
526 # changed directory.
539 # changed directory.
527 for i in xrange(1,len(args)):
540 for i in xrange(1,len(args)):
528 if args[i].startswith('--cwd='):
541 if args[i].startswith('--cwd='):
529 del args[i]
542 del args[i]
530 break
543 break
531 elif args[i].startswith('--cwd'):
544 elif args[i].startswith('--cwd'):
532 del args[i:i+2]
545 del args[i:i+2]
533 break
546 break
534 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
547 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
535 args[0], args)
548 args[0], args)
536 os.close(wfd)
549 os.close(wfd)
537 os.read(rfd, 1)
550 os.read(rfd, 1)
538 if parentfn:
551 if parentfn:
539 return parentfn(pid)
552 return parentfn(pid)
540 else:
553 else:
541 os._exit(0)
554 os._exit(0)
542
555
543 if initfn:
556 if initfn:
544 initfn()
557 initfn()
545
558
546 if opts['pid_file']:
559 if opts['pid_file']:
547 fp = open(opts['pid_file'], 'w')
560 fp = open(opts['pid_file'], 'w')
548 fp.write(str(os.getpid()) + '\n')
561 fp.write(str(os.getpid()) + '\n')
549 fp.close()
562 fp.close()
550
563
551 if opts['daemon_pipefds']:
564 if opts['daemon_pipefds']:
552 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
565 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
553 os.close(rfd)
566 os.close(rfd)
554 try:
567 try:
555 os.setsid()
568 os.setsid()
556 except AttributeError:
569 except AttributeError:
557 pass
570 pass
558 os.write(wfd, 'y')
571 os.write(wfd, 'y')
559 os.close(wfd)
572 os.close(wfd)
560 sys.stdout.flush()
573 sys.stdout.flush()
561 sys.stderr.flush()
574 sys.stderr.flush()
562 fd = os.open(util.nulldev, os.O_RDWR)
575 fd = os.open(util.nulldev, os.O_RDWR)
563 if fd != 0: os.dup2(fd, 0)
576 if fd != 0: os.dup2(fd, 0)
564 if fd != 1: os.dup2(fd, 1)
577 if fd != 1: os.dup2(fd, 1)
565 if fd != 2: os.dup2(fd, 2)
578 if fd != 2: os.dup2(fd, 2)
566 if fd not in (0, 1, 2): os.close(fd)
579 if fd not in (0, 1, 2): os.close(fd)
567
580
568 if runfn:
581 if runfn:
569 return runfn()
582 return runfn()
570
583
571 class changeset_printer(object):
584 class changeset_printer(object):
572 '''show changeset information when templating not requested.'''
585 '''show changeset information when templating not requested.'''
573
586
574 def __init__(self, ui, repo, patch, diffopts, buffered):
587 def __init__(self, ui, repo, patch, diffopts, buffered):
575 self.ui = ui
588 self.ui = ui
576 self.repo = repo
589 self.repo = repo
577 self.buffered = buffered
590 self.buffered = buffered
578 self.patch = patch
591 self.patch = patch
579 self.diffopts = diffopts
592 self.diffopts = diffopts
580 self.header = {}
593 self.header = {}
581 self.hunk = {}
594 self.hunk = {}
582 self.lastheader = None
595 self.lastheader = None
583
596
584 def flush(self, rev):
597 def flush(self, rev):
585 if rev in self.header:
598 if rev in self.header:
586 h = self.header[rev]
599 h = self.header[rev]
587 if h != self.lastheader:
600 if h != self.lastheader:
588 self.lastheader = h
601 self.lastheader = h
589 self.ui.write(h)
602 self.ui.write(h)
590 del self.header[rev]
603 del self.header[rev]
591 if rev in self.hunk:
604 if rev in self.hunk:
592 self.ui.write(self.hunk[rev])
605 self.ui.write(self.hunk[rev])
593 del self.hunk[rev]
606 del self.hunk[rev]
594 return 1
607 return 1
595 return 0
608 return 0
596
609
597 def show(self, ctx, copies=(), **props):
610 def show(self, ctx, copies=(), **props):
598 if self.buffered:
611 if self.buffered:
599 self.ui.pushbuffer()
612 self.ui.pushbuffer()
600 self._show(ctx, copies, props)
613 self._show(ctx, copies, props)
601 self.hunk[ctx.rev()] = self.ui.popbuffer()
614 self.hunk[ctx.rev()] = self.ui.popbuffer()
602 else:
615 else:
603 self._show(ctx, copies, props)
616 self._show(ctx, copies, props)
604
617
605 def _show(self, ctx, copies, props):
618 def _show(self, ctx, copies, props):
606 '''show a single changeset or file revision'''
619 '''show a single changeset or file revision'''
607 changenode = ctx.node()
620 changenode = ctx.node()
608 rev = ctx.rev()
621 rev = ctx.rev()
609
622
610 if self.ui.quiet:
623 if self.ui.quiet:
611 self.ui.write("%d:%s\n" % (rev, short(changenode)))
624 self.ui.write("%d:%s\n" % (rev, short(changenode)))
612 return
625 return
613
626
614 log = self.repo.changelog
627 log = self.repo.changelog
615 changes = log.read(changenode)
628 changes = log.read(changenode)
616 date = util.datestr(changes[2])
629 date = util.datestr(changes[2])
617 extra = changes[5]
630 extra = changes[5]
618 branch = extra.get("branch")
631 branch = extra.get("branch")
619
632
620 hexfunc = self.ui.debugflag and hex or short
633 hexfunc = self.ui.debugflag and hex or short
621
634
622 parents = [(p, hexfunc(log.node(p)))
635 parents = [(p, hexfunc(log.node(p)))
623 for p in self._meaningful_parentrevs(log, rev)]
636 for p in self._meaningful_parentrevs(log, rev)]
624
637
625 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
638 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
626
639
627 # don't show the default branch name
640 # don't show the default branch name
628 if branch != 'default':
641 if branch != 'default':
629 branch = encoding.tolocal(branch)
642 branch = encoding.tolocal(branch)
630 self.ui.write(_("branch: %s\n") % branch)
643 self.ui.write(_("branch: %s\n") % branch)
631 for tag in self.repo.nodetags(changenode):
644 for tag in self.repo.nodetags(changenode):
632 self.ui.write(_("tag: %s\n") % tag)
645 self.ui.write(_("tag: %s\n") % tag)
633 for parent in parents:
646 for parent in parents:
634 self.ui.write(_("parent: %d:%s\n") % parent)
647 self.ui.write(_("parent: %d:%s\n") % parent)
635
648
636 if self.ui.debugflag:
649 if self.ui.debugflag:
637 self.ui.write(_("manifest: %d:%s\n") %
650 self.ui.write(_("manifest: %d:%s\n") %
638 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
651 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
639 self.ui.write(_("user: %s\n") % changes[1])
652 self.ui.write(_("user: %s\n") % changes[1])
640 self.ui.write(_("date: %s\n") % date)
653 self.ui.write(_("date: %s\n") % date)
641
654
642 if self.ui.debugflag:
655 if self.ui.debugflag:
643 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
656 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
644 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
657 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
645 files):
658 files):
646 if value:
659 if value:
647 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
660 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
648 elif changes[3] and self.ui.verbose:
661 elif changes[3] and self.ui.verbose:
649 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
662 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
650 if copies and self.ui.verbose:
663 if copies and self.ui.verbose:
651 copies = ['%s (%s)' % c for c in copies]
664 copies = ['%s (%s)' % c for c in copies]
652 self.ui.write(_("copies: %s\n") % ' '.join(copies))
665 self.ui.write(_("copies: %s\n") % ' '.join(copies))
653
666
654 if extra and self.ui.debugflag:
667 if extra and self.ui.debugflag:
655 for key, value in util.sort(extra.items()):
668 for key, value in util.sort(extra.items()):
656 self.ui.write(_("extra: %s=%s\n")
669 self.ui.write(_("extra: %s=%s\n")
657 % (key, value.encode('string_escape')))
670 % (key, value.encode('string_escape')))
658
671
659 description = changes[4].strip()
672 description = changes[4].strip()
660 if description:
673 if description:
661 if self.ui.verbose:
674 if self.ui.verbose:
662 self.ui.write(_("description:\n"))
675 self.ui.write(_("description:\n"))
663 self.ui.write(description)
676 self.ui.write(description)
664 self.ui.write("\n\n")
677 self.ui.write("\n\n")
665 else:
678 else:
666 self.ui.write(_("summary: %s\n") %
679 self.ui.write(_("summary: %s\n") %
667 description.splitlines()[0])
680 description.splitlines()[0])
668 self.ui.write("\n")
681 self.ui.write("\n")
669
682
670 self.showpatch(changenode)
683 self.showpatch(changenode)
671
684
672 def showpatch(self, node):
685 def showpatch(self, node):
673 if self.patch:
686 if self.patch:
674 prev = self.repo.changelog.parents(node)[0]
687 prev = self.repo.changelog.parents(node)[0]
675 chunks = patch.diff(self.repo, prev, node, match=self.patch,
688 chunks = patch.diff(self.repo, prev, node, match=self.patch,
676 opts=patch.diffopts(self.ui, self.diffopts))
689 opts=patch.diffopts(self.ui, self.diffopts))
677 for chunk in chunks:
690 for chunk in chunks:
678 self.ui.write(chunk)
691 self.ui.write(chunk)
679 self.ui.write("\n")
692 self.ui.write("\n")
680
693
681 def _meaningful_parentrevs(self, log, rev):
694 def _meaningful_parentrevs(self, log, rev):
682 """Return list of meaningful (or all if debug) parentrevs for rev.
695 """Return list of meaningful (or all if debug) parentrevs for rev.
683
696
684 For merges (two non-nullrev revisions) both parents are meaningful.
697 For merges (two non-nullrev revisions) both parents are meaningful.
685 Otherwise the first parent revision is considered meaningful if it
698 Otherwise the first parent revision is considered meaningful if it
686 is not the preceding revision.
699 is not the preceding revision.
687 """
700 """
688 parents = log.parentrevs(rev)
701 parents = log.parentrevs(rev)
689 if not self.ui.debugflag and parents[1] == nullrev:
702 if not self.ui.debugflag and parents[1] == nullrev:
690 if parents[0] >= rev - 1:
703 if parents[0] >= rev - 1:
691 parents = []
704 parents = []
692 else:
705 else:
693 parents = [parents[0]]
706 parents = [parents[0]]
694 return parents
707 return parents
695
708
696
709
697 class changeset_templater(changeset_printer):
710 class changeset_templater(changeset_printer):
698 '''format changeset information.'''
711 '''format changeset information.'''
699
712
700 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
713 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
701 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
714 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
702 filters = templatefilters.filters.copy()
715 filters = templatefilters.filters.copy()
703 filters['formatnode'] = (ui.debugflag and (lambda x: x)
716 filters['formatnode'] = (ui.debugflag and (lambda x: x)
704 or (lambda x: x[:12]))
717 or (lambda x: x[:12]))
705 self.t = templater.templater(mapfile, filters,
718 self.t = templater.templater(mapfile, filters,
706 cache={
719 cache={
707 'parent': '{rev}:{node|formatnode} ',
720 'parent': '{rev}:{node|formatnode} ',
708 'manifest': '{rev}:{node|formatnode}',
721 'manifest': '{rev}:{node|formatnode}',
709 'filecopy': '{name} ({source})'})
722 'filecopy': '{name} ({source})'})
710
723
711 def use_template(self, t):
724 def use_template(self, t):
712 '''set template string to use'''
725 '''set template string to use'''
713 self.t.cache['changeset'] = t
726 self.t.cache['changeset'] = t
714
727
715 def _meaningful_parentrevs(self, ctx):
728 def _meaningful_parentrevs(self, ctx):
716 """Return list of meaningful (or all if debug) parentrevs for rev.
729 """Return list of meaningful (or all if debug) parentrevs for rev.
717 """
730 """
718 parents = ctx.parents()
731 parents = ctx.parents()
719 if len(parents) > 1:
732 if len(parents) > 1:
720 return parents
733 return parents
721 if self.ui.debugflag:
734 if self.ui.debugflag:
722 return [parents[0], self.repo['null']]
735 return [parents[0], self.repo['null']]
723 if parents[0].rev() >= ctx.rev() - 1:
736 if parents[0].rev() >= ctx.rev() - 1:
724 return []
737 return []
725 return parents
738 return parents
726
739
727 def _show(self, ctx, copies, props):
740 def _show(self, ctx, copies, props):
728 '''show a single changeset or file revision'''
741 '''show a single changeset or file revision'''
729
742
730 def showlist(name, values, plural=None, **args):
743 def showlist(name, values, plural=None, **args):
731 '''expand set of values.
744 '''expand set of values.
732 name is name of key in template map.
745 name is name of key in template map.
733 values is list of strings or dicts.
746 values is list of strings or dicts.
734 plural is plural of name, if not simply name + 's'.
747 plural is plural of name, if not simply name + 's'.
735
748
736 expansion works like this, given name 'foo'.
749 expansion works like this, given name 'foo'.
737
750
738 if values is empty, expand 'no_foos'.
751 if values is empty, expand 'no_foos'.
739
752
740 if 'foo' not in template map, return values as a string,
753 if 'foo' not in template map, return values as a string,
741 joined by space.
754 joined by space.
742
755
743 expand 'start_foos'.
756 expand 'start_foos'.
744
757
745 for each value, expand 'foo'. if 'last_foo' in template
758 for each value, expand 'foo'. if 'last_foo' in template
746 map, expand it instead of 'foo' for last key.
759 map, expand it instead of 'foo' for last key.
747
760
748 expand 'end_foos'.
761 expand 'end_foos'.
749 '''
762 '''
750 if plural: names = plural
763 if plural: names = plural
751 else: names = name + 's'
764 else: names = name + 's'
752 if not values:
765 if not values:
753 noname = 'no_' + names
766 noname = 'no_' + names
754 if noname in self.t:
767 if noname in self.t:
755 yield self.t(noname, **args)
768 yield self.t(noname, **args)
756 return
769 return
757 if name not in self.t:
770 if name not in self.t:
758 if isinstance(values[0], str):
771 if isinstance(values[0], str):
759 yield ' '.join(values)
772 yield ' '.join(values)
760 else:
773 else:
761 for v in values:
774 for v in values:
762 yield dict(v, **args)
775 yield dict(v, **args)
763 return
776 return
764 startname = 'start_' + names
777 startname = 'start_' + names
765 if startname in self.t:
778 if startname in self.t:
766 yield self.t(startname, **args)
779 yield self.t(startname, **args)
767 vargs = args.copy()
780 vargs = args.copy()
768 def one(v, tag=name):
781 def one(v, tag=name):
769 try:
782 try:
770 vargs.update(v)
783 vargs.update(v)
771 except (AttributeError, ValueError):
784 except (AttributeError, ValueError):
772 try:
785 try:
773 for a, b in v:
786 for a, b in v:
774 vargs[a] = b
787 vargs[a] = b
775 except ValueError:
788 except ValueError:
776 vargs[name] = v
789 vargs[name] = v
777 return self.t(tag, **vargs)
790 return self.t(tag, **vargs)
778 lastname = 'last_' + name
791 lastname = 'last_' + name
779 if lastname in self.t:
792 if lastname in self.t:
780 last = values.pop()
793 last = values.pop()
781 else:
794 else:
782 last = None
795 last = None
783 for v in values:
796 for v in values:
784 yield one(v)
797 yield one(v)
785 if last is not None:
798 if last is not None:
786 yield one(last, tag=lastname)
799 yield one(last, tag=lastname)
787 endname = 'end_' + names
800 endname = 'end_' + names
788 if endname in self.t:
801 if endname in self.t:
789 yield self.t(endname, **args)
802 yield self.t(endname, **args)
790
803
791 def showbranches(**args):
804 def showbranches(**args):
792 branch = ctx.branch()
805 branch = ctx.branch()
793 if branch != 'default':
806 if branch != 'default':
794 branch = encoding.tolocal(branch)
807 branch = encoding.tolocal(branch)
795 return showlist('branch', [branch], plural='branches', **args)
808 return showlist('branch', [branch], plural='branches', **args)
796
809
797 def showparents(**args):
810 def showparents(**args):
798 parents = [[('rev', p.rev()), ('node', p.hex())]
811 parents = [[('rev', p.rev()), ('node', p.hex())]
799 for p in self._meaningful_parentrevs(ctx)]
812 for p in self._meaningful_parentrevs(ctx)]
800 return showlist('parent', parents, **args)
813 return showlist('parent', parents, **args)
801
814
802 def showtags(**args):
815 def showtags(**args):
803 return showlist('tag', ctx.tags(), **args)
816 return showlist('tag', ctx.tags(), **args)
804
817
805 def showextras(**args):
818 def showextras(**args):
806 for key, value in util.sort(ctx.extra().items()):
819 for key, value in util.sort(ctx.extra().items()):
807 args = args.copy()
820 args = args.copy()
808 args.update(dict(key=key, value=value))
821 args.update(dict(key=key, value=value))
809 yield self.t('extra', **args)
822 yield self.t('extra', **args)
810
823
811 def showcopies(**args):
824 def showcopies(**args):
812 c = [{'name': x[0], 'source': x[1]} for x in copies]
825 c = [{'name': x[0], 'source': x[1]} for x in copies]
813 return showlist('file_copy', c, plural='file_copies', **args)
826 return showlist('file_copy', c, plural='file_copies', **args)
814
827
815 files = []
828 files = []
816 def getfiles():
829 def getfiles():
817 if not files:
830 if not files:
818 files[:] = self.repo.status(ctx.parents()[0].node(),
831 files[:] = self.repo.status(ctx.parents()[0].node(),
819 ctx.node())[:3]
832 ctx.node())[:3]
820 return files
833 return files
821 def showfiles(**args):
834 def showfiles(**args):
822 return showlist('file', ctx.files(), **args)
835 return showlist('file', ctx.files(), **args)
823 def showmods(**args):
836 def showmods(**args):
824 return showlist('file_mod', getfiles()[0], **args)
837 return showlist('file_mod', getfiles()[0], **args)
825 def showadds(**args):
838 def showadds(**args):
826 return showlist('file_add', getfiles()[1], **args)
839 return showlist('file_add', getfiles()[1], **args)
827 def showdels(**args):
840 def showdels(**args):
828 return showlist('file_del', getfiles()[2], **args)
841 return showlist('file_del', getfiles()[2], **args)
829 def showmanifest(**args):
842 def showmanifest(**args):
830 args = args.copy()
843 args = args.copy()
831 args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]),
844 args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]),
832 node=hex(ctx.changeset()[0])))
845 node=hex(ctx.changeset()[0])))
833 return self.t('manifest', **args)
846 return self.t('manifest', **args)
834
847
835 def showdiffstat(**args):
848 def showdiffstat(**args):
836 diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node())
849 diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node())
837 files, adds, removes = 0, 0, 0
850 files, adds, removes = 0, 0, 0
838 for i in patch.diffstatdata(util.iterlines(diff)):
851 for i in patch.diffstatdata(util.iterlines(diff)):
839 files += 1
852 files += 1
840 adds += i[1]
853 adds += i[1]
841 removes += i[2]
854 removes += i[2]
842 return '%s: +%s/-%s' % (files, adds, removes)
855 return '%s: +%s/-%s' % (files, adds, removes)
843
856
844 defprops = {
857 defprops = {
845 'author': ctx.user(),
858 'author': ctx.user(),
846 'branches': showbranches,
859 'branches': showbranches,
847 'date': ctx.date(),
860 'date': ctx.date(),
848 'desc': ctx.description().strip(),
861 'desc': ctx.description().strip(),
849 'file_adds': showadds,
862 'file_adds': showadds,
850 'file_dels': showdels,
863 'file_dels': showdels,
851 'file_mods': showmods,
864 'file_mods': showmods,
852 'files': showfiles,
865 'files': showfiles,
853 'file_copies': showcopies,
866 'file_copies': showcopies,
854 'manifest': showmanifest,
867 'manifest': showmanifest,
855 'node': ctx.hex(),
868 'node': ctx.hex(),
856 'parents': showparents,
869 'parents': showparents,
857 'rev': ctx.rev(),
870 'rev': ctx.rev(),
858 'tags': showtags,
871 'tags': showtags,
859 'extras': showextras,
872 'extras': showextras,
860 'diffstat': showdiffstat,
873 'diffstat': showdiffstat,
861 }
874 }
862 props = props.copy()
875 props = props.copy()
863 props.update(defprops)
876 props.update(defprops)
864
877
865 # find correct templates for current mode
878 # find correct templates for current mode
866
879
867 tmplmodes = [
880 tmplmodes = [
868 (True, None),
881 (True, None),
869 (self.ui.verbose, 'verbose'),
882 (self.ui.verbose, 'verbose'),
870 (self.ui.quiet, 'quiet'),
883 (self.ui.quiet, 'quiet'),
871 (self.ui.debugflag, 'debug'),
884 (self.ui.debugflag, 'debug'),
872 ]
885 ]
873
886
874 types = {'header': '', 'changeset': 'changeset'}
887 types = {'header': '', 'changeset': 'changeset'}
875 for mode, postfix in tmplmodes:
888 for mode, postfix in tmplmodes:
876 for type in types:
889 for type in types:
877 cur = postfix and ('%s_%s' % (type, postfix)) or type
890 cur = postfix and ('%s_%s' % (type, postfix)) or type
878 if mode and cur in self.t:
891 if mode and cur in self.t:
879 types[type] = cur
892 types[type] = cur
880
893
881 try:
894 try:
882
895
883 # write header
896 # write header
884 if types['header']:
897 if types['header']:
885 h = templater.stringify(self.t(types['header'], **props))
898 h = templater.stringify(self.t(types['header'], **props))
886 if self.buffered:
899 if self.buffered:
887 self.header[ctx.rev()] = h
900 self.header[ctx.rev()] = h
888 else:
901 else:
889 self.ui.write(h)
902 self.ui.write(h)
890
903
891 # write changeset metadata, then patch if requested
904 # write changeset metadata, then patch if requested
892 key = types['changeset']
905 key = types['changeset']
893 self.ui.write(templater.stringify(self.t(key, **props)))
906 self.ui.write(templater.stringify(self.t(key, **props)))
894 self.showpatch(ctx.node())
907 self.showpatch(ctx.node())
895
908
896 except KeyError, inst:
909 except KeyError, inst:
897 msg = _("%s: no key named '%s'")
910 msg = _("%s: no key named '%s'")
898 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
911 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
899 except SyntaxError, inst:
912 except SyntaxError, inst:
900 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
913 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
901
914
902 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
915 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
903 """show one changeset using template or regular display.
916 """show one changeset using template or regular display.
904
917
905 Display format will be the first non-empty hit of:
918 Display format will be the first non-empty hit of:
906 1. option 'template'
919 1. option 'template'
907 2. option 'style'
920 2. option 'style'
908 3. [ui] setting 'logtemplate'
921 3. [ui] setting 'logtemplate'
909 4. [ui] setting 'style'
922 4. [ui] setting 'style'
910 If all of these values are either the unset or the empty string,
923 If all of these values are either the unset or the empty string,
911 regular display via changeset_printer() is done.
924 regular display via changeset_printer() is done.
912 """
925 """
913 # options
926 # options
914 patch = False
927 patch = False
915 if opts.get('patch'):
928 if opts.get('patch'):
916 patch = matchfn or matchall(repo)
929 patch = matchfn or matchall(repo)
917
930
918 tmpl = opts.get('template')
931 tmpl = opts.get('template')
919 style = None
932 style = None
920 if tmpl:
933 if tmpl:
921 tmpl = templater.parsestring(tmpl, quoted=False)
934 tmpl = templater.parsestring(tmpl, quoted=False)
922 else:
935 else:
923 style = opts.get('style')
936 style = opts.get('style')
924
937
925 # ui settings
938 # ui settings
926 if not (tmpl or style):
939 if not (tmpl or style):
927 tmpl = ui.config('ui', 'logtemplate')
940 tmpl = ui.config('ui', 'logtemplate')
928 if tmpl:
941 if tmpl:
929 tmpl = templater.parsestring(tmpl)
942 tmpl = templater.parsestring(tmpl)
930 else:
943 else:
931 style = ui.config('ui', 'style')
944 style = ui.config('ui', 'style')
932
945
933 if not (tmpl or style):
946 if not (tmpl or style):
934 return changeset_printer(ui, repo, patch, opts, buffered)
947 return changeset_printer(ui, repo, patch, opts, buffered)
935
948
936 mapfile = None
949 mapfile = None
937 if style and not tmpl:
950 if style and not tmpl:
938 mapfile = style
951 mapfile = style
939 if not os.path.split(mapfile)[0]:
952 if not os.path.split(mapfile)[0]:
940 mapname = (templater.templatepath('map-cmdline.' + mapfile)
953 mapname = (templater.templatepath('map-cmdline.' + mapfile)
941 or templater.templatepath(mapfile))
954 or templater.templatepath(mapfile))
942 if mapname: mapfile = mapname
955 if mapname: mapfile = mapname
943
956
944 try:
957 try:
945 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
958 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
946 except SyntaxError, inst:
959 except SyntaxError, inst:
947 raise util.Abort(inst.args[0])
960 raise util.Abort(inst.args[0])
948 if tmpl: t.use_template(tmpl)
961 if tmpl: t.use_template(tmpl)
949 return t
962 return t
950
963
951 def finddate(ui, repo, date):
964 def finddate(ui, repo, date):
952 """Find the tipmost changeset that matches the given date spec"""
965 """Find the tipmost changeset that matches the given date spec"""
953 df = util.matchdate(date)
966 df = util.matchdate(date)
954 get = util.cachefunc(lambda r: repo[r].changeset())
967 get = util.cachefunc(lambda r: repo[r].changeset())
955 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
968 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
956 results = {}
969 results = {}
957 for st, rev, fns in changeiter:
970 for st, rev, fns in changeiter:
958 if st == 'add':
971 if st == 'add':
959 d = get(rev)[2]
972 d = get(rev)[2]
960 if df(d[0]):
973 if df(d[0]):
961 results[rev] = d
974 results[rev] = d
962 elif st == 'iter':
975 elif st == 'iter':
963 if rev in results:
976 if rev in results:
964 ui.status(_("Found revision %s from %s\n") %
977 ui.status(_("Found revision %s from %s\n") %
965 (rev, util.datestr(results[rev])))
978 (rev, util.datestr(results[rev])))
966 return str(rev)
979 return str(rev)
967
980
968 raise util.Abort(_("revision matching date not found"))
981 raise util.Abort(_("revision matching date not found"))
969
982
970 def walkchangerevs(ui, repo, pats, change, opts):
983 def walkchangerevs(ui, repo, pats, change, opts):
971 '''Iterate over files and the revs in which they changed.
984 '''Iterate over files and the revs in which they changed.
972
985
973 Callers most commonly need to iterate backwards over the history
986 Callers most commonly need to iterate backwards over the history
974 in which they are interested. Doing so has awful (quadratic-looking)
987 in which they are interested. Doing so has awful (quadratic-looking)
975 performance, so we use iterators in a "windowed" way.
988 performance, so we use iterators in a "windowed" way.
976
989
977 We walk a window of revisions in the desired order. Within the
990 We walk a window of revisions in the desired order. Within the
978 window, we first walk forwards to gather data, then in the desired
991 window, we first walk forwards to gather data, then in the desired
979 order (usually backwards) to display it.
992 order (usually backwards) to display it.
980
993
981 This function returns an (iterator, matchfn) tuple. The iterator
994 This function returns an (iterator, matchfn) tuple. The iterator
982 yields 3-tuples. They will be of one of the following forms:
995 yields 3-tuples. They will be of one of the following forms:
983
996
984 "window", incrementing, lastrev: stepping through a window,
997 "window", incrementing, lastrev: stepping through a window,
985 positive if walking forwards through revs, last rev in the
998 positive if walking forwards through revs, last rev in the
986 sequence iterated over - use to reset state for the current window
999 sequence iterated over - use to reset state for the current window
987
1000
988 "add", rev, fns: out-of-order traversal of the given file names
1001 "add", rev, fns: out-of-order traversal of the given file names
989 fns, which changed during revision rev - use to gather data for
1002 fns, which changed during revision rev - use to gather data for
990 possible display
1003 possible display
991
1004
992 "iter", rev, None: in-order traversal of the revs earlier iterated
1005 "iter", rev, None: in-order traversal of the revs earlier iterated
993 over with "add" - use to display data'''
1006 over with "add" - use to display data'''
994
1007
995 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1008 def increasing_windows(start, end, windowsize=8, sizelimit=512):
996 if start < end:
1009 if start < end:
997 while start < end:
1010 while start < end:
998 yield start, min(windowsize, end-start)
1011 yield start, min(windowsize, end-start)
999 start += windowsize
1012 start += windowsize
1000 if windowsize < sizelimit:
1013 if windowsize < sizelimit:
1001 windowsize *= 2
1014 windowsize *= 2
1002 else:
1015 else:
1003 while start > end:
1016 while start > end:
1004 yield start, min(windowsize, start-end-1)
1017 yield start, min(windowsize, start-end-1)
1005 start -= windowsize
1018 start -= windowsize
1006 if windowsize < sizelimit:
1019 if windowsize < sizelimit:
1007 windowsize *= 2
1020 windowsize *= 2
1008
1021
1009 m = match(repo, pats, opts)
1022 m = match(repo, pats, opts)
1010 follow = opts.get('follow') or opts.get('follow_first')
1023 follow = opts.get('follow') or opts.get('follow_first')
1011
1024
1012 if not len(repo):
1025 if not len(repo):
1013 return [], m
1026 return [], m
1014
1027
1015 if follow:
1028 if follow:
1016 defrange = '%s:0' % repo['.'].rev()
1029 defrange = '%s:0' % repo['.'].rev()
1017 else:
1030 else:
1018 defrange = '-1:0'
1031 defrange = '-1:0'
1019 revs = revrange(repo, opts['rev'] or [defrange])
1032 revs = revrange(repo, opts['rev'] or [defrange])
1020 wanted = set()
1033 wanted = set()
1021 slowpath = m.anypats() or (m.files() and opts.get('removed'))
1034 slowpath = m.anypats() or (m.files() and opts.get('removed'))
1022 fncache = {}
1035 fncache = {}
1023
1036
1024 if not slowpath and not m.files():
1037 if not slowpath and not m.files():
1025 # No files, no patterns. Display all revs.
1038 # No files, no patterns. Display all revs.
1026 wanted = set(revs)
1039 wanted = set(revs)
1027 copies = []
1040 copies = []
1028 if not slowpath:
1041 if not slowpath:
1029 # Only files, no patterns. Check the history of each file.
1042 # Only files, no patterns. Check the history of each file.
1030 def filerevgen(filelog, node):
1043 def filerevgen(filelog, node):
1031 cl_count = len(repo)
1044 cl_count = len(repo)
1032 if node is None:
1045 if node is None:
1033 last = len(filelog) - 1
1046 last = len(filelog) - 1
1034 else:
1047 else:
1035 last = filelog.rev(node)
1048 last = filelog.rev(node)
1036 for i, window in increasing_windows(last, nullrev):
1049 for i, window in increasing_windows(last, nullrev):
1037 revs = []
1050 revs = []
1038 for j in xrange(i - window, i + 1):
1051 for j in xrange(i - window, i + 1):
1039 n = filelog.node(j)
1052 n = filelog.node(j)
1040 revs.append((filelog.linkrev(j),
1053 revs.append((filelog.linkrev(j),
1041 follow and filelog.renamed(n)))
1054 follow and filelog.renamed(n)))
1042 revs.reverse()
1055 revs.reverse()
1043 for rev in revs:
1056 for rev in revs:
1044 # only yield rev for which we have the changelog, it can
1057 # only yield rev for which we have the changelog, it can
1045 # happen while doing "hg log" during a pull or commit
1058 # happen while doing "hg log" during a pull or commit
1046 if rev[0] < cl_count:
1059 if rev[0] < cl_count:
1047 yield rev
1060 yield rev
1048 def iterfiles():
1061 def iterfiles():
1049 for filename in m.files():
1062 for filename in m.files():
1050 yield filename, None
1063 yield filename, None
1051 for filename_node in copies:
1064 for filename_node in copies:
1052 yield filename_node
1065 yield filename_node
1053 minrev, maxrev = min(revs), max(revs)
1066 minrev, maxrev = min(revs), max(revs)
1054 for file_, node in iterfiles():
1067 for file_, node in iterfiles():
1055 filelog = repo.file(file_)
1068 filelog = repo.file(file_)
1056 if not len(filelog):
1069 if not len(filelog):
1057 if node is None:
1070 if node is None:
1058 # A zero count may be a directory or deleted file, so
1071 # A zero count may be a directory or deleted file, so
1059 # try to find matching entries on the slow path.
1072 # try to find matching entries on the slow path.
1060 if follow:
1073 if follow:
1061 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1074 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1062 slowpath = True
1075 slowpath = True
1063 break
1076 break
1064 else:
1077 else:
1065 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1078 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1066 % (file_, short(node)))
1079 % (file_, short(node)))
1067 continue
1080 continue
1068 for rev, copied in filerevgen(filelog, node):
1081 for rev, copied in filerevgen(filelog, node):
1069 if rev <= maxrev:
1082 if rev <= maxrev:
1070 if rev < minrev:
1083 if rev < minrev:
1071 break
1084 break
1072 fncache.setdefault(rev, [])
1085 fncache.setdefault(rev, [])
1073 fncache[rev].append(file_)
1086 fncache[rev].append(file_)
1074 wanted.add(rev)
1087 wanted.add(rev)
1075 if follow and copied:
1088 if follow and copied:
1076 copies.append(copied)
1089 copies.append(copied)
1077 if slowpath:
1090 if slowpath:
1078 if follow:
1091 if follow:
1079 raise util.Abort(_('can only follow copies/renames for explicit '
1092 raise util.Abort(_('can only follow copies/renames for explicit '
1080 'file names'))
1093 'file names'))
1081
1094
1082 # The slow path checks files modified in every changeset.
1095 # The slow path checks files modified in every changeset.
1083 def changerevgen():
1096 def changerevgen():
1084 for i, window in increasing_windows(len(repo) - 1, nullrev):
1097 for i, window in increasing_windows(len(repo) - 1, nullrev):
1085 for j in xrange(i - window, i + 1):
1098 for j in xrange(i - window, i + 1):
1086 yield j, change(j)[3]
1099 yield j, change(j)[3]
1087
1100
1088 for rev, changefiles in changerevgen():
1101 for rev, changefiles in changerevgen():
1089 matches = filter(m, changefiles)
1102 matches = filter(m, changefiles)
1090 if matches:
1103 if matches:
1091 fncache[rev] = matches
1104 fncache[rev] = matches
1092 wanted.add(rev)
1105 wanted.add(rev)
1093
1106
1094 class followfilter:
1107 class followfilter:
1095 def __init__(self, onlyfirst=False):
1108 def __init__(self, onlyfirst=False):
1096 self.startrev = nullrev
1109 self.startrev = nullrev
1097 self.roots = []
1110 self.roots = []
1098 self.onlyfirst = onlyfirst
1111 self.onlyfirst = onlyfirst
1099
1112
1100 def match(self, rev):
1113 def match(self, rev):
1101 def realparents(rev):
1114 def realparents(rev):
1102 if self.onlyfirst:
1115 if self.onlyfirst:
1103 return repo.changelog.parentrevs(rev)[0:1]
1116 return repo.changelog.parentrevs(rev)[0:1]
1104 else:
1117 else:
1105 return filter(lambda x: x != nullrev,
1118 return filter(lambda x: x != nullrev,
1106 repo.changelog.parentrevs(rev))
1119 repo.changelog.parentrevs(rev))
1107
1120
1108 if self.startrev == nullrev:
1121 if self.startrev == nullrev:
1109 self.startrev = rev
1122 self.startrev = rev
1110 return True
1123 return True
1111
1124
1112 if rev > self.startrev:
1125 if rev > self.startrev:
1113 # forward: all descendants
1126 # forward: all descendants
1114 if not self.roots:
1127 if not self.roots:
1115 self.roots.append(self.startrev)
1128 self.roots.append(self.startrev)
1116 for parent in realparents(rev):
1129 for parent in realparents(rev):
1117 if parent in self.roots:
1130 if parent in self.roots:
1118 self.roots.append(rev)
1131 self.roots.append(rev)
1119 return True
1132 return True
1120 else:
1133 else:
1121 # backwards: all parents
1134 # backwards: all parents
1122 if not self.roots:
1135 if not self.roots:
1123 self.roots.extend(realparents(self.startrev))
1136 self.roots.extend(realparents(self.startrev))
1124 if rev in self.roots:
1137 if rev in self.roots:
1125 self.roots.remove(rev)
1138 self.roots.remove(rev)
1126 self.roots.extend(realparents(rev))
1139 self.roots.extend(realparents(rev))
1127 return True
1140 return True
1128
1141
1129 return False
1142 return False
1130
1143
1131 # it might be worthwhile to do this in the iterator if the rev range
1144 # it might be worthwhile to do this in the iterator if the rev range
1132 # is descending and the prune args are all within that range
1145 # is descending and the prune args are all within that range
1133 for rev in opts.get('prune', ()):
1146 for rev in opts.get('prune', ()):
1134 rev = repo.changelog.rev(repo.lookup(rev))
1147 rev = repo.changelog.rev(repo.lookup(rev))
1135 ff = followfilter()
1148 ff = followfilter()
1136 stop = min(revs[0], revs[-1])
1149 stop = min(revs[0], revs[-1])
1137 for x in xrange(rev, stop-1, -1):
1150 for x in xrange(rev, stop-1, -1):
1138 if ff.match(x):
1151 if ff.match(x):
1139 wanted.discard(x)
1152 wanted.discard(x)
1140
1153
1141 def iterate():
1154 def iterate():
1142 if follow and not m.files():
1155 if follow and not m.files():
1143 ff = followfilter(onlyfirst=opts.get('follow_first'))
1156 ff = followfilter(onlyfirst=opts.get('follow_first'))
1144 def want(rev):
1157 def want(rev):
1145 return ff.match(rev) and rev in wanted
1158 return ff.match(rev) and rev in wanted
1146 else:
1159 else:
1147 def want(rev):
1160 def want(rev):
1148 return rev in wanted
1161 return rev in wanted
1149
1162
1150 for i, window in increasing_windows(0, len(revs)):
1163 for i, window in increasing_windows(0, len(revs)):
1151 yield 'window', revs[0] < revs[-1], revs[-1]
1164 yield 'window', revs[0] < revs[-1], revs[-1]
1152 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1165 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1153 for rev in util.sort(list(nrevs)):
1166 for rev in util.sort(list(nrevs)):
1154 fns = fncache.get(rev)
1167 fns = fncache.get(rev)
1155 if not fns:
1168 if not fns:
1156 def fns_generator():
1169 def fns_generator():
1157 for f in change(rev)[3]:
1170 for f in change(rev)[3]:
1158 if m(f):
1171 if m(f):
1159 yield f
1172 yield f
1160 fns = fns_generator()
1173 fns = fns_generator()
1161 yield 'add', rev, fns
1174 yield 'add', rev, fns
1162 for rev in nrevs:
1175 for rev in nrevs:
1163 yield 'iter', rev, None
1176 yield 'iter', rev, None
1164 return iterate(), m
1177 return iterate(), m
1165
1178
1166 def commit(ui, repo, commitfunc, pats, opts):
1179 def commit(ui, repo, commitfunc, pats, opts):
1167 '''commit the specified files or all outstanding changes'''
1180 '''commit the specified files or all outstanding changes'''
1168 date = opts.get('date')
1181 date = opts.get('date')
1169 if date:
1182 if date:
1170 opts['date'] = util.parsedate(date)
1183 opts['date'] = util.parsedate(date)
1171 message = logmessage(opts)
1184 message = logmessage(opts)
1172
1185
1173 # extract addremove carefully -- this function can be called from a command
1186 # extract addremove carefully -- this function can be called from a command
1174 # that doesn't support addremove
1187 # that doesn't support addremove
1175 if opts.get('addremove'):
1188 if opts.get('addremove'):
1176 addremove(repo, pats, opts)
1189 addremove(repo, pats, opts)
1177
1190
1178 m = match(repo, pats, opts)
1191 m = match(repo, pats, opts)
1179 if pats:
1192 if pats:
1180 modified, added, removed = repo.status(match=m)[:3]
1193 modified, added, removed = repo.status(match=m)[:3]
1181 files = util.sort(modified + added + removed)
1194 files = util.sort(modified + added + removed)
1182
1195
1183 def is_dir(f):
1196 def is_dir(f):
1184 name = f + '/'
1197 name = f + '/'
1185 i = bisect.bisect(files, name)
1198 i = bisect.bisect(files, name)
1186 return i < len(files) and files[i].startswith(name)
1199 return i < len(files) and files[i].startswith(name)
1187
1200
1188 for f in m.files():
1201 for f in m.files():
1189 if f == '.':
1202 if f == '.':
1190 continue
1203 continue
1191 if f not in files:
1204 if f not in files:
1192 rf = repo.wjoin(f)
1205 rf = repo.wjoin(f)
1193 rel = repo.pathto(f)
1206 rel = repo.pathto(f)
1194 try:
1207 try:
1195 mode = os.lstat(rf)[stat.ST_MODE]
1208 mode = os.lstat(rf)[stat.ST_MODE]
1196 except OSError:
1209 except OSError:
1197 if is_dir(f): # deleted directory ?
1210 if is_dir(f): # deleted directory ?
1198 continue
1211 continue
1199 raise util.Abort(_("file %s not found!") % rel)
1212 raise util.Abort(_("file %s not found!") % rel)
1200 if stat.S_ISDIR(mode):
1213 if stat.S_ISDIR(mode):
1201 if not is_dir(f):
1214 if not is_dir(f):
1202 raise util.Abort(_("no match under directory %s!")
1215 raise util.Abort(_("no match under directory %s!")
1203 % rel)
1216 % rel)
1204 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1217 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1205 raise util.Abort(_("can't commit %s: "
1218 raise util.Abort(_("can't commit %s: "
1206 "unsupported file type!") % rel)
1219 "unsupported file type!") % rel)
1207 elif f not in repo.dirstate:
1220 elif f not in repo.dirstate:
1208 raise util.Abort(_("file %s not tracked!") % rel)
1221 raise util.Abort(_("file %s not tracked!") % rel)
1209 m = matchfiles(repo, files)
1222 m = matchfiles(repo, files)
1210 try:
1223 try:
1211 return commitfunc(ui, repo, message, m, opts)
1224 return commitfunc(ui, repo, message, m, opts)
1212 except ValueError, inst:
1225 except ValueError, inst:
1213 raise util.Abort(str(inst))
1226 raise util.Abort(str(inst))
@@ -1,3475 +1,3465 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 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, textwrap
11 import os, re, sys, textwrap
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import difflib, patch, time, help, mdiff, tempfile, url, encoding
13 import difflib, patch, time, help, mdiff, tempfile, url, encoding
14 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
14 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 import merge as merge_
15 import merge as merge_
16
16
17 # Commands start here, listed alphabetically
17 # Commands start here, listed alphabetically
18
18
19 def add(ui, repo, *pats, **opts):
19 def add(ui, repo, *pats, **opts):
20 """add the specified files on the next commit
20 """add the specified files on the next commit
21
21
22 Schedule files to be version controlled and added to the
22 Schedule files to be version controlled and added to the
23 repository.
23 repository.
24
24
25 The files will be added to the repository at the next commit. To
25 The files will be added to the repository at the next commit. To
26 undo an add before that, see hg revert.
26 undo an add before that, see hg revert.
27
27
28 If no names are given, add all files to the repository.
28 If no names are given, add all files to the repository.
29 """
29 """
30
30
31 rejected = None
31 rejected = None
32 exacts = {}
32 exacts = {}
33 names = []
33 names = []
34 m = cmdutil.match(repo, pats, opts)
34 m = cmdutil.match(repo, pats, opts)
35 m.bad = lambda x,y: True
35 m.bad = lambda x,y: True
36 for abs in repo.walk(m):
36 for abs in repo.walk(m):
37 if m.exact(abs):
37 if m.exact(abs):
38 if ui.verbose:
38 if ui.verbose:
39 ui.status(_('adding %s\n') % m.rel(abs))
39 ui.status(_('adding %s\n') % m.rel(abs))
40 names.append(abs)
40 names.append(abs)
41 exacts[abs] = 1
41 exacts[abs] = 1
42 elif abs not in repo.dirstate:
42 elif abs not in repo.dirstate:
43 ui.status(_('adding %s\n') % m.rel(abs))
43 ui.status(_('adding %s\n') % m.rel(abs))
44 names.append(abs)
44 names.append(abs)
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 rejected = repo.add(names)
46 rejected = repo.add(names)
47 rejected = [p for p in rejected if p in exacts]
47 rejected = [p for p in rejected if p in exacts]
48 return rejected and 1 or 0
48 return rejected and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the
53 Add all new files and remove all missing files from the
54 repository.
54 repository.
55
55
56 New files are ignored if they match any of the patterns in
56 New files are ignored if they match any of the patterns in
57 .hgignore. As with add, these changes take effect at the next
57 .hgignore. As with add, these changes take effect at the next
58 commit.
58 commit.
59
59
60 Use the -s/--similarity option to detect renamed files. With a
60 Use the -s/--similarity option to detect renamed files. With a
61 parameter > 0, this compares every removed file with every added
61 parameter > 0, this compares every removed file with every added
62 file and records those similar enough as renames. This option
62 file and records those similar enough as renames. This option
63 takes a percentage between 0 (disabled) and 100 (files must be
63 takes a percentage between 0 (disabled) and 100 (files must be
64 identical) as its parameter. Detecting renamed files this way can
64 identical) as its parameter. Detecting renamed files this way can
65 be expensive.
65 be expensive.
66 """
66 """
67 try:
67 try:
68 sim = float(opts.get('similarity') or 0)
68 sim = float(opts.get('similarity') or 0)
69 except ValueError:
69 except ValueError:
70 raise util.Abort(_('similarity must be a number'))
70 raise util.Abort(_('similarity must be a number'))
71 if sim < 0 or sim > 100:
71 if sim < 0 or sim > 100:
72 raise util.Abort(_('similarity must be between 0 and 100'))
72 raise util.Abort(_('similarity must be between 0 and 100'))
73 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
74
74
75 def annotate(ui, repo, *pats, **opts):
75 def annotate(ui, repo, *pats, **opts):
76 """show changeset information per file line
76 """show changeset information per file line
77
77
78 List changes in files, showing the revision id responsible for
78 List changes in files, showing the revision id responsible for
79 each line
79 each line
80
80
81 This command is useful to discover who did a change or when a
81 This command is useful to discover who did a change or when a
82 change took place.
82 change took place.
83
83
84 Without the -a/--text option, annotate will avoid processing files
84 Without the -a/--text option, annotate will avoid processing files
85 it detects as binary. With -a, annotate will generate an
85 it detects as binary. With -a, annotate will generate an
86 annotation anyway, probably with undesirable results.
86 annotation anyway, probably with undesirable results.
87 """
87 """
88 datefunc = ui.quiet and util.shortdate or util.datestr
88 datefunc = ui.quiet and util.shortdate or util.datestr
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90
90
91 if not pats:
91 if not pats:
92 raise util.Abort(_('at least one file name or pattern required'))
92 raise util.Abort(_('at least one file name or pattern required'))
93
93
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 ('number', lambda x: str(x[0].rev())),
95 ('number', lambda x: str(x[0].rev())),
96 ('changeset', lambda x: short(x[0].node())),
96 ('changeset', lambda x: short(x[0].node())),
97 ('date', getdate),
97 ('date', getdate),
98 ('follow', lambda x: x[0].path()),
98 ('follow', lambda x: x[0].path()),
99 ]
99 ]
100
100
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 and not opts.get('follow')):
102 and not opts.get('follow')):
103 opts['number'] = 1
103 opts['number'] = 1
104
104
105 linenumber = opts.get('line_number') is not None
105 linenumber = opts.get('line_number') is not None
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108
108
109 funcmap = [func for op, func in opmap if opts.get(op)]
109 funcmap = [func for op, func in opmap if opts.get(op)]
110 if linenumber:
110 if linenumber:
111 lastfunc = funcmap[-1]
111 lastfunc = funcmap[-1]
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113
113
114 ctx = repo[opts.get('rev')]
114 ctx = repo[opts.get('rev')]
115
115
116 m = cmdutil.match(repo, pats, opts)
116 m = cmdutil.match(repo, pats, opts)
117 for abs in ctx.walk(m):
117 for abs in ctx.walk(m):
118 fctx = ctx[abs]
118 fctx = ctx[abs]
119 if not opts.get('text') and util.binary(fctx.data()):
119 if not opts.get('text') and util.binary(fctx.data()):
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 continue
121 continue
122
122
123 lines = fctx.annotate(follow=opts.get('follow'),
123 lines = fctx.annotate(follow=opts.get('follow'),
124 linenumber=linenumber)
124 linenumber=linenumber)
125 pieces = []
125 pieces = []
126
126
127 for f in funcmap:
127 for f in funcmap:
128 l = [f(n) for n, dummy in lines]
128 l = [f(n) for n, dummy in lines]
129 if l:
129 if l:
130 ml = max(map(len, l))
130 ml = max(map(len, l))
131 pieces.append(["%*s" % (ml, x) for x in l])
131 pieces.append(["%*s" % (ml, x) for x in l])
132
132
133 if pieces:
133 if pieces:
134 for p, l in zip(zip(*pieces), lines):
134 for p, l in zip(zip(*pieces), lines):
135 ui.write("%s: %s" % (" ".join(p), l[1]))
135 ui.write("%s: %s" % (" ".join(p), l[1]))
136
136
137 def archive(ui, repo, dest, **opts):
137 def archive(ui, repo, dest, **opts):
138 '''create unversioned archive of a repository revision
138 '''create unversioned archive of a repository revision
139
139
140 By default, the revision used is the parent of the working
140 By default, the revision used is the parent of the working
141 directory; use -r/--rev to specify a different revision.
141 directory; use -r/--rev to specify a different revision.
142
142
143 To specify the type of archive to create, use -t/--type. Valid
143 To specify the type of archive to create, use -t/--type. Valid
144 types are:
144 types are:
145
145
146 "files" (default): a directory full of files
146 "files" (default): a directory full of files
147 "tar": tar archive, uncompressed
147 "tar": tar archive, uncompressed
148 "tbz2": tar archive, compressed using bzip2
148 "tbz2": tar archive, compressed using bzip2
149 "tgz": tar archive, compressed using gzip
149 "tgz": tar archive, compressed using gzip
150 "uzip": zip archive, uncompressed
150 "uzip": zip archive, uncompressed
151 "zip": zip archive, compressed using deflate
151 "zip": zip archive, compressed using deflate
152
152
153 The exact name of the destination archive or directory is given
153 The exact name of the destination archive or directory is given
154 using a format string; see 'hg help export' for details.
154 using a format string; see 'hg help export' for details.
155
155
156 Each member added to an archive file has a directory prefix
156 Each member added to an archive file has a directory prefix
157 prepended. Use -p/--prefix to specify a format string for the
157 prepended. Use -p/--prefix to specify a format string for the
158 prefix. The default is the basename of the archive, with suffixes
158 prefix. The default is the basename of the archive, with suffixes
159 removed.
159 removed.
160 '''
160 '''
161
161
162 ctx = repo[opts.get('rev')]
162 ctx = repo[opts.get('rev')]
163 if not ctx:
163 if not ctx:
164 raise util.Abort(_('no working directory: please specify a revision'))
164 raise util.Abort(_('no working directory: please specify a revision'))
165 node = ctx.node()
165 node = ctx.node()
166 dest = cmdutil.make_filename(repo, dest, node)
166 dest = cmdutil.make_filename(repo, dest, node)
167 if os.path.realpath(dest) == repo.root:
167 if os.path.realpath(dest) == repo.root:
168 raise util.Abort(_('repository root cannot be destination'))
168 raise util.Abort(_('repository root cannot be destination'))
169 matchfn = cmdutil.match(repo, [], opts)
169 matchfn = cmdutil.match(repo, [], opts)
170 kind = opts.get('type') or 'files'
170 kind = opts.get('type') or 'files'
171 prefix = opts.get('prefix')
171 prefix = opts.get('prefix')
172 if dest == '-':
172 if dest == '-':
173 if kind == 'files':
173 if kind == 'files':
174 raise util.Abort(_('cannot archive plain files to stdout'))
174 raise util.Abort(_('cannot archive plain files to stdout'))
175 dest = sys.stdout
175 dest = sys.stdout
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 prefix = cmdutil.make_filename(repo, prefix, node)
177 prefix = cmdutil.make_filename(repo, prefix, node)
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 matchfn, prefix)
179 matchfn, prefix)
180
180
181 def backout(ui, repo, node=None, rev=None, **opts):
181 def backout(ui, repo, node=None, rev=None, **opts):
182 '''reverse effect of earlier changeset
182 '''reverse effect of earlier changeset
183
183
184 Commit the backed out changes as a new changeset. The new
184 Commit the backed out changes as a new changeset. The new
185 changeset is a child of the backed out changeset.
185 changeset is a child of the backed out changeset.
186
186
187 If you back out a changeset other than the tip, a new head is
187 If you back out a changeset other than the tip, a new head is
188 created. This head will be the new tip and you should merge this
188 created. This head will be the new tip and you should merge this
189 backout changeset with another head (current one by default).
189 backout changeset with another head (current one by default).
190
190
191 The --merge option remembers the parent of the working directory
191 The --merge option remembers the parent of the working directory
192 before starting the backout, then merges the new head with that
192 before starting the backout, then merges the new head with that
193 changeset afterwards. This saves you from doing the merge by hand.
193 changeset afterwards. This saves you from doing the merge by hand.
194 The result of this merge is not committed, as with a normal merge.
194 The result of this merge is not committed, as with a normal merge.
195
195
196 See \'hg help dates\' for a list of formats valid for -d/--date.
196 See \'hg help dates\' for a list of formats valid for -d/--date.
197 '''
197 '''
198 if rev and node:
198 if rev and node:
199 raise util.Abort(_("please specify just one revision"))
199 raise util.Abort(_("please specify just one revision"))
200
200
201 if not rev:
201 if not rev:
202 rev = node
202 rev = node
203
203
204 if not rev:
204 if not rev:
205 raise util.Abort(_("please specify a revision to backout"))
205 raise util.Abort(_("please specify a revision to backout"))
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 cmdutil.bail_if_changed(repo)
211 cmdutil.bail_if_changed(repo)
212 node = repo.lookup(rev)
212 node = repo.lookup(rev)
213
213
214 op1, op2 = repo.dirstate.parents()
214 op1, op2 = repo.dirstate.parents()
215 a = repo.changelog.ancestor(op1, node)
215 a = repo.changelog.ancestor(op1, node)
216 if a != node:
216 if a != node:
217 raise util.Abort(_('cannot back out change on a different branch'))
217 raise util.Abort(_('cannot back out change on a different branch'))
218
218
219 p1, p2 = repo.changelog.parents(node)
219 p1, p2 = repo.changelog.parents(node)
220 if p1 == nullid:
220 if p1 == nullid:
221 raise util.Abort(_('cannot back out a change with no parents'))
221 raise util.Abort(_('cannot back out a change with no parents'))
222 if p2 != nullid:
222 if p2 != nullid:
223 if not opts.get('parent'):
223 if not opts.get('parent'):
224 raise util.Abort(_('cannot back out a merge changeset without '
224 raise util.Abort(_('cannot back out a merge changeset without '
225 '--parent'))
225 '--parent'))
226 p = repo.lookup(opts['parent'])
226 p = repo.lookup(opts['parent'])
227 if p not in (p1, p2):
227 if p not in (p1, p2):
228 raise util.Abort(_('%s is not a parent of %s') %
228 raise util.Abort(_('%s is not a parent of %s') %
229 (short(p), short(node)))
229 (short(p), short(node)))
230 parent = p
230 parent = p
231 else:
231 else:
232 if opts.get('parent'):
232 if opts.get('parent'):
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 parent = p1
234 parent = p1
235
235
236 # the backout should appear on the same branch
236 # the backout should appear on the same branch
237 branch = repo.dirstate.branch()
237 branch = repo.dirstate.branch()
238 hg.clean(repo, node, show_stats=False)
238 hg.clean(repo, node, show_stats=False)
239 repo.dirstate.setbranch(branch)
239 repo.dirstate.setbranch(branch)
240 revert_opts = opts.copy()
240 revert_opts = opts.copy()
241 revert_opts['date'] = None
241 revert_opts['date'] = None
242 revert_opts['all'] = True
242 revert_opts['all'] = True
243 revert_opts['rev'] = hex(parent)
243 revert_opts['rev'] = hex(parent)
244 revert_opts['no_backup'] = None
244 revert_opts['no_backup'] = None
245 revert(ui, repo, **revert_opts)
245 revert(ui, repo, **revert_opts)
246 commit_opts = opts.copy()
246 commit_opts = opts.copy()
247 commit_opts['addremove'] = False
247 commit_opts['addremove'] = False
248 if not commit_opts['message'] and not commit_opts['logfile']:
248 if not commit_opts['message'] and not commit_opts['logfile']:
249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 commit_opts['force_editor'] = True
250 commit_opts['force_editor'] = True
251 commit(ui, repo, **commit_opts)
251 commit(ui, repo, **commit_opts)
252 def nice(node):
252 def nice(node):
253 return '%d:%s' % (repo.changelog.rev(node), short(node))
253 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 ui.status(_('changeset %s backs out changeset %s\n') %
254 ui.status(_('changeset %s backs out changeset %s\n') %
255 (nice(repo.changelog.tip()), nice(node)))
255 (nice(repo.changelog.tip()), nice(node)))
256 if op1 != node:
256 if op1 != node:
257 hg.clean(repo, op1, show_stats=False)
257 hg.clean(repo, op1, show_stats=False)
258 if opts.get('merge'):
258 if opts.get('merge'):
259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 hg.merge(repo, hex(repo.changelog.tip()))
260 hg.merge(repo, hex(repo.changelog.tip()))
261 else:
261 else:
262 ui.status(_('the backout changeset is a new head - '
262 ui.status(_('the backout changeset is a new head - '
263 'do not forget to merge\n'))
263 'do not forget to merge\n'))
264 ui.status(_('(use "backout --merge" '
264 ui.status(_('(use "backout --merge" '
265 'if you want to auto-merge)\n'))
265 'if you want to auto-merge)\n'))
266
266
267 def bisect(ui, repo, rev=None, extra=None, command=None,
267 def bisect(ui, repo, rev=None, extra=None, command=None,
268 reset=None, good=None, bad=None, skip=None, noupdate=None):
268 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 """subdivision search of changesets
269 """subdivision search of changesets
270
270
271 This command helps to find changesets which introduce problems. To
271 This command helps to find changesets which introduce problems. To
272 use, mark the earliest changeset you know exhibits the problem as
272 use, mark the earliest changeset you know exhibits the problem as
273 bad, then mark the latest changeset which is free from the problem
273 bad, then mark the latest changeset which is free from the problem
274 as good. Bisect will update your working directory to a revision
274 as good. Bisect will update your working directory to a revision
275 for testing (unless the -U/--noupdate option is specified). Once
275 for testing (unless the -U/--noupdate option is specified). Once
276 you have performed tests, mark the working directory as bad or
276 you have performed tests, mark the working directory as bad or
277 good and bisect will either update to another candidate changeset
277 good and bisect will either update to another candidate changeset
278 or announce that it has found the bad revision.
278 or announce that it has found the bad revision.
279
279
280 As a shortcut, you can also use the revision argument to mark a
280 As a shortcut, you can also use the revision argument to mark a
281 revision as good or bad without checking it out first.
281 revision as good or bad without checking it out first.
282
282
283 If you supply a command it will be used for automatic bisection.
283 If you supply a command it will be used for automatic bisection.
284 Its exit status will be used as flag to mark revision as bad or
284 Its exit status will be used as flag to mark revision as bad or
285 good. In case exit status is 0 the revision is marked as good, 125
285 good. In case exit status is 0 the revision is marked as good, 125
286 - skipped, 127 (command not found) - bisection will be aborted;
286 - skipped, 127 (command not found) - bisection will be aborted;
287 any other status bigger than 0 will mark revision as bad.
287 any other status bigger than 0 will mark revision as bad.
288 """
288 """
289 def print_result(nodes, good):
289 def print_result(nodes, good):
290 displayer = cmdutil.show_changeset(ui, repo, {})
290 displayer = cmdutil.show_changeset(ui, repo, {})
291 if len(nodes) == 1:
291 if len(nodes) == 1:
292 # narrowed it down to a single revision
292 # narrowed it down to a single revision
293 if good:
293 if good:
294 ui.write(_("The first good revision is:\n"))
294 ui.write(_("The first good revision is:\n"))
295 else:
295 else:
296 ui.write(_("The first bad revision is:\n"))
296 ui.write(_("The first bad revision is:\n"))
297 displayer.show(repo[nodes[0]])
297 displayer.show(repo[nodes[0]])
298 else:
298 else:
299 # multiple possible revisions
299 # multiple possible revisions
300 if good:
300 if good:
301 ui.write(_("Due to skipped revisions, the first "
301 ui.write(_("Due to skipped revisions, the first "
302 "good revision could be any of:\n"))
302 "good revision could be any of:\n"))
303 else:
303 else:
304 ui.write(_("Due to skipped revisions, the first "
304 ui.write(_("Due to skipped revisions, the first "
305 "bad revision could be any of:\n"))
305 "bad revision could be any of:\n"))
306 for n in nodes:
306 for n in nodes:
307 displayer.show(repo[n])
307 displayer.show(repo[n])
308
308
309 def check_state(state, interactive=True):
309 def check_state(state, interactive=True):
310 if not state['good'] or not state['bad']:
310 if not state['good'] or not state['bad']:
311 if (good or bad or skip or reset) and interactive:
311 if (good or bad or skip or reset) and interactive:
312 return
312 return
313 if not state['good']:
313 if not state['good']:
314 raise util.Abort(_('cannot bisect (no known good revisions)'))
314 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 else:
315 else:
316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 return True
317 return True
318
318
319 # backward compatibility
319 # backward compatibility
320 if rev in "good bad reset init".split():
320 if rev in "good bad reset init".split():
321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 cmd, rev, extra = rev, extra, None
322 cmd, rev, extra = rev, extra, None
323 if cmd == "good":
323 if cmd == "good":
324 good = True
324 good = True
325 elif cmd == "bad":
325 elif cmd == "bad":
326 bad = True
326 bad = True
327 else:
327 else:
328 reset = True
328 reset = True
329 elif extra or good + bad + skip + reset + bool(command) > 1:
329 elif extra or good + bad + skip + reset + bool(command) > 1:
330 raise util.Abort(_('incompatible arguments'))
330 raise util.Abort(_('incompatible arguments'))
331
331
332 if reset:
332 if reset:
333 p = repo.join("bisect.state")
333 p = repo.join("bisect.state")
334 if os.path.exists(p):
334 if os.path.exists(p):
335 os.unlink(p)
335 os.unlink(p)
336 return
336 return
337
337
338 state = hbisect.load_state(repo)
338 state = hbisect.load_state(repo)
339
339
340 if command:
340 if command:
341 commandpath = util.find_exe(command)
341 commandpath = util.find_exe(command)
342 changesets = 1
342 changesets = 1
343 try:
343 try:
344 while changesets:
344 while changesets:
345 # update state
345 # update state
346 status = os.spawnl(os.P_WAIT, commandpath, commandpath)
346 status = os.spawnl(os.P_WAIT, commandpath, commandpath)
347 if status == 125:
347 if status == 125:
348 transition = "skip"
348 transition = "skip"
349 elif status == 0:
349 elif status == 0:
350 transition = "good"
350 transition = "good"
351 # status < 0 means process was killed
351 # status < 0 means process was killed
352 elif status == 127:
352 elif status == 127:
353 raise util.Abort(_("failed to execute %s") % command)
353 raise util.Abort(_("failed to execute %s") % command)
354 elif status < 0:
354 elif status < 0:
355 raise util.Abort(_("%s killed") % command)
355 raise util.Abort(_("%s killed") % command)
356 else:
356 else:
357 transition = "bad"
357 transition = "bad"
358 node = repo.lookup(rev or '.')
358 node = repo.lookup(rev or '.')
359 state[transition].append(node)
359 state[transition].append(node)
360 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
360 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
361 check_state(state, interactive=False)
361 check_state(state, interactive=False)
362 # bisect
362 # bisect
363 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
363 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
364 # update to next check
364 # update to next check
365 cmdutil.bail_if_changed(repo)
365 cmdutil.bail_if_changed(repo)
366 hg.clean(repo, nodes[0], show_stats=False)
366 hg.clean(repo, nodes[0], show_stats=False)
367 finally:
367 finally:
368 hbisect.save_state(repo, state)
368 hbisect.save_state(repo, state)
369 return print_result(nodes, not status)
369 return print_result(nodes, not status)
370
370
371 # update state
371 # update state
372 node = repo.lookup(rev or '.')
372 node = repo.lookup(rev or '.')
373 if good:
373 if good:
374 state['good'].append(node)
374 state['good'].append(node)
375 elif bad:
375 elif bad:
376 state['bad'].append(node)
376 state['bad'].append(node)
377 elif skip:
377 elif skip:
378 state['skip'].append(node)
378 state['skip'].append(node)
379
379
380 hbisect.save_state(repo, state)
380 hbisect.save_state(repo, state)
381
381
382 if not check_state(state):
382 if not check_state(state):
383 return
383 return
384
384
385 # actually bisect
385 # actually bisect
386 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
386 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387 if changesets == 0:
387 if changesets == 0:
388 print_result(nodes, good)
388 print_result(nodes, good)
389 else:
389 else:
390 assert len(nodes) == 1 # only a single node can be tested next
390 assert len(nodes) == 1 # only a single node can be tested next
391 node = nodes[0]
391 node = nodes[0]
392 # compute the approximate number of remaining tests
392 # compute the approximate number of remaining tests
393 tests, size = 0, 2
393 tests, size = 0, 2
394 while size <= changesets:
394 while size <= changesets:
395 tests, size = tests + 1, size * 2
395 tests, size = tests + 1, size * 2
396 rev = repo.changelog.rev(node)
396 rev = repo.changelog.rev(node)
397 ui.write(_("Testing changeset %s:%s "
397 ui.write(_("Testing changeset %s:%s "
398 "(%s changesets remaining, ~%s tests)\n")
398 "(%s changesets remaining, ~%s tests)\n")
399 % (rev, short(node), changesets, tests))
399 % (rev, short(node), changesets, tests))
400 if not noupdate:
400 if not noupdate:
401 cmdutil.bail_if_changed(repo)
401 cmdutil.bail_if_changed(repo)
402 return hg.clean(repo, node)
402 return hg.clean(repo, node)
403
403
404 def branch(ui, repo, label=None, **opts):
404 def branch(ui, repo, label=None, **opts):
405 """set or show the current branch name
405 """set or show the current branch name
406
406
407 With no argument, show the current branch name. With one argument,
407 With no argument, show the current branch name. With one argument,
408 set the working directory branch name (the branch does not exist
408 set the working directory branch name (the branch does not exist
409 in the repository until the next commit). It is recommended to use
409 in the repository until the next commit). It is recommended to use
410 the 'default' branch as your primary development branch.
410 the 'default' branch as your primary development branch.
411
411
412 Unless -f/--force is specified, branch will not let you set a
412 Unless -f/--force is specified, branch will not let you set a
413 branch name that shadows an existing branch.
413 branch name that shadows an existing branch.
414
414
415 Use -C/--clean to reset the working directory branch to that of
415 Use -C/--clean to reset the working directory branch to that of
416 the parent of the working directory, negating a previous branch
416 the parent of the working directory, negating a previous branch
417 change.
417 change.
418
418
419 Use the command 'hg update' to switch to an existing branch.
419 Use the command 'hg update' to switch to an existing branch.
420 """
420 """
421
421
422 if opts.get('clean'):
422 if opts.get('clean'):
423 label = repo[None].parents()[0].branch()
423 label = repo[None].parents()[0].branch()
424 repo.dirstate.setbranch(label)
424 repo.dirstate.setbranch(label)
425 ui.status(_('reset working directory to branch %s\n') % label)
425 ui.status(_('reset working directory to branch %s\n') % label)
426 elif label:
426 elif label:
427 if not opts.get('force') and label in repo.branchtags():
427 if not opts.get('force') and label in repo.branchtags():
428 if label not in [p.branch() for p in repo.parents()]:
428 if label not in [p.branch() for p in repo.parents()]:
429 raise util.Abort(_('a branch of the same name already exists'
429 raise util.Abort(_('a branch of the same name already exists'
430 ' (use --force to override)'))
430 ' (use --force to override)'))
431 repo.dirstate.setbranch(encoding.fromlocal(label))
431 repo.dirstate.setbranch(encoding.fromlocal(label))
432 ui.status(_('marked working directory as branch %s\n') % label)
432 ui.status(_('marked working directory as branch %s\n') % label)
433 else:
433 else:
434 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
434 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
435
435
436 def branches(ui, repo, active=False):
436 def branches(ui, repo, active=False):
437 """list repository named branches
437 """list repository named branches
438
438
439 List the repository's named branches, indicating which ones are
439 List the repository's named branches, indicating which ones are
440 inactive. If active is specified, only show active branches.
440 inactive. If active is specified, only show active branches.
441
441
442 A branch is considered active if it contains repository heads.
442 A branch is considered active if it contains repository heads.
443
443
444 Use the command 'hg update' to switch to an existing branch.
444 Use the command 'hg update' to switch to an existing branch.
445 """
445 """
446 hexfunc = ui.debugflag and hex or short
446 hexfunc = ui.debugflag and hex or short
447 activebranches = [encoding.tolocal(repo[n].branch())
447 activebranches = [encoding.tolocal(repo[n].branch())
448 for n in repo.heads(closed=False)]
448 for n in repo.heads(closed=False)]
449 branches = util.sort([(tag in activebranches, repo.changelog.rev(node), tag)
449 branches = util.sort([(tag in activebranches, repo.changelog.rev(node), tag)
450 for tag, node in repo.branchtags().items()])
450 for tag, node in repo.branchtags().items()])
451 branches.reverse()
451 branches.reverse()
452
452
453 for isactive, node, tag in branches:
453 for isactive, node, tag in branches:
454 if (not active) or isactive:
454 if (not active) or isactive:
455 if ui.quiet:
455 if ui.quiet:
456 ui.write("%s\n" % tag)
456 ui.write("%s\n" % tag)
457 else:
457 else:
458 hn = repo.lookup(node)
458 hn = repo.lookup(node)
459 if isactive:
459 if isactive:
460 notice = ''
460 notice = ''
461 elif hn not in repo.branchheads(tag, closed=False):
461 elif hn not in repo.branchheads(tag, closed=False):
462 notice = ' (closed)'
462 notice = ' (closed)'
463 else:
463 else:
464 notice = ' (inactive)'
464 notice = ' (inactive)'
465 rev = str(node).rjust(31 - encoding.colwidth(tag))
465 rev = str(node).rjust(31 - encoding.colwidth(tag))
466 data = tag, rev, hexfunc(hn), notice
466 data = tag, rev, hexfunc(hn), notice
467 ui.write("%s %s:%s%s\n" % data)
467 ui.write("%s %s:%s%s\n" % data)
468
468
469 def bundle(ui, repo, fname, dest=None, **opts):
469 def bundle(ui, repo, fname, dest=None, **opts):
470 """create a changegroup file
470 """create a changegroup file
471
471
472 Generate a compressed changegroup file collecting changesets not
472 Generate a compressed changegroup file collecting changesets not
473 known to be in another repository.
473 known to be in another repository.
474
474
475 If no destination repository is specified the destination is
475 If no destination repository is specified the destination is
476 assumed to have all the nodes specified by one or more --base
476 assumed to have all the nodes specified by one or more --base
477 parameters. To create a bundle containing all changesets, use
477 parameters. To create a bundle containing all changesets, use
478 -a/--all (or --base null). To change the compression method
478 -a/--all (or --base null). To change the compression method
479 applied, use the -t/--type option (by default, bundles are
479 applied, use the -t/--type option (by default, bundles are
480 compressed using bz2).
480 compressed using bz2).
481
481
482 The bundle file can then be transferred using conventional means
482 The bundle file can then be transferred using conventional means
483 and applied to another repository with the unbundle or pull
483 and applied to another repository with the unbundle or pull
484 command. This is useful when direct push and pull are not
484 command. This is useful when direct push and pull are not
485 available or when exporting an entire repository is undesirable.
485 available or when exporting an entire repository is undesirable.
486
486
487 Applying bundles preserves all changeset contents including
487 Applying bundles preserves all changeset contents including
488 permissions, copy/rename information, and revision history.
488 permissions, copy/rename information, and revision history.
489 """
489 """
490 revs = opts.get('rev') or None
490 revs = opts.get('rev') or None
491 if revs:
491 if revs:
492 revs = [repo.lookup(rev) for rev in revs]
492 revs = [repo.lookup(rev) for rev in revs]
493 if opts.get('all'):
493 if opts.get('all'):
494 base = ['null']
494 base = ['null']
495 else:
495 else:
496 base = opts.get('base')
496 base = opts.get('base')
497 if base:
497 if base:
498 if dest:
498 if dest:
499 raise util.Abort(_("--base is incompatible with specifiying "
499 raise util.Abort(_("--base is incompatible with specifiying "
500 "a destination"))
500 "a destination"))
501 base = [repo.lookup(rev) for rev in base]
501 base = [repo.lookup(rev) for rev in base]
502 # create the right base
502 # create the right base
503 # XXX: nodesbetween / changegroup* should be "fixed" instead
503 # XXX: nodesbetween / changegroup* should be "fixed" instead
504 o = []
504 o = []
505 has = {nullid: None}
505 has = {nullid: None}
506 for n in base:
506 for n in base:
507 has.update(repo.changelog.reachable(n))
507 has.update(repo.changelog.reachable(n))
508 if revs:
508 if revs:
509 visit = list(revs)
509 visit = list(revs)
510 else:
510 else:
511 visit = repo.changelog.heads()
511 visit = repo.changelog.heads()
512 seen = {}
512 seen = {}
513 while visit:
513 while visit:
514 n = visit.pop(0)
514 n = visit.pop(0)
515 parents = [p for p in repo.changelog.parents(n) if p not in has]
515 parents = [p for p in repo.changelog.parents(n) if p not in has]
516 if len(parents) == 0:
516 if len(parents) == 0:
517 o.insert(0, n)
517 o.insert(0, n)
518 else:
518 else:
519 for p in parents:
519 for p in parents:
520 if p not in seen:
520 if p not in seen:
521 seen[p] = 1
521 seen[p] = 1
522 visit.append(p)
522 visit.append(p)
523 else:
523 else:
524 cmdutil.setremoteconfig(ui, opts)
525 dest, revs, checkout = hg.parseurl(
524 dest, revs, checkout = hg.parseurl(
526 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
525 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
527 other = hg.repository(ui, dest)
526 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
528 o = repo.findoutgoing(other, force=opts.get('force'))
527 o = repo.findoutgoing(other, force=opts.get('force'))
529
528
530 if revs:
529 if revs:
531 cg = repo.changegroupsubset(o, revs, 'bundle')
530 cg = repo.changegroupsubset(o, revs, 'bundle')
532 else:
531 else:
533 cg = repo.changegroup(o, 'bundle')
532 cg = repo.changegroup(o, 'bundle')
534
533
535 bundletype = opts.get('type', 'bzip2').lower()
534 bundletype = opts.get('type', 'bzip2').lower()
536 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
535 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
537 bundletype = btypes.get(bundletype)
536 bundletype = btypes.get(bundletype)
538 if bundletype not in changegroup.bundletypes:
537 if bundletype not in changegroup.bundletypes:
539 raise util.Abort(_('unknown bundle type specified with --type'))
538 raise util.Abort(_('unknown bundle type specified with --type'))
540
539
541 changegroup.writebundle(cg, fname, bundletype)
540 changegroup.writebundle(cg, fname, bundletype)
542
541
543 def cat(ui, repo, file1, *pats, **opts):
542 def cat(ui, repo, file1, *pats, **opts):
544 """output the current or given revision of files
543 """output the current or given revision of files
545
544
546 Print the specified files as they were at the given revision. If
545 Print the specified files as they were at the given revision. If
547 no revision is given, the parent of the working directory is used,
546 no revision is given, the parent of the working directory is used,
548 or tip if no revision is checked out.
547 or tip if no revision is checked out.
549
548
550 Output may be to a file, in which case the name of the file is
549 Output may be to a file, in which case the name of the file is
551 given using a format string. The formatting rules are the same as
550 given using a format string. The formatting rules are the same as
552 for the export command, with the following additions:
551 for the export command, with the following additions:
553
552
554 %s basename of file being printed
553 %s basename of file being printed
555 %d dirname of file being printed, or '.' if in repository root
554 %d dirname of file being printed, or '.' if in repository root
556 %p root-relative path name of file being printed
555 %p root-relative path name of file being printed
557 """
556 """
558 ctx = repo[opts.get('rev')]
557 ctx = repo[opts.get('rev')]
559 err = 1
558 err = 1
560 m = cmdutil.match(repo, (file1,) + pats, opts)
559 m = cmdutil.match(repo, (file1,) + pats, opts)
561 for abs in ctx.walk(m):
560 for abs in ctx.walk(m):
562 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
561 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
563 data = ctx[abs].data()
562 data = ctx[abs].data()
564 if opts.get('decode'):
563 if opts.get('decode'):
565 data = repo.wwritedata(abs, data)
564 data = repo.wwritedata(abs, data)
566 fp.write(data)
565 fp.write(data)
567 err = 0
566 err = 0
568 return err
567 return err
569
568
570 def clone(ui, source, dest=None, **opts):
569 def clone(ui, source, dest=None, **opts):
571 """make a copy of an existing repository
570 """make a copy of an existing repository
572
571
573 Create a copy of an existing repository in a new directory.
572 Create a copy of an existing repository in a new directory.
574
573
575 If no destination directory name is specified, it defaults to the
574 If no destination directory name is specified, it defaults to the
576 basename of the source.
575 basename of the source.
577
576
578 The location of the source is added to the new repository's
577 The location of the source is added to the new repository's
579 .hg/hgrc file, as the default to be used for future pulls.
578 .hg/hgrc file, as the default to be used for future pulls.
580
579
581 If you use the -r/--rev option to clone up to a specific revision,
580 If you use the -r/--rev option to clone up to a specific revision,
582 no subsequent revisions (including subsequent tags) will be
581 no subsequent revisions (including subsequent tags) will be
583 present in the cloned repository. This option implies --pull, even
582 present in the cloned repository. This option implies --pull, even
584 on local repositories.
583 on local repositories.
585
584
586 By default, clone will check out the head of the 'default' branch.
585 By default, clone will check out the head of the 'default' branch.
587 If the -U/--noupdate option is used, the new clone will contain
586 If the -U/--noupdate option is used, the new clone will contain
588 only a repository (.hg) and no working copy (the working copy
587 only a repository (.hg) and no working copy (the working copy
589 parent is the null revision).
588 parent is the null revision).
590
589
591 See 'hg help urls' for valid source format details.
590 See 'hg help urls' for valid source format details.
592
591
593 It is possible to specify an ssh:// URL as the destination, but no
592 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.
593 .hg/hgrc and working directory will be created on the remote side.
595 Look at the help text for URLs for important details about ssh://
594 Look at the help text for URLs for important details about ssh://
596 URLs.
595 URLs.
597
596
598 For efficiency, hardlinks are used for cloning whenever the source
597 For efficiency, hardlinks are used for cloning whenever the source
599 and destination are on the same filesystem (note this applies only
598 and destination are on the same filesystem (note this applies only
600 to the repository data, not to the checked out files). Some
599 to the repository data, not to the checked out files). Some
601 filesystems, such as AFS, implement hardlinking incorrectly, but
600 filesystems, such as AFS, implement hardlinking incorrectly, but
602 do not report errors. In these cases, use the --pull option to
601 do not report errors. In these cases, use the --pull option to
603 avoid hardlinking.
602 avoid hardlinking.
604
603
605 In some cases, you can clone repositories and checked out files
604 In some cases, you can clone repositories and checked out files
606 using full hardlinks with
605 using full hardlinks with
607
606
608 $ cp -al REPO REPOCLONE
607 $ cp -al REPO REPOCLONE
609
608
610 This is the fastest way to clone, but it is not always safe. The
609 This is the fastest way to clone, but it is not always safe. The
611 operation is not atomic (making sure REPO is not modified during
610 operation is not atomic (making sure REPO is not modified during
612 the operation is up to you) and you have to make sure your editor
611 the operation is up to you) and you have to make sure your editor
613 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
612 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
614 this is not compatible with certain extensions that place their
613 this is not compatible with certain extensions that place their
615 metadata under the .hg directory, such as mq.
614 metadata under the .hg directory, such as mq.
616
615
617 """
616 """
618 cmdutil.setremoteconfig(ui, opts)
617 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
619 hg.clone(ui, source, dest,
620 pull=opts.get('pull'),
618 pull=opts.get('pull'),
621 stream=opts.get('uncompressed'),
619 stream=opts.get('uncompressed'),
622 rev=opts.get('rev'),
620 rev=opts.get('rev'),
623 update=not opts.get('noupdate'))
621 update=not opts.get('noupdate'))
624
622
625 def commit(ui, repo, *pats, **opts):
623 def commit(ui, repo, *pats, **opts):
626 """commit the specified files or all outstanding changes
624 """commit the specified files or all outstanding changes
627
625
628 Commit changes to the given files into the repository. Unlike a
626 Commit changes to the given files into the repository. Unlike a
629 centralized RCS, this operation is a local operation. See hg push
627 centralized RCS, this operation is a local operation. See hg push
630 for means to actively distribute your changes.
628 for means to actively distribute your changes.
631
629
632 If a list of files is omitted, all changes reported by "hg status"
630 If a list of files is omitted, all changes reported by "hg status"
633 will be committed.
631 will be committed.
634
632
635 If you are committing the result of a merge, do not provide any
633 If you are committing the result of a merge, do not provide any
636 file names or -I/-X filters.
634 file names or -I/-X filters.
637
635
638 If no commit message is specified, the configured editor is
636 If no commit message is specified, the configured editor is
639 started to prompt you for a message.
637 started to prompt you for a message.
640
638
641 See 'hg help dates' for a list of formats valid for -d/--date.
639 See 'hg help dates' for a list of formats valid for -d/--date.
642 """
640 """
643 extra = {}
641 extra = {}
644 if opts.get('close_branch'):
642 if opts.get('close_branch'):
645 extra['close'] = 1
643 extra['close'] = 1
646 def commitfunc(ui, repo, message, match, opts):
644 def commitfunc(ui, repo, message, match, opts):
647 return repo.commit(match.files(), message, opts.get('user'),
645 return repo.commit(match.files(), message, opts.get('user'),
648 opts.get('date'), match, force_editor=opts.get('force_editor'),
646 opts.get('date'), match, force_editor=opts.get('force_editor'),
649 extra=extra)
647 extra=extra)
650
648
651 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
649 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
652 if not node:
650 if not node:
653 return
651 return
654 cl = repo.changelog
652 cl = repo.changelog
655 rev = cl.rev(node)
653 rev = cl.rev(node)
656 parents = cl.parentrevs(rev)
654 parents = cl.parentrevs(rev)
657 if rev - 1 in parents:
655 if rev - 1 in parents:
658 # one of the parents was the old tip
656 # one of the parents was the old tip
659 pass
657 pass
660 elif (parents == (nullrev, nullrev) or
658 elif (parents == (nullrev, nullrev) or
661 len(cl.heads(cl.node(parents[0]))) > 1 and
659 len(cl.heads(cl.node(parents[0]))) > 1 and
662 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
660 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
663 ui.status(_('created new head\n'))
661 ui.status(_('created new head\n'))
664
662
665 if ui.debugflag:
663 if ui.debugflag:
666 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
664 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
667 elif ui.verbose:
665 elif ui.verbose:
668 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
666 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
669
667
670 def copy(ui, repo, *pats, **opts):
668 def copy(ui, repo, *pats, **opts):
671 """mark files as copied for the next commit
669 """mark files as copied for the next commit
672
670
673 Mark dest as having copies of source files. If dest is a
671 Mark dest as having copies of source files. If dest is a
674 directory, copies are put in that directory. If dest is a file,
672 directory, copies are put in that directory. If dest is a file,
675 the source must be a single file.
673 the source must be a single file.
676
674
677 By default, this command copies the contents of files as they
675 By default, this command copies the contents of files as they
678 stand in the working directory. If invoked with -A/--after, the
676 stand in the working directory. If invoked with -A/--after, the
679 operation is recorded, but no copying is performed.
677 operation is recorded, but no copying is performed.
680
678
681 This command takes effect with the next commit. To undo a copy
679 This command takes effect with the next commit. To undo a copy
682 before that, see hg revert.
680 before that, see hg revert.
683 """
681 """
684 wlock = repo.wlock(False)
682 wlock = repo.wlock(False)
685 try:
683 try:
686 return cmdutil.copy(ui, repo, pats, opts)
684 return cmdutil.copy(ui, repo, pats, opts)
687 finally:
685 finally:
688 wlock.release()
686 wlock.release()
689
687
690 def debugancestor(ui, repo, *args):
688 def debugancestor(ui, repo, *args):
691 """find the ancestor revision of two revisions in a given index"""
689 """find the ancestor revision of two revisions in a given index"""
692 if len(args) == 3:
690 if len(args) == 3:
693 index, rev1, rev2 = args
691 index, rev1, rev2 = args
694 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
692 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
695 lookup = r.lookup
693 lookup = r.lookup
696 elif len(args) == 2:
694 elif len(args) == 2:
697 if not repo:
695 if not repo:
698 raise util.Abort(_("There is no Mercurial repository here "
696 raise util.Abort(_("There is no Mercurial repository here "
699 "(.hg not found)"))
697 "(.hg not found)"))
700 rev1, rev2 = args
698 rev1, rev2 = args
701 r = repo.changelog
699 r = repo.changelog
702 lookup = repo.lookup
700 lookup = repo.lookup
703 else:
701 else:
704 raise util.Abort(_('either two or three arguments required'))
702 raise util.Abort(_('either two or three arguments required'))
705 a = r.ancestor(lookup(rev1), lookup(rev2))
703 a = r.ancestor(lookup(rev1), lookup(rev2))
706 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
704 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
707
705
708 def debugcommands(ui, cmd='', *args):
706 def debugcommands(ui, cmd='', *args):
709 for cmd, vals in util.sort(table.iteritems()):
707 for cmd, vals in util.sort(table.iteritems()):
710 cmd = cmd.split('|')[0].strip('^')
708 cmd = cmd.split('|')[0].strip('^')
711 opts = ', '.join([i[1] for i in vals[1]])
709 opts = ', '.join([i[1] for i in vals[1]])
712 ui.write('%s: %s\n' % (cmd, opts))
710 ui.write('%s: %s\n' % (cmd, opts))
713
711
714 def debugcomplete(ui, cmd='', **opts):
712 def debugcomplete(ui, cmd='', **opts):
715 """returns the completion list associated with the given command"""
713 """returns the completion list associated with the given command"""
716
714
717 if opts.get('options'):
715 if opts.get('options'):
718 options = []
716 options = []
719 otables = [globalopts]
717 otables = [globalopts]
720 if cmd:
718 if cmd:
721 aliases, entry = cmdutil.findcmd(cmd, table, False)
719 aliases, entry = cmdutil.findcmd(cmd, table, False)
722 otables.append(entry[1])
720 otables.append(entry[1])
723 for t in otables:
721 for t in otables:
724 for o in t:
722 for o in t:
725 if o[0]:
723 if o[0]:
726 options.append('-%s' % o[0])
724 options.append('-%s' % o[0])
727 options.append('--%s' % o[1])
725 options.append('--%s' % o[1])
728 ui.write("%s\n" % "\n".join(options))
726 ui.write("%s\n" % "\n".join(options))
729 return
727 return
730
728
731 cmdlist = cmdutil.findpossible(cmd, table)
729 cmdlist = cmdutil.findpossible(cmd, table)
732 if ui.verbose:
730 if ui.verbose:
733 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
731 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
734 ui.write("%s\n" % "\n".join(util.sort(cmdlist)))
732 ui.write("%s\n" % "\n".join(util.sort(cmdlist)))
735
733
736 def debugfsinfo(ui, path = "."):
734 def debugfsinfo(ui, path = "."):
737 file('.debugfsinfo', 'w').write('')
735 file('.debugfsinfo', 'w').write('')
738 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
736 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
739 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
737 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
740 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
738 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
741 and 'yes' or 'no'))
739 and 'yes' or 'no'))
742 os.unlink('.debugfsinfo')
740 os.unlink('.debugfsinfo')
743
741
744 def debugrebuildstate(ui, repo, rev="tip"):
742 def debugrebuildstate(ui, repo, rev="tip"):
745 """rebuild the dirstate as it would look like for the given revision"""
743 """rebuild the dirstate as it would look like for the given revision"""
746 ctx = repo[rev]
744 ctx = repo[rev]
747 wlock = repo.wlock()
745 wlock = repo.wlock()
748 try:
746 try:
749 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
747 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
750 finally:
748 finally:
751 wlock.release()
749 wlock.release()
752
750
753 def debugcheckstate(ui, repo):
751 def debugcheckstate(ui, repo):
754 """validate the correctness of the current dirstate"""
752 """validate the correctness of the current dirstate"""
755 parent1, parent2 = repo.dirstate.parents()
753 parent1, parent2 = repo.dirstate.parents()
756 m1 = repo[parent1].manifest()
754 m1 = repo[parent1].manifest()
757 m2 = repo[parent2].manifest()
755 m2 = repo[parent2].manifest()
758 errors = 0
756 errors = 0
759 for f in repo.dirstate:
757 for f in repo.dirstate:
760 state = repo.dirstate[f]
758 state = repo.dirstate[f]
761 if state in "nr" and f not in m1:
759 if state in "nr" and f not in m1:
762 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
760 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
763 errors += 1
761 errors += 1
764 if state in "a" and f in m1:
762 if state in "a" and f in m1:
765 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
763 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
766 errors += 1
764 errors += 1
767 if state in "m" and f not in m1 and f not in m2:
765 if state in "m" and f not in m1 and f not in m2:
768 ui.warn(_("%s in state %s, but not in either manifest\n") %
766 ui.warn(_("%s in state %s, but not in either manifest\n") %
769 (f, state))
767 (f, state))
770 errors += 1
768 errors += 1
771 for f in m1:
769 for f in m1:
772 state = repo.dirstate[f]
770 state = repo.dirstate[f]
773 if state not in "nrm":
771 if state not in "nrm":
774 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
772 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
775 errors += 1
773 errors += 1
776 if errors:
774 if errors:
777 error = _(".hg/dirstate inconsistent with current parent's manifest")
775 error = _(".hg/dirstate inconsistent with current parent's manifest")
778 raise util.Abort(error)
776 raise util.Abort(error)
779
777
780 def showconfig(ui, repo, *values, **opts):
778 def showconfig(ui, repo, *values, **opts):
781 """show combined config settings from all hgrc files
779 """show combined config settings from all hgrc files
782
780
783 With no args, print names and values of all config items.
781 With no args, print names and values of all config items.
784
782
785 With one arg of the form section.name, print just the value of
783 With one arg of the form section.name, print just the value of
786 that config item.
784 that config item.
787
785
788 With multiple args, print names and values of all config items
786 With multiple args, print names and values of all config items
789 with matching section names."""
787 with matching section names."""
790
788
791 untrusted = bool(opts.get('untrusted'))
789 untrusted = bool(opts.get('untrusted'))
792 if values:
790 if values:
793 if len([v for v in values if '.' in v]) > 1:
791 if len([v for v in values if '.' in v]) > 1:
794 raise util.Abort(_('only one config item permitted'))
792 raise util.Abort(_('only one config item permitted'))
795 for section, name, value in ui.walkconfig(untrusted=untrusted):
793 for section, name, value in ui.walkconfig(untrusted=untrusted):
796 sectname = section + '.' + name
794 sectname = section + '.' + name
797 if values:
795 if values:
798 for v in values:
796 for v in values:
799 if v == section:
797 if v == section:
800 ui.debug('%s: ' %
798 ui.debug('%s: ' %
801 ui.configsource(section, name, untrusted))
799 ui.configsource(section, name, untrusted))
802 ui.write('%s=%s\n' % (sectname, value))
800 ui.write('%s=%s\n' % (sectname, value))
803 elif v == sectname:
801 elif v == sectname:
804 ui.debug('%s: ' %
802 ui.debug('%s: ' %
805 ui.configsource(section, name, untrusted))
803 ui.configsource(section, name, untrusted))
806 ui.write(value, '\n')
804 ui.write(value, '\n')
807 else:
805 else:
808 ui.debug('%s: ' %
806 ui.debug('%s: ' %
809 ui.configsource(section, name, untrusted))
807 ui.configsource(section, name, untrusted))
810 ui.write('%s=%s\n' % (sectname, value))
808 ui.write('%s=%s\n' % (sectname, value))
811
809
812 def debugsetparents(ui, repo, rev1, rev2=None):
810 def debugsetparents(ui, repo, rev1, rev2=None):
813 """manually set the parents of the current working directory
811 """manually set the parents of the current working directory
814
812
815 This is useful for writing repository conversion tools, but should
813 This is useful for writing repository conversion tools, but should
816 be used with care.
814 be used with care.
817 """
815 """
818
816
819 if not rev2:
817 if not rev2:
820 rev2 = hex(nullid)
818 rev2 = hex(nullid)
821
819
822 wlock = repo.wlock()
820 wlock = repo.wlock()
823 try:
821 try:
824 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
822 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
825 finally:
823 finally:
826 wlock.release()
824 wlock.release()
827
825
828 def debugstate(ui, repo, nodates=None):
826 def debugstate(ui, repo, nodates=None):
829 """show the contents of the current dirstate"""
827 """show the contents of the current dirstate"""
830 timestr = ""
828 timestr = ""
831 showdate = not nodates
829 showdate = not nodates
832 for file_, ent in util.sort(repo.dirstate._map.iteritems()):
830 for file_, ent in util.sort(repo.dirstate._map.iteritems()):
833 if showdate:
831 if showdate:
834 if ent[3] == -1:
832 if ent[3] == -1:
835 # Pad or slice to locale representation
833 # Pad or slice to locale representation
836 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
834 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
837 timestr = 'unset'
835 timestr = 'unset'
838 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
836 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
839 else:
837 else:
840 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
838 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
841 if ent[1] & 020000:
839 if ent[1] & 020000:
842 mode = 'lnk'
840 mode = 'lnk'
843 else:
841 else:
844 mode = '%3o' % (ent[1] & 0777)
842 mode = '%3o' % (ent[1] & 0777)
845 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
843 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
846 for f in repo.dirstate.copies():
844 for f in repo.dirstate.copies():
847 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
845 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
848
846
849 def debugdata(ui, file_, rev):
847 def debugdata(ui, file_, rev):
850 """dump the contents of a data file revision"""
848 """dump the contents of a data file revision"""
851 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
849 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
852 try:
850 try:
853 ui.write(r.revision(r.lookup(rev)))
851 ui.write(r.revision(r.lookup(rev)))
854 except KeyError:
852 except KeyError:
855 raise util.Abort(_('invalid revision identifier %s') % rev)
853 raise util.Abort(_('invalid revision identifier %s') % rev)
856
854
857 def debugdate(ui, date, range=None, **opts):
855 def debugdate(ui, date, range=None, **opts):
858 """parse and display a date"""
856 """parse and display a date"""
859 if opts["extended"]:
857 if opts["extended"]:
860 d = util.parsedate(date, util.extendeddateformats)
858 d = util.parsedate(date, util.extendeddateformats)
861 else:
859 else:
862 d = util.parsedate(date)
860 d = util.parsedate(date)
863 ui.write("internal: %s %s\n" % d)
861 ui.write("internal: %s %s\n" % d)
864 ui.write("standard: %s\n" % util.datestr(d))
862 ui.write("standard: %s\n" % util.datestr(d))
865 if range:
863 if range:
866 m = util.matchdate(range)
864 m = util.matchdate(range)
867 ui.write("match: %s\n" % m(d[0]))
865 ui.write("match: %s\n" % m(d[0]))
868
866
869 def debugindex(ui, file_):
867 def debugindex(ui, file_):
870 """dump the contents of an index file"""
868 """dump the contents of an index file"""
871 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
869 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
872 ui.write(" rev offset length base linkrev"
870 ui.write(" rev offset length base linkrev"
873 " nodeid p1 p2\n")
871 " nodeid p1 p2\n")
874 for i in r:
872 for i in r:
875 node = r.node(i)
873 node = r.node(i)
876 try:
874 try:
877 pp = r.parents(node)
875 pp = r.parents(node)
878 except:
876 except:
879 pp = [nullid, nullid]
877 pp = [nullid, nullid]
880 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
878 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
881 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
879 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
882 short(node), short(pp[0]), short(pp[1])))
880 short(node), short(pp[0]), short(pp[1])))
883
881
884 def debugindexdot(ui, file_):
882 def debugindexdot(ui, file_):
885 """dump an index DAG as a .dot file"""
883 """dump an index DAG as a .dot file"""
886 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
884 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
887 ui.write("digraph G {\n")
885 ui.write("digraph G {\n")
888 for i in r:
886 for i in r:
889 node = r.node(i)
887 node = r.node(i)
890 pp = r.parents(node)
888 pp = r.parents(node)
891 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
889 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
892 if pp[1] != nullid:
890 if pp[1] != nullid:
893 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
891 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
894 ui.write("}\n")
892 ui.write("}\n")
895
893
896 def debuginstall(ui):
894 def debuginstall(ui):
897 '''test Mercurial installation'''
895 '''test Mercurial installation'''
898
896
899 def writetemp(contents):
897 def writetemp(contents):
900 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
898 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
901 f = os.fdopen(fd, "wb")
899 f = os.fdopen(fd, "wb")
902 f.write(contents)
900 f.write(contents)
903 f.close()
901 f.close()
904 return name
902 return name
905
903
906 problems = 0
904 problems = 0
907
905
908 # encoding
906 # encoding
909 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
907 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
910 try:
908 try:
911 encoding.fromlocal("test")
909 encoding.fromlocal("test")
912 except util.Abort, inst:
910 except util.Abort, inst:
913 ui.write(" %s\n" % inst)
911 ui.write(" %s\n" % inst)
914 ui.write(_(" (check that your locale is properly set)\n"))
912 ui.write(_(" (check that your locale is properly set)\n"))
915 problems += 1
913 problems += 1
916
914
917 # compiled modules
915 # compiled modules
918 ui.status(_("Checking extensions...\n"))
916 ui.status(_("Checking extensions...\n"))
919 try:
917 try:
920 import bdiff, mpatch, base85
918 import bdiff, mpatch, base85
921 except Exception, inst:
919 except Exception, inst:
922 ui.write(" %s\n" % inst)
920 ui.write(" %s\n" % inst)
923 ui.write(_(" One or more extensions could not be found"))
921 ui.write(_(" One or more extensions could not be found"))
924 ui.write(_(" (check that you compiled the extensions)\n"))
922 ui.write(_(" (check that you compiled the extensions)\n"))
925 problems += 1
923 problems += 1
926
924
927 # templates
925 # templates
928 ui.status(_("Checking templates...\n"))
926 ui.status(_("Checking templates...\n"))
929 try:
927 try:
930 import templater
928 import templater
931 templater.templater(templater.templatepath("map-cmdline.default"))
929 templater.templater(templater.templatepath("map-cmdline.default"))
932 except Exception, inst:
930 except Exception, inst:
933 ui.write(" %s\n" % inst)
931 ui.write(" %s\n" % inst)
934 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
932 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
935 problems += 1
933 problems += 1
936
934
937 # patch
935 # patch
938 ui.status(_("Checking patch...\n"))
936 ui.status(_("Checking patch...\n"))
939 patchproblems = 0
937 patchproblems = 0
940 a = "1\n2\n3\n4\n"
938 a = "1\n2\n3\n4\n"
941 b = "1\n2\n3\ninsert\n4\n"
939 b = "1\n2\n3\ninsert\n4\n"
942 fa = writetemp(a)
940 fa = writetemp(a)
943 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
941 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
944 os.path.basename(fa))
942 os.path.basename(fa))
945 fd = writetemp(d)
943 fd = writetemp(d)
946
944
947 files = {}
945 files = {}
948 try:
946 try:
949 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
947 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
950 except util.Abort, e:
948 except util.Abort, e:
951 ui.write(_(" patch call failed:\n"))
949 ui.write(_(" patch call failed:\n"))
952 ui.write(" " + str(e) + "\n")
950 ui.write(" " + str(e) + "\n")
953 patchproblems += 1
951 patchproblems += 1
954 else:
952 else:
955 if list(files) != [os.path.basename(fa)]:
953 if list(files) != [os.path.basename(fa)]:
956 ui.write(_(" unexpected patch output!\n"))
954 ui.write(_(" unexpected patch output!\n"))
957 patchproblems += 1
955 patchproblems += 1
958 a = file(fa).read()
956 a = file(fa).read()
959 if a != b:
957 if a != b:
960 ui.write(_(" patch test failed!\n"))
958 ui.write(_(" patch test failed!\n"))
961 patchproblems += 1
959 patchproblems += 1
962
960
963 if patchproblems:
961 if patchproblems:
964 if ui.config('ui', 'patch'):
962 if ui.config('ui', 'patch'):
965 ui.write(_(" (Current patch tool may be incompatible with patch,"
963 ui.write(_(" (Current patch tool may be incompatible with patch,"
966 " or misconfigured. Please check your .hgrc file)\n"))
964 " or misconfigured. Please check your .hgrc file)\n"))
967 else:
965 else:
968 ui.write(_(" Internal patcher failure, please report this error"
966 ui.write(_(" Internal patcher failure, please report this error"
969 " to http://www.selenic.com/mercurial/bts\n"))
967 " to http://www.selenic.com/mercurial/bts\n"))
970 problems += patchproblems
968 problems += patchproblems
971
969
972 os.unlink(fa)
970 os.unlink(fa)
973 os.unlink(fd)
971 os.unlink(fd)
974
972
975 # editor
973 # editor
976 ui.status(_("Checking commit editor...\n"))
974 ui.status(_("Checking commit editor...\n"))
977 editor = ui.geteditor()
975 editor = ui.geteditor()
978 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
976 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
979 if not cmdpath:
977 if not cmdpath:
980 if editor == 'vi':
978 if editor == 'vi':
981 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
979 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
982 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
980 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
983 else:
981 else:
984 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
982 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
985 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
983 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
986 problems += 1
984 problems += 1
987
985
988 # check username
986 # check username
989 ui.status(_("Checking username...\n"))
987 ui.status(_("Checking username...\n"))
990 user = os.environ.get("HGUSER")
988 user = os.environ.get("HGUSER")
991 if user is None:
989 if user is None:
992 user = ui.config("ui", "username")
990 user = ui.config("ui", "username")
993 if user is None:
991 if user is None:
994 user = os.environ.get("EMAIL")
992 user = os.environ.get("EMAIL")
995 if not user:
993 if not user:
996 ui.warn(" ")
994 ui.warn(" ")
997 ui.username()
995 ui.username()
998 ui.write(_(" (specify a username in your .hgrc file)\n"))
996 ui.write(_(" (specify a username in your .hgrc file)\n"))
999
997
1000 if not problems:
998 if not problems:
1001 ui.status(_("No problems detected\n"))
999 ui.status(_("No problems detected\n"))
1002 else:
1000 else:
1003 ui.write(_("%s problems detected,"
1001 ui.write(_("%s problems detected,"
1004 " please check your install!\n") % problems)
1002 " please check your install!\n") % problems)
1005
1003
1006 return problems
1004 return problems
1007
1005
1008 def debugrename(ui, repo, file1, *pats, **opts):
1006 def debugrename(ui, repo, file1, *pats, **opts):
1009 """dump rename information"""
1007 """dump rename information"""
1010
1008
1011 ctx = repo[opts.get('rev')]
1009 ctx = repo[opts.get('rev')]
1012 m = cmdutil.match(repo, (file1,) + pats, opts)
1010 m = cmdutil.match(repo, (file1,) + pats, opts)
1013 for abs in ctx.walk(m):
1011 for abs in ctx.walk(m):
1014 fctx = ctx[abs]
1012 fctx = ctx[abs]
1015 o = fctx.filelog().renamed(fctx.filenode())
1013 o = fctx.filelog().renamed(fctx.filenode())
1016 rel = m.rel(abs)
1014 rel = m.rel(abs)
1017 if o:
1015 if o:
1018 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1016 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1019 else:
1017 else:
1020 ui.write(_("%s not renamed\n") % rel)
1018 ui.write(_("%s not renamed\n") % rel)
1021
1019
1022 def debugwalk(ui, repo, *pats, **opts):
1020 def debugwalk(ui, repo, *pats, **opts):
1023 """show how files match on given patterns"""
1021 """show how files match on given patterns"""
1024 m = cmdutil.match(repo, pats, opts)
1022 m = cmdutil.match(repo, pats, opts)
1025 items = list(repo.walk(m))
1023 items = list(repo.walk(m))
1026 if not items:
1024 if not items:
1027 return
1025 return
1028 fmt = 'f %%-%ds %%-%ds %%s' % (
1026 fmt = 'f %%-%ds %%-%ds %%s' % (
1029 max([len(abs) for abs in items]),
1027 max([len(abs) for abs in items]),
1030 max([len(m.rel(abs)) for abs in items]))
1028 max([len(m.rel(abs)) for abs in items]))
1031 for abs in items:
1029 for abs in items:
1032 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1030 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1033 ui.write("%s\n" % line.rstrip())
1031 ui.write("%s\n" % line.rstrip())
1034
1032
1035 def diff(ui, repo, *pats, **opts):
1033 def diff(ui, repo, *pats, **opts):
1036 """diff repository (or selected files)
1034 """diff repository (or selected files)
1037
1035
1038 Show differences between revisions for the specified files.
1036 Show differences between revisions for the specified files.
1039
1037
1040 Differences between files are shown using the unified diff format.
1038 Differences between files are shown using the unified diff format.
1041
1039
1042 NOTE: diff may generate unexpected results for merges, as it will
1040 NOTE: diff may generate unexpected results for merges, as it will
1043 default to comparing against the working directory's first parent
1041 default to comparing against the working directory's first parent
1044 changeset if no revisions are specified.
1042 changeset if no revisions are specified.
1045
1043
1046 When two revision arguments are given, then changes are shown
1044 When two revision arguments are given, then changes are shown
1047 between those revisions. If only one revision is specified then
1045 between those revisions. If only one revision is specified then
1048 that revision is compared to the working directory, and, when no
1046 that revision is compared to the working directory, and, when no
1049 revisions are specified, the working directory files are compared
1047 revisions are specified, the working directory files are compared
1050 to its parent.
1048 to its parent.
1051
1049
1052 Without the -a/--text option, diff will avoid generating diffs of
1050 Without the -a/--text option, diff will avoid generating diffs of
1053 files it detects as binary. With -a, diff will generate a diff
1051 files it detects as binary. With -a, diff will generate a diff
1054 anyway, probably with undesirable results.
1052 anyway, probably with undesirable results.
1055
1053
1056 Use the -g/--git option to generate diffs in the git extended diff
1054 Use the -g/--git option to generate diffs in the git extended diff
1057 format. For more information, read 'hg help diffs'.
1055 format. For more information, read 'hg help diffs'.
1058 """
1056 """
1059
1057
1060 revs = opts.get('rev')
1058 revs = opts.get('rev')
1061 change = opts.get('change')
1059 change = opts.get('change')
1062
1060
1063 if revs and change:
1061 if revs and change:
1064 msg = _('cannot specify --rev and --change at the same time')
1062 msg = _('cannot specify --rev and --change at the same time')
1065 raise util.Abort(msg)
1063 raise util.Abort(msg)
1066 elif change:
1064 elif change:
1067 node2 = repo.lookup(change)
1065 node2 = repo.lookup(change)
1068 node1 = repo[node2].parents()[0].node()
1066 node1 = repo[node2].parents()[0].node()
1069 else:
1067 else:
1070 node1, node2 = cmdutil.revpair(repo, revs)
1068 node1, node2 = cmdutil.revpair(repo, revs)
1071
1069
1072 m = cmdutil.match(repo, pats, opts)
1070 m = cmdutil.match(repo, pats, opts)
1073 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1071 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1074 for chunk in it:
1072 for chunk in it:
1075 repo.ui.write(chunk)
1073 repo.ui.write(chunk)
1076
1074
1077 def export(ui, repo, *changesets, **opts):
1075 def export(ui, repo, *changesets, **opts):
1078 """dump the header and diffs for one or more changesets
1076 """dump the header and diffs for one or more changesets
1079
1077
1080 Print the changeset header and diffs for one or more revisions.
1078 Print the changeset header and diffs for one or more revisions.
1081
1079
1082 The information shown in the changeset header is: author,
1080 The information shown in the changeset header is: author,
1083 changeset hash, parent(s) and commit comment.
1081 changeset hash, parent(s) and commit comment.
1084
1082
1085 NOTE: export may generate unexpected diff output for merge
1083 NOTE: export may generate unexpected diff output for merge
1086 changesets, as it will compare the merge changeset against its
1084 changesets, as it will compare the merge changeset against its
1087 first parent only.
1085 first parent only.
1088
1086
1089 Output may be to a file, in which case the name of the file is
1087 Output may be to a file, in which case the name of the file is
1090 given using a format string. The formatting rules are as follows:
1088 given using a format string. The formatting rules are as follows:
1091
1089
1092 %% literal "%" character
1090 %% literal "%" character
1093 %H changeset hash (40 bytes of hexadecimal)
1091 %H changeset hash (40 bytes of hexadecimal)
1094 %N number of patches being generated
1092 %N number of patches being generated
1095 %R changeset revision number
1093 %R changeset revision number
1096 %b basename of the exporting repository
1094 %b basename of the exporting repository
1097 %h short-form changeset hash (12 bytes of hexadecimal)
1095 %h short-form changeset hash (12 bytes of hexadecimal)
1098 %n zero-padded sequence number, starting at 1
1096 %n zero-padded sequence number, starting at 1
1099 %r zero-padded changeset revision number
1097 %r zero-padded changeset revision number
1100
1098
1101 Without the -a/--text option, export will avoid generating diffs
1099 Without the -a/--text option, export will avoid generating diffs
1102 of files it detects as binary. With -a, export will generate a
1100 of files it detects as binary. With -a, export will generate a
1103 diff anyway, probably with undesirable results.
1101 diff anyway, probably with undesirable results.
1104
1102
1105 Use the -g/--git option to generate diffs in the git extended diff
1103 Use the -g/--git option to generate diffs in the git extended diff
1106 format. Read the diffs help topic for more information.
1104 format. Read the diffs help topic for more information.
1107
1105
1108 With the --switch-parent option, the diff will be against the
1106 With the --switch-parent option, the diff will be against the
1109 second parent. It can be useful to review a merge.
1107 second parent. It can be useful to review a merge.
1110 """
1108 """
1111 if not changesets:
1109 if not changesets:
1112 raise util.Abort(_("export requires at least one changeset"))
1110 raise util.Abort(_("export requires at least one changeset"))
1113 revs = cmdutil.revrange(repo, changesets)
1111 revs = cmdutil.revrange(repo, changesets)
1114 if len(revs) > 1:
1112 if len(revs) > 1:
1115 ui.note(_('exporting patches:\n'))
1113 ui.note(_('exporting patches:\n'))
1116 else:
1114 else:
1117 ui.note(_('exporting patch:\n'))
1115 ui.note(_('exporting patch:\n'))
1118 patch.export(repo, revs, template=opts.get('output'),
1116 patch.export(repo, revs, template=opts.get('output'),
1119 switch_parent=opts.get('switch_parent'),
1117 switch_parent=opts.get('switch_parent'),
1120 opts=patch.diffopts(ui, opts))
1118 opts=patch.diffopts(ui, opts))
1121
1119
1122 def grep(ui, repo, pattern, *pats, **opts):
1120 def grep(ui, repo, pattern, *pats, **opts):
1123 """search for a pattern in specified files and revisions
1121 """search for a pattern in specified files and revisions
1124
1122
1125 Search revisions of files for a regular expression.
1123 Search revisions of files for a regular expression.
1126
1124
1127 This command behaves differently than Unix grep. It only accepts
1125 This command behaves differently than Unix grep. It only accepts
1128 Python/Perl regexps. It searches repository history, not the
1126 Python/Perl regexps. It searches repository history, not the
1129 working directory. It always prints the revision number in which a
1127 working directory. It always prints the revision number in which a
1130 match appears.
1128 match appears.
1131
1129
1132 By default, grep only prints output for the first revision of a
1130 By default, grep only prints output for the first revision of a
1133 file in which it finds a match. To get it to print every revision
1131 file in which it finds a match. To get it to print every revision
1134 that contains a change in match status ("-" for a match that
1132 that contains a change in match status ("-" for a match that
1135 becomes a non-match, or "+" for a non-match that becomes a match),
1133 becomes a non-match, or "+" for a non-match that becomes a match),
1136 use the --all flag.
1134 use the --all flag.
1137 """
1135 """
1138 reflags = 0
1136 reflags = 0
1139 if opts.get('ignore_case'):
1137 if opts.get('ignore_case'):
1140 reflags |= re.I
1138 reflags |= re.I
1141 try:
1139 try:
1142 regexp = re.compile(pattern, reflags)
1140 regexp = re.compile(pattern, reflags)
1143 except Exception, inst:
1141 except Exception, inst:
1144 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1142 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1145 return None
1143 return None
1146 sep, eol = ':', '\n'
1144 sep, eol = ':', '\n'
1147 if opts.get('print0'):
1145 if opts.get('print0'):
1148 sep = eol = '\0'
1146 sep = eol = '\0'
1149
1147
1150 fcache = {}
1148 fcache = {}
1151 def getfile(fn):
1149 def getfile(fn):
1152 if fn not in fcache:
1150 if fn not in fcache:
1153 fcache[fn] = repo.file(fn)
1151 fcache[fn] = repo.file(fn)
1154 return fcache[fn]
1152 return fcache[fn]
1155
1153
1156 def matchlines(body):
1154 def matchlines(body):
1157 begin = 0
1155 begin = 0
1158 linenum = 0
1156 linenum = 0
1159 while True:
1157 while True:
1160 match = regexp.search(body, begin)
1158 match = regexp.search(body, begin)
1161 if not match:
1159 if not match:
1162 break
1160 break
1163 mstart, mend = match.span()
1161 mstart, mend = match.span()
1164 linenum += body.count('\n', begin, mstart) + 1
1162 linenum += body.count('\n', begin, mstart) + 1
1165 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1163 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1166 begin = body.find('\n', mend) + 1 or len(body)
1164 begin = body.find('\n', mend) + 1 or len(body)
1167 lend = begin - 1
1165 lend = begin - 1
1168 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1166 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1169
1167
1170 class linestate(object):
1168 class linestate(object):
1171 def __init__(self, line, linenum, colstart, colend):
1169 def __init__(self, line, linenum, colstart, colend):
1172 self.line = line
1170 self.line = line
1173 self.linenum = linenum
1171 self.linenum = linenum
1174 self.colstart = colstart
1172 self.colstart = colstart
1175 self.colend = colend
1173 self.colend = colend
1176
1174
1177 def __hash__(self):
1175 def __hash__(self):
1178 return hash((self.linenum, self.line))
1176 return hash((self.linenum, self.line))
1179
1177
1180 def __eq__(self, other):
1178 def __eq__(self, other):
1181 return self.line == other.line
1179 return self.line == other.line
1182
1180
1183 matches = {}
1181 matches = {}
1184 copies = {}
1182 copies = {}
1185 def grepbody(fn, rev, body):
1183 def grepbody(fn, rev, body):
1186 matches[rev].setdefault(fn, [])
1184 matches[rev].setdefault(fn, [])
1187 m = matches[rev][fn]
1185 m = matches[rev][fn]
1188 for lnum, cstart, cend, line in matchlines(body):
1186 for lnum, cstart, cend, line in matchlines(body):
1189 s = linestate(line, lnum, cstart, cend)
1187 s = linestate(line, lnum, cstart, cend)
1190 m.append(s)
1188 m.append(s)
1191
1189
1192 def difflinestates(a, b):
1190 def difflinestates(a, b):
1193 sm = difflib.SequenceMatcher(None, a, b)
1191 sm = difflib.SequenceMatcher(None, a, b)
1194 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1192 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1195 if tag == 'insert':
1193 if tag == 'insert':
1196 for i in xrange(blo, bhi):
1194 for i in xrange(blo, bhi):
1197 yield ('+', b[i])
1195 yield ('+', b[i])
1198 elif tag == 'delete':
1196 elif tag == 'delete':
1199 for i in xrange(alo, ahi):
1197 for i in xrange(alo, ahi):
1200 yield ('-', a[i])
1198 yield ('-', a[i])
1201 elif tag == 'replace':
1199 elif tag == 'replace':
1202 for i in xrange(alo, ahi):
1200 for i in xrange(alo, ahi):
1203 yield ('-', a[i])
1201 yield ('-', a[i])
1204 for i in xrange(blo, bhi):
1202 for i in xrange(blo, bhi):
1205 yield ('+', b[i])
1203 yield ('+', b[i])
1206
1204
1207 prev = {}
1205 prev = {}
1208 def display(fn, rev, states, prevstates):
1206 def display(fn, rev, states, prevstates):
1209 datefunc = ui.quiet and util.shortdate or util.datestr
1207 datefunc = ui.quiet and util.shortdate or util.datestr
1210 found = False
1208 found = False
1211 filerevmatches = {}
1209 filerevmatches = {}
1212 r = prev.get(fn, -1)
1210 r = prev.get(fn, -1)
1213 if opts.get('all'):
1211 if opts.get('all'):
1214 iter = difflinestates(states, prevstates)
1212 iter = difflinestates(states, prevstates)
1215 else:
1213 else:
1216 iter = [('', l) for l in prevstates]
1214 iter = [('', l) for l in prevstates]
1217 for change, l in iter:
1215 for change, l in iter:
1218 cols = [fn, str(r)]
1216 cols = [fn, str(r)]
1219 if opts.get('line_number'):
1217 if opts.get('line_number'):
1220 cols.append(str(l.linenum))
1218 cols.append(str(l.linenum))
1221 if opts.get('all'):
1219 if opts.get('all'):
1222 cols.append(change)
1220 cols.append(change)
1223 if opts.get('user'):
1221 if opts.get('user'):
1224 cols.append(ui.shortuser(get(r)[1]))
1222 cols.append(ui.shortuser(get(r)[1]))
1225 if opts.get('date'):
1223 if opts.get('date'):
1226 cols.append(datefunc(get(r)[2]))
1224 cols.append(datefunc(get(r)[2]))
1227 if opts.get('files_with_matches'):
1225 if opts.get('files_with_matches'):
1228 c = (fn, r)
1226 c = (fn, r)
1229 if c in filerevmatches:
1227 if c in filerevmatches:
1230 continue
1228 continue
1231 filerevmatches[c] = 1
1229 filerevmatches[c] = 1
1232 else:
1230 else:
1233 cols.append(l.line)
1231 cols.append(l.line)
1234 ui.write(sep.join(cols), eol)
1232 ui.write(sep.join(cols), eol)
1235 found = True
1233 found = True
1236 return found
1234 return found
1237
1235
1238 fstate = {}
1236 fstate = {}
1239 skip = {}
1237 skip = {}
1240 get = util.cachefunc(lambda r: repo[r].changeset())
1238 get = util.cachefunc(lambda r: repo[r].changeset())
1241 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1239 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1242 found = False
1240 found = False
1243 follow = opts.get('follow')
1241 follow = opts.get('follow')
1244 for st, rev, fns in changeiter:
1242 for st, rev, fns in changeiter:
1245 if st == 'window':
1243 if st == 'window':
1246 matches.clear()
1244 matches.clear()
1247 elif st == 'add':
1245 elif st == 'add':
1248 ctx = repo[rev]
1246 ctx = repo[rev]
1249 matches[rev] = {}
1247 matches[rev] = {}
1250 for fn in fns:
1248 for fn in fns:
1251 if fn in skip:
1249 if fn in skip:
1252 continue
1250 continue
1253 try:
1251 try:
1254 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1252 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1255 fstate.setdefault(fn, [])
1253 fstate.setdefault(fn, [])
1256 if follow:
1254 if follow:
1257 copied = getfile(fn).renamed(ctx.filenode(fn))
1255 copied = getfile(fn).renamed(ctx.filenode(fn))
1258 if copied:
1256 if copied:
1259 copies.setdefault(rev, {})[fn] = copied[0]
1257 copies.setdefault(rev, {})[fn] = copied[0]
1260 except error.LookupError:
1258 except error.LookupError:
1261 pass
1259 pass
1262 elif st == 'iter':
1260 elif st == 'iter':
1263 for fn, m in util.sort(matches[rev].items()):
1261 for fn, m in util.sort(matches[rev].items()):
1264 copy = copies.get(rev, {}).get(fn)
1262 copy = copies.get(rev, {}).get(fn)
1265 if fn in skip:
1263 if fn in skip:
1266 if copy:
1264 if copy:
1267 skip[copy] = True
1265 skip[copy] = True
1268 continue
1266 continue
1269 if fn in prev or fstate[fn]:
1267 if fn in prev or fstate[fn]:
1270 r = display(fn, rev, m, fstate[fn])
1268 r = display(fn, rev, m, fstate[fn])
1271 found = found or r
1269 found = found or r
1272 if r and not opts.get('all'):
1270 if r and not opts.get('all'):
1273 skip[fn] = True
1271 skip[fn] = True
1274 if copy:
1272 if copy:
1275 skip[copy] = True
1273 skip[copy] = True
1276 fstate[fn] = m
1274 fstate[fn] = m
1277 if copy:
1275 if copy:
1278 fstate[copy] = m
1276 fstate[copy] = m
1279 prev[fn] = rev
1277 prev[fn] = rev
1280
1278
1281 for fn, state in util.sort(fstate.items()):
1279 for fn, state in util.sort(fstate.items()):
1282 if fn in skip:
1280 if fn in skip:
1283 continue
1281 continue
1284 if fn not in copies.get(prev[fn], {}):
1282 if fn not in copies.get(prev[fn], {}):
1285 found = display(fn, rev, {}, state) or found
1283 found = display(fn, rev, {}, state) or found
1286 return (not found and 1) or 0
1284 return (not found and 1) or 0
1287
1285
1288 def heads(ui, repo, *branchrevs, **opts):
1286 def heads(ui, repo, *branchrevs, **opts):
1289 """show current repository heads or show branch heads
1287 """show current repository heads or show branch heads
1290
1288
1291 With no arguments, show all repository head changesets.
1289 With no arguments, show all repository head changesets.
1292
1290
1293 If branch or revisions names are given this will show the heads of
1291 If branch or revisions names are given this will show the heads of
1294 the specified branches or the branches those revisions are tagged
1292 the specified branches or the branches those revisions are tagged
1295 with.
1293 with.
1296
1294
1297 Repository "heads" are changesets that don't have child
1295 Repository "heads" are changesets that don't have child
1298 changesets. They are where development generally takes place and
1296 changesets. They are where development generally takes place and
1299 are the usual targets for update and merge operations.
1297 are the usual targets for update and merge operations.
1300
1298
1301 Branch heads are changesets that have a given branch tag, but have
1299 Branch heads are changesets that have a given branch tag, but have
1302 no child changesets with that tag. They are usually where
1300 no child changesets with that tag. They are usually where
1303 development on the given branch takes place.
1301 development on the given branch takes place.
1304 """
1302 """
1305 if opts.get('rev'):
1303 if opts.get('rev'):
1306 start = repo.lookup(opts['rev'])
1304 start = repo.lookup(opts['rev'])
1307 else:
1305 else:
1308 start = None
1306 start = None
1309 closed = not opts.get('active')
1307 closed = not opts.get('active')
1310 if not branchrevs:
1308 if not branchrevs:
1311 # Assume we're looking repo-wide heads if no revs were specified.
1309 # Assume we're looking repo-wide heads if no revs were specified.
1312 heads = repo.heads(start, closed=closed)
1310 heads = repo.heads(start, closed=closed)
1313 else:
1311 else:
1314 heads = []
1312 heads = []
1315 visitedset = set()
1313 visitedset = set()
1316 for branchrev in branchrevs:
1314 for branchrev in branchrevs:
1317 branch = repo[branchrev].branch()
1315 branch = repo[branchrev].branch()
1318 if branch in visitedset:
1316 if branch in visitedset:
1319 continue
1317 continue
1320 visitedset.add(branch)
1318 visitedset.add(branch)
1321 bheads = repo.branchheads(branch, start, closed=closed)
1319 bheads = repo.branchheads(branch, start, closed=closed)
1322 if not bheads:
1320 if not bheads:
1323 if branch != branchrev:
1321 if branch != branchrev:
1324 ui.warn(_("no changes on branch %s containing %s are "
1322 ui.warn(_("no changes on branch %s containing %s are "
1325 "reachable from %s\n")
1323 "reachable from %s\n")
1326 % (branch, branchrev, opts.get('rev')))
1324 % (branch, branchrev, opts.get('rev')))
1327 else:
1325 else:
1328 ui.warn(_("no changes on branch %s are reachable from %s\n")
1326 ui.warn(_("no changes on branch %s are reachable from %s\n")
1329 % (branch, opts.get('rev')))
1327 % (branch, opts.get('rev')))
1330 heads.extend(bheads)
1328 heads.extend(bheads)
1331 if not heads:
1329 if not heads:
1332 return 1
1330 return 1
1333 displayer = cmdutil.show_changeset(ui, repo, opts)
1331 displayer = cmdutil.show_changeset(ui, repo, opts)
1334 for n in heads:
1332 for n in heads:
1335 displayer.show(repo[n])
1333 displayer.show(repo[n])
1336
1334
1337 def help_(ui, name=None, with_version=False):
1335 def help_(ui, name=None, with_version=False):
1338 """show help for a given topic or a help overview
1336 """show help for a given topic or a help overview
1339
1337
1340 With no arguments, print a list of commands and short help.
1338 With no arguments, print a list of commands and short help.
1341
1339
1342 Given a topic, extension, or command name, print help for that
1340 Given a topic, extension, or command name, print help for that
1343 topic."""
1341 topic."""
1344 option_lists = []
1342 option_lists = []
1345
1343
1346 def addglobalopts(aliases):
1344 def addglobalopts(aliases):
1347 if ui.verbose:
1345 if ui.verbose:
1348 option_lists.append((_("global options:"), globalopts))
1346 option_lists.append((_("global options:"), globalopts))
1349 if name == 'shortlist':
1347 if name == 'shortlist':
1350 option_lists.append((_('use "hg help" for the full list '
1348 option_lists.append((_('use "hg help" for the full list '
1351 'of commands'), ()))
1349 'of commands'), ()))
1352 else:
1350 else:
1353 if name == 'shortlist':
1351 if name == 'shortlist':
1354 msg = _('use "hg help" for the full list of commands '
1352 msg = _('use "hg help" for the full list of commands '
1355 'or "hg -v" for details')
1353 'or "hg -v" for details')
1356 elif aliases:
1354 elif aliases:
1357 msg = _('use "hg -v help%s" to show aliases and '
1355 msg = _('use "hg -v help%s" to show aliases and '
1358 'global options') % (name and " " + name or "")
1356 'global options') % (name and " " + name or "")
1359 else:
1357 else:
1360 msg = _('use "hg -v help %s" to show global options') % name
1358 msg = _('use "hg -v help %s" to show global options') % name
1361 option_lists.append((msg, ()))
1359 option_lists.append((msg, ()))
1362
1360
1363 def helpcmd(name):
1361 def helpcmd(name):
1364 if with_version:
1362 if with_version:
1365 version_(ui)
1363 version_(ui)
1366 ui.write('\n')
1364 ui.write('\n')
1367
1365
1368 try:
1366 try:
1369 aliases, i = cmdutil.findcmd(name, table, False)
1367 aliases, i = cmdutil.findcmd(name, table, False)
1370 except error.AmbiguousCommand, inst:
1368 except error.AmbiguousCommand, inst:
1371 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1369 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1372 helplist(_('list of commands:\n\n'), select)
1370 helplist(_('list of commands:\n\n'), select)
1373 return
1371 return
1374
1372
1375 # synopsis
1373 # synopsis
1376 if len(i) > 2:
1374 if len(i) > 2:
1377 if i[2].startswith('hg'):
1375 if i[2].startswith('hg'):
1378 ui.write("%s\n" % i[2])
1376 ui.write("%s\n" % i[2])
1379 else:
1377 else:
1380 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1378 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1381 else:
1379 else:
1382 ui.write('hg %s\n' % aliases[0])
1380 ui.write('hg %s\n' % aliases[0])
1383
1381
1384 # aliases
1382 # aliases
1385 if not ui.quiet and len(aliases) > 1:
1383 if not ui.quiet and len(aliases) > 1:
1386 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1384 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1387
1385
1388 # description
1386 # description
1389 doc = gettext(i[0].__doc__)
1387 doc = gettext(i[0].__doc__)
1390 if not doc:
1388 if not doc:
1391 doc = _("(no help text available)")
1389 doc = _("(no help text available)")
1392 if ui.quiet:
1390 if ui.quiet:
1393 doc = doc.splitlines(0)[0]
1391 doc = doc.splitlines(0)[0]
1394 ui.write("\n%s\n" % doc.rstrip())
1392 ui.write("\n%s\n" % doc.rstrip())
1395
1393
1396 if not ui.quiet:
1394 if not ui.quiet:
1397 # options
1395 # options
1398 if i[1]:
1396 if i[1]:
1399 option_lists.append((_("options:\n"), i[1]))
1397 option_lists.append((_("options:\n"), i[1]))
1400
1398
1401 addglobalopts(False)
1399 addglobalopts(False)
1402
1400
1403 def helplist(header, select=None):
1401 def helplist(header, select=None):
1404 h = {}
1402 h = {}
1405 cmds = {}
1403 cmds = {}
1406 for c, e in table.iteritems():
1404 for c, e in table.iteritems():
1407 f = c.split("|", 1)[0]
1405 f = c.split("|", 1)[0]
1408 if select and not select(f):
1406 if select and not select(f):
1409 continue
1407 continue
1410 if (not select and name != 'shortlist' and
1408 if (not select and name != 'shortlist' and
1411 e[0].__module__ != __name__):
1409 e[0].__module__ != __name__):
1412 continue
1410 continue
1413 if name == "shortlist" and not f.startswith("^"):
1411 if name == "shortlist" and not f.startswith("^"):
1414 continue
1412 continue
1415 f = f.lstrip("^")
1413 f = f.lstrip("^")
1416 if not ui.debugflag and f.startswith("debug"):
1414 if not ui.debugflag and f.startswith("debug"):
1417 continue
1415 continue
1418 doc = gettext(e[0].__doc__)
1416 doc = gettext(e[0].__doc__)
1419 if not doc:
1417 if not doc:
1420 doc = _("(no help text available)")
1418 doc = _("(no help text available)")
1421 h[f] = doc.splitlines(0)[0].rstrip()
1419 h[f] = doc.splitlines(0)[0].rstrip()
1422 cmds[f] = c.lstrip("^")
1420 cmds[f] = c.lstrip("^")
1423
1421
1424 if not h:
1422 if not h:
1425 ui.status(_('no commands defined\n'))
1423 ui.status(_('no commands defined\n'))
1426 return
1424 return
1427
1425
1428 ui.status(header)
1426 ui.status(header)
1429 fns = util.sort(h)
1427 fns = util.sort(h)
1430 m = max(map(len, fns))
1428 m = max(map(len, fns))
1431 for f in fns:
1429 for f in fns:
1432 if ui.verbose:
1430 if ui.verbose:
1433 commands = cmds[f].replace("|",", ")
1431 commands = cmds[f].replace("|",", ")
1434 ui.write(" %s:\n %s\n"%(commands, h[f]))
1432 ui.write(" %s:\n %s\n"%(commands, h[f]))
1435 else:
1433 else:
1436 ui.write(' %-*s %s\n' % (m, f, h[f]))
1434 ui.write(' %-*s %s\n' % (m, f, h[f]))
1437
1435
1438 exts = list(extensions.extensions())
1436 exts = list(extensions.extensions())
1439 if exts and name != 'shortlist':
1437 if exts and name != 'shortlist':
1440 ui.write(_('\nenabled extensions:\n\n'))
1438 ui.write(_('\nenabled extensions:\n\n'))
1441 maxlength = 0
1439 maxlength = 0
1442 exthelps = []
1440 exthelps = []
1443 for ename, ext in exts:
1441 for ename, ext in exts:
1444 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1442 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1445 ename = ename.split('.')[-1]
1443 ename = ename.split('.')[-1]
1446 maxlength = max(len(ename), maxlength)
1444 maxlength = max(len(ename), maxlength)
1447 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1445 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1448 for ename, text in exthelps:
1446 for ename, text in exthelps:
1449 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1447 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1450
1448
1451 if not ui.quiet:
1449 if not ui.quiet:
1452 addglobalopts(True)
1450 addglobalopts(True)
1453
1451
1454 def helptopic(name):
1452 def helptopic(name):
1455 for names, header, doc in help.helptable:
1453 for names, header, doc in help.helptable:
1456 if name in names:
1454 if name in names:
1457 break
1455 break
1458 else:
1456 else:
1459 raise error.UnknownCommand(name)
1457 raise error.UnknownCommand(name)
1460
1458
1461 # description
1459 # description
1462 if not doc:
1460 if not doc:
1463 doc = _("(no help text available)")
1461 doc = _("(no help text available)")
1464 if callable(doc):
1462 if callable(doc):
1465 doc = doc()
1463 doc = doc()
1466
1464
1467 ui.write("%s\n" % header)
1465 ui.write("%s\n" % header)
1468 ui.write("%s\n" % doc.rstrip())
1466 ui.write("%s\n" % doc.rstrip())
1469
1467
1470 def helpext(name):
1468 def helpext(name):
1471 try:
1469 try:
1472 mod = extensions.find(name)
1470 mod = extensions.find(name)
1473 except KeyError:
1471 except KeyError:
1474 raise error.UnknownCommand(name)
1472 raise error.UnknownCommand(name)
1475
1473
1476 doc = gettext(mod.__doc__) or _('no help text available')
1474 doc = gettext(mod.__doc__) or _('no help text available')
1477 doc = doc.splitlines(0)
1475 doc = doc.splitlines(0)
1478 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1476 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1479 for d in doc[1:]:
1477 for d in doc[1:]:
1480 ui.write(d, '\n')
1478 ui.write(d, '\n')
1481
1479
1482 ui.status('\n')
1480 ui.status('\n')
1483
1481
1484 try:
1482 try:
1485 ct = mod.cmdtable
1483 ct = mod.cmdtable
1486 except AttributeError:
1484 except AttributeError:
1487 ct = {}
1485 ct = {}
1488
1486
1489 modcmds = set([c.split('|', 1)[0] for c in ct])
1487 modcmds = set([c.split('|', 1)[0] for c in ct])
1490 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1488 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1491
1489
1492 if name and name != 'shortlist':
1490 if name and name != 'shortlist':
1493 i = None
1491 i = None
1494 for f in (helptopic, helpcmd, helpext):
1492 for f in (helptopic, helpcmd, helpext):
1495 try:
1493 try:
1496 f(name)
1494 f(name)
1497 i = None
1495 i = None
1498 break
1496 break
1499 except error.UnknownCommand, inst:
1497 except error.UnknownCommand, inst:
1500 i = inst
1498 i = inst
1501 if i:
1499 if i:
1502 raise i
1500 raise i
1503
1501
1504 else:
1502 else:
1505 # program name
1503 # program name
1506 if ui.verbose or with_version:
1504 if ui.verbose or with_version:
1507 version_(ui)
1505 version_(ui)
1508 else:
1506 else:
1509 ui.status(_("Mercurial Distributed SCM\n"))
1507 ui.status(_("Mercurial Distributed SCM\n"))
1510 ui.status('\n')
1508 ui.status('\n')
1511
1509
1512 # list of commands
1510 # list of commands
1513 if name == "shortlist":
1511 if name == "shortlist":
1514 header = _('basic commands:\n\n')
1512 header = _('basic commands:\n\n')
1515 else:
1513 else:
1516 header = _('list of commands:\n\n')
1514 header = _('list of commands:\n\n')
1517
1515
1518 helplist(header)
1516 helplist(header)
1519
1517
1520 # list all option lists
1518 # list all option lists
1521 opt_output = []
1519 opt_output = []
1522 for title, options in option_lists:
1520 for title, options in option_lists:
1523 opt_output.append(("\n%s" % title, None))
1521 opt_output.append(("\n%s" % title, None))
1524 for shortopt, longopt, default, desc in options:
1522 for shortopt, longopt, default, desc in options:
1525 if "DEPRECATED" in desc and not ui.verbose: continue
1523 if "DEPRECATED" in desc and not ui.verbose: continue
1526 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1524 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1527 longopt and " --%s" % longopt),
1525 longopt and " --%s" % longopt),
1528 "%s%s" % (desc,
1526 "%s%s" % (desc,
1529 default
1527 default
1530 and _(" (default: %s)") % default
1528 and _(" (default: %s)") % default
1531 or "")))
1529 or "")))
1532
1530
1533 if not name:
1531 if not name:
1534 ui.write(_("\nadditional help topics:\n\n"))
1532 ui.write(_("\nadditional help topics:\n\n"))
1535 topics = []
1533 topics = []
1536 for names, header, doc in help.helptable:
1534 for names, header, doc in help.helptable:
1537 names = [(-len(name), name) for name in names]
1535 names = [(-len(name), name) for name in names]
1538 names.sort()
1536 names.sort()
1539 topics.append((names[0][1], header))
1537 topics.append((names[0][1], header))
1540 topics_len = max([len(s[0]) for s in topics])
1538 topics_len = max([len(s[0]) for s in topics])
1541 for t, desc in topics:
1539 for t, desc in topics:
1542 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1540 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1543
1541
1544 if opt_output:
1542 if opt_output:
1545 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1543 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1546 for first, second in opt_output:
1544 for first, second in opt_output:
1547 if second:
1545 if second:
1548 # wrap descriptions at 70 characters, just like the
1546 # wrap descriptions at 70 characters, just like the
1549 # main help texts
1547 # main help texts
1550 second = textwrap.wrap(second, width=70 - opts_len - 3)
1548 second = textwrap.wrap(second, width=70 - opts_len - 3)
1551 pad = '\n' + ' ' * (opts_len + 3)
1549 pad = '\n' + ' ' * (opts_len + 3)
1552 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1550 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1553 else:
1551 else:
1554 ui.write("%s\n" % first)
1552 ui.write("%s\n" % first)
1555
1553
1556 def identify(ui, repo, source=None,
1554 def identify(ui, repo, source=None,
1557 rev=None, num=None, id=None, branch=None, tags=None):
1555 rev=None, num=None, id=None, branch=None, tags=None):
1558 """identify the working copy or specified revision
1556 """identify the working copy or specified revision
1559
1557
1560 With no revision, print a summary of the current state of the
1558 With no revision, print a summary of the current state of the
1561 repository.
1559 repository.
1562
1560
1563 With a path, do a lookup in another repository.
1561 With a path, do a lookup in another repository.
1564
1562
1565 This summary identifies the repository state using one or two
1563 This summary identifies the repository state using one or two
1566 parent hash identifiers, followed by a "+" if there are
1564 parent hash identifiers, followed by a "+" if there are
1567 uncommitted changes in the working directory, a list of tags for
1565 uncommitted changes in the working directory, a list of tags for
1568 this revision and a branch name for non-default branches.
1566 this revision and a branch name for non-default branches.
1569 """
1567 """
1570
1568
1571 if not repo and not source:
1569 if not repo and not source:
1572 raise util.Abort(_("There is no Mercurial repository here "
1570 raise util.Abort(_("There is no Mercurial repository here "
1573 "(.hg not found)"))
1571 "(.hg not found)"))
1574
1572
1575 hexfunc = ui.debugflag and hex or short
1573 hexfunc = ui.debugflag and hex or short
1576 default = not (num or id or branch or tags)
1574 default = not (num or id or branch or tags)
1577 output = []
1575 output = []
1578
1576
1579 revs = []
1577 revs = []
1580 if source:
1578 if source:
1581 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1579 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1582 repo = hg.repository(ui, source)
1580 repo = hg.repository(ui, source)
1583
1581
1584 if not repo.local():
1582 if not repo.local():
1585 if not rev and revs:
1583 if not rev and revs:
1586 rev = revs[0]
1584 rev = revs[0]
1587 if not rev:
1585 if not rev:
1588 rev = "tip"
1586 rev = "tip"
1589 if num or branch or tags:
1587 if num or branch or tags:
1590 raise util.Abort(
1588 raise util.Abort(
1591 "can't query remote revision number, branch, or tags")
1589 "can't query remote revision number, branch, or tags")
1592 output = [hexfunc(repo.lookup(rev))]
1590 output = [hexfunc(repo.lookup(rev))]
1593 elif not rev:
1591 elif not rev:
1594 ctx = repo[None]
1592 ctx = repo[None]
1595 parents = ctx.parents()
1593 parents = ctx.parents()
1596 changed = False
1594 changed = False
1597 if default or id or num:
1595 if default or id or num:
1598 changed = ctx.files() + ctx.deleted()
1596 changed = ctx.files() + ctx.deleted()
1599 if default or id:
1597 if default or id:
1600 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1598 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1601 (changed) and "+" or "")]
1599 (changed) and "+" or "")]
1602 if num:
1600 if num:
1603 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1601 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1604 (changed) and "+" or ""))
1602 (changed) and "+" or ""))
1605 else:
1603 else:
1606 ctx = repo[rev]
1604 ctx = repo[rev]
1607 if default or id:
1605 if default or id:
1608 output = [hexfunc(ctx.node())]
1606 output = [hexfunc(ctx.node())]
1609 if num:
1607 if num:
1610 output.append(str(ctx.rev()))
1608 output.append(str(ctx.rev()))
1611
1609
1612 if repo.local() and default and not ui.quiet:
1610 if repo.local() and default and not ui.quiet:
1613 b = encoding.tolocal(ctx.branch())
1611 b = encoding.tolocal(ctx.branch())
1614 if b != 'default':
1612 if b != 'default':
1615 output.append("(%s)" % b)
1613 output.append("(%s)" % b)
1616
1614
1617 # multiple tags for a single parent separated by '/'
1615 # multiple tags for a single parent separated by '/'
1618 t = "/".join(ctx.tags())
1616 t = "/".join(ctx.tags())
1619 if t:
1617 if t:
1620 output.append(t)
1618 output.append(t)
1621
1619
1622 if branch:
1620 if branch:
1623 output.append(encoding.tolocal(ctx.branch()))
1621 output.append(encoding.tolocal(ctx.branch()))
1624
1622
1625 if tags:
1623 if tags:
1626 output.extend(ctx.tags())
1624 output.extend(ctx.tags())
1627
1625
1628 ui.write("%s\n" % ' '.join(output))
1626 ui.write("%s\n" % ' '.join(output))
1629
1627
1630 def import_(ui, repo, patch1, *patches, **opts):
1628 def import_(ui, repo, patch1, *patches, **opts):
1631 """import an ordered set of patches
1629 """import an ordered set of patches
1632
1630
1633 Import a list of patches and commit them individually.
1631 Import a list of patches and commit them individually.
1634
1632
1635 If there are outstanding changes in the working directory, import
1633 If there are outstanding changes in the working directory, import
1636 will abort unless given the -f/--force flag.
1634 will abort unless given the -f/--force flag.
1637
1635
1638 You can import a patch straight from a mail message. Even patches
1636 You can import a patch straight from a mail message. Even patches
1639 as attachments work (body part must be type text/plain or
1637 as attachments work (body part must be type text/plain or
1640 text/x-patch to be used). From and Subject headers of email
1638 text/x-patch to be used). From and Subject headers of email
1641 message are used as default committer and commit message. All
1639 message are used as default committer and commit message. All
1642 text/plain body parts before first diff are added to commit
1640 text/plain body parts before first diff are added to commit
1643 message.
1641 message.
1644
1642
1645 If the imported patch was generated by hg export, user and
1643 If the imported patch was generated by hg export, user and
1646 description from patch override values from message headers and
1644 description from patch override values from message headers and
1647 body. Values given on command line with -m/--message and -u/--user
1645 body. Values given on command line with -m/--message and -u/--user
1648 override these.
1646 override these.
1649
1647
1650 If --exact is specified, import will set the working directory to
1648 If --exact is specified, import will set the working directory to
1651 the parent of each patch before applying it, and will abort if the
1649 the parent of each patch before applying it, and will abort if the
1652 resulting changeset has a different ID than the one recorded in
1650 resulting changeset has a different ID than the one recorded in
1653 the patch. This may happen due to character set problems or other
1651 the patch. This may happen due to character set problems or other
1654 deficiencies in the text patch format.
1652 deficiencies in the text patch format.
1655
1653
1656 With -s/--similarity, hg will attempt to discover renames and
1654 With -s/--similarity, hg will attempt to discover renames and
1657 copies in the patch in the same way as 'addremove'.
1655 copies in the patch in the same way as 'addremove'.
1658
1656
1659 To read a patch from standard input, use patch name "-". See 'hg
1657 To read a patch from standard input, use patch name "-". See 'hg
1660 help dates' for a list of formats valid for -d/--date.
1658 help dates' for a list of formats valid for -d/--date.
1661 """
1659 """
1662 patches = (patch1,) + patches
1660 patches = (patch1,) + patches
1663
1661
1664 date = opts.get('date')
1662 date = opts.get('date')
1665 if date:
1663 if date:
1666 opts['date'] = util.parsedate(date)
1664 opts['date'] = util.parsedate(date)
1667
1665
1668 try:
1666 try:
1669 sim = float(opts.get('similarity') or 0)
1667 sim = float(opts.get('similarity') or 0)
1670 except ValueError:
1668 except ValueError:
1671 raise util.Abort(_('similarity must be a number'))
1669 raise util.Abort(_('similarity must be a number'))
1672 if sim < 0 or sim > 100:
1670 if sim < 0 or sim > 100:
1673 raise util.Abort(_('similarity must be between 0 and 100'))
1671 raise util.Abort(_('similarity must be between 0 and 100'))
1674
1672
1675 if opts.get('exact') or not opts.get('force'):
1673 if opts.get('exact') or not opts.get('force'):
1676 cmdutil.bail_if_changed(repo)
1674 cmdutil.bail_if_changed(repo)
1677
1675
1678 d = opts["base"]
1676 d = opts["base"]
1679 strip = opts["strip"]
1677 strip = opts["strip"]
1680 wlock = lock = None
1678 wlock = lock = None
1681 try:
1679 try:
1682 wlock = repo.wlock()
1680 wlock = repo.wlock()
1683 lock = repo.lock()
1681 lock = repo.lock()
1684 for p in patches:
1682 for p in patches:
1685 pf = os.path.join(d, p)
1683 pf = os.path.join(d, p)
1686
1684
1687 if pf == '-':
1685 if pf == '-':
1688 ui.status(_("applying patch from stdin\n"))
1686 ui.status(_("applying patch from stdin\n"))
1689 pf = sys.stdin
1687 pf = sys.stdin
1690 else:
1688 else:
1691 ui.status(_("applying %s\n") % p)
1689 ui.status(_("applying %s\n") % p)
1692 pf = url.open(ui, pf)
1690 pf = url.open(ui, pf)
1693 data = patch.extract(ui, pf)
1691 data = patch.extract(ui, pf)
1694 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1692 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1695
1693
1696 if tmpname is None:
1694 if tmpname is None:
1697 raise util.Abort(_('no diffs found'))
1695 raise util.Abort(_('no diffs found'))
1698
1696
1699 try:
1697 try:
1700 cmdline_message = cmdutil.logmessage(opts)
1698 cmdline_message = cmdutil.logmessage(opts)
1701 if cmdline_message:
1699 if cmdline_message:
1702 # pickup the cmdline msg
1700 # pickup the cmdline msg
1703 message = cmdline_message
1701 message = cmdline_message
1704 elif message:
1702 elif message:
1705 # pickup the patch msg
1703 # pickup the patch msg
1706 message = message.strip()
1704 message = message.strip()
1707 else:
1705 else:
1708 # launch the editor
1706 # launch the editor
1709 message = None
1707 message = None
1710 ui.debug(_('message:\n%s\n') % message)
1708 ui.debug(_('message:\n%s\n') % message)
1711
1709
1712 wp = repo.parents()
1710 wp = repo.parents()
1713 if opts.get('exact'):
1711 if opts.get('exact'):
1714 if not nodeid or not p1:
1712 if not nodeid or not p1:
1715 raise util.Abort(_('not a mercurial patch'))
1713 raise util.Abort(_('not a mercurial patch'))
1716 p1 = repo.lookup(p1)
1714 p1 = repo.lookup(p1)
1717 p2 = repo.lookup(p2 or hex(nullid))
1715 p2 = repo.lookup(p2 or hex(nullid))
1718
1716
1719 if p1 != wp[0].node():
1717 if p1 != wp[0].node():
1720 hg.clean(repo, p1)
1718 hg.clean(repo, p1)
1721 repo.dirstate.setparents(p1, p2)
1719 repo.dirstate.setparents(p1, p2)
1722 elif p2:
1720 elif p2:
1723 try:
1721 try:
1724 p1 = repo.lookup(p1)
1722 p1 = repo.lookup(p1)
1725 p2 = repo.lookup(p2)
1723 p2 = repo.lookup(p2)
1726 if p1 == wp[0].node():
1724 if p1 == wp[0].node():
1727 repo.dirstate.setparents(p1, p2)
1725 repo.dirstate.setparents(p1, p2)
1728 except error.RepoError:
1726 except error.RepoError:
1729 pass
1727 pass
1730 if opts.get('exact') or opts.get('import_branch'):
1728 if opts.get('exact') or opts.get('import_branch'):
1731 repo.dirstate.setbranch(branch or 'default')
1729 repo.dirstate.setbranch(branch or 'default')
1732
1730
1733 files = {}
1731 files = {}
1734 try:
1732 try:
1735 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1733 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1736 files=files)
1734 files=files)
1737 finally:
1735 finally:
1738 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1736 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1739 if not opts.get('no_commit'):
1737 if not opts.get('no_commit'):
1740 n = repo.commit(files, message, opts.get('user') or user,
1738 n = repo.commit(files, message, opts.get('user') or user,
1741 opts.get('date') or date)
1739 opts.get('date') or date)
1742 if opts.get('exact'):
1740 if opts.get('exact'):
1743 if hex(n) != nodeid:
1741 if hex(n) != nodeid:
1744 repo.rollback()
1742 repo.rollback()
1745 raise util.Abort(_('patch is damaged'
1743 raise util.Abort(_('patch is damaged'
1746 ' or loses information'))
1744 ' or loses information'))
1747 # Force a dirstate write so that the next transaction
1745 # Force a dirstate write so that the next transaction
1748 # backups an up-do-date file.
1746 # backups an up-do-date file.
1749 repo.dirstate.write()
1747 repo.dirstate.write()
1750 finally:
1748 finally:
1751 os.unlink(tmpname)
1749 os.unlink(tmpname)
1752 finally:
1750 finally:
1753 release(lock, wlock)
1751 release(lock, wlock)
1754
1752
1755 def incoming(ui, repo, source="default", **opts):
1753 def incoming(ui, repo, source="default", **opts):
1756 """show new changesets found in source
1754 """show new changesets found in source
1757
1755
1758 Show new changesets found in the specified path/URL or the default
1756 Show new changesets found in the specified path/URL or the default
1759 pull location. These are the changesets that would be pulled if a
1757 pull location. These are the changesets that would be pulled if a
1760 pull was requested.
1758 pull was requested.
1761
1759
1762 For remote repository, using --bundle avoids downloading the
1760 For remote repository, using --bundle avoids downloading the
1763 changesets twice if the incoming is followed by a pull.
1761 changesets twice if the incoming is followed by a pull.
1764
1762
1765 See pull for valid source format details.
1763 See pull for valid source format details.
1766 """
1764 """
1767 limit = cmdutil.loglimit(opts)
1765 limit = cmdutil.loglimit(opts)
1768 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1766 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1769 cmdutil.setremoteconfig(ui, opts)
1767 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1770
1771 other = hg.repository(ui, source)
1772 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1768 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1773 if revs:
1769 if revs:
1774 revs = [other.lookup(rev) for rev in revs]
1770 revs = [other.lookup(rev) for rev in revs]
1775 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1771 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1776 force=opts["force"])
1772 force=opts["force"])
1777 if not incoming:
1773 if not incoming:
1778 try:
1774 try:
1779 os.unlink(opts["bundle"])
1775 os.unlink(opts["bundle"])
1780 except:
1776 except:
1781 pass
1777 pass
1782 ui.status(_("no changes found\n"))
1778 ui.status(_("no changes found\n"))
1783 return 1
1779 return 1
1784
1780
1785 cleanup = None
1781 cleanup = None
1786 try:
1782 try:
1787 fname = opts["bundle"]
1783 fname = opts["bundle"]
1788 if fname or not other.local():
1784 if fname or not other.local():
1789 # create a bundle (uncompressed if other repo is not local)
1785 # create a bundle (uncompressed if other repo is not local)
1790
1786
1791 if revs is None and other.capable('changegroupsubset'):
1787 if revs is None and other.capable('changegroupsubset'):
1792 revs = rheads
1788 revs = rheads
1793
1789
1794 if revs is None:
1790 if revs is None:
1795 cg = other.changegroup(incoming, "incoming")
1791 cg = other.changegroup(incoming, "incoming")
1796 else:
1792 else:
1797 cg = other.changegroupsubset(incoming, revs, 'incoming')
1793 cg = other.changegroupsubset(incoming, revs, 'incoming')
1798 bundletype = other.local() and "HG10BZ" or "HG10UN"
1794 bundletype = other.local() and "HG10BZ" or "HG10UN"
1799 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1795 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1800 # keep written bundle?
1796 # keep written bundle?
1801 if opts["bundle"]:
1797 if opts["bundle"]:
1802 cleanup = None
1798 cleanup = None
1803 if not other.local():
1799 if not other.local():
1804 # use the created uncompressed bundlerepo
1800 # use the created uncompressed bundlerepo
1805 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1801 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1806
1802
1807 o = other.changelog.nodesbetween(incoming, revs)[0]
1803 o = other.changelog.nodesbetween(incoming, revs)[0]
1808 if opts.get('newest_first'):
1804 if opts.get('newest_first'):
1809 o.reverse()
1805 o.reverse()
1810 displayer = cmdutil.show_changeset(ui, other, opts)
1806 displayer = cmdutil.show_changeset(ui, other, opts)
1811 count = 0
1807 count = 0
1812 for n in o:
1808 for n in o:
1813 if count >= limit:
1809 if count >= limit:
1814 break
1810 break
1815 parents = [p for p in other.changelog.parents(n) if p != nullid]
1811 parents = [p for p in other.changelog.parents(n) if p != nullid]
1816 if opts.get('no_merges') and len(parents) == 2:
1812 if opts.get('no_merges') and len(parents) == 2:
1817 continue
1813 continue
1818 count += 1
1814 count += 1
1819 displayer.show(other[n])
1815 displayer.show(other[n])
1820 finally:
1816 finally:
1821 if hasattr(other, 'close'):
1817 if hasattr(other, 'close'):
1822 other.close()
1818 other.close()
1823 if cleanup:
1819 if cleanup:
1824 os.unlink(cleanup)
1820 os.unlink(cleanup)
1825
1821
1826 def init(ui, dest=".", **opts):
1822 def init(ui, dest=".", **opts):
1827 """create a new repository in the given directory
1823 """create a new repository in the given directory
1828
1824
1829 Initialize a new repository in the given directory. If the given
1825 Initialize a new repository in the given directory. If the given
1830 directory does not exist, it is created.
1826 directory does not exist, it is created.
1831
1827
1832 If no directory is given, the current directory is used.
1828 If no directory is given, the current directory is used.
1833
1829
1834 It is possible to specify an ssh:// URL as the destination.
1830 It is possible to specify an ssh:// URL as the destination.
1835 See 'hg help urls' for more information.
1831 See 'hg help urls' for more information.
1836 """
1832 """
1837 cmdutil.setremoteconfig(ui, opts)
1833 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1838 hg.repository(ui, dest, create=1)
1839
1834
1840 def locate(ui, repo, *pats, **opts):
1835 def locate(ui, repo, *pats, **opts):
1841 """locate files matching specific patterns
1836 """locate files matching specific patterns
1842
1837
1843 Print all files under Mercurial control whose names match the
1838 Print all files under Mercurial control whose names match the
1844 given patterns.
1839 given patterns.
1845
1840
1846 This command searches the entire repository by default. To search
1841 This command searches the entire repository by default. To search
1847 just the current directory and its subdirectories, use
1842 just the current directory and its subdirectories, use
1848 "--include .".
1843 "--include .".
1849
1844
1850 If no patterns are given to match, this command prints all file
1845 If no patterns are given to match, this command prints all file
1851 names.
1846 names.
1852
1847
1853 If you want to feed the output of this command into the "xargs"
1848 If you want to feed the output of this command into the "xargs"
1854 command, use the -0 option to both this command and "xargs". This
1849 command, use the -0 option to both this command and "xargs". This
1855 will avoid the problem of "xargs" treating single filenames that
1850 will avoid the problem of "xargs" treating single filenames that
1856 contain white space as multiple filenames.
1851 contain white space as multiple filenames.
1857 """
1852 """
1858 end = opts.get('print0') and '\0' or '\n'
1853 end = opts.get('print0') and '\0' or '\n'
1859 rev = opts.get('rev') or None
1854 rev = opts.get('rev') or None
1860
1855
1861 ret = 1
1856 ret = 1
1862 m = cmdutil.match(repo, pats, opts, default='relglob')
1857 m = cmdutil.match(repo, pats, opts, default='relglob')
1863 m.bad = lambda x,y: False
1858 m.bad = lambda x,y: False
1864 for abs in repo[rev].walk(m):
1859 for abs in repo[rev].walk(m):
1865 if not rev and abs not in repo.dirstate:
1860 if not rev and abs not in repo.dirstate:
1866 continue
1861 continue
1867 if opts.get('fullpath'):
1862 if opts.get('fullpath'):
1868 ui.write(repo.wjoin(abs), end)
1863 ui.write(repo.wjoin(abs), end)
1869 else:
1864 else:
1870 ui.write(((pats and m.rel(abs)) or abs), end)
1865 ui.write(((pats and m.rel(abs)) or abs), end)
1871 ret = 0
1866 ret = 0
1872
1867
1873 return ret
1868 return ret
1874
1869
1875 def log(ui, repo, *pats, **opts):
1870 def log(ui, repo, *pats, **opts):
1876 """show revision history of entire repository or files
1871 """show revision history of entire repository or files
1877
1872
1878 Print the revision history of the specified files or the entire
1873 Print the revision history of the specified files or the entire
1879 project.
1874 project.
1880
1875
1881 File history is shown without following rename or copy history of
1876 File history is shown without following rename or copy history of
1882 files. Use -f/--follow with a file name to follow history across
1877 files. Use -f/--follow with a file name to follow history across
1883 renames and copies. --follow without a file name will only show
1878 renames and copies. --follow without a file name will only show
1884 ancestors or descendants of the starting revision. --follow-first
1879 ancestors or descendants of the starting revision. --follow-first
1885 only follows the first parent of merge revisions.
1880 only follows the first parent of merge revisions.
1886
1881
1887 If no revision range is specified, the default is tip:0 unless
1882 If no revision range is specified, the default is tip:0 unless
1888 --follow is set, in which case the working directory parent is
1883 --follow is set, in which case the working directory parent is
1889 used as the starting revision.
1884 used as the starting revision.
1890
1885
1891 See 'hg help dates' for a list of formats valid for -d/--date.
1886 See 'hg help dates' for a list of formats valid for -d/--date.
1892
1887
1893 By default this command outputs: changeset id and hash, tags,
1888 By default this command outputs: changeset id and hash, tags,
1894 non-trivial parents, user, date and time, and a summary for each
1889 non-trivial parents, user, date and time, and a summary for each
1895 commit. When the -v/--verbose switch is used, the list of changed
1890 commit. When the -v/--verbose switch is used, the list of changed
1896 files and full commit message is shown.
1891 files and full commit message is shown.
1897
1892
1898 NOTE: log -p/--patch may generate unexpected diff output for merge
1893 NOTE: log -p/--patch may generate unexpected diff output for merge
1899 changesets, as it will only compare the merge changeset against
1894 changesets, as it will only compare the merge changeset against
1900 its first parent. Also, the files: list will only reflect files
1895 its first parent. Also, the files: list will only reflect files
1901 that are different from BOTH parents.
1896 that are different from BOTH parents.
1902
1897
1903 """
1898 """
1904
1899
1905 get = util.cachefunc(lambda r: repo[r].changeset())
1900 get = util.cachefunc(lambda r: repo[r].changeset())
1906 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1901 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1907
1902
1908 limit = cmdutil.loglimit(opts)
1903 limit = cmdutil.loglimit(opts)
1909 count = 0
1904 count = 0
1910
1905
1911 if opts.get('copies') and opts.get('rev'):
1906 if opts.get('copies') and opts.get('rev'):
1912 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1907 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1913 else:
1908 else:
1914 endrev = len(repo)
1909 endrev = len(repo)
1915 rcache = {}
1910 rcache = {}
1916 ncache = {}
1911 ncache = {}
1917 def getrenamed(fn, rev):
1912 def getrenamed(fn, rev):
1918 '''looks up all renames for a file (up to endrev) the first
1913 '''looks up all renames for a file (up to endrev) the first
1919 time the file is given. It indexes on the changerev and only
1914 time the file is given. It indexes on the changerev and only
1920 parses the manifest if linkrev != changerev.
1915 parses the manifest if linkrev != changerev.
1921 Returns rename info for fn at changerev rev.'''
1916 Returns rename info for fn at changerev rev.'''
1922 if fn not in rcache:
1917 if fn not in rcache:
1923 rcache[fn] = {}
1918 rcache[fn] = {}
1924 ncache[fn] = {}
1919 ncache[fn] = {}
1925 fl = repo.file(fn)
1920 fl = repo.file(fn)
1926 for i in fl:
1921 for i in fl:
1927 node = fl.node(i)
1922 node = fl.node(i)
1928 lr = fl.linkrev(i)
1923 lr = fl.linkrev(i)
1929 renamed = fl.renamed(node)
1924 renamed = fl.renamed(node)
1930 rcache[fn][lr] = renamed
1925 rcache[fn][lr] = renamed
1931 if renamed:
1926 if renamed:
1932 ncache[fn][node] = renamed
1927 ncache[fn][node] = renamed
1933 if lr >= endrev:
1928 if lr >= endrev:
1934 break
1929 break
1935 if rev in rcache[fn]:
1930 if rev in rcache[fn]:
1936 return rcache[fn][rev]
1931 return rcache[fn][rev]
1937
1932
1938 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1933 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1939 # filectx logic.
1934 # filectx logic.
1940
1935
1941 try:
1936 try:
1942 return repo[rev][fn].renamed()
1937 return repo[rev][fn].renamed()
1943 except error.LookupError:
1938 except error.LookupError:
1944 pass
1939 pass
1945 return None
1940 return None
1946
1941
1947 df = False
1942 df = False
1948 if opts["date"]:
1943 if opts["date"]:
1949 df = util.matchdate(opts["date"])
1944 df = util.matchdate(opts["date"])
1950
1945
1951 only_branches = opts.get('only_branch')
1946 only_branches = opts.get('only_branch')
1952
1947
1953 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1948 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1954 for st, rev, fns in changeiter:
1949 for st, rev, fns in changeiter:
1955 if st == 'add':
1950 if st == 'add':
1956 parents = [p for p in repo.changelog.parentrevs(rev)
1951 parents = [p for p in repo.changelog.parentrevs(rev)
1957 if p != nullrev]
1952 if p != nullrev]
1958 if opts.get('no_merges') and len(parents) == 2:
1953 if opts.get('no_merges') and len(parents) == 2:
1959 continue
1954 continue
1960 if opts.get('only_merges') and len(parents) != 2:
1955 if opts.get('only_merges') and len(parents) != 2:
1961 continue
1956 continue
1962
1957
1963 if only_branches:
1958 if only_branches:
1964 revbranch = get(rev)[5]['branch']
1959 revbranch = get(rev)[5]['branch']
1965 if revbranch not in only_branches:
1960 if revbranch not in only_branches:
1966 continue
1961 continue
1967
1962
1968 if df:
1963 if df:
1969 changes = get(rev)
1964 changes = get(rev)
1970 if not df(changes[2][0]):
1965 if not df(changes[2][0]):
1971 continue
1966 continue
1972
1967
1973 if opts.get('keyword'):
1968 if opts.get('keyword'):
1974 changes = get(rev)
1969 changes = get(rev)
1975 miss = 0
1970 miss = 0
1976 for k in [kw.lower() for kw in opts['keyword']]:
1971 for k in [kw.lower() for kw in opts['keyword']]:
1977 if not (k in changes[1].lower() or
1972 if not (k in changes[1].lower() or
1978 k in changes[4].lower() or
1973 k in changes[4].lower() or
1979 k in " ".join(changes[3]).lower()):
1974 k in " ".join(changes[3]).lower()):
1980 miss = 1
1975 miss = 1
1981 break
1976 break
1982 if miss:
1977 if miss:
1983 continue
1978 continue
1984
1979
1985 if opts['user']:
1980 if opts['user']:
1986 changes = get(rev)
1981 changes = get(rev)
1987 if not [k for k in opts['user'] if k in changes[1]]:
1982 if not [k for k in opts['user'] if k in changes[1]]:
1988 continue
1983 continue
1989
1984
1990 copies = []
1985 copies = []
1991 if opts.get('copies') and rev:
1986 if opts.get('copies') and rev:
1992 for fn in get(rev)[3]:
1987 for fn in get(rev)[3]:
1993 rename = getrenamed(fn, rev)
1988 rename = getrenamed(fn, rev)
1994 if rename:
1989 if rename:
1995 copies.append((fn, rename[0]))
1990 copies.append((fn, rename[0]))
1996 displayer.show(context.changectx(repo, rev), copies=copies)
1991 displayer.show(context.changectx(repo, rev), copies=copies)
1997 elif st == 'iter':
1992 elif st == 'iter':
1998 if count == limit: break
1993 if count == limit: break
1999 if displayer.flush(rev):
1994 if displayer.flush(rev):
2000 count += 1
1995 count += 1
2001
1996
2002 def manifest(ui, repo, node=None, rev=None):
1997 def manifest(ui, repo, node=None, rev=None):
2003 """output the current or given revision of the project manifest
1998 """output the current or given revision of the project manifest
2004
1999
2005 Print a list of version controlled files for the given revision.
2000 Print a list of version controlled files for the given revision.
2006 If no revision is given, the first parent of the working directory
2001 If no revision is given, the first parent of the working directory
2007 is used, or the null revision if none is checked out.
2002 is used, or the null revision if none is checked out.
2008
2003
2009 With -v flag, print file permissions, symlink and executable bits.
2004 With -v flag, print file permissions, symlink and executable bits.
2010 With --debug flag, print file revision hashes.
2005 With --debug flag, print file revision hashes.
2011 """
2006 """
2012
2007
2013 if rev and node:
2008 if rev and node:
2014 raise util.Abort(_("please specify just one revision"))
2009 raise util.Abort(_("please specify just one revision"))
2015
2010
2016 if not node:
2011 if not node:
2017 node = rev
2012 node = rev
2018
2013
2019 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2014 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2020 ctx = repo[node]
2015 ctx = repo[node]
2021 for f in ctx:
2016 for f in ctx:
2022 if ui.debugflag:
2017 if ui.debugflag:
2023 ui.write("%40s " % hex(ctx.manifest()[f]))
2018 ui.write("%40s " % hex(ctx.manifest()[f]))
2024 if ui.verbose:
2019 if ui.verbose:
2025 ui.write(decor[ctx.flags(f)])
2020 ui.write(decor[ctx.flags(f)])
2026 ui.write("%s\n" % f)
2021 ui.write("%s\n" % f)
2027
2022
2028 def merge(ui, repo, node=None, force=None, rev=None):
2023 def merge(ui, repo, node=None, force=None, rev=None):
2029 """merge working directory with another revision
2024 """merge working directory with another revision
2030
2025
2031 The contents of the current working directory is updated with all
2026 The contents of the current working directory is updated with all
2032 changes made in the requested revision since the last common
2027 changes made in the requested revision since the last common
2033 predecessor revision.
2028 predecessor revision.
2034
2029
2035 Files that changed between either parent are marked as changed for
2030 Files that changed between either parent are marked as changed for
2036 the next commit and a commit must be performed before any further
2031 the next commit and a commit must be performed before any further
2037 updates are allowed. The next commit has two parents.
2032 updates are allowed. The next commit has two parents.
2038
2033
2039 If no revision is specified, the working directory's parent is a
2034 If no revision is specified, the working directory's parent is a
2040 head revision, and the current branch contains exactly one other
2035 head revision, and the current branch contains exactly one other
2041 head, the other head is merged with by default. Otherwise, an
2036 head, the other head is merged with by default. Otherwise, an
2042 explicit revision to merge with must be provided.
2037 explicit revision to merge with must be provided.
2043 """
2038 """
2044
2039
2045 if rev and node:
2040 if rev and node:
2046 raise util.Abort(_("please specify just one revision"))
2041 raise util.Abort(_("please specify just one revision"))
2047 if not node:
2042 if not node:
2048 node = rev
2043 node = rev
2049
2044
2050 if not node:
2045 if not node:
2051 branch = repo.changectx(None).branch()
2046 branch = repo.changectx(None).branch()
2052 bheads = repo.branchheads(branch)
2047 bheads = repo.branchheads(branch)
2053 if len(bheads) > 2:
2048 if len(bheads) > 2:
2054 raise util.Abort(_("branch '%s' has %d heads - "
2049 raise util.Abort(_("branch '%s' has %d heads - "
2055 "please merge with an explicit rev") %
2050 "please merge with an explicit rev") %
2056 (branch, len(bheads)))
2051 (branch, len(bheads)))
2057
2052
2058 parent = repo.dirstate.parents()[0]
2053 parent = repo.dirstate.parents()[0]
2059 if len(bheads) == 1:
2054 if len(bheads) == 1:
2060 if len(repo.heads()) > 1:
2055 if len(repo.heads()) > 1:
2061 raise util.Abort(_("branch '%s' has one head - "
2056 raise util.Abort(_("branch '%s' has one head - "
2062 "please merge with an explicit rev") %
2057 "please merge with an explicit rev") %
2063 branch)
2058 branch)
2064 msg = _('there is nothing to merge')
2059 msg = _('there is nothing to merge')
2065 if parent != repo.lookup(repo[None].branch()):
2060 if parent != repo.lookup(repo[None].branch()):
2066 msg = _('%s - use "hg update" instead') % msg
2061 msg = _('%s - use "hg update" instead') % msg
2067 raise util.Abort(msg)
2062 raise util.Abort(msg)
2068
2063
2069 if parent not in bheads:
2064 if parent not in bheads:
2070 raise util.Abort(_('working dir not at a head rev - '
2065 raise util.Abort(_('working dir not at a head rev - '
2071 'use "hg update" or merge with an explicit rev'))
2066 'use "hg update" or merge with an explicit rev'))
2072 node = parent == bheads[0] and bheads[-1] or bheads[0]
2067 node = parent == bheads[0] and bheads[-1] or bheads[0]
2073 return hg.merge(repo, node, force=force)
2068 return hg.merge(repo, node, force=force)
2074
2069
2075 def outgoing(ui, repo, dest=None, **opts):
2070 def outgoing(ui, repo, dest=None, **opts):
2076 """show changesets not found in destination
2071 """show changesets not found in destination
2077
2072
2078 Show changesets not found in the specified destination repository
2073 Show changesets not found in the specified destination repository
2079 or the default push location. These are the changesets that would
2074 or the default push location. These are the changesets that would
2080 be pushed if a push was requested.
2075 be pushed if a push was requested.
2081
2076
2082 See pull for valid destination format details.
2077 See pull for valid destination format details.
2083 """
2078 """
2084 limit = cmdutil.loglimit(opts)
2079 limit = cmdutil.loglimit(opts)
2085 dest, revs, checkout = hg.parseurl(
2080 dest, revs, checkout = hg.parseurl(
2086 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2081 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2087 cmdutil.setremoteconfig(ui, opts)
2088 if revs:
2082 if revs:
2089 revs = [repo.lookup(rev) for rev in revs]
2083 revs = [repo.lookup(rev) for rev in revs]
2090
2084
2091 other = hg.repository(ui, dest)
2085 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2092 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2086 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2093 o = repo.findoutgoing(other, force=opts.get('force'))
2087 o = repo.findoutgoing(other, force=opts.get('force'))
2094 if not o:
2088 if not o:
2095 ui.status(_("no changes found\n"))
2089 ui.status(_("no changes found\n"))
2096 return 1
2090 return 1
2097 o = repo.changelog.nodesbetween(o, revs)[0]
2091 o = repo.changelog.nodesbetween(o, revs)[0]
2098 if opts.get('newest_first'):
2092 if opts.get('newest_first'):
2099 o.reverse()
2093 o.reverse()
2100 displayer = cmdutil.show_changeset(ui, repo, opts)
2094 displayer = cmdutil.show_changeset(ui, repo, opts)
2101 count = 0
2095 count = 0
2102 for n in o:
2096 for n in o:
2103 if count >= limit:
2097 if count >= limit:
2104 break
2098 break
2105 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2099 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2106 if opts.get('no_merges') and len(parents) == 2:
2100 if opts.get('no_merges') and len(parents) == 2:
2107 continue
2101 continue
2108 count += 1
2102 count += 1
2109 displayer.show(repo[n])
2103 displayer.show(repo[n])
2110
2104
2111 def parents(ui, repo, file_=None, **opts):
2105 def parents(ui, repo, file_=None, **opts):
2112 """show the parents of the working directory or revision
2106 """show the parents of the working directory or revision
2113
2107
2114 Print the working directory's parent revisions. If a revision is
2108 Print the working directory's parent revisions. If a revision is
2115 given via -r/--rev, the parent of that revision will be printed.
2109 given via -r/--rev, the parent of that revision will be printed.
2116 If a file argument is given, revision in which the file was last
2110 If a file argument is given, revision in which the file was last
2117 changed (before the working directory revision or the argument to
2111 changed (before the working directory revision or the argument to
2118 --rev if given) is printed.
2112 --rev if given) is printed.
2119 """
2113 """
2120 rev = opts.get('rev')
2114 rev = opts.get('rev')
2121 if rev:
2115 if rev:
2122 ctx = repo[rev]
2116 ctx = repo[rev]
2123 else:
2117 else:
2124 ctx = repo[None]
2118 ctx = repo[None]
2125
2119
2126 if file_:
2120 if file_:
2127 m = cmdutil.match(repo, (file_,), opts)
2121 m = cmdutil.match(repo, (file_,), opts)
2128 if m.anypats() or len(m.files()) != 1:
2122 if m.anypats() or len(m.files()) != 1:
2129 raise util.Abort(_('can only specify an explicit file name'))
2123 raise util.Abort(_('can only specify an explicit file name'))
2130 file_ = m.files()[0]
2124 file_ = m.files()[0]
2131 filenodes = []
2125 filenodes = []
2132 for cp in ctx.parents():
2126 for cp in ctx.parents():
2133 if not cp:
2127 if not cp:
2134 continue
2128 continue
2135 try:
2129 try:
2136 filenodes.append(cp.filenode(file_))
2130 filenodes.append(cp.filenode(file_))
2137 except error.LookupError:
2131 except error.LookupError:
2138 pass
2132 pass
2139 if not filenodes:
2133 if not filenodes:
2140 raise util.Abort(_("'%s' not found in manifest!") % file_)
2134 raise util.Abort(_("'%s' not found in manifest!") % file_)
2141 fl = repo.file(file_)
2135 fl = repo.file(file_)
2142 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2136 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2143 else:
2137 else:
2144 p = [cp.node() for cp in ctx.parents()]
2138 p = [cp.node() for cp in ctx.parents()]
2145
2139
2146 displayer = cmdutil.show_changeset(ui, repo, opts)
2140 displayer = cmdutil.show_changeset(ui, repo, opts)
2147 for n in p:
2141 for n in p:
2148 if n != nullid:
2142 if n != nullid:
2149 displayer.show(repo[n])
2143 displayer.show(repo[n])
2150
2144
2151 def paths(ui, repo, search=None):
2145 def paths(ui, repo, search=None):
2152 """show aliases for remote repositories
2146 """show aliases for remote repositories
2153
2147
2154 Show definition of symbolic path name NAME. If no name is given,
2148 Show definition of symbolic path name NAME. If no name is given,
2155 show definition of available names.
2149 show definition of available names.
2156
2150
2157 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2151 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2158 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2152 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2159
2153
2160 See 'hg help urls' for more information.
2154 See 'hg help urls' for more information.
2161 """
2155 """
2162 if search:
2156 if search:
2163 for name, path in ui.configitems("paths"):
2157 for name, path in ui.configitems("paths"):
2164 if name == search:
2158 if name == search:
2165 ui.write("%s\n" % url.hidepassword(path))
2159 ui.write("%s\n" % url.hidepassword(path))
2166 return
2160 return
2167 ui.warn(_("not found!\n"))
2161 ui.warn(_("not found!\n"))
2168 return 1
2162 return 1
2169 else:
2163 else:
2170 for name, path in ui.configitems("paths"):
2164 for name, path in ui.configitems("paths"):
2171 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2165 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2172
2166
2173 def postincoming(ui, repo, modheads, optupdate, checkout):
2167 def postincoming(ui, repo, modheads, optupdate, checkout):
2174 if modheads == 0:
2168 if modheads == 0:
2175 return
2169 return
2176 if optupdate:
2170 if optupdate:
2177 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2171 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2178 return hg.update(repo, checkout)
2172 return hg.update(repo, checkout)
2179 else:
2173 else:
2180 ui.status(_("not updating, since new heads added\n"))
2174 ui.status(_("not updating, since new heads added\n"))
2181 if modheads > 1:
2175 if modheads > 1:
2182 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2176 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2183 else:
2177 else:
2184 ui.status(_("(run 'hg update' to get a working copy)\n"))
2178 ui.status(_("(run 'hg update' to get a working copy)\n"))
2185
2179
2186 def pull(ui, repo, source="default", **opts):
2180 def pull(ui, repo, source="default", **opts):
2187 """pull changes from the specified source
2181 """pull changes from the specified source
2188
2182
2189 Pull changes from a remote repository to the local one.
2183 Pull changes from a remote repository to the local one.
2190
2184
2191 This finds all changes from the repository at the specified path
2185 This finds all changes from the repository at the specified path
2192 or URL and adds them to the local repository. By default, this
2186 or URL and adds them to the local repository. By default, this
2193 does not update the copy of the project in the working directory.
2187 does not update the copy of the project in the working directory.
2194
2188
2195 Use hg incoming if you want to see what will be added by the next
2189 Use hg incoming if you want to see what will be added by the next
2196 pull without actually adding the changes to the repository.
2190 pull without actually adding the changes to the repository.
2197
2191
2198 If SOURCE is omitted, the 'default' path will be used.
2192 If SOURCE is omitted, the 'default' path will be used.
2199 See 'hg help urls' for more information.
2193 See 'hg help urls' for more information.
2200 """
2194 """
2201 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2195 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2202 cmdutil.setremoteconfig(ui, opts)
2196 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2203
2204 other = hg.repository(ui, source)
2205 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2197 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2206 if revs:
2198 if revs:
2207 try:
2199 try:
2208 revs = [other.lookup(rev) for rev in revs]
2200 revs = [other.lookup(rev) for rev in revs]
2209 except error.CapabilityError:
2201 except error.CapabilityError:
2210 err = _("Other repository doesn't support revision lookup, "
2202 err = _("Other repository doesn't support revision lookup, "
2211 "so a rev cannot be specified.")
2203 "so a rev cannot be specified.")
2212 raise util.Abort(err)
2204 raise util.Abort(err)
2213
2205
2214 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2206 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2215 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2207 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2216
2208
2217 def push(ui, repo, dest=None, **opts):
2209 def push(ui, repo, dest=None, **opts):
2218 """push changes to the specified destination
2210 """push changes to the specified destination
2219
2211
2220 Push changes from the local repository to the given destination.
2212 Push changes from the local repository to the given destination.
2221
2213
2222 This is the symmetrical operation for pull. It moves changes from
2214 This is the symmetrical operation for pull. It moves changes from
2223 the current repository to a different one. If the destination is
2215 the current repository to a different one. If the destination is
2224 local this is identical to a pull in that directory from the
2216 local this is identical to a pull in that directory from the
2225 current one.
2217 current one.
2226
2218
2227 By default, push will refuse to run if it detects the result would
2219 By default, push will refuse to run if it detects the result would
2228 increase the number of remote heads. This generally indicates the
2220 increase the number of remote heads. This generally indicates the
2229 the client has forgotten to pull and merge before pushing.
2221 the client has forgotten to pull and merge before pushing.
2230
2222
2231 If -r/--rev is used, the named revision and all its ancestors will
2223 If -r/--rev is used, the named revision and all its ancestors will
2232 be pushed to the remote repository.
2224 be pushed to the remote repository.
2233
2225
2234 Look at the help text for URLs for important details about ssh://
2226 Look at the help text for URLs for important details about ssh://
2235 URLs. If DESTINATION is omitted, a default path will be used.
2227 URLs. If DESTINATION is omitted, a default path will be used.
2236 See 'hg help urls' for more information.
2228 See 'hg help urls' for more information.
2237 """
2229 """
2238 dest, revs, checkout = hg.parseurl(
2230 dest, revs, checkout = hg.parseurl(
2239 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2231 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2240 cmdutil.setremoteconfig(ui, opts)
2232 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2241
2242 other = hg.repository(ui, dest)
2243 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2233 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2244 if revs:
2234 if revs:
2245 revs = [repo.lookup(rev) for rev in revs]
2235 revs = [repo.lookup(rev) for rev in revs]
2246 r = repo.push(other, opts.get('force'), revs=revs)
2236 r = repo.push(other, opts.get('force'), revs=revs)
2247 return r == 0
2237 return r == 0
2248
2238
2249 def rawcommit(ui, repo, *pats, **opts):
2239 def rawcommit(ui, repo, *pats, **opts):
2250 """raw commit interface (DEPRECATED)
2240 """raw commit interface (DEPRECATED)
2251
2241
2252 (DEPRECATED)
2242 (DEPRECATED)
2253 Lowlevel commit, for use in helper scripts.
2243 Lowlevel commit, for use in helper scripts.
2254
2244
2255 This command is not intended to be used by normal users, as it is
2245 This command is not intended to be used by normal users, as it is
2256 primarily useful for importing from other SCMs.
2246 primarily useful for importing from other SCMs.
2257
2247
2258 This command is now deprecated and will be removed in a future
2248 This command is now deprecated and will be removed in a future
2259 release, please use debugsetparents and commit instead.
2249 release, please use debugsetparents and commit instead.
2260 """
2250 """
2261
2251
2262 ui.warn(_("(the rawcommit command is deprecated)\n"))
2252 ui.warn(_("(the rawcommit command is deprecated)\n"))
2263
2253
2264 message = cmdutil.logmessage(opts)
2254 message = cmdutil.logmessage(opts)
2265
2255
2266 files = cmdutil.match(repo, pats, opts).files()
2256 files = cmdutil.match(repo, pats, opts).files()
2267 if opts.get('files'):
2257 if opts.get('files'):
2268 files += open(opts['files']).read().splitlines()
2258 files += open(opts['files']).read().splitlines()
2269
2259
2270 parents = [repo.lookup(p) for p in opts['parent']]
2260 parents = [repo.lookup(p) for p in opts['parent']]
2271
2261
2272 try:
2262 try:
2273 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2263 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2274 except ValueError, inst:
2264 except ValueError, inst:
2275 raise util.Abort(str(inst))
2265 raise util.Abort(str(inst))
2276
2266
2277 def recover(ui, repo):
2267 def recover(ui, repo):
2278 """roll back an interrupted transaction
2268 """roll back an interrupted transaction
2279
2269
2280 Recover from an interrupted commit or pull.
2270 Recover from an interrupted commit or pull.
2281
2271
2282 This command tries to fix the repository status after an
2272 This command tries to fix the repository status after an
2283 interrupted operation. It should only be necessary when Mercurial
2273 interrupted operation. It should only be necessary when Mercurial
2284 suggests it.
2274 suggests it.
2285 """
2275 """
2286 if repo.recover():
2276 if repo.recover():
2287 return hg.verify(repo)
2277 return hg.verify(repo)
2288 return 1
2278 return 1
2289
2279
2290 def remove(ui, repo, *pats, **opts):
2280 def remove(ui, repo, *pats, **opts):
2291 """remove the specified files on the next commit
2281 """remove the specified files on the next commit
2292
2282
2293 Schedule the indicated files for removal from the repository.
2283 Schedule the indicated files for removal from the repository.
2294
2284
2295 This only removes files from the current branch, not from the
2285 This only removes files from the current branch, not from the
2296 entire project history. -A/--after can be used to remove only
2286 entire project history. -A/--after can be used to remove only
2297 files that have already been deleted, -f/--force can be used to
2287 files that have already been deleted, -f/--force can be used to
2298 force deletion, and -Af can be used to remove files from the next
2288 force deletion, and -Af can be used to remove files from the next
2299 revision without deleting them.
2289 revision without deleting them.
2300
2290
2301 The following table details the behavior of remove for different
2291 The following table details the behavior of remove for different
2302 file states (columns) and option combinations (rows). The file
2292 file states (columns) and option combinations (rows). The file
2303 states are Added, Clean, Modified and Missing (as reported by hg
2293 states are Added, Clean, Modified and Missing (as reported by hg
2304 status). The actions are Warn, Remove (from branch) and Delete
2294 status). The actions are Warn, Remove (from branch) and Delete
2305 (from disk).
2295 (from disk).
2306
2296
2307 A C M !
2297 A C M !
2308 none W RD W R
2298 none W RD W R
2309 -f R RD RD R
2299 -f R RD RD R
2310 -A W W W R
2300 -A W W W R
2311 -Af R R R R
2301 -Af R R R R
2312
2302
2313 This command schedules the files to be removed at the next commit.
2303 This command schedules the files to be removed at the next commit.
2314 To undo a remove before that, see hg revert.
2304 To undo a remove before that, see hg revert.
2315 """
2305 """
2316
2306
2317 after, force = opts.get('after'), opts.get('force')
2307 after, force = opts.get('after'), opts.get('force')
2318 if not pats and not after:
2308 if not pats and not after:
2319 raise util.Abort(_('no files specified'))
2309 raise util.Abort(_('no files specified'))
2320
2310
2321 m = cmdutil.match(repo, pats, opts)
2311 m = cmdutil.match(repo, pats, opts)
2322 s = repo.status(match=m, clean=True)
2312 s = repo.status(match=m, clean=True)
2323 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2313 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2324
2314
2325 def warn(files, reason):
2315 def warn(files, reason):
2326 for f in files:
2316 for f in files:
2327 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2317 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2328 % (m.rel(f), reason))
2318 % (m.rel(f), reason))
2329
2319
2330 if force:
2320 if force:
2331 remove, forget = modified + deleted + clean, added
2321 remove, forget = modified + deleted + clean, added
2332 elif after:
2322 elif after:
2333 remove, forget = deleted, []
2323 remove, forget = deleted, []
2334 warn(modified + added + clean, _('still exists'))
2324 warn(modified + added + clean, _('still exists'))
2335 else:
2325 else:
2336 remove, forget = deleted + clean, []
2326 remove, forget = deleted + clean, []
2337 warn(modified, _('is modified'))
2327 warn(modified, _('is modified'))
2338 warn(added, _('has been marked for add'))
2328 warn(added, _('has been marked for add'))
2339
2329
2340 for f in util.sort(remove + forget):
2330 for f in util.sort(remove + forget):
2341 if ui.verbose or not m.exact(f):
2331 if ui.verbose or not m.exact(f):
2342 ui.status(_('removing %s\n') % m.rel(f))
2332 ui.status(_('removing %s\n') % m.rel(f))
2343
2333
2344 repo.forget(forget)
2334 repo.forget(forget)
2345 repo.remove(remove, unlink=not after)
2335 repo.remove(remove, unlink=not after)
2346
2336
2347 def rename(ui, repo, *pats, **opts):
2337 def rename(ui, repo, *pats, **opts):
2348 """rename files; equivalent of copy + remove
2338 """rename files; equivalent of copy + remove
2349
2339
2350 Mark dest as copies of sources; mark sources for deletion. If dest
2340 Mark dest as copies of sources; mark sources for deletion. If dest
2351 is a directory, copies are put in that directory. If dest is a
2341 is a directory, copies are put in that directory. If dest is a
2352 file, there can only be one source.
2342 file, there can only be one source.
2353
2343
2354 By default, this command copies the contents of files as they
2344 By default, this command copies the contents of files as they
2355 exist in the working directory. If invoked with -A/--after, the
2345 exist in the working directory. If invoked with -A/--after, the
2356 operation is recorded, but no copying is performed.
2346 operation is recorded, but no copying is performed.
2357
2347
2358 This command takes effect at the next commit. To undo a rename
2348 This command takes effect at the next commit. To undo a rename
2359 before that, see hg revert.
2349 before that, see hg revert.
2360 """
2350 """
2361 wlock = repo.wlock(False)
2351 wlock = repo.wlock(False)
2362 try:
2352 try:
2363 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2353 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2364 finally:
2354 finally:
2365 wlock.release()
2355 wlock.release()
2366
2356
2367 def resolve(ui, repo, *pats, **opts):
2357 def resolve(ui, repo, *pats, **opts):
2368 """retry file merges from a merge or update
2358 """retry file merges from a merge or update
2369
2359
2370 This command will cleanly retry unresolved file merges using file
2360 This command will cleanly retry unresolved file merges using file
2371 revisions preserved from the last update or merge. To attempt to
2361 revisions preserved from the last update or merge. To attempt to
2372 resolve all unresolved files, use the -a/--all switch.
2362 resolve all unresolved files, use the -a/--all switch.
2373
2363
2374 If a conflict is resolved manually, please note that the changes
2364 If a conflict is resolved manually, please note that the changes
2375 will be overwritten if the merge is retried with resolve. The
2365 will be overwritten if the merge is retried with resolve. The
2376 -m/--mark switch should be used to mark the file as resolved.
2366 -m/--mark switch should be used to mark the file as resolved.
2377
2367
2378 This command will also allow listing resolved files and manually
2368 This command will also allow listing resolved files and manually
2379 marking and unmarking files as resolved. All files must be marked
2369 marking and unmarking files as resolved. All files must be marked
2380 as resolved before the new commits are permitted.
2370 as resolved before the new commits are permitted.
2381
2371
2382 The codes used to show the status of files are:
2372 The codes used to show the status of files are:
2383 U = unresolved
2373 U = unresolved
2384 R = resolved
2374 R = resolved
2385 """
2375 """
2386
2376
2387 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2377 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2388
2378
2389 if (show and (mark or unmark)) or (mark and unmark):
2379 if (show and (mark or unmark)) or (mark and unmark):
2390 raise util.Abort(_("too many options specified"))
2380 raise util.Abort(_("too many options specified"))
2391 if pats and all:
2381 if pats and all:
2392 raise util.Abort(_("can't specify --all and patterns"))
2382 raise util.Abort(_("can't specify --all and patterns"))
2393 if not (all or pats or show or mark or unmark):
2383 if not (all or pats or show or mark or unmark):
2394 raise util.Abort(_('no files or directories specified; '
2384 raise util.Abort(_('no files or directories specified; '
2395 'use --all to remerge all files'))
2385 'use --all to remerge all files'))
2396
2386
2397 ms = merge_.mergestate(repo)
2387 ms = merge_.mergestate(repo)
2398 m = cmdutil.match(repo, pats, opts)
2388 m = cmdutil.match(repo, pats, opts)
2399
2389
2400 for f in ms:
2390 for f in ms:
2401 if m(f):
2391 if m(f):
2402 if show:
2392 if show:
2403 ui.write("%s %s\n" % (ms[f].upper(), f))
2393 ui.write("%s %s\n" % (ms[f].upper(), f))
2404 elif mark:
2394 elif mark:
2405 ms.mark(f, "r")
2395 ms.mark(f, "r")
2406 elif unmark:
2396 elif unmark:
2407 ms.mark(f, "u")
2397 ms.mark(f, "u")
2408 else:
2398 else:
2409 wctx = repo[None]
2399 wctx = repo[None]
2410 mctx = wctx.parents()[-1]
2400 mctx = wctx.parents()[-1]
2411
2401
2412 # backup pre-resolve (merge uses .orig for its own purposes)
2402 # backup pre-resolve (merge uses .orig for its own purposes)
2413 a = repo.wjoin(f)
2403 a = repo.wjoin(f)
2414 util.copyfile(a, a + ".resolve")
2404 util.copyfile(a, a + ".resolve")
2415
2405
2416 # resolve file
2406 # resolve file
2417 ms.resolve(f, wctx, mctx)
2407 ms.resolve(f, wctx, mctx)
2418
2408
2419 # replace filemerge's .orig file with our resolve file
2409 # replace filemerge's .orig file with our resolve file
2420 util.rename(a + ".resolve", a + ".orig")
2410 util.rename(a + ".resolve", a + ".orig")
2421
2411
2422 def revert(ui, repo, *pats, **opts):
2412 def revert(ui, repo, *pats, **opts):
2423 """restore individual files or directories to an earlier state
2413 """restore individual files or directories to an earlier state
2424
2414
2425 (use update -r to check out earlier revisions, revert does not
2415 (use update -r to check out earlier revisions, revert does not
2426 change the working directory parents)
2416 change the working directory parents)
2427
2417
2428 With no revision specified, revert the named files or directories
2418 With no revision specified, revert the named files or directories
2429 to the contents they had in the parent of the working directory.
2419 to the contents they had in the parent of the working directory.
2430 This restores the contents of the affected files to an unmodified
2420 This restores the contents of the affected files to an unmodified
2431 state and unschedules adds, removes, copies, and renames. If the
2421 state and unschedules adds, removes, copies, and renames. If the
2432 working directory has two parents, you must explicitly specify the
2422 working directory has two parents, you must explicitly specify the
2433 revision to revert to.
2423 revision to revert to.
2434
2424
2435 Using the -r/--rev option, revert the given files or directories
2425 Using the -r/--rev option, revert the given files or directories
2436 to their contents as of a specific revision. This can be helpful
2426 to their contents as of a specific revision. This can be helpful
2437 to "roll back" some or all of an earlier change. See 'hg help
2427 to "roll back" some or all of an earlier change. See 'hg help
2438 dates' for a list of formats valid for -d/--date.
2428 dates' for a list of formats valid for -d/--date.
2439
2429
2440 Revert modifies the working directory. It does not commit any
2430 Revert modifies the working directory. It does not commit any
2441 changes, or change the parent of the working directory. If you
2431 changes, or change the parent of the working directory. If you
2442 revert to a revision other than the parent of the working
2432 revert to a revision other than the parent of the working
2443 directory, the reverted files will thus appear modified
2433 directory, the reverted files will thus appear modified
2444 afterwards.
2434 afterwards.
2445
2435
2446 If a file has been deleted, it is restored. If the executable mode
2436 If a file has been deleted, it is restored. If the executable mode
2447 of a file was changed, it is reset.
2437 of a file was changed, it is reset.
2448
2438
2449 If names are given, all files matching the names are reverted.
2439 If names are given, all files matching the names are reverted.
2450 If no arguments are given, no files are reverted.
2440 If no arguments are given, no files are reverted.
2451
2441
2452 Modified files are saved with a .orig suffix before reverting.
2442 Modified files are saved with a .orig suffix before reverting.
2453 To disable these backups, use --no-backup.
2443 To disable these backups, use --no-backup.
2454 """
2444 """
2455
2445
2456 if opts["date"]:
2446 if opts["date"]:
2457 if opts["rev"]:
2447 if opts["rev"]:
2458 raise util.Abort(_("you can't specify a revision and a date"))
2448 raise util.Abort(_("you can't specify a revision and a date"))
2459 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2449 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2460
2450
2461 if not pats and not opts.get('all'):
2451 if not pats and not opts.get('all'):
2462 raise util.Abort(_('no files or directories specified; '
2452 raise util.Abort(_('no files or directories specified; '
2463 'use --all to revert the whole repo'))
2453 'use --all to revert the whole repo'))
2464
2454
2465 parent, p2 = repo.dirstate.parents()
2455 parent, p2 = repo.dirstate.parents()
2466 if not opts.get('rev') and p2 != nullid:
2456 if not opts.get('rev') and p2 != nullid:
2467 raise util.Abort(_('uncommitted merge - please provide a '
2457 raise util.Abort(_('uncommitted merge - please provide a '
2468 'specific revision'))
2458 'specific revision'))
2469 ctx = repo[opts.get('rev')]
2459 ctx = repo[opts.get('rev')]
2470 node = ctx.node()
2460 node = ctx.node()
2471 mf = ctx.manifest()
2461 mf = ctx.manifest()
2472 if node == parent:
2462 if node == parent:
2473 pmf = mf
2463 pmf = mf
2474 else:
2464 else:
2475 pmf = None
2465 pmf = None
2476
2466
2477 # need all matching names in dirstate and manifest of target rev,
2467 # need all matching names in dirstate and manifest of target rev,
2478 # so have to walk both. do not print errors if files exist in one
2468 # so have to walk both. do not print errors if files exist in one
2479 # but not other.
2469 # but not other.
2480
2470
2481 names = {}
2471 names = {}
2482
2472
2483 wlock = repo.wlock()
2473 wlock = repo.wlock()
2484 try:
2474 try:
2485 # walk dirstate.
2475 # walk dirstate.
2486
2476
2487 m = cmdutil.match(repo, pats, opts)
2477 m = cmdutil.match(repo, pats, opts)
2488 m.bad = lambda x,y: False
2478 m.bad = lambda x,y: False
2489 for abs in repo.walk(m):
2479 for abs in repo.walk(m):
2490 names[abs] = m.rel(abs), m.exact(abs)
2480 names[abs] = m.rel(abs), m.exact(abs)
2491
2481
2492 # walk target manifest.
2482 # walk target manifest.
2493
2483
2494 def badfn(path, msg):
2484 def badfn(path, msg):
2495 if path in names:
2485 if path in names:
2496 return False
2486 return False
2497 path_ = path + '/'
2487 path_ = path + '/'
2498 for f in names:
2488 for f in names:
2499 if f.startswith(path_):
2489 if f.startswith(path_):
2500 return False
2490 return False
2501 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2491 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2502 return False
2492 return False
2503
2493
2504 m = cmdutil.match(repo, pats, opts)
2494 m = cmdutil.match(repo, pats, opts)
2505 m.bad = badfn
2495 m.bad = badfn
2506 for abs in repo[node].walk(m):
2496 for abs in repo[node].walk(m):
2507 if abs not in names:
2497 if abs not in names:
2508 names[abs] = m.rel(abs), m.exact(abs)
2498 names[abs] = m.rel(abs), m.exact(abs)
2509
2499
2510 m = cmdutil.matchfiles(repo, names)
2500 m = cmdutil.matchfiles(repo, names)
2511 changes = repo.status(match=m)[:4]
2501 changes = repo.status(match=m)[:4]
2512 modified, added, removed, deleted = map(set, changes)
2502 modified, added, removed, deleted = map(set, changes)
2513
2503
2514 # if f is a rename, also revert the source
2504 # if f is a rename, also revert the source
2515 cwd = repo.getcwd()
2505 cwd = repo.getcwd()
2516 for f in added:
2506 for f in added:
2517 src = repo.dirstate.copied(f)
2507 src = repo.dirstate.copied(f)
2518 if src and src not in names and repo.dirstate[src] == 'r':
2508 if src and src not in names and repo.dirstate[src] == 'r':
2519 removed.add(src)
2509 removed.add(src)
2520 names[src] = (repo.pathto(src, cwd), True)
2510 names[src] = (repo.pathto(src, cwd), True)
2521
2511
2522 def removeforget(abs):
2512 def removeforget(abs):
2523 if repo.dirstate[abs] == 'a':
2513 if repo.dirstate[abs] == 'a':
2524 return _('forgetting %s\n')
2514 return _('forgetting %s\n')
2525 return _('removing %s\n')
2515 return _('removing %s\n')
2526
2516
2527 revert = ([], _('reverting %s\n'))
2517 revert = ([], _('reverting %s\n'))
2528 add = ([], _('adding %s\n'))
2518 add = ([], _('adding %s\n'))
2529 remove = ([], removeforget)
2519 remove = ([], removeforget)
2530 undelete = ([], _('undeleting %s\n'))
2520 undelete = ([], _('undeleting %s\n'))
2531
2521
2532 disptable = (
2522 disptable = (
2533 # dispatch table:
2523 # dispatch table:
2534 # file state
2524 # file state
2535 # action if in target manifest
2525 # action if in target manifest
2536 # action if not in target manifest
2526 # action if not in target manifest
2537 # make backup if in target manifest
2527 # make backup if in target manifest
2538 # make backup if not in target manifest
2528 # make backup if not in target manifest
2539 (modified, revert, remove, True, True),
2529 (modified, revert, remove, True, True),
2540 (added, revert, remove, True, False),
2530 (added, revert, remove, True, False),
2541 (removed, undelete, None, False, False),
2531 (removed, undelete, None, False, False),
2542 (deleted, revert, remove, False, False),
2532 (deleted, revert, remove, False, False),
2543 )
2533 )
2544
2534
2545 for abs, (rel, exact) in util.sort(names.items()):
2535 for abs, (rel, exact) in util.sort(names.items()):
2546 mfentry = mf.get(abs)
2536 mfentry = mf.get(abs)
2547 target = repo.wjoin(abs)
2537 target = repo.wjoin(abs)
2548 def handle(xlist, dobackup):
2538 def handle(xlist, dobackup):
2549 xlist[0].append(abs)
2539 xlist[0].append(abs)
2550 if dobackup and not opts.get('no_backup') and util.lexists(target):
2540 if dobackup and not opts.get('no_backup') and util.lexists(target):
2551 bakname = "%s.orig" % rel
2541 bakname = "%s.orig" % rel
2552 ui.note(_('saving current version of %s as %s\n') %
2542 ui.note(_('saving current version of %s as %s\n') %
2553 (rel, bakname))
2543 (rel, bakname))
2554 if not opts.get('dry_run'):
2544 if not opts.get('dry_run'):
2555 util.copyfile(target, bakname)
2545 util.copyfile(target, bakname)
2556 if ui.verbose or not exact:
2546 if ui.verbose or not exact:
2557 msg = xlist[1]
2547 msg = xlist[1]
2558 if not isinstance(msg, basestring):
2548 if not isinstance(msg, basestring):
2559 msg = msg(abs)
2549 msg = msg(abs)
2560 ui.status(msg % rel)
2550 ui.status(msg % rel)
2561 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2551 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2562 if abs not in table: continue
2552 if abs not in table: continue
2563 # file has changed in dirstate
2553 # file has changed in dirstate
2564 if mfentry:
2554 if mfentry:
2565 handle(hitlist, backuphit)
2555 handle(hitlist, backuphit)
2566 elif misslist is not None:
2556 elif misslist is not None:
2567 handle(misslist, backupmiss)
2557 handle(misslist, backupmiss)
2568 break
2558 break
2569 else:
2559 else:
2570 if abs not in repo.dirstate:
2560 if abs not in repo.dirstate:
2571 if mfentry:
2561 if mfentry:
2572 handle(add, True)
2562 handle(add, True)
2573 elif exact:
2563 elif exact:
2574 ui.warn(_('file not managed: %s\n') % rel)
2564 ui.warn(_('file not managed: %s\n') % rel)
2575 continue
2565 continue
2576 # file has not changed in dirstate
2566 # file has not changed in dirstate
2577 if node == parent:
2567 if node == parent:
2578 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2568 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2579 continue
2569 continue
2580 if pmf is None:
2570 if pmf is None:
2581 # only need parent manifest in this unlikely case,
2571 # only need parent manifest in this unlikely case,
2582 # so do not read by default
2572 # so do not read by default
2583 pmf = repo[parent].manifest()
2573 pmf = repo[parent].manifest()
2584 if abs in pmf:
2574 if abs in pmf:
2585 if mfentry:
2575 if mfentry:
2586 # if version of file is same in parent and target
2576 # if version of file is same in parent and target
2587 # manifests, do nothing
2577 # manifests, do nothing
2588 if (pmf[abs] != mfentry or
2578 if (pmf[abs] != mfentry or
2589 pmf.flags(abs) != mf.flags(abs)):
2579 pmf.flags(abs) != mf.flags(abs)):
2590 handle(revert, False)
2580 handle(revert, False)
2591 else:
2581 else:
2592 handle(remove, False)
2582 handle(remove, False)
2593
2583
2594 if not opts.get('dry_run'):
2584 if not opts.get('dry_run'):
2595 def checkout(f):
2585 def checkout(f):
2596 fc = ctx[f]
2586 fc = ctx[f]
2597 repo.wwrite(f, fc.data(), fc.flags())
2587 repo.wwrite(f, fc.data(), fc.flags())
2598
2588
2599 audit_path = util.path_auditor(repo.root)
2589 audit_path = util.path_auditor(repo.root)
2600 for f in remove[0]:
2590 for f in remove[0]:
2601 if repo.dirstate[f] == 'a':
2591 if repo.dirstate[f] == 'a':
2602 repo.dirstate.forget(f)
2592 repo.dirstate.forget(f)
2603 continue
2593 continue
2604 audit_path(f)
2594 audit_path(f)
2605 try:
2595 try:
2606 util.unlink(repo.wjoin(f))
2596 util.unlink(repo.wjoin(f))
2607 except OSError:
2597 except OSError:
2608 pass
2598 pass
2609 repo.dirstate.remove(f)
2599 repo.dirstate.remove(f)
2610
2600
2611 normal = None
2601 normal = None
2612 if node == parent:
2602 if node == parent:
2613 # We're reverting to our parent. If possible, we'd like status
2603 # We're reverting to our parent. If possible, we'd like status
2614 # to report the file as clean. We have to use normallookup for
2604 # to report the file as clean. We have to use normallookup for
2615 # merges to avoid losing information about merged/dirty files.
2605 # merges to avoid losing information about merged/dirty files.
2616 if p2 != nullid:
2606 if p2 != nullid:
2617 normal = repo.dirstate.normallookup
2607 normal = repo.dirstate.normallookup
2618 else:
2608 else:
2619 normal = repo.dirstate.normal
2609 normal = repo.dirstate.normal
2620 for f in revert[0]:
2610 for f in revert[0]:
2621 checkout(f)
2611 checkout(f)
2622 if normal:
2612 if normal:
2623 normal(f)
2613 normal(f)
2624
2614
2625 for f in add[0]:
2615 for f in add[0]:
2626 checkout(f)
2616 checkout(f)
2627 repo.dirstate.add(f)
2617 repo.dirstate.add(f)
2628
2618
2629 normal = repo.dirstate.normallookup
2619 normal = repo.dirstate.normallookup
2630 if node == parent and p2 == nullid:
2620 if node == parent and p2 == nullid:
2631 normal = repo.dirstate.normal
2621 normal = repo.dirstate.normal
2632 for f in undelete[0]:
2622 for f in undelete[0]:
2633 checkout(f)
2623 checkout(f)
2634 normal(f)
2624 normal(f)
2635
2625
2636 finally:
2626 finally:
2637 wlock.release()
2627 wlock.release()
2638
2628
2639 def rollback(ui, repo):
2629 def rollback(ui, repo):
2640 """roll back the last transaction
2630 """roll back the last transaction
2641
2631
2642 This command should be used with care. There is only one level of
2632 This command should be used with care. There is only one level of
2643 rollback, and there is no way to undo a rollback. It will also
2633 rollback, and there is no way to undo a rollback. It will also
2644 restore the dirstate at the time of the last transaction, losing
2634 restore the dirstate at the time of the last transaction, losing
2645 any dirstate changes since that time.
2635 any dirstate changes since that time.
2646
2636
2647 Transactions are used to encapsulate the effects of all commands
2637 Transactions are used to encapsulate the effects of all commands
2648 that create new changesets or propagate existing changesets into a
2638 that create new changesets or propagate existing changesets into a
2649 repository. For example, the following commands are transactional,
2639 repository. For example, the following commands are transactional,
2650 and their effects can be rolled back:
2640 and their effects can be rolled back:
2651
2641
2652 commit
2642 commit
2653 import
2643 import
2654 pull
2644 pull
2655 push (with this repository as destination)
2645 push (with this repository as destination)
2656 unbundle
2646 unbundle
2657
2647
2658 This command is not intended for use on public repositories. Once
2648 This command is not intended for use on public repositories. Once
2659 changes are visible for pull by other users, rolling a transaction
2649 changes are visible for pull by other users, rolling a transaction
2660 back locally is ineffective (someone else may already have pulled
2650 back locally is ineffective (someone else may already have pulled
2661 the changes). Furthermore, a race is possible with readers of the
2651 the changes). Furthermore, a race is possible with readers of the
2662 repository; for example an in-progress pull from the repository
2652 repository; for example an in-progress pull from the repository
2663 may fail if a rollback is performed.
2653 may fail if a rollback is performed.
2664 """
2654 """
2665 repo.rollback()
2655 repo.rollback()
2666
2656
2667 def root(ui, repo):
2657 def root(ui, repo):
2668 """print the root (top) of the current working directory
2658 """print the root (top) of the current working directory
2669
2659
2670 Print the root directory of the current repository.
2660 Print the root directory of the current repository.
2671 """
2661 """
2672 ui.write(repo.root + "\n")
2662 ui.write(repo.root + "\n")
2673
2663
2674 def serve(ui, repo, **opts):
2664 def serve(ui, repo, **opts):
2675 """export the repository via HTTP
2665 """export the repository via HTTP
2676
2666
2677 Start a local HTTP repository browser and pull server.
2667 Start a local HTTP repository browser and pull server.
2678
2668
2679 By default, the server logs accesses to stdout and errors to
2669 By default, the server logs accesses to stdout and errors to
2680 stderr. Use the -A and -E options to log to files.
2670 stderr. Use the -A and -E options to log to files.
2681 """
2671 """
2682
2672
2683 if opts["stdio"]:
2673 if opts["stdio"]:
2684 if repo is None:
2674 if repo is None:
2685 raise error.RepoError(_("There is no Mercurial repository here"
2675 raise error.RepoError(_("There is no Mercurial repository here"
2686 " (.hg not found)"))
2676 " (.hg not found)"))
2687 s = sshserver.sshserver(ui, repo)
2677 s = sshserver.sshserver(ui, repo)
2688 s.serve_forever()
2678 s.serve_forever()
2689
2679
2690 parentui = ui.parentui or ui
2680 parentui = ui.parentui or ui
2691 optlist = ("name templates style address port prefix ipv6"
2681 optlist = ("name templates style address port prefix ipv6"
2692 " accesslog errorlog webdir_conf certificate")
2682 " accesslog errorlog webdir_conf certificate")
2693 for o in optlist.split():
2683 for o in optlist.split():
2694 if opts[o]:
2684 if opts[o]:
2695 parentui.setconfig("web", o, str(opts[o]))
2685 parentui.setconfig("web", o, str(opts[o]))
2696 if (repo is not None) and (repo.ui != parentui):
2686 if (repo is not None) and (repo.ui != parentui):
2697 repo.ui.setconfig("web", o, str(opts[o]))
2687 repo.ui.setconfig("web", o, str(opts[o]))
2698
2688
2699 if repo is None and not ui.config("web", "webdir_conf"):
2689 if repo is None and not ui.config("web", "webdir_conf"):
2700 raise error.RepoError(_("There is no Mercurial repository here"
2690 raise error.RepoError(_("There is no Mercurial repository here"
2701 " (.hg not found)"))
2691 " (.hg not found)"))
2702
2692
2703 class service:
2693 class service:
2704 def init(self):
2694 def init(self):
2705 util.set_signal_handler()
2695 util.set_signal_handler()
2706 self.httpd = hgweb.server.create_server(parentui, repo)
2696 self.httpd = hgweb.server.create_server(parentui, repo)
2707
2697
2708 if not ui.verbose: return
2698 if not ui.verbose: return
2709
2699
2710 if self.httpd.prefix:
2700 if self.httpd.prefix:
2711 prefix = self.httpd.prefix.strip('/') + '/'
2701 prefix = self.httpd.prefix.strip('/') + '/'
2712 else:
2702 else:
2713 prefix = ''
2703 prefix = ''
2714
2704
2715 port = ':%d' % self.httpd.port
2705 port = ':%d' % self.httpd.port
2716 if port == ':80':
2706 if port == ':80':
2717 port = ''
2707 port = ''
2718
2708
2719 bindaddr = self.httpd.addr
2709 bindaddr = self.httpd.addr
2720 if bindaddr == '0.0.0.0':
2710 if bindaddr == '0.0.0.0':
2721 bindaddr = '*'
2711 bindaddr = '*'
2722 elif ':' in bindaddr: # IPv6
2712 elif ':' in bindaddr: # IPv6
2723 bindaddr = '[%s]' % bindaddr
2713 bindaddr = '[%s]' % bindaddr
2724
2714
2725 fqaddr = self.httpd.fqaddr
2715 fqaddr = self.httpd.fqaddr
2726 if ':' in fqaddr:
2716 if ':' in fqaddr:
2727 fqaddr = '[%s]' % fqaddr
2717 fqaddr = '[%s]' % fqaddr
2728 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2718 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2729 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2719 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2730
2720
2731 def run(self):
2721 def run(self):
2732 self.httpd.serve_forever()
2722 self.httpd.serve_forever()
2733
2723
2734 service = service()
2724 service = service()
2735
2725
2736 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2726 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2737
2727
2738 def status(ui, repo, *pats, **opts):
2728 def status(ui, repo, *pats, **opts):
2739 """show changed files in the working directory
2729 """show changed files in the working directory
2740
2730
2741 Show status of files in the repository. If names are given, only
2731 Show status of files in the repository. If names are given, only
2742 files that match are shown. Files that are clean or ignored or
2732 files that match are shown. Files that are clean or ignored or
2743 source of a copy/move operation, are not listed unless -c/--clean,
2733 source of a copy/move operation, are not listed unless -c/--clean,
2744 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2734 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2745 described with "show only ..." are given, the options -mardu are
2735 described with "show only ..." are given, the options -mardu are
2746 used.
2736 used.
2747
2737
2748 Option -q/--quiet hides untracked (unknown and ignored) files
2738 Option -q/--quiet hides untracked (unknown and ignored) files
2749 unless explicitly requested with -u/--unknown or -i/--ignored.
2739 unless explicitly requested with -u/--unknown or -i/--ignored.
2750
2740
2751 NOTE: status may appear to disagree with diff if permissions have
2741 NOTE: status may appear to disagree with diff if permissions have
2752 changed or a merge has occurred. The standard diff format does not
2742 changed or a merge has occurred. The standard diff format does not
2753 report permission changes and diff only reports changes relative
2743 report permission changes and diff only reports changes relative
2754 to one merge parent.
2744 to one merge parent.
2755
2745
2756 If one revision is given, it is used as the base revision.
2746 If one revision is given, it is used as the base revision.
2757 If two revisions are given, the difference between them is shown.
2747 If two revisions are given, the difference between them is shown.
2758
2748
2759 The codes used to show the status of files are:
2749 The codes used to show the status of files are:
2760 M = modified
2750 M = modified
2761 A = added
2751 A = added
2762 R = removed
2752 R = removed
2763 C = clean
2753 C = clean
2764 ! = missing (deleted by non-hg command, but still tracked)
2754 ! = missing (deleted by non-hg command, but still tracked)
2765 ? = not tracked
2755 ? = not tracked
2766 I = ignored
2756 I = ignored
2767 = the previous added file was copied from here
2757 = the previous added file was copied from here
2768 """
2758 """
2769
2759
2770 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2760 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2771 cwd = (pats and repo.getcwd()) or ''
2761 cwd = (pats and repo.getcwd()) or ''
2772 end = opts.get('print0') and '\0' or '\n'
2762 end = opts.get('print0') and '\0' or '\n'
2773 copy = {}
2763 copy = {}
2774 states = 'modified added removed deleted unknown ignored clean'.split()
2764 states = 'modified added removed deleted unknown ignored clean'.split()
2775 show = [k for k in states if opts.get(k)]
2765 show = [k for k in states if opts.get(k)]
2776 if opts.get('all'):
2766 if opts.get('all'):
2777 show += ui.quiet and (states[:4] + ['clean']) or states
2767 show += ui.quiet and (states[:4] + ['clean']) or states
2778 if not show:
2768 if not show:
2779 show = ui.quiet and states[:4] or states[:5]
2769 show = ui.quiet and states[:4] or states[:5]
2780
2770
2781 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2771 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2782 'ignored' in show, 'clean' in show, 'unknown' in show)
2772 'ignored' in show, 'clean' in show, 'unknown' in show)
2783 changestates = zip(states, 'MAR!?IC', stat)
2773 changestates = zip(states, 'MAR!?IC', stat)
2784
2774
2785 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2775 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2786 ctxn = repo[nullid]
2776 ctxn = repo[nullid]
2787 ctx1 = repo[node1]
2777 ctx1 = repo[node1]
2788 ctx2 = repo[node2]
2778 ctx2 = repo[node2]
2789 added = stat[1]
2779 added = stat[1]
2790 if node2 is None:
2780 if node2 is None:
2791 added = stat[0] + stat[1] # merged?
2781 added = stat[0] + stat[1] # merged?
2792
2782
2793 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2783 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2794 if k in added:
2784 if k in added:
2795 copy[k] = v
2785 copy[k] = v
2796 elif v in added:
2786 elif v in added:
2797 copy[v] = k
2787 copy[v] = k
2798
2788
2799 for state, char, files in changestates:
2789 for state, char, files in changestates:
2800 if state in show:
2790 if state in show:
2801 format = "%s %%s%s" % (char, end)
2791 format = "%s %%s%s" % (char, end)
2802 if opts.get('no_status'):
2792 if opts.get('no_status'):
2803 format = "%%s%s" % end
2793 format = "%%s%s" % end
2804
2794
2805 for f in files:
2795 for f in files:
2806 ui.write(format % repo.pathto(f, cwd))
2796 ui.write(format % repo.pathto(f, cwd))
2807 if f in copy:
2797 if f in copy:
2808 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2798 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2809
2799
2810 def tag(ui, repo, name1, *names, **opts):
2800 def tag(ui, repo, name1, *names, **opts):
2811 """add one or more tags for the current or given revision
2801 """add one or more tags for the current or given revision
2812
2802
2813 Name a particular revision using <name>.
2803 Name a particular revision using <name>.
2814
2804
2815 Tags are used to name particular revisions of the repository and are
2805 Tags are used to name particular revisions of the repository and are
2816 very useful to compare different revisions, to go back to significant
2806 very useful to compare different revisions, to go back to significant
2817 earlier versions or to mark branch points as releases, etc.
2807 earlier versions or to mark branch points as releases, etc.
2818
2808
2819 If no revision is given, the parent of the working directory is
2809 If no revision is given, the parent of the working directory is
2820 used, or tip if no revision is checked out.
2810 used, or tip if no revision is checked out.
2821
2811
2822 To facilitate version control, distribution, and merging of tags,
2812 To facilitate version control, distribution, and merging of tags,
2823 they are stored as a file named ".hgtags" which is managed
2813 they are stored as a file named ".hgtags" which is managed
2824 similarly to other project files and can be hand-edited if
2814 similarly to other project files and can be hand-edited if
2825 necessary. The file '.hg/localtags' is used for local tags (not
2815 necessary. The file '.hg/localtags' is used for local tags (not
2826 shared among repositories).
2816 shared among repositories).
2827
2817
2828 See 'hg help dates' for a list of formats valid for -d/--date.
2818 See 'hg help dates' for a list of formats valid for -d/--date.
2829 """
2819 """
2830
2820
2831 rev_ = "."
2821 rev_ = "."
2832 names = (name1,) + names
2822 names = (name1,) + names
2833 if len(names) != len(set(names)):
2823 if len(names) != len(set(names)):
2834 raise util.Abort(_('tag names must be unique'))
2824 raise util.Abort(_('tag names must be unique'))
2835 for n in names:
2825 for n in names:
2836 if n in ['tip', '.', 'null']:
2826 if n in ['tip', '.', 'null']:
2837 raise util.Abort(_('the name \'%s\' is reserved') % n)
2827 raise util.Abort(_('the name \'%s\' is reserved') % n)
2838 if opts.get('rev') and opts.get('remove'):
2828 if opts.get('rev') and opts.get('remove'):
2839 raise util.Abort(_("--rev and --remove are incompatible"))
2829 raise util.Abort(_("--rev and --remove are incompatible"))
2840 if opts.get('rev'):
2830 if opts.get('rev'):
2841 rev_ = opts['rev']
2831 rev_ = opts['rev']
2842 message = opts.get('message')
2832 message = opts.get('message')
2843 if opts.get('remove'):
2833 if opts.get('remove'):
2844 expectedtype = opts.get('local') and 'local' or 'global'
2834 expectedtype = opts.get('local') and 'local' or 'global'
2845 for n in names:
2835 for n in names:
2846 if not repo.tagtype(n):
2836 if not repo.tagtype(n):
2847 raise util.Abort(_('tag \'%s\' does not exist') % n)
2837 raise util.Abort(_('tag \'%s\' does not exist') % n)
2848 if repo.tagtype(n) != expectedtype:
2838 if repo.tagtype(n) != expectedtype:
2849 if expectedtype == 'global':
2839 if expectedtype == 'global':
2850 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2840 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2851 else:
2841 else:
2852 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2842 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2853 rev_ = nullid
2843 rev_ = nullid
2854 if not message:
2844 if not message:
2855 message = _('Removed tag %s') % ', '.join(names)
2845 message = _('Removed tag %s') % ', '.join(names)
2856 elif not opts.get('force'):
2846 elif not opts.get('force'):
2857 for n in names:
2847 for n in names:
2858 if n in repo.tags():
2848 if n in repo.tags():
2859 raise util.Abort(_('tag \'%s\' already exists '
2849 raise util.Abort(_('tag \'%s\' already exists '
2860 '(use -f to force)') % n)
2850 '(use -f to force)') % n)
2861 if not rev_ and repo.dirstate.parents()[1] != nullid:
2851 if not rev_ and repo.dirstate.parents()[1] != nullid:
2862 raise util.Abort(_('uncommitted merge - please provide a '
2852 raise util.Abort(_('uncommitted merge - please provide a '
2863 'specific revision'))
2853 'specific revision'))
2864 r = repo[rev_].node()
2854 r = repo[rev_].node()
2865
2855
2866 if not message:
2856 if not message:
2867 message = (_('Added tag %s for changeset %s') %
2857 message = (_('Added tag %s for changeset %s') %
2868 (', '.join(names), short(r)))
2858 (', '.join(names), short(r)))
2869
2859
2870 date = opts.get('date')
2860 date = opts.get('date')
2871 if date:
2861 if date:
2872 date = util.parsedate(date)
2862 date = util.parsedate(date)
2873
2863
2874 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2864 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2875
2865
2876 def tags(ui, repo):
2866 def tags(ui, repo):
2877 """list repository tags
2867 """list repository tags
2878
2868
2879 This lists both regular and local tags. When the -v/--verbose
2869 This lists both regular and local tags. When the -v/--verbose
2880 switch is used, a third column "local" is printed for local tags.
2870 switch is used, a third column "local" is printed for local tags.
2881 """
2871 """
2882
2872
2883 l = repo.tagslist()
2873 l = repo.tagslist()
2884 l.reverse()
2874 l.reverse()
2885 hexfunc = ui.debugflag and hex or short
2875 hexfunc = ui.debugflag and hex or short
2886 tagtype = ""
2876 tagtype = ""
2887
2877
2888 for t, n in l:
2878 for t, n in l:
2889 if ui.quiet:
2879 if ui.quiet:
2890 ui.write("%s\n" % t)
2880 ui.write("%s\n" % t)
2891 continue
2881 continue
2892
2882
2893 try:
2883 try:
2894 hn = hexfunc(n)
2884 hn = hexfunc(n)
2895 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2885 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2896 except error.LookupError:
2886 except error.LookupError:
2897 r = " ?:%s" % hn
2887 r = " ?:%s" % hn
2898 else:
2888 else:
2899 spaces = " " * (30 - encoding.colwidth(t))
2889 spaces = " " * (30 - encoding.colwidth(t))
2900 if ui.verbose:
2890 if ui.verbose:
2901 if repo.tagtype(t) == 'local':
2891 if repo.tagtype(t) == 'local':
2902 tagtype = " local"
2892 tagtype = " local"
2903 else:
2893 else:
2904 tagtype = ""
2894 tagtype = ""
2905 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2895 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2906
2896
2907 def tip(ui, repo, **opts):
2897 def tip(ui, repo, **opts):
2908 """show the tip revision
2898 """show the tip revision
2909
2899
2910 The tip revision (usually just called the tip) is the most
2900 The tip revision (usually just called the tip) is the most
2911 recently added changeset in the repository, the most recently
2901 recently added changeset in the repository, the most recently
2912 changed head.
2902 changed head.
2913
2903
2914 If you have just made a commit, that commit will be the tip. If
2904 If you have just made a commit, that commit will be the tip. If
2915 you have just pulled changes from another repository, the tip of
2905 you have just pulled changes from another repository, the tip of
2916 that repository becomes the current tip. The "tip" tag is special
2906 that repository becomes the current tip. The "tip" tag is special
2917 and cannot be renamed or assigned to a different changeset.
2907 and cannot be renamed or assigned to a different changeset.
2918 """
2908 """
2919 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2909 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2920
2910
2921 def unbundle(ui, repo, fname1, *fnames, **opts):
2911 def unbundle(ui, repo, fname1, *fnames, **opts):
2922 """apply one or more changegroup files
2912 """apply one or more changegroup files
2923
2913
2924 Apply one or more compressed changegroup files generated by the
2914 Apply one or more compressed changegroup files generated by the
2925 bundle command.
2915 bundle command.
2926 """
2916 """
2927 fnames = (fname1,) + fnames
2917 fnames = (fname1,) + fnames
2928
2918
2929 lock = repo.lock()
2919 lock = repo.lock()
2930 try:
2920 try:
2931 for fname in fnames:
2921 for fname in fnames:
2932 f = url.open(ui, fname)
2922 f = url.open(ui, fname)
2933 gen = changegroup.readbundle(f, fname)
2923 gen = changegroup.readbundle(f, fname)
2934 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2924 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2935 finally:
2925 finally:
2936 lock.release()
2926 lock.release()
2937
2927
2938 return postincoming(ui, repo, modheads, opts.get('update'), None)
2928 return postincoming(ui, repo, modheads, opts.get('update'), None)
2939
2929
2940 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2930 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2941 """update working directory
2931 """update working directory
2942
2932
2943 Update the repository's working directory to the specified
2933 Update the repository's working directory to the specified
2944 revision, or the tip of the current branch if none is specified.
2934 revision, or the tip of the current branch if none is specified.
2945 Use null as the revision to remove the working copy (like 'hg
2935 Use null as the revision to remove the working copy (like 'hg
2946 clone -U').
2936 clone -U').
2947
2937
2948 When the working directory contains no uncommitted changes, it
2938 When the working directory contains no uncommitted changes, it
2949 will be replaced by the state of the requested revision from the
2939 will be replaced by the state of the requested revision from the
2950 repository. When the requested revision is on a different branch,
2940 repository. When the requested revision is on a different branch,
2951 the working directory will additionally be switched to that
2941 the working directory will additionally be switched to that
2952 branch.
2942 branch.
2953
2943
2954 When there are uncommitted changes, use option -C to discard them,
2944 When there are uncommitted changes, use option -C to discard them,
2955 forcibly replacing the state of the working directory with the
2945 forcibly replacing the state of the working directory with the
2956 requested revision.
2946 requested revision.
2957
2947
2958 When there are uncommitted changes and option -C is not used, and
2948 When there are uncommitted changes and option -C is not used, and
2959 the parent revision and requested revision are on the same branch,
2949 the parent revision and requested revision are on the same branch,
2960 and one of them is an ancestor of the other, then the new working
2950 and one of them is an ancestor of the other, then the new working
2961 directory will contain the requested revision merged with the
2951 directory will contain the requested revision merged with the
2962 uncommitted changes. Otherwise, the update will fail with a
2952 uncommitted changes. Otherwise, the update will fail with a
2963 suggestion to use 'merge' or 'update -C' instead.
2953 suggestion to use 'merge' or 'update -C' instead.
2964
2954
2965 If you want to update just one file to an older revision, use
2955 If you want to update just one file to an older revision, use
2966 revert.
2956 revert.
2967
2957
2968 See 'hg help dates' for a list of formats valid for -d/--date.
2958 See 'hg help dates' for a list of formats valid for -d/--date.
2969 """
2959 """
2970 if rev and node:
2960 if rev and node:
2971 raise util.Abort(_("please specify just one revision"))
2961 raise util.Abort(_("please specify just one revision"))
2972
2962
2973 if not rev:
2963 if not rev:
2974 rev = node
2964 rev = node
2975
2965
2976 if date:
2966 if date:
2977 if rev:
2967 if rev:
2978 raise util.Abort(_("you can't specify a revision and a date"))
2968 raise util.Abort(_("you can't specify a revision and a date"))
2979 rev = cmdutil.finddate(ui, repo, date)
2969 rev = cmdutil.finddate(ui, repo, date)
2980
2970
2981 if clean:
2971 if clean:
2982 return hg.clean(repo, rev)
2972 return hg.clean(repo, rev)
2983 else:
2973 else:
2984 return hg.update(repo, rev)
2974 return hg.update(repo, rev)
2985
2975
2986 def verify(ui, repo):
2976 def verify(ui, repo):
2987 """verify the integrity of the repository
2977 """verify the integrity of the repository
2988
2978
2989 Verify the integrity of the current repository.
2979 Verify the integrity of the current repository.
2990
2980
2991 This will perform an extensive check of the repository's
2981 This will perform an extensive check of the repository's
2992 integrity, validating the hashes and checksums of each entry in
2982 integrity, validating the hashes and checksums of each entry in
2993 the changelog, manifest, and tracked files, as well as the
2983 the changelog, manifest, and tracked files, as well as the
2994 integrity of their crosslinks and indices.
2984 integrity of their crosslinks and indices.
2995 """
2985 """
2996 return hg.verify(repo)
2986 return hg.verify(repo)
2997
2987
2998 def version_(ui):
2988 def version_(ui):
2999 """output version and copyright information"""
2989 """output version and copyright information"""
3000 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2990 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3001 % util.version())
2991 % util.version())
3002 ui.status(_(
2992 ui.status(_(
3003 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2993 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3004 "This is free software; see the source for copying conditions. "
2994 "This is free software; see the source for copying conditions. "
3005 "There is NO\nwarranty; "
2995 "There is NO\nwarranty; "
3006 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2996 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3007 ))
2997 ))
3008
2998
3009 # Command options and aliases are listed here, alphabetically
2999 # Command options and aliases are listed here, alphabetically
3010
3000
3011 globalopts = [
3001 globalopts = [
3012 ('R', 'repository', '',
3002 ('R', 'repository', '',
3013 _('repository root directory or symbolic path name')),
3003 _('repository root directory or symbolic path name')),
3014 ('', 'cwd', '', _('change working directory')),
3004 ('', 'cwd', '', _('change working directory')),
3015 ('y', 'noninteractive', None,
3005 ('y', 'noninteractive', None,
3016 _('do not prompt, assume \'yes\' for any required answers')),
3006 _('do not prompt, assume \'yes\' for any required answers')),
3017 ('q', 'quiet', None, _('suppress output')),
3007 ('q', 'quiet', None, _('suppress output')),
3018 ('v', 'verbose', None, _('enable additional output')),
3008 ('v', 'verbose', None, _('enable additional output')),
3019 ('', 'config', [], _('set/override config option')),
3009 ('', 'config', [], _('set/override config option')),
3020 ('', 'debug', None, _('enable debugging output')),
3010 ('', 'debug', None, _('enable debugging output')),
3021 ('', 'debugger', None, _('start debugger')),
3011 ('', 'debugger', None, _('start debugger')),
3022 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3012 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3023 ('', 'encodingmode', encoding.encodingmode,
3013 ('', 'encodingmode', encoding.encodingmode,
3024 _('set the charset encoding mode')),
3014 _('set the charset encoding mode')),
3025 ('', 'traceback', None, _('print traceback on exception')),
3015 ('', 'traceback', None, _('print traceback on exception')),
3026 ('', 'time', None, _('time how long the command takes')),
3016 ('', 'time', None, _('time how long the command takes')),
3027 ('', 'profile', None, _('print command execution profile')),
3017 ('', 'profile', None, _('print command execution profile')),
3028 ('', 'version', None, _('output version information and exit')),
3018 ('', 'version', None, _('output version information and exit')),
3029 ('h', 'help', None, _('display help and exit')),
3019 ('h', 'help', None, _('display help and exit')),
3030 ]
3020 ]
3031
3021
3032 dryrunopts = [('n', 'dry-run', None,
3022 dryrunopts = [('n', 'dry-run', None,
3033 _('do not perform actions, just print output'))]
3023 _('do not perform actions, just print output'))]
3034
3024
3035 remoteopts = [
3025 remoteopts = [
3036 ('e', 'ssh', '', _('specify ssh command to use')),
3026 ('e', 'ssh', '', _('specify ssh command to use')),
3037 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3027 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3038 ]
3028 ]
3039
3029
3040 walkopts = [
3030 walkopts = [
3041 ('I', 'include', [], _('include names matching the given patterns')),
3031 ('I', 'include', [], _('include names matching the given patterns')),
3042 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3032 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3043 ]
3033 ]
3044
3034
3045 commitopts = [
3035 commitopts = [
3046 ('m', 'message', '', _('use <text> as commit message')),
3036 ('m', 'message', '', _('use <text> as commit message')),
3047 ('l', 'logfile', '', _('read commit message from <file>')),
3037 ('l', 'logfile', '', _('read commit message from <file>')),
3048 ]
3038 ]
3049
3039
3050 commitopts2 = [
3040 commitopts2 = [
3051 ('d', 'date', '', _('record datecode as commit date')),
3041 ('d', 'date', '', _('record datecode as commit date')),
3052 ('u', 'user', '', _('record user as committer')),
3042 ('u', 'user', '', _('record user as committer')),
3053 ]
3043 ]
3054
3044
3055 templateopts = [
3045 templateopts = [
3056 ('', 'style', '', _('display using template map file')),
3046 ('', 'style', '', _('display using template map file')),
3057 ('', 'template', '', _('display with template')),
3047 ('', 'template', '', _('display with template')),
3058 ]
3048 ]
3059
3049
3060 logopts = [
3050 logopts = [
3061 ('p', 'patch', None, _('show patch')),
3051 ('p', 'patch', None, _('show patch')),
3062 ('g', 'git', None, _('use git extended diff format')),
3052 ('g', 'git', None, _('use git extended diff format')),
3063 ('l', 'limit', '', _('limit number of changes displayed')),
3053 ('l', 'limit', '', _('limit number of changes displayed')),
3064 ('M', 'no-merges', None, _('do not show merges')),
3054 ('M', 'no-merges', None, _('do not show merges')),
3065 ] + templateopts
3055 ] + templateopts
3066
3056
3067 diffopts = [
3057 diffopts = [
3068 ('a', 'text', None, _('treat all files as text')),
3058 ('a', 'text', None, _('treat all files as text')),
3069 ('g', 'git', None, _('use git extended diff format')),
3059 ('g', 'git', None, _('use git extended diff format')),
3070 ('', 'nodates', None, _("don't include dates in diff headers"))
3060 ('', 'nodates', None, _("don't include dates in diff headers"))
3071 ]
3061 ]
3072
3062
3073 diffopts2 = [
3063 diffopts2 = [
3074 ('p', 'show-function', None, _('show which function each change is in')),
3064 ('p', 'show-function', None, _('show which function each change is in')),
3075 ('w', 'ignore-all-space', None,
3065 ('w', 'ignore-all-space', None,
3076 _('ignore white space when comparing lines')),
3066 _('ignore white space when comparing lines')),
3077 ('b', 'ignore-space-change', None,
3067 ('b', 'ignore-space-change', None,
3078 _('ignore changes in the amount of white space')),
3068 _('ignore changes in the amount of white space')),
3079 ('B', 'ignore-blank-lines', None,
3069 ('B', 'ignore-blank-lines', None,
3080 _('ignore changes whose lines are all blank')),
3070 _('ignore changes whose lines are all blank')),
3081 ('U', 'unified', '', _('number of lines of context to show'))
3071 ('U', 'unified', '', _('number of lines of context to show'))
3082 ]
3072 ]
3083
3073
3084 similarityopts = [
3074 similarityopts = [
3085 ('s', 'similarity', '',
3075 ('s', 'similarity', '',
3086 _('guess renamed files by similarity (0<=s<=100)'))
3076 _('guess renamed files by similarity (0<=s<=100)'))
3087 ]
3077 ]
3088
3078
3089 table = {
3079 table = {
3090 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3080 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3091 "addremove":
3081 "addremove":
3092 (addremove, similarityopts + walkopts + dryrunopts,
3082 (addremove, similarityopts + walkopts + dryrunopts,
3093 _('[OPTION]... [FILE]...')),
3083 _('[OPTION]... [FILE]...')),
3094 "^annotate|blame":
3084 "^annotate|blame":
3095 (annotate,
3085 (annotate,
3096 [('r', 'rev', '', _('annotate the specified revision')),
3086 [('r', 'rev', '', _('annotate the specified revision')),
3097 ('f', 'follow', None, _('follow file copies and renames')),
3087 ('f', 'follow', None, _('follow file copies and renames')),
3098 ('a', 'text', None, _('treat all files as text')),
3088 ('a', 'text', None, _('treat all files as text')),
3099 ('u', 'user', None, _('list the author (long with -v)')),
3089 ('u', 'user', None, _('list the author (long with -v)')),
3100 ('d', 'date', None, _('list the date (short with -q)')),
3090 ('d', 'date', None, _('list the date (short with -q)')),
3101 ('n', 'number', None, _('list the revision number (default)')),
3091 ('n', 'number', None, _('list the revision number (default)')),
3102 ('c', 'changeset', None, _('list the changeset')),
3092 ('c', 'changeset', None, _('list the changeset')),
3103 ('l', 'line-number', None,
3093 ('l', 'line-number', None,
3104 _('show line number at the first appearance'))
3094 _('show line number at the first appearance'))
3105 ] + walkopts,
3095 ] + walkopts,
3106 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3096 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3107 "archive":
3097 "archive":
3108 (archive,
3098 (archive,
3109 [('', 'no-decode', None, _('do not pass files through decoders')),
3099 [('', 'no-decode', None, _('do not pass files through decoders')),
3110 ('p', 'prefix', '', _('directory prefix for files in archive')),
3100 ('p', 'prefix', '', _('directory prefix for files in archive')),
3111 ('r', 'rev', '', _('revision to distribute')),
3101 ('r', 'rev', '', _('revision to distribute')),
3112 ('t', 'type', '', _('type of distribution to create')),
3102 ('t', 'type', '', _('type of distribution to create')),
3113 ] + walkopts,
3103 ] + walkopts,
3114 _('[OPTION]... DEST')),
3104 _('[OPTION]... DEST')),
3115 "backout":
3105 "backout":
3116 (backout,
3106 (backout,
3117 [('', 'merge', None,
3107 [('', 'merge', None,
3118 _('merge with old dirstate parent after backout')),
3108 _('merge with old dirstate parent after backout')),
3119 ('', 'parent', '', _('parent to choose when backing out merge')),
3109 ('', 'parent', '', _('parent to choose when backing out merge')),
3120 ('r', 'rev', '', _('revision to backout')),
3110 ('r', 'rev', '', _('revision to backout')),
3121 ] + walkopts + commitopts + commitopts2,
3111 ] + walkopts + commitopts + commitopts2,
3122 _('[OPTION]... [-r] REV')),
3112 _('[OPTION]... [-r] REV')),
3123 "bisect":
3113 "bisect":
3124 (bisect,
3114 (bisect,
3125 [('r', 'reset', False, _('reset bisect state')),
3115 [('r', 'reset', False, _('reset bisect state')),
3126 ('g', 'good', False, _('mark changeset good')),
3116 ('g', 'good', False, _('mark changeset good')),
3127 ('b', 'bad', False, _('mark changeset bad')),
3117 ('b', 'bad', False, _('mark changeset bad')),
3128 ('s', 'skip', False, _('skip testing changeset')),
3118 ('s', 'skip', False, _('skip testing changeset')),
3129 ('c', 'command', '', _('use command to check changeset state')),
3119 ('c', 'command', '', _('use command to check changeset state')),
3130 ('U', 'noupdate', False, _('do not update to target'))],
3120 ('U', 'noupdate', False, _('do not update to target'))],
3131 _("[-gbsr] [-c CMD] [REV]")),
3121 _("[-gbsr] [-c CMD] [REV]")),
3132 "branch":
3122 "branch":
3133 (branch,
3123 (branch,
3134 [('f', 'force', None,
3124 [('f', 'force', None,
3135 _('set branch name even if it shadows an existing branch')),
3125 _('set branch name even if it shadows an existing branch')),
3136 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3126 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3137 _('[-fC] [NAME]')),
3127 _('[-fC] [NAME]')),
3138 "branches":
3128 "branches":
3139 (branches,
3129 (branches,
3140 [('a', 'active', False,
3130 [('a', 'active', False,
3141 _('show only branches that have unmerged heads'))],
3131 _('show only branches that have unmerged heads'))],
3142 _('[-a]')),
3132 _('[-a]')),
3143 "bundle":
3133 "bundle":
3144 (bundle,
3134 (bundle,
3145 [('f', 'force', None,
3135 [('f', 'force', None,
3146 _('run even when remote repository is unrelated')),
3136 _('run even when remote repository is unrelated')),
3147 ('r', 'rev', [],
3137 ('r', 'rev', [],
3148 _('a changeset up to which you would like to bundle')),
3138 _('a changeset up to which you would like to bundle')),
3149 ('', 'base', [],
3139 ('', 'base', [],
3150 _('a base changeset to specify instead of a destination')),
3140 _('a base changeset to specify instead of a destination')),
3151 ('a', 'all', None, _('bundle all changesets in the repository')),
3141 ('a', 'all', None, _('bundle all changesets in the repository')),
3152 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3142 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3153 ] + remoteopts,
3143 ] + remoteopts,
3154 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3144 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3155 "cat":
3145 "cat":
3156 (cat,
3146 (cat,
3157 [('o', 'output', '', _('print output to file with formatted name')),
3147 [('o', 'output', '', _('print output to file with formatted name')),
3158 ('r', 'rev', '', _('print the given revision')),
3148 ('r', 'rev', '', _('print the given revision')),
3159 ('', 'decode', None, _('apply any matching decode filter')),
3149 ('', 'decode', None, _('apply any matching decode filter')),
3160 ] + walkopts,
3150 ] + walkopts,
3161 _('[OPTION]... FILE...')),
3151 _('[OPTION]... FILE...')),
3162 "^clone":
3152 "^clone":
3163 (clone,
3153 (clone,
3164 [('U', 'noupdate', None,
3154 [('U', 'noupdate', None,
3165 _('the clone will only contain a repository (no working copy)')),
3155 _('the clone will only contain a repository (no working copy)')),
3166 ('r', 'rev', [],
3156 ('r', 'rev', [],
3167 _('a changeset you would like to have after cloning')),
3157 _('a changeset you would like to have after cloning')),
3168 ('', 'pull', None, _('use pull protocol to copy metadata')),
3158 ('', 'pull', None, _('use pull protocol to copy metadata')),
3169 ('', 'uncompressed', None,
3159 ('', 'uncompressed', None,
3170 _('use uncompressed transfer (fast over LAN)')),
3160 _('use uncompressed transfer (fast over LAN)')),
3171 ] + remoteopts,
3161 ] + remoteopts,
3172 _('[OPTION]... SOURCE [DEST]')),
3162 _('[OPTION]... SOURCE [DEST]')),
3173 "^commit|ci":
3163 "^commit|ci":
3174 (commit,
3164 (commit,
3175 [('A', 'addremove', None,
3165 [('A', 'addremove', None,
3176 _('mark new/missing files as added/removed before committing')),
3166 _('mark new/missing files as added/removed before committing')),
3177 ('', 'close-branch', None,
3167 ('', 'close-branch', None,
3178 _('mark a branch as closed, hiding it from the branch list')),
3168 _('mark a branch as closed, hiding it from the branch list')),
3179 ] + walkopts + commitopts + commitopts2,
3169 ] + walkopts + commitopts + commitopts2,
3180 _('[OPTION]... [FILE]...')),
3170 _('[OPTION]... [FILE]...')),
3181 "copy|cp":
3171 "copy|cp":
3182 (copy,
3172 (copy,
3183 [('A', 'after', None, _('record a copy that has already occurred')),
3173 [('A', 'after', None, _('record a copy that has already occurred')),
3184 ('f', 'force', None,
3174 ('f', 'force', None,
3185 _('forcibly copy over an existing managed file')),
3175 _('forcibly copy over an existing managed file')),
3186 ] + walkopts + dryrunopts,
3176 ] + walkopts + dryrunopts,
3187 _('[OPTION]... [SOURCE]... DEST')),
3177 _('[OPTION]... [SOURCE]... DEST')),
3188 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3178 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3189 "debugcheckstate": (debugcheckstate, []),
3179 "debugcheckstate": (debugcheckstate, []),
3190 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3180 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3191 "debugcomplete":
3181 "debugcomplete":
3192 (debugcomplete,
3182 (debugcomplete,
3193 [('o', 'options', None, _('show the command options'))],
3183 [('o', 'options', None, _('show the command options'))],
3194 _('[-o] CMD')),
3184 _('[-o] CMD')),
3195 "debugdate":
3185 "debugdate":
3196 (debugdate,
3186 (debugdate,
3197 [('e', 'extended', None, _('try extended date formats'))],
3187 [('e', 'extended', None, _('try extended date formats'))],
3198 _('[-e] DATE [RANGE]')),
3188 _('[-e] DATE [RANGE]')),
3199 "debugdata": (debugdata, [], _('FILE REV')),
3189 "debugdata": (debugdata, [], _('FILE REV')),
3200 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3190 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3201 "debugindex": (debugindex, [], _('FILE')),
3191 "debugindex": (debugindex, [], _('FILE')),
3202 "debugindexdot": (debugindexdot, [], _('FILE')),
3192 "debugindexdot": (debugindexdot, [], _('FILE')),
3203 "debuginstall": (debuginstall, []),
3193 "debuginstall": (debuginstall, []),
3204 "debugrawcommit|rawcommit":
3194 "debugrawcommit|rawcommit":
3205 (rawcommit,
3195 (rawcommit,
3206 [('p', 'parent', [], _('parent')),
3196 [('p', 'parent', [], _('parent')),
3207 ('F', 'files', '', _('file list'))
3197 ('F', 'files', '', _('file list'))
3208 ] + commitopts + commitopts2,
3198 ] + commitopts + commitopts2,
3209 _('[OPTION]... [FILE]...')),
3199 _('[OPTION]... [FILE]...')),
3210 "debugrebuildstate":
3200 "debugrebuildstate":
3211 (debugrebuildstate,
3201 (debugrebuildstate,
3212 [('r', 'rev', '', _('revision to rebuild to'))],
3202 [('r', 'rev', '', _('revision to rebuild to'))],
3213 _('[-r REV] [REV]')),
3203 _('[-r REV] [REV]')),
3214 "debugrename":
3204 "debugrename":
3215 (debugrename,
3205 (debugrename,
3216 [('r', 'rev', '', _('revision to debug'))],
3206 [('r', 'rev', '', _('revision to debug'))],
3217 _('[-r REV] FILE')),
3207 _('[-r REV] FILE')),
3218 "debugsetparents":
3208 "debugsetparents":
3219 (debugsetparents, [], _('REV1 [REV2]')),
3209 (debugsetparents, [], _('REV1 [REV2]')),
3220 "debugstate":
3210 "debugstate":
3221 (debugstate,
3211 (debugstate,
3222 [('', 'nodates', None, _('do not display the saved mtime'))],
3212 [('', 'nodates', None, _('do not display the saved mtime'))],
3223 _('[OPTION]...')),
3213 _('[OPTION]...')),
3224 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3214 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3225 "^diff":
3215 "^diff":
3226 (diff,
3216 (diff,
3227 [('r', 'rev', [], _('revision')),
3217 [('r', 'rev', [], _('revision')),
3228 ('c', 'change', '', _('change made by revision'))
3218 ('c', 'change', '', _('change made by revision'))
3229 ] + diffopts + diffopts2 + walkopts,
3219 ] + diffopts + diffopts2 + walkopts,
3230 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3220 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3231 "^export":
3221 "^export":
3232 (export,
3222 (export,
3233 [('o', 'output', '', _('print output to file with formatted name')),
3223 [('o', 'output', '', _('print output to file with formatted name')),
3234 ('', 'switch-parent', None, _('diff against the second parent'))
3224 ('', 'switch-parent', None, _('diff against the second parent'))
3235 ] + diffopts,
3225 ] + diffopts,
3236 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3226 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3237 "grep":
3227 "grep":
3238 (grep,
3228 (grep,
3239 [('0', 'print0', None, _('end fields with NUL')),
3229 [('0', 'print0', None, _('end fields with NUL')),
3240 ('', 'all', None, _('print all revisions that match')),
3230 ('', 'all', None, _('print all revisions that match')),
3241 ('f', 'follow', None,
3231 ('f', 'follow', None,
3242 _('follow changeset history, or file history across copies and renames')),
3232 _('follow changeset history, or file history across copies and renames')),
3243 ('i', 'ignore-case', None, _('ignore case when matching')),
3233 ('i', 'ignore-case', None, _('ignore case when matching')),
3244 ('l', 'files-with-matches', None,
3234 ('l', 'files-with-matches', None,
3245 _('print only filenames and revisions that match')),
3235 _('print only filenames and revisions that match')),
3246 ('n', 'line-number', None, _('print matching line numbers')),
3236 ('n', 'line-number', None, _('print matching line numbers')),
3247 ('r', 'rev', [], _('search in given revision range')),
3237 ('r', 'rev', [], _('search in given revision range')),
3248 ('u', 'user', None, _('list the author (long with -v)')),
3238 ('u', 'user', None, _('list the author (long with -v)')),
3249 ('d', 'date', None, _('list the date (short with -q)')),
3239 ('d', 'date', None, _('list the date (short with -q)')),
3250 ] + walkopts,
3240 ] + walkopts,
3251 _('[OPTION]... PATTERN [FILE]...')),
3241 _('[OPTION]... PATTERN [FILE]...')),
3252 "heads":
3242 "heads":
3253 (heads,
3243 (heads,
3254 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3244 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3255 ('a', 'active', False,
3245 ('a', 'active', False,
3256 _('show only the active heads from open branches')),
3246 _('show only the active heads from open branches')),
3257 ] + templateopts,
3247 ] + templateopts,
3258 _('[-r REV] [REV]...')),
3248 _('[-r REV] [REV]...')),
3259 "help": (help_, [], _('[TOPIC]')),
3249 "help": (help_, [], _('[TOPIC]')),
3260 "identify|id":
3250 "identify|id":
3261 (identify,
3251 (identify,
3262 [('r', 'rev', '', _('identify the specified revision')),
3252 [('r', 'rev', '', _('identify the specified revision')),
3263 ('n', 'num', None, _('show local revision number')),
3253 ('n', 'num', None, _('show local revision number')),
3264 ('i', 'id', None, _('show global revision id')),
3254 ('i', 'id', None, _('show global revision id')),
3265 ('b', 'branch', None, _('show branch')),
3255 ('b', 'branch', None, _('show branch')),
3266 ('t', 'tags', None, _('show tags'))],
3256 ('t', 'tags', None, _('show tags'))],
3267 _('[-nibt] [-r REV] [SOURCE]')),
3257 _('[-nibt] [-r REV] [SOURCE]')),
3268 "import|patch":
3258 "import|patch":
3269 (import_,
3259 (import_,
3270 [('p', 'strip', 1,
3260 [('p', 'strip', 1,
3271 _('directory strip option for patch. This has the same '
3261 _('directory strip option for patch. This has the same '
3272 'meaning as the corresponding patch option')),
3262 'meaning as the corresponding patch option')),
3273 ('b', 'base', '', _('base path')),
3263 ('b', 'base', '', _('base path')),
3274 ('f', 'force', None,
3264 ('f', 'force', None,
3275 _('skip check for outstanding uncommitted changes')),
3265 _('skip check for outstanding uncommitted changes')),
3276 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3266 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3277 ('', 'exact', None,
3267 ('', 'exact', None,
3278 _('apply patch to the nodes from which it was generated')),
3268 _('apply patch to the nodes from which it was generated')),
3279 ('', 'import-branch', None,
3269 ('', 'import-branch', None,
3280 _('use any branch information in patch (implied by --exact)'))] +
3270 _('use any branch information in patch (implied by --exact)'))] +
3281 commitopts + commitopts2 + similarityopts,
3271 commitopts + commitopts2 + similarityopts,
3282 _('[OPTION]... PATCH...')),
3272 _('[OPTION]... PATCH...')),
3283 "incoming|in":
3273 "incoming|in":
3284 (incoming,
3274 (incoming,
3285 [('f', 'force', None,
3275 [('f', 'force', None,
3286 _('run even when remote repository is unrelated')),
3276 _('run even when remote repository is unrelated')),
3287 ('n', 'newest-first', None, _('show newest record first')),
3277 ('n', 'newest-first', None, _('show newest record first')),
3288 ('', 'bundle', '', _('file to store the bundles into')),
3278 ('', 'bundle', '', _('file to store the bundles into')),
3289 ('r', 'rev', [],
3279 ('r', 'rev', [],
3290 _('a specific revision up to which you would like to pull')),
3280 _('a specific revision up to which you would like to pull')),
3291 ] + logopts + remoteopts,
3281 ] + logopts + remoteopts,
3292 _('[-p] [-n] [-M] [-f] [-r REV]...'
3282 _('[-p] [-n] [-M] [-f] [-r REV]...'
3293 ' [--bundle FILENAME] [SOURCE]')),
3283 ' [--bundle FILENAME] [SOURCE]')),
3294 "^init":
3284 "^init":
3295 (init,
3285 (init,
3296 remoteopts,
3286 remoteopts,
3297 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3287 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3298 "locate":
3288 "locate":
3299 (locate,
3289 (locate,
3300 [('r', 'rev', '', _('search the repository as it stood at REV')),
3290 [('r', 'rev', '', _('search the repository as it stood at REV')),
3301 ('0', 'print0', None,
3291 ('0', 'print0', None,
3302 _('end filenames with NUL, for use with xargs')),
3292 _('end filenames with NUL, for use with xargs')),
3303 ('f', 'fullpath', None,
3293 ('f', 'fullpath', None,
3304 _('print complete paths from the filesystem root')),
3294 _('print complete paths from the filesystem root')),
3305 ] + walkopts,
3295 ] + walkopts,
3306 _('[OPTION]... [PATTERN]...')),
3296 _('[OPTION]... [PATTERN]...')),
3307 "^log|history":
3297 "^log|history":
3308 (log,
3298 (log,
3309 [('f', 'follow', None,
3299 [('f', 'follow', None,
3310 _('follow changeset history, or file history across copies and renames')),
3300 _('follow changeset history, or file history across copies and renames')),
3311 ('', 'follow-first', None,
3301 ('', 'follow-first', None,
3312 _('only follow the first parent of merge changesets')),
3302 _('only follow the first parent of merge changesets')),
3313 ('d', 'date', '', _('show revisions matching date spec')),
3303 ('d', 'date', '', _('show revisions matching date spec')),
3314 ('C', 'copies', None, _('show copied files')),
3304 ('C', 'copies', None, _('show copied files')),
3315 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3305 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3316 ('r', 'rev', [], _('show the specified revision or range')),
3306 ('r', 'rev', [], _('show the specified revision or range')),
3317 ('', 'removed', None, _('include revisions where files were removed')),
3307 ('', 'removed', None, _('include revisions where files were removed')),
3318 ('m', 'only-merges', None, _('show only merges')),
3308 ('m', 'only-merges', None, _('show only merges')),
3319 ('u', 'user', [], _('revisions committed by user')),
3309 ('u', 'user', [], _('revisions committed by user')),
3320 ('b', 'only-branch', [],
3310 ('b', 'only-branch', [],
3321 _('show only changesets within the given named branch')),
3311 _('show only changesets within the given named branch')),
3322 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3312 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3323 ] + logopts + walkopts,
3313 ] + logopts + walkopts,
3324 _('[OPTION]... [FILE]')),
3314 _('[OPTION]... [FILE]')),
3325 "manifest":
3315 "manifest":
3326 (manifest,
3316 (manifest,
3327 [('r', 'rev', '', _('revision to display'))],
3317 [('r', 'rev', '', _('revision to display'))],
3328 _('[-r REV]')),
3318 _('[-r REV]')),
3329 "^merge":
3319 "^merge":
3330 (merge,
3320 (merge,
3331 [('f', 'force', None, _('force a merge with outstanding changes')),
3321 [('f', 'force', None, _('force a merge with outstanding changes')),
3332 ('r', 'rev', '', _('revision to merge')),
3322 ('r', 'rev', '', _('revision to merge')),
3333 ],
3323 ],
3334 _('[-f] [[-r] REV]')),
3324 _('[-f] [[-r] REV]')),
3335 "outgoing|out":
3325 "outgoing|out":
3336 (outgoing,
3326 (outgoing,
3337 [('f', 'force', None,
3327 [('f', 'force', None,
3338 _('run even when remote repository is unrelated')),
3328 _('run even when remote repository is unrelated')),
3339 ('r', 'rev', [],
3329 ('r', 'rev', [],
3340 _('a specific revision up to which you would like to push')),
3330 _('a specific revision up to which you would like to push')),
3341 ('n', 'newest-first', None, _('show newest record first')),
3331 ('n', 'newest-first', None, _('show newest record first')),
3342 ] + logopts + remoteopts,
3332 ] + logopts + remoteopts,
3343 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3333 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3344 "^parents":
3334 "^parents":
3345 (parents,
3335 (parents,
3346 [('r', 'rev', '', _('show parents from the specified revision')),
3336 [('r', 'rev', '', _('show parents from the specified revision')),
3347 ] + templateopts,
3337 ] + templateopts,
3348 _('hg parents [-r REV] [FILE]')),
3338 _('hg parents [-r REV] [FILE]')),
3349 "paths": (paths, [], _('[NAME]')),
3339 "paths": (paths, [], _('[NAME]')),
3350 "^pull":
3340 "^pull":
3351 (pull,
3341 (pull,
3352 [('u', 'update', None,
3342 [('u', 'update', None,
3353 _('update to new tip if changesets were pulled')),
3343 _('update to new tip if changesets were pulled')),
3354 ('f', 'force', None,
3344 ('f', 'force', None,
3355 _('run even when remote repository is unrelated')),
3345 _('run even when remote repository is unrelated')),
3356 ('r', 'rev', [],
3346 ('r', 'rev', [],
3357 _('a specific revision up to which you would like to pull')),
3347 _('a specific revision up to which you would like to pull')),
3358 ] + remoteopts,
3348 ] + remoteopts,
3359 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3349 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3360 "^push":
3350 "^push":
3361 (push,
3351 (push,
3362 [('f', 'force', None, _('force push')),
3352 [('f', 'force', None, _('force push')),
3363 ('r', 'rev', [],
3353 ('r', 'rev', [],
3364 _('a specific revision up to which you would like to push')),
3354 _('a specific revision up to which you would like to push')),
3365 ] + remoteopts,
3355 ] + remoteopts,
3366 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3356 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3367 "recover": (recover, []),
3357 "recover": (recover, []),
3368 "^remove|rm":
3358 "^remove|rm":
3369 (remove,
3359 (remove,
3370 [('A', 'after', None, _('record delete for missing files')),
3360 [('A', 'after', None, _('record delete for missing files')),
3371 ('f', 'force', None,
3361 ('f', 'force', None,
3372 _('remove (and delete) file even if added or modified')),
3362 _('remove (and delete) file even if added or modified')),
3373 ] + walkopts,
3363 ] + walkopts,
3374 _('[OPTION]... FILE...')),
3364 _('[OPTION]... FILE...')),
3375 "rename|mv":
3365 "rename|mv":
3376 (rename,
3366 (rename,
3377 [('A', 'after', None, _('record a rename that has already occurred')),
3367 [('A', 'after', None, _('record a rename that has already occurred')),
3378 ('f', 'force', None,
3368 ('f', 'force', None,
3379 _('forcibly copy over an existing managed file')),
3369 _('forcibly copy over an existing managed file')),
3380 ] + walkopts + dryrunopts,
3370 ] + walkopts + dryrunopts,
3381 _('[OPTION]... SOURCE... DEST')),
3371 _('[OPTION]... SOURCE... DEST')),
3382 "resolve":
3372 "resolve":
3383 (resolve,
3373 (resolve,
3384 [('a', 'all', None, _('remerge all unresolved files')),
3374 [('a', 'all', None, _('remerge all unresolved files')),
3385 ('l', 'list', None, _('list state of files needing merge')),
3375 ('l', 'list', None, _('list state of files needing merge')),
3386 ('m', 'mark', None, _('mark files as resolved')),
3376 ('m', 'mark', None, _('mark files as resolved')),
3387 ('u', 'unmark', None, _('unmark files as resolved'))]
3377 ('u', 'unmark', None, _('unmark files as resolved'))]
3388 + walkopts,
3378 + walkopts,
3389 _('[OPTION]... [FILE]...')),
3379 _('[OPTION]... [FILE]...')),
3390 "revert":
3380 "revert":
3391 (revert,
3381 (revert,
3392 [('a', 'all', None, _('revert all changes when no arguments given')),
3382 [('a', 'all', None, _('revert all changes when no arguments given')),
3393 ('d', 'date', '', _('tipmost revision matching date')),
3383 ('d', 'date', '', _('tipmost revision matching date')),
3394 ('r', 'rev', '', _('revision to revert to')),
3384 ('r', 'rev', '', _('revision to revert to')),
3395 ('', 'no-backup', None, _('do not save backup copies of files')),
3385 ('', 'no-backup', None, _('do not save backup copies of files')),
3396 ] + walkopts + dryrunopts,
3386 ] + walkopts + dryrunopts,
3397 _('[OPTION]... [-r REV] [NAME]...')),
3387 _('[OPTION]... [-r REV] [NAME]...')),
3398 "rollback": (rollback, []),
3388 "rollback": (rollback, []),
3399 "root": (root, []),
3389 "root": (root, []),
3400 "^serve":
3390 "^serve":
3401 (serve,
3391 (serve,
3402 [('A', 'accesslog', '', _('name of access log file to write to')),
3392 [('A', 'accesslog', '', _('name of access log file to write to')),
3403 ('d', 'daemon', None, _('run server in background')),
3393 ('d', 'daemon', None, _('run server in background')),
3404 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3394 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3405 ('E', 'errorlog', '', _('name of error log file to write to')),
3395 ('E', 'errorlog', '', _('name of error log file to write to')),
3406 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3396 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3407 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3397 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3408 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3398 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3409 ('n', 'name', '',
3399 ('n', 'name', '',
3410 _('name to show in web pages (default: working directory)')),
3400 _('name to show in web pages (default: working directory)')),
3411 ('', 'webdir-conf', '', _('name of the webdir config file'
3401 ('', 'webdir-conf', '', _('name of the webdir config file'
3412 ' (serve more than one repository)')),
3402 ' (serve more than one repository)')),
3413 ('', 'pid-file', '', _('name of file to write process ID to')),
3403 ('', 'pid-file', '', _('name of file to write process ID to')),
3414 ('', 'stdio', None, _('for remote clients')),
3404 ('', 'stdio', None, _('for remote clients')),
3415 ('t', 'templates', '', _('web templates to use')),
3405 ('t', 'templates', '', _('web templates to use')),
3416 ('', 'style', '', _('template style to use')),
3406 ('', 'style', '', _('template style to use')),
3417 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3407 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3418 ('', 'certificate', '', _('SSL certificate file'))],
3408 ('', 'certificate', '', _('SSL certificate file'))],
3419 _('[OPTION]...')),
3409 _('[OPTION]...')),
3420 "showconfig|debugconfig":
3410 "showconfig|debugconfig":
3421 (showconfig,
3411 (showconfig,
3422 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3412 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3423 _('[-u] [NAME]...')),
3413 _('[-u] [NAME]...')),
3424 "^status|st":
3414 "^status|st":
3425 (status,
3415 (status,
3426 [('A', 'all', None, _('show status of all files')),
3416 [('A', 'all', None, _('show status of all files')),
3427 ('m', 'modified', None, _('show only modified files')),
3417 ('m', 'modified', None, _('show only modified files')),
3428 ('a', 'added', None, _('show only added files')),
3418 ('a', 'added', None, _('show only added files')),
3429 ('r', 'removed', None, _('show only removed files')),
3419 ('r', 'removed', None, _('show only removed files')),
3430 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3420 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3431 ('c', 'clean', None, _('show only files without changes')),
3421 ('c', 'clean', None, _('show only files without changes')),
3432 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3422 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3433 ('i', 'ignored', None, _('show only ignored files')),
3423 ('i', 'ignored', None, _('show only ignored files')),
3434 ('n', 'no-status', None, _('hide status prefix')),
3424 ('n', 'no-status', None, _('hide status prefix')),
3435 ('C', 'copies', None, _('show source of copied files')),
3425 ('C', 'copies', None, _('show source of copied files')),
3436 ('0', 'print0', None,
3426 ('0', 'print0', None,
3437 _('end filenames with NUL, for use with xargs')),
3427 _('end filenames with NUL, for use with xargs')),
3438 ('', 'rev', [], _('show difference from revision')),
3428 ('', 'rev', [], _('show difference from revision')),
3439 ] + walkopts,
3429 ] + walkopts,
3440 _('[OPTION]... [FILE]...')),
3430 _('[OPTION]... [FILE]...')),
3441 "tag":
3431 "tag":
3442 (tag,
3432 (tag,
3443 [('f', 'force', None, _('replace existing tag')),
3433 [('f', 'force', None, _('replace existing tag')),
3444 ('l', 'local', None, _('make the tag local')),
3434 ('l', 'local', None, _('make the tag local')),
3445 ('r', 'rev', '', _('revision to tag')),
3435 ('r', 'rev', '', _('revision to tag')),
3446 ('', 'remove', None, _('remove a tag')),
3436 ('', 'remove', None, _('remove a tag')),
3447 # -l/--local is already there, commitopts cannot be used
3437 # -l/--local is already there, commitopts cannot be used
3448 ('m', 'message', '', _('use <text> as commit message')),
3438 ('m', 'message', '', _('use <text> as commit message')),
3449 ] + commitopts2,
3439 ] + commitopts2,
3450 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3440 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3451 "tags": (tags, []),
3441 "tags": (tags, []),
3452 "tip":
3442 "tip":
3453 (tip,
3443 (tip,
3454 [('p', 'patch', None, _('show patch')),
3444 [('p', 'patch', None, _('show patch')),
3455 ('g', 'git', None, _('use git extended diff format')),
3445 ('g', 'git', None, _('use git extended diff format')),
3456 ] + templateopts,
3446 ] + templateopts,
3457 _('[-p]')),
3447 _('[-p]')),
3458 "unbundle":
3448 "unbundle":
3459 (unbundle,
3449 (unbundle,
3460 [('u', 'update', None,
3450 [('u', 'update', None,
3461 _('update to new tip if changesets were unbundled'))],
3451 _('update to new tip if changesets were unbundled'))],
3462 _('[-u] FILE...')),
3452 _('[-u] FILE...')),
3463 "^update|up|checkout|co":
3453 "^update|up|checkout|co":
3464 (update,
3454 (update,
3465 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3455 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3466 ('d', 'date', '', _('tipmost revision matching date')),
3456 ('d', 'date', '', _('tipmost revision matching date')),
3467 ('r', 'rev', '', _('revision'))],
3457 ('r', 'rev', '', _('revision'))],
3468 _('[-C] [-d DATE] [[-r] REV]')),
3458 _('[-C] [-d DATE] [[-r] REV]')),
3469 "verify": (verify, []),
3459 "verify": (verify, []),
3470 "version": (version_, []),
3460 "version": (version_, []),
3471 }
3461 }
3472
3462
3473 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3463 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3474 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3464 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3475 optionalrepo = ("identify paths serve showconfig debugancestor")
3465 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now