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