##// END OF EJS Templates
interpret repo#name url syntax as branch instead of revision...
Sune Foldager -
r10365:d757bc0c default
parent child Browse files
Show More
@@ -0,0 +1,41 b''
1 #!/bin/sh
2
3 # test branch selection options
4 hg init branch
5 cd branch
6 hg branch a
7 echo a > foo
8 hg ci -d '0 0' -Ama
9 echo a2 > foo
10 hg ci -d '0 0' -ma2
11 hg up 0
12 hg branch c
13 echo c > foo
14 hg ci -d '0 0' -mc
15 cd ..
16 hg clone -r 0 branch branch2
17 cd branch2
18 hg up 0
19 hg branch b
20 echo b > foo
21 hg ci -d '0 0' -mb
22 hg up 0
23 hg branch -f b
24 echo b2 > foo
25 hg ci -d '0 0' -mb2
26
27 echo in rev c branch a
28 hg in -qr c ../branch#a
29 echo out branch .
30 hg out -q ../branch#.
31 echo clone branch b
32 cd ..
33 hg clone branch2#b branch3
34 hg -q -R branch3 heads b
35 hg -q -R branch3 parents
36 rm -rf branch3
37 echo clone rev a branch b
38 hg clone -r a branch2#b branch3
39 hg -q -R branch3 heads b
40 hg -q -R branch3 parents
41 rm -rf branch3
@@ -0,0 +1,44 b''
1 marked working directory as branch a
2 adding foo
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 marked working directory as branch c
5 created new head
6 requesting all changes
7 adding changesets
8 adding manifests
9 adding file changes
10 added 1 changesets with 1 changes to 1 files
11 updating to branch a
12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 marked working directory as branch b
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 marked working directory as branch b
17 created new head
18 in rev c branch a
19 1:dd6e60a716c6
20 2:f25d57ab0566
21 out branch .
22 2:65511d0e2b55
23 clone branch b
24 requesting all changes
25 adding changesets
26 adding manifests
27 adding file changes
28 added 3 changesets with 3 changes to 1 files (+1 heads)
29 updating to branch b
30 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 2:65511d0e2b55
32 1:b84708d77ab7
33 2:65511d0e2b55
34 clone rev a branch b
35 requesting all changes
36 adding changesets
37 adding manifests
38 adding file changes
39 added 3 changesets with 3 changes to 1 files (+1 heads)
40 updating to branch a
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 2:65511d0e2b55
43 1:b84708d77ab7
44 0:5b65ba7c951d
@@ -1,376 +1,377 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 the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to view revision graphs from a shell
8 '''command to view revision graphs from a shell
9
9
10 This extension adds a --graph option to the incoming, outgoing and log
10 This extension adds a --graph option to the incoming, outgoing and log
11 commands. When this options is given, an ASCII representation of the
11 commands. When this options is given, an ASCII representation of the
12 revision graph is also shown.
12 revision graph is also shown.
13 '''
13 '''
14
14
15 import os
15 import os
16 from mercurial.cmdutil import revrange, show_changeset
16 from mercurial.cmdutil import revrange, show_changeset
17 from mercurial.commands import templateopts
17 from mercurial.commands import templateopts
18 from mercurial.i18n import _
18 from mercurial.i18n import _
19 from mercurial.node import nullrev
19 from mercurial.node import nullrev
20 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
20 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
21 from mercurial import hg, url, util, graphmod
21 from mercurial import hg, url, util, graphmod
22
22
23 ASCIIDATA = 'ASC'
23 ASCIIDATA = 'ASC'
24
24
25 def asciiedges(seen, rev, parents):
25 def asciiedges(seen, rev, parents):
26 """adds edge info to changelog DAG walk suitable for ascii()"""
26 """adds edge info to changelog DAG walk suitable for ascii()"""
27 if rev not in seen:
27 if rev not in seen:
28 seen.append(rev)
28 seen.append(rev)
29 nodeidx = seen.index(rev)
29 nodeidx = seen.index(rev)
30
30
31 knownparents = []
31 knownparents = []
32 newparents = []
32 newparents = []
33 for parent in parents:
33 for parent in parents:
34 if parent in seen:
34 if parent in seen:
35 knownparents.append(parent)
35 knownparents.append(parent)
36 else:
36 else:
37 newparents.append(parent)
37 newparents.append(parent)
38
38
39 ncols = len(seen)
39 ncols = len(seen)
40 seen[nodeidx:nodeidx + 1] = newparents
40 seen[nodeidx:nodeidx + 1] = newparents
41 edges = [(nodeidx, seen.index(p)) for p in knownparents]
41 edges = [(nodeidx, seen.index(p)) for p in knownparents]
42
42
43 if len(newparents) > 0:
43 if len(newparents) > 0:
44 edges.append((nodeidx, nodeidx))
44 edges.append((nodeidx, nodeidx))
45 if len(newparents) > 1:
45 if len(newparents) > 1:
46 edges.append((nodeidx, nodeidx + 1))
46 edges.append((nodeidx, nodeidx + 1))
47
47
48 nmorecols = len(seen) - ncols
48 nmorecols = len(seen) - ncols
49 return nodeidx, edges, ncols, nmorecols
49 return nodeidx, edges, ncols, nmorecols
50
50
51 def fix_long_right_edges(edges):
51 def fix_long_right_edges(edges):
52 for (i, (start, end)) in enumerate(edges):
52 for (i, (start, end)) in enumerate(edges):
53 if end > start:
53 if end > start:
54 edges[i] = (start, end + 1)
54 edges[i] = (start, end + 1)
55
55
56 def get_nodeline_edges_tail(
56 def get_nodeline_edges_tail(
57 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
57 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
58 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
58 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
59 # Still going in the same non-vertical direction.
59 # Still going in the same non-vertical direction.
60 if n_columns_diff == -1:
60 if n_columns_diff == -1:
61 start = max(node_index + 1, p_node_index)
61 start = max(node_index + 1, p_node_index)
62 tail = ["|", " "] * (start - node_index - 1)
62 tail = ["|", " "] * (start - node_index - 1)
63 tail.extend(["/", " "] * (n_columns - start))
63 tail.extend(["/", " "] * (n_columns - start))
64 return tail
64 return tail
65 else:
65 else:
66 return ["\\", " "] * (n_columns - node_index - 1)
66 return ["\\", " "] * (n_columns - node_index - 1)
67 else:
67 else:
68 return ["|", " "] * (n_columns - node_index - 1)
68 return ["|", " "] * (n_columns - node_index - 1)
69
69
70 def draw_edges(edges, nodeline, interline):
70 def draw_edges(edges, nodeline, interline):
71 for (start, end) in edges:
71 for (start, end) in edges:
72 if start == end + 1:
72 if start == end + 1:
73 interline[2 * end + 1] = "/"
73 interline[2 * end + 1] = "/"
74 elif start == end - 1:
74 elif start == end - 1:
75 interline[2 * start + 1] = "\\"
75 interline[2 * start + 1] = "\\"
76 elif start == end:
76 elif start == end:
77 interline[2 * start] = "|"
77 interline[2 * start] = "|"
78 else:
78 else:
79 nodeline[2 * end] = "+"
79 nodeline[2 * end] = "+"
80 if start > end:
80 if start > end:
81 (start, end) = (end, start)
81 (start, end) = (end, start)
82 for i in range(2 * start + 1, 2 * end):
82 for i in range(2 * start + 1, 2 * end):
83 if nodeline[i] != "+":
83 if nodeline[i] != "+":
84 nodeline[i] = "-"
84 nodeline[i] = "-"
85
85
86 def get_padding_line(ni, n_columns, edges):
86 def get_padding_line(ni, n_columns, edges):
87 line = []
87 line = []
88 line.extend(["|", " "] * ni)
88 line.extend(["|", " "] * ni)
89 if (ni, ni - 1) in edges or (ni, ni) in edges:
89 if (ni, ni - 1) in edges or (ni, ni) in edges:
90 # (ni, ni - 1) (ni, ni)
90 # (ni, ni - 1) (ni, ni)
91 # | | | | | | | |
91 # | | | | | | | |
92 # +---o | | o---+
92 # +---o | | o---+
93 # | | c | | c | |
93 # | | c | | c | |
94 # | |/ / | |/ /
94 # | |/ / | |/ /
95 # | | | | | |
95 # | | | | | |
96 c = "|"
96 c = "|"
97 else:
97 else:
98 c = " "
98 c = " "
99 line.extend([c, " "])
99 line.extend([c, " "])
100 line.extend(["|", " "] * (n_columns - ni - 1))
100 line.extend(["|", " "] * (n_columns - ni - 1))
101 return line
101 return line
102
102
103 def asciistate():
103 def asciistate():
104 """returns the initial value for the "state" argument to ascii()"""
104 """returns the initial value for the "state" argument to ascii()"""
105 return [0, 0]
105 return [0, 0]
106
106
107 def ascii(ui, state, type, char, text, coldata):
107 def ascii(ui, state, type, char, text, coldata):
108 """prints an ASCII graph of the DAG
108 """prints an ASCII graph of the DAG
109
109
110 takes the following arguments (one call per node in the graph):
110 takes the following arguments (one call per node in the graph):
111
111
112 - ui to write to
112 - ui to write to
113 - Somewhere to keep the needed state in (init to asciistate())
113 - Somewhere to keep the needed state in (init to asciistate())
114 - Column of the current node in the set of ongoing edges.
114 - Column of the current node in the set of ongoing edges.
115 - Type indicator of node data == ASCIIDATA.
115 - Type indicator of node data == ASCIIDATA.
116 - Payload: (char, lines):
116 - Payload: (char, lines):
117 - Character to use as node's symbol.
117 - Character to use as node's symbol.
118 - List of lines to display as the node's text.
118 - List of lines to display as the node's text.
119 - Edges; a list of (col, next_col) indicating the edges between
119 - Edges; a list of (col, next_col) indicating the edges between
120 the current node and its parents.
120 the current node and its parents.
121 - Number of columns (ongoing edges) in the current revision.
121 - Number of columns (ongoing edges) in the current revision.
122 - The difference between the number of columns (ongoing edges)
122 - The difference between the number of columns (ongoing edges)
123 in the next revision and the number of columns (ongoing edges)
123 in the next revision and the number of columns (ongoing edges)
124 in the current revision. That is: -1 means one column removed;
124 in the current revision. That is: -1 means one column removed;
125 0 means no columns added or removed; 1 means one column added.
125 0 means no columns added or removed; 1 means one column added.
126 """
126 """
127
127
128 idx, edges, ncols, coldiff = coldata
128 idx, edges, ncols, coldiff = coldata
129 assert -2 < coldiff < 2
129 assert -2 < coldiff < 2
130 if coldiff == -1:
130 if coldiff == -1:
131 # Transform
131 # Transform
132 #
132 #
133 # | | | | | |
133 # | | | | | |
134 # o | | into o---+
134 # o | | into o---+
135 # |X / |/ /
135 # |X / |/ /
136 # | | | |
136 # | | | |
137 fix_long_right_edges(edges)
137 fix_long_right_edges(edges)
138
138
139 # add_padding_line says whether to rewrite
139 # add_padding_line says whether to rewrite
140 #
140 #
141 # | | | | | | | |
141 # | | | | | | | |
142 # | o---+ into | o---+
142 # | o---+ into | o---+
143 # | / / | | | # <--- padding line
143 # | / / | | | # <--- padding line
144 # o | | | / /
144 # o | | | / /
145 # o | |
145 # o | |
146 add_padding_line = (len(text) > 2 and coldiff == -1 and
146 add_padding_line = (len(text) > 2 and coldiff == -1 and
147 [x for (x, y) in edges if x + 1 < y])
147 [x for (x, y) in edges if x + 1 < y])
148
148
149 # fix_nodeline_tail says whether to rewrite
149 # fix_nodeline_tail says whether to rewrite
150 #
150 #
151 # | | o | | | | o | |
151 # | | o | | | | o | |
152 # | | |/ / | | |/ /
152 # | | |/ / | | |/ /
153 # | o | | into | o / / # <--- fixed nodeline tail
153 # | o | | into | o / / # <--- fixed nodeline tail
154 # | |/ / | |/ /
154 # | |/ / | |/ /
155 # o | | o | |
155 # o | | o | |
156 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
156 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
157
157
158 # nodeline is the line containing the node character (typically o)
158 # nodeline is the line containing the node character (typically o)
159 nodeline = ["|", " "] * idx
159 nodeline = ["|", " "] * idx
160 nodeline.extend([char, " "])
160 nodeline.extend([char, " "])
161
161
162 nodeline.extend(
162 nodeline.extend(
163 get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
163 get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
164 state[0], fix_nodeline_tail))
164 state[0], fix_nodeline_tail))
165
165
166 # shift_interline is the line containing the non-vertical
166 # shift_interline is the line containing the non-vertical
167 # edges between this entry and the next
167 # edges between this entry and the next
168 shift_interline = ["|", " "] * idx
168 shift_interline = ["|", " "] * idx
169 if coldiff == -1:
169 if coldiff == -1:
170 n_spaces = 1
170 n_spaces = 1
171 edge_ch = "/"
171 edge_ch = "/"
172 elif coldiff == 0:
172 elif coldiff == 0:
173 n_spaces = 2
173 n_spaces = 2
174 edge_ch = "|"
174 edge_ch = "|"
175 else:
175 else:
176 n_spaces = 3
176 n_spaces = 3
177 edge_ch = "\\"
177 edge_ch = "\\"
178 shift_interline.extend(n_spaces * [" "])
178 shift_interline.extend(n_spaces * [" "])
179 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
179 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
180
180
181 # draw edges from the current node to its parents
181 # draw edges from the current node to its parents
182 draw_edges(edges, nodeline, shift_interline)
182 draw_edges(edges, nodeline, shift_interline)
183
183
184 # lines is the list of all graph lines to print
184 # lines is the list of all graph lines to print
185 lines = [nodeline]
185 lines = [nodeline]
186 if add_padding_line:
186 if add_padding_line:
187 lines.append(get_padding_line(idx, ncols, edges))
187 lines.append(get_padding_line(idx, ncols, edges))
188 lines.append(shift_interline)
188 lines.append(shift_interline)
189
189
190 # make sure that there are as many graph lines as there are
190 # make sure that there are as many graph lines as there are
191 # log strings
191 # log strings
192 while len(text) < len(lines):
192 while len(text) < len(lines):
193 text.append("")
193 text.append("")
194 if len(lines) < len(text):
194 if len(lines) < len(text):
195 extra_interline = ["|", " "] * (ncols + coldiff)
195 extra_interline = ["|", " "] * (ncols + coldiff)
196 while len(lines) < len(text):
196 while len(lines) < len(text):
197 lines.append(extra_interline)
197 lines.append(extra_interline)
198
198
199 # print lines
199 # print lines
200 indentation_level = max(ncols, ncols + coldiff)
200 indentation_level = max(ncols, ncols + coldiff)
201 for (line, logstr) in zip(lines, text):
201 for (line, logstr) in zip(lines, text):
202 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
202 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
203 ui.write(ln.rstrip() + '\n')
203 ui.write(ln.rstrip() + '\n')
204
204
205 # ... and start over
205 # ... and start over
206 state[0] = coldiff
206 state[0] = coldiff
207 state[1] = idx
207 state[1] = idx
208
208
209 def get_revs(repo, rev_opt):
209 def get_revs(repo, rev_opt):
210 if rev_opt:
210 if rev_opt:
211 revs = revrange(repo, rev_opt)
211 revs = revrange(repo, rev_opt)
212 return (max(revs), min(revs))
212 return (max(revs), min(revs))
213 else:
213 else:
214 return (len(repo) - 1, 0)
214 return (len(repo) - 1, 0)
215
215
216 def check_unsupported_flags(opts):
216 def check_unsupported_flags(opts):
217 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
217 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
218 "only_merges", "user", "only_branch", "prune", "newest_first",
218 "only_merges", "user", "only_branch", "prune", "newest_first",
219 "no_merges", "include", "exclude"]:
219 "no_merges", "include", "exclude"]:
220 if op in opts and opts[op]:
220 if op in opts and opts[op]:
221 raise util.Abort(_("--graph option is incompatible with --%s")
221 raise util.Abort(_("--graph option is incompatible with --%s")
222 % op.replace("_", "-"))
222 % op.replace("_", "-"))
223
223
224 def generate(ui, dag, displayer, showparents, edgefn):
224 def generate(ui, dag, displayer, showparents, edgefn):
225 seen, state = [], asciistate()
225 seen, state = [], asciistate()
226 for rev, type, ctx, parents in dag:
226 for rev, type, ctx, parents in dag:
227 char = ctx.node() in showparents and '@' or 'o'
227 char = ctx.node() in showparents and '@' or 'o'
228 displayer.show(ctx)
228 displayer.show(ctx)
229 lines = displayer.hunk.pop(rev).split('\n')[:-1]
229 lines = displayer.hunk.pop(rev).split('\n')[:-1]
230 ascii(ui, state, type, char, lines, edgefn(seen, rev, parents))
230 ascii(ui, state, type, char, lines, edgefn(seen, rev, parents))
231
231
232 def graphlog(ui, repo, path=None, **opts):
232 def graphlog(ui, repo, path=None, **opts):
233 """show revision history alongside an ASCII revision graph
233 """show revision history alongside an ASCII revision graph
234
234
235 Print a revision history alongside a revision graph drawn with
235 Print a revision history alongside a revision graph drawn with
236 ASCII characters.
236 ASCII characters.
237
237
238 Nodes printed as an @ character are parents of the working
238 Nodes printed as an @ character are parents of the working
239 directory.
239 directory.
240 """
240 """
241
241
242 check_unsupported_flags(opts)
242 check_unsupported_flags(opts)
243 limit = cmdutil.loglimit(opts)
243 limit = cmdutil.loglimit(opts)
244 start, stop = get_revs(repo, opts["rev"])
244 start, stop = get_revs(repo, opts["rev"])
245 if start == nullrev:
245 if start == nullrev:
246 return
246 return
247
247
248 if path:
248 if path:
249 path = util.canonpath(repo.root, os.getcwd(), path)
249 path = util.canonpath(repo.root, os.getcwd(), path)
250 if path: # could be reset in canonpath
250 if path: # could be reset in canonpath
251 revdag = graphmod.filerevs(repo, path, start, stop, limit)
251 revdag = graphmod.filerevs(repo, path, start, stop, limit)
252 else:
252 else:
253 if limit is not None:
253 if limit is not None:
254 stop = max(stop, start - limit + 1)
254 stop = max(stop, start - limit + 1)
255 revdag = graphmod.revisions(repo, start, stop)
255 revdag = graphmod.revisions(repo, start, stop)
256
256
257 displayer = show_changeset(ui, repo, opts, buffered=True)
257 displayer = show_changeset(ui, repo, opts, buffered=True)
258 showparents = [ctx.node() for ctx in repo[None].parents()]
258 showparents = [ctx.node() for ctx in repo[None].parents()]
259 generate(ui, revdag, displayer, showparents, asciiedges)
259 generate(ui, revdag, displayer, showparents, asciiedges)
260
260
261 def graphrevs(repo, nodes, opts):
261 def graphrevs(repo, nodes, opts):
262 limit = cmdutil.loglimit(opts)
262 limit = cmdutil.loglimit(opts)
263 nodes.reverse()
263 nodes.reverse()
264 if limit is not None:
264 if limit is not None:
265 nodes = nodes[:limit]
265 nodes = nodes[:limit]
266 return graphmod.nodes(repo, nodes)
266 return graphmod.nodes(repo, nodes)
267
267
268 def goutgoing(ui, repo, dest=None, **opts):
268 def goutgoing(ui, repo, dest=None, **opts):
269 """show the outgoing changesets alongside an ASCII revision graph
269 """show the outgoing changesets alongside an ASCII revision graph
270
270
271 Print the outgoing changesets alongside a revision graph drawn with
271 Print the outgoing changesets alongside a revision graph drawn with
272 ASCII characters.
272 ASCII characters.
273
273
274 Nodes printed as an @ character are parents of the working
274 Nodes printed as an @ character are parents of the working
275 directory.
275 directory.
276 """
276 """
277
277
278 check_unsupported_flags(opts)
278 check_unsupported_flags(opts)
279 dest, revs, checkout = hg.parseurl(
279 dest = ui.expandpath(dest or 'default-push', dest or 'default')
280 ui.expandpath(dest or 'default-push', dest or 'default'),
280 dest, branches = hg.parseurl(dest)
281 opts.get('rev'))
281 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
282 other = hg.repository(cmdutil.remoteui(ui, opts), dest)
282 if revs:
283 if revs:
283 revs = [repo.lookup(rev) for rev in revs]
284 revs = [repo.lookup(rev) for rev in revs]
284 other = hg.repository(cmdutil.remoteui(ui, opts), dest)
285 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
285 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
286 o = repo.findoutgoing(other, force=opts.get('force'))
286 o = repo.findoutgoing(other, force=opts.get('force'))
287 if not o:
287 if not o:
288 ui.status(_("no changes found\n"))
288 ui.status(_("no changes found\n"))
289 return
289 return
290
290
291 o = repo.changelog.nodesbetween(o, revs)[0]
291 o = repo.changelog.nodesbetween(o, revs)[0]
292 revdag = graphrevs(repo, o, opts)
292 revdag = graphrevs(repo, o, opts)
293 displayer = show_changeset(ui, repo, opts, buffered=True)
293 displayer = show_changeset(ui, repo, opts, buffered=True)
294 showparents = [ctx.node() for ctx in repo[None].parents()]
294 showparents = [ctx.node() for ctx in repo[None].parents()]
295 generate(ui, revdag, displayer, showparents, asciiedges)
295 generate(ui, revdag, displayer, showparents, asciiedges)
296
296
297 def gincoming(ui, repo, source="default", **opts):
297 def gincoming(ui, repo, source="default", **opts):
298 """show the incoming changesets alongside an ASCII revision graph
298 """show the incoming changesets alongside an ASCII revision graph
299
299
300 Print the incoming changesets alongside a revision graph drawn with
300 Print the incoming changesets alongside a revision graph drawn with
301 ASCII characters.
301 ASCII characters.
302
302
303 Nodes printed as an @ character are parents of the working
303 Nodes printed as an @ character are parents of the working
304 directory.
304 directory.
305 """
305 """
306
306
307 check_unsupported_flags(opts)
307 check_unsupported_flags(opts)
308 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
308 source, branches = hg.parseurl(ui.expandpath(source))
309 other = hg.repository(cmdutil.remoteui(repo, opts), source)
309 other = hg.repository(cmdutil.remoteui(repo, opts), source)
310 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
310 ui.status(_('comparing with %s\n') % url.hidepassword(source))
311 ui.status(_('comparing with %s\n') % url.hidepassword(source))
311 if revs:
312 if revs:
312 revs = [other.lookup(rev) for rev in revs]
313 revs = [other.lookup(rev) for rev in revs]
313 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
314 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
314 if not incoming:
315 if not incoming:
315 try:
316 try:
316 os.unlink(opts["bundle"])
317 os.unlink(opts["bundle"])
317 except:
318 except:
318 pass
319 pass
319 ui.status(_("no changes found\n"))
320 ui.status(_("no changes found\n"))
320 return
321 return
321
322
322 cleanup = None
323 cleanup = None
323 try:
324 try:
324
325
325 fname = opts["bundle"]
326 fname = opts["bundle"]
326 if fname or not other.local():
327 if fname or not other.local():
327 # create a bundle (uncompressed if other repo is not local)
328 # create a bundle (uncompressed if other repo is not local)
328 if revs is None:
329 if revs is None:
329 cg = other.changegroup(incoming, "incoming")
330 cg = other.changegroup(incoming, "incoming")
330 else:
331 else:
331 cg = other.changegroupsubset(incoming, revs, 'incoming')
332 cg = other.changegroupsubset(incoming, revs, 'incoming')
332 bundletype = other.local() and "HG10BZ" or "HG10UN"
333 bundletype = other.local() and "HG10BZ" or "HG10UN"
333 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
334 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
334 # keep written bundle?
335 # keep written bundle?
335 if opts["bundle"]:
336 if opts["bundle"]:
336 cleanup = None
337 cleanup = None
337 if not other.local():
338 if not other.local():
338 # use the created uncompressed bundlerepo
339 # use the created uncompressed bundlerepo
339 other = bundlerepo.bundlerepository(ui, repo.root, fname)
340 other = bundlerepo.bundlerepository(ui, repo.root, fname)
340
341
341 chlist = other.changelog.nodesbetween(incoming, revs)[0]
342 chlist = other.changelog.nodesbetween(incoming, revs)[0]
342 revdag = graphrevs(other, chlist, opts)
343 revdag = graphrevs(other, chlist, opts)
343 displayer = show_changeset(ui, other, opts, buffered=True)
344 displayer = show_changeset(ui, other, opts, buffered=True)
344 showparents = [ctx.node() for ctx in repo[None].parents()]
345 showparents = [ctx.node() for ctx in repo[None].parents()]
345 generate(ui, revdag, displayer, showparents, asciiedges)
346 generate(ui, revdag, displayer, showparents, asciiedges)
346
347
347 finally:
348 finally:
348 if hasattr(other, 'close'):
349 if hasattr(other, 'close'):
349 other.close()
350 other.close()
350 if cleanup:
351 if cleanup:
351 os.unlink(cleanup)
352 os.unlink(cleanup)
352
353
353 def uisetup(ui):
354 def uisetup(ui):
354 '''Initialize the extension.'''
355 '''Initialize the extension.'''
355 _wrapcmd(ui, 'log', commands.table, graphlog)
356 _wrapcmd(ui, 'log', commands.table, graphlog)
356 _wrapcmd(ui, 'incoming', commands.table, gincoming)
357 _wrapcmd(ui, 'incoming', commands.table, gincoming)
357 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
358 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
358
359
359 def _wrapcmd(ui, cmd, table, wrapfn):
360 def _wrapcmd(ui, cmd, table, wrapfn):
360 '''wrap the command'''
361 '''wrap the command'''
361 def graph(orig, *args, **kwargs):
362 def graph(orig, *args, **kwargs):
362 if kwargs['graph']:
363 if kwargs['graph']:
363 return wrapfn(*args, **kwargs)
364 return wrapfn(*args, **kwargs)
364 return orig(*args, **kwargs)
365 return orig(*args, **kwargs)
365 entry = extensions.wrapcommand(table, cmd, graph)
366 entry = extensions.wrapcommand(table, cmd, graph)
366 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
367 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
367
368
368 cmdtable = {
369 cmdtable = {
369 "glog":
370 "glog":
370 (graphlog,
371 (graphlog,
371 [('l', 'limit', '', _('limit number of changes displayed')),
372 [('l', 'limit', '', _('limit number of changes displayed')),
372 ('p', 'patch', False, _('show patch')),
373 ('p', 'patch', False, _('show patch')),
373 ('r', 'rev', [], _('show the specified revision or range')),
374 ('r', 'rev', [], _('show the specified revision or range')),
374 ] + templateopts,
375 ] + templateopts,
375 _('hg glog [OPTION]... [FILE]')),
376 _('hg glog [OPTION]... [FILE]')),
376 }
377 }
@@ -1,522 +1,523 b''
1 # patchbomb.py - sending Mercurial changesets as patch emails
1 # patchbomb.py - sending Mercurial changesets as patch emails
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to send changesets as (a series of) patch emails
8 '''command to send changesets as (a series of) patch emails
9
9
10 The series is started off with a "[PATCH 0 of N]" introduction, which
10 The series is started off with a "[PATCH 0 of N]" introduction, which
11 describes the series as a whole.
11 describes the series as a whole.
12
12
13 Each patch email has a Subject line of "[PATCH M of N] ...", using the
13 Each patch email has a Subject line of "[PATCH M of N] ...", using the
14 first line of the changeset description as the subject text. The
14 first line of the changeset description as the subject text. The
15 message contains two or three body parts:
15 message contains two or three body parts:
16
16
17 - The changeset description.
17 - The changeset description.
18 - [Optional] The result of running diffstat on the patch.
18 - [Optional] The result of running diffstat on the patch.
19 - The patch itself, as generated by "hg export".
19 - The patch itself, as generated by "hg export".
20
20
21 Each message refers to the first in the series using the In-Reply-To
21 Each message refers to the first in the series using the In-Reply-To
22 and References headers, so they will show up as a sequence in threaded
22 and References headers, so they will show up as a sequence in threaded
23 mail and news readers, and in mail archives.
23 mail and news readers, and in mail archives.
24
24
25 With the -d/--diffstat option, you will be prompted for each changeset
25 With the -d/--diffstat option, you will be prompted for each changeset
26 with a diffstat summary and the changeset summary, so you can be sure
26 with a diffstat summary and the changeset summary, so you can be sure
27 you are sending the right changes.
27 you are sending the right changes.
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 Use ``[patchbomb]`` as configuration section name if you need to
38 Use ``[patchbomb]`` as configuration section name if you need to
39 override global ``[email]`` address settings.
39 override global ``[email]`` address settings.
40
40
41 Then you can use the "hg email" command to mail a series of changesets
41 Then you can use the "hg email" command to mail a series of changesets
42 as a patchbomb.
42 as a patchbomb.
43
43
44 To avoid sending patches prematurely, it is a good idea to first run
44 To avoid sending patches prematurely, it is a good idea to first run
45 the "email" command with the "-n" option (test only). You will be
45 the "email" command with the "-n" option (test only). You will be
46 prompted for an email recipient address, a subject and an introductory
46 prompted for an email recipient address, a subject and an introductory
47 message describing the patches of your patchbomb. Then when all is
47 message describing the patches of your patchbomb. Then when all is
48 done, patchbomb messages are displayed. If the PAGER environment
48 done, patchbomb messages are displayed. If the PAGER environment
49 variable is set, your pager will be fired up once for each patchbomb
49 variable is set, your pager will be fired up once for each patchbomb
50 message, so you can verify everything is alright.
50 message, so you can verify everything is alright.
51
51
52 The -m/--mbox option is also very useful. Instead of previewing each
52 The -m/--mbox option is also very useful. Instead of previewing each
53 patchbomb message in a pager or sending the messages directly, it will
53 patchbomb message in a pager or sending the messages directly, it will
54 create a UNIX mailbox file with the patch emails. This mailbox file
54 create a UNIX mailbox file with the patch emails. This mailbox file
55 can be previewed with any mail user agent which supports UNIX mbox
55 can be previewed with any mail user agent which supports UNIX mbox
56 files, e.g. with mutt::
56 files, e.g. with mutt::
57
57
58 % mutt -R -f mbox
58 % mutt -R -f mbox
59
59
60 When you are previewing the patchbomb messages, you can use ``formail``
60 When you are previewing the patchbomb messages, you can use ``formail``
61 (a utility that is commonly installed as part of the procmail
61 (a utility that is commonly installed as part of the procmail
62 package), to send each message out::
62 package), to send each message out::
63
63
64 % formail -s sendmail -bm -t < mbox
64 % formail -s sendmail -bm -t < mbox
65
65
66 That should be all. Now your patchbomb is on its way out.
66 That should be all. Now your patchbomb is on its way out.
67
67
68 You can also either configure the method option in the email section
68 You can also either configure the method option in the email section
69 to be a sendmail compatible mailer or fill out the [smtp] section so
69 to be a sendmail compatible mailer or fill out the [smtp] section so
70 that the patchbomb extension can automatically send patchbombs
70 that the patchbomb extension can automatically send patchbombs
71 directly from the commandline. See the [email] and [smtp] sections in
71 directly from the commandline. See the [email] and [smtp] sections in
72 hgrc(5) for details.
72 hgrc(5) for details.
73 '''
73 '''
74
74
75 import os, errno, socket, tempfile, cStringIO, time
75 import os, errno, socket, tempfile, cStringIO, time
76 import email.MIMEMultipart, email.MIMEBase
76 import email.MIMEMultipart, email.MIMEBase
77 import email.Utils, email.Encoders, email.Generator
77 import email.Utils, email.Encoders, email.Generator
78 from mercurial import cmdutil, commands, hg, mail, patch, util
78 from mercurial import cmdutil, commands, hg, mail, patch, util
79 from mercurial.i18n import _
79 from mercurial.i18n import _
80 from mercurial.node import bin
80 from mercurial.node import bin
81
81
82 def prompt(ui, prompt, default=None, rest=':'):
82 def prompt(ui, prompt, default=None, rest=':'):
83 if not ui.interactive():
83 if not ui.interactive():
84 if default is not None:
84 if default is not None:
85 return default
85 return default
86 raise util.Abort(_("%s Please enter a valid value" % (prompt + rest)))
86 raise util.Abort(_("%s Please enter a valid value" % (prompt + rest)))
87 if default:
87 if default:
88 prompt += ' [%s]' % default
88 prompt += ' [%s]' % default
89 prompt += rest
89 prompt += rest
90 while True:
90 while True:
91 r = ui.prompt(prompt, default=default)
91 r = ui.prompt(prompt, default=default)
92 if r:
92 if r:
93 return r
93 return r
94 if default is not None:
94 if default is not None:
95 return default
95 return default
96 ui.warn(_('Please enter a valid value.\n'))
96 ui.warn(_('Please enter a valid value.\n'))
97
97
98 def cdiffstat(ui, summary, patchlines):
98 def cdiffstat(ui, summary, patchlines):
99 s = patch.diffstat(patchlines)
99 s = patch.diffstat(patchlines)
100 if summary:
100 if summary:
101 ui.write(summary, '\n')
101 ui.write(summary, '\n')
102 ui.write(s, '\n')
102 ui.write(s, '\n')
103 ans = prompt(ui, _('does the diffstat above look okay?'), 'y')
103 ans = prompt(ui, _('does the diffstat above look okay?'), 'y')
104 if not ans.lower().startswith('y'):
104 if not ans.lower().startswith('y'):
105 raise util.Abort(_('diffstat rejected'))
105 raise util.Abort(_('diffstat rejected'))
106 return s
106 return s
107
107
108 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
108 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
109
109
110 desc = []
110 desc = []
111 node = None
111 node = None
112 body = ''
112 body = ''
113
113
114 for line in patch:
114 for line in patch:
115 if line.startswith('#'):
115 if line.startswith('#'):
116 if line.startswith('# Node ID'):
116 if line.startswith('# Node ID'):
117 node = line.split()[-1]
117 node = line.split()[-1]
118 continue
118 continue
119 if line.startswith('diff -r') or line.startswith('diff --git'):
119 if line.startswith('diff -r') or line.startswith('diff --git'):
120 break
120 break
121 desc.append(line)
121 desc.append(line)
122
122
123 if not patchname and not node:
123 if not patchname and not node:
124 raise ValueError
124 raise ValueError
125
125
126 if opts.get('attach'):
126 if opts.get('attach'):
127 body = ('\n'.join(desc[1:]).strip() or
127 body = ('\n'.join(desc[1:]).strip() or
128 'Patch subject is complete summary.')
128 'Patch subject is complete summary.')
129 body += '\n\n\n'
129 body += '\n\n\n'
130
130
131 if opts.get('plain'):
131 if opts.get('plain'):
132 while patch and patch[0].startswith('# '):
132 while patch and patch[0].startswith('# '):
133 patch.pop(0)
133 patch.pop(0)
134 if patch:
134 if patch:
135 patch.pop(0)
135 patch.pop(0)
136 while patch and not patch[0].strip():
136 while patch and not patch[0].strip():
137 patch.pop(0)
137 patch.pop(0)
138
138
139 if opts.get('diffstat'):
139 if opts.get('diffstat'):
140 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n'
140 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n'
141
141
142 if opts.get('attach') or opts.get('inline'):
142 if opts.get('attach') or opts.get('inline'):
143 msg = email.MIMEMultipart.MIMEMultipart()
143 msg = email.MIMEMultipart.MIMEMultipart()
144 if body:
144 if body:
145 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
145 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
146 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test'))
146 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test'))
147 binnode = bin(node)
147 binnode = bin(node)
148 # if node is mq patch, it will have the patch file's name as a tag
148 # if node is mq patch, it will have the patch file's name as a tag
149 if not patchname:
149 if not patchname:
150 patchtags = [t for t in repo.nodetags(binnode)
150 patchtags = [t for t in repo.nodetags(binnode)
151 if t.endswith('.patch') or t.endswith('.diff')]
151 if t.endswith('.patch') or t.endswith('.diff')]
152 if patchtags:
152 if patchtags:
153 patchname = patchtags[0]
153 patchname = patchtags[0]
154 elif total > 1:
154 elif total > 1:
155 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
155 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
156 binnode, seqno=idx, total=total)
156 binnode, seqno=idx, total=total)
157 else:
157 else:
158 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
158 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
159 disposition = 'inline'
159 disposition = 'inline'
160 if opts.get('attach'):
160 if opts.get('attach'):
161 disposition = 'attachment'
161 disposition = 'attachment'
162 p['Content-Disposition'] = disposition + '; filename=' + patchname
162 p['Content-Disposition'] = disposition + '; filename=' + patchname
163 msg.attach(p)
163 msg.attach(p)
164 else:
164 else:
165 body += '\n'.join(patch)
165 body += '\n'.join(patch)
166 msg = mail.mimetextpatch(body, display=opts.get('test'))
166 msg = mail.mimetextpatch(body, display=opts.get('test'))
167
167
168 flag = ' '.join(opts.get('flag'))
168 flag = ' '.join(opts.get('flag'))
169 if flag:
169 if flag:
170 flag = ' ' + flag
170 flag = ' ' + flag
171
171
172 subj = desc[0].strip().rstrip('. ')
172 subj = desc[0].strip().rstrip('. ')
173 if total == 1 and not opts.get('intro'):
173 if total == 1 and not opts.get('intro'):
174 subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
174 subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
175 else:
175 else:
176 tlen = len(str(total))
176 tlen = len(str(total))
177 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj)
177 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj)
178 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
178 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
179 msg['X-Mercurial-Node'] = node
179 msg['X-Mercurial-Node'] = node
180 return msg, subj
180 return msg, subj
181
181
182 def patchbomb(ui, repo, *revs, **opts):
182 def patchbomb(ui, repo, *revs, **opts):
183 '''send changesets by email
183 '''send changesets by email
184
184
185 By default, diffs are sent in the format generated by hg export,
185 By default, diffs are sent in the format generated by hg export,
186 one per message. The series starts with a "[PATCH 0 of N]"
186 one per message. The series starts with a "[PATCH 0 of N]"
187 introduction, which describes the series as a whole.
187 introduction, which describes the series as a whole.
188
188
189 Each patch email has a Subject line of "[PATCH M of N] ...", using
189 Each patch email has a Subject line of "[PATCH M of N] ...", using
190 the first line of the changeset description as the subject text.
190 the first line of the changeset description as the subject text.
191 The message contains two or three parts. First, the changeset
191 The message contains two or three parts. First, the changeset
192 description. Next, (optionally) if the diffstat program is
192 description. Next, (optionally) if the diffstat program is
193 installed and -d/--diffstat is used, the result of running
193 installed and -d/--diffstat is used, the result of running
194 diffstat on the patch. Finally, the patch itself, as generated by
194 diffstat on the patch. Finally, the patch itself, as generated by
195 "hg export".
195 "hg export".
196
196
197 By default the patch is included as text in the email body for
197 By default the patch is included as text in the email body for
198 easy reviewing. Using the -a/--attach option will instead create
198 easy reviewing. Using the -a/--attach option will instead create
199 an attachment for the patch. With -i/--inline an inline attachment
199 an attachment for the patch. With -i/--inline an inline attachment
200 will be created.
200 will be created.
201
201
202 With -o/--outgoing, emails will be generated for patches not found
202 With -o/--outgoing, emails will be generated for patches not found
203 in the destination repository (or only those which are ancestors
203 in the destination repository (or only those which are ancestors
204 of the specified revisions if any are provided)
204 of the specified revisions if any are provided)
205
205
206 With -b/--bundle, changesets are selected as for --outgoing, but a
206 With -b/--bundle, changesets are selected as for --outgoing, but a
207 single email containing a binary Mercurial bundle as an attachment
207 single email containing a binary Mercurial bundle as an attachment
208 will be sent.
208 will be sent.
209
209
210 Examples::
210 Examples::
211
211
212 hg email -r 3000 # send patch 3000 only
212 hg email -r 3000 # send patch 3000 only
213 hg email -r 3000 -r 3001 # send patches 3000 and 3001
213 hg email -r 3000 -r 3001 # send patches 3000 and 3001
214 hg email -r 3000:3005 # send patches 3000 through 3005
214 hg email -r 3000:3005 # send patches 3000 through 3005
215 hg email 3000 # send patch 3000 (deprecated)
215 hg email 3000 # send patch 3000 (deprecated)
216
216
217 hg email -o # send all patches not in default
217 hg email -o # send all patches not in default
218 hg email -o DEST # send all patches not in DEST
218 hg email -o DEST # send all patches not in DEST
219 hg email -o -r 3000 # send all ancestors of 3000 not in default
219 hg email -o -r 3000 # send all ancestors of 3000 not in default
220 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
220 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
221
221
222 hg email -b # send bundle of all patches not in default
222 hg email -b # send bundle of all patches not in default
223 hg email -b DEST # send bundle of all patches not in DEST
223 hg email -b DEST # send bundle of all patches not in DEST
224 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
224 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
225 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
225 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
226
226
227 Before using this command, you will need to enable email in your
227 Before using this command, you will need to enable email in your
228 hgrc. See the [email] section in hgrc(5) for details.
228 hgrc. See the [email] section in hgrc(5) for details.
229 '''
229 '''
230
230
231 _charsets = mail._charsets(ui)
231 _charsets = mail._charsets(ui)
232
232
233 def outgoing(dest, revs):
233 def outgoing(dest, revs):
234 '''Return the revisions present locally but not in dest'''
234 '''Return the revisions present locally but not in dest'''
235 dest = ui.expandpath(dest or 'default-push', dest or 'default')
235 dest = ui.expandpath(dest or 'default-push', dest or 'default')
236 dest, revs, checkout = hg.parseurl(dest, revs)
236 dest, branches = hg.parseurl(dest)
237 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
237 if revs:
238 if revs:
238 revs = [repo.lookup(rev) for rev in revs]
239 revs = [repo.lookup(rev) for rev in revs]
239 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
240 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
240 ui.status(_('comparing with %s\n') % dest)
241 ui.status(_('comparing with %s\n') % dest)
241 o = repo.findoutgoing(other)
242 o = repo.findoutgoing(other)
242 if not o:
243 if not o:
243 ui.status(_("no changes found\n"))
244 ui.status(_("no changes found\n"))
244 return []
245 return []
245 o = repo.changelog.nodesbetween(o, revs)[0]
246 o = repo.changelog.nodesbetween(o, revs)[0]
246 return [str(repo.changelog.rev(r)) for r in o]
247 return [str(repo.changelog.rev(r)) for r in o]
247
248
248 def getpatches(revs):
249 def getpatches(revs):
249 for r in cmdutil.revrange(repo, revs):
250 for r in cmdutil.revrange(repo, revs):
250 output = cStringIO.StringIO()
251 output = cStringIO.StringIO()
251 patch.export(repo, [r], fp=output,
252 patch.export(repo, [r], fp=output,
252 opts=patch.diffopts(ui, opts))
253 opts=patch.diffopts(ui, opts))
253 yield output.getvalue().split('\n')
254 yield output.getvalue().split('\n')
254
255
255 def getbundle(dest):
256 def getbundle(dest):
256 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
257 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
257 tmpfn = os.path.join(tmpdir, 'bundle')
258 tmpfn = os.path.join(tmpdir, 'bundle')
258 try:
259 try:
259 commands.bundle(ui, repo, tmpfn, dest, **opts)
260 commands.bundle(ui, repo, tmpfn, dest, **opts)
260 return open(tmpfn, 'rb').read()
261 return open(tmpfn, 'rb').read()
261 finally:
262 finally:
262 try:
263 try:
263 os.unlink(tmpfn)
264 os.unlink(tmpfn)
264 except:
265 except:
265 pass
266 pass
266 os.rmdir(tmpdir)
267 os.rmdir(tmpdir)
267
268
268 if not (opts.get('test') or opts.get('mbox')):
269 if not (opts.get('test') or opts.get('mbox')):
269 # really sending
270 # really sending
270 mail.validateconfig(ui)
271 mail.validateconfig(ui)
271
272
272 if not (revs or opts.get('rev')
273 if not (revs or opts.get('rev')
273 or opts.get('outgoing') or opts.get('bundle')
274 or opts.get('outgoing') or opts.get('bundle')
274 or opts.get('patches')):
275 or opts.get('patches')):
275 raise util.Abort(_('specify at least one changeset with -r or -o'))
276 raise util.Abort(_('specify at least one changeset with -r or -o'))
276
277
277 if opts.get('outgoing') and opts.get('bundle'):
278 if opts.get('outgoing') and opts.get('bundle'):
278 raise util.Abort(_("--outgoing mode always on with --bundle;"
279 raise util.Abort(_("--outgoing mode always on with --bundle;"
279 " do not re-specify --outgoing"))
280 " do not re-specify --outgoing"))
280
281
281 if opts.get('outgoing') or opts.get('bundle'):
282 if opts.get('outgoing') or opts.get('bundle'):
282 if len(revs) > 1:
283 if len(revs) > 1:
283 raise util.Abort(_("too many destinations"))
284 raise util.Abort(_("too many destinations"))
284 dest = revs and revs[0] or None
285 dest = revs and revs[0] or None
285 revs = []
286 revs = []
286
287
287 if opts.get('rev'):
288 if opts.get('rev'):
288 if revs:
289 if revs:
289 raise util.Abort(_('use only one form to specify the revision'))
290 raise util.Abort(_('use only one form to specify the revision'))
290 revs = opts.get('rev')
291 revs = opts.get('rev')
291
292
292 if opts.get('outgoing'):
293 if opts.get('outgoing'):
293 revs = outgoing(dest, opts.get('rev'))
294 revs = outgoing(dest, opts.get('rev'))
294 if opts.get('bundle'):
295 if opts.get('bundle'):
295 opts['revs'] = revs
296 opts['revs'] = revs
296
297
297 # start
298 # start
298 if opts.get('date'):
299 if opts.get('date'):
299 start_time = util.parsedate(opts.get('date'))
300 start_time = util.parsedate(opts.get('date'))
300 else:
301 else:
301 start_time = util.makedate()
302 start_time = util.makedate()
302
303
303 def genmsgid(id):
304 def genmsgid(id):
304 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
305 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
305
306
306 def getdescription(body, sender):
307 def getdescription(body, sender):
307 if opts.get('desc'):
308 if opts.get('desc'):
308 body = open(opts.get('desc')).read()
309 body = open(opts.get('desc')).read()
309 else:
310 else:
310 ui.write(_('\nWrite the introductory message for the '
311 ui.write(_('\nWrite the introductory message for the '
311 'patch series.\n\n'))
312 'patch series.\n\n'))
312 body = ui.edit(body, sender)
313 body = ui.edit(body, sender)
313 return body
314 return body
314
315
315 def getpatchmsgs(patches, patchnames=None):
316 def getpatchmsgs(patches, patchnames=None):
316 jumbo = []
317 jumbo = []
317 msgs = []
318 msgs = []
318
319
319 ui.write(_('This patch series consists of %d patches.\n\n')
320 ui.write(_('This patch series consists of %d patches.\n\n')
320 % len(patches))
321 % len(patches))
321
322
322 name = None
323 name = None
323 for i, p in enumerate(patches):
324 for i, p in enumerate(patches):
324 jumbo.extend(p)
325 jumbo.extend(p)
325 if patchnames:
326 if patchnames:
326 name = patchnames[i]
327 name = patchnames[i]
327 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
328 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
328 len(patches), name)
329 len(patches), name)
329 msgs.append(msg)
330 msgs.append(msg)
330
331
331 if len(patches) > 1 or opts.get('intro'):
332 if len(patches) > 1 or opts.get('intro'):
332 tlen = len(str(len(patches)))
333 tlen = len(str(len(patches)))
333
334
334 flag = ' '.join(opts.get('flag'))
335 flag = ' '.join(opts.get('flag'))
335 if flag:
336 if flag:
336 subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag)
337 subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag)
337 else:
338 else:
338 subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches))
339 subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches))
339 subj += ' ' + (opts.get('subject') or
340 subj += ' ' + (opts.get('subject') or
340 prompt(ui, 'Subject: ', rest=subj))
341 prompt(ui, 'Subject: ', rest=subj))
341
342
342 body = ''
343 body = ''
343 if opts.get('diffstat'):
344 if opts.get('diffstat'):
344 d = cdiffstat(ui, _('Final summary:\n'), jumbo)
345 d = cdiffstat(ui, _('Final summary:\n'), jumbo)
345 if d:
346 if d:
346 body = '\n' + d
347 body = '\n' + d
347
348
348 body = getdescription(body, sender)
349 body = getdescription(body, sender)
349 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
350 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
350 msg['Subject'] = mail.headencode(ui, subj, _charsets,
351 msg['Subject'] = mail.headencode(ui, subj, _charsets,
351 opts.get('test'))
352 opts.get('test'))
352
353
353 msgs.insert(0, (msg, subj))
354 msgs.insert(0, (msg, subj))
354 return msgs
355 return msgs
355
356
356 def getbundlemsgs(bundle):
357 def getbundlemsgs(bundle):
357 subj = (opts.get('subject')
358 subj = (opts.get('subject')
358 or prompt(ui, 'Subject:', 'A bundle for your repository'))
359 or prompt(ui, 'Subject:', 'A bundle for your repository'))
359
360
360 body = getdescription('', sender)
361 body = getdescription('', sender)
361 msg = email.MIMEMultipart.MIMEMultipart()
362 msg = email.MIMEMultipart.MIMEMultipart()
362 if body:
363 if body:
363 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
364 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
364 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
365 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
365 datapart.set_payload(bundle)
366 datapart.set_payload(bundle)
366 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
367 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
367 datapart.add_header('Content-Disposition', 'attachment',
368 datapart.add_header('Content-Disposition', 'attachment',
368 filename=bundlename)
369 filename=bundlename)
369 email.Encoders.encode_base64(datapart)
370 email.Encoders.encode_base64(datapart)
370 msg.attach(datapart)
371 msg.attach(datapart)
371 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
372 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
372 return [(msg, subj)]
373 return [(msg, subj)]
373
374
374 sender = (opts.get('from') or ui.config('email', 'from') or
375 sender = (opts.get('from') or ui.config('email', 'from') or
375 ui.config('patchbomb', 'from') or
376 ui.config('patchbomb', 'from') or
376 prompt(ui, 'From', ui.username()))
377 prompt(ui, 'From', ui.username()))
377
378
378 # internal option used by pbranches
379 # internal option used by pbranches
379 patches = opts.get('patches')
380 patches = opts.get('patches')
380 if patches:
381 if patches:
381 msgs = getpatchmsgs(patches, opts.get('patchnames'))
382 msgs = getpatchmsgs(patches, opts.get('patchnames'))
382 elif opts.get('bundle'):
383 elif opts.get('bundle'):
383 msgs = getbundlemsgs(getbundle(dest))
384 msgs = getbundlemsgs(getbundle(dest))
384 else:
385 else:
385 msgs = getpatchmsgs(list(getpatches(revs)))
386 msgs = getpatchmsgs(list(getpatches(revs)))
386
387
387 def getaddrs(opt, prpt=None, default=None):
388 def getaddrs(opt, prpt=None, default=None):
388 if opts.get(opt):
389 if opts.get(opt):
389 return mail.addrlistencode(ui, opts.get(opt), _charsets,
390 return mail.addrlistencode(ui, opts.get(opt), _charsets,
390 opts.get('test'))
391 opts.get('test'))
391
392
392 addrs = (ui.config('email', opt) or
393 addrs = (ui.config('email', opt) or
393 ui.config('patchbomb', opt) or '')
394 ui.config('patchbomb', opt) or '')
394 if not addrs and prpt:
395 if not addrs and prpt:
395 addrs = prompt(ui, prpt, default)
396 addrs = prompt(ui, prpt, default)
396
397
397 return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test'))
398 return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test'))
398
399
399 to = getaddrs('to', 'To')
400 to = getaddrs('to', 'To')
400 cc = getaddrs('cc', 'Cc', '')
401 cc = getaddrs('cc', 'Cc', '')
401 bcc = getaddrs('bcc')
402 bcc = getaddrs('bcc')
402
403
403 ui.write('\n')
404 ui.write('\n')
404
405
405 parent = opts.get('in_reply_to') or None
406 parent = opts.get('in_reply_to') or None
406 # angle brackets may be omitted, they're not semantically part of the msg-id
407 # angle brackets may be omitted, they're not semantically part of the msg-id
407 if parent is not None:
408 if parent is not None:
408 if not parent.startswith('<'):
409 if not parent.startswith('<'):
409 parent = '<' + parent
410 parent = '<' + parent
410 if not parent.endswith('>'):
411 if not parent.endswith('>'):
411 parent += '>'
412 parent += '>'
412
413
413 first = True
414 first = True
414
415
415 sender_addr = email.Utils.parseaddr(sender)[1]
416 sender_addr = email.Utils.parseaddr(sender)[1]
416 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
417 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
417 sendmail = None
418 sendmail = None
418 for m, subj in msgs:
419 for m, subj in msgs:
419 try:
420 try:
420 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
421 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
421 except TypeError:
422 except TypeError:
422 m['Message-Id'] = genmsgid('patchbomb')
423 m['Message-Id'] = genmsgid('patchbomb')
423 if parent:
424 if parent:
424 m['In-Reply-To'] = parent
425 m['In-Reply-To'] = parent
425 m['References'] = parent
426 m['References'] = parent
426 if first:
427 if first:
427 parent = m['Message-Id']
428 parent = m['Message-Id']
428 first = False
429 first = False
429
430
430 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
431 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
431 m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
432 m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
432
433
433 start_time = (start_time[0] + 1, start_time[1])
434 start_time = (start_time[0] + 1, start_time[1])
434 m['From'] = sender
435 m['From'] = sender
435 m['To'] = ', '.join(to)
436 m['To'] = ', '.join(to)
436 if cc:
437 if cc:
437 m['Cc'] = ', '.join(cc)
438 m['Cc'] = ', '.join(cc)
438 if bcc:
439 if bcc:
439 m['Bcc'] = ', '.join(bcc)
440 m['Bcc'] = ', '.join(bcc)
440 if opts.get('test'):
441 if opts.get('test'):
441 ui.status(_('Displaying '), subj, ' ...\n')
442 ui.status(_('Displaying '), subj, ' ...\n')
442 ui.flush()
443 ui.flush()
443 if 'PAGER' in os.environ:
444 if 'PAGER' in os.environ:
444 fp = util.popen(os.environ['PAGER'], 'w')
445 fp = util.popen(os.environ['PAGER'], 'w')
445 else:
446 else:
446 fp = ui
447 fp = ui
447 generator = email.Generator.Generator(fp, mangle_from_=False)
448 generator = email.Generator.Generator(fp, mangle_from_=False)
448 try:
449 try:
449 generator.flatten(m, 0)
450 generator.flatten(m, 0)
450 fp.write('\n')
451 fp.write('\n')
451 except IOError, inst:
452 except IOError, inst:
452 if inst.errno != errno.EPIPE:
453 if inst.errno != errno.EPIPE:
453 raise
454 raise
454 if fp is not ui:
455 if fp is not ui:
455 fp.close()
456 fp.close()
456 elif opts.get('mbox'):
457 elif opts.get('mbox'):
457 ui.status(_('Writing '), subj, ' ...\n')
458 ui.status(_('Writing '), subj, ' ...\n')
458 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
459 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
459 generator = email.Generator.Generator(fp, mangle_from_=True)
460 generator = email.Generator.Generator(fp, mangle_from_=True)
460 # Should be time.asctime(), but Windows prints 2-characters day
461 # Should be time.asctime(), but Windows prints 2-characters day
461 # of month instead of one. Make them print the same thing.
462 # of month instead of one. Make them print the same thing.
462 date = time.strftime('%a %b %d %H:%M:%S %Y',
463 date = time.strftime('%a %b %d %H:%M:%S %Y',
463 time.localtime(start_time[0]))
464 time.localtime(start_time[0]))
464 fp.write('From %s %s\n' % (sender_addr, date))
465 fp.write('From %s %s\n' % (sender_addr, date))
465 generator.flatten(m, 0)
466 generator.flatten(m, 0)
466 fp.write('\n\n')
467 fp.write('\n\n')
467 fp.close()
468 fp.close()
468 else:
469 else:
469 if not sendmail:
470 if not sendmail:
470 sendmail = mail.connect(ui)
471 sendmail = mail.connect(ui)
471 ui.status(_('Sending '), subj, ' ...\n')
472 ui.status(_('Sending '), subj, ' ...\n')
472 # Exim does not remove the Bcc field
473 # Exim does not remove the Bcc field
473 del m['Bcc']
474 del m['Bcc']
474 fp = cStringIO.StringIO()
475 fp = cStringIO.StringIO()
475 generator = email.Generator.Generator(fp, mangle_from_=False)
476 generator = email.Generator.Generator(fp, mangle_from_=False)
476 generator.flatten(m, 0)
477 generator.flatten(m, 0)
477 sendmail(sender, to + bcc + cc, fp.getvalue())
478 sendmail(sender, to + bcc + cc, fp.getvalue())
478
479
479 emailopts = [
480 emailopts = [
480 ('a', 'attach', None, _('send patches as attachments')),
481 ('a', 'attach', None, _('send patches as attachments')),
481 ('i', 'inline', None, _('send patches as inline attachments')),
482 ('i', 'inline', None, _('send patches as inline attachments')),
482 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
483 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
483 ('c', 'cc', [], _('email addresses of copy recipients')),
484 ('c', 'cc', [], _('email addresses of copy recipients')),
484 ('d', 'diffstat', None, _('add diffstat output to messages')),
485 ('d', 'diffstat', None, _('add diffstat output to messages')),
485 ('', 'date', '', _('use the given date as the sending date')),
486 ('', 'date', '', _('use the given date as the sending date')),
486 ('', 'desc', '', _('use the given file as the series description')),
487 ('', 'desc', '', _('use the given file as the series description')),
487 ('f', 'from', '', _('email address of sender')),
488 ('f', 'from', '', _('email address of sender')),
488 ('n', 'test', None, _('print messages that would be sent')),
489 ('n', 'test', None, _('print messages that would be sent')),
489 ('m', 'mbox', '',
490 ('m', 'mbox', '',
490 _('write messages to mbox file instead of sending them')),
491 _('write messages to mbox file instead of sending them')),
491 ('s', 'subject', '',
492 ('s', 'subject', '',
492 _('subject of first message (intro or single patch)')),
493 _('subject of first message (intro or single patch)')),
493 ('', 'in-reply-to', '',
494 ('', 'in-reply-to', '',
494 _('message identifier to reply to')),
495 _('message identifier to reply to')),
495 ('', 'flag', [], _('flags to add in subject prefixes')),
496 ('', 'flag', [], _('flags to add in subject prefixes')),
496 ('t', 'to', [], _('email addresses of recipients')),
497 ('t', 'to', [], _('email addresses of recipients')),
497 ]
498 ]
498
499
499
500
500 cmdtable = {
501 cmdtable = {
501 "email":
502 "email":
502 (patchbomb,
503 (patchbomb,
503 [('g', 'git', None, _('use git extended diff format')),
504 [('g', 'git', None, _('use git extended diff format')),
504 ('', 'plain', None, _('omit hg patch header')),
505 ('', 'plain', None, _('omit hg patch header')),
505 ('o', 'outgoing', None,
506 ('o', 'outgoing', None,
506 _('send changes not found in the target repository')),
507 _('send changes not found in the target repository')),
507 ('b', 'bundle', None,
508 ('b', 'bundle', None,
508 _('send changes not in target as a binary bundle')),
509 _('send changes not in target as a binary bundle')),
509 ('', 'bundlename', 'bundle',
510 ('', 'bundlename', 'bundle',
510 _('name of the bundle attachment file')),
511 _('name of the bundle attachment file')),
511 ('r', 'rev', [], _('a revision to send')),
512 ('r', 'rev', [], _('a revision to send')),
512 ('', 'force', None,
513 ('', 'force', None,
513 _('run even when remote repository is unrelated '
514 _('run even when remote repository is unrelated '
514 '(with -b/--bundle)')),
515 '(with -b/--bundle)')),
515 ('', 'base', [],
516 ('', 'base', [],
516 _('a base changeset to specify instead of a destination '
517 _('a base changeset to specify instead of a destination '
517 '(with -b/--bundle)')),
518 '(with -b/--bundle)')),
518 ('', 'intro', None,
519 ('', 'intro', None,
519 _('send an introduction email for a single patch')),
520 _('send an introduction email for a single patch')),
520 ] + emailopts + commands.remoteopts,
521 ] + emailopts + commands.remoteopts,
521 _('hg email [OPTION]... [DEST]...'))
522 _('hg email [OPTION]... [DEST]...'))
522 }
523 }
@@ -1,3759 +1,3764 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw
13 import patch, help, mdiff, url, encoding, templatekw
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17 import minirst
17 import minirst
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see hg forget.
28 undo an add before that, see hg forget.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31 """
31 """
32
32
33 bad = []
33 bad = []
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
37 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the
52 Add all new files and remove all missing files from the
53 repository.
53 repository.
54
54
55 New files are ignored if they match any of the patterns in
55 New files are ignored if they match any of the patterns in
56 .hgignore. As with add, these changes take effect at the next
56 .hgignore. As with add, these changes take effect at the next
57 commit.
57 commit.
58
58
59 Use the -s/--similarity option to detect renamed files. With a
59 Use the -s/--similarity option to detect renamed files. With a
60 parameter greater than 0, this compares every removed file with
60 parameter greater than 0, this compares every removed file with
61 every added file and records those similar enough as renames. This
61 every added file and records those similar enough as renames. This
62 option takes a percentage between 0 (disabled) and 100 (files must
62 option takes a percentage between 0 (disabled) and 100 (files must
63 be identical) as its parameter. Detecting renamed files this way
63 be identical) as its parameter. Detecting renamed files this way
64 can be expensive.
64 can be expensive.
65 """
65 """
66 try:
66 try:
67 sim = float(opts.get('similarity') or 0)
67 sim = float(opts.get('similarity') or 0)
68 except ValueError:
68 except ValueError:
69 raise util.Abort(_('similarity must be a number'))
69 raise util.Abort(_('similarity must be a number'))
70 if sim < 0 or sim > 100:
70 if sim < 0 or sim > 100:
71 raise util.Abort(_('similarity must be between 0 and 100'))
71 raise util.Abort(_('similarity must be between 0 and 100'))
72 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
72 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
73
73
74 def annotate(ui, repo, *pats, **opts):
74 def annotate(ui, repo, *pats, **opts):
75 """show changeset information by line for each file
75 """show changeset information by line for each file
76
76
77 List changes in files, showing the revision id responsible for
77 List changes in files, showing the revision id responsible for
78 each line
78 each line
79
79
80 This command is useful for discovering when a change was made and
80 This command is useful for discovering when a change was made and
81 by whom.
81 by whom.
82
82
83 Without the -a/--text option, annotate will avoid processing files
83 Without the -a/--text option, annotate will avoid processing files
84 it detects as binary. With -a, annotate will annotate the file
84 it detects as binary. With -a, annotate will annotate the file
85 anyway, although the results will probably be neither useful
85 anyway, although the results will probably be neither useful
86 nor desirable.
86 nor desirable.
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 filename or pattern is required'))
92 raise util.Abort(_('at least one filename or pattern is 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')
101 if (not opts.get('user') and not opts.get('changeset')
102 and not opts.get('date') and not opts.get('follow')):
102 and not opts.get('date') 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 an unversioned archive of a repository revision
138 '''create an 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``: a directory full of files (default)
146 :``files``: a directory full of files (default)
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:
176 if not prefix:
177 prefix = os.path.basename(repo.root) + '-%h'
177 prefix = os.path.basename(repo.root) + '-%h'
178 prefix = cmdutil.make_filename(repo, prefix, node)
178 prefix = cmdutil.make_filename(repo, prefix, node)
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
180 matchfn, prefix)
180 matchfn, prefix)
181
181
182 def backout(ui, repo, node=None, rev=None, **opts):
182 def backout(ui, repo, node=None, rev=None, **opts):
183 '''reverse effect of earlier changeset
183 '''reverse effect of earlier changeset
184
184
185 Commit the backed out changes as a new changeset. The new
185 Commit the backed out changes as a new changeset. The new
186 changeset is a child of the backed out changeset.
186 changeset is a child of the backed out changeset.
187
187
188 If you backout a changeset other than the tip, a new head is
188 If you backout a changeset other than the tip, a new head is
189 created. This head will be the new tip and you should merge this
189 created. This head will be the new tip and you should merge this
190 backout changeset with another head.
190 backout changeset with another head.
191
191
192 The --merge option remembers the parent of the working directory
192 The --merge option remembers the parent of the working directory
193 before starting the backout, then merges the new head with that
193 before starting the backout, then merges the new head with that
194 changeset afterwards. This saves you from doing the merge by hand.
194 changeset afterwards. This saves you from doing the merge by hand.
195 The result of this merge is not committed, as with a normal merge.
195 The result of this merge is not committed, as with a normal merge.
196
196
197 See 'hg help dates' for a list of formats valid for -d/--date.
197 See 'hg help dates' for a list of formats valid for -d/--date.
198 '''
198 '''
199 if rev and node:
199 if rev and node:
200 raise util.Abort(_("please specify just one revision"))
200 raise util.Abort(_("please specify just one revision"))
201
201
202 if not rev:
202 if not rev:
203 rev = node
203 rev = node
204
204
205 if not rev:
205 if not rev:
206 raise util.Abort(_("please specify a revision to backout"))
206 raise util.Abort(_("please specify a revision to backout"))
207
207
208 date = opts.get('date')
208 date = opts.get('date')
209 if date:
209 if date:
210 opts['date'] = util.parsedate(date)
210 opts['date'] = util.parsedate(date)
211
211
212 cmdutil.bail_if_changed(repo)
212 cmdutil.bail_if_changed(repo)
213 node = repo.lookup(rev)
213 node = repo.lookup(rev)
214
214
215 op1, op2 = repo.dirstate.parents()
215 op1, op2 = repo.dirstate.parents()
216 a = repo.changelog.ancestor(op1, node)
216 a = repo.changelog.ancestor(op1, node)
217 if a != node:
217 if a != node:
218 raise util.Abort(_('cannot backout change on a different branch'))
218 raise util.Abort(_('cannot backout change on a different branch'))
219
219
220 p1, p2 = repo.changelog.parents(node)
220 p1, p2 = repo.changelog.parents(node)
221 if p1 == nullid:
221 if p1 == nullid:
222 raise util.Abort(_('cannot backout a change with no parents'))
222 raise util.Abort(_('cannot backout a change with no parents'))
223 if p2 != nullid:
223 if p2 != nullid:
224 if not opts.get('parent'):
224 if not opts.get('parent'):
225 raise util.Abort(_('cannot backout a merge changeset without '
225 raise util.Abort(_('cannot backout a merge changeset without '
226 '--parent'))
226 '--parent'))
227 p = repo.lookup(opts['parent'])
227 p = repo.lookup(opts['parent'])
228 if p not in (p1, p2):
228 if p not in (p1, p2):
229 raise util.Abort(_('%s is not a parent of %s') %
229 raise util.Abort(_('%s is not a parent of %s') %
230 (short(p), short(node)))
230 (short(p), short(node)))
231 parent = p
231 parent = p
232 else:
232 else:
233 if opts.get('parent'):
233 if opts.get('parent'):
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
235 parent = p1
235 parent = p1
236
236
237 # the backout should appear on the same branch
237 # the backout should appear on the same branch
238 branch = repo.dirstate.branch()
238 branch = repo.dirstate.branch()
239 hg.clean(repo, node, show_stats=False)
239 hg.clean(repo, node, show_stats=False)
240 repo.dirstate.setbranch(branch)
240 repo.dirstate.setbranch(branch)
241 revert_opts = opts.copy()
241 revert_opts = opts.copy()
242 revert_opts['date'] = None
242 revert_opts['date'] = None
243 revert_opts['all'] = True
243 revert_opts['all'] = True
244 revert_opts['rev'] = hex(parent)
244 revert_opts['rev'] = hex(parent)
245 revert_opts['no_backup'] = None
245 revert_opts['no_backup'] = None
246 revert(ui, repo, **revert_opts)
246 revert(ui, repo, **revert_opts)
247 commit_opts = opts.copy()
247 commit_opts = opts.copy()
248 commit_opts['addremove'] = False
248 commit_opts['addremove'] = False
249 if not commit_opts['message'] and not commit_opts['logfile']:
249 if not commit_opts['message'] and not commit_opts['logfile']:
250 # we don't translate commit messages
250 # we don't translate commit messages
251 commit_opts['message'] = "Backed out changeset %s" % short(node)
251 commit_opts['message'] = "Backed out changeset %s" % short(node)
252 commit_opts['force_editor'] = True
252 commit_opts['force_editor'] = True
253 commit(ui, repo, **commit_opts)
253 commit(ui, repo, **commit_opts)
254 def nice(node):
254 def nice(node):
255 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 return '%d:%s' % (repo.changelog.rev(node), short(node))
256 ui.status(_('changeset %s backs out changeset %s\n') %
256 ui.status(_('changeset %s backs out changeset %s\n') %
257 (nice(repo.changelog.tip()), nice(node)))
257 (nice(repo.changelog.tip()), nice(node)))
258 if op1 != node:
258 if op1 != node:
259 hg.clean(repo, op1, show_stats=False)
259 hg.clean(repo, op1, show_stats=False)
260 if opts.get('merge'):
260 if opts.get('merge'):
261 ui.status(_('merging with changeset %s\n')
261 ui.status(_('merging with changeset %s\n')
262 % nice(repo.changelog.tip()))
262 % nice(repo.changelog.tip()))
263 hg.merge(repo, hex(repo.changelog.tip()))
263 hg.merge(repo, hex(repo.changelog.tip()))
264 else:
264 else:
265 ui.status(_('the backout changeset is a new head - '
265 ui.status(_('the backout changeset is a new head - '
266 'do not forget to merge\n'))
266 'do not forget to merge\n'))
267 ui.status(_('(use "backout --merge" '
267 ui.status(_('(use "backout --merge" '
268 'if you want to auto-merge)\n'))
268 'if you want to auto-merge)\n'))
269
269
270 def bisect(ui, repo, rev=None, extra=None, command=None,
270 def bisect(ui, repo, rev=None, extra=None, command=None,
271 reset=None, good=None, bad=None, skip=None, noupdate=None):
271 reset=None, good=None, bad=None, skip=None, noupdate=None):
272 """subdivision search of changesets
272 """subdivision search of changesets
273
273
274 This command helps to find changesets which introduce problems. To
274 This command helps to find changesets which introduce problems. To
275 use, mark the earliest changeset you know exhibits the problem as
275 use, mark the earliest changeset you know exhibits the problem as
276 bad, then mark the latest changeset which is free from the problem
276 bad, then mark the latest changeset which is free from the problem
277 as good. Bisect will update your working directory to a revision
277 as good. Bisect will update your working directory to a revision
278 for testing (unless the -U/--noupdate option is specified). Once
278 for testing (unless the -U/--noupdate option is specified). Once
279 you have performed tests, mark the working directory as good or
279 you have performed tests, mark the working directory as good or
280 bad, and bisect will either update to another candidate changeset
280 bad, and bisect will either update to another candidate changeset
281 or announce that it has found the bad revision.
281 or announce that it has found the bad revision.
282
282
283 As a shortcut, you can also use the revision argument to mark a
283 As a shortcut, you can also use the revision argument to mark a
284 revision as good or bad without checking it out first.
284 revision as good or bad without checking it out first.
285
285
286 If you supply a command, it will be used for automatic bisection.
286 If you supply a command, it will be used for automatic bisection.
287 Its exit status will be used to mark revisions as good or bad:
287 Its exit status will be used to mark revisions as good or bad:
288 status 0 means good, 125 means to skip the revision, 127
288 status 0 means good, 125 means to skip the revision, 127
289 (command not found) will abort the bisection, and any other
289 (command not found) will abort the bisection, and any other
290 non-zero exit status means the revision is bad.
290 non-zero exit status means the revision is bad.
291 """
291 """
292 def print_result(nodes, good):
292 def print_result(nodes, good):
293 displayer = cmdutil.show_changeset(ui, repo, {})
293 displayer = cmdutil.show_changeset(ui, repo, {})
294 if len(nodes) == 1:
294 if len(nodes) == 1:
295 # narrowed it down to a single revision
295 # narrowed it down to a single revision
296 if good:
296 if good:
297 ui.write(_("The first good revision is:\n"))
297 ui.write(_("The first good revision is:\n"))
298 else:
298 else:
299 ui.write(_("The first bad revision is:\n"))
299 ui.write(_("The first bad revision is:\n"))
300 displayer.show(repo[nodes[0]])
300 displayer.show(repo[nodes[0]])
301 else:
301 else:
302 # multiple possible revisions
302 # multiple possible revisions
303 if good:
303 if good:
304 ui.write(_("Due to skipped revisions, the first "
304 ui.write(_("Due to skipped revisions, the first "
305 "good revision could be any of:\n"))
305 "good revision could be any of:\n"))
306 else:
306 else:
307 ui.write(_("Due to skipped revisions, the first "
307 ui.write(_("Due to skipped revisions, the first "
308 "bad revision could be any of:\n"))
308 "bad revision could be any of:\n"))
309 for n in nodes:
309 for n in nodes:
310 displayer.show(repo[n])
310 displayer.show(repo[n])
311 displayer.close()
311 displayer.close()
312
312
313 def check_state(state, interactive=True):
313 def check_state(state, interactive=True):
314 if not state['good'] or not state['bad']:
314 if not state['good'] or not state['bad']:
315 if (good or bad or skip or reset) and interactive:
315 if (good or bad or skip or reset) and interactive:
316 return
316 return
317 if not state['good']:
317 if not state['good']:
318 raise util.Abort(_('cannot bisect (no known good revisions)'))
318 raise util.Abort(_('cannot bisect (no known good revisions)'))
319 else:
319 else:
320 raise util.Abort(_('cannot bisect (no known bad revisions)'))
320 raise util.Abort(_('cannot bisect (no known bad revisions)'))
321 return True
321 return True
322
322
323 # backward compatibility
323 # backward compatibility
324 if rev in "good bad reset init".split():
324 if rev in "good bad reset init".split():
325 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
325 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
326 cmd, rev, extra = rev, extra, None
326 cmd, rev, extra = rev, extra, None
327 if cmd == "good":
327 if cmd == "good":
328 good = True
328 good = True
329 elif cmd == "bad":
329 elif cmd == "bad":
330 bad = True
330 bad = True
331 else:
331 else:
332 reset = True
332 reset = True
333 elif extra or good + bad + skip + reset + bool(command) > 1:
333 elif extra or good + bad + skip + reset + bool(command) > 1:
334 raise util.Abort(_('incompatible arguments'))
334 raise util.Abort(_('incompatible arguments'))
335
335
336 if reset:
336 if reset:
337 p = repo.join("bisect.state")
337 p = repo.join("bisect.state")
338 if os.path.exists(p):
338 if os.path.exists(p):
339 os.unlink(p)
339 os.unlink(p)
340 return
340 return
341
341
342 state = hbisect.load_state(repo)
342 state = hbisect.load_state(repo)
343
343
344 if command:
344 if command:
345 changesets = 1
345 changesets = 1
346 try:
346 try:
347 while changesets:
347 while changesets:
348 # update state
348 # update state
349 status = util.system(command)
349 status = util.system(command)
350 if status == 125:
350 if status == 125:
351 transition = "skip"
351 transition = "skip"
352 elif status == 0:
352 elif status == 0:
353 transition = "good"
353 transition = "good"
354 # status < 0 means process was killed
354 # status < 0 means process was killed
355 elif status == 127:
355 elif status == 127:
356 raise util.Abort(_("failed to execute %s") % command)
356 raise util.Abort(_("failed to execute %s") % command)
357 elif status < 0:
357 elif status < 0:
358 raise util.Abort(_("%s killed") % command)
358 raise util.Abort(_("%s killed") % command)
359 else:
359 else:
360 transition = "bad"
360 transition = "bad"
361 ctx = repo[rev or '.']
361 ctx = repo[rev or '.']
362 state[transition].append(ctx.node())
362 state[transition].append(ctx.node())
363 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
363 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
364 check_state(state, interactive=False)
364 check_state(state, interactive=False)
365 # bisect
365 # bisect
366 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
366 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
367 # update to next check
367 # update to next check
368 cmdutil.bail_if_changed(repo)
368 cmdutil.bail_if_changed(repo)
369 hg.clean(repo, nodes[0], show_stats=False)
369 hg.clean(repo, nodes[0], show_stats=False)
370 finally:
370 finally:
371 hbisect.save_state(repo, state)
371 hbisect.save_state(repo, state)
372 return print_result(nodes, good)
372 return print_result(nodes, good)
373
373
374 # update state
374 # update state
375 node = repo.lookup(rev or '.')
375 node = repo.lookup(rev or '.')
376 if good or bad or skip:
376 if good or bad or skip:
377 if good:
377 if good:
378 state['good'].append(node)
378 state['good'].append(node)
379 elif bad:
379 elif bad:
380 state['bad'].append(node)
380 state['bad'].append(node)
381 elif skip:
381 elif skip:
382 state['skip'].append(node)
382 state['skip'].append(node)
383 hbisect.save_state(repo, state)
383 hbisect.save_state(repo, state)
384
384
385 if not check_state(state):
385 if not check_state(state):
386 return
386 return
387
387
388 # actually bisect
388 # actually bisect
389 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
389 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
390 if changesets == 0:
390 if changesets == 0:
391 print_result(nodes, good)
391 print_result(nodes, good)
392 else:
392 else:
393 assert len(nodes) == 1 # only a single node can be tested next
393 assert len(nodes) == 1 # only a single node can be tested next
394 node = nodes[0]
394 node = nodes[0]
395 # compute the approximate number of remaining tests
395 # compute the approximate number of remaining tests
396 tests, size = 0, 2
396 tests, size = 0, 2
397 while size <= changesets:
397 while size <= changesets:
398 tests, size = tests + 1, size * 2
398 tests, size = tests + 1, size * 2
399 rev = repo.changelog.rev(node)
399 rev = repo.changelog.rev(node)
400 ui.write(_("Testing changeset %d:%s "
400 ui.write(_("Testing changeset %d:%s "
401 "(%d changesets remaining, ~%d tests)\n")
401 "(%d changesets remaining, ~%d tests)\n")
402 % (rev, short(node), changesets, tests))
402 % (rev, short(node), changesets, tests))
403 if not noupdate:
403 if not noupdate:
404 cmdutil.bail_if_changed(repo)
404 cmdutil.bail_if_changed(repo)
405 return hg.clean(repo, node)
405 return hg.clean(repo, node)
406
406
407 def branch(ui, repo, label=None, **opts):
407 def branch(ui, repo, label=None, **opts):
408 """set or show the current branch name
408 """set or show the current branch name
409
409
410 With no argument, show the current branch name. With one argument,
410 With no argument, show the current branch name. With one argument,
411 set the working directory branch name (the branch will not exist
411 set the working directory branch name (the branch will not exist
412 in the repository until the next commit). Standard practice
412 in the repository until the next commit). Standard practice
413 recommends that primary development take place on the 'default'
413 recommends that primary development take place on the 'default'
414 branch.
414 branch.
415
415
416 Unless -f/--force is specified, branch will not let you set a
416 Unless -f/--force is specified, branch will not let you set a
417 branch name that already exists, even if it's inactive.
417 branch name that already exists, even if it's inactive.
418
418
419 Use -C/--clean to reset the working directory branch to that of
419 Use -C/--clean to reset the working directory branch to that of
420 the parent of the working directory, negating a previous branch
420 the parent of the working directory, negating a previous branch
421 change.
421 change.
422
422
423 Use the command 'hg update' to switch to an existing branch. Use
423 Use the command 'hg update' to switch to an existing branch. Use
424 'hg commit --close-branch' to mark this branch as closed.
424 'hg commit --close-branch' to mark this branch as closed.
425 """
425 """
426
426
427 if opts.get('clean'):
427 if opts.get('clean'):
428 label = repo[None].parents()[0].branch()
428 label = repo[None].parents()[0].branch()
429 repo.dirstate.setbranch(label)
429 repo.dirstate.setbranch(label)
430 ui.status(_('reset working directory to branch %s\n') % label)
430 ui.status(_('reset working directory to branch %s\n') % label)
431 elif label:
431 elif label:
432 utflabel = encoding.fromlocal(label)
432 utflabel = encoding.fromlocal(label)
433 if not opts.get('force') and utflabel in repo.branchtags():
433 if not opts.get('force') and utflabel in repo.branchtags():
434 if label not in [p.branch() for p in repo.parents()]:
434 if label not in [p.branch() for p in repo.parents()]:
435 raise util.Abort(_('a branch of the same name already exists'
435 raise util.Abort(_('a branch of the same name already exists'
436 ' (use --force to override)'))
436 ' (use --force to override)'))
437 repo.dirstate.setbranch(utflabel)
437 repo.dirstate.setbranch(utflabel)
438 ui.status(_('marked working directory as branch %s\n') % label)
438 ui.status(_('marked working directory as branch %s\n') % label)
439 else:
439 else:
440 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
440 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
441
441
442 def branches(ui, repo, active=False, closed=False):
442 def branches(ui, repo, active=False, closed=False):
443 """list repository named branches
443 """list repository named branches
444
444
445 List the repository's named branches, indicating which ones are
445 List the repository's named branches, indicating which ones are
446 inactive. If -c/--closed is specified, also list branches which have
446 inactive. If -c/--closed is specified, also list branches which have
447 been marked closed (see hg commit --close-branch).
447 been marked closed (see hg commit --close-branch).
448
448
449 If -a/--active is specified, only show active branches. A branch
449 If -a/--active is specified, only show active branches. A branch
450 is considered active if it contains repository heads.
450 is considered active if it contains repository heads.
451
451
452 Use the command 'hg update' to switch to an existing branch.
452 Use the command 'hg update' to switch to an existing branch.
453 """
453 """
454
454
455 hexfunc = ui.debugflag and hex or short
455 hexfunc = ui.debugflag and hex or short
456 activebranches = [repo[n].branch() for n in repo.heads()]
456 activebranches = [repo[n].branch() for n in repo.heads()]
457 def testactive(tag, node):
457 def testactive(tag, node):
458 realhead = tag in activebranches
458 realhead = tag in activebranches
459 open = node in repo.branchheads(tag, closed=False)
459 open = node in repo.branchheads(tag, closed=False)
460 return realhead and open
460 return realhead and open
461 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
461 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
462 for tag, node in repo.branchtags().items()],
462 for tag, node in repo.branchtags().items()],
463 reverse=True)
463 reverse=True)
464
464
465 for isactive, node, tag in branches:
465 for isactive, node, tag in branches:
466 if (not active) or isactive:
466 if (not active) or isactive:
467 encodedtag = encoding.tolocal(tag)
467 encodedtag = encoding.tolocal(tag)
468 if ui.quiet:
468 if ui.quiet:
469 ui.write("%s\n" % encodedtag)
469 ui.write("%s\n" % encodedtag)
470 else:
470 else:
471 hn = repo.lookup(node)
471 hn = repo.lookup(node)
472 if isactive:
472 if isactive:
473 notice = ''
473 notice = ''
474 elif hn not in repo.branchheads(tag, closed=False):
474 elif hn not in repo.branchheads(tag, closed=False):
475 if not closed:
475 if not closed:
476 continue
476 continue
477 notice = _(' (closed)')
477 notice = _(' (closed)')
478 else:
478 else:
479 notice = _(' (inactive)')
479 notice = _(' (inactive)')
480 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
480 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
481 data = encodedtag, rev, hexfunc(hn), notice
481 data = encodedtag, rev, hexfunc(hn), notice
482 ui.write("%s %s:%s%s\n" % data)
482 ui.write("%s %s:%s%s\n" % data)
483
483
484 def bundle(ui, repo, fname, dest=None, **opts):
484 def bundle(ui, repo, fname, dest=None, **opts):
485 """create a changegroup file
485 """create a changegroup file
486
486
487 Generate a compressed changegroup file collecting changesets not
487 Generate a compressed changegroup file collecting changesets not
488 known to be in another repository.
488 known to be in another repository.
489
489
490 If no destination repository is specified the destination is
490 If no destination repository is specified the destination is
491 assumed to have all the nodes specified by one or more --base
491 assumed to have all the nodes specified by one or more --base
492 parameters. To create a bundle containing all changesets, use
492 parameters. To create a bundle containing all changesets, use
493 -a/--all (or --base null).
493 -a/--all (or --base null).
494
494
495 You can change compression method with the -t/--type option.
495 You can change compression method with the -t/--type option.
496 The available compression methods are: none, bzip2, and
496 The available compression methods are: none, bzip2, and
497 gzip (by default, bundles are compressed using bzip2).
497 gzip (by default, bundles are compressed using bzip2).
498
498
499 The bundle file can then be transferred using conventional means
499 The bundle file can then be transferred using conventional means
500 and applied to another repository with the unbundle or pull
500 and applied to another repository with the unbundle or pull
501 command. This is useful when direct push and pull are not
501 command. This is useful when direct push and pull are not
502 available or when exporting an entire repository is undesirable.
502 available or when exporting an entire repository is undesirable.
503
503
504 Applying bundles preserves all changeset contents including
504 Applying bundles preserves all changeset contents including
505 permissions, copy/rename information, and revision history.
505 permissions, copy/rename information, and revision history.
506 """
506 """
507 revs = opts.get('rev') or None
507 revs = opts.get('rev') or None
508 if revs:
508 if revs:
509 revs = [repo.lookup(rev) for rev in revs]
509 revs = [repo.lookup(rev) for rev in revs]
510 if opts.get('all'):
510 if opts.get('all'):
511 base = ['null']
511 base = ['null']
512 else:
512 else:
513 base = opts.get('base')
513 base = opts.get('base')
514 if base:
514 if base:
515 if dest:
515 if dest:
516 raise util.Abort(_("--base is incompatible with specifying "
516 raise util.Abort(_("--base is incompatible with specifying "
517 "a destination"))
517 "a destination"))
518 base = [repo.lookup(rev) for rev in base]
518 base = [repo.lookup(rev) for rev in base]
519 # create the right base
519 # create the right base
520 # XXX: nodesbetween / changegroup* should be "fixed" instead
520 # XXX: nodesbetween / changegroup* should be "fixed" instead
521 o = []
521 o = []
522 has = set((nullid,))
522 has = set((nullid,))
523 for n in base:
523 for n in base:
524 has.update(repo.changelog.reachable(n))
524 has.update(repo.changelog.reachable(n))
525 if revs:
525 if revs:
526 visit = list(revs)
526 visit = list(revs)
527 else:
527 else:
528 visit = repo.changelog.heads()
528 visit = repo.changelog.heads()
529 seen = {}
529 seen = {}
530 while visit:
530 while visit:
531 n = visit.pop(0)
531 n = visit.pop(0)
532 parents = [p for p in repo.changelog.parents(n) if p not in has]
532 parents = [p for p in repo.changelog.parents(n) if p not in has]
533 if len(parents) == 0:
533 if len(parents) == 0:
534 o.insert(0, n)
534 o.insert(0, n)
535 else:
535 else:
536 for p in parents:
536 for p in parents:
537 if p not in seen:
537 if p not in seen:
538 seen[p] = 1
538 seen[p] = 1
539 visit.append(p)
539 visit.append(p)
540 else:
540 else:
541 dest, revs, checkout = hg.parseurl(
541 dest = ui.expandpath(dest or 'default-push', dest or 'default')
542 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
542 dest, branches = hg.parseurl(dest)
543 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
543 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
544 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
544 o = repo.findoutgoing(other, force=opts.get('force'))
545 o = repo.findoutgoing(other, force=opts.get('force'))
545
546
546 if revs:
547 if revs:
547 cg = repo.changegroupsubset(o, revs, 'bundle')
548 cg = repo.changegroupsubset(o, revs, 'bundle')
548 else:
549 else:
549 cg = repo.changegroup(o, 'bundle')
550 cg = repo.changegroup(o, 'bundle')
550
551
551 bundletype = opts.get('type', 'bzip2').lower()
552 bundletype = opts.get('type', 'bzip2').lower()
552 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
553 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
553 bundletype = btypes.get(bundletype)
554 bundletype = btypes.get(bundletype)
554 if bundletype not in changegroup.bundletypes:
555 if bundletype not in changegroup.bundletypes:
555 raise util.Abort(_('unknown bundle type specified with --type'))
556 raise util.Abort(_('unknown bundle type specified with --type'))
556
557
557 changegroup.writebundle(cg, fname, bundletype)
558 changegroup.writebundle(cg, fname, bundletype)
558
559
559 def cat(ui, repo, file1, *pats, **opts):
560 def cat(ui, repo, file1, *pats, **opts):
560 """output the current or given revision of files
561 """output the current or given revision of files
561
562
562 Print the specified files as they were at the given revision. If
563 Print the specified files as they were at the given revision. If
563 no revision is given, the parent of the working directory is used,
564 no revision is given, the parent of the working directory is used,
564 or tip if no revision is checked out.
565 or tip if no revision is checked out.
565
566
566 Output may be to a file, in which case the name of the file is
567 Output may be to a file, in which case the name of the file is
567 given using a format string. The formatting rules are the same as
568 given using a format string. The formatting rules are the same as
568 for the export command, with the following additions:
569 for the export command, with the following additions:
569
570
570 :``%s``: basename of file being printed
571 :``%s``: basename of file being printed
571 :``%d``: dirname of file being printed, or '.' if in repository root
572 :``%d``: dirname of file being printed, or '.' if in repository root
572 :``%p``: root-relative path name of file being printed
573 :``%p``: root-relative path name of file being printed
573 """
574 """
574 ctx = repo[opts.get('rev')]
575 ctx = repo[opts.get('rev')]
575 err = 1
576 err = 1
576 m = cmdutil.match(repo, (file1,) + pats, opts)
577 m = cmdutil.match(repo, (file1,) + pats, opts)
577 for abs in ctx.walk(m):
578 for abs in ctx.walk(m):
578 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
579 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
579 data = ctx[abs].data()
580 data = ctx[abs].data()
580 if opts.get('decode'):
581 if opts.get('decode'):
581 data = repo.wwritedata(abs, data)
582 data = repo.wwritedata(abs, data)
582 fp.write(data)
583 fp.write(data)
583 err = 0
584 err = 0
584 return err
585 return err
585
586
586 def clone(ui, source, dest=None, **opts):
587 def clone(ui, source, dest=None, **opts):
587 """make a copy of an existing repository
588 """make a copy of an existing repository
588
589
589 Create a copy of an existing repository in a new directory.
590 Create a copy of an existing repository in a new directory.
590
591
591 If no destination directory name is specified, it defaults to the
592 If no destination directory name is specified, it defaults to the
592 basename of the source.
593 basename of the source.
593
594
594 The location of the source is added to the new repository's
595 The location of the source is added to the new repository's
595 .hg/hgrc file, as the default to be used for future pulls.
596 .hg/hgrc file, as the default to be used for future pulls.
596
597
597 See 'hg help urls' for valid source format details.
598 See 'hg help urls' for valid source format details.
598
599
599 It is possible to specify an ``ssh://`` URL as the destination, but no
600 It is possible to specify an ``ssh://`` URL as the destination, but no
600 .hg/hgrc and working directory will be created on the remote side.
601 .hg/hgrc and working directory will be created on the remote side.
601 Please see 'hg help urls' for important details about ``ssh://`` URLs.
602 Please see 'hg help urls' for important details about ``ssh://`` URLs.
602
603
603 If the -U/--noupdate option is specified, the new clone will contain
604 If the -U/--noupdate option is specified, the new clone will contain
604 only a repository (.hg) and no working copy (the working copy parent
605 only a repository (.hg) and no working copy (the working copy parent
605 will be the null changeset). Otherwise, clone will initially check
606 will be the null changeset). Otherwise, clone will initially check
606 out (in order of precedence):
607 out (in order of precedence):
607
608
608 a) the changeset, tag or branch specified with -u/--updaterev
609 a) the changeset, tag or branch specified with -u/--updaterev
609 b) the changeset, tag or branch given with the first -r/--rev
610 b) the changeset, tag or branch given with the first -r/--rev
610 c) the head of the default branch
611 c) the branch given with the url#branch source syntax
612 d) the head of the default branch
611
613
612 Use 'hg clone -u . src dst' to checkout the source repository's
614 Use 'hg clone -u . src dst' to checkout the source repository's
613 parent changeset (applicable for local source repositories only).
615 parent changeset (applicable for local source repositories only).
614
616
615 A set of changesets (tags, or branch names) to pull may be specified
617 A set of changesets (tags, or branch names) to pull may be specified
616 by listing each changeset (tag, or branch name) with -r/--rev.
618 by listing each changeset (tag, or branch name) with -r/--rev.
617 If -r/--rev is used, the cloned repository will contain only a subset
619 If -r/--rev is used, the cloned repository will contain only a subset
618 of the changesets of the source repository. Only the set of changesets
620 of the changesets of the source repository. Only the set of changesets
619 defined by all -r/--rev options (including all their ancestors)
621 defined by all -r/--rev options (including all their ancestors)
620 will be pulled into the destination repository.
622 will be pulled into the destination repository.
621 No subsequent changesets (including subsequent tags) will be present
623 No subsequent changesets (including subsequent tags) will be present
622 in the destination.
624 in the destination.
623
625
624 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
626 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
625 local source repositories.
627 local source repositories.
626
628
627 For efficiency, hardlinks are used for cloning whenever the source
629 For efficiency, hardlinks are used for cloning whenever the source
628 and destination are on the same filesystem (note this applies only
630 and destination are on the same filesystem (note this applies only
629 to the repository data, not to the checked out files). Some
631 to the repository data, not to the checked out files). Some
630 filesystems, such as AFS, implement hardlinking incorrectly, but
632 filesystems, such as AFS, implement hardlinking incorrectly, but
631 do not report errors. In these cases, use the --pull option to
633 do not report errors. In these cases, use the --pull option to
632 avoid hardlinking.
634 avoid hardlinking.
633
635
634 In some cases, you can clone repositories and checked out files
636 In some cases, you can clone repositories and checked out files
635 using full hardlinks with ::
637 using full hardlinks with ::
636
638
637 $ cp -al REPO REPOCLONE
639 $ cp -al REPO REPOCLONE
638
640
639 This is the fastest way to clone, but it is not always safe. The
641 This is the fastest way to clone, but it is not always safe. The
640 operation is not atomic (making sure REPO is not modified during
642 operation is not atomic (making sure REPO is not modified during
641 the operation is up to you) and you have to make sure your editor
643 the operation is up to you) and you have to make sure your editor
642 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
644 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
643 this is not compatible with certain extensions that place their
645 this is not compatible with certain extensions that place their
644 metadata under the .hg directory, such as mq.
646 metadata under the .hg directory, such as mq.
645 """
647 """
646 if opts.get('noupdate') and opts.get('updaterev'):
648 if opts.get('noupdate') and opts.get('updaterev'):
647 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
649 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
648
650
649 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
651 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
650 pull=opts.get('pull'),
652 pull=opts.get('pull'),
651 stream=opts.get('uncompressed'),
653 stream=opts.get('uncompressed'),
652 rev=opts.get('rev'),
654 rev=opts.get('rev'),
653 update=opts.get('updaterev') or not opts.get('noupdate'))
655 update=opts.get('updaterev') or not opts.get('noupdate'))
654
656
655 def commit(ui, repo, *pats, **opts):
657 def commit(ui, repo, *pats, **opts):
656 """commit the specified files or all outstanding changes
658 """commit the specified files or all outstanding changes
657
659
658 Commit changes to the given files into the repository. Unlike a
660 Commit changes to the given files into the repository. Unlike a
659 centralized RCS, this operation is a local operation. See hg push
661 centralized RCS, this operation is a local operation. See hg push
660 for a way to actively distribute your changes.
662 for a way to actively distribute your changes.
661
663
662 If a list of files is omitted, all changes reported by "hg status"
664 If a list of files is omitted, all changes reported by "hg status"
663 will be committed.
665 will be committed.
664
666
665 If you are committing the result of a merge, do not provide any
667 If you are committing the result of a merge, do not provide any
666 filenames or -I/-X filters.
668 filenames or -I/-X filters.
667
669
668 If no commit message is specified, the configured editor is
670 If no commit message is specified, the configured editor is
669 started to prompt you for a message.
671 started to prompt you for a message.
670
672
671 See 'hg help dates' for a list of formats valid for -d/--date.
673 See 'hg help dates' for a list of formats valid for -d/--date.
672 """
674 """
673 extra = {}
675 extra = {}
674 if opts.get('close_branch'):
676 if opts.get('close_branch'):
675 extra['close'] = 1
677 extra['close'] = 1
676 e = cmdutil.commiteditor
678 e = cmdutil.commiteditor
677 if opts.get('force_editor'):
679 if opts.get('force_editor'):
678 e = cmdutil.commitforceeditor
680 e = cmdutil.commitforceeditor
679
681
680 def commitfunc(ui, repo, message, match, opts):
682 def commitfunc(ui, repo, message, match, opts):
681 return repo.commit(message, opts.get('user'), opts.get('date'), match,
683 return repo.commit(message, opts.get('user'), opts.get('date'), match,
682 editor=e, extra=extra)
684 editor=e, extra=extra)
683
685
684 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
686 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
685 if not node:
687 if not node:
686 ui.status(_("nothing changed\n"))
688 ui.status(_("nothing changed\n"))
687 return
689 return
688 cl = repo.changelog
690 cl = repo.changelog
689 rev = cl.rev(node)
691 rev = cl.rev(node)
690 parents = cl.parentrevs(rev)
692 parents = cl.parentrevs(rev)
691 if rev - 1 in parents:
693 if rev - 1 in parents:
692 # one of the parents was the old tip
694 # one of the parents was the old tip
693 pass
695 pass
694 elif (parents == (nullrev, nullrev) or
696 elif (parents == (nullrev, nullrev) or
695 len(cl.heads(cl.node(parents[0]))) > 1 and
697 len(cl.heads(cl.node(parents[0]))) > 1 and
696 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
698 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
697 ui.status(_('created new head\n'))
699 ui.status(_('created new head\n'))
698
700
699 if ui.debugflag:
701 if ui.debugflag:
700 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
702 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
701 elif ui.verbose:
703 elif ui.verbose:
702 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
704 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
703
705
704 def copy(ui, repo, *pats, **opts):
706 def copy(ui, repo, *pats, **opts):
705 """mark files as copied for the next commit
707 """mark files as copied for the next commit
706
708
707 Mark dest as having copies of source files. If dest is a
709 Mark dest as having copies of source files. If dest is a
708 directory, copies are put in that directory. If dest is a file,
710 directory, copies are put in that directory. If dest is a file,
709 the source must be a single file.
711 the source must be a single file.
710
712
711 By default, this command copies the contents of files as they
713 By default, this command copies the contents of files as they
712 exist in the working directory. If invoked with -A/--after, the
714 exist in the working directory. If invoked with -A/--after, the
713 operation is recorded, but no copying is performed.
715 operation is recorded, but no copying is performed.
714
716
715 This command takes effect with the next commit. To undo a copy
717 This command takes effect with the next commit. To undo a copy
716 before that, see hg revert.
718 before that, see hg revert.
717 """
719 """
718 wlock = repo.wlock(False)
720 wlock = repo.wlock(False)
719 try:
721 try:
720 return cmdutil.copy(ui, repo, pats, opts)
722 return cmdutil.copy(ui, repo, pats, opts)
721 finally:
723 finally:
722 wlock.release()
724 wlock.release()
723
725
724 def debugancestor(ui, repo, *args):
726 def debugancestor(ui, repo, *args):
725 """find the ancestor revision of two revisions in a given index"""
727 """find the ancestor revision of two revisions in a given index"""
726 if len(args) == 3:
728 if len(args) == 3:
727 index, rev1, rev2 = args
729 index, rev1, rev2 = args
728 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
730 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
729 lookup = r.lookup
731 lookup = r.lookup
730 elif len(args) == 2:
732 elif len(args) == 2:
731 if not repo:
733 if not repo:
732 raise util.Abort(_("There is no Mercurial repository here "
734 raise util.Abort(_("There is no Mercurial repository here "
733 "(.hg not found)"))
735 "(.hg not found)"))
734 rev1, rev2 = args
736 rev1, rev2 = args
735 r = repo.changelog
737 r = repo.changelog
736 lookup = repo.lookup
738 lookup = repo.lookup
737 else:
739 else:
738 raise util.Abort(_('either two or three arguments required'))
740 raise util.Abort(_('either two or three arguments required'))
739 a = r.ancestor(lookup(rev1), lookup(rev2))
741 a = r.ancestor(lookup(rev1), lookup(rev2))
740 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
742 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
741
743
742 def debugcommands(ui, cmd='', *args):
744 def debugcommands(ui, cmd='', *args):
743 for cmd, vals in sorted(table.iteritems()):
745 for cmd, vals in sorted(table.iteritems()):
744 cmd = cmd.split('|')[0].strip('^')
746 cmd = cmd.split('|')[0].strip('^')
745 opts = ', '.join([i[1] for i in vals[1]])
747 opts = ', '.join([i[1] for i in vals[1]])
746 ui.write('%s: %s\n' % (cmd, opts))
748 ui.write('%s: %s\n' % (cmd, opts))
747
749
748 def debugcomplete(ui, cmd='', **opts):
750 def debugcomplete(ui, cmd='', **opts):
749 """returns the completion list associated with the given command"""
751 """returns the completion list associated with the given command"""
750
752
751 if opts.get('options'):
753 if opts.get('options'):
752 options = []
754 options = []
753 otables = [globalopts]
755 otables = [globalopts]
754 if cmd:
756 if cmd:
755 aliases, entry = cmdutil.findcmd(cmd, table, False)
757 aliases, entry = cmdutil.findcmd(cmd, table, False)
756 otables.append(entry[1])
758 otables.append(entry[1])
757 for t in otables:
759 for t in otables:
758 for o in t:
760 for o in t:
759 if o[0]:
761 if o[0]:
760 options.append('-%s' % o[0])
762 options.append('-%s' % o[0])
761 options.append('--%s' % o[1])
763 options.append('--%s' % o[1])
762 ui.write("%s\n" % "\n".join(options))
764 ui.write("%s\n" % "\n".join(options))
763 return
765 return
764
766
765 cmdlist = cmdutil.findpossible(cmd, table)
767 cmdlist = cmdutil.findpossible(cmd, table)
766 if ui.verbose:
768 if ui.verbose:
767 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
769 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
768 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
770 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
769
771
770 def debugfsinfo(ui, path = "."):
772 def debugfsinfo(ui, path = "."):
771 open('.debugfsinfo', 'w').write('')
773 open('.debugfsinfo', 'w').write('')
772 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
774 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
773 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
775 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
774 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
776 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
775 and 'yes' or 'no'))
777 and 'yes' or 'no'))
776 os.unlink('.debugfsinfo')
778 os.unlink('.debugfsinfo')
777
779
778 def debugrebuildstate(ui, repo, rev="tip"):
780 def debugrebuildstate(ui, repo, rev="tip"):
779 """rebuild the dirstate as it would look like for the given revision"""
781 """rebuild the dirstate as it would look like for the given revision"""
780 ctx = repo[rev]
782 ctx = repo[rev]
781 wlock = repo.wlock()
783 wlock = repo.wlock()
782 try:
784 try:
783 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
785 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
784 finally:
786 finally:
785 wlock.release()
787 wlock.release()
786
788
787 def debugcheckstate(ui, repo):
789 def debugcheckstate(ui, repo):
788 """validate the correctness of the current dirstate"""
790 """validate the correctness of the current dirstate"""
789 parent1, parent2 = repo.dirstate.parents()
791 parent1, parent2 = repo.dirstate.parents()
790 m1 = repo[parent1].manifest()
792 m1 = repo[parent1].manifest()
791 m2 = repo[parent2].manifest()
793 m2 = repo[parent2].manifest()
792 errors = 0
794 errors = 0
793 for f in repo.dirstate:
795 for f in repo.dirstate:
794 state = repo.dirstate[f]
796 state = repo.dirstate[f]
795 if state in "nr" and f not in m1:
797 if state in "nr" and f not in m1:
796 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
798 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
797 errors += 1
799 errors += 1
798 if state in "a" and f in m1:
800 if state in "a" and f in m1:
799 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
801 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
800 errors += 1
802 errors += 1
801 if state in "m" and f not in m1 and f not in m2:
803 if state in "m" and f not in m1 and f not in m2:
802 ui.warn(_("%s in state %s, but not in either manifest\n") %
804 ui.warn(_("%s in state %s, but not in either manifest\n") %
803 (f, state))
805 (f, state))
804 errors += 1
806 errors += 1
805 for f in m1:
807 for f in m1:
806 state = repo.dirstate[f]
808 state = repo.dirstate[f]
807 if state not in "nrm":
809 if state not in "nrm":
808 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
810 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
809 errors += 1
811 errors += 1
810 if errors:
812 if errors:
811 error = _(".hg/dirstate inconsistent with current parent's manifest")
813 error = _(".hg/dirstate inconsistent with current parent's manifest")
812 raise util.Abort(error)
814 raise util.Abort(error)
813
815
814 def showconfig(ui, repo, *values, **opts):
816 def showconfig(ui, repo, *values, **opts):
815 """show combined config settings from all hgrc files
817 """show combined config settings from all hgrc files
816
818
817 With no arguments, print names and values of all config items.
819 With no arguments, print names and values of all config items.
818
820
819 With one argument of the form section.name, print just the value
821 With one argument of the form section.name, print just the value
820 of that config item.
822 of that config item.
821
823
822 With multiple arguments, print names and values of all config
824 With multiple arguments, print names and values of all config
823 items with matching section names.
825 items with matching section names.
824
826
825 With --debug, the source (filename and line number) is printed
827 With --debug, the source (filename and line number) is printed
826 for each config item.
828 for each config item.
827 """
829 """
828
830
829 untrusted = bool(opts.get('untrusted'))
831 untrusted = bool(opts.get('untrusted'))
830 if values:
832 if values:
831 if len([v for v in values if '.' in v]) > 1:
833 if len([v for v in values if '.' in v]) > 1:
832 raise util.Abort(_('only one config item permitted'))
834 raise util.Abort(_('only one config item permitted'))
833 for section, name, value in ui.walkconfig(untrusted=untrusted):
835 for section, name, value in ui.walkconfig(untrusted=untrusted):
834 sectname = section + '.' + name
836 sectname = section + '.' + name
835 if values:
837 if values:
836 for v in values:
838 for v in values:
837 if v == section:
839 if v == section:
838 ui.debug('%s: ' %
840 ui.debug('%s: ' %
839 ui.configsource(section, name, untrusted))
841 ui.configsource(section, name, untrusted))
840 ui.write('%s=%s\n' % (sectname, value))
842 ui.write('%s=%s\n' % (sectname, value))
841 elif v == sectname:
843 elif v == sectname:
842 ui.debug('%s: ' %
844 ui.debug('%s: ' %
843 ui.configsource(section, name, untrusted))
845 ui.configsource(section, name, untrusted))
844 ui.write(value, '\n')
846 ui.write(value, '\n')
845 else:
847 else:
846 ui.debug('%s: ' %
848 ui.debug('%s: ' %
847 ui.configsource(section, name, untrusted))
849 ui.configsource(section, name, untrusted))
848 ui.write('%s=%s\n' % (sectname, value))
850 ui.write('%s=%s\n' % (sectname, value))
849
851
850 def debugsetparents(ui, repo, rev1, rev2=None):
852 def debugsetparents(ui, repo, rev1, rev2=None):
851 """manually set the parents of the current working directory
853 """manually set the parents of the current working directory
852
854
853 This is useful for writing repository conversion tools, but should
855 This is useful for writing repository conversion tools, but should
854 be used with care.
856 be used with care.
855 """
857 """
856
858
857 if not rev2:
859 if not rev2:
858 rev2 = hex(nullid)
860 rev2 = hex(nullid)
859
861
860 wlock = repo.wlock()
862 wlock = repo.wlock()
861 try:
863 try:
862 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
864 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
863 finally:
865 finally:
864 wlock.release()
866 wlock.release()
865
867
866 def debugstate(ui, repo, nodates=None):
868 def debugstate(ui, repo, nodates=None):
867 """show the contents of the current dirstate"""
869 """show the contents of the current dirstate"""
868 timestr = ""
870 timestr = ""
869 showdate = not nodates
871 showdate = not nodates
870 for file_, ent in sorted(repo.dirstate._map.iteritems()):
872 for file_, ent in sorted(repo.dirstate._map.iteritems()):
871 if showdate:
873 if showdate:
872 if ent[3] == -1:
874 if ent[3] == -1:
873 # Pad or slice to locale representation
875 # Pad or slice to locale representation
874 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
876 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
875 time.localtime(0)))
877 time.localtime(0)))
876 timestr = 'unset'
878 timestr = 'unset'
877 timestr = (timestr[:locale_len] +
879 timestr = (timestr[:locale_len] +
878 ' ' * (locale_len - len(timestr)))
880 ' ' * (locale_len - len(timestr)))
879 else:
881 else:
880 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
882 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
881 time.localtime(ent[3]))
883 time.localtime(ent[3]))
882 if ent[1] & 020000:
884 if ent[1] & 020000:
883 mode = 'lnk'
885 mode = 'lnk'
884 else:
886 else:
885 mode = '%3o' % (ent[1] & 0777)
887 mode = '%3o' % (ent[1] & 0777)
886 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
888 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
887 for f in repo.dirstate.copies():
889 for f in repo.dirstate.copies():
888 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
890 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
889
891
890 def debugsub(ui, repo, rev=None):
892 def debugsub(ui, repo, rev=None):
891 if rev == '':
893 if rev == '':
892 rev = None
894 rev = None
893 for k, v in sorted(repo[rev].substate.items()):
895 for k, v in sorted(repo[rev].substate.items()):
894 ui.write('path %s\n' % k)
896 ui.write('path %s\n' % k)
895 ui.write(' source %s\n' % v[0])
897 ui.write(' source %s\n' % v[0])
896 ui.write(' revision %s\n' % v[1])
898 ui.write(' revision %s\n' % v[1])
897
899
898 def debugdata(ui, file_, rev):
900 def debugdata(ui, file_, rev):
899 """dump the contents of a data file revision"""
901 """dump the contents of a data file revision"""
900 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
902 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
901 try:
903 try:
902 ui.write(r.revision(r.lookup(rev)))
904 ui.write(r.revision(r.lookup(rev)))
903 except KeyError:
905 except KeyError:
904 raise util.Abort(_('invalid revision identifier %s') % rev)
906 raise util.Abort(_('invalid revision identifier %s') % rev)
905
907
906 def debugdate(ui, date, range=None, **opts):
908 def debugdate(ui, date, range=None, **opts):
907 """parse and display a date"""
909 """parse and display a date"""
908 if opts["extended"]:
910 if opts["extended"]:
909 d = util.parsedate(date, util.extendeddateformats)
911 d = util.parsedate(date, util.extendeddateformats)
910 else:
912 else:
911 d = util.parsedate(date)
913 d = util.parsedate(date)
912 ui.write("internal: %s %s\n" % d)
914 ui.write("internal: %s %s\n" % d)
913 ui.write("standard: %s\n" % util.datestr(d))
915 ui.write("standard: %s\n" % util.datestr(d))
914 if range:
916 if range:
915 m = util.matchdate(range)
917 m = util.matchdate(range)
916 ui.write("match: %s\n" % m(d[0]))
918 ui.write("match: %s\n" % m(d[0]))
917
919
918 def debugindex(ui, file_):
920 def debugindex(ui, file_):
919 """dump the contents of an index file"""
921 """dump the contents of an index file"""
920 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
922 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
921 ui.write(" rev offset length base linkrev"
923 ui.write(" rev offset length base linkrev"
922 " nodeid p1 p2\n")
924 " nodeid p1 p2\n")
923 for i in r:
925 for i in r:
924 node = r.node(i)
926 node = r.node(i)
925 try:
927 try:
926 pp = r.parents(node)
928 pp = r.parents(node)
927 except:
929 except:
928 pp = [nullid, nullid]
930 pp = [nullid, nullid]
929 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
931 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
930 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
932 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
931 short(node), short(pp[0]), short(pp[1])))
933 short(node), short(pp[0]), short(pp[1])))
932
934
933 def debugindexdot(ui, file_):
935 def debugindexdot(ui, file_):
934 """dump an index DAG as a graphviz dot file"""
936 """dump an index DAG as a graphviz dot file"""
935 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
937 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
936 ui.write("digraph G {\n")
938 ui.write("digraph G {\n")
937 for i in r:
939 for i in r:
938 node = r.node(i)
940 node = r.node(i)
939 pp = r.parents(node)
941 pp = r.parents(node)
940 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
942 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
941 if pp[1] != nullid:
943 if pp[1] != nullid:
942 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
944 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
943 ui.write("}\n")
945 ui.write("}\n")
944
946
945 def debuginstall(ui):
947 def debuginstall(ui):
946 '''test Mercurial installation'''
948 '''test Mercurial installation'''
947
949
948 def writetemp(contents):
950 def writetemp(contents):
949 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
951 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
950 f = os.fdopen(fd, "wb")
952 f = os.fdopen(fd, "wb")
951 f.write(contents)
953 f.write(contents)
952 f.close()
954 f.close()
953 return name
955 return name
954
956
955 problems = 0
957 problems = 0
956
958
957 # encoding
959 # encoding
958 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
960 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
959 try:
961 try:
960 encoding.fromlocal("test")
962 encoding.fromlocal("test")
961 except util.Abort, inst:
963 except util.Abort, inst:
962 ui.write(" %s\n" % inst)
964 ui.write(" %s\n" % inst)
963 ui.write(_(" (check that your locale is properly set)\n"))
965 ui.write(_(" (check that your locale is properly set)\n"))
964 problems += 1
966 problems += 1
965
967
966 # compiled modules
968 # compiled modules
967 ui.status(_("Checking extensions...\n"))
969 ui.status(_("Checking extensions...\n"))
968 try:
970 try:
969 import bdiff, mpatch, base85
971 import bdiff, mpatch, base85
970 except Exception, inst:
972 except Exception, inst:
971 ui.write(" %s\n" % inst)
973 ui.write(" %s\n" % inst)
972 ui.write(_(" One or more extensions could not be found"))
974 ui.write(_(" One or more extensions could not be found"))
973 ui.write(_(" (check that you compiled the extensions)\n"))
975 ui.write(_(" (check that you compiled the extensions)\n"))
974 problems += 1
976 problems += 1
975
977
976 # templates
978 # templates
977 ui.status(_("Checking templates...\n"))
979 ui.status(_("Checking templates...\n"))
978 try:
980 try:
979 import templater
981 import templater
980 templater.templater(templater.templatepath("map-cmdline.default"))
982 templater.templater(templater.templatepath("map-cmdline.default"))
981 except Exception, inst:
983 except Exception, inst:
982 ui.write(" %s\n" % inst)
984 ui.write(" %s\n" % inst)
983 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
985 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
984 problems += 1
986 problems += 1
985
987
986 # patch
988 # patch
987 ui.status(_("Checking patch...\n"))
989 ui.status(_("Checking patch...\n"))
988 patchproblems = 0
990 patchproblems = 0
989 a = "1\n2\n3\n4\n"
991 a = "1\n2\n3\n4\n"
990 b = "1\n2\n3\ninsert\n4\n"
992 b = "1\n2\n3\ninsert\n4\n"
991 fa = writetemp(a)
993 fa = writetemp(a)
992 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
994 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
993 os.path.basename(fa))
995 os.path.basename(fa))
994 fd = writetemp(d)
996 fd = writetemp(d)
995
997
996 files = {}
998 files = {}
997 try:
999 try:
998 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1000 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
999 except util.Abort, e:
1001 except util.Abort, e:
1000 ui.write(_(" patch call failed:\n"))
1002 ui.write(_(" patch call failed:\n"))
1001 ui.write(" " + str(e) + "\n")
1003 ui.write(" " + str(e) + "\n")
1002 patchproblems += 1
1004 patchproblems += 1
1003 else:
1005 else:
1004 if list(files) != [os.path.basename(fa)]:
1006 if list(files) != [os.path.basename(fa)]:
1005 ui.write(_(" unexpected patch output!\n"))
1007 ui.write(_(" unexpected patch output!\n"))
1006 patchproblems += 1
1008 patchproblems += 1
1007 a = open(fa).read()
1009 a = open(fa).read()
1008 if a != b:
1010 if a != b:
1009 ui.write(_(" patch test failed!\n"))
1011 ui.write(_(" patch test failed!\n"))
1010 patchproblems += 1
1012 patchproblems += 1
1011
1013
1012 if patchproblems:
1014 if patchproblems:
1013 if ui.config('ui', 'patch'):
1015 if ui.config('ui', 'patch'):
1014 ui.write(_(" (Current patch tool may be incompatible with patch,"
1016 ui.write(_(" (Current patch tool may be incompatible with patch,"
1015 " or misconfigured. Please check your .hgrc file)\n"))
1017 " or misconfigured. Please check your .hgrc file)\n"))
1016 else:
1018 else:
1017 ui.write(_(" Internal patcher failure, please report this error"
1019 ui.write(_(" Internal patcher failure, please report this error"
1018 " to http://mercurial.selenic.com/bts/\n"))
1020 " to http://mercurial.selenic.com/bts/\n"))
1019 problems += patchproblems
1021 problems += patchproblems
1020
1022
1021 os.unlink(fa)
1023 os.unlink(fa)
1022 os.unlink(fd)
1024 os.unlink(fd)
1023
1025
1024 # editor
1026 # editor
1025 ui.status(_("Checking commit editor...\n"))
1027 ui.status(_("Checking commit editor...\n"))
1026 editor = ui.geteditor()
1028 editor = ui.geteditor()
1027 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1029 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1028 if not cmdpath:
1030 if not cmdpath:
1029 if editor == 'vi':
1031 if editor == 'vi':
1030 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1032 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1031 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1033 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1032 else:
1034 else:
1033 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1035 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1034 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1036 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1035 problems += 1
1037 problems += 1
1036
1038
1037 # check username
1039 # check username
1038 ui.status(_("Checking username...\n"))
1040 ui.status(_("Checking username...\n"))
1039 try:
1041 try:
1040 user = ui.username()
1042 user = ui.username()
1041 except util.Abort, e:
1043 except util.Abort, e:
1042 ui.write(" %s\n" % e)
1044 ui.write(" %s\n" % e)
1043 ui.write(_(" (specify a username in your .hgrc file)\n"))
1045 ui.write(_(" (specify a username in your .hgrc file)\n"))
1044 problems += 1
1046 problems += 1
1045
1047
1046 if not problems:
1048 if not problems:
1047 ui.status(_("No problems detected\n"))
1049 ui.status(_("No problems detected\n"))
1048 else:
1050 else:
1049 ui.write(_("%s problems detected,"
1051 ui.write(_("%s problems detected,"
1050 " please check your install!\n") % problems)
1052 " please check your install!\n") % problems)
1051
1053
1052 return problems
1054 return problems
1053
1055
1054 def debugrename(ui, repo, file1, *pats, **opts):
1056 def debugrename(ui, repo, file1, *pats, **opts):
1055 """dump rename information"""
1057 """dump rename information"""
1056
1058
1057 ctx = repo[opts.get('rev')]
1059 ctx = repo[opts.get('rev')]
1058 m = cmdutil.match(repo, (file1,) + pats, opts)
1060 m = cmdutil.match(repo, (file1,) + pats, opts)
1059 for abs in ctx.walk(m):
1061 for abs in ctx.walk(m):
1060 fctx = ctx[abs]
1062 fctx = ctx[abs]
1061 o = fctx.filelog().renamed(fctx.filenode())
1063 o = fctx.filelog().renamed(fctx.filenode())
1062 rel = m.rel(abs)
1064 rel = m.rel(abs)
1063 if o:
1065 if o:
1064 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1066 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1065 else:
1067 else:
1066 ui.write(_("%s not renamed\n") % rel)
1068 ui.write(_("%s not renamed\n") % rel)
1067
1069
1068 def debugwalk(ui, repo, *pats, **opts):
1070 def debugwalk(ui, repo, *pats, **opts):
1069 """show how files match on given patterns"""
1071 """show how files match on given patterns"""
1070 m = cmdutil.match(repo, pats, opts)
1072 m = cmdutil.match(repo, pats, opts)
1071 items = list(repo.walk(m))
1073 items = list(repo.walk(m))
1072 if not items:
1074 if not items:
1073 return
1075 return
1074 fmt = 'f %%-%ds %%-%ds %%s' % (
1076 fmt = 'f %%-%ds %%-%ds %%s' % (
1075 max([len(abs) for abs in items]),
1077 max([len(abs) for abs in items]),
1076 max([len(m.rel(abs)) for abs in items]))
1078 max([len(m.rel(abs)) for abs in items]))
1077 for abs in items:
1079 for abs in items:
1078 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1080 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1079 ui.write("%s\n" % line.rstrip())
1081 ui.write("%s\n" % line.rstrip())
1080
1082
1081 def diff(ui, repo, *pats, **opts):
1083 def diff(ui, repo, *pats, **opts):
1082 """diff repository (or selected files)
1084 """diff repository (or selected files)
1083
1085
1084 Show differences between revisions for the specified files.
1086 Show differences between revisions for the specified files.
1085
1087
1086 Differences between files are shown using the unified diff format.
1088 Differences between files are shown using the unified diff format.
1087
1089
1088 NOTE: diff may generate unexpected results for merges, as it will
1090 NOTE: diff may generate unexpected results for merges, as it will
1089 default to comparing against the working directory's first parent
1091 default to comparing against the working directory's first parent
1090 changeset if no revisions are specified.
1092 changeset if no revisions are specified.
1091
1093
1092 When two revision arguments are given, then changes are shown
1094 When two revision arguments are given, then changes are shown
1093 between those revisions. If only one revision is specified then
1095 between those revisions. If only one revision is specified then
1094 that revision is compared to the working directory, and, when no
1096 that revision is compared to the working directory, and, when no
1095 revisions are specified, the working directory files are compared
1097 revisions are specified, the working directory files are compared
1096 to its parent.
1098 to its parent.
1097
1099
1098 Without the -a/--text option, diff will avoid generating diffs of
1100 Without the -a/--text option, diff will avoid generating diffs of
1099 files it detects as binary. With -a, diff will generate a diff
1101 files it detects as binary. With -a, diff will generate a diff
1100 anyway, probably with undesirable results.
1102 anyway, probably with undesirable results.
1101
1103
1102 Use the -g/--git option to generate diffs in the git extended diff
1104 Use the -g/--git option to generate diffs in the git extended diff
1103 format. For more information, read 'hg help diffs'.
1105 format. For more information, read 'hg help diffs'.
1104 """
1106 """
1105
1107
1106 revs = opts.get('rev')
1108 revs = opts.get('rev')
1107 change = opts.get('change')
1109 change = opts.get('change')
1108 stat = opts.get('stat')
1110 stat = opts.get('stat')
1109 reverse = opts.get('reverse')
1111 reverse = opts.get('reverse')
1110
1112
1111 if revs and change:
1113 if revs and change:
1112 msg = _('cannot specify --rev and --change at the same time')
1114 msg = _('cannot specify --rev and --change at the same time')
1113 raise util.Abort(msg)
1115 raise util.Abort(msg)
1114 elif change:
1116 elif change:
1115 node2 = repo.lookup(change)
1117 node2 = repo.lookup(change)
1116 node1 = repo[node2].parents()[0].node()
1118 node1 = repo[node2].parents()[0].node()
1117 else:
1119 else:
1118 node1, node2 = cmdutil.revpair(repo, revs)
1120 node1, node2 = cmdutil.revpair(repo, revs)
1119
1121
1120 if reverse:
1122 if reverse:
1121 node1, node2 = node2, node1
1123 node1, node2 = node2, node1
1122
1124
1123 if stat:
1125 if stat:
1124 opts['unified'] = '0'
1126 opts['unified'] = '0'
1125 diffopts = patch.diffopts(ui, opts)
1127 diffopts = patch.diffopts(ui, opts)
1126
1128
1127 m = cmdutil.match(repo, pats, opts)
1129 m = cmdutil.match(repo, pats, opts)
1128 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1130 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1129 if stat:
1131 if stat:
1130 width = ui.interactive() and util.termwidth() or 80
1132 width = ui.interactive() and util.termwidth() or 80
1131 ui.write(patch.diffstat(util.iterlines(it), width=width,
1133 ui.write(patch.diffstat(util.iterlines(it), width=width,
1132 git=diffopts.git))
1134 git=diffopts.git))
1133 else:
1135 else:
1134 for chunk in it:
1136 for chunk in it:
1135 ui.write(chunk)
1137 ui.write(chunk)
1136
1138
1137 def export(ui, repo, *changesets, **opts):
1139 def export(ui, repo, *changesets, **opts):
1138 """dump the header and diffs for one or more changesets
1140 """dump the header and diffs for one or more changesets
1139
1141
1140 Print the changeset header and diffs for one or more revisions.
1142 Print the changeset header and diffs for one or more revisions.
1141
1143
1142 The information shown in the changeset header is: author, date,
1144 The information shown in the changeset header is: author, date,
1143 branch name (if non-default), changeset hash, parent(s) and commit
1145 branch name (if non-default), changeset hash, parent(s) and commit
1144 comment.
1146 comment.
1145
1147
1146 NOTE: export may generate unexpected diff output for merge
1148 NOTE: export may generate unexpected diff output for merge
1147 changesets, as it will compare the merge changeset against its
1149 changesets, as it will compare the merge changeset against its
1148 first parent only.
1150 first parent only.
1149
1151
1150 Output may be to a file, in which case the name of the file is
1152 Output may be to a file, in which case the name of the file is
1151 given using a format string. The formatting rules are as follows:
1153 given using a format string. The formatting rules are as follows:
1152
1154
1153 :``%%``: literal "%" character
1155 :``%%``: literal "%" character
1154 :``%H``: changeset hash (40 bytes of hexadecimal)
1156 :``%H``: changeset hash (40 bytes of hexadecimal)
1155 :``%N``: number of patches being generated
1157 :``%N``: number of patches being generated
1156 :``%R``: changeset revision number
1158 :``%R``: changeset revision number
1157 :``%b``: basename of the exporting repository
1159 :``%b``: basename of the exporting repository
1158 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1160 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1159 :``%n``: zero-padded sequence number, starting at 1
1161 :``%n``: zero-padded sequence number, starting at 1
1160 :``%r``: zero-padded changeset revision number
1162 :``%r``: zero-padded changeset revision number
1161
1163
1162 Without the -a/--text option, export will avoid generating diffs
1164 Without the -a/--text option, export will avoid generating diffs
1163 of files it detects as binary. With -a, export will generate a
1165 of files it detects as binary. With -a, export will generate a
1164 diff anyway, probably with undesirable results.
1166 diff anyway, probably with undesirable results.
1165
1167
1166 Use the -g/--git option to generate diffs in the git extended diff
1168 Use the -g/--git option to generate diffs in the git extended diff
1167 format. See 'hg help diffs' for more information.
1169 format. See 'hg help diffs' for more information.
1168
1170
1169 With the --switch-parent option, the diff will be against the
1171 With the --switch-parent option, the diff will be against the
1170 second parent. It can be useful to review a merge.
1172 second parent. It can be useful to review a merge.
1171 """
1173 """
1172 changesets += tuple(opts.get('rev', []))
1174 changesets += tuple(opts.get('rev', []))
1173 if not changesets:
1175 if not changesets:
1174 raise util.Abort(_("export requires at least one changeset"))
1176 raise util.Abort(_("export requires at least one changeset"))
1175 revs = cmdutil.revrange(repo, changesets)
1177 revs = cmdutil.revrange(repo, changesets)
1176 if len(revs) > 1:
1178 if len(revs) > 1:
1177 ui.note(_('exporting patches:\n'))
1179 ui.note(_('exporting patches:\n'))
1178 else:
1180 else:
1179 ui.note(_('exporting patch:\n'))
1181 ui.note(_('exporting patch:\n'))
1180 patch.export(repo, revs, template=opts.get('output'),
1182 patch.export(repo, revs, template=opts.get('output'),
1181 switch_parent=opts.get('switch_parent'),
1183 switch_parent=opts.get('switch_parent'),
1182 opts=patch.diffopts(ui, opts))
1184 opts=patch.diffopts(ui, opts))
1183
1185
1184 def forget(ui, repo, *pats, **opts):
1186 def forget(ui, repo, *pats, **opts):
1185 """forget the specified files on the next commit
1187 """forget the specified files on the next commit
1186
1188
1187 Mark the specified files so they will no longer be tracked
1189 Mark the specified files so they will no longer be tracked
1188 after the next commit.
1190 after the next commit.
1189
1191
1190 This only removes files from the current branch, not from the
1192 This only removes files from the current branch, not from the
1191 entire project history, and it does not delete them from the
1193 entire project history, and it does not delete them from the
1192 working directory.
1194 working directory.
1193
1195
1194 To undo a forget before the next commit, see hg add.
1196 To undo a forget before the next commit, see hg add.
1195 """
1197 """
1196
1198
1197 if not pats:
1199 if not pats:
1198 raise util.Abort(_('no files specified'))
1200 raise util.Abort(_('no files specified'))
1199
1201
1200 m = cmdutil.match(repo, pats, opts)
1202 m = cmdutil.match(repo, pats, opts)
1201 s = repo.status(match=m, clean=True)
1203 s = repo.status(match=m, clean=True)
1202 forget = sorted(s[0] + s[1] + s[3] + s[6])
1204 forget = sorted(s[0] + s[1] + s[3] + s[6])
1203
1205
1204 for f in m.files():
1206 for f in m.files():
1205 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1207 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1206 ui.warn(_('not removing %s: file is already untracked\n')
1208 ui.warn(_('not removing %s: file is already untracked\n')
1207 % m.rel(f))
1209 % m.rel(f))
1208
1210
1209 for f in forget:
1211 for f in forget:
1210 if ui.verbose or not m.exact(f):
1212 if ui.verbose or not m.exact(f):
1211 ui.status(_('removing %s\n') % m.rel(f))
1213 ui.status(_('removing %s\n') % m.rel(f))
1212
1214
1213 repo.remove(forget, unlink=False)
1215 repo.remove(forget, unlink=False)
1214
1216
1215 def grep(ui, repo, pattern, *pats, **opts):
1217 def grep(ui, repo, pattern, *pats, **opts):
1216 """search for a pattern in specified files and revisions
1218 """search for a pattern in specified files and revisions
1217
1219
1218 Search revisions of files for a regular expression.
1220 Search revisions of files for a regular expression.
1219
1221
1220 This command behaves differently than Unix grep. It only accepts
1222 This command behaves differently than Unix grep. It only accepts
1221 Python/Perl regexps. It searches repository history, not the
1223 Python/Perl regexps. It searches repository history, not the
1222 working directory. It always prints the revision number in which a
1224 working directory. It always prints the revision number in which a
1223 match appears.
1225 match appears.
1224
1226
1225 By default, grep only prints output for the first revision of a
1227 By default, grep only prints output for the first revision of a
1226 file in which it finds a match. To get it to print every revision
1228 file in which it finds a match. To get it to print every revision
1227 that contains a change in match status ("-" for a match that
1229 that contains a change in match status ("-" for a match that
1228 becomes a non-match, or "+" for a non-match that becomes a match),
1230 becomes a non-match, or "+" for a non-match that becomes a match),
1229 use the --all flag.
1231 use the --all flag.
1230 """
1232 """
1231 reflags = 0
1233 reflags = 0
1232 if opts.get('ignore_case'):
1234 if opts.get('ignore_case'):
1233 reflags |= re.I
1235 reflags |= re.I
1234 try:
1236 try:
1235 regexp = re.compile(pattern, reflags)
1237 regexp = re.compile(pattern, reflags)
1236 except Exception, inst:
1238 except Exception, inst:
1237 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1239 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1238 return None
1240 return None
1239 sep, eol = ':', '\n'
1241 sep, eol = ':', '\n'
1240 if opts.get('print0'):
1242 if opts.get('print0'):
1241 sep = eol = '\0'
1243 sep = eol = '\0'
1242
1244
1243 getfile = util.lrucachefunc(repo.file)
1245 getfile = util.lrucachefunc(repo.file)
1244
1246
1245 def matchlines(body):
1247 def matchlines(body):
1246 begin = 0
1248 begin = 0
1247 linenum = 0
1249 linenum = 0
1248 while True:
1250 while True:
1249 match = regexp.search(body, begin)
1251 match = regexp.search(body, begin)
1250 if not match:
1252 if not match:
1251 break
1253 break
1252 mstart, mend = match.span()
1254 mstart, mend = match.span()
1253 linenum += body.count('\n', begin, mstart) + 1
1255 linenum += body.count('\n', begin, mstart) + 1
1254 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1256 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1255 begin = body.find('\n', mend) + 1 or len(body)
1257 begin = body.find('\n', mend) + 1 or len(body)
1256 lend = begin - 1
1258 lend = begin - 1
1257 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1259 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1258
1260
1259 class linestate(object):
1261 class linestate(object):
1260 def __init__(self, line, linenum, colstart, colend):
1262 def __init__(self, line, linenum, colstart, colend):
1261 self.line = line
1263 self.line = line
1262 self.linenum = linenum
1264 self.linenum = linenum
1263 self.colstart = colstart
1265 self.colstart = colstart
1264 self.colend = colend
1266 self.colend = colend
1265
1267
1266 def __hash__(self):
1268 def __hash__(self):
1267 return hash((self.linenum, self.line))
1269 return hash((self.linenum, self.line))
1268
1270
1269 def __eq__(self, other):
1271 def __eq__(self, other):
1270 return self.line == other.line
1272 return self.line == other.line
1271
1273
1272 matches = {}
1274 matches = {}
1273 copies = {}
1275 copies = {}
1274 def grepbody(fn, rev, body):
1276 def grepbody(fn, rev, body):
1275 matches[rev].setdefault(fn, [])
1277 matches[rev].setdefault(fn, [])
1276 m = matches[rev][fn]
1278 m = matches[rev][fn]
1277 for lnum, cstart, cend, line in matchlines(body):
1279 for lnum, cstart, cend, line in matchlines(body):
1278 s = linestate(line, lnum, cstart, cend)
1280 s = linestate(line, lnum, cstart, cend)
1279 m.append(s)
1281 m.append(s)
1280
1282
1281 def difflinestates(a, b):
1283 def difflinestates(a, b):
1282 sm = difflib.SequenceMatcher(None, a, b)
1284 sm = difflib.SequenceMatcher(None, a, b)
1283 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1285 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1284 if tag == 'insert':
1286 if tag == 'insert':
1285 for i in xrange(blo, bhi):
1287 for i in xrange(blo, bhi):
1286 yield ('+', b[i])
1288 yield ('+', b[i])
1287 elif tag == 'delete':
1289 elif tag == 'delete':
1288 for i in xrange(alo, ahi):
1290 for i in xrange(alo, ahi):
1289 yield ('-', a[i])
1291 yield ('-', a[i])
1290 elif tag == 'replace':
1292 elif tag == 'replace':
1291 for i in xrange(alo, ahi):
1293 for i in xrange(alo, ahi):
1292 yield ('-', a[i])
1294 yield ('-', a[i])
1293 for i in xrange(blo, bhi):
1295 for i in xrange(blo, bhi):
1294 yield ('+', b[i])
1296 yield ('+', b[i])
1295
1297
1296 def display(fn, ctx, pstates, states):
1298 def display(fn, ctx, pstates, states):
1297 rev = ctx.rev()
1299 rev = ctx.rev()
1298 datefunc = ui.quiet and util.shortdate or util.datestr
1300 datefunc = ui.quiet and util.shortdate or util.datestr
1299 found = False
1301 found = False
1300 filerevmatches = {}
1302 filerevmatches = {}
1301 if opts.get('all'):
1303 if opts.get('all'):
1302 iter = difflinestates(pstates, states)
1304 iter = difflinestates(pstates, states)
1303 else:
1305 else:
1304 iter = [('', l) for l in states]
1306 iter = [('', l) for l in states]
1305 for change, l in iter:
1307 for change, l in iter:
1306 cols = [fn, str(rev)]
1308 cols = [fn, str(rev)]
1307 if opts.get('line_number'):
1309 if opts.get('line_number'):
1308 cols.append(str(l.linenum))
1310 cols.append(str(l.linenum))
1309 if opts.get('all'):
1311 if opts.get('all'):
1310 cols.append(change)
1312 cols.append(change)
1311 if opts.get('user'):
1313 if opts.get('user'):
1312 cols.append(ui.shortuser(ctx.user()))
1314 cols.append(ui.shortuser(ctx.user()))
1313 if opts.get('date'):
1315 if opts.get('date'):
1314 cols.append(datefunc(ctx.date()))
1316 cols.append(datefunc(ctx.date()))
1315 if opts.get('files_with_matches'):
1317 if opts.get('files_with_matches'):
1316 c = (fn, rev)
1318 c = (fn, rev)
1317 if c in filerevmatches:
1319 if c in filerevmatches:
1318 continue
1320 continue
1319 filerevmatches[c] = 1
1321 filerevmatches[c] = 1
1320 else:
1322 else:
1321 cols.append(l.line)
1323 cols.append(l.line)
1322 ui.write(sep.join(cols), eol)
1324 ui.write(sep.join(cols), eol)
1323 found = True
1325 found = True
1324 return found
1326 return found
1325
1327
1326 skip = {}
1328 skip = {}
1327 revfiles = {}
1329 revfiles = {}
1328 matchfn = cmdutil.match(repo, pats, opts)
1330 matchfn = cmdutil.match(repo, pats, opts)
1329 found = False
1331 found = False
1330 follow = opts.get('follow')
1332 follow = opts.get('follow')
1331
1333
1332 def prep(ctx, fns):
1334 def prep(ctx, fns):
1333 rev = ctx.rev()
1335 rev = ctx.rev()
1334 pctx = ctx.parents()[0]
1336 pctx = ctx.parents()[0]
1335 parent = pctx.rev()
1337 parent = pctx.rev()
1336 matches.setdefault(rev, {})
1338 matches.setdefault(rev, {})
1337 matches.setdefault(parent, {})
1339 matches.setdefault(parent, {})
1338 files = revfiles.setdefault(rev, [])
1340 files = revfiles.setdefault(rev, [])
1339 for fn in fns:
1341 for fn in fns:
1340 flog = getfile(fn)
1342 flog = getfile(fn)
1341 try:
1343 try:
1342 fnode = ctx.filenode(fn)
1344 fnode = ctx.filenode(fn)
1343 except error.LookupError:
1345 except error.LookupError:
1344 continue
1346 continue
1345
1347
1346 copied = flog.renamed(fnode)
1348 copied = flog.renamed(fnode)
1347 copy = follow and copied and copied[0]
1349 copy = follow and copied and copied[0]
1348 if copy:
1350 if copy:
1349 copies.setdefault(rev, {})[fn] = copy
1351 copies.setdefault(rev, {})[fn] = copy
1350 if fn in skip:
1352 if fn in skip:
1351 if copy:
1353 if copy:
1352 skip[copy] = True
1354 skip[copy] = True
1353 continue
1355 continue
1354 files.append(fn)
1356 files.append(fn)
1355
1357
1356 if fn not in matches[rev]:
1358 if fn not in matches[rev]:
1357 grepbody(fn, rev, flog.read(fnode))
1359 grepbody(fn, rev, flog.read(fnode))
1358
1360
1359 pfn = copy or fn
1361 pfn = copy or fn
1360 if pfn not in matches[parent]:
1362 if pfn not in matches[parent]:
1361 try:
1363 try:
1362 fnode = pctx.filenode(pfn)
1364 fnode = pctx.filenode(pfn)
1363 grepbody(pfn, parent, flog.read(fnode))
1365 grepbody(pfn, parent, flog.read(fnode))
1364 except error.LookupError:
1366 except error.LookupError:
1365 pass
1367 pass
1366
1368
1367 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1369 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1368 rev = ctx.rev()
1370 rev = ctx.rev()
1369 parent = ctx.parents()[0].rev()
1371 parent = ctx.parents()[0].rev()
1370 for fn in sorted(revfiles.get(rev, [])):
1372 for fn in sorted(revfiles.get(rev, [])):
1371 states = matches[rev][fn]
1373 states = matches[rev][fn]
1372 copy = copies.get(rev, {}).get(fn)
1374 copy = copies.get(rev, {}).get(fn)
1373 if fn in skip:
1375 if fn in skip:
1374 if copy:
1376 if copy:
1375 skip[copy] = True
1377 skip[copy] = True
1376 continue
1378 continue
1377 pstates = matches.get(parent, {}).get(copy or fn, [])
1379 pstates = matches.get(parent, {}).get(copy or fn, [])
1378 if pstates or states:
1380 if pstates or states:
1379 r = display(fn, ctx, pstates, states)
1381 r = display(fn, ctx, pstates, states)
1380 found = found or r
1382 found = found or r
1381 if r and not opts.get('all'):
1383 if r and not opts.get('all'):
1382 skip[fn] = True
1384 skip[fn] = True
1383 if copy:
1385 if copy:
1384 skip[copy] = True
1386 skip[copy] = True
1385 del matches[rev]
1387 del matches[rev]
1386 del revfiles[rev]
1388 del revfiles[rev]
1387
1389
1388 def heads(ui, repo, *branchrevs, **opts):
1390 def heads(ui, repo, *branchrevs, **opts):
1389 """show current repository heads or show branch heads
1391 """show current repository heads or show branch heads
1390
1392
1391 With no arguments, show all repository branch heads.
1393 With no arguments, show all repository branch heads.
1392
1394
1393 Repository "heads" are changesets with no child changesets. They are
1395 Repository "heads" are changesets with no child changesets. They are
1394 where development generally takes place and are the usual targets
1396 where development generally takes place and are the usual targets
1395 for update and merge operations. Branch heads are changesets that have
1397 for update and merge operations. Branch heads are changesets that have
1396 no child changeset on the same branch.
1398 no child changeset on the same branch.
1397
1399
1398 If one or more REVs are given, only branch heads on the branches
1400 If one or more REVs are given, only branch heads on the branches
1399 associated with the specified changesets are shown.
1401 associated with the specified changesets are shown.
1400
1402
1401 If -c/--closed is specified, also show branch heads marked closed
1403 If -c/--closed is specified, also show branch heads marked closed
1402 (see hg commit --close-branch).
1404 (see hg commit --close-branch).
1403
1405
1404 If STARTREV is specified, only those heads that are descendants of
1406 If STARTREV is specified, only those heads that are descendants of
1405 STARTREV will be displayed.
1407 STARTREV will be displayed.
1406
1408
1407 If -t/--topo is specified, named branch mechanics will be ignored and only
1409 If -t/--topo is specified, named branch mechanics will be ignored and only
1408 changesets without children will be shown.
1410 changesets without children will be shown.
1409 """
1411 """
1410
1412
1411 if opts.get('rev'):
1413 if opts.get('rev'):
1412 start = repo.lookup(opts['rev'])
1414 start = repo.lookup(opts['rev'])
1413 else:
1415 else:
1414 start = None
1416 start = None
1415
1417
1416 if opts.get('topo'):
1418 if opts.get('topo'):
1417 heads = [repo[h] for h in repo.heads(start)]
1419 heads = [repo[h] for h in repo.heads(start)]
1418 else:
1420 else:
1419 heads = []
1421 heads = []
1420 for b, ls in repo.branchmap().iteritems():
1422 for b, ls in repo.branchmap().iteritems():
1421 if start is None:
1423 if start is None:
1422 heads += [repo[h] for h in ls]
1424 heads += [repo[h] for h in ls]
1423 continue
1425 continue
1424 startrev = repo.changelog.rev(start)
1426 startrev = repo.changelog.rev(start)
1425 descendants = set(repo.changelog.descendants(startrev))
1427 descendants = set(repo.changelog.descendants(startrev))
1426 descendants.add(startrev)
1428 descendants.add(startrev)
1427 rev = repo.changelog.rev
1429 rev = repo.changelog.rev
1428 heads += [repo[h] for h in ls if rev(h) in descendants]
1430 heads += [repo[h] for h in ls if rev(h) in descendants]
1429
1431
1430 if branchrevs:
1432 if branchrevs:
1431 decode, encode = encoding.fromlocal, encoding.tolocal
1433 decode, encode = encoding.fromlocal, encoding.tolocal
1432 branches = set(repo[decode(br)].branch() for br in branchrevs)
1434 branches = set(repo[decode(br)].branch() for br in branchrevs)
1433 heads = [h for h in heads if h.branch() in branches]
1435 heads = [h for h in heads if h.branch() in branches]
1434
1436
1435 if not opts.get('closed'):
1437 if not opts.get('closed'):
1436 heads = [h for h in heads if not h.extra().get('close')]
1438 heads = [h for h in heads if not h.extra().get('close')]
1437
1439
1438 if opts.get('active') and branchrevs:
1440 if opts.get('active') and branchrevs:
1439 dagheads = repo.heads(start)
1441 dagheads = repo.heads(start)
1440 heads = [h for h in heads if h.node() in dagheads]
1442 heads = [h for h in heads if h.node() in dagheads]
1441
1443
1442 if branchrevs:
1444 if branchrevs:
1443 haveheads = set(h.branch() for h in heads)
1445 haveheads = set(h.branch() for h in heads)
1444 if branches - haveheads:
1446 if branches - haveheads:
1445 headless = ', '.join(encode(b) for b in branches - haveheads)
1447 headless = ', '.join(encode(b) for b in branches - haveheads)
1446 msg = _('no open branch heads found on branches %s')
1448 msg = _('no open branch heads found on branches %s')
1447 if opts.get('rev'):
1449 if opts.get('rev'):
1448 msg += _(' (started at %s)' % opts['rev'])
1450 msg += _(' (started at %s)' % opts['rev'])
1449 ui.warn((msg + '\n') % headless)
1451 ui.warn((msg + '\n') % headless)
1450
1452
1451 if not heads:
1453 if not heads:
1452 return 1
1454 return 1
1453
1455
1454 heads = sorted(heads, key=lambda x: -x.rev())
1456 heads = sorted(heads, key=lambda x: -x.rev())
1455 displayer = cmdutil.show_changeset(ui, repo, opts)
1457 displayer = cmdutil.show_changeset(ui, repo, opts)
1456 for ctx in heads:
1458 for ctx in heads:
1457 displayer.show(ctx)
1459 displayer.show(ctx)
1458 displayer.close()
1460 displayer.close()
1459
1461
1460 def help_(ui, name=None, with_version=False, unknowncmd=False):
1462 def help_(ui, name=None, with_version=False, unknowncmd=False):
1461 """show help for a given topic or a help overview
1463 """show help for a given topic or a help overview
1462
1464
1463 With no arguments, print a list of commands with short help messages.
1465 With no arguments, print a list of commands with short help messages.
1464
1466
1465 Given a topic, extension, or command name, print help for that
1467 Given a topic, extension, or command name, print help for that
1466 topic."""
1468 topic."""
1467 option_lists = []
1469 option_lists = []
1468 textwidth = util.termwidth() - 2
1470 textwidth = util.termwidth() - 2
1469
1471
1470 def addglobalopts(aliases):
1472 def addglobalopts(aliases):
1471 if ui.verbose:
1473 if ui.verbose:
1472 option_lists.append((_("global options:"), globalopts))
1474 option_lists.append((_("global options:"), globalopts))
1473 if name == 'shortlist':
1475 if name == 'shortlist':
1474 option_lists.append((_('use "hg help" for the full list '
1476 option_lists.append((_('use "hg help" for the full list '
1475 'of commands'), ()))
1477 'of commands'), ()))
1476 else:
1478 else:
1477 if name == 'shortlist':
1479 if name == 'shortlist':
1478 msg = _('use "hg help" for the full list of commands '
1480 msg = _('use "hg help" for the full list of commands '
1479 'or "hg -v" for details')
1481 'or "hg -v" for details')
1480 elif aliases:
1482 elif aliases:
1481 msg = _('use "hg -v help%s" to show aliases and '
1483 msg = _('use "hg -v help%s" to show aliases and '
1482 'global options') % (name and " " + name or "")
1484 'global options') % (name and " " + name or "")
1483 else:
1485 else:
1484 msg = _('use "hg -v help %s" to show global options') % name
1486 msg = _('use "hg -v help %s" to show global options') % name
1485 option_lists.append((msg, ()))
1487 option_lists.append((msg, ()))
1486
1488
1487 def helpcmd(name):
1489 def helpcmd(name):
1488 if with_version:
1490 if with_version:
1489 version_(ui)
1491 version_(ui)
1490 ui.write('\n')
1492 ui.write('\n')
1491
1493
1492 try:
1494 try:
1493 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1495 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1494 except error.AmbiguousCommand, inst:
1496 except error.AmbiguousCommand, inst:
1495 # py3k fix: except vars can't be used outside the scope of the
1497 # py3k fix: except vars can't be used outside the scope of the
1496 # except block, nor can be used inside a lambda. python issue4617
1498 # except block, nor can be used inside a lambda. python issue4617
1497 prefix = inst.args[0]
1499 prefix = inst.args[0]
1498 select = lambda c: c.lstrip('^').startswith(prefix)
1500 select = lambda c: c.lstrip('^').startswith(prefix)
1499 helplist(_('list of commands:\n\n'), select)
1501 helplist(_('list of commands:\n\n'), select)
1500 return
1502 return
1501
1503
1502 # check if it's an invalid alias and display its error if it is
1504 # check if it's an invalid alias and display its error if it is
1503 if getattr(entry[0], 'badalias', False):
1505 if getattr(entry[0], 'badalias', False):
1504 if not unknowncmd:
1506 if not unknowncmd:
1505 entry[0](ui)
1507 entry[0](ui)
1506 return
1508 return
1507
1509
1508 # synopsis
1510 # synopsis
1509 if len(entry) > 2:
1511 if len(entry) > 2:
1510 if entry[2].startswith('hg'):
1512 if entry[2].startswith('hg'):
1511 ui.write("%s\n" % entry[2])
1513 ui.write("%s\n" % entry[2])
1512 else:
1514 else:
1513 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1515 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1514 else:
1516 else:
1515 ui.write('hg %s\n' % aliases[0])
1517 ui.write('hg %s\n' % aliases[0])
1516
1518
1517 # aliases
1519 # aliases
1518 if not ui.quiet and len(aliases) > 1:
1520 if not ui.quiet and len(aliases) > 1:
1519 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1521 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1520
1522
1521 # description
1523 # description
1522 doc = gettext(entry[0].__doc__)
1524 doc = gettext(entry[0].__doc__)
1523 if not doc:
1525 if not doc:
1524 doc = _("(no help text available)")
1526 doc = _("(no help text available)")
1525 if ui.quiet:
1527 if ui.quiet:
1526 doc = doc.splitlines()[0]
1528 doc = doc.splitlines()[0]
1527 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1529 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1528
1530
1529 if not ui.quiet:
1531 if not ui.quiet:
1530 # options
1532 # options
1531 if entry[1]:
1533 if entry[1]:
1532 option_lists.append((_("options:\n"), entry[1]))
1534 option_lists.append((_("options:\n"), entry[1]))
1533
1535
1534 addglobalopts(False)
1536 addglobalopts(False)
1535
1537
1536 def helplist(header, select=None):
1538 def helplist(header, select=None):
1537 h = {}
1539 h = {}
1538 cmds = {}
1540 cmds = {}
1539 for c, e in table.iteritems():
1541 for c, e in table.iteritems():
1540 f = c.split("|", 1)[0]
1542 f = c.split("|", 1)[0]
1541 if select and not select(f):
1543 if select and not select(f):
1542 continue
1544 continue
1543 if (not select and name != 'shortlist' and
1545 if (not select and name != 'shortlist' and
1544 e[0].__module__ != __name__):
1546 e[0].__module__ != __name__):
1545 continue
1547 continue
1546 if name == "shortlist" and not f.startswith("^"):
1548 if name == "shortlist" and not f.startswith("^"):
1547 continue
1549 continue
1548 f = f.lstrip("^")
1550 f = f.lstrip("^")
1549 if not ui.debugflag and f.startswith("debug"):
1551 if not ui.debugflag and f.startswith("debug"):
1550 continue
1552 continue
1551 doc = e[0].__doc__
1553 doc = e[0].__doc__
1552 if doc and 'DEPRECATED' in doc and not ui.verbose:
1554 if doc and 'DEPRECATED' in doc and not ui.verbose:
1553 continue
1555 continue
1554 doc = gettext(doc)
1556 doc = gettext(doc)
1555 if not doc:
1557 if not doc:
1556 doc = _("(no help text available)")
1558 doc = _("(no help text available)")
1557 h[f] = doc.splitlines()[0].rstrip()
1559 h[f] = doc.splitlines()[0].rstrip()
1558 cmds[f] = c.lstrip("^")
1560 cmds[f] = c.lstrip("^")
1559
1561
1560 if not h:
1562 if not h:
1561 ui.status(_('no commands defined\n'))
1563 ui.status(_('no commands defined\n'))
1562 return
1564 return
1563
1565
1564 ui.status(header)
1566 ui.status(header)
1565 fns = sorted(h)
1567 fns = sorted(h)
1566 m = max(map(len, fns))
1568 m = max(map(len, fns))
1567 for f in fns:
1569 for f in fns:
1568 if ui.verbose:
1570 if ui.verbose:
1569 commands = cmds[f].replace("|",", ")
1571 commands = cmds[f].replace("|",", ")
1570 ui.write(" %s:\n %s\n"%(commands, h[f]))
1572 ui.write(" %s:\n %s\n"%(commands, h[f]))
1571 else:
1573 else:
1572 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1574 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1573
1575
1574 if not ui.quiet:
1576 if not ui.quiet:
1575 addglobalopts(True)
1577 addglobalopts(True)
1576
1578
1577 def helptopic(name):
1579 def helptopic(name):
1578 for names, header, doc in help.helptable:
1580 for names, header, doc in help.helptable:
1579 if name in names:
1581 if name in names:
1580 break
1582 break
1581 else:
1583 else:
1582 raise error.UnknownCommand(name)
1584 raise error.UnknownCommand(name)
1583
1585
1584 # description
1586 # description
1585 if not doc:
1587 if not doc:
1586 doc = _("(no help text available)")
1588 doc = _("(no help text available)")
1587 if hasattr(doc, '__call__'):
1589 if hasattr(doc, '__call__'):
1588 doc = doc()
1590 doc = doc()
1589
1591
1590 ui.write("%s\n\n" % header)
1592 ui.write("%s\n\n" % header)
1591 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1593 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1592
1594
1593 def helpext(name):
1595 def helpext(name):
1594 try:
1596 try:
1595 mod = extensions.find(name)
1597 mod = extensions.find(name)
1596 doc = gettext(mod.__doc__) or _('no help text available')
1598 doc = gettext(mod.__doc__) or _('no help text available')
1597 except KeyError:
1599 except KeyError:
1598 mod = None
1600 mod = None
1599 doc = extensions.disabledext(name)
1601 doc = extensions.disabledext(name)
1600 if not doc:
1602 if not doc:
1601 raise error.UnknownCommand(name)
1603 raise error.UnknownCommand(name)
1602
1604
1603 if '\n' not in doc:
1605 if '\n' not in doc:
1604 head, tail = doc, ""
1606 head, tail = doc, ""
1605 else:
1607 else:
1606 head, tail = doc.split('\n', 1)
1608 head, tail = doc.split('\n', 1)
1607 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1609 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1608 if tail:
1610 if tail:
1609 ui.write(minirst.format(tail, textwidth))
1611 ui.write(minirst.format(tail, textwidth))
1610 ui.status('\n\n')
1612 ui.status('\n\n')
1611
1613
1612 if mod:
1614 if mod:
1613 try:
1615 try:
1614 ct = mod.cmdtable
1616 ct = mod.cmdtable
1615 except AttributeError:
1617 except AttributeError:
1616 ct = {}
1618 ct = {}
1617 modcmds = set([c.split('|', 1)[0] for c in ct])
1619 modcmds = set([c.split('|', 1)[0] for c in ct])
1618 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1620 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1619 else:
1621 else:
1620 ui.write(_('use "hg help extensions" for information on enabling '
1622 ui.write(_('use "hg help extensions" for information on enabling '
1621 'extensions\n'))
1623 'extensions\n'))
1622
1624
1623 def helpextcmd(name):
1625 def helpextcmd(name):
1624 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1626 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1625 doc = gettext(mod.__doc__).splitlines()[0]
1627 doc = gettext(mod.__doc__).splitlines()[0]
1626
1628
1627 msg = help.listexts(_("'%s' is provided by the following "
1629 msg = help.listexts(_("'%s' is provided by the following "
1628 "extension:") % cmd, {ext: doc}, len(ext),
1630 "extension:") % cmd, {ext: doc}, len(ext),
1629 indent=4)
1631 indent=4)
1630 ui.write(minirst.format(msg, textwidth))
1632 ui.write(minirst.format(msg, textwidth))
1631 ui.write('\n\n')
1633 ui.write('\n\n')
1632 ui.write(_('use "hg help extensions" for information on enabling '
1634 ui.write(_('use "hg help extensions" for information on enabling '
1633 'extensions\n'))
1635 'extensions\n'))
1634
1636
1635 if name and name != 'shortlist':
1637 if name and name != 'shortlist':
1636 i = None
1638 i = None
1637 if unknowncmd:
1639 if unknowncmd:
1638 queries = (helpextcmd,)
1640 queries = (helpextcmd,)
1639 else:
1641 else:
1640 queries = (helptopic, helpcmd, helpext, helpextcmd)
1642 queries = (helptopic, helpcmd, helpext, helpextcmd)
1641 for f in queries:
1643 for f in queries:
1642 try:
1644 try:
1643 f(name)
1645 f(name)
1644 i = None
1646 i = None
1645 break
1647 break
1646 except error.UnknownCommand, inst:
1648 except error.UnknownCommand, inst:
1647 i = inst
1649 i = inst
1648 if i:
1650 if i:
1649 raise i
1651 raise i
1650
1652
1651 else:
1653 else:
1652 # program name
1654 # program name
1653 if ui.verbose or with_version:
1655 if ui.verbose or with_version:
1654 version_(ui)
1656 version_(ui)
1655 else:
1657 else:
1656 ui.status(_("Mercurial Distributed SCM\n"))
1658 ui.status(_("Mercurial Distributed SCM\n"))
1657 ui.status('\n')
1659 ui.status('\n')
1658
1660
1659 # list of commands
1661 # list of commands
1660 if name == "shortlist":
1662 if name == "shortlist":
1661 header = _('basic commands:\n\n')
1663 header = _('basic commands:\n\n')
1662 else:
1664 else:
1663 header = _('list of commands:\n\n')
1665 header = _('list of commands:\n\n')
1664
1666
1665 helplist(header)
1667 helplist(header)
1666 if name != 'shortlist':
1668 if name != 'shortlist':
1667 exts, maxlength = extensions.enabled()
1669 exts, maxlength = extensions.enabled()
1668 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1670 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1669 if text:
1671 if text:
1670 ui.write("\n%s\n" % minirst.format(text, textwidth))
1672 ui.write("\n%s\n" % minirst.format(text, textwidth))
1671
1673
1672 # list all option lists
1674 # list all option lists
1673 opt_output = []
1675 opt_output = []
1674 for title, options in option_lists:
1676 for title, options in option_lists:
1675 opt_output.append(("\n%s" % title, None))
1677 opt_output.append(("\n%s" % title, None))
1676 for shortopt, longopt, default, desc in options:
1678 for shortopt, longopt, default, desc in options:
1677 if _("DEPRECATED") in desc and not ui.verbose:
1679 if _("DEPRECATED") in desc and not ui.verbose:
1678 continue
1680 continue
1679 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1681 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1680 longopt and " --%s" % longopt),
1682 longopt and " --%s" % longopt),
1681 "%s%s" % (desc,
1683 "%s%s" % (desc,
1682 default
1684 default
1683 and _(" (default: %s)") % default
1685 and _(" (default: %s)") % default
1684 or "")))
1686 or "")))
1685
1687
1686 if not name:
1688 if not name:
1687 ui.write(_("\nadditional help topics:\n\n"))
1689 ui.write(_("\nadditional help topics:\n\n"))
1688 topics = []
1690 topics = []
1689 for names, header, doc in help.helptable:
1691 for names, header, doc in help.helptable:
1690 topics.append((sorted(names, key=len, reverse=True)[0], header))
1692 topics.append((sorted(names, key=len, reverse=True)[0], header))
1691 topics_len = max([len(s[0]) for s in topics])
1693 topics_len = max([len(s[0]) for s in topics])
1692 for t, desc in topics:
1694 for t, desc in topics:
1693 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1695 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1694
1696
1695 if opt_output:
1697 if opt_output:
1696 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1698 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1697 for first, second in opt_output:
1699 for first, second in opt_output:
1698 if second:
1700 if second:
1699 second = util.wrap(second, opts_len + 3)
1701 second = util.wrap(second, opts_len + 3)
1700 ui.write(" %-*s %s\n" % (opts_len, first, second))
1702 ui.write(" %-*s %s\n" % (opts_len, first, second))
1701 else:
1703 else:
1702 ui.write("%s\n" % first)
1704 ui.write("%s\n" % first)
1703
1705
1704 def identify(ui, repo, source=None,
1706 def identify(ui, repo, source=None,
1705 rev=None, num=None, id=None, branch=None, tags=None):
1707 rev=None, num=None, id=None, branch=None, tags=None):
1706 """identify the working copy or specified revision
1708 """identify the working copy or specified revision
1707
1709
1708 With no revision, print a summary of the current state of the
1710 With no revision, print a summary of the current state of the
1709 repository.
1711 repository.
1710
1712
1711 Specifying a path to a repository root or Mercurial bundle will
1713 Specifying a path to a repository root or Mercurial bundle will
1712 cause lookup to operate on that repository/bundle.
1714 cause lookup to operate on that repository/bundle.
1713
1715
1714 This summary identifies the repository state using one or two
1716 This summary identifies the repository state using one or two
1715 parent hash identifiers, followed by a "+" if there are
1717 parent hash identifiers, followed by a "+" if there are
1716 uncommitted changes in the working directory, a list of tags for
1718 uncommitted changes in the working directory, a list of tags for
1717 this revision and a branch name for non-default branches.
1719 this revision and a branch name for non-default branches.
1718 """
1720 """
1719
1721
1720 if not repo and not source:
1722 if not repo and not source:
1721 raise util.Abort(_("There is no Mercurial repository here "
1723 raise util.Abort(_("There is no Mercurial repository here "
1722 "(.hg not found)"))
1724 "(.hg not found)"))
1723
1725
1724 hexfunc = ui.debugflag and hex or short
1726 hexfunc = ui.debugflag and hex or short
1725 default = not (num or id or branch or tags)
1727 default = not (num or id or branch or tags)
1726 output = []
1728 output = []
1727
1729
1728 revs = []
1730 revs = []
1729 if source:
1731 if source:
1730 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1732 source, branches = hg.parseurl(ui.expandpath(source))
1731 repo = hg.repository(ui, source)
1733 repo = hg.repository(ui, source)
1734 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1732
1735
1733 if not repo.local():
1736 if not repo.local():
1734 if not rev and revs:
1737 if not rev and revs:
1735 rev = revs[0]
1738 rev = revs[0]
1736 if not rev:
1739 if not rev:
1737 rev = "tip"
1740 rev = "tip"
1738 if num or branch or tags:
1741 if num or branch or tags:
1739 raise util.Abort(
1742 raise util.Abort(
1740 "can't query remote revision number, branch, or tags")
1743 "can't query remote revision number, branch, or tags")
1741 output = [hexfunc(repo.lookup(rev))]
1744 output = [hexfunc(repo.lookup(rev))]
1742 elif not rev:
1745 elif not rev:
1743 ctx = repo[None]
1746 ctx = repo[None]
1744 parents = ctx.parents()
1747 parents = ctx.parents()
1745 changed = False
1748 changed = False
1746 if default or id or num:
1749 if default or id or num:
1747 changed = ctx.files() + ctx.deleted()
1750 changed = ctx.files() + ctx.deleted()
1748 if default or id:
1751 if default or id:
1749 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1752 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1750 (changed) and "+" or "")]
1753 (changed) and "+" or "")]
1751 if num:
1754 if num:
1752 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1755 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1753 (changed) and "+" or ""))
1756 (changed) and "+" or ""))
1754 else:
1757 else:
1755 ctx = repo[rev]
1758 ctx = repo[rev]
1756 if default or id:
1759 if default or id:
1757 output = [hexfunc(ctx.node())]
1760 output = [hexfunc(ctx.node())]
1758 if num:
1761 if num:
1759 output.append(str(ctx.rev()))
1762 output.append(str(ctx.rev()))
1760
1763
1761 if repo.local() and default and not ui.quiet:
1764 if repo.local() and default and not ui.quiet:
1762 b = encoding.tolocal(ctx.branch())
1765 b = encoding.tolocal(ctx.branch())
1763 if b != 'default':
1766 if b != 'default':
1764 output.append("(%s)" % b)
1767 output.append("(%s)" % b)
1765
1768
1766 # multiple tags for a single parent separated by '/'
1769 # multiple tags for a single parent separated by '/'
1767 t = "/".join(ctx.tags())
1770 t = "/".join(ctx.tags())
1768 if t:
1771 if t:
1769 output.append(t)
1772 output.append(t)
1770
1773
1771 if branch:
1774 if branch:
1772 output.append(encoding.tolocal(ctx.branch()))
1775 output.append(encoding.tolocal(ctx.branch()))
1773
1776
1774 if tags:
1777 if tags:
1775 output.extend(ctx.tags())
1778 output.extend(ctx.tags())
1776
1779
1777 ui.write("%s\n" % ' '.join(output))
1780 ui.write("%s\n" % ' '.join(output))
1778
1781
1779 def import_(ui, repo, patch1, *patches, **opts):
1782 def import_(ui, repo, patch1, *patches, **opts):
1780 """import an ordered set of patches
1783 """import an ordered set of patches
1781
1784
1782 Import a list of patches and commit them individually (unless
1785 Import a list of patches and commit them individually (unless
1783 --no-commit is specified).
1786 --no-commit is specified).
1784
1787
1785 If there are outstanding changes in the working directory, import
1788 If there are outstanding changes in the working directory, import
1786 will abort unless given the -f/--force flag.
1789 will abort unless given the -f/--force flag.
1787
1790
1788 You can import a patch straight from a mail message. Even patches
1791 You can import a patch straight from a mail message. Even patches
1789 as attachments work (to use the body part, it must have type
1792 as attachments work (to use the body part, it must have type
1790 text/plain or text/x-patch). From and Subject headers of email
1793 text/plain or text/x-patch). From and Subject headers of email
1791 message are used as default committer and commit message. All
1794 message are used as default committer and commit message. All
1792 text/plain body parts before first diff are added to commit
1795 text/plain body parts before first diff are added to commit
1793 message.
1796 message.
1794
1797
1795 If the imported patch was generated by hg export, user and
1798 If the imported patch was generated by hg export, user and
1796 description from patch override values from message headers and
1799 description from patch override values from message headers and
1797 body. Values given on command line with -m/--message and -u/--user
1800 body. Values given on command line with -m/--message and -u/--user
1798 override these.
1801 override these.
1799
1802
1800 If --exact is specified, import will set the working directory to
1803 If --exact is specified, import will set the working directory to
1801 the parent of each patch before applying it, and will abort if the
1804 the parent of each patch before applying it, and will abort if the
1802 resulting changeset has a different ID than the one recorded in
1805 resulting changeset has a different ID than the one recorded in
1803 the patch. This may happen due to character set problems or other
1806 the patch. This may happen due to character set problems or other
1804 deficiencies in the text patch format.
1807 deficiencies in the text patch format.
1805
1808
1806 With -s/--similarity, hg will attempt to discover renames and
1809 With -s/--similarity, hg will attempt to discover renames and
1807 copies in the patch in the same way as 'addremove'.
1810 copies in the patch in the same way as 'addremove'.
1808
1811
1809 To read a patch from standard input, use "-" as the patch name. If
1812 To read a patch from standard input, use "-" as the patch name. If
1810 a URL is specified, the patch will be downloaded from it.
1813 a URL is specified, the patch will be downloaded from it.
1811 See 'hg help dates' for a list of formats valid for -d/--date.
1814 See 'hg help dates' for a list of formats valid for -d/--date.
1812 """
1815 """
1813 patches = (patch1,) + patches
1816 patches = (patch1,) + patches
1814
1817
1815 date = opts.get('date')
1818 date = opts.get('date')
1816 if date:
1819 if date:
1817 opts['date'] = util.parsedate(date)
1820 opts['date'] = util.parsedate(date)
1818
1821
1819 try:
1822 try:
1820 sim = float(opts.get('similarity') or 0)
1823 sim = float(opts.get('similarity') or 0)
1821 except ValueError:
1824 except ValueError:
1822 raise util.Abort(_('similarity must be a number'))
1825 raise util.Abort(_('similarity must be a number'))
1823 if sim < 0 or sim > 100:
1826 if sim < 0 or sim > 100:
1824 raise util.Abort(_('similarity must be between 0 and 100'))
1827 raise util.Abort(_('similarity must be between 0 and 100'))
1825
1828
1826 if opts.get('exact') or not opts.get('force'):
1829 if opts.get('exact') or not opts.get('force'):
1827 cmdutil.bail_if_changed(repo)
1830 cmdutil.bail_if_changed(repo)
1828
1831
1829 d = opts["base"]
1832 d = opts["base"]
1830 strip = opts["strip"]
1833 strip = opts["strip"]
1831 wlock = lock = None
1834 wlock = lock = None
1832 try:
1835 try:
1833 wlock = repo.wlock()
1836 wlock = repo.wlock()
1834 lock = repo.lock()
1837 lock = repo.lock()
1835 for p in patches:
1838 for p in patches:
1836 pf = os.path.join(d, p)
1839 pf = os.path.join(d, p)
1837
1840
1838 if pf == '-':
1841 if pf == '-':
1839 ui.status(_("applying patch from stdin\n"))
1842 ui.status(_("applying patch from stdin\n"))
1840 pf = sys.stdin
1843 pf = sys.stdin
1841 else:
1844 else:
1842 ui.status(_("applying %s\n") % p)
1845 ui.status(_("applying %s\n") % p)
1843 pf = url.open(ui, pf)
1846 pf = url.open(ui, pf)
1844 data = patch.extract(ui, pf)
1847 data = patch.extract(ui, pf)
1845 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1848 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1846
1849
1847 if tmpname is None:
1850 if tmpname is None:
1848 raise util.Abort(_('no diffs found'))
1851 raise util.Abort(_('no diffs found'))
1849
1852
1850 try:
1853 try:
1851 cmdline_message = cmdutil.logmessage(opts)
1854 cmdline_message = cmdutil.logmessage(opts)
1852 if cmdline_message:
1855 if cmdline_message:
1853 # pickup the cmdline msg
1856 # pickup the cmdline msg
1854 message = cmdline_message
1857 message = cmdline_message
1855 elif message:
1858 elif message:
1856 # pickup the patch msg
1859 # pickup the patch msg
1857 message = message.strip()
1860 message = message.strip()
1858 else:
1861 else:
1859 # launch the editor
1862 # launch the editor
1860 message = None
1863 message = None
1861 ui.debug('message:\n%s\n' % message)
1864 ui.debug('message:\n%s\n' % message)
1862
1865
1863 wp = repo.parents()
1866 wp = repo.parents()
1864 if opts.get('exact'):
1867 if opts.get('exact'):
1865 if not nodeid or not p1:
1868 if not nodeid or not p1:
1866 raise util.Abort(_('not a Mercurial patch'))
1869 raise util.Abort(_('not a Mercurial patch'))
1867 p1 = repo.lookup(p1)
1870 p1 = repo.lookup(p1)
1868 p2 = repo.lookup(p2 or hex(nullid))
1871 p2 = repo.lookup(p2 or hex(nullid))
1869
1872
1870 if p1 != wp[0].node():
1873 if p1 != wp[0].node():
1871 hg.clean(repo, p1)
1874 hg.clean(repo, p1)
1872 repo.dirstate.setparents(p1, p2)
1875 repo.dirstate.setparents(p1, p2)
1873 elif p2:
1876 elif p2:
1874 try:
1877 try:
1875 p1 = repo.lookup(p1)
1878 p1 = repo.lookup(p1)
1876 p2 = repo.lookup(p2)
1879 p2 = repo.lookup(p2)
1877 if p1 == wp[0].node():
1880 if p1 == wp[0].node():
1878 repo.dirstate.setparents(p1, p2)
1881 repo.dirstate.setparents(p1, p2)
1879 except error.RepoError:
1882 except error.RepoError:
1880 pass
1883 pass
1881 if opts.get('exact') or opts.get('import_branch'):
1884 if opts.get('exact') or opts.get('import_branch'):
1882 repo.dirstate.setbranch(branch or 'default')
1885 repo.dirstate.setbranch(branch or 'default')
1883
1886
1884 files = {}
1887 files = {}
1885 try:
1888 try:
1886 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1889 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1887 files=files, eolmode=None)
1890 files=files, eolmode=None)
1888 finally:
1891 finally:
1889 files = patch.updatedir(ui, repo, files,
1892 files = patch.updatedir(ui, repo, files,
1890 similarity=sim / 100.0)
1893 similarity=sim / 100.0)
1891 if not opts.get('no_commit'):
1894 if not opts.get('no_commit'):
1892 m = cmdutil.matchfiles(repo, files or [])
1895 m = cmdutil.matchfiles(repo, files or [])
1893 n = repo.commit(message, opts.get('user') or user,
1896 n = repo.commit(message, opts.get('user') or user,
1894 opts.get('date') or date, match=m,
1897 opts.get('date') or date, match=m,
1895 editor=cmdutil.commiteditor)
1898 editor=cmdutil.commiteditor)
1896 if opts.get('exact'):
1899 if opts.get('exact'):
1897 if hex(n) != nodeid:
1900 if hex(n) != nodeid:
1898 repo.rollback()
1901 repo.rollback()
1899 raise util.Abort(_('patch is damaged'
1902 raise util.Abort(_('patch is damaged'
1900 ' or loses information'))
1903 ' or loses information'))
1901 # Force a dirstate write so that the next transaction
1904 # Force a dirstate write so that the next transaction
1902 # backups an up-do-date file.
1905 # backups an up-do-date file.
1903 repo.dirstate.write()
1906 repo.dirstate.write()
1904 finally:
1907 finally:
1905 os.unlink(tmpname)
1908 os.unlink(tmpname)
1906 finally:
1909 finally:
1907 release(lock, wlock)
1910 release(lock, wlock)
1908
1911
1909 def incoming(ui, repo, source="default", **opts):
1912 def incoming(ui, repo, source="default", **opts):
1910 """show new changesets found in source
1913 """show new changesets found in source
1911
1914
1912 Show new changesets found in the specified path/URL or the default
1915 Show new changesets found in the specified path/URL or the default
1913 pull location. These are the changesets that would have been pulled
1916 pull location. These are the changesets that would have been pulled
1914 if a pull at the time you issued this command.
1917 if a pull at the time you issued this command.
1915
1918
1916 For remote repository, using --bundle avoids downloading the
1919 For remote repository, using --bundle avoids downloading the
1917 changesets twice if the incoming is followed by a pull.
1920 changesets twice if the incoming is followed by a pull.
1918
1921
1919 See pull for valid source format details.
1922 See pull for valid source format details.
1920 """
1923 """
1921 limit = cmdutil.loglimit(opts)
1924 limit = cmdutil.loglimit(opts)
1922 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1925 source, branches = hg.parseurl(ui.expandpath(source))
1923 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1926 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1924 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1927 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1928 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
1925 if revs:
1929 if revs:
1926 revs = [other.lookup(rev) for rev in revs]
1930 revs = [other.lookup(rev) for rev in revs]
1927 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1931 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1928 force=opts["force"])
1932 force=opts["force"])
1929 if not incoming:
1933 if not incoming:
1930 try:
1934 try:
1931 os.unlink(opts["bundle"])
1935 os.unlink(opts["bundle"])
1932 except:
1936 except:
1933 pass
1937 pass
1934 ui.status(_("no changes found\n"))
1938 ui.status(_("no changes found\n"))
1935 return 1
1939 return 1
1936
1940
1937 cleanup = None
1941 cleanup = None
1938 try:
1942 try:
1939 fname = opts["bundle"]
1943 fname = opts["bundle"]
1940 if fname or not other.local():
1944 if fname or not other.local():
1941 # create a bundle (uncompressed if other repo is not local)
1945 # create a bundle (uncompressed if other repo is not local)
1942
1946
1943 if revs is None and other.capable('changegroupsubset'):
1947 if revs is None and other.capable('changegroupsubset'):
1944 revs = rheads
1948 revs = rheads
1945
1949
1946 if revs is None:
1950 if revs is None:
1947 cg = other.changegroup(incoming, "incoming")
1951 cg = other.changegroup(incoming, "incoming")
1948 else:
1952 else:
1949 cg = other.changegroupsubset(incoming, revs, 'incoming')
1953 cg = other.changegroupsubset(incoming, revs, 'incoming')
1950 bundletype = other.local() and "HG10BZ" or "HG10UN"
1954 bundletype = other.local() and "HG10BZ" or "HG10UN"
1951 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1955 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1952 # keep written bundle?
1956 # keep written bundle?
1953 if opts["bundle"]:
1957 if opts["bundle"]:
1954 cleanup = None
1958 cleanup = None
1955 if not other.local():
1959 if not other.local():
1956 # use the created uncompressed bundlerepo
1960 # use the created uncompressed bundlerepo
1957 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1961 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1958
1962
1959 o = other.changelog.nodesbetween(incoming, revs)[0]
1963 o = other.changelog.nodesbetween(incoming, revs)[0]
1960 if opts.get('newest_first'):
1964 if opts.get('newest_first'):
1961 o.reverse()
1965 o.reverse()
1962 displayer = cmdutil.show_changeset(ui, other, opts)
1966 displayer = cmdutil.show_changeset(ui, other, opts)
1963 count = 0
1967 count = 0
1964 for n in o:
1968 for n in o:
1965 if limit is not None and count >= limit:
1969 if limit is not None and count >= limit:
1966 break
1970 break
1967 parents = [p for p in other.changelog.parents(n) if p != nullid]
1971 parents = [p for p in other.changelog.parents(n) if p != nullid]
1968 if opts.get('no_merges') and len(parents) == 2:
1972 if opts.get('no_merges') and len(parents) == 2:
1969 continue
1973 continue
1970 count += 1
1974 count += 1
1971 displayer.show(other[n])
1975 displayer.show(other[n])
1972 displayer.close()
1976 displayer.close()
1973 finally:
1977 finally:
1974 if hasattr(other, 'close'):
1978 if hasattr(other, 'close'):
1975 other.close()
1979 other.close()
1976 if cleanup:
1980 if cleanup:
1977 os.unlink(cleanup)
1981 os.unlink(cleanup)
1978
1982
1979 def init(ui, dest=".", **opts):
1983 def init(ui, dest=".", **opts):
1980 """create a new repository in the given directory
1984 """create a new repository in the given directory
1981
1985
1982 Initialize a new repository in the given directory. If the given
1986 Initialize a new repository in the given directory. If the given
1983 directory does not exist, it will be created.
1987 directory does not exist, it will be created.
1984
1988
1985 If no directory is given, the current directory is used.
1989 If no directory is given, the current directory is used.
1986
1990
1987 It is possible to specify an ``ssh://`` URL as the destination.
1991 It is possible to specify an ``ssh://`` URL as the destination.
1988 See 'hg help urls' for more information.
1992 See 'hg help urls' for more information.
1989 """
1993 """
1990 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1994 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1991
1995
1992 def locate(ui, repo, *pats, **opts):
1996 def locate(ui, repo, *pats, **opts):
1993 """locate files matching specific patterns
1997 """locate files matching specific patterns
1994
1998
1995 Print files under Mercurial control in the working directory whose
1999 Print files under Mercurial control in the working directory whose
1996 names match the given patterns.
2000 names match the given patterns.
1997
2001
1998 By default, this command searches all directories in the working
2002 By default, this command searches all directories in the working
1999 directory. To search just the current directory and its
2003 directory. To search just the current directory and its
2000 subdirectories, use "--include .".
2004 subdirectories, use "--include .".
2001
2005
2002 If no patterns are given to match, this command prints the names
2006 If no patterns are given to match, this command prints the names
2003 of all files under Mercurial control in the working directory.
2007 of all files under Mercurial control in the working directory.
2004
2008
2005 If you want to feed the output of this command into the "xargs"
2009 If you want to feed the output of this command into the "xargs"
2006 command, use the -0 option to both this command and "xargs". This
2010 command, use the -0 option to both this command and "xargs". This
2007 will avoid the problem of "xargs" treating single filenames that
2011 will avoid the problem of "xargs" treating single filenames that
2008 contain whitespace as multiple filenames.
2012 contain whitespace as multiple filenames.
2009 """
2013 """
2010 end = opts.get('print0') and '\0' or '\n'
2014 end = opts.get('print0') and '\0' or '\n'
2011 rev = opts.get('rev') or None
2015 rev = opts.get('rev') or None
2012
2016
2013 ret = 1
2017 ret = 1
2014 m = cmdutil.match(repo, pats, opts, default='relglob')
2018 m = cmdutil.match(repo, pats, opts, default='relglob')
2015 m.bad = lambda x, y: False
2019 m.bad = lambda x, y: False
2016 for abs in repo[rev].walk(m):
2020 for abs in repo[rev].walk(m):
2017 if not rev and abs not in repo.dirstate:
2021 if not rev and abs not in repo.dirstate:
2018 continue
2022 continue
2019 if opts.get('fullpath'):
2023 if opts.get('fullpath'):
2020 ui.write(repo.wjoin(abs), end)
2024 ui.write(repo.wjoin(abs), end)
2021 else:
2025 else:
2022 ui.write(((pats and m.rel(abs)) or abs), end)
2026 ui.write(((pats and m.rel(abs)) or abs), end)
2023 ret = 0
2027 ret = 0
2024
2028
2025 return ret
2029 return ret
2026
2030
2027 def log(ui, repo, *pats, **opts):
2031 def log(ui, repo, *pats, **opts):
2028 """show revision history of entire repository or files
2032 """show revision history of entire repository or files
2029
2033
2030 Print the revision history of the specified files or the entire
2034 Print the revision history of the specified files or the entire
2031 project.
2035 project.
2032
2036
2033 File history is shown without following rename or copy history of
2037 File history is shown without following rename or copy history of
2034 files. Use -f/--follow with a filename to follow history across
2038 files. Use -f/--follow with a filename to follow history across
2035 renames and copies. --follow without a filename will only show
2039 renames and copies. --follow without a filename will only show
2036 ancestors or descendants of the starting revision. --follow-first
2040 ancestors or descendants of the starting revision. --follow-first
2037 only follows the first parent of merge revisions.
2041 only follows the first parent of merge revisions.
2038
2042
2039 If no revision range is specified, the default is tip:0 unless
2043 If no revision range is specified, the default is tip:0 unless
2040 --follow is set, in which case the working directory parent is
2044 --follow is set, in which case the working directory parent is
2041 used as the starting revision.
2045 used as the starting revision.
2042
2046
2043 See 'hg help dates' for a list of formats valid for -d/--date.
2047 See 'hg help dates' for a list of formats valid for -d/--date.
2044
2048
2045 By default this command prints revision number and changeset id,
2049 By default this command prints revision number and changeset id,
2046 tags, non-trivial parents, user, date and time, and a summary for
2050 tags, non-trivial parents, user, date and time, and a summary for
2047 each commit. When the -v/--verbose switch is used, the list of
2051 each commit. When the -v/--verbose switch is used, the list of
2048 changed files and full commit message are shown.
2052 changed files and full commit message are shown.
2049
2053
2050 NOTE: log -p/--patch may generate unexpected diff output for merge
2054 NOTE: log -p/--patch may generate unexpected diff output for merge
2051 changesets, as it will only compare the merge changeset against
2055 changesets, as it will only compare the merge changeset against
2052 its first parent. Also, only files different from BOTH parents
2056 its first parent. Also, only files different from BOTH parents
2053 will appear in files:.
2057 will appear in files:.
2054 """
2058 """
2055
2059
2056 matchfn = cmdutil.match(repo, pats, opts)
2060 matchfn = cmdutil.match(repo, pats, opts)
2057 limit = cmdutil.loglimit(opts)
2061 limit = cmdutil.loglimit(opts)
2058 count = 0
2062 count = 0
2059
2063
2060 endrev = None
2064 endrev = None
2061 if opts.get('copies') and opts.get('rev'):
2065 if opts.get('copies') and opts.get('rev'):
2062 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2066 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2063
2067
2064 df = False
2068 df = False
2065 if opts["date"]:
2069 if opts["date"]:
2066 df = util.matchdate(opts["date"])
2070 df = util.matchdate(opts["date"])
2067
2071
2068 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2072 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2069 def prep(ctx, fns):
2073 def prep(ctx, fns):
2070 rev = ctx.rev()
2074 rev = ctx.rev()
2071 parents = [p for p in repo.changelog.parentrevs(rev)
2075 parents = [p for p in repo.changelog.parentrevs(rev)
2072 if p != nullrev]
2076 if p != nullrev]
2073 if opts.get('no_merges') and len(parents) == 2:
2077 if opts.get('no_merges') and len(parents) == 2:
2074 return
2078 return
2075 if opts.get('only_merges') and len(parents) != 2:
2079 if opts.get('only_merges') and len(parents) != 2:
2076 return
2080 return
2077 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2081 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2078 return
2082 return
2079 if df and not df(ctx.date()[0]):
2083 if df and not df(ctx.date()[0]):
2080 return
2084 return
2081 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2085 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2082 return
2086 return
2083 if opts.get('keyword'):
2087 if opts.get('keyword'):
2084 for k in [kw.lower() for kw in opts['keyword']]:
2088 for k in [kw.lower() for kw in opts['keyword']]:
2085 if (k in ctx.user().lower() or
2089 if (k in ctx.user().lower() or
2086 k in ctx.description().lower() or
2090 k in ctx.description().lower() or
2087 k in " ".join(ctx.files()).lower()):
2091 k in " ".join(ctx.files()).lower()):
2088 break
2092 break
2089 else:
2093 else:
2090 return
2094 return
2091
2095
2092 copies = None
2096 copies = None
2093 if opts.get('copies') and rev:
2097 if opts.get('copies') and rev:
2094 copies = []
2098 copies = []
2095 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2099 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2096 for fn in ctx.files():
2100 for fn in ctx.files():
2097 rename = getrenamed(fn, rev)
2101 rename = getrenamed(fn, rev)
2098 if rename:
2102 if rename:
2099 copies.append((fn, rename[0]))
2103 copies.append((fn, rename[0]))
2100
2104
2101 displayer.show(ctx, copies=copies)
2105 displayer.show(ctx, copies=copies)
2102
2106
2103 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2107 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2104 if count == limit:
2108 if count == limit:
2105 break
2109 break
2106 if displayer.flush(ctx.rev()):
2110 if displayer.flush(ctx.rev()):
2107 count += 1
2111 count += 1
2108 displayer.close()
2112 displayer.close()
2109
2113
2110 def manifest(ui, repo, node=None, rev=None):
2114 def manifest(ui, repo, node=None, rev=None):
2111 """output the current or given revision of the project manifest
2115 """output the current or given revision of the project manifest
2112
2116
2113 Print a list of version controlled files for the given revision.
2117 Print a list of version controlled files for the given revision.
2114 If no revision is given, the first parent of the working directory
2118 If no revision is given, the first parent of the working directory
2115 is used, or the null revision if no revision is checked out.
2119 is used, or the null revision if no revision is checked out.
2116
2120
2117 With -v, print file permissions, symlink and executable bits.
2121 With -v, print file permissions, symlink and executable bits.
2118 With --debug, print file revision hashes.
2122 With --debug, print file revision hashes.
2119 """
2123 """
2120
2124
2121 if rev and node:
2125 if rev and node:
2122 raise util.Abort(_("please specify just one revision"))
2126 raise util.Abort(_("please specify just one revision"))
2123
2127
2124 if not node:
2128 if not node:
2125 node = rev
2129 node = rev
2126
2130
2127 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2131 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2128 ctx = repo[node]
2132 ctx = repo[node]
2129 for f in ctx:
2133 for f in ctx:
2130 if ui.debugflag:
2134 if ui.debugflag:
2131 ui.write("%40s " % hex(ctx.manifest()[f]))
2135 ui.write("%40s " % hex(ctx.manifest()[f]))
2132 if ui.verbose:
2136 if ui.verbose:
2133 ui.write(decor[ctx.flags(f)])
2137 ui.write(decor[ctx.flags(f)])
2134 ui.write("%s\n" % f)
2138 ui.write("%s\n" % f)
2135
2139
2136 def merge(ui, repo, node=None, **opts):
2140 def merge(ui, repo, node=None, **opts):
2137 """merge working directory with another revision
2141 """merge working directory with another revision
2138
2142
2139 The current working directory is updated with all changes made in
2143 The current working directory is updated with all changes made in
2140 the requested revision since the last common predecessor revision.
2144 the requested revision since the last common predecessor revision.
2141
2145
2142 Files that changed between either parent are marked as changed for
2146 Files that changed between either parent are marked as changed for
2143 the next commit and a commit must be performed before any further
2147 the next commit and a commit must be performed before any further
2144 updates to the repository are allowed. The next commit will have
2148 updates to the repository are allowed. The next commit will have
2145 two parents.
2149 two parents.
2146
2150
2147 If no revision is specified, the working directory's parent is a
2151 If no revision is specified, the working directory's parent is a
2148 head revision, and the current branch contains exactly one other
2152 head revision, and the current branch contains exactly one other
2149 head, the other head is merged with by default. Otherwise, an
2153 head, the other head is merged with by default. Otherwise, an
2150 explicit revision with which to merge with must be provided.
2154 explicit revision with which to merge with must be provided.
2151 """
2155 """
2152
2156
2153 if opts.get('rev') and node:
2157 if opts.get('rev') and node:
2154 raise util.Abort(_("please specify just one revision"))
2158 raise util.Abort(_("please specify just one revision"))
2155 if not node:
2159 if not node:
2156 node = opts.get('rev')
2160 node = opts.get('rev')
2157
2161
2158 if not node:
2162 if not node:
2159 branch = repo.changectx(None).branch()
2163 branch = repo.changectx(None).branch()
2160 bheads = repo.branchheads(branch)
2164 bheads = repo.branchheads(branch)
2161 if len(bheads) > 2:
2165 if len(bheads) > 2:
2162 ui.warn(_("abort: branch '%s' has %d heads - "
2166 ui.warn(_("abort: branch '%s' has %d heads - "
2163 "please merge with an explicit rev\n")
2167 "please merge with an explicit rev\n")
2164 % (branch, len(bheads)))
2168 % (branch, len(bheads)))
2165 ui.status(_("(run 'hg heads .' to see heads)\n"))
2169 ui.status(_("(run 'hg heads .' to see heads)\n"))
2166 return False
2170 return False
2167
2171
2168 parent = repo.dirstate.parents()[0]
2172 parent = repo.dirstate.parents()[0]
2169 if len(bheads) == 1:
2173 if len(bheads) == 1:
2170 if len(repo.heads()) > 1:
2174 if len(repo.heads()) > 1:
2171 ui.warn(_("abort: branch '%s' has one head - "
2175 ui.warn(_("abort: branch '%s' has one head - "
2172 "please merge with an explicit rev\n" % branch))
2176 "please merge with an explicit rev\n" % branch))
2173 ui.status(_("(run 'hg heads' to see all heads)\n"))
2177 ui.status(_("(run 'hg heads' to see all heads)\n"))
2174 return False
2178 return False
2175 msg = _('there is nothing to merge')
2179 msg = _('there is nothing to merge')
2176 if parent != repo.lookup(repo[None].branch()):
2180 if parent != repo.lookup(repo[None].branch()):
2177 msg = _('%s - use "hg update" instead') % msg
2181 msg = _('%s - use "hg update" instead') % msg
2178 raise util.Abort(msg)
2182 raise util.Abort(msg)
2179
2183
2180 if parent not in bheads:
2184 if parent not in bheads:
2181 raise util.Abort(_('working dir not at a head rev - '
2185 raise util.Abort(_('working dir not at a head rev - '
2182 'use "hg update" or merge with an explicit rev'))
2186 'use "hg update" or merge with an explicit rev'))
2183 node = parent == bheads[0] and bheads[-1] or bheads[0]
2187 node = parent == bheads[0] and bheads[-1] or bheads[0]
2184
2188
2185 if opts.get('preview'):
2189 if opts.get('preview'):
2186 p1 = repo['.']
2190 p1 = repo['.']
2187 p2 = repo[node]
2191 p2 = repo[node]
2188 common = p1.ancestor(p2)
2192 common = p1.ancestor(p2)
2189 roots, heads = [common.node()], [p2.node()]
2193 roots, heads = [common.node()], [p2.node()]
2190 displayer = cmdutil.show_changeset(ui, repo, opts)
2194 displayer = cmdutil.show_changeset(ui, repo, opts)
2191 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2195 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2192 if node not in roots:
2196 if node not in roots:
2193 displayer.show(repo[node])
2197 displayer.show(repo[node])
2194 displayer.close()
2198 displayer.close()
2195 return 0
2199 return 0
2196
2200
2197 return hg.merge(repo, node, force=opts.get('force'))
2201 return hg.merge(repo, node, force=opts.get('force'))
2198
2202
2199 def outgoing(ui, repo, dest=None, **opts):
2203 def outgoing(ui, repo, dest=None, **opts):
2200 """show changesets not found in destination
2204 """show changesets not found in destination
2201
2205
2202 Show changesets not found in the specified destination repository
2206 Show changesets not found in the specified destination repository
2203 or the default push location. These are the changesets that would
2207 or the default push location. These are the changesets that would
2204 be pushed if a push was requested.
2208 be pushed if a push was requested.
2205
2209
2206 See pull for valid destination format details.
2210 See pull for valid destination format details.
2207 """
2211 """
2208 limit = cmdutil.loglimit(opts)
2212 limit = cmdutil.loglimit(opts)
2209 dest, revs, checkout = hg.parseurl(
2213 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2210 ui.expandpath(dest or 'default-push', dest or 'default'),
2214 dest, branches = hg.parseurl(dest)
2211 opts.get('rev'))
2215 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2212 if revs:
2216 if revs:
2213 revs = [repo.lookup(rev) for rev in revs]
2217 revs = [repo.lookup(rev) for rev in revs]
2214
2218
2215 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2219 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2216 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2220 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2217 o = repo.findoutgoing(other, force=opts.get('force'))
2221 o = repo.findoutgoing(other, force=opts.get('force'))
2218 if not o:
2222 if not o:
2219 ui.status(_("no changes found\n"))
2223 ui.status(_("no changes found\n"))
2220 return 1
2224 return 1
2221 o = repo.changelog.nodesbetween(o, revs)[0]
2225 o = repo.changelog.nodesbetween(o, revs)[0]
2222 if opts.get('newest_first'):
2226 if opts.get('newest_first'):
2223 o.reverse()
2227 o.reverse()
2224 displayer = cmdutil.show_changeset(ui, repo, opts)
2228 displayer = cmdutil.show_changeset(ui, repo, opts)
2225 count = 0
2229 count = 0
2226 for n in o:
2230 for n in o:
2227 if limit is not None and count >= limit:
2231 if limit is not None and count >= limit:
2228 break
2232 break
2229 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2233 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2230 if opts.get('no_merges') and len(parents) == 2:
2234 if opts.get('no_merges') and len(parents) == 2:
2231 continue
2235 continue
2232 count += 1
2236 count += 1
2233 displayer.show(repo[n])
2237 displayer.show(repo[n])
2234 displayer.close()
2238 displayer.close()
2235
2239
2236 def parents(ui, repo, file_=None, **opts):
2240 def parents(ui, repo, file_=None, **opts):
2237 """show the parents of the working directory or revision
2241 """show the parents of the working directory or revision
2238
2242
2239 Print the working directory's parent revisions. If a revision is
2243 Print the working directory's parent revisions. If a revision is
2240 given via -r/--rev, the parent of that revision will be printed.
2244 given via -r/--rev, the parent of that revision will be printed.
2241 If a file argument is given, the revision in which the file was
2245 If a file argument is given, the revision in which the file was
2242 last changed (before the working directory revision or the
2246 last changed (before the working directory revision or the
2243 argument to --rev if given) is printed.
2247 argument to --rev if given) is printed.
2244 """
2248 """
2245 rev = opts.get('rev')
2249 rev = opts.get('rev')
2246 if rev:
2250 if rev:
2247 ctx = repo[rev]
2251 ctx = repo[rev]
2248 else:
2252 else:
2249 ctx = repo[None]
2253 ctx = repo[None]
2250
2254
2251 if file_:
2255 if file_:
2252 m = cmdutil.match(repo, (file_,), opts)
2256 m = cmdutil.match(repo, (file_,), opts)
2253 if m.anypats() or len(m.files()) != 1:
2257 if m.anypats() or len(m.files()) != 1:
2254 raise util.Abort(_('can only specify an explicit filename'))
2258 raise util.Abort(_('can only specify an explicit filename'))
2255 file_ = m.files()[0]
2259 file_ = m.files()[0]
2256 filenodes = []
2260 filenodes = []
2257 for cp in ctx.parents():
2261 for cp in ctx.parents():
2258 if not cp:
2262 if not cp:
2259 continue
2263 continue
2260 try:
2264 try:
2261 filenodes.append(cp.filenode(file_))
2265 filenodes.append(cp.filenode(file_))
2262 except error.LookupError:
2266 except error.LookupError:
2263 pass
2267 pass
2264 if not filenodes:
2268 if not filenodes:
2265 raise util.Abort(_("'%s' not found in manifest!") % file_)
2269 raise util.Abort(_("'%s' not found in manifest!") % file_)
2266 fl = repo.file(file_)
2270 fl = repo.file(file_)
2267 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2271 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2268 else:
2272 else:
2269 p = [cp.node() for cp in ctx.parents()]
2273 p = [cp.node() for cp in ctx.parents()]
2270
2274
2271 displayer = cmdutil.show_changeset(ui, repo, opts)
2275 displayer = cmdutil.show_changeset(ui, repo, opts)
2272 for n in p:
2276 for n in p:
2273 if n != nullid:
2277 if n != nullid:
2274 displayer.show(repo[n])
2278 displayer.show(repo[n])
2275 displayer.close()
2279 displayer.close()
2276
2280
2277 def paths(ui, repo, search=None):
2281 def paths(ui, repo, search=None):
2278 """show aliases for remote repositories
2282 """show aliases for remote repositories
2279
2283
2280 Show definition of symbolic path name NAME. If no name is given,
2284 Show definition of symbolic path name NAME. If no name is given,
2281 show definition of all available names.
2285 show definition of all available names.
2282
2286
2283 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2287 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2284 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2288 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2285
2289
2286 See 'hg help urls' for more information.
2290 See 'hg help urls' for more information.
2287 """
2291 """
2288 if search:
2292 if search:
2289 for name, path in ui.configitems("paths"):
2293 for name, path in ui.configitems("paths"):
2290 if name == search:
2294 if name == search:
2291 ui.write("%s\n" % url.hidepassword(path))
2295 ui.write("%s\n" % url.hidepassword(path))
2292 return
2296 return
2293 ui.warn(_("not found!\n"))
2297 ui.warn(_("not found!\n"))
2294 return 1
2298 return 1
2295 else:
2299 else:
2296 for name, path in ui.configitems("paths"):
2300 for name, path in ui.configitems("paths"):
2297 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2301 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2298
2302
2299 def postincoming(ui, repo, modheads, optupdate, checkout):
2303 def postincoming(ui, repo, modheads, optupdate, checkout):
2300 if modheads == 0:
2304 if modheads == 0:
2301 return
2305 return
2302 if optupdate:
2306 if optupdate:
2303 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2307 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2304 return hg.update(repo, checkout)
2308 return hg.update(repo, checkout)
2305 else:
2309 else:
2306 ui.status(_("not updating, since new heads added\n"))
2310 ui.status(_("not updating, since new heads added\n"))
2307 if modheads > 1:
2311 if modheads > 1:
2308 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2312 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2309 else:
2313 else:
2310 ui.status(_("(run 'hg update' to get a working copy)\n"))
2314 ui.status(_("(run 'hg update' to get a working copy)\n"))
2311
2315
2312 def pull(ui, repo, source="default", **opts):
2316 def pull(ui, repo, source="default", **opts):
2313 """pull changes from the specified source
2317 """pull changes from the specified source
2314
2318
2315 Pull changes from a remote repository to a local one.
2319 Pull changes from a remote repository to a local one.
2316
2320
2317 This finds all changes from the repository at the specified path
2321 This finds all changes from the repository at the specified path
2318 or URL and adds them to a local repository (the current one unless
2322 or URL and adds them to a local repository (the current one unless
2319 -R is specified). By default, this does not update the copy of the
2323 -R is specified). By default, this does not update the copy of the
2320 project in the working directory.
2324 project in the working directory.
2321
2325
2322 Use hg incoming if you want to see what would have been added by a
2326 Use hg incoming if you want to see what would have been added by a
2323 pull at the time you issued this command. If you then decide to
2327 pull at the time you issued this command. If you then decide to
2324 added those changes to the repository, you should use pull -r X
2328 added those changes to the repository, you should use pull -r X
2325 where X is the last changeset listed by hg incoming.
2329 where X is the last changeset listed by hg incoming.
2326
2330
2327 If SOURCE is omitted, the 'default' path will be used.
2331 If SOURCE is omitted, the 'default' path will be used.
2328 See 'hg help urls' for more information.
2332 See 'hg help urls' for more information.
2329 """
2333 """
2330 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2334 source, branches = hg.parseurl(ui.expandpath(source))
2331 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2335 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2332 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2336 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2337 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2333 if revs:
2338 if revs:
2334 try:
2339 try:
2335 revs = [other.lookup(rev) for rev in revs]
2340 revs = [other.lookup(rev) for rev in revs]
2336 except error.CapabilityError:
2341 except error.CapabilityError:
2337 err = _("Other repository doesn't support revision lookup, "
2342 err = _("Other repository doesn't support revision lookup, "
2338 "so a rev cannot be specified.")
2343 "so a rev cannot be specified.")
2339 raise util.Abort(err)
2344 raise util.Abort(err)
2340
2345
2341 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2346 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2342 if checkout:
2347 if checkout:
2343 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2348 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2344 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2349 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2345
2350
2346 def push(ui, repo, dest=None, **opts):
2351 def push(ui, repo, dest=None, **opts):
2347 """push changes to the specified destination
2352 """push changes to the specified destination
2348
2353
2349 Push changes from the local repository to the given destination.
2354 Push changes from the local repository to the given destination.
2350
2355
2351 This is the symmetrical operation for pull. It moves changes from
2356 This is the symmetrical operation for pull. It moves changes from
2352 the current repository to a different one. If the destination is
2357 the current repository to a different one. If the destination is
2353 local this is identical to a pull in that directory from the
2358 local this is identical to a pull in that directory from the
2354 current one.
2359 current one.
2355
2360
2356 By default, push will refuse to run if it detects the result would
2361 By default, push will refuse to run if it detects the result would
2357 increase the number of remote heads. This generally indicates the
2362 increase the number of remote heads. This generally indicates the
2358 user forgot to pull and merge before pushing.
2363 user forgot to pull and merge before pushing.
2359
2364
2360 If -r/--rev is used, the named revision and all its ancestors will
2365 If -r/--rev is used, the named revision and all its ancestors will
2361 be pushed to the remote repository.
2366 be pushed to the remote repository.
2362
2367
2363 Please see 'hg help urls' for important details about ``ssh://``
2368 Please see 'hg help urls' for important details about ``ssh://``
2364 URLs. If DESTINATION is omitted, a default path will be used.
2369 URLs. If DESTINATION is omitted, a default path will be used.
2365 """
2370 """
2366 dest, revs, checkout = hg.parseurl(
2371 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2367 ui.expandpath(dest or 'default-push', dest or 'default'),
2372 dest, branches = hg.parseurl(dest)
2368 opts.get('rev'))
2373 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2369 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2374 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2370 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2375 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2371 if revs:
2376 if revs:
2372 revs = [repo.lookup(rev) for rev in revs]
2377 revs = [repo.lookup(rev) for rev in revs]
2373
2378
2374 # push subrepos depth-first for coherent ordering
2379 # push subrepos depth-first for coherent ordering
2375 c = repo['']
2380 c = repo['']
2376 subs = c.substate # only repos that are committed
2381 subs = c.substate # only repos that are committed
2377 for s in sorted(subs):
2382 for s in sorted(subs):
2378 c.sub(s).push(opts.get('force'))
2383 c.sub(s).push(opts.get('force'))
2379
2384
2380 r = repo.push(other, opts.get('force'), revs=revs)
2385 r = repo.push(other, opts.get('force'), revs=revs)
2381 return r == 0
2386 return r == 0
2382
2387
2383 def recover(ui, repo):
2388 def recover(ui, repo):
2384 """roll back an interrupted transaction
2389 """roll back an interrupted transaction
2385
2390
2386 Recover from an interrupted commit or pull.
2391 Recover from an interrupted commit or pull.
2387
2392
2388 This command tries to fix the repository status after an
2393 This command tries to fix the repository status after an
2389 interrupted operation. It should only be necessary when Mercurial
2394 interrupted operation. It should only be necessary when Mercurial
2390 suggests it.
2395 suggests it.
2391 """
2396 """
2392 if repo.recover():
2397 if repo.recover():
2393 return hg.verify(repo)
2398 return hg.verify(repo)
2394 return 1
2399 return 1
2395
2400
2396 def remove(ui, repo, *pats, **opts):
2401 def remove(ui, repo, *pats, **opts):
2397 """remove the specified files on the next commit
2402 """remove the specified files on the next commit
2398
2403
2399 Schedule the indicated files for removal from the repository.
2404 Schedule the indicated files for removal from the repository.
2400
2405
2401 This only removes files from the current branch, not from the
2406 This only removes files from the current branch, not from the
2402 entire project history. -A/--after can be used to remove only
2407 entire project history. -A/--after can be used to remove only
2403 files that have already been deleted, -f/--force can be used to
2408 files that have already been deleted, -f/--force can be used to
2404 force deletion, and -Af can be used to remove files from the next
2409 force deletion, and -Af can be used to remove files from the next
2405 revision without deleting them from the working directory.
2410 revision without deleting them from the working directory.
2406
2411
2407 The following table details the behavior of remove for different
2412 The following table details the behavior of remove for different
2408 file states (columns) and option combinations (rows). The file
2413 file states (columns) and option combinations (rows). The file
2409 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2414 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2410 reported by hg status). The actions are Warn, Remove (from branch)
2415 reported by hg status). The actions are Warn, Remove (from branch)
2411 and Delete (from disk)::
2416 and Delete (from disk)::
2412
2417
2413 A C M !
2418 A C M !
2414 none W RD W R
2419 none W RD W R
2415 -f R RD RD R
2420 -f R RD RD R
2416 -A W W W R
2421 -A W W W R
2417 -Af R R R R
2422 -Af R R R R
2418
2423
2419 This command schedules the files to be removed at the next commit.
2424 This command schedules the files to be removed at the next commit.
2420 To undo a remove before that, see hg revert.
2425 To undo a remove before that, see hg revert.
2421 """
2426 """
2422
2427
2423 after, force = opts.get('after'), opts.get('force')
2428 after, force = opts.get('after'), opts.get('force')
2424 if not pats and not after:
2429 if not pats and not after:
2425 raise util.Abort(_('no files specified'))
2430 raise util.Abort(_('no files specified'))
2426
2431
2427 m = cmdutil.match(repo, pats, opts)
2432 m = cmdutil.match(repo, pats, opts)
2428 s = repo.status(match=m, clean=True)
2433 s = repo.status(match=m, clean=True)
2429 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2434 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2430
2435
2431 for f in m.files():
2436 for f in m.files():
2432 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2437 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2433 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2438 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2434
2439
2435 def warn(files, reason):
2440 def warn(files, reason):
2436 for f in files:
2441 for f in files:
2437 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2442 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2438 % (m.rel(f), reason))
2443 % (m.rel(f), reason))
2439
2444
2440 if force:
2445 if force:
2441 remove, forget = modified + deleted + clean, added
2446 remove, forget = modified + deleted + clean, added
2442 elif after:
2447 elif after:
2443 remove, forget = deleted, []
2448 remove, forget = deleted, []
2444 warn(modified + added + clean, _('still exists'))
2449 warn(modified + added + clean, _('still exists'))
2445 else:
2450 else:
2446 remove, forget = deleted + clean, []
2451 remove, forget = deleted + clean, []
2447 warn(modified, _('is modified'))
2452 warn(modified, _('is modified'))
2448 warn(added, _('has been marked for add'))
2453 warn(added, _('has been marked for add'))
2449
2454
2450 for f in sorted(remove + forget):
2455 for f in sorted(remove + forget):
2451 if ui.verbose or not m.exact(f):
2456 if ui.verbose or not m.exact(f):
2452 ui.status(_('removing %s\n') % m.rel(f))
2457 ui.status(_('removing %s\n') % m.rel(f))
2453
2458
2454 repo.forget(forget)
2459 repo.forget(forget)
2455 repo.remove(remove, unlink=not after)
2460 repo.remove(remove, unlink=not after)
2456
2461
2457 def rename(ui, repo, *pats, **opts):
2462 def rename(ui, repo, *pats, **opts):
2458 """rename files; equivalent of copy + remove
2463 """rename files; equivalent of copy + remove
2459
2464
2460 Mark dest as copies of sources; mark sources for deletion. If dest
2465 Mark dest as copies of sources; mark sources for deletion. If dest
2461 is a directory, copies are put in that directory. If dest is a
2466 is a directory, copies are put in that directory. If dest is a
2462 file, there can only be one source.
2467 file, there can only be one source.
2463
2468
2464 By default, this command copies the contents of files as they
2469 By default, this command copies the contents of files as they
2465 exist in the working directory. If invoked with -A/--after, the
2470 exist in the working directory. If invoked with -A/--after, the
2466 operation is recorded, but no copying is performed.
2471 operation is recorded, but no copying is performed.
2467
2472
2468 This command takes effect at the next commit. To undo a rename
2473 This command takes effect at the next commit. To undo a rename
2469 before that, see hg revert.
2474 before that, see hg revert.
2470 """
2475 """
2471 wlock = repo.wlock(False)
2476 wlock = repo.wlock(False)
2472 try:
2477 try:
2473 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2478 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2474 finally:
2479 finally:
2475 wlock.release()
2480 wlock.release()
2476
2481
2477 def resolve(ui, repo, *pats, **opts):
2482 def resolve(ui, repo, *pats, **opts):
2478 """retry file merges from a merge or update
2483 """retry file merges from a merge or update
2479
2484
2480 This command can cleanly retry unresolved file merges using file
2485 This command can cleanly retry unresolved file merges using file
2481 revisions preserved from the last update or merge.
2486 revisions preserved from the last update or merge.
2482
2487
2483 If a conflict is resolved manually, please note that the changes
2488 If a conflict is resolved manually, please note that the changes
2484 will be overwritten if the merge is retried with resolve. The
2489 will be overwritten if the merge is retried with resolve. The
2485 -m/--mark switch should be used to mark the file as resolved.
2490 -m/--mark switch should be used to mark the file as resolved.
2486
2491
2487 You can specify a set of files to operate on, or use the -a/--all
2492 You can specify a set of files to operate on, or use the -a/--all
2488 switch to select all unresolved files.
2493 switch to select all unresolved files.
2489
2494
2490 This command also allows listing resolved files and manually
2495 This command also allows listing resolved files and manually
2491 indicating whether or not files are resolved. All files must be
2496 indicating whether or not files are resolved. All files must be
2492 marked as resolved before a commit is permitted.
2497 marked as resolved before a commit is permitted.
2493
2498
2494 The codes used to show the status of files are::
2499 The codes used to show the status of files are::
2495
2500
2496 U = unresolved
2501 U = unresolved
2497 R = resolved
2502 R = resolved
2498 """
2503 """
2499
2504
2500 all, mark, unmark, show, nostatus = \
2505 all, mark, unmark, show, nostatus = \
2501 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2506 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2502
2507
2503 if (show and (mark or unmark)) or (mark and unmark):
2508 if (show and (mark or unmark)) or (mark and unmark):
2504 raise util.Abort(_("too many options specified"))
2509 raise util.Abort(_("too many options specified"))
2505 if pats and all:
2510 if pats and all:
2506 raise util.Abort(_("can't specify --all and patterns"))
2511 raise util.Abort(_("can't specify --all and patterns"))
2507 if not (all or pats or show or mark or unmark):
2512 if not (all or pats or show or mark or unmark):
2508 raise util.Abort(_('no files or directories specified; '
2513 raise util.Abort(_('no files or directories specified; '
2509 'use --all to remerge all files'))
2514 'use --all to remerge all files'))
2510
2515
2511 ms = merge_.mergestate(repo)
2516 ms = merge_.mergestate(repo)
2512 m = cmdutil.match(repo, pats, opts)
2517 m = cmdutil.match(repo, pats, opts)
2513
2518
2514 for f in ms:
2519 for f in ms:
2515 if m(f):
2520 if m(f):
2516 if show:
2521 if show:
2517 if nostatus:
2522 if nostatus:
2518 ui.write("%s\n" % f)
2523 ui.write("%s\n" % f)
2519 else:
2524 else:
2520 ui.write("%s %s\n" % (ms[f].upper(), f))
2525 ui.write("%s %s\n" % (ms[f].upper(), f))
2521 elif mark:
2526 elif mark:
2522 ms.mark(f, "r")
2527 ms.mark(f, "r")
2523 elif unmark:
2528 elif unmark:
2524 ms.mark(f, "u")
2529 ms.mark(f, "u")
2525 else:
2530 else:
2526 wctx = repo[None]
2531 wctx = repo[None]
2527 mctx = wctx.parents()[-1]
2532 mctx = wctx.parents()[-1]
2528
2533
2529 # backup pre-resolve (merge uses .orig for its own purposes)
2534 # backup pre-resolve (merge uses .orig for its own purposes)
2530 a = repo.wjoin(f)
2535 a = repo.wjoin(f)
2531 util.copyfile(a, a + ".resolve")
2536 util.copyfile(a, a + ".resolve")
2532
2537
2533 # resolve file
2538 # resolve file
2534 ms.resolve(f, wctx, mctx)
2539 ms.resolve(f, wctx, mctx)
2535
2540
2536 # replace filemerge's .orig file with our resolve file
2541 # replace filemerge's .orig file with our resolve file
2537 util.rename(a + ".resolve", a + ".orig")
2542 util.rename(a + ".resolve", a + ".orig")
2538
2543
2539 def revert(ui, repo, *pats, **opts):
2544 def revert(ui, repo, *pats, **opts):
2540 """restore individual files or directories to an earlier state
2545 """restore individual files or directories to an earlier state
2541
2546
2542 (Use update -r to check out earlier revisions, revert does not
2547 (Use update -r to check out earlier revisions, revert does not
2543 change the working directory parents.)
2548 change the working directory parents.)
2544
2549
2545 With no revision specified, revert the named files or directories
2550 With no revision specified, revert the named files or directories
2546 to the contents they had in the parent of the working directory.
2551 to the contents they had in the parent of the working directory.
2547 This restores the contents of the affected files to an unmodified
2552 This restores the contents of the affected files to an unmodified
2548 state and unschedules adds, removes, copies, and renames. If the
2553 state and unschedules adds, removes, copies, and renames. If the
2549 working directory has two parents, you must explicitly specify the
2554 working directory has two parents, you must explicitly specify the
2550 revision to revert to.
2555 revision to revert to.
2551
2556
2552 Using the -r/--rev option, revert the given files or directories
2557 Using the -r/--rev option, revert the given files or directories
2553 to their contents as of a specific revision. This can be helpful
2558 to their contents as of a specific revision. This can be helpful
2554 to "roll back" some or all of an earlier change. See 'hg help
2559 to "roll back" some or all of an earlier change. See 'hg help
2555 dates' for a list of formats valid for -d/--date.
2560 dates' for a list of formats valid for -d/--date.
2556
2561
2557 Revert modifies the working directory. It does not commit any
2562 Revert modifies the working directory. It does not commit any
2558 changes, or change the parent of the working directory. If you
2563 changes, or change the parent of the working directory. If you
2559 revert to a revision other than the parent of the working
2564 revert to a revision other than the parent of the working
2560 directory, the reverted files will thus appear modified
2565 directory, the reverted files will thus appear modified
2561 afterwards.
2566 afterwards.
2562
2567
2563 If a file has been deleted, it is restored. If the executable mode
2568 If a file has been deleted, it is restored. If the executable mode
2564 of a file was changed, it is reset.
2569 of a file was changed, it is reset.
2565
2570
2566 If names are given, all files matching the names are reverted.
2571 If names are given, all files matching the names are reverted.
2567 If no arguments are given, no files are reverted.
2572 If no arguments are given, no files are reverted.
2568
2573
2569 Modified files are saved with a .orig suffix before reverting.
2574 Modified files are saved with a .orig suffix before reverting.
2570 To disable these backups, use --no-backup.
2575 To disable these backups, use --no-backup.
2571 """
2576 """
2572
2577
2573 if opts["date"]:
2578 if opts["date"]:
2574 if opts["rev"]:
2579 if opts["rev"]:
2575 raise util.Abort(_("you can't specify a revision and a date"))
2580 raise util.Abort(_("you can't specify a revision and a date"))
2576 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2581 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2577
2582
2578 if not pats and not opts.get('all'):
2583 if not pats and not opts.get('all'):
2579 raise util.Abort(_('no files or directories specified; '
2584 raise util.Abort(_('no files or directories specified; '
2580 'use --all to revert the whole repo'))
2585 'use --all to revert the whole repo'))
2581
2586
2582 parent, p2 = repo.dirstate.parents()
2587 parent, p2 = repo.dirstate.parents()
2583 if not opts.get('rev') and p2 != nullid:
2588 if not opts.get('rev') and p2 != nullid:
2584 raise util.Abort(_('uncommitted merge - please provide a '
2589 raise util.Abort(_('uncommitted merge - please provide a '
2585 'specific revision'))
2590 'specific revision'))
2586 ctx = repo[opts.get('rev')]
2591 ctx = repo[opts.get('rev')]
2587 node = ctx.node()
2592 node = ctx.node()
2588 mf = ctx.manifest()
2593 mf = ctx.manifest()
2589 if node == parent:
2594 if node == parent:
2590 pmf = mf
2595 pmf = mf
2591 else:
2596 else:
2592 pmf = None
2597 pmf = None
2593
2598
2594 # need all matching names in dirstate and manifest of target rev,
2599 # need all matching names in dirstate and manifest of target rev,
2595 # so have to walk both. do not print errors if files exist in one
2600 # so have to walk both. do not print errors if files exist in one
2596 # but not other.
2601 # but not other.
2597
2602
2598 names = {}
2603 names = {}
2599
2604
2600 wlock = repo.wlock()
2605 wlock = repo.wlock()
2601 try:
2606 try:
2602 # walk dirstate.
2607 # walk dirstate.
2603
2608
2604 m = cmdutil.match(repo, pats, opts)
2609 m = cmdutil.match(repo, pats, opts)
2605 m.bad = lambda x, y: False
2610 m.bad = lambda x, y: False
2606 for abs in repo.walk(m):
2611 for abs in repo.walk(m):
2607 names[abs] = m.rel(abs), m.exact(abs)
2612 names[abs] = m.rel(abs), m.exact(abs)
2608
2613
2609 # walk target manifest.
2614 # walk target manifest.
2610
2615
2611 def badfn(path, msg):
2616 def badfn(path, msg):
2612 if path in names:
2617 if path in names:
2613 return
2618 return
2614 path_ = path + '/'
2619 path_ = path + '/'
2615 for f in names:
2620 for f in names:
2616 if f.startswith(path_):
2621 if f.startswith(path_):
2617 return
2622 return
2618 ui.warn("%s: %s\n" % (m.rel(path), msg))
2623 ui.warn("%s: %s\n" % (m.rel(path), msg))
2619
2624
2620 m = cmdutil.match(repo, pats, opts)
2625 m = cmdutil.match(repo, pats, opts)
2621 m.bad = badfn
2626 m.bad = badfn
2622 for abs in repo[node].walk(m):
2627 for abs in repo[node].walk(m):
2623 if abs not in names:
2628 if abs not in names:
2624 names[abs] = m.rel(abs), m.exact(abs)
2629 names[abs] = m.rel(abs), m.exact(abs)
2625
2630
2626 m = cmdutil.matchfiles(repo, names)
2631 m = cmdutil.matchfiles(repo, names)
2627 changes = repo.status(match=m)[:4]
2632 changes = repo.status(match=m)[:4]
2628 modified, added, removed, deleted = map(set, changes)
2633 modified, added, removed, deleted = map(set, changes)
2629
2634
2630 # if f is a rename, also revert the source
2635 # if f is a rename, also revert the source
2631 cwd = repo.getcwd()
2636 cwd = repo.getcwd()
2632 for f in added:
2637 for f in added:
2633 src = repo.dirstate.copied(f)
2638 src = repo.dirstate.copied(f)
2634 if src and src not in names and repo.dirstate[src] == 'r':
2639 if src and src not in names and repo.dirstate[src] == 'r':
2635 removed.add(src)
2640 removed.add(src)
2636 names[src] = (repo.pathto(src, cwd), True)
2641 names[src] = (repo.pathto(src, cwd), True)
2637
2642
2638 def removeforget(abs):
2643 def removeforget(abs):
2639 if repo.dirstate[abs] == 'a':
2644 if repo.dirstate[abs] == 'a':
2640 return _('forgetting %s\n')
2645 return _('forgetting %s\n')
2641 return _('removing %s\n')
2646 return _('removing %s\n')
2642
2647
2643 revert = ([], _('reverting %s\n'))
2648 revert = ([], _('reverting %s\n'))
2644 add = ([], _('adding %s\n'))
2649 add = ([], _('adding %s\n'))
2645 remove = ([], removeforget)
2650 remove = ([], removeforget)
2646 undelete = ([], _('undeleting %s\n'))
2651 undelete = ([], _('undeleting %s\n'))
2647
2652
2648 disptable = (
2653 disptable = (
2649 # dispatch table:
2654 # dispatch table:
2650 # file state
2655 # file state
2651 # action if in target manifest
2656 # action if in target manifest
2652 # action if not in target manifest
2657 # action if not in target manifest
2653 # make backup if in target manifest
2658 # make backup if in target manifest
2654 # make backup if not in target manifest
2659 # make backup if not in target manifest
2655 (modified, revert, remove, True, True),
2660 (modified, revert, remove, True, True),
2656 (added, revert, remove, True, False),
2661 (added, revert, remove, True, False),
2657 (removed, undelete, None, False, False),
2662 (removed, undelete, None, False, False),
2658 (deleted, revert, remove, False, False),
2663 (deleted, revert, remove, False, False),
2659 )
2664 )
2660
2665
2661 for abs, (rel, exact) in sorted(names.items()):
2666 for abs, (rel, exact) in sorted(names.items()):
2662 mfentry = mf.get(abs)
2667 mfentry = mf.get(abs)
2663 target = repo.wjoin(abs)
2668 target = repo.wjoin(abs)
2664 def handle(xlist, dobackup):
2669 def handle(xlist, dobackup):
2665 xlist[0].append(abs)
2670 xlist[0].append(abs)
2666 if dobackup and not opts.get('no_backup') and util.lexists(target):
2671 if dobackup and not opts.get('no_backup') and util.lexists(target):
2667 bakname = "%s.orig" % rel
2672 bakname = "%s.orig" % rel
2668 ui.note(_('saving current version of %s as %s\n') %
2673 ui.note(_('saving current version of %s as %s\n') %
2669 (rel, bakname))
2674 (rel, bakname))
2670 if not opts.get('dry_run'):
2675 if not opts.get('dry_run'):
2671 util.copyfile(target, bakname)
2676 util.copyfile(target, bakname)
2672 if ui.verbose or not exact:
2677 if ui.verbose or not exact:
2673 msg = xlist[1]
2678 msg = xlist[1]
2674 if not isinstance(msg, basestring):
2679 if not isinstance(msg, basestring):
2675 msg = msg(abs)
2680 msg = msg(abs)
2676 ui.status(msg % rel)
2681 ui.status(msg % rel)
2677 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2682 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2678 if abs not in table:
2683 if abs not in table:
2679 continue
2684 continue
2680 # file has changed in dirstate
2685 # file has changed in dirstate
2681 if mfentry:
2686 if mfentry:
2682 handle(hitlist, backuphit)
2687 handle(hitlist, backuphit)
2683 elif misslist is not None:
2688 elif misslist is not None:
2684 handle(misslist, backupmiss)
2689 handle(misslist, backupmiss)
2685 break
2690 break
2686 else:
2691 else:
2687 if abs not in repo.dirstate:
2692 if abs not in repo.dirstate:
2688 if mfentry:
2693 if mfentry:
2689 handle(add, True)
2694 handle(add, True)
2690 elif exact:
2695 elif exact:
2691 ui.warn(_('file not managed: %s\n') % rel)
2696 ui.warn(_('file not managed: %s\n') % rel)
2692 continue
2697 continue
2693 # file has not changed in dirstate
2698 # file has not changed in dirstate
2694 if node == parent:
2699 if node == parent:
2695 if exact:
2700 if exact:
2696 ui.warn(_('no changes needed to %s\n') % rel)
2701 ui.warn(_('no changes needed to %s\n') % rel)
2697 continue
2702 continue
2698 if pmf is None:
2703 if pmf is None:
2699 # only need parent manifest in this unlikely case,
2704 # only need parent manifest in this unlikely case,
2700 # so do not read by default
2705 # so do not read by default
2701 pmf = repo[parent].manifest()
2706 pmf = repo[parent].manifest()
2702 if abs in pmf:
2707 if abs in pmf:
2703 if mfentry:
2708 if mfentry:
2704 # if version of file is same in parent and target
2709 # if version of file is same in parent and target
2705 # manifests, do nothing
2710 # manifests, do nothing
2706 if (pmf[abs] != mfentry or
2711 if (pmf[abs] != mfentry or
2707 pmf.flags(abs) != mf.flags(abs)):
2712 pmf.flags(abs) != mf.flags(abs)):
2708 handle(revert, False)
2713 handle(revert, False)
2709 else:
2714 else:
2710 handle(remove, False)
2715 handle(remove, False)
2711
2716
2712 if not opts.get('dry_run'):
2717 if not opts.get('dry_run'):
2713 def checkout(f):
2718 def checkout(f):
2714 fc = ctx[f]
2719 fc = ctx[f]
2715 repo.wwrite(f, fc.data(), fc.flags())
2720 repo.wwrite(f, fc.data(), fc.flags())
2716
2721
2717 audit_path = util.path_auditor(repo.root)
2722 audit_path = util.path_auditor(repo.root)
2718 for f in remove[0]:
2723 for f in remove[0]:
2719 if repo.dirstate[f] == 'a':
2724 if repo.dirstate[f] == 'a':
2720 repo.dirstate.forget(f)
2725 repo.dirstate.forget(f)
2721 continue
2726 continue
2722 audit_path(f)
2727 audit_path(f)
2723 try:
2728 try:
2724 util.unlink(repo.wjoin(f))
2729 util.unlink(repo.wjoin(f))
2725 except OSError:
2730 except OSError:
2726 pass
2731 pass
2727 repo.dirstate.remove(f)
2732 repo.dirstate.remove(f)
2728
2733
2729 normal = None
2734 normal = None
2730 if node == parent:
2735 if node == parent:
2731 # We're reverting to our parent. If possible, we'd like status
2736 # We're reverting to our parent. If possible, we'd like status
2732 # to report the file as clean. We have to use normallookup for
2737 # to report the file as clean. We have to use normallookup for
2733 # merges to avoid losing information about merged/dirty files.
2738 # merges to avoid losing information about merged/dirty files.
2734 if p2 != nullid:
2739 if p2 != nullid:
2735 normal = repo.dirstate.normallookup
2740 normal = repo.dirstate.normallookup
2736 else:
2741 else:
2737 normal = repo.dirstate.normal
2742 normal = repo.dirstate.normal
2738 for f in revert[0]:
2743 for f in revert[0]:
2739 checkout(f)
2744 checkout(f)
2740 if normal:
2745 if normal:
2741 normal(f)
2746 normal(f)
2742
2747
2743 for f in add[0]:
2748 for f in add[0]:
2744 checkout(f)
2749 checkout(f)
2745 repo.dirstate.add(f)
2750 repo.dirstate.add(f)
2746
2751
2747 normal = repo.dirstate.normallookup
2752 normal = repo.dirstate.normallookup
2748 if node == parent and p2 == nullid:
2753 if node == parent and p2 == nullid:
2749 normal = repo.dirstate.normal
2754 normal = repo.dirstate.normal
2750 for f in undelete[0]:
2755 for f in undelete[0]:
2751 checkout(f)
2756 checkout(f)
2752 normal(f)
2757 normal(f)
2753
2758
2754 finally:
2759 finally:
2755 wlock.release()
2760 wlock.release()
2756
2761
2757 def rollback(ui, repo):
2762 def rollback(ui, repo):
2758 """roll back the last transaction
2763 """roll back the last transaction
2759
2764
2760 This command should be used with care. There is only one level of
2765 This command should be used with care. There is only one level of
2761 rollback, and there is no way to undo a rollback. It will also
2766 rollback, and there is no way to undo a rollback. It will also
2762 restore the dirstate at the time of the last transaction, losing
2767 restore the dirstate at the time of the last transaction, losing
2763 any dirstate changes since that time. This command does not alter
2768 any dirstate changes since that time. This command does not alter
2764 the working directory.
2769 the working directory.
2765
2770
2766 Transactions are used to encapsulate the effects of all commands
2771 Transactions are used to encapsulate the effects of all commands
2767 that create new changesets or propagate existing changesets into a
2772 that create new changesets or propagate existing changesets into a
2768 repository. For example, the following commands are transactional,
2773 repository. For example, the following commands are transactional,
2769 and their effects can be rolled back:
2774 and their effects can be rolled back:
2770
2775
2771 - commit
2776 - commit
2772 - import
2777 - import
2773 - pull
2778 - pull
2774 - push (with this repository as destination)
2779 - push (with this repository as destination)
2775 - unbundle
2780 - unbundle
2776
2781
2777 This command is not intended for use on public repositories. Once
2782 This command is not intended for use on public repositories. Once
2778 changes are visible for pull by other users, rolling a transaction
2783 changes are visible for pull by other users, rolling a transaction
2779 back locally is ineffective (someone else may already have pulled
2784 back locally is ineffective (someone else may already have pulled
2780 the changes). Furthermore, a race is possible with readers of the
2785 the changes). Furthermore, a race is possible with readers of the
2781 repository; for example an in-progress pull from the repository
2786 repository; for example an in-progress pull from the repository
2782 may fail if a rollback is performed.
2787 may fail if a rollback is performed.
2783 """
2788 """
2784 repo.rollback()
2789 repo.rollback()
2785
2790
2786 def root(ui, repo):
2791 def root(ui, repo):
2787 """print the root (top) of the current working directory
2792 """print the root (top) of the current working directory
2788
2793
2789 Print the root directory of the current repository.
2794 Print the root directory of the current repository.
2790 """
2795 """
2791 ui.write(repo.root + "\n")
2796 ui.write(repo.root + "\n")
2792
2797
2793 def serve(ui, repo, **opts):
2798 def serve(ui, repo, **opts):
2794 """export the repository via HTTP
2799 """export the repository via HTTP
2795
2800
2796 Start a local HTTP repository browser and pull server.
2801 Start a local HTTP repository browser and pull server.
2797
2802
2798 By default, the server logs accesses to stdout and errors to
2803 By default, the server logs accesses to stdout and errors to
2799 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2804 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2800 files.
2805 files.
2801 """
2806 """
2802
2807
2803 if opts["stdio"]:
2808 if opts["stdio"]:
2804 if repo is None:
2809 if repo is None:
2805 raise error.RepoError(_("There is no Mercurial repository here"
2810 raise error.RepoError(_("There is no Mercurial repository here"
2806 " (.hg not found)"))
2811 " (.hg not found)"))
2807 s = sshserver.sshserver(ui, repo)
2812 s = sshserver.sshserver(ui, repo)
2808 s.serve_forever()
2813 s.serve_forever()
2809
2814
2810 baseui = repo and repo.baseui or ui
2815 baseui = repo and repo.baseui or ui
2811 optlist = ("name templates style address port prefix ipv6"
2816 optlist = ("name templates style address port prefix ipv6"
2812 " accesslog errorlog webdir_conf certificate encoding")
2817 " accesslog errorlog webdir_conf certificate encoding")
2813 for o in optlist.split():
2818 for o in optlist.split():
2814 if opts.get(o, None):
2819 if opts.get(o, None):
2815 baseui.setconfig("web", o, str(opts[o]))
2820 baseui.setconfig("web", o, str(opts[o]))
2816 if (repo is not None) and (repo.ui != baseui):
2821 if (repo is not None) and (repo.ui != baseui):
2817 repo.ui.setconfig("web", o, str(opts[o]))
2822 repo.ui.setconfig("web", o, str(opts[o]))
2818
2823
2819 if repo is None and not ui.config("web", "webdir_conf"):
2824 if repo is None and not ui.config("web", "webdir_conf"):
2820 raise error.RepoError(_("There is no Mercurial repository here"
2825 raise error.RepoError(_("There is no Mercurial repository here"
2821 " (.hg not found)"))
2826 " (.hg not found)"))
2822
2827
2823 class service(object):
2828 class service(object):
2824 def init(self):
2829 def init(self):
2825 util.set_signal_handler()
2830 util.set_signal_handler()
2826 self.httpd = server.create_server(baseui, repo)
2831 self.httpd = server.create_server(baseui, repo)
2827
2832
2828 if not ui.verbose:
2833 if not ui.verbose:
2829 return
2834 return
2830
2835
2831 if self.httpd.prefix:
2836 if self.httpd.prefix:
2832 prefix = self.httpd.prefix.strip('/') + '/'
2837 prefix = self.httpd.prefix.strip('/') + '/'
2833 else:
2838 else:
2834 prefix = ''
2839 prefix = ''
2835
2840
2836 port = ':%d' % self.httpd.port
2841 port = ':%d' % self.httpd.port
2837 if port == ':80':
2842 if port == ':80':
2838 port = ''
2843 port = ''
2839
2844
2840 bindaddr = self.httpd.addr
2845 bindaddr = self.httpd.addr
2841 if bindaddr == '0.0.0.0':
2846 if bindaddr == '0.0.0.0':
2842 bindaddr = '*'
2847 bindaddr = '*'
2843 elif ':' in bindaddr: # IPv6
2848 elif ':' in bindaddr: # IPv6
2844 bindaddr = '[%s]' % bindaddr
2849 bindaddr = '[%s]' % bindaddr
2845
2850
2846 fqaddr = self.httpd.fqaddr
2851 fqaddr = self.httpd.fqaddr
2847 if ':' in fqaddr:
2852 if ':' in fqaddr:
2848 fqaddr = '[%s]' % fqaddr
2853 fqaddr = '[%s]' % fqaddr
2849 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2854 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2850 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2855 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2851
2856
2852 def run(self):
2857 def run(self):
2853 self.httpd.serve_forever()
2858 self.httpd.serve_forever()
2854
2859
2855 service = service()
2860 service = service()
2856
2861
2857 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2862 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2858
2863
2859 def status(ui, repo, *pats, **opts):
2864 def status(ui, repo, *pats, **opts):
2860 """show changed files in the working directory
2865 """show changed files in the working directory
2861
2866
2862 Show status of files in the repository. If names are given, only
2867 Show status of files in the repository. If names are given, only
2863 files that match are shown. Files that are clean or ignored or
2868 files that match are shown. Files that are clean or ignored or
2864 the source of a copy/move operation, are not listed unless
2869 the source of a copy/move operation, are not listed unless
2865 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2870 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2866 Unless options described with "show only ..." are given, the
2871 Unless options described with "show only ..." are given, the
2867 options -mardu are used.
2872 options -mardu are used.
2868
2873
2869 Option -q/--quiet hides untracked (unknown and ignored) files
2874 Option -q/--quiet hides untracked (unknown and ignored) files
2870 unless explicitly requested with -u/--unknown or -i/--ignored.
2875 unless explicitly requested with -u/--unknown or -i/--ignored.
2871
2876
2872 NOTE: status may appear to disagree with diff if permissions have
2877 NOTE: status may appear to disagree with diff if permissions have
2873 changed or a merge has occurred. The standard diff format does not
2878 changed or a merge has occurred. The standard diff format does not
2874 report permission changes and diff only reports changes relative
2879 report permission changes and diff only reports changes relative
2875 to one merge parent.
2880 to one merge parent.
2876
2881
2877 If one revision is given, it is used as the base revision.
2882 If one revision is given, it is used as the base revision.
2878 If two revisions are given, the differences between them are
2883 If two revisions are given, the differences between them are
2879 shown. The --change option can also be used as a shortcut to list
2884 shown. The --change option can also be used as a shortcut to list
2880 the changed files of a revision from its first parent.
2885 the changed files of a revision from its first parent.
2881
2886
2882 The codes used to show the status of files are::
2887 The codes used to show the status of files are::
2883
2888
2884 M = modified
2889 M = modified
2885 A = added
2890 A = added
2886 R = removed
2891 R = removed
2887 C = clean
2892 C = clean
2888 ! = missing (deleted by non-hg command, but still tracked)
2893 ! = missing (deleted by non-hg command, but still tracked)
2889 ? = not tracked
2894 ? = not tracked
2890 I = ignored
2895 I = ignored
2891 = origin of the previous file listed as A (added)
2896 = origin of the previous file listed as A (added)
2892 """
2897 """
2893
2898
2894 revs = opts.get('rev')
2899 revs = opts.get('rev')
2895 change = opts.get('change')
2900 change = opts.get('change')
2896
2901
2897 if revs and change:
2902 if revs and change:
2898 msg = _('cannot specify --rev and --change at the same time')
2903 msg = _('cannot specify --rev and --change at the same time')
2899 raise util.Abort(msg)
2904 raise util.Abort(msg)
2900 elif change:
2905 elif change:
2901 node2 = repo.lookup(change)
2906 node2 = repo.lookup(change)
2902 node1 = repo[node2].parents()[0].node()
2907 node1 = repo[node2].parents()[0].node()
2903 else:
2908 else:
2904 node1, node2 = cmdutil.revpair(repo, revs)
2909 node1, node2 = cmdutil.revpair(repo, revs)
2905
2910
2906 cwd = (pats and repo.getcwd()) or ''
2911 cwd = (pats and repo.getcwd()) or ''
2907 end = opts.get('print0') and '\0' or '\n'
2912 end = opts.get('print0') and '\0' or '\n'
2908 copy = {}
2913 copy = {}
2909 states = 'modified added removed deleted unknown ignored clean'.split()
2914 states = 'modified added removed deleted unknown ignored clean'.split()
2910 show = [k for k in states if opts.get(k)]
2915 show = [k for k in states if opts.get(k)]
2911 if opts.get('all'):
2916 if opts.get('all'):
2912 show += ui.quiet and (states[:4] + ['clean']) or states
2917 show += ui.quiet and (states[:4] + ['clean']) or states
2913 if not show:
2918 if not show:
2914 show = ui.quiet and states[:4] or states[:5]
2919 show = ui.quiet and states[:4] or states[:5]
2915
2920
2916 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2921 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2917 'ignored' in show, 'clean' in show, 'unknown' in show)
2922 'ignored' in show, 'clean' in show, 'unknown' in show)
2918 changestates = zip(states, 'MAR!?IC', stat)
2923 changestates = zip(states, 'MAR!?IC', stat)
2919
2924
2920 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2925 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2921 ctxn = repo[nullid]
2926 ctxn = repo[nullid]
2922 ctx1 = repo[node1]
2927 ctx1 = repo[node1]
2923 ctx2 = repo[node2]
2928 ctx2 = repo[node2]
2924 added = stat[1]
2929 added = stat[1]
2925 if node2 is None:
2930 if node2 is None:
2926 added = stat[0] + stat[1] # merged?
2931 added = stat[0] + stat[1] # merged?
2927
2932
2928 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2933 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2929 if k in added:
2934 if k in added:
2930 copy[k] = v
2935 copy[k] = v
2931 elif v in added:
2936 elif v in added:
2932 copy[v] = k
2937 copy[v] = k
2933
2938
2934 for state, char, files in changestates:
2939 for state, char, files in changestates:
2935 if state in show:
2940 if state in show:
2936 format = "%s %%s%s" % (char, end)
2941 format = "%s %%s%s" % (char, end)
2937 if opts.get('no_status'):
2942 if opts.get('no_status'):
2938 format = "%%s%s" % end
2943 format = "%%s%s" % end
2939
2944
2940 for f in files:
2945 for f in files:
2941 ui.write(format % repo.pathto(f, cwd))
2946 ui.write(format % repo.pathto(f, cwd))
2942 if f in copy:
2947 if f in copy:
2943 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2948 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2944
2949
2945 def summary(ui, repo, **opts):
2950 def summary(ui, repo, **opts):
2946 """summarize working directory state
2951 """summarize working directory state
2947
2952
2948 This generates a brief summary of the working directory state,
2953 This generates a brief summary of the working directory state,
2949 including parents, branch, commit status, and available updates.
2954 including parents, branch, commit status, and available updates.
2950
2955
2951 With the --remote option, this will check the default paths for
2956 With the --remote option, this will check the default paths for
2952 incoming and outgoing changes. This can be time-consuming.
2957 incoming and outgoing changes. This can be time-consuming.
2953 """
2958 """
2954
2959
2955 ctx = repo[None]
2960 ctx = repo[None]
2956 parents = ctx.parents()
2961 parents = ctx.parents()
2957 pnode = parents[0].node()
2962 pnode = parents[0].node()
2958 tags = repo.tags()
2963 tags = repo.tags()
2959
2964
2960 for p in parents:
2965 for p in parents:
2961 t = ' '.join([t for t in tags if tags[t] == p.node()])
2966 t = ' '.join([t for t in tags if tags[t] == p.node()])
2962 if p.rev() == -1:
2967 if p.rev() == -1:
2963 if not len(repo):
2968 if not len(repo):
2964 t += _(' (empty repository)')
2969 t += _(' (empty repository)')
2965 else:
2970 else:
2966 t += _(' (no revision checked out)')
2971 t += _(' (no revision checked out)')
2967 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2972 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2968 if p.description():
2973 if p.description():
2969 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2974 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2970
2975
2971 branch = ctx.branch()
2976 branch = ctx.branch()
2972 bheads = repo.branchheads(branch)
2977 bheads = repo.branchheads(branch)
2973 m = _('branch: %s\n') % branch
2978 m = _('branch: %s\n') % branch
2974 if branch != 'default':
2979 if branch != 'default':
2975 ui.write(m)
2980 ui.write(m)
2976 else:
2981 else:
2977 ui.status(m)
2982 ui.status(m)
2978
2983
2979 st = list(repo.status(unknown=True))[:7]
2984 st = list(repo.status(unknown=True))[:7]
2980 ms = merge_.mergestate(repo)
2985 ms = merge_.mergestate(repo)
2981 st.append([f for f in ms if f == 'u'])
2986 st.append([f for f in ms if f == 'u'])
2982 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2987 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2983 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2988 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2984 _('%d unresolved')]
2989 _('%d unresolved')]
2985 t = []
2990 t = []
2986 for s, l in zip(st, labels):
2991 for s, l in zip(st, labels):
2987 if s:
2992 if s:
2988 t.append(l % len(s))
2993 t.append(l % len(s))
2989
2994
2990 t = ', '.join(t)
2995 t = ', '.join(t)
2991 cleanworkdir = False
2996 cleanworkdir = False
2992
2997
2993 if len(parents) > 1:
2998 if len(parents) > 1:
2994 t += _(' (merge)')
2999 t += _(' (merge)')
2995 elif branch != parents[0].branch():
3000 elif branch != parents[0].branch():
2996 t += _(' (new branch)')
3001 t += _(' (new branch)')
2997 elif (not st[0] and not st[1] and not st[2]):
3002 elif (not st[0] and not st[1] and not st[2]):
2998 t += _(' (clean)')
3003 t += _(' (clean)')
2999 cleanworkdir = True
3004 cleanworkdir = True
3000 elif pnode not in bheads:
3005 elif pnode not in bheads:
3001 t += _(' (new branch head)')
3006 t += _(' (new branch head)')
3002
3007
3003 if cleanworkdir:
3008 if cleanworkdir:
3004 ui.status(_('commit: %s\n') % t.strip())
3009 ui.status(_('commit: %s\n') % t.strip())
3005 else:
3010 else:
3006 ui.write(_('commit: %s\n') % t.strip())
3011 ui.write(_('commit: %s\n') % t.strip())
3007
3012
3008 # all ancestors of branch heads - all ancestors of parent = new csets
3013 # all ancestors of branch heads - all ancestors of parent = new csets
3009 new = [0] * len(repo)
3014 new = [0] * len(repo)
3010 cl = repo.changelog
3015 cl = repo.changelog
3011 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3016 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3012 new[a] = 1
3017 new[a] = 1
3013 for a in cl.ancestors(*[p.rev() for p in parents]):
3018 for a in cl.ancestors(*[p.rev() for p in parents]):
3014 new[a] = 0
3019 new[a] = 0
3015 new = sum(new)
3020 new = sum(new)
3016
3021
3017 if new == 0:
3022 if new == 0:
3018 ui.status(_('update: (current)\n'))
3023 ui.status(_('update: (current)\n'))
3019 elif pnode not in bheads:
3024 elif pnode not in bheads:
3020 ui.write(_('update: %d new changesets (update)\n') % new)
3025 ui.write(_('update: %d new changesets (update)\n') % new)
3021 else:
3026 else:
3022 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3027 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3023 (new, len(bheads)))
3028 (new, len(bheads)))
3024
3029
3025 if opts.get('remote'):
3030 if opts.get('remote'):
3026 t = []
3031 t = []
3027 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
3032 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
3028 opts.get('rev'))
3033 opts.get('rev'))
3029 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3034 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3030 ui.debug('comparing with %s\n' % url.hidepassword(source))
3035 ui.debug('comparing with %s\n' % url.hidepassword(source))
3031 repo.ui.pushbuffer()
3036 repo.ui.pushbuffer()
3032 common, incoming, rheads = repo.findcommonincoming(other)
3037 common, incoming, rheads = repo.findcommonincoming(other)
3033 repo.ui.popbuffer()
3038 repo.ui.popbuffer()
3034 if incoming:
3039 if incoming:
3035 t.append(_('1 or more incoming'))
3040 t.append(_('1 or more incoming'))
3036
3041
3037 dest, revs, checkout = hg.parseurl(
3042 dest, revs, checkout = hg.parseurl(
3038 ui.expandpath('default-push', 'default'))
3043 ui.expandpath('default-push', 'default'))
3039 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3044 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3040 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3045 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3041 repo.ui.pushbuffer()
3046 repo.ui.pushbuffer()
3042 o = repo.findoutgoing(other)
3047 o = repo.findoutgoing(other)
3043 repo.ui.popbuffer()
3048 repo.ui.popbuffer()
3044 o = repo.changelog.nodesbetween(o, revs)[0]
3049 o = repo.changelog.nodesbetween(o, revs)[0]
3045 if o:
3050 if o:
3046 t.append(_('%d outgoing') % len(o))
3051 t.append(_('%d outgoing') % len(o))
3047
3052
3048 if t:
3053 if t:
3049 ui.write(_('remote: %s\n') % (', '.join(t)))
3054 ui.write(_('remote: %s\n') % (', '.join(t)))
3050 else:
3055 else:
3051 ui.status(_('remote: (synced)\n'))
3056 ui.status(_('remote: (synced)\n'))
3052
3057
3053 def tag(ui, repo, name1, *names, **opts):
3058 def tag(ui, repo, name1, *names, **opts):
3054 """add one or more tags for the current or given revision
3059 """add one or more tags for the current or given revision
3055
3060
3056 Name a particular revision using <name>.
3061 Name a particular revision using <name>.
3057
3062
3058 Tags are used to name particular revisions of the repository and are
3063 Tags are used to name particular revisions of the repository and are
3059 very useful to compare different revisions, to go back to significant
3064 very useful to compare different revisions, to go back to significant
3060 earlier versions or to mark branch points as releases, etc.
3065 earlier versions or to mark branch points as releases, etc.
3061
3066
3062 If no revision is given, the parent of the working directory is
3067 If no revision is given, the parent of the working directory is
3063 used, or tip if no revision is checked out.
3068 used, or tip if no revision is checked out.
3064
3069
3065 To facilitate version control, distribution, and merging of tags,
3070 To facilitate version control, distribution, and merging of tags,
3066 they are stored as a file named ".hgtags" which is managed
3071 they are stored as a file named ".hgtags" which is managed
3067 similarly to other project files and can be hand-edited if
3072 similarly to other project files and can be hand-edited if
3068 necessary. The file '.hg/localtags' is used for local tags (not
3073 necessary. The file '.hg/localtags' is used for local tags (not
3069 shared among repositories).
3074 shared among repositories).
3070
3075
3071 See 'hg help dates' for a list of formats valid for -d/--date.
3076 See 'hg help dates' for a list of formats valid for -d/--date.
3072 """
3077 """
3073
3078
3074 rev_ = "."
3079 rev_ = "."
3075 names = (name1,) + names
3080 names = (name1,) + names
3076 if len(names) != len(set(names)):
3081 if len(names) != len(set(names)):
3077 raise util.Abort(_('tag names must be unique'))
3082 raise util.Abort(_('tag names must be unique'))
3078 for n in names:
3083 for n in names:
3079 if n in ['tip', '.', 'null']:
3084 if n in ['tip', '.', 'null']:
3080 raise util.Abort(_('the name \'%s\' is reserved') % n)
3085 raise util.Abort(_('the name \'%s\' is reserved') % n)
3081 if opts.get('rev') and opts.get('remove'):
3086 if opts.get('rev') and opts.get('remove'):
3082 raise util.Abort(_("--rev and --remove are incompatible"))
3087 raise util.Abort(_("--rev and --remove are incompatible"))
3083 if opts.get('rev'):
3088 if opts.get('rev'):
3084 rev_ = opts['rev']
3089 rev_ = opts['rev']
3085 message = opts.get('message')
3090 message = opts.get('message')
3086 if opts.get('remove'):
3091 if opts.get('remove'):
3087 expectedtype = opts.get('local') and 'local' or 'global'
3092 expectedtype = opts.get('local') and 'local' or 'global'
3088 for n in names:
3093 for n in names:
3089 if not repo.tagtype(n):
3094 if not repo.tagtype(n):
3090 raise util.Abort(_('tag \'%s\' does not exist') % n)
3095 raise util.Abort(_('tag \'%s\' does not exist') % n)
3091 if repo.tagtype(n) != expectedtype:
3096 if repo.tagtype(n) != expectedtype:
3092 if expectedtype == 'global':
3097 if expectedtype == 'global':
3093 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3098 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3094 else:
3099 else:
3095 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3100 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3096 rev_ = nullid
3101 rev_ = nullid
3097 if not message:
3102 if not message:
3098 # we don't translate commit messages
3103 # we don't translate commit messages
3099 message = 'Removed tag %s' % ', '.join(names)
3104 message = 'Removed tag %s' % ', '.join(names)
3100 elif not opts.get('force'):
3105 elif not opts.get('force'):
3101 for n in names:
3106 for n in names:
3102 if n in repo.tags():
3107 if n in repo.tags():
3103 raise util.Abort(_('tag \'%s\' already exists '
3108 raise util.Abort(_('tag \'%s\' already exists '
3104 '(use -f to force)') % n)
3109 '(use -f to force)') % n)
3105 if not rev_ and repo.dirstate.parents()[1] != nullid:
3110 if not rev_ and repo.dirstate.parents()[1] != nullid:
3106 raise util.Abort(_('uncommitted merge - please provide a '
3111 raise util.Abort(_('uncommitted merge - please provide a '
3107 'specific revision'))
3112 'specific revision'))
3108 r = repo[rev_].node()
3113 r = repo[rev_].node()
3109
3114
3110 if not message:
3115 if not message:
3111 # we don't translate commit messages
3116 # we don't translate commit messages
3112 message = ('Added tag %s for changeset %s' %
3117 message = ('Added tag %s for changeset %s' %
3113 (', '.join(names), short(r)))
3118 (', '.join(names), short(r)))
3114
3119
3115 date = opts.get('date')
3120 date = opts.get('date')
3116 if date:
3121 if date:
3117 date = util.parsedate(date)
3122 date = util.parsedate(date)
3118
3123
3119 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3124 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3120
3125
3121 def tags(ui, repo):
3126 def tags(ui, repo):
3122 """list repository tags
3127 """list repository tags
3123
3128
3124 This lists both regular and local tags. When the -v/--verbose
3129 This lists both regular and local tags. When the -v/--verbose
3125 switch is used, a third column "local" is printed for local tags.
3130 switch is used, a third column "local" is printed for local tags.
3126 """
3131 """
3127
3132
3128 hexfunc = ui.debugflag and hex or short
3133 hexfunc = ui.debugflag and hex or short
3129 tagtype = ""
3134 tagtype = ""
3130
3135
3131 for t, n in reversed(repo.tagslist()):
3136 for t, n in reversed(repo.tagslist()):
3132 if ui.quiet:
3137 if ui.quiet:
3133 ui.write("%s\n" % t)
3138 ui.write("%s\n" % t)
3134 continue
3139 continue
3135
3140
3136 try:
3141 try:
3137 hn = hexfunc(n)
3142 hn = hexfunc(n)
3138 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3143 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3139 except error.LookupError:
3144 except error.LookupError:
3140 r = " ?:%s" % hn
3145 r = " ?:%s" % hn
3141 else:
3146 else:
3142 spaces = " " * (30 - encoding.colwidth(t))
3147 spaces = " " * (30 - encoding.colwidth(t))
3143 if ui.verbose:
3148 if ui.verbose:
3144 if repo.tagtype(t) == 'local':
3149 if repo.tagtype(t) == 'local':
3145 tagtype = " local"
3150 tagtype = " local"
3146 else:
3151 else:
3147 tagtype = ""
3152 tagtype = ""
3148 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3153 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3149
3154
3150 def tip(ui, repo, **opts):
3155 def tip(ui, repo, **opts):
3151 """show the tip revision
3156 """show the tip revision
3152
3157
3153 The tip revision (usually just called the tip) is the changeset
3158 The tip revision (usually just called the tip) is the changeset
3154 most recently added to the repository (and therefore the most
3159 most recently added to the repository (and therefore the most
3155 recently changed head).
3160 recently changed head).
3156
3161
3157 If you have just made a commit, that commit will be the tip. If
3162 If you have just made a commit, that commit will be the tip. If
3158 you have just pulled changes from another repository, the tip of
3163 you have just pulled changes from another repository, the tip of
3159 that repository becomes the current tip. The "tip" tag is special
3164 that repository becomes the current tip. The "tip" tag is special
3160 and cannot be renamed or assigned to a different changeset.
3165 and cannot be renamed or assigned to a different changeset.
3161 """
3166 """
3162 displayer = cmdutil.show_changeset(ui, repo, opts)
3167 displayer = cmdutil.show_changeset(ui, repo, opts)
3163 displayer.show(repo[len(repo) - 1])
3168 displayer.show(repo[len(repo) - 1])
3164 displayer.close()
3169 displayer.close()
3165
3170
3166 def unbundle(ui, repo, fname1, *fnames, **opts):
3171 def unbundle(ui, repo, fname1, *fnames, **opts):
3167 """apply one or more changegroup files
3172 """apply one or more changegroup files
3168
3173
3169 Apply one or more compressed changegroup files generated by the
3174 Apply one or more compressed changegroup files generated by the
3170 bundle command.
3175 bundle command.
3171 """
3176 """
3172 fnames = (fname1,) + fnames
3177 fnames = (fname1,) + fnames
3173
3178
3174 lock = repo.lock()
3179 lock = repo.lock()
3175 try:
3180 try:
3176 for fname in fnames:
3181 for fname in fnames:
3177 f = url.open(ui, fname)
3182 f = url.open(ui, fname)
3178 gen = changegroup.readbundle(f, fname)
3183 gen = changegroup.readbundle(f, fname)
3179 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3184 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3180 finally:
3185 finally:
3181 lock.release()
3186 lock.release()
3182
3187
3183 return postincoming(ui, repo, modheads, opts.get('update'), None)
3188 return postincoming(ui, repo, modheads, opts.get('update'), None)
3184
3189
3185 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3190 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3186 """update working directory
3191 """update working directory
3187
3192
3188 Update the repository's working directory to the specified
3193 Update the repository's working directory to the specified
3189 changeset.
3194 changeset.
3190
3195
3191 If no changeset is specified, attempt to update to the head of the
3196 If no changeset is specified, attempt to update to the head of the
3192 current branch. If this head is a descendant of the working
3197 current branch. If this head is a descendant of the working
3193 directory's parent, update to it, otherwise abort.
3198 directory's parent, update to it, otherwise abort.
3194
3199
3195 The following rules apply when the working directory contains
3200 The following rules apply when the working directory contains
3196 uncommitted changes:
3201 uncommitted changes:
3197
3202
3198 1. If neither -c/--check nor -C/--clean is specified, and if
3203 1. If neither -c/--check nor -C/--clean is specified, and if
3199 the requested changeset is an ancestor or descendant of
3204 the requested changeset is an ancestor or descendant of
3200 the working directory's parent, the uncommitted changes
3205 the working directory's parent, the uncommitted changes
3201 are merged into the requested changeset and the merged
3206 are merged into the requested changeset and the merged
3202 result is left uncommitted. If the requested changeset is
3207 result is left uncommitted. If the requested changeset is
3203 not an ancestor or descendant (that is, it is on another
3208 not an ancestor or descendant (that is, it is on another
3204 branch), the update is aborted and the uncommitted changes
3209 branch), the update is aborted and the uncommitted changes
3205 are preserved.
3210 are preserved.
3206
3211
3207 2. With the -c/--check option, the update is aborted and the
3212 2. With the -c/--check option, the update is aborted and the
3208 uncommitted changes are preserved.
3213 uncommitted changes are preserved.
3209
3214
3210 3. With the -C/--clean option, uncommitted changes are discarded and
3215 3. With the -C/--clean option, uncommitted changes are discarded and
3211 the working directory is updated to the requested changeset.
3216 the working directory is updated to the requested changeset.
3212
3217
3213 Use null as the changeset to remove the working directory (like 'hg
3218 Use null as the changeset to remove the working directory (like 'hg
3214 clone -U').
3219 clone -U').
3215
3220
3216 If you want to update just one file to an older changeset, use 'hg revert'.
3221 If you want to update just one file to an older changeset, use 'hg revert'.
3217
3222
3218 See 'hg help dates' for a list of formats valid for -d/--date.
3223 See 'hg help dates' for a list of formats valid for -d/--date.
3219 """
3224 """
3220 if rev and node:
3225 if rev and node:
3221 raise util.Abort(_("please specify just one revision"))
3226 raise util.Abort(_("please specify just one revision"))
3222
3227
3223 if not rev:
3228 if not rev:
3224 rev = node
3229 rev = node
3225
3230
3226 if check and clean:
3231 if check and clean:
3227 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3232 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3228
3233
3229 if check:
3234 if check:
3230 # we could use dirty() but we can ignore merge and branch trivia
3235 # we could use dirty() but we can ignore merge and branch trivia
3231 c = repo[None]
3236 c = repo[None]
3232 if c.modified() or c.added() or c.removed():
3237 if c.modified() or c.added() or c.removed():
3233 raise util.Abort(_("uncommitted local changes"))
3238 raise util.Abort(_("uncommitted local changes"))
3234
3239
3235 if date:
3240 if date:
3236 if rev:
3241 if rev:
3237 raise util.Abort(_("you can't specify a revision and a date"))
3242 raise util.Abort(_("you can't specify a revision and a date"))
3238 rev = cmdutil.finddate(ui, repo, date)
3243 rev = cmdutil.finddate(ui, repo, date)
3239
3244
3240 if clean or check:
3245 if clean or check:
3241 return hg.clean(repo, rev)
3246 return hg.clean(repo, rev)
3242 else:
3247 else:
3243 return hg.update(repo, rev)
3248 return hg.update(repo, rev)
3244
3249
3245 def verify(ui, repo):
3250 def verify(ui, repo):
3246 """verify the integrity of the repository
3251 """verify the integrity of the repository
3247
3252
3248 Verify the integrity of the current repository.
3253 Verify the integrity of the current repository.
3249
3254
3250 This will perform an extensive check of the repository's
3255 This will perform an extensive check of the repository's
3251 integrity, validating the hashes and checksums of each entry in
3256 integrity, validating the hashes and checksums of each entry in
3252 the changelog, manifest, and tracked files, as well as the
3257 the changelog, manifest, and tracked files, as well as the
3253 integrity of their crosslinks and indices.
3258 integrity of their crosslinks and indices.
3254 """
3259 """
3255 return hg.verify(repo)
3260 return hg.verify(repo)
3256
3261
3257 def version_(ui):
3262 def version_(ui):
3258 """output version and copyright information"""
3263 """output version and copyright information"""
3259 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3264 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3260 % util.version())
3265 % util.version())
3261 ui.status(_(
3266 ui.status(_(
3262 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3267 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3263 "This is free software; see the source for copying conditions. "
3268 "This is free software; see the source for copying conditions. "
3264 "There is NO\nwarranty; "
3269 "There is NO\nwarranty; "
3265 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3270 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3266 ))
3271 ))
3267
3272
3268 # Command options and aliases are listed here, alphabetically
3273 # Command options and aliases are listed here, alphabetically
3269
3274
3270 globalopts = [
3275 globalopts = [
3271 ('R', 'repository', '',
3276 ('R', 'repository', '',
3272 _('repository root directory or name of overlay bundle file')),
3277 _('repository root directory or name of overlay bundle file')),
3273 ('', 'cwd', '', _('change working directory')),
3278 ('', 'cwd', '', _('change working directory')),
3274 ('y', 'noninteractive', None,
3279 ('y', 'noninteractive', None,
3275 _('do not prompt, assume \'yes\' for any required answers')),
3280 _('do not prompt, assume \'yes\' for any required answers')),
3276 ('q', 'quiet', None, _('suppress output')),
3281 ('q', 'quiet', None, _('suppress output')),
3277 ('v', 'verbose', None, _('enable additional output')),
3282 ('v', 'verbose', None, _('enable additional output')),
3278 ('', 'config', [], _('set/override config option')),
3283 ('', 'config', [], _('set/override config option')),
3279 ('', 'debug', None, _('enable debugging output')),
3284 ('', 'debug', None, _('enable debugging output')),
3280 ('', 'debugger', None, _('start debugger')),
3285 ('', 'debugger', None, _('start debugger')),
3281 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3286 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3282 ('', 'encodingmode', encoding.encodingmode,
3287 ('', 'encodingmode', encoding.encodingmode,
3283 _('set the charset encoding mode')),
3288 _('set the charset encoding mode')),
3284 ('', 'traceback', None, _('always print a traceback on exception')),
3289 ('', 'traceback', None, _('always print a traceback on exception')),
3285 ('', 'time', None, _('time how long the command takes')),
3290 ('', 'time', None, _('time how long the command takes')),
3286 ('', 'profile', None, _('print command execution profile')),
3291 ('', 'profile', None, _('print command execution profile')),
3287 ('', 'version', None, _('output version information and exit')),
3292 ('', 'version', None, _('output version information and exit')),
3288 ('h', 'help', None, _('display help and exit')),
3293 ('h', 'help', None, _('display help and exit')),
3289 ]
3294 ]
3290
3295
3291 dryrunopts = [('n', 'dry-run', None,
3296 dryrunopts = [('n', 'dry-run', None,
3292 _('do not perform actions, just print output'))]
3297 _('do not perform actions, just print output'))]
3293
3298
3294 remoteopts = [
3299 remoteopts = [
3295 ('e', 'ssh', '', _('specify ssh command to use')),
3300 ('e', 'ssh', '', _('specify ssh command to use')),
3296 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3301 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3297 ]
3302 ]
3298
3303
3299 walkopts = [
3304 walkopts = [
3300 ('I', 'include', [], _('include names matching the given patterns')),
3305 ('I', 'include', [], _('include names matching the given patterns')),
3301 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3306 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3302 ]
3307 ]
3303
3308
3304 commitopts = [
3309 commitopts = [
3305 ('m', 'message', '', _('use <text> as commit message')),
3310 ('m', 'message', '', _('use <text> as commit message')),
3306 ('l', 'logfile', '', _('read commit message from <file>')),
3311 ('l', 'logfile', '', _('read commit message from <file>')),
3307 ]
3312 ]
3308
3313
3309 commitopts2 = [
3314 commitopts2 = [
3310 ('d', 'date', '', _('record datecode as commit date')),
3315 ('d', 'date', '', _('record datecode as commit date')),
3311 ('u', 'user', '', _('record the specified user as committer')),
3316 ('u', 'user', '', _('record the specified user as committer')),
3312 ]
3317 ]
3313
3318
3314 templateopts = [
3319 templateopts = [
3315 ('', 'style', '', _('display using template map file')),
3320 ('', 'style', '', _('display using template map file')),
3316 ('', 'template', '', _('display with template')),
3321 ('', 'template', '', _('display with template')),
3317 ]
3322 ]
3318
3323
3319 logopts = [
3324 logopts = [
3320 ('p', 'patch', None, _('show patch')),
3325 ('p', 'patch', None, _('show patch')),
3321 ('g', 'git', None, _('use git extended diff format')),
3326 ('g', 'git', None, _('use git extended diff format')),
3322 ('l', 'limit', '', _('limit number of changes displayed')),
3327 ('l', 'limit', '', _('limit number of changes displayed')),
3323 ('M', 'no-merges', None, _('do not show merges')),
3328 ('M', 'no-merges', None, _('do not show merges')),
3324 ] + templateopts
3329 ] + templateopts
3325
3330
3326 diffopts = [
3331 diffopts = [
3327 ('a', 'text', None, _('treat all files as text')),
3332 ('a', 'text', None, _('treat all files as text')),
3328 ('g', 'git', None, _('use git extended diff format')),
3333 ('g', 'git', None, _('use git extended diff format')),
3329 ('', 'nodates', None, _("don't include dates in diff headers"))
3334 ('', 'nodates', None, _("don't include dates in diff headers"))
3330 ]
3335 ]
3331
3336
3332 diffopts2 = [
3337 diffopts2 = [
3333 ('p', 'show-function', None, _('show which function each change is in')),
3338 ('p', 'show-function', None, _('show which function each change is in')),
3334 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3339 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3335 ('w', 'ignore-all-space', None,
3340 ('w', 'ignore-all-space', None,
3336 _('ignore white space when comparing lines')),
3341 _('ignore white space when comparing lines')),
3337 ('b', 'ignore-space-change', None,
3342 ('b', 'ignore-space-change', None,
3338 _('ignore changes in the amount of white space')),
3343 _('ignore changes in the amount of white space')),
3339 ('B', 'ignore-blank-lines', None,
3344 ('B', 'ignore-blank-lines', None,
3340 _('ignore changes whose lines are all blank')),
3345 _('ignore changes whose lines are all blank')),
3341 ('U', 'unified', '', _('number of lines of context to show')),
3346 ('U', 'unified', '', _('number of lines of context to show')),
3342 ('', 'stat', None, _('output diffstat-style summary of changes')),
3347 ('', 'stat', None, _('output diffstat-style summary of changes')),
3343 ]
3348 ]
3344
3349
3345 similarityopts = [
3350 similarityopts = [
3346 ('s', 'similarity', '',
3351 ('s', 'similarity', '',
3347 _('guess renamed files by similarity (0<=s<=100)'))
3352 _('guess renamed files by similarity (0<=s<=100)'))
3348 ]
3353 ]
3349
3354
3350 table = {
3355 table = {
3351 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3356 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3352 "addremove":
3357 "addremove":
3353 (addremove, similarityopts + walkopts + dryrunopts,
3358 (addremove, similarityopts + walkopts + dryrunopts,
3354 _('[OPTION]... [FILE]...')),
3359 _('[OPTION]... [FILE]...')),
3355 "^annotate|blame":
3360 "^annotate|blame":
3356 (annotate,
3361 (annotate,
3357 [('r', 'rev', '', _('annotate the specified revision')),
3362 [('r', 'rev', '', _('annotate the specified revision')),
3358 ('f', 'follow', None, _('follow file copies and renames')),
3363 ('f', 'follow', None, _('follow file copies and renames')),
3359 ('a', 'text', None, _('treat all files as text')),
3364 ('a', 'text', None, _('treat all files as text')),
3360 ('u', 'user', None, _('list the author (long with -v)')),
3365 ('u', 'user', None, _('list the author (long with -v)')),
3361 ('d', 'date', None, _('list the date (short with -q)')),
3366 ('d', 'date', None, _('list the date (short with -q)')),
3362 ('n', 'number', None, _('list the revision number (default)')),
3367 ('n', 'number', None, _('list the revision number (default)')),
3363 ('c', 'changeset', None, _('list the changeset')),
3368 ('c', 'changeset', None, _('list the changeset')),
3364 ('l', 'line-number', None,
3369 ('l', 'line-number', None,
3365 _('show line number at the first appearance'))
3370 _('show line number at the first appearance'))
3366 ] + walkopts,
3371 ] + walkopts,
3367 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3372 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3368 "archive":
3373 "archive":
3369 (archive,
3374 (archive,
3370 [('', 'no-decode', None, _('do not pass files through decoders')),
3375 [('', 'no-decode', None, _('do not pass files through decoders')),
3371 ('p', 'prefix', '', _('directory prefix for files in archive')),
3376 ('p', 'prefix', '', _('directory prefix for files in archive')),
3372 ('r', 'rev', '', _('revision to distribute')),
3377 ('r', 'rev', '', _('revision to distribute')),
3373 ('t', 'type', '', _('type of distribution to create')),
3378 ('t', 'type', '', _('type of distribution to create')),
3374 ] + walkopts,
3379 ] + walkopts,
3375 _('[OPTION]... DEST')),
3380 _('[OPTION]... DEST')),
3376 "backout":
3381 "backout":
3377 (backout,
3382 (backout,
3378 [('', 'merge', None,
3383 [('', 'merge', None,
3379 _('merge with old dirstate parent after backout')),
3384 _('merge with old dirstate parent after backout')),
3380 ('', 'parent', '', _('parent to choose when backing out merge')),
3385 ('', 'parent', '', _('parent to choose when backing out merge')),
3381 ('r', 'rev', '', _('revision to backout')),
3386 ('r', 'rev', '', _('revision to backout')),
3382 ] + walkopts + commitopts + commitopts2,
3387 ] + walkopts + commitopts + commitopts2,
3383 _('[OPTION]... [-r] REV')),
3388 _('[OPTION]... [-r] REV')),
3384 "bisect":
3389 "bisect":
3385 (bisect,
3390 (bisect,
3386 [('r', 'reset', False, _('reset bisect state')),
3391 [('r', 'reset', False, _('reset bisect state')),
3387 ('g', 'good', False, _('mark changeset good')),
3392 ('g', 'good', False, _('mark changeset good')),
3388 ('b', 'bad', False, _('mark changeset bad')),
3393 ('b', 'bad', False, _('mark changeset bad')),
3389 ('s', 'skip', False, _('skip testing changeset')),
3394 ('s', 'skip', False, _('skip testing changeset')),
3390 ('c', 'command', '', _('use command to check changeset state')),
3395 ('c', 'command', '', _('use command to check changeset state')),
3391 ('U', 'noupdate', False, _('do not update to target'))],
3396 ('U', 'noupdate', False, _('do not update to target'))],
3392 _("[-gbsr] [-U] [-c CMD] [REV]")),
3397 _("[-gbsr] [-U] [-c CMD] [REV]")),
3393 "branch":
3398 "branch":
3394 (branch,
3399 (branch,
3395 [('f', 'force', None,
3400 [('f', 'force', None,
3396 _('set branch name even if it shadows an existing branch')),
3401 _('set branch name even if it shadows an existing branch')),
3397 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3402 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3398 _('[-fC] [NAME]')),
3403 _('[-fC] [NAME]')),
3399 "branches":
3404 "branches":
3400 (branches,
3405 (branches,
3401 [('a', 'active', False,
3406 [('a', 'active', False,
3402 _('show only branches that have unmerged heads')),
3407 _('show only branches that have unmerged heads')),
3403 ('c', 'closed', False,
3408 ('c', 'closed', False,
3404 _('show normal and closed branches'))],
3409 _('show normal and closed branches'))],
3405 _('[-ac]')),
3410 _('[-ac]')),
3406 "bundle":
3411 "bundle":
3407 (bundle,
3412 (bundle,
3408 [('f', 'force', None,
3413 [('f', 'force', None,
3409 _('run even when remote repository is unrelated')),
3414 _('run even when remote repository is unrelated')),
3410 ('r', 'rev', [],
3415 ('r', 'rev', [],
3411 _('a changeset up to which you would like to bundle')),
3416 _('a changeset up to which you would like to bundle')),
3412 ('', 'base', [],
3417 ('', 'base', [],
3413 _('a base changeset to specify instead of a destination')),
3418 _('a base changeset to specify instead of a destination')),
3414 ('a', 'all', None, _('bundle all changesets in the repository')),
3419 ('a', 'all', None, _('bundle all changesets in the repository')),
3415 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3420 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3416 ] + remoteopts,
3421 ] + remoteopts,
3417 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3422 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3418 "cat":
3423 "cat":
3419 (cat,
3424 (cat,
3420 [('o', 'output', '', _('print output to file with formatted name')),
3425 [('o', 'output', '', _('print output to file with formatted name')),
3421 ('r', 'rev', '', _('print the given revision')),
3426 ('r', 'rev', '', _('print the given revision')),
3422 ('', 'decode', None, _('apply any matching decode filter')),
3427 ('', 'decode', None, _('apply any matching decode filter')),
3423 ] + walkopts,
3428 ] + walkopts,
3424 _('[OPTION]... FILE...')),
3429 _('[OPTION]... FILE...')),
3425 "^clone":
3430 "^clone":
3426 (clone,
3431 (clone,
3427 [('U', 'noupdate', None,
3432 [('U', 'noupdate', None,
3428 _('the clone will only contain a repository (no working copy)')),
3433 _('the clone will only contain a repository (no working copy)')),
3429 ('u', 'updaterev', '',
3434 ('u', 'updaterev', '',
3430 _('revision, tag or branch to check out')),
3435 _('revision, tag or branch to check out')),
3431 ('r', 'rev', [],
3436 ('r', 'rev', [],
3432 _('clone only the specified revisions and ancestors')),
3437 _('clone only the specified revisions and ancestors')),
3433 ('', 'pull', None, _('use pull protocol to copy metadata')),
3438 ('', 'pull', None, _('use pull protocol to copy metadata')),
3434 ('', 'uncompressed', None,
3439 ('', 'uncompressed', None,
3435 _('use uncompressed transfer (fast over LAN)')),
3440 _('use uncompressed transfer (fast over LAN)')),
3436 ] + remoteopts,
3441 ] + remoteopts,
3437 _('[OPTION]... SOURCE [DEST]')),
3442 _('[OPTION]... SOURCE [DEST]')),
3438 "^commit|ci":
3443 "^commit|ci":
3439 (commit,
3444 (commit,
3440 [('A', 'addremove', None,
3445 [('A', 'addremove', None,
3441 _('mark new/missing files as added/removed before committing')),
3446 _('mark new/missing files as added/removed before committing')),
3442 ('', 'close-branch', None,
3447 ('', 'close-branch', None,
3443 _('mark a branch as closed, hiding it from the branch list')),
3448 _('mark a branch as closed, hiding it from the branch list')),
3444 ] + walkopts + commitopts + commitopts2,
3449 ] + walkopts + commitopts + commitopts2,
3445 _('[OPTION]... [FILE]...')),
3450 _('[OPTION]... [FILE]...')),
3446 "copy|cp":
3451 "copy|cp":
3447 (copy,
3452 (copy,
3448 [('A', 'after', None, _('record a copy that has already occurred')),
3453 [('A', 'after', None, _('record a copy that has already occurred')),
3449 ('f', 'force', None,
3454 ('f', 'force', None,
3450 _('forcibly copy over an existing managed file')),
3455 _('forcibly copy over an existing managed file')),
3451 ] + walkopts + dryrunopts,
3456 ] + walkopts + dryrunopts,
3452 _('[OPTION]... [SOURCE]... DEST')),
3457 _('[OPTION]... [SOURCE]... DEST')),
3453 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3458 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3454 "debugcheckstate": (debugcheckstate, [], ''),
3459 "debugcheckstate": (debugcheckstate, [], ''),
3455 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3460 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3456 "debugcomplete":
3461 "debugcomplete":
3457 (debugcomplete,
3462 (debugcomplete,
3458 [('o', 'options', None, _('show the command options'))],
3463 [('o', 'options', None, _('show the command options'))],
3459 _('[-o] CMD')),
3464 _('[-o] CMD')),
3460 "debugdate":
3465 "debugdate":
3461 (debugdate,
3466 (debugdate,
3462 [('e', 'extended', None, _('try extended date formats'))],
3467 [('e', 'extended', None, _('try extended date formats'))],
3463 _('[-e] DATE [RANGE]')),
3468 _('[-e] DATE [RANGE]')),
3464 "debugdata": (debugdata, [], _('FILE REV')),
3469 "debugdata": (debugdata, [], _('FILE REV')),
3465 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3470 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3466 "debugindex": (debugindex, [], _('FILE')),
3471 "debugindex": (debugindex, [], _('FILE')),
3467 "debugindexdot": (debugindexdot, [], _('FILE')),
3472 "debugindexdot": (debugindexdot, [], _('FILE')),
3468 "debuginstall": (debuginstall, [], ''),
3473 "debuginstall": (debuginstall, [], ''),
3469 "debugrebuildstate":
3474 "debugrebuildstate":
3470 (debugrebuildstate,
3475 (debugrebuildstate,
3471 [('r', 'rev', '', _('revision to rebuild to'))],
3476 [('r', 'rev', '', _('revision to rebuild to'))],
3472 _('[-r REV] [REV]')),
3477 _('[-r REV] [REV]')),
3473 "debugrename":
3478 "debugrename":
3474 (debugrename,
3479 (debugrename,
3475 [('r', 'rev', '', _('revision to debug'))],
3480 [('r', 'rev', '', _('revision to debug'))],
3476 _('[-r REV] FILE')),
3481 _('[-r REV] FILE')),
3477 "debugsetparents":
3482 "debugsetparents":
3478 (debugsetparents, [], _('REV1 [REV2]')),
3483 (debugsetparents, [], _('REV1 [REV2]')),
3479 "debugstate":
3484 "debugstate":
3480 (debugstate,
3485 (debugstate,
3481 [('', 'nodates', None, _('do not display the saved mtime'))],
3486 [('', 'nodates', None, _('do not display the saved mtime'))],
3482 _('[OPTION]...')),
3487 _('[OPTION]...')),
3483 "debugsub":
3488 "debugsub":
3484 (debugsub,
3489 (debugsub,
3485 [('r', 'rev', '', _('revision to check'))],
3490 [('r', 'rev', '', _('revision to check'))],
3486 _('[-r REV] [REV]')),
3491 _('[-r REV] [REV]')),
3487 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3492 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3488 "^diff":
3493 "^diff":
3489 (diff,
3494 (diff,
3490 [('r', 'rev', [], _('revision')),
3495 [('r', 'rev', [], _('revision')),
3491 ('c', 'change', '', _('change made by revision'))
3496 ('c', 'change', '', _('change made by revision'))
3492 ] + diffopts + diffopts2 + walkopts,
3497 ] + diffopts + diffopts2 + walkopts,
3493 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3498 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3494 "^export":
3499 "^export":
3495 (export,
3500 (export,
3496 [('o', 'output', '', _('print output to file with formatted name')),
3501 [('o', 'output', '', _('print output to file with formatted name')),
3497 ('', 'switch-parent', None, _('diff against the second parent')),
3502 ('', 'switch-parent', None, _('diff against the second parent')),
3498 ('r', 'rev', [], _('revisions to export')),
3503 ('r', 'rev', [], _('revisions to export')),
3499 ] + diffopts,
3504 ] + diffopts,
3500 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3505 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3501 "^forget":
3506 "^forget":
3502 (forget,
3507 (forget,
3503 [] + walkopts,
3508 [] + walkopts,
3504 _('[OPTION]... FILE...')),
3509 _('[OPTION]... FILE...')),
3505 "grep":
3510 "grep":
3506 (grep,
3511 (grep,
3507 [('0', 'print0', None, _('end fields with NUL')),
3512 [('0', 'print0', None, _('end fields with NUL')),
3508 ('', 'all', None, _('print all revisions that match')),
3513 ('', 'all', None, _('print all revisions that match')),
3509 ('f', 'follow', None,
3514 ('f', 'follow', None,
3510 _('follow changeset history,'
3515 _('follow changeset history,'
3511 ' or file history across copies and renames')),
3516 ' or file history across copies and renames')),
3512 ('i', 'ignore-case', None, _('ignore case when matching')),
3517 ('i', 'ignore-case', None, _('ignore case when matching')),
3513 ('l', 'files-with-matches', None,
3518 ('l', 'files-with-matches', None,
3514 _('print only filenames and revisions that match')),
3519 _('print only filenames and revisions that match')),
3515 ('n', 'line-number', None, _('print matching line numbers')),
3520 ('n', 'line-number', None, _('print matching line numbers')),
3516 ('r', 'rev', [], _('search in given revision range')),
3521 ('r', 'rev', [], _('search in given revision range')),
3517 ('u', 'user', None, _('list the author (long with -v)')),
3522 ('u', 'user', None, _('list the author (long with -v)')),
3518 ('d', 'date', None, _('list the date (short with -q)')),
3523 ('d', 'date', None, _('list the date (short with -q)')),
3519 ] + walkopts,
3524 ] + walkopts,
3520 _('[OPTION]... PATTERN [FILE]...')),
3525 _('[OPTION]... PATTERN [FILE]...')),
3521 "heads":
3526 "heads":
3522 (heads,
3527 (heads,
3523 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3528 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3524 ('t', 'topo', False, _('show topological heads only')),
3529 ('t', 'topo', False, _('show topological heads only')),
3525 ('a', 'active', False,
3530 ('a', 'active', False,
3526 _('show active branchheads only [DEPRECATED]')),
3531 _('show active branchheads only [DEPRECATED]')),
3527 ('c', 'closed', False,
3532 ('c', 'closed', False,
3528 _('show normal and closed branch heads')),
3533 _('show normal and closed branch heads')),
3529 ] + templateopts,
3534 ] + templateopts,
3530 _('[-ac] [-r STARTREV] [REV]...')),
3535 _('[-ac] [-r STARTREV] [REV]...')),
3531 "help": (help_, [], _('[TOPIC]')),
3536 "help": (help_, [], _('[TOPIC]')),
3532 "identify|id":
3537 "identify|id":
3533 (identify,
3538 (identify,
3534 [('r', 'rev', '', _('identify the specified revision')),
3539 [('r', 'rev', '', _('identify the specified revision')),
3535 ('n', 'num', None, _('show local revision number')),
3540 ('n', 'num', None, _('show local revision number')),
3536 ('i', 'id', None, _('show global revision id')),
3541 ('i', 'id', None, _('show global revision id')),
3537 ('b', 'branch', None, _('show branch')),
3542 ('b', 'branch', None, _('show branch')),
3538 ('t', 'tags', None, _('show tags'))],
3543 ('t', 'tags', None, _('show tags'))],
3539 _('[-nibt] [-r REV] [SOURCE]')),
3544 _('[-nibt] [-r REV] [SOURCE]')),
3540 "import|patch":
3545 "import|patch":
3541 (import_,
3546 (import_,
3542 [('p', 'strip', 1,
3547 [('p', 'strip', 1,
3543 _('directory strip option for patch. This has the same '
3548 _('directory strip option for patch. This has the same '
3544 'meaning as the corresponding patch option')),
3549 'meaning as the corresponding patch option')),
3545 ('b', 'base', '', _('base path')),
3550 ('b', 'base', '', _('base path')),
3546 ('f', 'force', None,
3551 ('f', 'force', None,
3547 _('skip check for outstanding uncommitted changes')),
3552 _('skip check for outstanding uncommitted changes')),
3548 ('', 'no-commit', None,
3553 ('', 'no-commit', None,
3549 _("don't commit, just update the working directory")),
3554 _("don't commit, just update the working directory")),
3550 ('', 'exact', None,
3555 ('', 'exact', None,
3551 _('apply patch to the nodes from which it was generated')),
3556 _('apply patch to the nodes from which it was generated')),
3552 ('', 'import-branch', None,
3557 ('', 'import-branch', None,
3553 _('use any branch information in patch (implied by --exact)'))] +
3558 _('use any branch information in patch (implied by --exact)'))] +
3554 commitopts + commitopts2 + similarityopts,
3559 commitopts + commitopts2 + similarityopts,
3555 _('[OPTION]... PATCH...')),
3560 _('[OPTION]... PATCH...')),
3556 "incoming|in":
3561 "incoming|in":
3557 (incoming,
3562 (incoming,
3558 [('f', 'force', None,
3563 [('f', 'force', None,
3559 _('run even when remote repository is unrelated')),
3564 _('run even when remote repository is unrelated')),
3560 ('n', 'newest-first', None, _('show newest record first')),
3565 ('n', 'newest-first', None, _('show newest record first')),
3561 ('', 'bundle', '', _('file to store the bundles into')),
3566 ('', 'bundle', '', _('file to store the bundles into')),
3562 ('r', 'rev', [],
3567 ('r', 'rev', [],
3563 _('a specific remote revision up to which you would like to pull')),
3568 _('a specific remote revision up to which you would like to pull')),
3564 ] + logopts + remoteopts,
3569 ] + logopts + remoteopts,
3565 _('[-p] [-n] [-M] [-f] [-r REV]...'
3570 _('[-p] [-n] [-M] [-f] [-r REV]...'
3566 ' [--bundle FILENAME] [SOURCE]')),
3571 ' [--bundle FILENAME] [SOURCE]')),
3567 "^init":
3572 "^init":
3568 (init,
3573 (init,
3569 remoteopts,
3574 remoteopts,
3570 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3575 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3571 "locate":
3576 "locate":
3572 (locate,
3577 (locate,
3573 [('r', 'rev', '', _('search the repository as it stood at REV')),
3578 [('r', 'rev', '', _('search the repository as it stood at REV')),
3574 ('0', 'print0', None,
3579 ('0', 'print0', None,
3575 _('end filenames with NUL, for use with xargs')),
3580 _('end filenames with NUL, for use with xargs')),
3576 ('f', 'fullpath', None,
3581 ('f', 'fullpath', None,
3577 _('print complete paths from the filesystem root')),
3582 _('print complete paths from the filesystem root')),
3578 ] + walkopts,
3583 ] + walkopts,
3579 _('[OPTION]... [PATTERN]...')),
3584 _('[OPTION]... [PATTERN]...')),
3580 "^log|history":
3585 "^log|history":
3581 (log,
3586 (log,
3582 [('f', 'follow', None,
3587 [('f', 'follow', None,
3583 _('follow changeset history,'
3588 _('follow changeset history,'
3584 ' or file history across copies and renames')),
3589 ' or file history across copies and renames')),
3585 ('', 'follow-first', None,
3590 ('', 'follow-first', None,
3586 _('only follow the first parent of merge changesets')),
3591 _('only follow the first parent of merge changesets')),
3587 ('d', 'date', '', _('show revisions matching date spec')),
3592 ('d', 'date', '', _('show revisions matching date spec')),
3588 ('C', 'copies', None, _('show copied files')),
3593 ('C', 'copies', None, _('show copied files')),
3589 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3594 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3590 ('r', 'rev', [], _('show the specified revision or range')),
3595 ('r', 'rev', [], _('show the specified revision or range')),
3591 ('', 'removed', None, _('include revisions where files were removed')),
3596 ('', 'removed', None, _('include revisions where files were removed')),
3592 ('m', 'only-merges', None, _('show only merges')),
3597 ('m', 'only-merges', None, _('show only merges')),
3593 ('u', 'user', [], _('revisions committed by user')),
3598 ('u', 'user', [], _('revisions committed by user')),
3594 ('b', 'only-branch', [],
3599 ('b', 'only-branch', [],
3595 _('show only changesets within the given named branch')),
3600 _('show only changesets within the given named branch')),
3596 ('P', 'prune', [],
3601 ('P', 'prune', [],
3597 _('do not display revision or any of its ancestors')),
3602 _('do not display revision or any of its ancestors')),
3598 ] + logopts + walkopts,
3603 ] + logopts + walkopts,
3599 _('[OPTION]... [FILE]')),
3604 _('[OPTION]... [FILE]')),
3600 "manifest":
3605 "manifest":
3601 (manifest,
3606 (manifest,
3602 [('r', 'rev', '', _('revision to display'))],
3607 [('r', 'rev', '', _('revision to display'))],
3603 _('[-r REV]')),
3608 _('[-r REV]')),
3604 "^merge":
3609 "^merge":
3605 (merge,
3610 (merge,
3606 [('f', 'force', None, _('force a merge with outstanding changes')),
3611 [('f', 'force', None, _('force a merge with outstanding changes')),
3607 ('r', 'rev', '', _('revision to merge')),
3612 ('r', 'rev', '', _('revision to merge')),
3608 ('P', 'preview', None,
3613 ('P', 'preview', None,
3609 _('review revisions to merge (no merge is performed)'))],
3614 _('review revisions to merge (no merge is performed)'))],
3610 _('[-P] [-f] [[-r] REV]')),
3615 _('[-P] [-f] [[-r] REV]')),
3611 "outgoing|out":
3616 "outgoing|out":
3612 (outgoing,
3617 (outgoing,
3613 [('f', 'force', None,
3618 [('f', 'force', None,
3614 _('run even when remote repository is unrelated')),
3619 _('run even when remote repository is unrelated')),
3615 ('r', 'rev', [],
3620 ('r', 'rev', [],
3616 _('a specific revision up to which you would like to push')),
3621 _('a specific revision up to which you would like to push')),
3617 ('n', 'newest-first', None, _('show newest record first')),
3622 ('n', 'newest-first', None, _('show newest record first')),
3618 ] + logopts + remoteopts,
3623 ] + logopts + remoteopts,
3619 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3624 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3620 "parents":
3625 "parents":
3621 (parents,
3626 (parents,
3622 [('r', 'rev', '', _('show parents from the specified revision')),
3627 [('r', 'rev', '', _('show parents from the specified revision')),
3623 ] + templateopts,
3628 ] + templateopts,
3624 _('[-r REV] [FILE]')),
3629 _('[-r REV] [FILE]')),
3625 "paths": (paths, [], _('[NAME]')),
3630 "paths": (paths, [], _('[NAME]')),
3626 "^pull":
3631 "^pull":
3627 (pull,
3632 (pull,
3628 [('u', 'update', None,
3633 [('u', 'update', None,
3629 _('update to new branch head if changesets were pulled')),
3634 _('update to new branch head if changesets were pulled')),
3630 ('f', 'force', None,
3635 ('f', 'force', None,
3631 _('run even when remote repository is unrelated')),
3636 _('run even when remote repository is unrelated')),
3632 ('r', 'rev', [],
3637 ('r', 'rev', [],
3633 _('a specific remote revision up to which you would like to pull')),
3638 _('a specific remote revision up to which you would like to pull')),
3634 ] + remoteopts,
3639 ] + remoteopts,
3635 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3640 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3636 "^push":
3641 "^push":
3637 (push,
3642 (push,
3638 [('f', 'force', None, _('force push')),
3643 [('f', 'force', None, _('force push')),
3639 ('r', 'rev', [],
3644 ('r', 'rev', [],
3640 _('a specific revision up to which you would like to push')),
3645 _('a specific revision up to which you would like to push')),
3641 ] + remoteopts,
3646 ] + remoteopts,
3642 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3647 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3643 "recover": (recover, []),
3648 "recover": (recover, []),
3644 "^remove|rm":
3649 "^remove|rm":
3645 (remove,
3650 (remove,
3646 [('A', 'after', None, _('record delete for missing files')),
3651 [('A', 'after', None, _('record delete for missing files')),
3647 ('f', 'force', None,
3652 ('f', 'force', None,
3648 _('remove (and delete) file even if added or modified')),
3653 _('remove (and delete) file even if added or modified')),
3649 ] + walkopts,
3654 ] + walkopts,
3650 _('[OPTION]... FILE...')),
3655 _('[OPTION]... FILE...')),
3651 "rename|mv":
3656 "rename|mv":
3652 (rename,
3657 (rename,
3653 [('A', 'after', None, _('record a rename that has already occurred')),
3658 [('A', 'after', None, _('record a rename that has already occurred')),
3654 ('f', 'force', None,
3659 ('f', 'force', None,
3655 _('forcibly copy over an existing managed file')),
3660 _('forcibly copy over an existing managed file')),
3656 ] + walkopts + dryrunopts,
3661 ] + walkopts + dryrunopts,
3657 _('[OPTION]... SOURCE... DEST')),
3662 _('[OPTION]... SOURCE... DEST')),
3658 "resolve":
3663 "resolve":
3659 (resolve,
3664 (resolve,
3660 [('a', 'all', None, _('select all unresolved files')),
3665 [('a', 'all', None, _('select all unresolved files')),
3661 ('l', 'list', None, _('list state of files needing merge')),
3666 ('l', 'list', None, _('list state of files needing merge')),
3662 ('m', 'mark', None, _('mark files as resolved')),
3667 ('m', 'mark', None, _('mark files as resolved')),
3663 ('u', 'unmark', None, _('unmark files as resolved')),
3668 ('u', 'unmark', None, _('unmark files as resolved')),
3664 ('n', 'no-status', None, _('hide status prefix'))]
3669 ('n', 'no-status', None, _('hide status prefix'))]
3665 + walkopts,
3670 + walkopts,
3666 _('[OPTION]... [FILE]...')),
3671 _('[OPTION]... [FILE]...')),
3667 "revert":
3672 "revert":
3668 (revert,
3673 (revert,
3669 [('a', 'all', None, _('revert all changes when no arguments given')),
3674 [('a', 'all', None, _('revert all changes when no arguments given')),
3670 ('d', 'date', '', _('tipmost revision matching date')),
3675 ('d', 'date', '', _('tipmost revision matching date')),
3671 ('r', 'rev', '', _('revision to revert to')),
3676 ('r', 'rev', '', _('revision to revert to')),
3672 ('', 'no-backup', None, _('do not save backup copies of files')),
3677 ('', 'no-backup', None, _('do not save backup copies of files')),
3673 ] + walkopts + dryrunopts,
3678 ] + walkopts + dryrunopts,
3674 _('[OPTION]... [-r REV] [NAME]...')),
3679 _('[OPTION]... [-r REV] [NAME]...')),
3675 "rollback": (rollback, []),
3680 "rollback": (rollback, []),
3676 "root": (root, []),
3681 "root": (root, []),
3677 "^serve":
3682 "^serve":
3678 (serve,
3683 (serve,
3679 [('A', 'accesslog', '', _('name of access log file to write to')),
3684 [('A', 'accesslog', '', _('name of access log file to write to')),
3680 ('d', 'daemon', None, _('run server in background')),
3685 ('d', 'daemon', None, _('run server in background')),
3681 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3686 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3682 ('E', 'errorlog', '', _('name of error log file to write to')),
3687 ('E', 'errorlog', '', _('name of error log file to write to')),
3683 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3688 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3684 ('a', 'address', '',
3689 ('a', 'address', '',
3685 _('address to listen on (default: all interfaces)')),
3690 _('address to listen on (default: all interfaces)')),
3686 ('', 'prefix', '',
3691 ('', 'prefix', '',
3687 _('prefix path to serve from (default: server root)')),
3692 _('prefix path to serve from (default: server root)')),
3688 ('n', 'name', '',
3693 ('n', 'name', '',
3689 _('name to show in web pages (default: working directory)')),
3694 _('name to show in web pages (default: working directory)')),
3690 ('', 'webdir-conf', '', _('name of the webdir config file'
3695 ('', 'webdir-conf', '', _('name of the webdir config file'
3691 ' (serve more than one repository)')),
3696 ' (serve more than one repository)')),
3692 ('', 'pid-file', '', _('name of file to write process ID to')),
3697 ('', 'pid-file', '', _('name of file to write process ID to')),
3693 ('', 'stdio', None, _('for remote clients')),
3698 ('', 'stdio', None, _('for remote clients')),
3694 ('t', 'templates', '', _('web templates to use')),
3699 ('t', 'templates', '', _('web templates to use')),
3695 ('', 'style', '', _('template style to use')),
3700 ('', 'style', '', _('template style to use')),
3696 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3701 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3697 ('', 'certificate', '', _('SSL certificate file'))],
3702 ('', 'certificate', '', _('SSL certificate file'))],
3698 _('[OPTION]...')),
3703 _('[OPTION]...')),
3699 "showconfig|debugconfig":
3704 "showconfig|debugconfig":
3700 (showconfig,
3705 (showconfig,
3701 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3706 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3702 _('[-u] [NAME]...')),
3707 _('[-u] [NAME]...')),
3703 "^summary|sum":
3708 "^summary|sum":
3704 (summary,
3709 (summary,
3705 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3710 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3706 "^status|st":
3711 "^status|st":
3707 (status,
3712 (status,
3708 [('A', 'all', None, _('show status of all files')),
3713 [('A', 'all', None, _('show status of all files')),
3709 ('m', 'modified', None, _('show only modified files')),
3714 ('m', 'modified', None, _('show only modified files')),
3710 ('a', 'added', None, _('show only added files')),
3715 ('a', 'added', None, _('show only added files')),
3711 ('r', 'removed', None, _('show only removed files')),
3716 ('r', 'removed', None, _('show only removed files')),
3712 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3717 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3713 ('c', 'clean', None, _('show only files without changes')),
3718 ('c', 'clean', None, _('show only files without changes')),
3714 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3719 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3715 ('i', 'ignored', None, _('show only ignored files')),
3720 ('i', 'ignored', None, _('show only ignored files')),
3716 ('n', 'no-status', None, _('hide status prefix')),
3721 ('n', 'no-status', None, _('hide status prefix')),
3717 ('C', 'copies', None, _('show source of copied files')),
3722 ('C', 'copies', None, _('show source of copied files')),
3718 ('0', 'print0', None,
3723 ('0', 'print0', None,
3719 _('end filenames with NUL, for use with xargs')),
3724 _('end filenames with NUL, for use with xargs')),
3720 ('', 'rev', [], _('show difference from revision')),
3725 ('', 'rev', [], _('show difference from revision')),
3721 ('', 'change', '', _('list the changed files of a revision')),
3726 ('', 'change', '', _('list the changed files of a revision')),
3722 ] + walkopts,
3727 ] + walkopts,
3723 _('[OPTION]... [FILE]...')),
3728 _('[OPTION]... [FILE]...')),
3724 "tag":
3729 "tag":
3725 (tag,
3730 (tag,
3726 [('f', 'force', None, _('replace existing tag')),
3731 [('f', 'force', None, _('replace existing tag')),
3727 ('l', 'local', None, _('make the tag local')),
3732 ('l', 'local', None, _('make the tag local')),
3728 ('r', 'rev', '', _('revision to tag')),
3733 ('r', 'rev', '', _('revision to tag')),
3729 ('', 'remove', None, _('remove a tag')),
3734 ('', 'remove', None, _('remove a tag')),
3730 # -l/--local is already there, commitopts cannot be used
3735 # -l/--local is already there, commitopts cannot be used
3731 ('m', 'message', '', _('use <text> as commit message')),
3736 ('m', 'message', '', _('use <text> as commit message')),
3732 ] + commitopts2,
3737 ] + commitopts2,
3733 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3738 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3734 "tags": (tags, [], ''),
3739 "tags": (tags, [], ''),
3735 "tip":
3740 "tip":
3736 (tip,
3741 (tip,
3737 [('p', 'patch', None, _('show patch')),
3742 [('p', 'patch', None, _('show patch')),
3738 ('g', 'git', None, _('use git extended diff format')),
3743 ('g', 'git', None, _('use git extended diff format')),
3739 ] + templateopts,
3744 ] + templateopts,
3740 _('[-p] [-g]')),
3745 _('[-p] [-g]')),
3741 "unbundle":
3746 "unbundle":
3742 (unbundle,
3747 (unbundle,
3743 [('u', 'update', None,
3748 [('u', 'update', None,
3744 _('update to new branch head if changesets were unbundled'))],
3749 _('update to new branch head if changesets were unbundled'))],
3745 _('[-u] FILE...')),
3750 _('[-u] FILE...')),
3746 "^update|up|checkout|co":
3751 "^update|up|checkout|co":
3747 (update,
3752 (update,
3748 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3753 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3749 ('c', 'check', None, _('check for uncommitted changes')),
3754 ('c', 'check', None, _('check for uncommitted changes')),
3750 ('d', 'date', '', _('tipmost revision matching date')),
3755 ('d', 'date', '', _('tipmost revision matching date')),
3751 ('r', 'rev', '', _('revision'))],
3756 ('r', 'rev', '', _('revision'))],
3752 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3757 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3753 "verify": (verify, []),
3758 "verify": (verify, []),
3754 "version": (version_, []),
3759 "version": (version_, []),
3755 }
3760 }
3756
3761
3757 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3762 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3758 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3763 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3759 optionalrepo = ("identify paths serve showconfig debugancestor")
3764 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,372 +1,389 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
11 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
12 import lock, util, extensions, error, encoding
12 import lock, util, extensions, error, encoding, node
13 import merge as _merge
13 import merge as _merge
14 import verify as _verify
14 import verify as _verify
15 import errno, os, shutil
15 import errno, os, shutil
16
16
17 def _local(path):
17 def _local(path):
18 return (os.path.isfile(util.drop_scheme('file', path)) and
18 return (os.path.isfile(util.drop_scheme('file', path)) and
19 bundlerepo or localrepo)
19 bundlerepo or localrepo)
20
20
21 def parseurl(url, revs=[]):
21 def addbranchrevs(lrepo, repo, branches, revs):
22 '''parse url#branch, returning url, branch + revs'''
22 if not branches:
23 return revs or None, revs and revs[0] or None
24 branchmap = repo.branchmap()
25 revs = revs and list(revs) or []
26 for branch in branches:
27 if branch == '.':
28 if not lrepo or not lrepo.local():
29 raise util.Abort(_("dirstate branch not accessible"))
30 revs.append(lrepo.dirstate.branch())
31 else:
32 butf8 = encoding.fromlocal(branch)
33 if butf8 in branchmap:
34 revs.extend(node.hex(r) for r in reversed(branchmap[butf8]))
35 else:
36 revs.append(branch)
37 return revs, revs[0]
38
39 def parseurl(url, branches=None):
40 '''parse url#branch, returning url, branches+[branch]'''
23
41
24 if '#' not in url:
42 if '#' not in url:
25 return url, (revs or None), revs and revs[0] or None
43 return url, branches or []
26
27 url, branch = url.split('#', 1)
44 url, branch = url.split('#', 1)
28 checkout = revs and revs[0] or branch
45 return url, (branches or []) + [branch]
29 return url, (revs or []) + [branch], checkout
30
46
31 schemes = {
47 schemes = {
32 'bundle': bundlerepo,
48 'bundle': bundlerepo,
33 'file': _local,
49 'file': _local,
34 'http': httprepo,
50 'http': httprepo,
35 'https': httprepo,
51 'https': httprepo,
36 'ssh': sshrepo,
52 'ssh': sshrepo,
37 'static-http': statichttprepo,
53 'static-http': statichttprepo,
38 }
54 }
39
55
40 def _lookup(path):
56 def _lookup(path):
41 scheme = 'file'
57 scheme = 'file'
42 if path:
58 if path:
43 c = path.find(':')
59 c = path.find(':')
44 if c > 0:
60 if c > 0:
45 scheme = path[:c]
61 scheme = path[:c]
46 thing = schemes.get(scheme) or schemes['file']
62 thing = schemes.get(scheme) or schemes['file']
47 try:
63 try:
48 return thing(path)
64 return thing(path)
49 except TypeError:
65 except TypeError:
50 return thing
66 return thing
51
67
52 def islocal(repo):
68 def islocal(repo):
53 '''return true if repo or path is local'''
69 '''return true if repo or path is local'''
54 if isinstance(repo, str):
70 if isinstance(repo, str):
55 try:
71 try:
56 return _lookup(repo).islocal(repo)
72 return _lookup(repo).islocal(repo)
57 except AttributeError:
73 except AttributeError:
58 return False
74 return False
59 return repo.local()
75 return repo.local()
60
76
61 def repository(ui, path='', create=False):
77 def repository(ui, path='', create=False):
62 """return a repository object for the specified path"""
78 """return a repository object for the specified path"""
63 repo = _lookup(path).instance(ui, path, create)
79 repo = _lookup(path).instance(ui, path, create)
64 ui = getattr(repo, "ui", ui)
80 ui = getattr(repo, "ui", ui)
65 for name, module in extensions.extensions():
81 for name, module in extensions.extensions():
66 hook = getattr(module, 'reposetup', None)
82 hook = getattr(module, 'reposetup', None)
67 if hook:
83 if hook:
68 hook(ui, repo)
84 hook(ui, repo)
69 return repo
85 return repo
70
86
71 def defaultdest(source):
87 def defaultdest(source):
72 '''return default destination of clone if none is given'''
88 '''return default destination of clone if none is given'''
73 return os.path.basename(os.path.normpath(source))
89 return os.path.basename(os.path.normpath(source))
74
90
75 def localpath(path):
91 def localpath(path):
76 if path.startswith('file://localhost/'):
92 if path.startswith('file://localhost/'):
77 return path[16:]
93 return path[16:]
78 if path.startswith('file://'):
94 if path.startswith('file://'):
79 return path[7:]
95 return path[7:]
80 if path.startswith('file:'):
96 if path.startswith('file:'):
81 return path[5:]
97 return path[5:]
82 return path
98 return path
83
99
84 def share(ui, source, dest=None, update=True):
100 def share(ui, source, dest=None, update=True):
85 '''create a shared repository'''
101 '''create a shared repository'''
86
102
87 if not islocal(source):
103 if not islocal(source):
88 raise util.Abort(_('can only share local repositories'))
104 raise util.Abort(_('can only share local repositories'))
89
105
90 if not dest:
106 if not dest:
91 dest = defaultdest(source)
107 dest = defaultdest(source)
92 else:
108 else:
93 dest = ui.expandpath(dest)
109 dest = ui.expandpath(dest)
94
110
95 if isinstance(source, str):
111 if isinstance(source, str):
96 origsource = ui.expandpath(source)
112 origsource = ui.expandpath(source)
97 source, rev, checkout = parseurl(origsource, '')
113 source, branches = parseurl(origsource)
98 srcrepo = repository(ui, source)
114 srcrepo = repository(ui, source)
115 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
99 else:
116 else:
100 srcrepo = source
117 srcrepo = source
101 origsource = source = srcrepo.url()
118 origsource = source = srcrepo.url()
102 checkout = None
119 checkout = None
103
120
104 sharedpath = srcrepo.sharedpath # if our source is already sharing
121 sharedpath = srcrepo.sharedpath # if our source is already sharing
105
122
106 root = os.path.realpath(dest)
123 root = os.path.realpath(dest)
107 roothg = os.path.join(root, '.hg')
124 roothg = os.path.join(root, '.hg')
108
125
109 if os.path.exists(roothg):
126 if os.path.exists(roothg):
110 raise util.Abort(_('destination already exists'))
127 raise util.Abort(_('destination already exists'))
111
128
112 if not os.path.isdir(root):
129 if not os.path.isdir(root):
113 os.mkdir(root)
130 os.mkdir(root)
114 os.mkdir(roothg)
131 os.mkdir(roothg)
115
132
116 requirements = ''
133 requirements = ''
117 try:
134 try:
118 requirements = srcrepo.opener('requires').read()
135 requirements = srcrepo.opener('requires').read()
119 except IOError, inst:
136 except IOError, inst:
120 if inst.errno != errno.ENOENT:
137 if inst.errno != errno.ENOENT:
121 raise
138 raise
122
139
123 requirements += 'shared\n'
140 requirements += 'shared\n'
124 file(os.path.join(roothg, 'requires'), 'w').write(requirements)
141 file(os.path.join(roothg, 'requires'), 'w').write(requirements)
125 file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
142 file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
126
143
127 default = srcrepo.ui.config('paths', 'default')
144 default = srcrepo.ui.config('paths', 'default')
128 if default:
145 if default:
129 f = file(os.path.join(roothg, 'hgrc'), 'w')
146 f = file(os.path.join(roothg, 'hgrc'), 'w')
130 f.write('[paths]\ndefault = %s\n' % default)
147 f.write('[paths]\ndefault = %s\n' % default)
131 f.close()
148 f.close()
132
149
133 r = repository(ui, root)
150 r = repository(ui, root)
134
151
135 if update:
152 if update:
136 r.ui.status(_("updating working directory\n"))
153 r.ui.status(_("updating working directory\n"))
137 if update is not True:
154 if update is not True:
138 checkout = update
155 checkout = update
139 for test in (checkout, 'default', 'tip'):
156 for test in (checkout, 'default', 'tip'):
140 if test is None:
157 if test is None:
141 continue
158 continue
142 try:
159 try:
143 uprev = r.lookup(test)
160 uprev = r.lookup(test)
144 break
161 break
145 except error.RepoLookupError:
162 except error.RepoLookupError:
146 continue
163 continue
147 _update(r, uprev)
164 _update(r, uprev)
148
165
149 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
166 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
150 stream=False):
167 stream=False):
151 """Make a copy of an existing repository.
168 """Make a copy of an existing repository.
152
169
153 Create a copy of an existing repository in a new directory. The
170 Create a copy of an existing repository in a new directory. The
154 source and destination are URLs, as passed to the repository
171 source and destination are URLs, as passed to the repository
155 function. Returns a pair of repository objects, the source and
172 function. Returns a pair of repository objects, the source and
156 newly created destination.
173 newly created destination.
157
174
158 The location of the source is added to the new repository's
175 The location of the source is added to the new repository's
159 .hg/hgrc file, as the default to be used for future pulls and
176 .hg/hgrc file, as the default to be used for future pulls and
160 pushes.
177 pushes.
161
178
162 If an exception is raised, the partly cloned/updated destination
179 If an exception is raised, the partly cloned/updated destination
163 repository will be deleted.
180 repository will be deleted.
164
181
165 Arguments:
182 Arguments:
166
183
167 source: repository object or URL
184 source: repository object or URL
168
185
169 dest: URL of destination repository to create (defaults to base
186 dest: URL of destination repository to create (defaults to base
170 name of source repository)
187 name of source repository)
171
188
172 pull: always pull from source repository, even in local case
189 pull: always pull from source repository, even in local case
173
190
174 stream: stream raw data uncompressed from repository (fast over
191 stream: stream raw data uncompressed from repository (fast over
175 LAN, slow over WAN)
192 LAN, slow over WAN)
176
193
177 rev: revision to clone up to (implies pull=True)
194 rev: revision to clone up to (implies pull=True)
178
195
179 update: update working directory after clone completes, if
196 update: update working directory after clone completes, if
180 destination is local repository (True means update to default rev,
197 destination is local repository (True means update to default rev,
181 anything else is treated as a revision)
198 anything else is treated as a revision)
182 """
199 """
183
200
184 if isinstance(source, str):
201 if isinstance(source, str):
185 origsource = ui.expandpath(source)
202 origsource = ui.expandpath(source)
186 source, rev, checkout = parseurl(origsource, rev)
203 source, branch = parseurl(origsource)
187 src_repo = repository(ui, source)
204 src_repo = repository(ui, source)
188 else:
205 else:
189 src_repo = source
206 src_repo = source
190 origsource = source = src_repo.url()
207 origsource = source = src_repo.url()
191 checkout = rev and rev[0] or None
208 rev, checkout = addbranchrevs(src_repo, src_repo, branch, rev)
192
209
193 if dest is None:
210 if dest is None:
194 dest = defaultdest(source)
211 dest = defaultdest(source)
195 ui.status(_("destination directory: %s\n") % dest)
212 ui.status(_("destination directory: %s\n") % dest)
196 else:
213 else:
197 dest = ui.expandpath(dest)
214 dest = ui.expandpath(dest)
198
215
199 dest = localpath(dest)
216 dest = localpath(dest)
200 source = localpath(source)
217 source = localpath(source)
201
218
202 if os.path.exists(dest):
219 if os.path.exists(dest):
203 if not os.path.isdir(dest):
220 if not os.path.isdir(dest):
204 raise util.Abort(_("destination '%s' already exists") % dest)
221 raise util.Abort(_("destination '%s' already exists") % dest)
205 elif os.listdir(dest):
222 elif os.listdir(dest):
206 raise util.Abort(_("destination '%s' is not empty") % dest)
223 raise util.Abort(_("destination '%s' is not empty") % dest)
207
224
208 class DirCleanup(object):
225 class DirCleanup(object):
209 def __init__(self, dir_):
226 def __init__(self, dir_):
210 self.rmtree = shutil.rmtree
227 self.rmtree = shutil.rmtree
211 self.dir_ = dir_
228 self.dir_ = dir_
212 def close(self):
229 def close(self):
213 self.dir_ = None
230 self.dir_ = None
214 def cleanup(self):
231 def cleanup(self):
215 if self.dir_:
232 if self.dir_:
216 self.rmtree(self.dir_, True)
233 self.rmtree(self.dir_, True)
217
234
218 src_lock = dest_lock = dir_cleanup = None
235 src_lock = dest_lock = dir_cleanup = None
219 try:
236 try:
220 if islocal(dest):
237 if islocal(dest):
221 dir_cleanup = DirCleanup(dest)
238 dir_cleanup = DirCleanup(dest)
222
239
223 abspath = origsource
240 abspath = origsource
224 copy = False
241 copy = False
225 if src_repo.cancopy() and islocal(dest):
242 if src_repo.cancopy() and islocal(dest):
226 abspath = os.path.abspath(util.drop_scheme('file', origsource))
243 abspath = os.path.abspath(util.drop_scheme('file', origsource))
227 copy = not pull and not rev
244 copy = not pull and not rev
228
245
229 if copy:
246 if copy:
230 try:
247 try:
231 # we use a lock here because if we race with commit, we
248 # we use a lock here because if we race with commit, we
232 # can end up with extra data in the cloned revlogs that's
249 # can end up with extra data in the cloned revlogs that's
233 # not pointed to by changesets, thus causing verify to
250 # not pointed to by changesets, thus causing verify to
234 # fail
251 # fail
235 src_lock = src_repo.lock(wait=False)
252 src_lock = src_repo.lock(wait=False)
236 except error.LockError:
253 except error.LockError:
237 copy = False
254 copy = False
238
255
239 if copy:
256 if copy:
240 src_repo.hook('preoutgoing', throw=True, source='clone')
257 src_repo.hook('preoutgoing', throw=True, source='clone')
241 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
258 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
242 if not os.path.exists(dest):
259 if not os.path.exists(dest):
243 os.mkdir(dest)
260 os.mkdir(dest)
244 else:
261 else:
245 # only clean up directories we create ourselves
262 # only clean up directories we create ourselves
246 dir_cleanup.dir_ = hgdir
263 dir_cleanup.dir_ = hgdir
247 try:
264 try:
248 dest_path = hgdir
265 dest_path = hgdir
249 os.mkdir(dest_path)
266 os.mkdir(dest_path)
250 except OSError, inst:
267 except OSError, inst:
251 if inst.errno == errno.EEXIST:
268 if inst.errno == errno.EEXIST:
252 dir_cleanup.close()
269 dir_cleanup.close()
253 raise util.Abort(_("destination '%s' already exists")
270 raise util.Abort(_("destination '%s' already exists")
254 % dest)
271 % dest)
255 raise
272 raise
256
273
257 for f in src_repo.store.copylist():
274 for f in src_repo.store.copylist():
258 src = os.path.join(src_repo.sharedpath, f)
275 src = os.path.join(src_repo.sharedpath, f)
259 dst = os.path.join(dest_path, f)
276 dst = os.path.join(dest_path, f)
260 dstbase = os.path.dirname(dst)
277 dstbase = os.path.dirname(dst)
261 if dstbase and not os.path.exists(dstbase):
278 if dstbase and not os.path.exists(dstbase):
262 os.mkdir(dstbase)
279 os.mkdir(dstbase)
263 if os.path.exists(src):
280 if os.path.exists(src):
264 if dst.endswith('data'):
281 if dst.endswith('data'):
265 # lock to avoid premature writing to the target
282 # lock to avoid premature writing to the target
266 dest_lock = lock.lock(os.path.join(dstbase, "lock"))
283 dest_lock = lock.lock(os.path.join(dstbase, "lock"))
267 util.copyfiles(src, dst)
284 util.copyfiles(src, dst)
268
285
269 # we need to re-init the repo after manually copying the data
286 # we need to re-init the repo after manually copying the data
270 # into it
287 # into it
271 dest_repo = repository(ui, dest)
288 dest_repo = repository(ui, dest)
272 src_repo.hook('outgoing', source='clone', node='0'*40)
289 src_repo.hook('outgoing', source='clone', node='0'*40)
273 else:
290 else:
274 try:
291 try:
275 dest_repo = repository(ui, dest, create=True)
292 dest_repo = repository(ui, dest, create=True)
276 except OSError, inst:
293 except OSError, inst:
277 if inst.errno == errno.EEXIST:
294 if inst.errno == errno.EEXIST:
278 dir_cleanup.close()
295 dir_cleanup.close()
279 raise util.Abort(_("destination '%s' already exists")
296 raise util.Abort(_("destination '%s' already exists")
280 % dest)
297 % dest)
281 raise
298 raise
282
299
283 revs = None
300 revs = None
284 if rev:
301 if rev:
285 if 'lookup' not in src_repo.capabilities:
302 if 'lookup' not in src_repo.capabilities:
286 raise util.Abort(_("src repository does not support "
303 raise util.Abort(_("src repository does not support "
287 "revision lookup and so doesn't "
304 "revision lookup and so doesn't "
288 "support clone by revision"))
305 "support clone by revision"))
289 revs = [src_repo.lookup(r) for r in rev]
306 revs = [src_repo.lookup(r) for r in rev]
290 checkout = revs[0]
307 checkout = revs[0]
291 if dest_repo.local():
308 if dest_repo.local():
292 dest_repo.clone(src_repo, heads=revs, stream=stream)
309 dest_repo.clone(src_repo, heads=revs, stream=stream)
293 elif src_repo.local():
310 elif src_repo.local():
294 src_repo.push(dest_repo, revs=revs)
311 src_repo.push(dest_repo, revs=revs)
295 else:
312 else:
296 raise util.Abort(_("clone from remote to remote not supported"))
313 raise util.Abort(_("clone from remote to remote not supported"))
297
314
298 if dir_cleanup:
315 if dir_cleanup:
299 dir_cleanup.close()
316 dir_cleanup.close()
300
317
301 if dest_repo.local():
318 if dest_repo.local():
302 fp = dest_repo.opener("hgrc", "w", text=True)
319 fp = dest_repo.opener("hgrc", "w", text=True)
303 fp.write("[paths]\n")
320 fp.write("[paths]\n")
304 fp.write("default = %s\n" % abspath)
321 fp.write("default = %s\n" % abspath)
305 fp.close()
322 fp.close()
306
323
307 dest_repo.ui.setconfig('paths', 'default', abspath)
324 dest_repo.ui.setconfig('paths', 'default', abspath)
308
325
309 if update:
326 if update:
310 if update is not True:
327 if update is not True:
311 checkout = update
328 checkout = update
312 if src_repo.local():
329 if src_repo.local():
313 checkout = src_repo.lookup(update)
330 checkout = src_repo.lookup(update)
314 for test in (checkout, 'default', 'tip'):
331 for test in (checkout, 'default', 'tip'):
315 if test is None:
332 if test is None:
316 continue
333 continue
317 try:
334 try:
318 uprev = dest_repo.lookup(test)
335 uprev = dest_repo.lookup(test)
319 break
336 break
320 except error.RepoLookupError:
337 except error.RepoLookupError:
321 continue
338 continue
322 bn = dest_repo[uprev].branch()
339 bn = dest_repo[uprev].branch()
323 dest_repo.ui.status(_("updating to branch %s\n")
340 dest_repo.ui.status(_("updating to branch %s\n")
324 % encoding.tolocal(bn))
341 % encoding.tolocal(bn))
325 _update(dest_repo, uprev)
342 _update(dest_repo, uprev)
326
343
327 return src_repo, dest_repo
344 return src_repo, dest_repo
328 finally:
345 finally:
329 release(src_lock, dest_lock)
346 release(src_lock, dest_lock)
330 if dir_cleanup is not None:
347 if dir_cleanup is not None:
331 dir_cleanup.cleanup()
348 dir_cleanup.cleanup()
332
349
333 def _showstats(repo, stats):
350 def _showstats(repo, stats):
334 repo.ui.status(_("%d files updated, %d files merged, "
351 repo.ui.status(_("%d files updated, %d files merged, "
335 "%d files removed, %d files unresolved\n") % stats)
352 "%d files removed, %d files unresolved\n") % stats)
336
353
337 def update(repo, node):
354 def update(repo, node):
338 """update the working directory to node, merging linear changes"""
355 """update the working directory to node, merging linear changes"""
339 stats = _merge.update(repo, node, False, False, None)
356 stats = _merge.update(repo, node, False, False, None)
340 _showstats(repo, stats)
357 _showstats(repo, stats)
341 if stats[3]:
358 if stats[3]:
342 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
359 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
343 return stats[3] > 0
360 return stats[3] > 0
344
361
345 # naming conflict in clone()
362 # naming conflict in clone()
346 _update = update
363 _update = update
347
364
348 def clean(repo, node, show_stats=True):
365 def clean(repo, node, show_stats=True):
349 """forcibly switch the working directory to node, clobbering changes"""
366 """forcibly switch the working directory to node, clobbering changes"""
350 stats = _merge.update(repo, node, False, True, None)
367 stats = _merge.update(repo, node, False, True, None)
351 if show_stats:
368 if show_stats:
352 _showstats(repo, stats)
369 _showstats(repo, stats)
353 return stats[3] > 0
370 return stats[3] > 0
354
371
355 def merge(repo, node, force=None, remind=True):
372 def merge(repo, node, force=None, remind=True):
356 """branch merge with node, resolving changes"""
373 """branch merge with node, resolving changes"""
357 stats = _merge.update(repo, node, True, force, False)
374 stats = _merge.update(repo, node, True, force, False)
358 _showstats(repo, stats)
375 _showstats(repo, stats)
359 if stats[3]:
376 if stats[3]:
360 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
377 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
361 "or 'hg update -C' to abandon\n"))
378 "or 'hg update -C' to abandon\n"))
362 elif remind:
379 elif remind:
363 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
380 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
364 return stats[3] > 0
381 return stats[3] > 0
365
382
366 def revert(repo, node, choose):
383 def revert(repo, node, choose):
367 """revert changes to revision in node without updating dirstate"""
384 """revert changes to revision in node without updating dirstate"""
368 return _merge.update(repo, node, False, True, choose)[3] > 0
385 return _merge.update(repo, node, False, True, choose)[3] > 0
369
386
370 def verify(repo):
387 def verify(repo):
371 """verify the consistency of a repository"""
388 """verify the consistency of a repository"""
372 return _verify.verify(repo)
389 return _verify.verify(repo)
@@ -1,12 +1,12 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 from mercurial.hg import parseurl
3 from mercurial.hg import parseurl
4
4
5 def testparse(url, rev=[]):
5 def testparse(url, branch=[]):
6 print '%s, revs: %r, checkout: %r' % parseurl(url, rev)
6 print '%s, branches: %r' % parseurl(url, branch)
7
7
8 testparse('http://example.com/no/anchor')
8 testparse('http://example.com/no/anchor')
9 testparse('http://example.com/an/anchor#foo')
9 testparse('http://example.com/an/anchor#foo')
10 testparse('http://example.com/no/anchor/revs', rev=['foo'])
10 testparse('http://example.com/no/anchor/branches', branch=['foo'])
11 testparse('http://example.com/an/anchor/revs#bar', rev=['foo'])
11 testparse('http://example.com/an/anchor/branches#bar', branch=['foo'])
12 testparse('http://example.com/an/anchor/rev-None#foo', rev=None)
12 testparse('http://example.com/an/anchor/branches-None#foo', branch=None)
@@ -1,5 +1,5 b''
1 http://example.com/no/anchor, revs: None, checkout: None
1 http://example.com/no/anchor, branches: []
2 http://example.com/an/anchor, revs: ['foo'], checkout: 'foo'
2 http://example.com/an/anchor, branches: ['foo']
3 http://example.com/no/anchor/revs, revs: ['foo'], checkout: 'foo'
3 http://example.com/no/anchor/branches, branches: ['foo']
4 http://example.com/an/anchor/revs, revs: ['foo', 'bar'], checkout: 'foo'
4 http://example.com/an/anchor/branches, branches: ['foo', 'bar']
5 http://example.com/an/anchor/rev-None, revs: ['foo'], checkout: 'foo'
5 http://example.com/an/anchor/branches-None, branches: ['foo']
General Comments 0
You need to be logged in to leave comments. Login now