##// END OF EJS Templates
merge with crew-stable
Alexis S. L. Carvalho -
r4896:ee04732f merge default
parent child Browse files
Show More
@@ -1,351 +1,353
1 1 # convert.py Foreign SCM converter
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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from common import NoRepo, converter_source, converter_sink
9 9 from cvs import convert_cvs
10 10 from git import convert_git
11 11 from hg import convert_mercurial
12 12 from subversion import convert_svn
13 13
14 14 import os, shutil
15 15 from mercurial import hg, ui, util, commands
16 16
17 17 commands.norepo += " convert"
18 18
19 19 converters = [convert_cvs, convert_git, convert_svn, convert_mercurial]
20 20
21 21 def convertsource(ui, path, **opts):
22 22 for c in converters:
23 23 if not hasattr(c, 'getcommit'):
24 24 continue
25 25 try:
26 26 return c(ui, path, **opts)
27 27 except NoRepo:
28 28 pass
29 29 raise util.Abort('%s: unknown repository type' % path)
30 30
31 31 def convertsink(ui, path):
32 32 if not os.path.isdir(path):
33 33 raise util.Abort("%s: not a directory" % path)
34 34 for c in converters:
35 35 if not hasattr(c, 'putcommit'):
36 36 continue
37 37 try:
38 38 return c(ui, path)
39 39 except NoRepo:
40 40 pass
41 41 raise util.Abort('%s: unknown repository type' % path)
42 42
43 43 class convert(object):
44 44 def __init__(self, ui, source, dest, mapfile, opts):
45 45
46 46 self.source = source
47 47 self.dest = dest
48 48 self.ui = ui
49 49 self.opts = opts
50 50 self.commitcache = {}
51 51 self.mapfile = mapfile
52 52 self.mapfilefd = None
53 53 self.authors = {}
54 54 self.authorfile = None
55 55
56 56 self.map = {}
57 57 try:
58 58 origmapfile = open(self.mapfile, 'r')
59 59 for l in origmapfile:
60 60 sv, dv = l[:-1].split()
61 61 self.map[sv] = dv
62 62 origmapfile.close()
63 63 except IOError:
64 64 pass
65 65
66 66 # Read first the dst author map if any
67 67 authorfile = self.dest.authorfile()
68 68 if authorfile and os.path.exists(authorfile):
69 69 self.readauthormap(authorfile)
70 70 # Extend/Override with new author map if necessary
71 71 if opts.get('authors'):
72 72 self.readauthormap(opts.get('authors'))
73 73 self.authorfile = self.dest.authorfile()
74 74
75 75 def walktree(self, heads):
76 76 '''Return a mapping that identifies the uncommitted parents of every
77 77 uncommitted changeset.'''
78 78 visit = heads
79 79 known = {}
80 80 parents = {}
81 81 while visit:
82 82 n = visit.pop(0)
83 83 if n in known or n in self.map: continue
84 84 known[n] = 1
85 85 self.commitcache[n] = self.source.getcommit(n)
86 86 cp = self.commitcache[n].parents
87 87 parents[n] = []
88 88 for p in cp:
89 89 parents[n].append(p)
90 90 visit.append(p)
91 91
92 92 return parents
93 93
94 94 def toposort(self, parents):
95 95 '''Return an ordering such that every uncommitted changeset is
96 96 preceeded by all its uncommitted ancestors.'''
97 97 visit = parents.keys()
98 98 seen = {}
99 99 children = {}
100 100
101 101 while visit:
102 102 n = visit.pop(0)
103 103 if n in seen: continue
104 104 seen[n] = 1
105 105 # Ensure that nodes without parents are present in the 'children'
106 106 # mapping.
107 107 children.setdefault(n, [])
108 108 for p in parents[n]:
109 109 if not p in self.map:
110 110 visit.append(p)
111 111 children.setdefault(p, []).append(n)
112 112
113 113 s = []
114 114 removed = {}
115 115 visit = children.keys()
116 116 while visit:
117 117 n = visit.pop(0)
118 118 if n in removed: continue
119 119 dep = 0
120 120 if n in parents:
121 121 for p in parents[n]:
122 122 if p in self.map: continue
123 123 if p not in removed:
124 124 # we're still dependent
125 125 visit.append(n)
126 126 dep = 1
127 127 break
128 128
129 129 if not dep:
130 130 # all n's parents are in the list
131 131 removed[n] = 1
132 132 if n not in self.map:
133 133 s.append(n)
134 134 if n in children:
135 135 for c in children[n]:
136 136 visit.insert(0, c)
137 137
138 138 if self.opts.get('datesort'):
139 139 depth = {}
140 140 for n in s:
141 141 depth[n] = 0
142 142 pl = [p for p in self.commitcache[n].parents
143 143 if p not in self.map]
144 144 if pl:
145 145 depth[n] = max([depth[p] for p in pl]) + 1
146 146
147 147 s = [(depth[n], self.commitcache[n].date, n) for n in s]
148 148 s.sort()
149 149 s = [e[2] for e in s]
150 150
151 151 return s
152 152
153 153 def mapentry(self, src, dst):
154 154 if self.mapfilefd is None:
155 155 try:
156 156 self.mapfilefd = open(self.mapfile, "a")
157 157 except IOError, (errno, strerror):
158 158 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
159 159 self.map[src] = dst
160 160 self.mapfilefd.write("%s %s\n" % (src, dst))
161 161 self.mapfilefd.flush()
162 162
163 163 def writeauthormap(self):
164 164 authorfile = self.authorfile
165 165 if authorfile:
166 166 self.ui.status('Writing author map file %s\n' % authorfile)
167 167 ofile = open(authorfile, 'w+')
168 168 for author in self.authors:
169 169 ofile.write("%s=%s\n" % (author, self.authors[author]))
170 170 ofile.close()
171 171
172 172 def readauthormap(self, authorfile):
173 173 afile = open(authorfile, 'r')
174 174 for line in afile:
175 175 try:
176 176 srcauthor = line.split('=')[0].strip()
177 177 dstauthor = line.split('=')[1].strip()
178 178 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
179 179 self.ui.status(
180 180 'Overriding mapping for author %s, was %s, will be %s\n'
181 181 % (srcauthor, self.authors[srcauthor], dstauthor))
182 182 else:
183 183 self.ui.debug('Mapping author %s to %s\n'
184 184 % (srcauthor, dstauthor))
185 185 self.authors[srcauthor] = dstauthor
186 186 except IndexError:
187 187 self.ui.warn(
188 188 'Ignoring bad line in author file map %s: %s\n'
189 189 % (authorfile, line))
190 190 afile.close()
191 191
192 192 def copy(self, rev):
193 193 c = self.commitcache[rev]
194 194 files = self.source.getchanges(rev)
195 195
196 196 do_copies = (hasattr(c, 'copies') and hasattr(self.dest, 'copyfile'))
197 197
198 198 for f, v in files:
199 199 try:
200 200 data = self.source.getfile(f, v)
201 201 except IOError, inst:
202 202 self.dest.delfile(f)
203 203 else:
204 204 e = self.source.getmode(f, v)
205 205 self.dest.putfile(f, e, data)
206 206 if do_copies:
207 207 if f in c.copies:
208 208 # Merely marks that a copy happened.
209 209 self.dest.copyfile(c.copies[f], f)
210 210
211 211
212 212 r = [self.map[v] for v in c.parents]
213 213 f = [f for f, v in files]
214 214 newnode = self.dest.putcommit(f, r, c)
215 215 self.mapentry(rev, newnode)
216 216
217 217 def convert(self):
218 218 try:
219 219 self.source.setrevmap(self.map)
220 220 self.ui.status("scanning source...\n")
221 221 heads = self.source.getheads()
222 222 parents = self.walktree(heads)
223 223 self.ui.status("sorting...\n")
224 224 t = self.toposort(parents)
225 225 num = len(t)
226 226 c = None
227 227
228 228 self.ui.status("converting...\n")
229 229 for c in t:
230 230 num -= 1
231 231 desc = self.commitcache[c].desc
232 232 if "\n" in desc:
233 233 desc = desc.splitlines()[0]
234 234 author = self.commitcache[c].author
235 235 author = self.authors.get(author, author)
236 236 self.commitcache[c].author = author
237 237 self.ui.status("%d %s\n" % (num, desc))
238 238 self.copy(c)
239 239
240 240 tags = self.source.gettags()
241 241 ctags = {}
242 242 for k in tags:
243 243 v = tags[k]
244 244 if v in self.map:
245 245 ctags[k] = self.map[v]
246 246
247 247 if c and ctags:
248 248 nrev = self.dest.puttags(ctags)
249 249 # write another hash correspondence to override the previous
250 250 # one so we don't end up with extra tag heads
251 251 if nrev:
252 252 self.mapentry(c, nrev)
253 253
254 254 self.writeauthormap()
255 255 finally:
256 256 self.cleanup()
257 257
258 258 def cleanup(self):
259 259 if self.mapfilefd:
260 260 self.mapfilefd.close()
261 261
262 262 def _convert(ui, src, dest=None, mapfile=None, **opts):
263 263 '''Convert a foreign SCM repository to a Mercurial one.
264 264
265 265 Accepted source formats:
266 266 - GIT
267 267 - CVS
268 268 - SVN
269 269
270 270 Accepted destination formats:
271 271 - Mercurial
272 272
273 273 If no revision is given, all revisions will be converted. Otherwise,
274 274 convert will only import up to the named revision (given in a format
275 275 understood by the source).
276 276
277 277 If no destination directory name is specified, it defaults to the
278 278 basename of the source with '-hg' appended. If the destination
279 279 repository doesn't exist, it will be created.
280 280
281 281 If <mapfile> isn't given, it will be put in a default location
282 282 (<dest>/.hg/shamap by default). The <mapfile> is a simple text
283 283 file that maps each source commit ID to the destination ID for
284 284 that revision, like so:
285 285 <source ID> <destination ID>
286 286
287 287 If the file doesn't exist, it's automatically created. It's updated
288 288 on each commit copied, so convert-repo can be interrupted and can
289 289 be run repeatedly to copy new commits.
290 290
291 291 The [username mapping] file is a simple text file that maps each source
292 292 commit author to a destination commit author. It is handy for source SCMs
293 293 that use unix logins to identify authors (eg: CVS). One line per author
294 294 mapping and the line format is:
295 295 srcauthor=whatever string you want
296 296 '''
297 297
298 util._encoding = 'UTF-8'
299
298 300 if not dest:
299 301 dest = hg.defaultdest(src) + "-hg"
300 302 ui.status("assuming destination %s\n" % dest)
301 303
302 304 # Try to be smart and initalize things when required
303 305 created = False
304 306 if os.path.isdir(dest):
305 307 if len(os.listdir(dest)) > 0:
306 308 try:
307 309 hg.repository(ui, dest)
308 310 ui.status("destination %s is a Mercurial repository\n" % dest)
309 311 except hg.RepoError:
310 312 raise util.Abort(
311 313 "destination directory %s is not empty.\n"
312 314 "Please specify an empty directory to be initialized\n"
313 315 "or an already initialized mercurial repository"
314 316 % dest)
315 317 else:
316 318 ui.status("initializing destination %s repository\n" % dest)
317 319 hg.repository(ui, dest, create=True)
318 320 created = True
319 321 elif os.path.exists(dest):
320 322 raise util.Abort("destination %s exists and is not a directory" % dest)
321 323 else:
322 324 ui.status("initializing destination %s repository\n" % dest)
323 325 hg.repository(ui, dest, create=True)
324 326 created = True
325 327
326 328 destc = convertsink(ui, dest)
327 329
328 330 try:
329 331 srcc = convertsource(ui, src, rev=opts.get('rev'))
330 332 except Exception:
331 333 if created:
332 334 shutil.rmtree(dest, True)
333 335 raise
334 336
335 337 if not mapfile:
336 338 try:
337 339 mapfile = destc.mapfile()
338 340 except:
339 341 mapfile = os.path.join(destc, "map")
340 342
341 343 c = convert(ui, srcc, destc, mapfile, opts)
342 344 c.convert()
343 345
344 346 cmdtable = {
345 347 "convert":
346 348 (_convert,
347 349 [('A', 'authors', '', 'username mapping filename'),
348 350 ('r', 'rev', '', 'import up to target revision REV'),
349 351 ('', 'datesort', None, 'try to sort changesets by date')],
350 352 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
351 353 }
@@ -1,3156 +1,3159
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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import demandimport; demandimport.enable()
9 9 from node import *
10 10 from i18n import _
11 11 import bisect, os, re, sys, urllib, shlex, stat
12 12 import ui, hg, util, revlog, bundlerepo, extensions
13 13 import difflib, patch, time, help, mdiff, tempfile
14 14 import errno, version, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16 16
17 17 # Commands start here, listed alphabetically
18 18
19 19 def add(ui, repo, *pats, **opts):
20 20 """add the specified files on the next commit
21 21
22 22 Schedule files to be version controlled and added to the repository.
23 23
24 24 The files will be added to the repository at the next commit. To
25 25 undo an add before that, see hg revert.
26 26
27 27 If no names are given, add all files in the repository.
28 28 """
29 29
30 30 names = []
31 31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
32 32 if exact:
33 33 if ui.verbose:
34 34 ui.status(_('adding %s\n') % rel)
35 35 names.append(abs)
36 36 elif repo.dirstate.state(abs) == '?':
37 37 ui.status(_('adding %s\n') % rel)
38 38 names.append(abs)
39 39 if not opts.get('dry_run'):
40 40 repo.add(names)
41 41
42 42 def addremove(ui, repo, *pats, **opts):
43 43 """add all new files, delete all missing files
44 44
45 45 Add all new files and remove all missing files from the repository.
46 46
47 47 New files are ignored if they match any of the patterns in .hgignore. As
48 48 with add, these changes take effect at the next commit.
49 49
50 50 Use the -s option to detect renamed files. With a parameter > 0,
51 51 this compares every removed file with every added file and records
52 52 those similar enough as renames. This option takes a percentage
53 53 between 0 (disabled) and 100 (files must be identical) as its
54 54 parameter. Detecting renamed files this way can be expensive.
55 55 """
56 56 sim = float(opts.get('similarity') or 0)
57 57 if sim < 0 or sim > 100:
58 58 raise util.Abort(_('similarity must be between 0 and 100'))
59 59 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
60 60
61 61 def annotate(ui, repo, *pats, **opts):
62 62 """show changeset information per file line
63 63
64 64 List changes in files, showing the revision id responsible for each line
65 65
66 66 This command is useful to discover who did a change or when a change took
67 67 place.
68 68
69 69 Without the -a option, annotate will avoid processing files it
70 70 detects as binary. With -a, annotate will generate an annotation
71 71 anyway, probably with undesirable results.
72 72 """
73 73 getdate = util.cachefunc(lambda x: util.datestr(x[0].date()))
74 74
75 75 if not pats:
76 76 raise util.Abort(_('at least one file name or pattern required'))
77 77
78 78 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
79 79 ('number', lambda x: str(x[0].rev())),
80 80 ('changeset', lambda x: short(x[0].node())),
81 81 ('date', getdate),
82 82 ('follow', lambda x: x[0].path()),
83 83 ]
84 84
85 85 if (not opts['user'] and not opts['changeset'] and not opts['date']
86 86 and not opts['follow']):
87 87 opts['number'] = 1
88 88
89 89 linenumber = opts.get('line_number') is not None
90 90 if (linenumber and (not opts['changeset']) and (not opts['number'])):
91 91 raise util.Abort(_('at least one of -n/-c is required for -l'))
92 92
93 93 funcmap = [func for op, func in opmap if opts.get(op)]
94 94 if linenumber:
95 95 lastfunc = funcmap[-1]
96 96 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
97 97
98 98 ctx = repo.changectx(opts['rev'])
99 99
100 100 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
101 101 node=ctx.node()):
102 102 fctx = ctx.filectx(abs)
103 103 if not opts['text'] and util.binary(fctx.data()):
104 104 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
105 105 continue
106 106
107 107 lines = fctx.annotate(follow=opts.get('follow'),
108 108 linenumber=linenumber)
109 109 pieces = []
110 110
111 111 for f in funcmap:
112 112 l = [f(n) for n, dummy in lines]
113 113 if l:
114 114 m = max(map(len, l))
115 115 pieces.append(["%*s" % (m, x) for x in l])
116 116
117 117 if pieces:
118 118 for p, l in zip(zip(*pieces), lines):
119 119 ui.write("%s: %s" % (" ".join(p), l[1]))
120 120
121 121 def archive(ui, repo, dest, **opts):
122 122 '''create unversioned archive of a repository revision
123 123
124 124 By default, the revision used is the parent of the working
125 125 directory; use "-r" to specify a different revision.
126 126
127 127 To specify the type of archive to create, use "-t". Valid
128 128 types are:
129 129
130 130 "files" (default): a directory full of files
131 131 "tar": tar archive, uncompressed
132 132 "tbz2": tar archive, compressed using bzip2
133 133 "tgz": tar archive, compressed using gzip
134 134 "uzip": zip archive, uncompressed
135 135 "zip": zip archive, compressed using deflate
136 136
137 137 The exact name of the destination archive or directory is given
138 138 using a format string; see "hg help export" for details.
139 139
140 140 Each member added to an archive file has a directory prefix
141 141 prepended. Use "-p" to specify a format string for the prefix.
142 142 The default is the basename of the archive, with suffixes removed.
143 143 '''
144 144
145 145 ctx = repo.changectx(opts['rev'])
146 146 if not ctx:
147 147 raise util.Abort(_('repository has no revisions'))
148 148 node = ctx.node()
149 149 dest = cmdutil.make_filename(repo, dest, node)
150 150 if os.path.realpath(dest) == repo.root:
151 151 raise util.Abort(_('repository root cannot be destination'))
152 152 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
153 153 kind = opts.get('type') or 'files'
154 154 prefix = opts['prefix']
155 155 if dest == '-':
156 156 if kind == 'files':
157 157 raise util.Abort(_('cannot archive plain files to stdout'))
158 158 dest = sys.stdout
159 159 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
160 160 prefix = cmdutil.make_filename(repo, prefix, node)
161 161 archival.archive(repo, dest, node, kind, not opts['no_decode'],
162 162 matchfn, prefix)
163 163
164 164 def backout(ui, repo, node=None, rev=None, **opts):
165 165 '''reverse effect of earlier changeset
166 166
167 167 Commit the backed out changes as a new changeset. The new
168 168 changeset is a child of the backed out changeset.
169 169
170 170 If you back out a changeset other than the tip, a new head is
171 171 created. This head is the parent of the working directory. If
172 172 you back out an old changeset, your working directory will appear
173 173 old after the backout. You should merge the backout changeset
174 174 with another head.
175 175
176 176 The --merge option remembers the parent of the working directory
177 177 before starting the backout, then merges the new head with that
178 178 changeset afterwards. This saves you from doing the merge by
179 179 hand. The result of this merge is not committed, as for a normal
180 180 merge.'''
181 181 if rev and node:
182 182 raise util.Abort(_("please specify just one revision"))
183 183
184 184 if not rev:
185 185 rev = node
186 186
187 187 if not rev:
188 188 raise util.Abort(_("please specify a revision to backout"))
189 189
190 190 cmdutil.bail_if_changed(repo)
191 191 op1, op2 = repo.dirstate.parents()
192 192 if op2 != nullid:
193 193 raise util.Abort(_('outstanding uncommitted merge'))
194 194 node = repo.lookup(rev)
195 195 p1, p2 = repo.changelog.parents(node)
196 196 if p1 == nullid:
197 197 raise util.Abort(_('cannot back out a change with no parents'))
198 198 if p2 != nullid:
199 199 if not opts['parent']:
200 200 raise util.Abort(_('cannot back out a merge changeset without '
201 201 '--parent'))
202 202 p = repo.lookup(opts['parent'])
203 203 if p not in (p1, p2):
204 204 raise util.Abort(_('%s is not a parent of %s') %
205 205 (short(p), short(node)))
206 206 parent = p
207 207 else:
208 208 if opts['parent']:
209 209 raise util.Abort(_('cannot use --parent on non-merge changeset'))
210 210 parent = p1
211 211 hg.clean(repo, node, show_stats=False)
212 212 revert_opts = opts.copy()
213 213 revert_opts['date'] = None
214 214 revert_opts['all'] = True
215 215 revert_opts['rev'] = hex(parent)
216 216 revert(ui, repo, **revert_opts)
217 217 commit_opts = opts.copy()
218 218 commit_opts['addremove'] = False
219 219 if not commit_opts['message'] and not commit_opts['logfile']:
220 220 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
221 221 commit_opts['force_editor'] = True
222 222 commit(ui, repo, **commit_opts)
223 223 def nice(node):
224 224 return '%d:%s' % (repo.changelog.rev(node), short(node))
225 225 ui.status(_('changeset %s backs out changeset %s\n') %
226 226 (nice(repo.changelog.tip()), nice(node)))
227 227 if op1 != node:
228 228 if opts['merge']:
229 229 ui.status(_('merging with changeset %s\n') % nice(op1))
230 230 hg.merge(repo, hex(op1))
231 231 else:
232 232 ui.status(_('the backout changeset is a new head - '
233 233 'do not forget to merge\n'))
234 234 ui.status(_('(use "backout --merge" '
235 235 'if you want to auto-merge)\n'))
236 236
237 237 def branch(ui, repo, label=None, **opts):
238 238 """set or show the current branch name
239 239
240 240 With no argument, show the current branch name. With one argument,
241 241 set the working directory branch name (the branch does not exist in
242 242 the repository until the next commit).
243 243
244 244 Unless --force is specified, branch will not let you set a
245 245 branch name that shadows an existing branch.
246 246 """
247 247
248 248 if label:
249 249 if not opts.get('force') and label in repo.branchtags():
250 250 if label not in [p.branch() for p in repo.workingctx().parents()]:
251 251 raise util.Abort(_('a branch of the same name already exists'
252 252 ' (use --force to override)'))
253 253 repo.dirstate.setbranch(util.fromlocal(label))
254 254 ui.status(_('marked working directory as branch %s\n') % label)
255 255 else:
256 256 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
257 257
258 258 def branches(ui, repo, active=False):
259 259 """list repository named branches
260 260
261 261 List the repository's named branches, indicating which ones are
262 262 inactive. If active is specified, only show active branches.
263 263
264 264 A branch is considered active if it contains unmerged heads.
265 265 """
266 266 b = repo.branchtags()
267 267 heads = dict.fromkeys(repo.heads(), 1)
268 268 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
269 269 l.sort()
270 270 l.reverse()
271 271 for ishead, r, n, t in l:
272 272 if active and not ishead:
273 273 # If we're only displaying active branches, abort the loop on
274 274 # encountering the first inactive head
275 275 break
276 276 else:
277 277 hexfunc = ui.debugflag and hex or short
278 278 if ui.quiet:
279 279 ui.write("%s\n" % t)
280 280 else:
281 281 spaces = " " * (30 - util.locallen(t))
282 282 # The code only gets here if inactive branches are being
283 283 # displayed or the branch is active.
284 284 isinactive = ((not ishead) and " (inactive)") or ''
285 285 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
286 286
287 287 def bundle(ui, repo, fname, dest=None, **opts):
288 288 """create a changegroup file
289 289
290 290 Generate a compressed changegroup file collecting changesets not
291 291 found in the other repository.
292 292
293 293 If no destination repository is specified the destination is assumed
294 294 to have all the nodes specified by one or more --base parameters.
295 295
296 296 The bundle file can then be transferred using conventional means and
297 297 applied to another repository with the unbundle or pull command.
298 298 This is useful when direct push and pull are not available or when
299 299 exporting an entire repository is undesirable.
300 300
301 301 Applying bundles preserves all changeset contents including
302 302 permissions, copy/rename information, and revision history.
303 303 """
304 304 revs = opts.get('rev') or None
305 305 if revs:
306 306 revs = [repo.lookup(rev) for rev in revs]
307 307 base = opts.get('base')
308 308 if base:
309 309 if dest:
310 310 raise util.Abort(_("--base is incompatible with specifiying "
311 311 "a destination"))
312 312 base = [repo.lookup(rev) for rev in base]
313 313 # create the right base
314 314 # XXX: nodesbetween / changegroup* should be "fixed" instead
315 315 o = []
316 316 has = {nullid: None}
317 317 for n in base:
318 318 has.update(repo.changelog.reachable(n))
319 319 if revs:
320 320 visit = list(revs)
321 321 else:
322 322 visit = repo.changelog.heads()
323 323 seen = {}
324 324 while visit:
325 325 n = visit.pop(0)
326 326 parents = [p for p in repo.changelog.parents(n) if p not in has]
327 327 if len(parents) == 0:
328 328 o.insert(0, n)
329 329 else:
330 330 for p in parents:
331 331 if p not in seen:
332 332 seen[p] = 1
333 333 visit.append(p)
334 334 else:
335 335 cmdutil.setremoteconfig(ui, opts)
336 336 dest, revs = cmdutil.parseurl(
337 337 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
338 338 other = hg.repository(ui, dest)
339 339 o = repo.findoutgoing(other, force=opts['force'])
340 340
341 341 if revs:
342 342 cg = repo.changegroupsubset(o, revs, 'bundle')
343 343 else:
344 344 cg = repo.changegroup(o, 'bundle')
345 345 changegroup.writebundle(cg, fname, "HG10BZ")
346 346
347 347 def cat(ui, repo, file1, *pats, **opts):
348 348 """output the current or given revision of files
349 349
350 350 Print the specified files as they were at the given revision.
351 351 If no revision is given, the parent of the working directory is used,
352 352 or tip if no revision is checked out.
353 353
354 354 Output may be to a file, in which case the name of the file is
355 355 given using a format string. The formatting rules are the same as
356 356 for the export command, with the following additions:
357 357
358 358 %s basename of file being printed
359 359 %d dirname of file being printed, or '.' if in repo root
360 360 %p root-relative path name of file being printed
361 361 """
362 362 ctx = repo.changectx(opts['rev'])
363 363 err = 1
364 364 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
365 365 ctx.node()):
366 366 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
367 367 fp.write(ctx.filectx(abs).data())
368 368 err = 0
369 369 return err
370 370
371 371 def clone(ui, source, dest=None, **opts):
372 372 """make a copy of an existing repository
373 373
374 374 Create a copy of an existing repository in a new directory.
375 375
376 376 If no destination directory name is specified, it defaults to the
377 377 basename of the source.
378 378
379 379 The location of the source is added to the new repository's
380 380 .hg/hgrc file, as the default to be used for future pulls.
381 381
382 382 For efficiency, hardlinks are used for cloning whenever the source
383 383 and destination are on the same filesystem (note this applies only
384 384 to the repository data, not to the checked out files). Some
385 385 filesystems, such as AFS, implement hardlinking incorrectly, but
386 386 do not report errors. In these cases, use the --pull option to
387 387 avoid hardlinking.
388 388
389 389 You can safely clone repositories and checked out files using full
390 390 hardlinks with
391 391
392 392 $ cp -al REPO REPOCLONE
393 393
394 394 which is the fastest way to clone. However, the operation is not
395 395 atomic (making sure REPO is not modified during the operation is
396 396 up to you) and you have to make sure your editor breaks hardlinks
397 397 (Emacs and most Linux Kernel tools do so).
398 398
399 399 If you use the -r option to clone up to a specific revision, no
400 400 subsequent revisions will be present in the cloned repository.
401 401 This option implies --pull, even on local repositories.
402 402
403 403 See pull for valid source format details.
404 404
405 405 It is possible to specify an ssh:// URL as the destination, but no
406 406 .hg/hgrc and working directory will be created on the remote side.
407 407 Look at the help text for the pull command for important details
408 408 about ssh:// URLs.
409 409 """
410 410 cmdutil.setremoteconfig(ui, opts)
411 411 hg.clone(ui, source, dest,
412 412 pull=opts['pull'],
413 413 stream=opts['uncompressed'],
414 414 rev=opts['rev'],
415 415 update=not opts['noupdate'])
416 416
417 417 def commit(ui, repo, *pats, **opts):
418 418 """commit the specified files or all outstanding changes
419 419
420 420 Commit changes to the given files into the repository.
421 421
422 422 If a list of files is omitted, all changes reported by "hg status"
423 423 will be committed.
424 424
425 425 If no commit message is specified, the editor configured in your hgrc
426 426 or in the EDITOR environment variable is started to enter a message.
427 427 """
428 428 message = cmdutil.logmessage(opts)
429 429
430 430 if opts['addremove']:
431 431 cmdutil.addremove(repo, pats, opts)
432 432 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
433 433 if pats:
434 434 status = repo.status(files=fns, match=match)
435 435 modified, added, removed, deleted, unknown = status[:5]
436 436 files = modified + added + removed
437 437 slist = None
438 438 for f in fns:
439 439 if f == '.':
440 440 continue
441 441 if f not in files:
442 442 rf = repo.wjoin(f)
443 443 try:
444 444 mode = os.lstat(rf)[stat.ST_MODE]
445 445 except OSError:
446 446 raise util.Abort(_("file %s not found!") % rf)
447 447 if stat.S_ISDIR(mode):
448 448 name = f + '/'
449 449 if slist is None:
450 450 slist = list(files)
451 451 slist.sort()
452 452 i = bisect.bisect(slist, name)
453 453 if i >= len(slist) or not slist[i].startswith(name):
454 454 raise util.Abort(_("no match under directory %s!")
455 455 % rf)
456 456 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
457 457 raise util.Abort(_("can't commit %s: "
458 458 "unsupported file type!") % rf)
459 459 elif repo.dirstate.state(f) == '?':
460 460 raise util.Abort(_("file %s not tracked!") % rf)
461 461 else:
462 462 files = []
463 463 try:
464 464 repo.commit(files, message, opts['user'], opts['date'], match,
465 465 force_editor=opts.get('force_editor'))
466 466 except ValueError, inst:
467 467 raise util.Abort(str(inst))
468 468
469 469 def docopy(ui, repo, pats, opts, wlock):
470 470 # called with the repo lock held
471 471 #
472 472 # hgsep => pathname that uses "/" to separate directories
473 473 # ossep => pathname that uses os.sep to separate directories
474 474 cwd = repo.getcwd()
475 475 errors = 0
476 476 copied = []
477 477 targets = {}
478 478
479 479 # abs: hgsep
480 480 # rel: ossep
481 481 # return: hgsep
482 482 def okaytocopy(abs, rel, exact):
483 483 reasons = {'?': _('is not managed'),
484 484 'r': _('has been marked for remove')}
485 485 state = repo.dirstate.state(abs)
486 486 reason = reasons.get(state)
487 487 if reason:
488 488 if exact:
489 489 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
490 490 else:
491 491 if state == 'a':
492 492 origsrc = repo.dirstate.copied(abs)
493 493 if origsrc is not None:
494 494 return origsrc
495 495 return abs
496 496
497 497 # origsrc: hgsep
498 498 # abssrc: hgsep
499 499 # relsrc: ossep
500 500 # otarget: ossep
501 501 def copy(origsrc, abssrc, relsrc, otarget, exact):
502 502 abstarget = util.canonpath(repo.root, cwd, otarget)
503 503 reltarget = repo.pathto(abstarget, cwd)
504 504 prevsrc = targets.get(abstarget)
505 505 src = repo.wjoin(abssrc)
506 506 target = repo.wjoin(abstarget)
507 507 if prevsrc is not None:
508 508 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
509 509 (reltarget, repo.pathto(abssrc, cwd),
510 510 repo.pathto(prevsrc, cwd)))
511 511 return
512 512 if (not opts['after'] and os.path.exists(target) or
513 513 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
514 514 if not opts['force']:
515 515 ui.warn(_('%s: not overwriting - file exists\n') %
516 516 reltarget)
517 517 return
518 518 if not opts['after'] and not opts.get('dry_run'):
519 519 os.unlink(target)
520 520 if opts['after']:
521 521 if not os.path.exists(target):
522 522 return
523 523 else:
524 524 targetdir = os.path.dirname(target) or '.'
525 525 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
526 526 os.makedirs(targetdir)
527 527 try:
528 528 restore = repo.dirstate.state(abstarget) == 'r'
529 529 if restore and not opts.get('dry_run'):
530 530 repo.undelete([abstarget], wlock)
531 531 try:
532 532 if not opts.get('dry_run'):
533 533 util.copyfile(src, target)
534 534 restore = False
535 535 finally:
536 536 if restore:
537 537 repo.remove([abstarget], wlock=wlock)
538 538 except IOError, inst:
539 539 if inst.errno == errno.ENOENT:
540 540 ui.warn(_('%s: deleted in working copy\n') % relsrc)
541 541 else:
542 542 ui.warn(_('%s: cannot copy - %s\n') %
543 543 (relsrc, inst.strerror))
544 544 errors += 1
545 545 return
546 546 if ui.verbose or not exact:
547 547 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
548 548 targets[abstarget] = abssrc
549 549 if abstarget != origsrc:
550 550 if repo.dirstate.state(origsrc) == 'a':
551 551 if not ui.quiet:
552 552 ui.warn(_("%s has not been committed yet, so no copy "
553 553 "data will be stored for %s.\n")
554 554 % (repo.pathto(origsrc, cwd), reltarget))
555 555 if abstarget not in repo.dirstate and not opts.get('dry_run'):
556 556 repo.add([abstarget], wlock)
557 557 elif not opts.get('dry_run'):
558 558 repo.copy(origsrc, abstarget, wlock)
559 559 copied.append((abssrc, relsrc, exact))
560 560
561 561 # pat: ossep
562 562 # dest ossep
563 563 # srcs: list of (hgsep, hgsep, ossep, bool)
564 564 # return: function that takes hgsep and returns ossep
565 565 def targetpathfn(pat, dest, srcs):
566 566 if os.path.isdir(pat):
567 567 abspfx = util.canonpath(repo.root, cwd, pat)
568 568 abspfx = util.localpath(abspfx)
569 569 if destdirexists:
570 570 striplen = len(os.path.split(abspfx)[0])
571 571 else:
572 572 striplen = len(abspfx)
573 573 if striplen:
574 574 striplen += len(os.sep)
575 575 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
576 576 elif destdirexists:
577 577 res = lambda p: os.path.join(dest,
578 578 os.path.basename(util.localpath(p)))
579 579 else:
580 580 res = lambda p: dest
581 581 return res
582 582
583 583 # pat: ossep
584 584 # dest ossep
585 585 # srcs: list of (hgsep, hgsep, ossep, bool)
586 586 # return: function that takes hgsep and returns ossep
587 587 def targetpathafterfn(pat, dest, srcs):
588 588 if util.patkind(pat, None)[0]:
589 589 # a mercurial pattern
590 590 res = lambda p: os.path.join(dest,
591 591 os.path.basename(util.localpath(p)))
592 592 else:
593 593 abspfx = util.canonpath(repo.root, cwd, pat)
594 594 if len(abspfx) < len(srcs[0][0]):
595 595 # A directory. Either the target path contains the last
596 596 # component of the source path or it does not.
597 597 def evalpath(striplen):
598 598 score = 0
599 599 for s in srcs:
600 600 t = os.path.join(dest, util.localpath(s[0])[striplen:])
601 601 if os.path.exists(t):
602 602 score += 1
603 603 return score
604 604
605 605 abspfx = util.localpath(abspfx)
606 606 striplen = len(abspfx)
607 607 if striplen:
608 608 striplen += len(os.sep)
609 609 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
610 610 score = evalpath(striplen)
611 611 striplen1 = len(os.path.split(abspfx)[0])
612 612 if striplen1:
613 613 striplen1 += len(os.sep)
614 614 if evalpath(striplen1) > score:
615 615 striplen = striplen1
616 616 res = lambda p: os.path.join(dest,
617 617 util.localpath(p)[striplen:])
618 618 else:
619 619 # a file
620 620 if destdirexists:
621 621 res = lambda p: os.path.join(dest,
622 622 os.path.basename(util.localpath(p)))
623 623 else:
624 624 res = lambda p: dest
625 625 return res
626 626
627 627
628 628 pats = util.expand_glob(pats)
629 629 if not pats:
630 630 raise util.Abort(_('no source or destination specified'))
631 631 if len(pats) == 1:
632 632 raise util.Abort(_('no destination specified'))
633 633 dest = pats.pop()
634 634 destdirexists = os.path.isdir(dest)
635 635 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
636 636 raise util.Abort(_('with multiple sources, destination must be an '
637 637 'existing directory'))
638 638 if opts['after']:
639 639 tfn = targetpathafterfn
640 640 else:
641 641 tfn = targetpathfn
642 642 copylist = []
643 643 for pat in pats:
644 644 srcs = []
645 645 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
646 646 globbed=True):
647 647 origsrc = okaytocopy(abssrc, relsrc, exact)
648 648 if origsrc:
649 649 srcs.append((origsrc, abssrc, relsrc, exact))
650 650 if not srcs:
651 651 continue
652 652 copylist.append((tfn(pat, dest, srcs), srcs))
653 653 if not copylist:
654 654 raise util.Abort(_('no files to copy'))
655 655
656 656 for targetpath, srcs in copylist:
657 657 for origsrc, abssrc, relsrc, exact in srcs:
658 658 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
659 659
660 660 if errors:
661 661 ui.warn(_('(consider using --after)\n'))
662 662 return errors, copied
663 663
664 664 def copy(ui, repo, *pats, **opts):
665 665 """mark files as copied for the next commit
666 666
667 667 Mark dest as having copies of source files. If dest is a
668 668 directory, copies are put in that directory. If dest is a file,
669 669 there can only be one source.
670 670
671 671 By default, this command copies the contents of files as they
672 672 stand in the working directory. If invoked with --after, the
673 673 operation is recorded, but no copying is performed.
674 674
675 675 This command takes effect in the next commit. To undo a copy
676 676 before that, see hg revert.
677 677 """
678 678 wlock = repo.wlock(0)
679 679 errs, copied = docopy(ui, repo, pats, opts, wlock)
680 680 return errs
681 681
682 682 def debugancestor(ui, index, rev1, rev2):
683 683 """find the ancestor revision of two revisions in a given index"""
684 684 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
685 685 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
686 686 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
687 687
688 688 def debugcomplete(ui, cmd='', **opts):
689 689 """returns the completion list associated with the given command"""
690 690
691 691 if opts['options']:
692 692 options = []
693 693 otables = [globalopts]
694 694 if cmd:
695 695 aliases, entry = cmdutil.findcmd(ui, cmd)
696 696 otables.append(entry[1])
697 697 for t in otables:
698 698 for o in t:
699 699 if o[0]:
700 700 options.append('-%s' % o[0])
701 701 options.append('--%s' % o[1])
702 702 ui.write("%s\n" % "\n".join(options))
703 703 return
704 704
705 705 clist = cmdutil.findpossible(ui, cmd).keys()
706 706 clist.sort()
707 707 ui.write("%s\n" % "\n".join(clist))
708 708
709 709 def debugrebuildstate(ui, repo, rev=""):
710 710 """rebuild the dirstate as it would look like for the given revision"""
711 711 if rev == "":
712 712 rev = repo.changelog.tip()
713 713 ctx = repo.changectx(rev)
714 714 files = ctx.manifest()
715 715 wlock = repo.wlock()
716 716 repo.dirstate.rebuild(rev, files)
717 717
718 718 def debugcheckstate(ui, repo):
719 719 """validate the correctness of the current dirstate"""
720 720 parent1, parent2 = repo.dirstate.parents()
721 721 dc = repo.dirstate
722 722 m1 = repo.changectx(parent1).manifest()
723 723 m2 = repo.changectx(parent2).manifest()
724 724 errors = 0
725 725 for f in dc:
726 726 state = repo.dirstate.state(f)
727 727 if state in "nr" and f not in m1:
728 728 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
729 729 errors += 1
730 730 if state in "a" and f in m1:
731 731 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
732 732 errors += 1
733 733 if state in "m" and f not in m1 and f not in m2:
734 734 ui.warn(_("%s in state %s, but not in either manifest\n") %
735 735 (f, state))
736 736 errors += 1
737 737 for f in m1:
738 738 state = repo.dirstate.state(f)
739 739 if state not in "nrm":
740 740 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
741 741 errors += 1
742 742 if errors:
743 743 error = _(".hg/dirstate inconsistent with current parent's manifest")
744 744 raise util.Abort(error)
745 745
746 746 def showconfig(ui, repo, *values, **opts):
747 747 """show combined config settings from all hgrc files
748 748
749 749 With no args, print names and values of all config items.
750 750
751 751 With one arg of the form section.name, print just the value of
752 752 that config item.
753 753
754 754 With multiple args, print names and values of all config items
755 755 with matching section names."""
756 756
757 757 untrusted = bool(opts.get('untrusted'))
758 758 if values:
759 759 if len([v for v in values if '.' in v]) > 1:
760 760 raise util.Abort(_('only one config item permitted'))
761 761 for section, name, value in ui.walkconfig(untrusted=untrusted):
762 762 sectname = section + '.' + name
763 763 if values:
764 764 for v in values:
765 765 if v == section:
766 766 ui.write('%s=%s\n' % (sectname, value))
767 767 elif v == sectname:
768 768 ui.write(value, '\n')
769 769 else:
770 770 ui.write('%s=%s\n' % (sectname, value))
771 771
772 772 def debugsetparents(ui, repo, rev1, rev2=None):
773 773 """manually set the parents of the current working directory
774 774
775 775 This is useful for writing repository conversion tools, but should
776 776 be used with care.
777 777 """
778 778
779 779 if not rev2:
780 780 rev2 = hex(nullid)
781 781
782 782 wlock = repo.wlock()
783 783 try:
784 784 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
785 785 finally:
786 786 wlock.release()
787 787
788 788 def debugstate(ui, repo):
789 789 """show the contents of the current dirstate"""
790 790 dc = repo.dirstate
791 791 for file_ in dc:
792 792 if dc[file_][3] == -1:
793 793 # Pad or slice to locale representation
794 794 locale_len = len(time.strftime("%x %X", time.localtime(0)))
795 795 timestr = 'unset'
796 796 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
797 797 else:
798 798 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
799 799 ui.write("%c %3o %10d %s %s\n"
800 800 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
801 801 timestr, file_))
802 802 for f in repo.dirstate.copies():
803 803 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
804 804
805 805 def debugdata(ui, file_, rev):
806 806 """dump the contents of a data file revision"""
807 807 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
808 808 try:
809 809 ui.write(r.revision(r.lookup(rev)))
810 810 except KeyError:
811 811 raise util.Abort(_('invalid revision identifier %s') % rev)
812 812
813 813 def debugdate(ui, date, range=None, **opts):
814 814 """parse and display a date"""
815 815 if opts["extended"]:
816 816 d = util.parsedate(date, util.extendeddateformats)
817 817 else:
818 818 d = util.parsedate(date)
819 819 ui.write("internal: %s %s\n" % d)
820 820 ui.write("standard: %s\n" % util.datestr(d))
821 821 if range:
822 822 m = util.matchdate(range)
823 823 ui.write("match: %s\n" % m(d[0]))
824 824
825 825 def debugindex(ui, file_):
826 826 """dump the contents of an index file"""
827 827 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
828 828 ui.write(" rev offset length base linkrev" +
829 829 " nodeid p1 p2\n")
830 830 for i in xrange(r.count()):
831 831 node = r.node(i)
832 832 pp = r.parents(node)
833 833 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
834 834 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
835 835 short(node), short(pp[0]), short(pp[1])))
836 836
837 837 def debugindexdot(ui, file_):
838 838 """dump an index DAG as a .dot file"""
839 839 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
840 840 ui.write("digraph G {\n")
841 841 for i in xrange(r.count()):
842 842 node = r.node(i)
843 843 pp = r.parents(node)
844 844 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
845 845 if pp[1] != nullid:
846 846 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
847 847 ui.write("}\n")
848 848
849 849 def debuginstall(ui):
850 850 '''test Mercurial installation'''
851 851
852 852 def writetemp(contents):
853 853 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
854 854 f = os.fdopen(fd, "wb")
855 855 f.write(contents)
856 856 f.close()
857 857 return name
858 858
859 859 problems = 0
860 860
861 861 # encoding
862 862 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
863 863 try:
864 864 util.fromlocal("test")
865 865 except util.Abort, inst:
866 866 ui.write(" %s\n" % inst)
867 867 ui.write(_(" (check that your locale is properly set)\n"))
868 868 problems += 1
869 869
870 870 # compiled modules
871 871 ui.status(_("Checking extensions...\n"))
872 872 try:
873 873 import bdiff, mpatch, base85
874 874 except Exception, inst:
875 875 ui.write(" %s\n" % inst)
876 876 ui.write(_(" One or more extensions could not be found"))
877 877 ui.write(_(" (check that you compiled the extensions)\n"))
878 878 problems += 1
879 879
880 880 # templates
881 881 ui.status(_("Checking templates...\n"))
882 882 try:
883 883 import templater
884 884 t = templater.templater(templater.templatepath("map-cmdline.default"))
885 885 except Exception, inst:
886 886 ui.write(" %s\n" % inst)
887 887 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
888 888 problems += 1
889 889
890 890 # patch
891 891 ui.status(_("Checking patch...\n"))
892 892 patcher = ui.config('ui', 'patch')
893 893 patcher = ((patcher and util.find_exe(patcher)) or
894 894 util.find_exe('gpatch') or
895 895 util.find_exe('patch'))
896 896 if not patcher:
897 897 ui.write(_(" Can't find patch or gpatch in PATH\n"))
898 898 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
899 899 problems += 1
900 900 else:
901 901 # actually attempt a patch here
902 902 a = "1\n2\n3\n4\n"
903 903 b = "1\n2\n3\ninsert\n4\n"
904 904 fa = writetemp(a)
905 905 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
906 906 fd = writetemp(d)
907 907
908 908 files = {}
909 909 try:
910 910 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
911 911 except util.Abort, e:
912 912 ui.write(_(" patch call failed:\n"))
913 913 ui.write(" " + str(e) + "\n")
914 914 problems += 1
915 915 else:
916 916 if list(files) != [os.path.basename(fa)]:
917 917 ui.write(_(" unexpected patch output!"))
918 918 ui.write(_(" (you may have an incompatible version of patch)\n"))
919 919 problems += 1
920 920 a = file(fa).read()
921 921 if a != b:
922 922 ui.write(_(" patch test failed!"))
923 923 ui.write(_(" (you may have an incompatible version of patch)\n"))
924 924 problems += 1
925 925
926 926 os.unlink(fa)
927 927 os.unlink(fd)
928 928
929 929 # merge helper
930 930 ui.status(_("Checking merge helper...\n"))
931 931 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
932 932 or "hgmerge")
933 933 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
934 934 if not cmdpath:
935 935 if cmd == 'hgmerge':
936 936 ui.write(_(" No merge helper set and can't find default"
937 937 " hgmerge script in PATH\n"))
938 938 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
939 939 else:
940 940 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
941 941 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
942 942 problems += 1
943 943 else:
944 944 # actually attempt a patch here
945 945 fa = writetemp("1\n2\n3\n4\n")
946 946 fl = writetemp("1\n2\n3\ninsert\n4\n")
947 947 fr = writetemp("begin\n1\n2\n3\n4\n")
948 948 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
949 949 if r:
950 950 ui.write(_(" Got unexpected merge error %d!\n") % r)
951 951 problems += 1
952 952 m = file(fl).read()
953 953 if m != "begin\n1\n2\n3\ninsert\n4\n":
954 954 ui.write(_(" Got unexpected merge results!\n"))
955 955 ui.write(_(" (your merge helper may have the"
956 956 " wrong argument order)\n"))
957 957 ui.write(_(" Result: %r\n") % m)
958 958 problems += 1
959 959 os.unlink(fa)
960 960 os.unlink(fl)
961 961 os.unlink(fr)
962 962
963 963 # editor
964 964 ui.status(_("Checking commit editor...\n"))
965 965 editor = (os.environ.get("HGEDITOR") or
966 966 ui.config("ui", "editor") or
967 967 os.environ.get("EDITOR", "vi"))
968 968 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
969 969 if not cmdpath:
970 970 if editor == 'vi':
971 971 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
972 972 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
973 973 else:
974 974 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
975 975 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
976 976 problems += 1
977 977
978 978 # check username
979 979 ui.status(_("Checking username...\n"))
980 980 user = os.environ.get("HGUSER")
981 981 if user is None:
982 982 user = ui.config("ui", "username")
983 983 if user is None:
984 984 user = os.environ.get("EMAIL")
985 985 if not user:
986 986 ui.warn(" ")
987 987 ui.username()
988 988 ui.write(_(" (specify a username in your .hgrc file)\n"))
989 989
990 990 if not problems:
991 991 ui.status(_("No problems detected\n"))
992 992 else:
993 993 ui.write(_("%s problems detected,"
994 994 " please check your install!\n") % problems)
995 995
996 996 return problems
997 997
998 998 def debugrename(ui, repo, file1, *pats, **opts):
999 999 """dump rename information"""
1000 1000
1001 1001 ctx = repo.changectx(opts.get('rev', 'tip'))
1002 1002 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
1003 1003 ctx.node()):
1004 1004 m = ctx.filectx(abs).renamed()
1005 1005 if m:
1006 1006 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
1007 1007 else:
1008 1008 ui.write(_("%s not renamed\n") % rel)
1009 1009
1010 1010 def debugwalk(ui, repo, *pats, **opts):
1011 1011 """show how files match on given patterns"""
1012 1012 items = list(cmdutil.walk(repo, pats, opts))
1013 1013 if not items:
1014 1014 return
1015 1015 fmt = '%%s %%-%ds %%-%ds %%s' % (
1016 1016 max([len(abs) for (src, abs, rel, exact) in items]),
1017 1017 max([len(rel) for (src, abs, rel, exact) in items]))
1018 1018 for src, abs, rel, exact in items:
1019 1019 line = fmt % (src, abs, rel, exact and 'exact' or '')
1020 1020 ui.write("%s\n" % line.rstrip())
1021 1021
1022 1022 def diff(ui, repo, *pats, **opts):
1023 1023 """diff repository (or selected files)
1024 1024
1025 1025 Show differences between revisions for the specified files.
1026 1026
1027 1027 Differences between files are shown using the unified diff format.
1028 1028
1029 1029 NOTE: diff may generate unexpected results for merges, as it will
1030 1030 default to comparing against the working directory's first parent
1031 1031 changeset if no revisions are specified.
1032 1032
1033 1033 When two revision arguments are given, then changes are shown
1034 1034 between those revisions. If only one revision is specified then
1035 1035 that revision is compared to the working directory, and, when no
1036 1036 revisions are specified, the working directory files are compared
1037 1037 to its parent.
1038 1038
1039 1039 Without the -a option, diff will avoid generating diffs of files
1040 1040 it detects as binary. With -a, diff will generate a diff anyway,
1041 1041 probably with undesirable results.
1042 1042 """
1043 1043 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1044 1044
1045 1045 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1046 1046
1047 1047 patch.diff(repo, node1, node2, fns, match=matchfn,
1048 1048 opts=patch.diffopts(ui, opts))
1049 1049
1050 1050 def export(ui, repo, *changesets, **opts):
1051 1051 """dump the header and diffs for one or more changesets
1052 1052
1053 1053 Print the changeset header and diffs for one or more revisions.
1054 1054
1055 1055 The information shown in the changeset header is: author,
1056 1056 changeset hash, parent(s) and commit comment.
1057 1057
1058 1058 NOTE: export may generate unexpected diff output for merge changesets,
1059 1059 as it will compare the merge changeset against its first parent only.
1060 1060
1061 1061 Output may be to a file, in which case the name of the file is
1062 1062 given using a format string. The formatting rules are as follows:
1063 1063
1064 1064 %% literal "%" character
1065 1065 %H changeset hash (40 bytes of hexadecimal)
1066 1066 %N number of patches being generated
1067 1067 %R changeset revision number
1068 1068 %b basename of the exporting repository
1069 1069 %h short-form changeset hash (12 bytes of hexadecimal)
1070 1070 %n zero-padded sequence number, starting at 1
1071 1071 %r zero-padded changeset revision number
1072 1072
1073 1073 Without the -a option, export will avoid generating diffs of files
1074 1074 it detects as binary. With -a, export will generate a diff anyway,
1075 1075 probably with undesirable results.
1076 1076
1077 1077 With the --switch-parent option, the diff will be against the second
1078 1078 parent. It can be useful to review a merge.
1079 1079 """
1080 1080 if not changesets:
1081 1081 raise util.Abort(_("export requires at least one changeset"))
1082 1082 revs = cmdutil.revrange(repo, changesets)
1083 1083 if len(revs) > 1:
1084 1084 ui.note(_('exporting patches:\n'))
1085 1085 else:
1086 1086 ui.note(_('exporting patch:\n'))
1087 1087 patch.export(repo, revs, template=opts['output'],
1088 1088 switch_parent=opts['switch_parent'],
1089 1089 opts=patch.diffopts(ui, opts))
1090 1090
1091 1091 def grep(ui, repo, pattern, *pats, **opts):
1092 1092 """search for a pattern in specified files and revisions
1093 1093
1094 1094 Search revisions of files for a regular expression.
1095 1095
1096 1096 This command behaves differently than Unix grep. It only accepts
1097 1097 Python/Perl regexps. It searches repository history, not the
1098 1098 working directory. It always prints the revision number in which
1099 1099 a match appears.
1100 1100
1101 1101 By default, grep only prints output for the first revision of a
1102 1102 file in which it finds a match. To get it to print every revision
1103 1103 that contains a change in match status ("-" for a match that
1104 1104 becomes a non-match, or "+" for a non-match that becomes a match),
1105 1105 use the --all flag.
1106 1106 """
1107 1107 reflags = 0
1108 1108 if opts['ignore_case']:
1109 1109 reflags |= re.I
1110 1110 try:
1111 1111 regexp = re.compile(pattern, reflags)
1112 1112 except Exception, inst:
1113 1113 ui.warn(_("grep: invalid match pattern: %s!\n") % inst)
1114 1114 return None
1115 1115 sep, eol = ':', '\n'
1116 1116 if opts['print0']:
1117 1117 sep = eol = '\0'
1118 1118
1119 1119 fcache = {}
1120 1120 def getfile(fn):
1121 1121 if fn not in fcache:
1122 1122 fcache[fn] = repo.file(fn)
1123 1123 return fcache[fn]
1124 1124
1125 1125 def matchlines(body):
1126 1126 begin = 0
1127 1127 linenum = 0
1128 1128 while True:
1129 1129 match = regexp.search(body, begin)
1130 1130 if not match:
1131 1131 break
1132 1132 mstart, mend = match.span()
1133 1133 linenum += body.count('\n', begin, mstart) + 1
1134 1134 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1135 1135 lend = body.find('\n', mend)
1136 1136 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1137 1137 begin = lend + 1
1138 1138
1139 1139 class linestate(object):
1140 1140 def __init__(self, line, linenum, colstart, colend):
1141 1141 self.line = line
1142 1142 self.linenum = linenum
1143 1143 self.colstart = colstart
1144 1144 self.colend = colend
1145 1145
1146 1146 def __eq__(self, other):
1147 1147 return self.line == other.line
1148 1148
1149 1149 matches = {}
1150 1150 copies = {}
1151 1151 def grepbody(fn, rev, body):
1152 1152 matches[rev].setdefault(fn, [])
1153 1153 m = matches[rev][fn]
1154 1154 for lnum, cstart, cend, line in matchlines(body):
1155 1155 s = linestate(line, lnum, cstart, cend)
1156 1156 m.append(s)
1157 1157
1158 1158 def difflinestates(a, b):
1159 1159 sm = difflib.SequenceMatcher(None, a, b)
1160 1160 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1161 1161 if tag == 'insert':
1162 1162 for i in xrange(blo, bhi):
1163 1163 yield ('+', b[i])
1164 1164 elif tag == 'delete':
1165 1165 for i in xrange(alo, ahi):
1166 1166 yield ('-', a[i])
1167 1167 elif tag == 'replace':
1168 1168 for i in xrange(alo, ahi):
1169 1169 yield ('-', a[i])
1170 1170 for i in xrange(blo, bhi):
1171 1171 yield ('+', b[i])
1172 1172
1173 1173 prev = {}
1174 1174 def display(fn, rev, states, prevstates):
1175 1175 found = False
1176 1176 filerevmatches = {}
1177 1177 r = prev.get(fn, -1)
1178 1178 if opts['all']:
1179 1179 iter = difflinestates(states, prevstates)
1180 1180 else:
1181 1181 iter = [('', l) for l in prevstates]
1182 1182 for change, l in iter:
1183 1183 cols = [fn, str(r)]
1184 1184 if opts['line_number']:
1185 1185 cols.append(str(l.linenum))
1186 1186 if opts['all']:
1187 1187 cols.append(change)
1188 1188 if opts['user']:
1189 1189 cols.append(ui.shortuser(get(r)[1]))
1190 1190 if opts['files_with_matches']:
1191 1191 c = (fn, r)
1192 1192 if c in filerevmatches:
1193 1193 continue
1194 1194 filerevmatches[c] = 1
1195 1195 else:
1196 1196 cols.append(l.line)
1197 1197 ui.write(sep.join(cols), eol)
1198 1198 found = True
1199 1199 return found
1200 1200
1201 1201 fstate = {}
1202 1202 skip = {}
1203 1203 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1204 1204 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1205 1205 found = False
1206 1206 follow = opts.get('follow')
1207 1207 for st, rev, fns in changeiter:
1208 1208 if st == 'window':
1209 1209 matches.clear()
1210 1210 elif st == 'add':
1211 1211 mf = repo.changectx(rev).manifest()
1212 1212 matches[rev] = {}
1213 1213 for fn in fns:
1214 1214 if fn in skip:
1215 1215 continue
1216 1216 fstate.setdefault(fn, {})
1217 1217 try:
1218 1218 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1219 1219 if follow:
1220 1220 copied = getfile(fn).renamed(mf[fn])
1221 1221 if copied:
1222 1222 copies.setdefault(rev, {})[fn] = copied[0]
1223 1223 except KeyError:
1224 1224 pass
1225 1225 elif st == 'iter':
1226 1226 states = matches[rev].items()
1227 1227 states.sort()
1228 1228 for fn, m in states:
1229 1229 copy = copies.get(rev, {}).get(fn)
1230 1230 if fn in skip:
1231 1231 if copy:
1232 1232 skip[copy] = True
1233 1233 continue
1234 1234 if fn in prev or fstate[fn]:
1235 1235 r = display(fn, rev, m, fstate[fn])
1236 1236 found = found or r
1237 1237 if r and not opts['all']:
1238 1238 skip[fn] = True
1239 1239 if copy:
1240 1240 skip[copy] = True
1241 1241 fstate[fn] = m
1242 1242 if copy:
1243 1243 fstate[copy] = m
1244 1244 prev[fn] = rev
1245 1245
1246 1246 fstate = fstate.items()
1247 1247 fstate.sort()
1248 1248 for fn, state in fstate:
1249 1249 if fn in skip:
1250 1250 continue
1251 1251 if fn not in copies.get(prev[fn], {}):
1252 1252 found = display(fn, rev, {}, state) or found
1253 1253 return (not found and 1) or 0
1254 1254
1255 1255 def heads(ui, repo, *branchrevs, **opts):
1256 1256 """show current repository heads or show branch heads
1257 1257
1258 1258 With no arguments, show all repository head changesets.
1259 1259
1260 1260 If branch or revisions names are given this will show the heads of
1261 1261 the specified branches or the branches those revisions are tagged
1262 1262 with.
1263 1263
1264 1264 Repository "heads" are changesets that don't have child
1265 1265 changesets. They are where development generally takes place and
1266 1266 are the usual targets for update and merge operations.
1267 1267
1268 1268 Branch heads are changesets that have a given branch tag, but have
1269 1269 no child changesets with that tag. They are usually where
1270 1270 development on the given branch takes place.
1271 1271 """
1272 1272 if opts['rev']:
1273 1273 start = repo.lookup(opts['rev'])
1274 1274 else:
1275 1275 start = None
1276 1276 if not branchrevs:
1277 1277 # Assume we're looking repo-wide heads if no revs were specified.
1278 1278 heads = repo.heads(start)
1279 1279 else:
1280 1280 heads = []
1281 1281 visitedset = util.set()
1282 1282 for branchrev in branchrevs:
1283 1283 branch = repo.changectx(branchrev).branch()
1284 1284 if branch in visitedset:
1285 1285 continue
1286 1286 visitedset.add(branch)
1287 1287 bheads = repo.branchheads(branch, start)
1288 1288 if not bheads:
1289 1289 if branch != branchrev:
1290 1290 ui.warn(_("no changes on branch %s containing %s are "
1291 1291 "reachable from %s\n")
1292 1292 % (branch, branchrev, opts['rev']))
1293 1293 else:
1294 1294 ui.warn(_("no changes on branch %s are reachable from %s\n")
1295 1295 % (branch, opts['rev']))
1296 1296 heads.extend(bheads)
1297 1297 if not heads:
1298 1298 return 1
1299 1299 displayer = cmdutil.show_changeset(ui, repo, opts)
1300 1300 for n in heads:
1301 1301 displayer.show(changenode=n)
1302 1302
1303 1303 def help_(ui, name=None, with_version=False):
1304 1304 """show help for a command, extension, or list of commands
1305 1305
1306 1306 With no arguments, print a list of commands and short help.
1307 1307
1308 1308 Given a command name, print help for that command.
1309 1309
1310 1310 Given an extension name, print help for that extension, and the
1311 1311 commands it provides."""
1312 1312 option_lists = []
1313 1313
1314 1314 def addglobalopts(aliases):
1315 1315 if ui.verbose:
1316 1316 option_lists.append((_("global options:"), globalopts))
1317 1317 if name == 'shortlist':
1318 1318 option_lists.append((_('use "hg help" for the full list '
1319 1319 'of commands'), ()))
1320 1320 else:
1321 1321 if name == 'shortlist':
1322 1322 msg = _('use "hg help" for the full list of commands '
1323 1323 'or "hg -v" for details')
1324 1324 elif aliases:
1325 1325 msg = _('use "hg -v help%s" to show aliases and '
1326 1326 'global options') % (name and " " + name or "")
1327 1327 else:
1328 1328 msg = _('use "hg -v help %s" to show global options') % name
1329 1329 option_lists.append((msg, ()))
1330 1330
1331 1331 def helpcmd(name):
1332 1332 if with_version:
1333 1333 version_(ui)
1334 1334 ui.write('\n')
1335 1335 aliases, i = cmdutil.findcmd(ui, name)
1336 1336 # synopsis
1337 1337 ui.write("%s\n\n" % i[2])
1338 1338
1339 1339 # description
1340 1340 doc = i[0].__doc__
1341 1341 if not doc:
1342 1342 doc = _("(No help text available)")
1343 1343 if ui.quiet:
1344 1344 doc = doc.splitlines(0)[0]
1345 1345 ui.write("%s\n" % doc.rstrip())
1346 1346
1347 1347 if not ui.quiet:
1348 1348 # aliases
1349 1349 if len(aliases) > 1:
1350 1350 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1351 1351
1352 1352 # options
1353 1353 if i[1]:
1354 1354 option_lists.append((_("options:\n"), i[1]))
1355 1355
1356 1356 addglobalopts(False)
1357 1357
1358 1358 def helplist(select=None):
1359 1359 h = {}
1360 1360 cmds = {}
1361 1361 for c, e in table.items():
1362 1362 f = c.split("|", 1)[0]
1363 1363 if select and not select(f):
1364 1364 continue
1365 1365 if name == "shortlist" and not f.startswith("^"):
1366 1366 continue
1367 1367 f = f.lstrip("^")
1368 1368 if not ui.debugflag and f.startswith("debug"):
1369 1369 continue
1370 1370 doc = e[0].__doc__
1371 1371 if not doc:
1372 1372 doc = _("(No help text available)")
1373 1373 h[f] = doc.splitlines(0)[0].rstrip()
1374 1374 cmds[f] = c.lstrip("^")
1375 1375
1376 1376 fns = h.keys()
1377 1377 fns.sort()
1378 1378 m = max(map(len, fns))
1379 1379 for f in fns:
1380 1380 if ui.verbose:
1381 1381 commands = cmds[f].replace("|",", ")
1382 1382 ui.write(" %s:\n %s\n"%(commands, h[f]))
1383 1383 else:
1384 1384 ui.write(' %-*s %s\n' % (m, f, h[f]))
1385 1385
1386 1386 if not ui.quiet:
1387 1387 addglobalopts(True)
1388 1388
1389 1389 def helptopic(name):
1390 1390 v = None
1391 1391 for i in help.helptable:
1392 1392 l = i.split('|')
1393 1393 if name in l:
1394 1394 v = i
1395 1395 header = l[-1]
1396 1396 if not v:
1397 1397 raise cmdutil.UnknownCommand(name)
1398 1398
1399 1399 # description
1400 1400 doc = help.helptable[v]
1401 1401 if not doc:
1402 1402 doc = _("(No help text available)")
1403 1403 if callable(doc):
1404 1404 doc = doc()
1405 1405
1406 1406 ui.write("%s\n" % header)
1407 1407 ui.write("%s\n" % doc.rstrip())
1408 1408
1409 1409 def helpext(name):
1410 1410 try:
1411 1411 mod = extensions.find(name)
1412 1412 except KeyError:
1413 1413 raise cmdutil.UnknownCommand(name)
1414 1414
1415 1415 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1416 1416 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1417 1417 for d in doc[1:]:
1418 1418 ui.write(d, '\n')
1419 1419
1420 1420 ui.status('\n')
1421 1421
1422 1422 try:
1423 1423 ct = mod.cmdtable
1424 1424 except AttributeError:
1425 1425 ct = None
1426 1426 if not ct:
1427 1427 ui.status(_('no commands defined\n'))
1428 1428 return
1429 1429
1430 1430 ui.status(_('list of commands:\n\n'))
1431 1431 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1432 1432 helplist(modcmds.has_key)
1433 1433
1434 1434 if name and name != 'shortlist':
1435 1435 i = None
1436 1436 for f in (helpcmd, helptopic, helpext):
1437 1437 try:
1438 1438 f(name)
1439 1439 i = None
1440 1440 break
1441 1441 except cmdutil.UnknownCommand, inst:
1442 1442 i = inst
1443 1443 if i:
1444 1444 raise i
1445 1445
1446 1446 else:
1447 1447 # program name
1448 1448 if ui.verbose or with_version:
1449 1449 version_(ui)
1450 1450 else:
1451 1451 ui.status(_("Mercurial Distributed SCM\n"))
1452 1452 ui.status('\n')
1453 1453
1454 1454 # list of commands
1455 1455 if name == "shortlist":
1456 1456 ui.status(_('basic commands:\n\n'))
1457 1457 else:
1458 1458 ui.status(_('list of commands:\n\n'))
1459 1459
1460 1460 helplist()
1461 1461
1462 1462 # list all option lists
1463 1463 opt_output = []
1464 1464 for title, options in option_lists:
1465 1465 opt_output.append(("\n%s" % title, None))
1466 1466 for shortopt, longopt, default, desc in options:
1467 1467 if "DEPRECATED" in desc and not ui.verbose: continue
1468 1468 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1469 1469 longopt and " --%s" % longopt),
1470 1470 "%s%s" % (desc,
1471 1471 default
1472 1472 and _(" (default: %s)") % default
1473 1473 or "")))
1474 1474
1475 1475 if opt_output:
1476 1476 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1477 1477 for first, second in opt_output:
1478 1478 if second:
1479 1479 ui.write(" %-*s %s\n" % (opts_len, first, second))
1480 1480 else:
1481 1481 ui.write("%s\n" % first)
1482 1482
1483 1483 def identify(ui, repo, source=None,
1484 1484 rev=None, num=None, id=None, branch=None, tags=None):
1485 1485 """identify the working copy or specified revision
1486 1486
1487 1487 With no revision, print a summary of the current state of the repo.
1488 1488
1489 1489 With a path, do a lookup in another repository.
1490 1490
1491 1491 This summary identifies the repository state using one or two parent
1492 1492 hash identifiers, followed by a "+" if there are uncommitted changes
1493 1493 in the working directory, a list of tags for this revision and a branch
1494 1494 name for non-default branches.
1495 1495 """
1496 1496
1497 1497 hexfunc = ui.debugflag and hex or short
1498 1498 default = not (num or id or branch or tags)
1499 1499 output = []
1500 1500
1501 1501 if source:
1502 1502 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1503 1503 srepo = hg.repository(ui, source)
1504 1504 if not rev and revs:
1505 1505 rev = revs[0]
1506 1506 if not rev:
1507 1507 rev = "tip"
1508 1508 if num or branch or tags:
1509 1509 raise util.Abort(
1510 1510 "can't query remote revision number, branch, or tags")
1511 1511 output = [hexfunc(srepo.lookup(rev))]
1512 1512 elif not rev:
1513 1513 ctx = repo.workingctx()
1514 1514 parents = ctx.parents()
1515 1515 changed = False
1516 1516 if default or id or num:
1517 1517 changed = ctx.files() + ctx.deleted()
1518 1518 if default or id:
1519 1519 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1520 1520 (changed) and "+" or "")]
1521 1521 if num:
1522 1522 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1523 1523 (changed) and "+" or ""))
1524 1524 else:
1525 1525 ctx = repo.changectx(rev)
1526 1526 if default or id:
1527 1527 output = [hexfunc(ctx.node())]
1528 1528 if num:
1529 1529 output.append(str(ctx.rev()))
1530 1530
1531 1531 if not source and default and not ui.quiet:
1532 1532 b = util.tolocal(ctx.branch())
1533 1533 if b != 'default':
1534 1534 output.append("(%s)" % b)
1535 1535
1536 1536 # multiple tags for a single parent separated by '/'
1537 1537 t = "/".join(ctx.tags())
1538 1538 if t:
1539 1539 output.append(t)
1540 1540
1541 1541 if branch:
1542 1542 output.append(util.tolocal(ctx.branch()))
1543 1543
1544 1544 if tags:
1545 1545 output.extend(ctx.tags())
1546 1546
1547 1547 ui.write("%s\n" % ' '.join(output))
1548 1548
1549 1549 def import_(ui, repo, patch1, *patches, **opts):
1550 1550 """import an ordered set of patches
1551 1551
1552 1552 Import a list of patches and commit them individually.
1553 1553
1554 1554 If there are outstanding changes in the working directory, import
1555 1555 will abort unless given the -f flag.
1556 1556
1557 1557 You can import a patch straight from a mail message. Even patches
1558 1558 as attachments work (body part must be type text/plain or
1559 1559 text/x-patch to be used). From and Subject headers of email
1560 1560 message are used as default committer and commit message. All
1561 1561 text/plain body parts before first diff are added to commit
1562 1562 message.
1563 1563
1564 1564 If the imported patch was generated by hg export, user and description
1565 1565 from patch override values from message headers and body. Values
1566 1566 given on command line with -m and -u override these.
1567 1567
1568 1568 If --exact is specified, import will set the working directory
1569 1569 to the parent of each patch before applying it, and will abort
1570 1570 if the resulting changeset has a different ID than the one
1571 1571 recorded in the patch. This may happen due to character set
1572 1572 problems or other deficiencies in the text patch format.
1573 1573
1574 1574 To read a patch from standard input, use patch name "-".
1575 1575 """
1576 1576 patches = (patch1,) + patches
1577 1577
1578 1578 if opts.get('exact') or not opts['force']:
1579 1579 cmdutil.bail_if_changed(repo)
1580 1580
1581 1581 d = opts["base"]
1582 1582 strip = opts["strip"]
1583 1583
1584 1584 wlock = repo.wlock()
1585 1585 lock = repo.lock()
1586 1586
1587 1587 for p in patches:
1588 1588 pf = os.path.join(d, p)
1589 1589
1590 1590 if pf == '-':
1591 1591 ui.status(_("applying patch from stdin\n"))
1592 1592 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1593 1593 else:
1594 1594 ui.status(_("applying %s\n") % p)
1595 1595 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf, 'rb'))
1596 1596
1597 1597 if tmpname is None:
1598 1598 raise util.Abort(_('no diffs found'))
1599 1599
1600 1600 try:
1601 1601 cmdline_message = cmdutil.logmessage(opts)
1602 1602 if cmdline_message:
1603 1603 # pickup the cmdline msg
1604 1604 message = cmdline_message
1605 1605 elif message:
1606 1606 # pickup the patch msg
1607 1607 message = message.strip()
1608 1608 else:
1609 1609 # launch the editor
1610 1610 message = None
1611 1611 ui.debug(_('message:\n%s\n') % message)
1612 1612
1613 1613 wp = repo.workingctx().parents()
1614 1614 if opts.get('exact'):
1615 1615 if not nodeid or not p1:
1616 1616 raise util.Abort(_('not a mercurial patch'))
1617 1617 p1 = repo.lookup(p1)
1618 1618 p2 = repo.lookup(p2 or hex(nullid))
1619 1619
1620 1620 if p1 != wp[0].node():
1621 1621 hg.clean(repo, p1, wlock=wlock)
1622 1622 repo.dirstate.setparents(p1, p2)
1623 1623 elif p2:
1624 1624 try:
1625 1625 p1 = repo.lookup(p1)
1626 1626 p2 = repo.lookup(p2)
1627 1627 if p1 == wp[0].node():
1628 1628 repo.dirstate.setparents(p1, p2)
1629 1629 except hg.RepoError:
1630 1630 pass
1631 1631 if opts.get('exact') or opts.get('import_branch'):
1632 1632 repo.dirstate.setbranch(branch or 'default')
1633 1633
1634 1634 files = {}
1635 1635 try:
1636 1636 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1637 1637 files=files)
1638 1638 finally:
1639 1639 files = patch.updatedir(ui, repo, files, wlock=wlock)
1640 1640 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1641 1641 if opts.get('exact'):
1642 1642 if hex(n) != nodeid:
1643 1643 repo.rollback(wlock=wlock, lock=lock)
1644 1644 raise util.Abort(_('patch is damaged or loses information'))
1645 1645 finally:
1646 1646 os.unlink(tmpname)
1647 1647
1648 1648 def incoming(ui, repo, source="default", **opts):
1649 1649 """show new changesets found in source
1650 1650
1651 1651 Show new changesets found in the specified path/URL or the default
1652 1652 pull location. These are the changesets that would be pulled if a pull
1653 1653 was requested.
1654 1654
1655 1655 For remote repository, using --bundle avoids downloading the changesets
1656 1656 twice if the incoming is followed by a pull.
1657 1657
1658 1658 See pull for valid source format details.
1659 1659 """
1660 1660 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1661 1661 cmdutil.setremoteconfig(ui, opts)
1662 1662
1663 1663 other = hg.repository(ui, source)
1664 1664 ui.status(_('comparing with %s\n') % source)
1665 1665 if revs:
1666 1666 if 'lookup' in other.capabilities:
1667 1667 revs = [other.lookup(rev) for rev in revs]
1668 1668 else:
1669 1669 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1670 1670 raise util.Abort(error)
1671 1671 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1672 1672 if not incoming:
1673 1673 try:
1674 1674 os.unlink(opts["bundle"])
1675 1675 except:
1676 1676 pass
1677 1677 ui.status(_("no changes found\n"))
1678 1678 return 1
1679 1679
1680 1680 cleanup = None
1681 1681 try:
1682 1682 fname = opts["bundle"]
1683 1683 if fname or not other.local():
1684 1684 # create a bundle (uncompressed if other repo is not local)
1685 1685 if revs is None:
1686 1686 cg = other.changegroup(incoming, "incoming")
1687 1687 else:
1688 1688 if 'changegroupsubset' not in other.capabilities:
1689 1689 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1690 1690 cg = other.changegroupsubset(incoming, revs, 'incoming')
1691 1691 bundletype = other.local() and "HG10BZ" or "HG10UN"
1692 1692 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1693 1693 # keep written bundle?
1694 1694 if opts["bundle"]:
1695 1695 cleanup = None
1696 1696 if not other.local():
1697 1697 # use the created uncompressed bundlerepo
1698 1698 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1699 1699
1700 1700 o = other.changelog.nodesbetween(incoming, revs)[0]
1701 1701 if opts['newest_first']:
1702 1702 o.reverse()
1703 1703 displayer = cmdutil.show_changeset(ui, other, opts)
1704 1704 for n in o:
1705 1705 parents = [p for p in other.changelog.parents(n) if p != nullid]
1706 1706 if opts['no_merges'] and len(parents) == 2:
1707 1707 continue
1708 1708 displayer.show(changenode=n)
1709 1709 finally:
1710 1710 if hasattr(other, 'close'):
1711 1711 other.close()
1712 1712 if cleanup:
1713 1713 os.unlink(cleanup)
1714 1714
1715 1715 def init(ui, dest=".", **opts):
1716 1716 """create a new repository in the given directory
1717 1717
1718 1718 Initialize a new repository in the given directory. If the given
1719 1719 directory does not exist, it is created.
1720 1720
1721 1721 If no directory is given, the current directory is used.
1722 1722
1723 1723 It is possible to specify an ssh:// URL as the destination.
1724 1724 Look at the help text for the pull command for important details
1725 1725 about ssh:// URLs.
1726 1726 """
1727 1727 cmdutil.setremoteconfig(ui, opts)
1728 1728 hg.repository(ui, dest, create=1)
1729 1729
1730 1730 def locate(ui, repo, *pats, **opts):
1731 1731 """locate files matching specific patterns
1732 1732
1733 1733 Print all files under Mercurial control whose names match the
1734 1734 given patterns.
1735 1735
1736 1736 This command searches the entire repository by default. To search
1737 1737 just the current directory and its subdirectories, use
1738 1738 "--include .".
1739 1739
1740 1740 If no patterns are given to match, this command prints all file
1741 1741 names.
1742 1742
1743 1743 If you want to feed the output of this command into the "xargs"
1744 1744 command, use the "-0" option to both this command and "xargs".
1745 1745 This will avoid the problem of "xargs" treating single filenames
1746 1746 that contain white space as multiple filenames.
1747 1747 """
1748 1748 end = opts['print0'] and '\0' or '\n'
1749 1749 rev = opts['rev']
1750 1750 if rev:
1751 1751 node = repo.lookup(rev)
1752 1752 else:
1753 1753 node = None
1754 1754
1755 1755 ret = 1
1756 1756 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1757 1757 badmatch=util.always,
1758 1758 default='relglob'):
1759 1759 if src == 'b':
1760 1760 continue
1761 1761 if not node and repo.dirstate.state(abs) == '?':
1762 1762 continue
1763 1763 if opts['fullpath']:
1764 1764 ui.write(os.path.join(repo.root, abs), end)
1765 1765 else:
1766 1766 ui.write(((pats and rel) or abs), end)
1767 1767 ret = 0
1768 1768
1769 1769 return ret
1770 1770
1771 1771 def log(ui, repo, *pats, **opts):
1772 1772 """show revision history of entire repository or files
1773 1773
1774 1774 Print the revision history of the specified files or the entire
1775 1775 project.
1776 1776
1777 1777 File history is shown without following rename or copy history of
1778 1778 files. Use -f/--follow with a file name to follow history across
1779 1779 renames and copies. --follow without a file name will only show
1780 1780 ancestors or descendants of the starting revision. --follow-first
1781 1781 only follows the first parent of merge revisions.
1782 1782
1783 1783 If no revision range is specified, the default is tip:0 unless
1784 1784 --follow is set, in which case the working directory parent is
1785 1785 used as the starting revision.
1786 1786
1787 1787 By default this command outputs: changeset id and hash, tags,
1788 1788 non-trivial parents, user, date and time, and a summary for each
1789 1789 commit. When the -v/--verbose switch is used, the list of changed
1790 1790 files and full commit message is shown.
1791 1791
1792 1792 NOTE: log -p may generate unexpected diff output for merge
1793 1793 changesets, as it will compare the merge changeset against its
1794 1794 first parent only. Also, the files: list will only reflect files
1795 1795 that are different from BOTH parents.
1796 1796
1797 1797 """
1798 1798
1799 1799 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1800 1800 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1801 1801
1802 1802 if opts['limit']:
1803 1803 try:
1804 1804 limit = int(opts['limit'])
1805 1805 except ValueError:
1806 1806 raise util.Abort(_('limit must be a positive integer'))
1807 1807 if limit <= 0: raise util.Abort(_('limit must be positive'))
1808 1808 else:
1809 1809 limit = sys.maxint
1810 1810 count = 0
1811 1811
1812 1812 if opts['copies'] and opts['rev']:
1813 1813 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1814 1814 else:
1815 1815 endrev = repo.changelog.count()
1816 1816 rcache = {}
1817 1817 ncache = {}
1818 1818 dcache = []
1819 1819 def getrenamed(fn, rev, man):
1820 1820 '''looks up all renames for a file (up to endrev) the first
1821 1821 time the file is given. It indexes on the changerev and only
1822 1822 parses the manifest if linkrev != changerev.
1823 1823 Returns rename info for fn at changerev rev.'''
1824 1824 if fn not in rcache:
1825 1825 rcache[fn] = {}
1826 1826 ncache[fn] = {}
1827 1827 fl = repo.file(fn)
1828 1828 for i in xrange(fl.count()):
1829 1829 node = fl.node(i)
1830 1830 lr = fl.linkrev(node)
1831 1831 renamed = fl.renamed(node)
1832 1832 rcache[fn][lr] = renamed
1833 1833 if renamed:
1834 1834 ncache[fn][node] = renamed
1835 1835 if lr >= endrev:
1836 1836 break
1837 1837 if rev in rcache[fn]:
1838 1838 return rcache[fn][rev]
1839 1839 mr = repo.manifest.rev(man)
1840 1840 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1841 1841 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1842 1842 if not dcache or dcache[0] != man:
1843 1843 dcache[:] = [man, repo.manifest.readdelta(man)]
1844 1844 if fn in dcache[1]:
1845 1845 return ncache[fn].get(dcache[1][fn])
1846 1846 return None
1847 1847
1848 1848 df = False
1849 1849 if opts["date"]:
1850 1850 df = util.matchdate(opts["date"])
1851 1851
1852 1852 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1853 1853 for st, rev, fns in changeiter:
1854 1854 if st == 'add':
1855 1855 changenode = repo.changelog.node(rev)
1856 1856 parents = [p for p in repo.changelog.parentrevs(rev)
1857 1857 if p != nullrev]
1858 1858 if opts['no_merges'] and len(parents) == 2:
1859 1859 continue
1860 1860 if opts['only_merges'] and len(parents) != 2:
1861 1861 continue
1862 1862
1863 1863 if df:
1864 1864 changes = get(rev)
1865 1865 if not df(changes[2][0]):
1866 1866 continue
1867 1867
1868 1868 if opts['keyword']:
1869 1869 changes = get(rev)
1870 1870 miss = 0
1871 1871 for k in [kw.lower() for kw in opts['keyword']]:
1872 1872 if not (k in changes[1].lower() or
1873 1873 k in changes[4].lower() or
1874 1874 k in " ".join(changes[3]).lower()):
1875 1875 miss = 1
1876 1876 break
1877 1877 if miss:
1878 1878 continue
1879 1879
1880 1880 copies = []
1881 1881 if opts.get('copies') and rev:
1882 1882 mf = get(rev)[0]
1883 1883 for fn in get(rev)[3]:
1884 1884 rename = getrenamed(fn, rev, mf)
1885 1885 if rename:
1886 1886 copies.append((fn, rename[0]))
1887 1887 displayer.show(rev, changenode, copies=copies)
1888 1888 elif st == 'iter':
1889 1889 if count == limit: break
1890 1890 if displayer.flush(rev):
1891 1891 count += 1
1892 1892
1893 1893 def manifest(ui, repo, rev=None):
1894 1894 """output the current or given revision of the project manifest
1895 1895
1896 1896 Print a list of version controlled files for the given revision.
1897 1897 If no revision is given, the parent of the working directory is used,
1898 1898 or tip if no revision is checked out.
1899 1899
1900 1900 The manifest is the list of files being version controlled. If no revision
1901 1901 is given then the first parent of the working directory is used.
1902 1902
1903 1903 With -v flag, print file permissions. With --debug flag, print
1904 1904 file revision hashes.
1905 1905 """
1906 1906
1907 1907 m = repo.changectx(rev).manifest()
1908 1908 files = m.keys()
1909 1909 files.sort()
1910 1910
1911 1911 for f in files:
1912 1912 if ui.debugflag:
1913 1913 ui.write("%40s " % hex(m[f]))
1914 1914 if ui.verbose:
1915 1915 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1916 1916 ui.write("%s\n" % f)
1917 1917
1918 1918 def merge(ui, repo, node=None, force=None, rev=None):
1919 1919 """merge working directory with another revision
1920 1920
1921 1921 Merge the contents of the current working directory and the
1922 1922 requested revision. Files that changed between either parent are
1923 1923 marked as changed for the next commit and a commit must be
1924 1924 performed before any further updates are allowed.
1925 1925
1926 1926 If no revision is specified, the working directory's parent is a
1927 1927 head revision, and the repository contains exactly one other head,
1928 1928 the other head is merged with by default. Otherwise, an explicit
1929 1929 revision to merge with must be provided.
1930 1930 """
1931 1931
1932 1932 if rev and node:
1933 1933 raise util.Abort(_("please specify just one revision"))
1934 1934
1935 1935 if not node:
1936 1936 node = rev
1937 1937
1938 1938 if not node:
1939 1939 heads = repo.heads()
1940 1940 if len(heads) > 2:
1941 1941 raise util.Abort(_('repo has %d heads - '
1942 1942 'please merge with an explicit rev') %
1943 1943 len(heads))
1944 1944 if len(heads) == 1:
1945 1945 raise util.Abort(_('there is nothing to merge - '
1946 1946 'use "hg update" instead'))
1947 1947 parent = repo.dirstate.parents()[0]
1948 1948 if parent not in heads:
1949 1949 raise util.Abort(_('working dir not at a head rev - '
1950 1950 'use "hg update" or merge with an explicit rev'))
1951 1951 node = parent == heads[0] and heads[-1] or heads[0]
1952 1952 return hg.merge(repo, node, force=force)
1953 1953
1954 1954 def outgoing(ui, repo, dest=None, **opts):
1955 1955 """show changesets not found in destination
1956 1956
1957 1957 Show changesets not found in the specified destination repository or
1958 1958 the default push location. These are the changesets that would be pushed
1959 1959 if a push was requested.
1960 1960
1961 1961 See pull for valid destination format details.
1962 1962 """
1963 1963 dest, revs = cmdutil.parseurl(
1964 1964 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1965 1965 cmdutil.setremoteconfig(ui, opts)
1966 1966 if revs:
1967 1967 revs = [repo.lookup(rev) for rev in revs]
1968 1968
1969 1969 other = hg.repository(ui, dest)
1970 1970 ui.status(_('comparing with %s\n') % dest)
1971 1971 o = repo.findoutgoing(other, force=opts['force'])
1972 1972 if not o:
1973 1973 ui.status(_("no changes found\n"))
1974 1974 return 1
1975 1975 o = repo.changelog.nodesbetween(o, revs)[0]
1976 1976 if opts['newest_first']:
1977 1977 o.reverse()
1978 1978 displayer = cmdutil.show_changeset(ui, repo, opts)
1979 1979 for n in o:
1980 1980 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1981 1981 if opts['no_merges'] and len(parents) == 2:
1982 1982 continue
1983 1983 displayer.show(changenode=n)
1984 1984
1985 1985 def parents(ui, repo, file_=None, **opts):
1986 1986 """show the parents of the working dir or revision
1987 1987
1988 1988 Print the working directory's parent revisions. If a
1989 1989 revision is given via --rev, the parent of that revision
1990 1990 will be printed. If a file argument is given, revision in
1991 1991 which the file was last changed (before the working directory
1992 1992 revision or the argument to --rev if given) is printed.
1993 1993 """
1994 1994 rev = opts.get('rev')
1995 1995 if file_:
1996 ctx = repo.filectx(file_, changeid=rev)
1996 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1997 if anypats or len(files) != 1:
1998 raise util.Abort(_('can only specify an explicit file name'))
1999 ctx = repo.filectx(files[0], changeid=rev)
1997 2000 elif rev:
1998 2001 ctx = repo.changectx(rev)
1999 2002 else:
2000 2003 ctx = repo.workingctx()
2001 2004 p = [cp.node() for cp in ctx.parents()]
2002 2005
2003 2006 displayer = cmdutil.show_changeset(ui, repo, opts)
2004 2007 for n in p:
2005 2008 if n != nullid:
2006 2009 displayer.show(changenode=n)
2007 2010
2008 2011 def paths(ui, repo, search=None):
2009 2012 """show definition of symbolic path names
2010 2013
2011 2014 Show definition of symbolic path name NAME. If no name is given, show
2012 2015 definition of available names.
2013 2016
2014 2017 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2015 2018 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2016 2019 """
2017 2020 if search:
2018 2021 for name, path in ui.configitems("paths"):
2019 2022 if name == search:
2020 2023 ui.write("%s\n" % path)
2021 2024 return
2022 2025 ui.warn(_("not found!\n"))
2023 2026 return 1
2024 2027 else:
2025 2028 for name, path in ui.configitems("paths"):
2026 2029 ui.write("%s = %s\n" % (name, path))
2027 2030
2028 2031 def postincoming(ui, repo, modheads, optupdate, wasempty):
2029 2032 if modheads == 0:
2030 2033 return
2031 2034 if optupdate:
2032 2035 if wasempty:
2033 2036 return hg.update(repo, repo.lookup('default'))
2034 2037 elif modheads == 1:
2035 2038 return hg.update(repo, repo.changelog.tip()) # update
2036 2039 else:
2037 2040 ui.status(_("not updating, since new heads added\n"))
2038 2041 if modheads > 1:
2039 2042 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2040 2043 else:
2041 2044 ui.status(_("(run 'hg update' to get a working copy)\n"))
2042 2045
2043 2046 def pull(ui, repo, source="default", **opts):
2044 2047 """pull changes from the specified source
2045 2048
2046 2049 Pull changes from a remote repository to a local one.
2047 2050
2048 2051 This finds all changes from the repository at the specified path
2049 2052 or URL and adds them to the local repository. By default, this
2050 2053 does not update the copy of the project in the working directory.
2051 2054
2052 2055 Valid URLs are of the form:
2053 2056
2054 2057 local/filesystem/path (or file://local/filesystem/path)
2055 2058 http://[user@]host[:port]/[path]
2056 2059 https://[user@]host[:port]/[path]
2057 2060 ssh://[user@]host[:port]/[path]
2058 2061 static-http://host[:port]/[path]
2059 2062
2060 2063 Paths in the local filesystem can either point to Mercurial
2061 2064 repositories or to bundle files (as created by 'hg bundle' or
2062 2065 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2063 2066 allows access to a Mercurial repository where you simply use a web
2064 2067 server to publish the .hg directory as static content.
2065 2068
2066 2069 An optional identifier after # indicates a particular branch, tag,
2067 2070 or changeset to pull.
2068 2071
2069 2072 Some notes about using SSH with Mercurial:
2070 2073 - SSH requires an accessible shell account on the destination machine
2071 2074 and a copy of hg in the remote path or specified with as remotecmd.
2072 2075 - path is relative to the remote user's home directory by default.
2073 2076 Use an extra slash at the start of a path to specify an absolute path:
2074 2077 ssh://example.com//tmp/repository
2075 2078 - Mercurial doesn't use its own compression via SSH; the right thing
2076 2079 to do is to configure it in your ~/.ssh/config, e.g.:
2077 2080 Host *.mylocalnetwork.example.com
2078 2081 Compression no
2079 2082 Host *
2080 2083 Compression yes
2081 2084 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2082 2085 with the --ssh command line option.
2083 2086 """
2084 2087 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2085 2088 cmdutil.setremoteconfig(ui, opts)
2086 2089
2087 2090 other = hg.repository(ui, source)
2088 2091 ui.status(_('pulling from %s\n') % (source))
2089 2092 if revs:
2090 2093 if 'lookup' in other.capabilities:
2091 2094 revs = [other.lookup(rev) for rev in revs]
2092 2095 else:
2093 2096 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2094 2097 raise util.Abort(error)
2095 2098
2096 2099 wasempty = repo.changelog.count() == 0
2097 2100 modheads = repo.pull(other, heads=revs, force=opts['force'])
2098 2101 return postincoming(ui, repo, modheads, opts['update'], wasempty)
2099 2102
2100 2103 def push(ui, repo, dest=None, **opts):
2101 2104 """push changes to the specified destination
2102 2105
2103 2106 Push changes from the local repository to the given destination.
2104 2107
2105 2108 This is the symmetrical operation for pull. It helps to move
2106 2109 changes from the current repository to a different one. If the
2107 2110 destination is local this is identical to a pull in that directory
2108 2111 from the current one.
2109 2112
2110 2113 By default, push will refuse to run if it detects the result would
2111 2114 increase the number of remote heads. This generally indicates the
2112 2115 the client has forgotten to sync and merge before pushing.
2113 2116
2114 2117 Valid URLs are of the form:
2115 2118
2116 2119 local/filesystem/path (or file://local/filesystem/path)
2117 2120 ssh://[user@]host[:port]/[path]
2118 2121 http://[user@]host[:port]/[path]
2119 2122 https://[user@]host[:port]/[path]
2120 2123
2121 2124 An optional identifier after # indicates a particular branch, tag,
2122 2125 or changeset to push.
2123 2126
2124 2127 Look at the help text for the pull command for important details
2125 2128 about ssh:// URLs.
2126 2129
2127 2130 Pushing to http:// and https:// URLs is only possible, if this
2128 2131 feature is explicitly enabled on the remote Mercurial server.
2129 2132 """
2130 2133 dest, revs = cmdutil.parseurl(
2131 2134 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2132 2135 cmdutil.setremoteconfig(ui, opts)
2133 2136
2134 2137 other = hg.repository(ui, dest)
2135 2138 ui.status('pushing to %s\n' % (dest))
2136 2139 if revs:
2137 2140 revs = [repo.lookup(rev) for rev in revs]
2138 2141 r = repo.push(other, opts['force'], revs=revs)
2139 2142 return r == 0
2140 2143
2141 2144 def rawcommit(ui, repo, *pats, **opts):
2142 2145 """raw commit interface (DEPRECATED)
2143 2146
2144 2147 (DEPRECATED)
2145 2148 Lowlevel commit, for use in helper scripts.
2146 2149
2147 2150 This command is not intended to be used by normal users, as it is
2148 2151 primarily useful for importing from other SCMs.
2149 2152
2150 2153 This command is now deprecated and will be removed in a future
2151 2154 release, please use debugsetparents and commit instead.
2152 2155 """
2153 2156
2154 2157 ui.warn(_("(the rawcommit command is deprecated)\n"))
2155 2158
2156 2159 message = cmdutil.logmessage(opts)
2157 2160
2158 2161 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2159 2162 if opts['files']:
2160 2163 files += open(opts['files']).read().splitlines()
2161 2164
2162 2165 parents = [repo.lookup(p) for p in opts['parent']]
2163 2166
2164 2167 try:
2165 2168 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2166 2169 except ValueError, inst:
2167 2170 raise util.Abort(str(inst))
2168 2171
2169 2172 def recover(ui, repo):
2170 2173 """roll back an interrupted transaction
2171 2174
2172 2175 Recover from an interrupted commit or pull.
2173 2176
2174 2177 This command tries to fix the repository status after an interrupted
2175 2178 operation. It should only be necessary when Mercurial suggests it.
2176 2179 """
2177 2180 if repo.recover():
2178 2181 return hg.verify(repo)
2179 2182 return 1
2180 2183
2181 2184 def remove(ui, repo, *pats, **opts):
2182 2185 """remove the specified files on the next commit
2183 2186
2184 2187 Schedule the indicated files for removal from the repository.
2185 2188
2186 2189 This only removes files from the current branch, not from the
2187 2190 entire project history. If the files still exist in the working
2188 2191 directory, they will be deleted from it. If invoked with --after,
2189 2192 files are marked as removed, but not actually unlinked unless --force
2190 2193 is also given. Without exact file names, --after will only mark
2191 2194 files as removed if they are no longer in the working directory.
2192 2195
2193 2196 This command schedules the files to be removed at the next commit.
2194 2197 To undo a remove before that, see hg revert.
2195 2198
2196 2199 Modified files and added files are not removed by default. To
2197 2200 remove them, use the -f/--force option.
2198 2201 """
2199 2202 names = []
2200 2203 if not opts['after'] and not pats:
2201 2204 raise util.Abort(_('no files specified'))
2202 2205 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2203 2206 exact = dict.fromkeys(files)
2204 2207 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2205 2208 modified, added, removed, deleted, unknown = mardu
2206 2209 remove, forget = [], []
2207 2210 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2208 2211 reason = None
2209 2212 if abs in modified and not opts['force']:
2210 2213 reason = _('is modified (use -f to force removal)')
2211 2214 elif abs in added:
2212 2215 if opts['force']:
2213 2216 forget.append(abs)
2214 2217 continue
2215 2218 reason = _('has been marked for add (use -f to force removal)')
2216 2219 elif repo.dirstate.state(abs) == '?':
2217 2220 reason = _('is not managed')
2218 2221 elif opts['after'] and not exact and abs not in deleted:
2219 2222 continue
2220 2223 elif abs in removed:
2221 2224 continue
2222 2225 if reason:
2223 2226 if exact:
2224 2227 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2225 2228 else:
2226 2229 if ui.verbose or not exact:
2227 2230 ui.status(_('removing %s\n') % rel)
2228 2231 remove.append(abs)
2229 2232 repo.forget(forget)
2230 2233 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2231 2234
2232 2235 def rename(ui, repo, *pats, **opts):
2233 2236 """rename files; equivalent of copy + remove
2234 2237
2235 2238 Mark dest as copies of sources; mark sources for deletion. If
2236 2239 dest is a directory, copies are put in that directory. If dest is
2237 2240 a file, there can only be one source.
2238 2241
2239 2242 By default, this command copies the contents of files as they
2240 2243 stand in the working directory. If invoked with --after, the
2241 2244 operation is recorded, but no copying is performed.
2242 2245
2243 2246 This command takes effect in the next commit. To undo a rename
2244 2247 before that, see hg revert.
2245 2248 """
2246 2249 wlock = repo.wlock(0)
2247 2250 errs, copied = docopy(ui, repo, pats, opts, wlock)
2248 2251 names = []
2249 2252 for abs, rel, exact in copied:
2250 2253 if ui.verbose or not exact:
2251 2254 ui.status(_('removing %s\n') % rel)
2252 2255 names.append(abs)
2253 2256 if not opts.get('dry_run'):
2254 2257 repo.remove(names, True, wlock=wlock)
2255 2258 return errs
2256 2259
2257 2260 def revert(ui, repo, *pats, **opts):
2258 2261 """revert files or dirs to their states as of some revision
2259 2262
2260 2263 With no revision specified, revert the named files or directories
2261 2264 to the contents they had in the parent of the working directory.
2262 2265 This restores the contents of the affected files to an unmodified
2263 2266 state and unschedules adds, removes, copies, and renames. If the
2264 2267 working directory has two parents, you must explicitly specify the
2265 2268 revision to revert to.
2266 2269
2267 2270 Modified files are saved with a .orig suffix before reverting.
2268 2271 To disable these backups, use --no-backup.
2269 2272
2270 2273 Using the -r option, revert the given files or directories to their
2271 2274 contents as of a specific revision. This can be helpful to "roll
2272 2275 back" some or all of a change that should not have been committed.
2273 2276
2274 2277 Revert modifies the working directory. It does not commit any
2275 2278 changes, or change the parent of the working directory. If you
2276 2279 revert to a revision other than the parent of the working
2277 2280 directory, the reverted files will thus appear modified
2278 2281 afterwards.
2279 2282
2280 2283 If a file has been deleted, it is restored. If the executable
2281 2284 mode of a file was changed, it is reset.
2282 2285
2283 2286 If names are given, all files matching the names are reverted.
2284 2287
2285 2288 If no arguments are given, no files are reverted.
2286 2289 """
2287 2290
2288 2291 if opts["date"]:
2289 2292 if opts["rev"]:
2290 2293 raise util.Abort(_("you can't specify a revision and a date"))
2291 2294 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2292 2295
2293 2296 if not pats and not opts['all']:
2294 2297 raise util.Abort(_('no files or directories specified; '
2295 2298 'use --all to revert the whole repo'))
2296 2299
2297 2300 parent, p2 = repo.dirstate.parents()
2298 2301 if not opts['rev'] and p2 != nullid:
2299 2302 raise util.Abort(_('uncommitted merge - please provide a '
2300 2303 'specific revision'))
2301 2304 ctx = repo.changectx(opts['rev'])
2302 2305 node = ctx.node()
2303 2306 mf = ctx.manifest()
2304 2307 if node == parent:
2305 2308 pmf = mf
2306 2309 else:
2307 2310 pmf = None
2308 2311
2309 2312 wlock = repo.wlock()
2310 2313
2311 2314 # need all matching names in dirstate and manifest of target rev,
2312 2315 # so have to walk both. do not print errors if files exist in one
2313 2316 # but not other.
2314 2317
2315 2318 names = {}
2316 2319 target_only = {}
2317 2320
2318 2321 # walk dirstate.
2319 2322
2320 2323 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2321 2324 badmatch=mf.has_key):
2322 2325 names[abs] = (rel, exact)
2323 2326 if src == 'b':
2324 2327 target_only[abs] = True
2325 2328
2326 2329 # walk target manifest.
2327 2330
2328 2331 def badmatch(path):
2329 2332 if path in names:
2330 2333 return True
2331 2334 path_ = path + '/'
2332 2335 for f in names:
2333 2336 if f.startswith(path_):
2334 2337 return True
2335 2338 return False
2336 2339
2337 2340 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2338 2341 badmatch=badmatch):
2339 2342 if abs in names or src == 'b':
2340 2343 continue
2341 2344 names[abs] = (rel, exact)
2342 2345 target_only[abs] = True
2343 2346
2344 2347 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2345 2348 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2346 2349
2347 2350 revert = ([], _('reverting %s\n'))
2348 2351 add = ([], _('adding %s\n'))
2349 2352 remove = ([], _('removing %s\n'))
2350 2353 forget = ([], _('forgetting %s\n'))
2351 2354 undelete = ([], _('undeleting %s\n'))
2352 2355 update = {}
2353 2356
2354 2357 disptable = (
2355 2358 # dispatch table:
2356 2359 # file state
2357 2360 # action if in target manifest
2358 2361 # action if not in target manifest
2359 2362 # make backup if in target manifest
2360 2363 # make backup if not in target manifest
2361 2364 (modified, revert, remove, True, True),
2362 2365 (added, revert, forget, True, False),
2363 2366 (removed, undelete, None, False, False),
2364 2367 (deleted, revert, remove, False, False),
2365 2368 (unknown, add, None, True, False),
2366 2369 (target_only, add, None, False, False),
2367 2370 )
2368 2371
2369 2372 entries = names.items()
2370 2373 entries.sort()
2371 2374
2372 2375 for abs, (rel, exact) in entries:
2373 2376 mfentry = mf.get(abs)
2374 2377 target = repo.wjoin(abs)
2375 2378 def handle(xlist, dobackup):
2376 2379 xlist[0].append(abs)
2377 2380 update[abs] = 1
2378 2381 if dobackup and not opts['no_backup'] and util.lexists(target):
2379 2382 bakname = "%s.orig" % rel
2380 2383 ui.note(_('saving current version of %s as %s\n') %
2381 2384 (rel, bakname))
2382 2385 if not opts.get('dry_run'):
2383 2386 util.copyfile(target, bakname)
2384 2387 if ui.verbose or not exact:
2385 2388 ui.status(xlist[1] % rel)
2386 2389 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2387 2390 if abs not in table: continue
2388 2391 # file has changed in dirstate
2389 2392 if mfentry:
2390 2393 handle(hitlist, backuphit)
2391 2394 elif misslist is not None:
2392 2395 handle(misslist, backupmiss)
2393 2396 else:
2394 2397 if exact: ui.warn(_('file not managed: %s\n') % rel)
2395 2398 break
2396 2399 else:
2397 2400 # file has not changed in dirstate
2398 2401 if node == parent:
2399 2402 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2400 2403 continue
2401 2404 if pmf is None:
2402 2405 # only need parent manifest in this unlikely case,
2403 2406 # so do not read by default
2404 2407 pmf = repo.changectx(parent).manifest()
2405 2408 if abs in pmf:
2406 2409 if mfentry:
2407 2410 # if version of file is same in parent and target
2408 2411 # manifests, do nothing
2409 2412 if pmf[abs] != mfentry:
2410 2413 handle(revert, False)
2411 2414 else:
2412 2415 handle(remove, False)
2413 2416
2414 2417 if not opts.get('dry_run'):
2415 2418 repo.dirstate.forget(forget[0])
2416 2419 r = hg.revert(repo, node, update.has_key, wlock)
2417 2420 repo.dirstate.update(add[0], 'a')
2418 2421 repo.dirstate.update(undelete[0], 'n')
2419 2422 repo.dirstate.update(remove[0], 'r')
2420 2423 return r
2421 2424
2422 2425 def rollback(ui, repo):
2423 2426 """roll back the last transaction in this repository
2424 2427
2425 2428 Roll back the last transaction in this repository, restoring the
2426 2429 project to its state prior to the transaction.
2427 2430
2428 2431 Transactions are used to encapsulate the effects of all commands
2429 2432 that create new changesets or propagate existing changesets into a
2430 2433 repository. For example, the following commands are transactional,
2431 2434 and their effects can be rolled back:
2432 2435
2433 2436 commit
2434 2437 import
2435 2438 pull
2436 2439 push (with this repository as destination)
2437 2440 unbundle
2438 2441
2439 2442 This command should be used with care. There is only one level of
2440 2443 rollback, and there is no way to undo a rollback. It will also
2441 2444 restore the dirstate at the time of the last transaction, which
2442 2445 may lose subsequent dirstate changes.
2443 2446
2444 2447 This command is not intended for use on public repositories. Once
2445 2448 changes are visible for pull by other users, rolling a transaction
2446 2449 back locally is ineffective (someone else may already have pulled
2447 2450 the changes). Furthermore, a race is possible with readers of the
2448 2451 repository; for example an in-progress pull from the repository
2449 2452 may fail if a rollback is performed.
2450 2453 """
2451 2454 repo.rollback()
2452 2455
2453 2456 def root(ui, repo):
2454 2457 """print the root (top) of the current working dir
2455 2458
2456 2459 Print the root directory of the current repository.
2457 2460 """
2458 2461 ui.write(repo.root + "\n")
2459 2462
2460 2463 def serve(ui, repo, **opts):
2461 2464 """export the repository via HTTP
2462 2465
2463 2466 Start a local HTTP repository browser and pull server.
2464 2467
2465 2468 By default, the server logs accesses to stdout and errors to
2466 2469 stderr. Use the "-A" and "-E" options to log to files.
2467 2470 """
2468 2471
2469 2472 if opts["stdio"]:
2470 2473 if repo is None:
2471 2474 raise hg.RepoError(_("There is no Mercurial repository here"
2472 2475 " (.hg not found)"))
2473 2476 s = sshserver.sshserver(ui, repo)
2474 2477 s.serve_forever()
2475 2478
2476 2479 parentui = ui.parentui or ui
2477 2480 optlist = ("name templates style address port ipv6"
2478 2481 " accesslog errorlog webdir_conf certificate")
2479 2482 for o in optlist.split():
2480 2483 if opts[o]:
2481 2484 parentui.setconfig("web", o, str(opts[o]))
2482 2485 if repo.ui != parentui:
2483 2486 repo.ui.setconfig("web", o, str(opts[o]))
2484 2487
2485 2488 if repo is None and not ui.config("web", "webdir_conf"):
2486 2489 raise hg.RepoError(_("There is no Mercurial repository here"
2487 2490 " (.hg not found)"))
2488 2491
2489 2492 class service:
2490 2493 def init(self):
2491 2494 util.set_signal_handler()
2492 2495 try:
2493 2496 self.httpd = hgweb.server.create_server(parentui, repo)
2494 2497 except socket.error, inst:
2495 2498 raise util.Abort(_('cannot start server: ') + inst.args[1])
2496 2499
2497 2500 if not ui.verbose: return
2498 2501
2499 2502 if self.httpd.port != 80:
2500 2503 ui.status(_('listening at http://%s:%d/\n') %
2501 2504 (self.httpd.addr, self.httpd.port))
2502 2505 else:
2503 2506 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2504 2507
2505 2508 def run(self):
2506 2509 self.httpd.serve_forever()
2507 2510
2508 2511 service = service()
2509 2512
2510 2513 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2511 2514
2512 2515 def status(ui, repo, *pats, **opts):
2513 2516 """show changed files in the working directory
2514 2517
2515 2518 Show status of files in the repository. If names are given, only
2516 2519 files that match are shown. Files that are clean or ignored, are
2517 2520 not listed unless -c (clean), -i (ignored) or -A is given.
2518 2521
2519 2522 NOTE: status may appear to disagree with diff if permissions have
2520 2523 changed or a merge has occurred. The standard diff format does not
2521 2524 report permission changes and diff only reports changes relative
2522 2525 to one merge parent.
2523 2526
2524 2527 If one revision is given, it is used as the base revision.
2525 2528 If two revisions are given, the difference between them is shown.
2526 2529
2527 2530 The codes used to show the status of files are:
2528 2531 M = modified
2529 2532 A = added
2530 2533 R = removed
2531 2534 C = clean
2532 2535 ! = deleted, but still tracked
2533 2536 ? = not tracked
2534 2537 I = ignored (not shown by default)
2535 2538 = the previous added file was copied from here
2536 2539 """
2537 2540
2538 2541 all = opts['all']
2539 2542 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2540 2543
2541 2544 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2542 2545 cwd = (pats and repo.getcwd()) or ''
2543 2546 modified, added, removed, deleted, unknown, ignored, clean = [
2544 2547 n for n in repo.status(node1=node1, node2=node2, files=files,
2545 2548 match=matchfn,
2546 2549 list_ignored=all or opts['ignored'],
2547 2550 list_clean=all or opts['clean'])]
2548 2551
2549 2552 changetypes = (('modified', 'M', modified),
2550 2553 ('added', 'A', added),
2551 2554 ('removed', 'R', removed),
2552 2555 ('deleted', '!', deleted),
2553 2556 ('unknown', '?', unknown),
2554 2557 ('ignored', 'I', ignored))
2555 2558
2556 2559 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2557 2560
2558 2561 end = opts['print0'] and '\0' or '\n'
2559 2562
2560 2563 for opt, char, changes in ([ct for ct in explicit_changetypes
2561 2564 if all or opts[ct[0]]]
2562 2565 or changetypes):
2563 2566 if opts['no_status']:
2564 2567 format = "%%s%s" % end
2565 2568 else:
2566 2569 format = "%s %%s%s" % (char, end)
2567 2570
2568 2571 for f in changes:
2569 2572 ui.write(format % repo.pathto(f, cwd))
2570 2573 if ((all or opts.get('copies')) and not opts.get('no_status')):
2571 2574 copied = repo.dirstate.copied(f)
2572 2575 if copied:
2573 2576 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2574 2577
2575 2578 def tag(ui, repo, name, rev_=None, **opts):
2576 2579 """add a tag for the current or given revision
2577 2580
2578 2581 Name a particular revision using <name>.
2579 2582
2580 2583 Tags are used to name particular revisions of the repository and are
2581 2584 very useful to compare different revision, to go back to significant
2582 2585 earlier versions or to mark branch points as releases, etc.
2583 2586
2584 2587 If no revision is given, the parent of the working directory is used,
2585 2588 or tip if no revision is checked out.
2586 2589
2587 2590 To facilitate version control, distribution, and merging of tags,
2588 2591 they are stored as a file named ".hgtags" which is managed
2589 2592 similarly to other project files and can be hand-edited if
2590 2593 necessary. The file '.hg/localtags' is used for local tags (not
2591 2594 shared among repositories).
2592 2595 """
2593 2596 if name in ['tip', '.', 'null']:
2594 2597 raise util.Abort(_("the name '%s' is reserved") % name)
2595 2598 if rev_ is not None:
2596 2599 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2597 2600 "please use 'hg tag [-r REV] NAME' instead\n"))
2598 2601 if opts['rev']:
2599 2602 raise util.Abort(_("use only one form to specify the revision"))
2600 2603 if opts['rev'] and opts['remove']:
2601 2604 raise util.Abort(_("--rev and --remove are incompatible"))
2602 2605 if opts['rev']:
2603 2606 rev_ = opts['rev']
2604 2607 message = opts['message']
2605 2608 if opts['remove']:
2606 2609 if not name in repo.tags():
2607 2610 raise util.Abort(_('tag %s does not exist') % name)
2608 2611 rev_ = nullid
2609 2612 if not message:
2610 2613 message = _('Removed tag %s') % name
2611 2614 elif name in repo.tags() and not opts['force']:
2612 2615 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2613 2616 % name)
2614 2617 if not rev_ and repo.dirstate.parents()[1] != nullid:
2615 2618 raise util.Abort(_('uncommitted merge - please provide a '
2616 2619 'specific revision'))
2617 2620 r = repo.changectx(rev_).node()
2618 2621
2619 2622 if not message:
2620 2623 message = _('Added tag %s for changeset %s') % (name, short(r))
2621 2624
2622 2625 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2623 2626
2624 2627 def tags(ui, repo):
2625 2628 """list repository tags
2626 2629
2627 2630 List the repository tags.
2628 2631
2629 2632 This lists both regular and local tags.
2630 2633 """
2631 2634
2632 2635 l = repo.tagslist()
2633 2636 l.reverse()
2634 2637 hexfunc = ui.debugflag and hex or short
2635 2638 for t, n in l:
2636 2639 try:
2637 2640 hn = hexfunc(n)
2638 2641 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2639 2642 except revlog.LookupError:
2640 2643 r = " ?:%s" % hn
2641 2644 if ui.quiet:
2642 2645 ui.write("%s\n" % t)
2643 2646 else:
2644 2647 spaces = " " * (30 - util.locallen(t))
2645 2648 ui.write("%s%s %s\n" % (t, spaces, r))
2646 2649
2647 2650 def tip(ui, repo, **opts):
2648 2651 """show the tip revision
2649 2652
2650 2653 Show the tip revision.
2651 2654 """
2652 2655 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2653 2656
2654 2657 def unbundle(ui, repo, fname1, *fnames, **opts):
2655 2658 """apply one or more changegroup files
2656 2659
2657 2660 Apply one or more compressed changegroup files generated by the
2658 2661 bundle command.
2659 2662 """
2660 2663 fnames = (fname1,) + fnames
2661 2664 result = None
2662 2665 wasempty = repo.changelog.count() == 0
2663 2666 for fname in fnames:
2664 2667 if os.path.exists(fname):
2665 2668 f = open(fname, "rb")
2666 2669 else:
2667 2670 f = urllib.urlopen(fname)
2668 2671 gen = changegroup.readbundle(f, fname)
2669 2672 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2670 2673
2671 2674 return postincoming(ui, repo, modheads, opts['update'], wasempty)
2672 2675
2673 2676 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2674 2677 """update working directory
2675 2678
2676 2679 Update the working directory to the specified revision, or the
2677 2680 tip of the current branch if none is specified.
2678 2681
2679 2682 If there are no outstanding changes in the working directory and
2680 2683 there is a linear relationship between the current version and the
2681 2684 requested version, the result is the requested version.
2682 2685
2683 2686 To merge the working directory with another revision, use the
2684 2687 merge command.
2685 2688
2686 2689 By default, update will refuse to run if doing so would require
2687 2690 discarding local changes.
2688 2691 """
2689 2692 if rev and node:
2690 2693 raise util.Abort(_("please specify just one revision"))
2691 2694
2692 2695 if not rev:
2693 2696 rev = node
2694 2697
2695 2698 if date:
2696 2699 if rev:
2697 2700 raise util.Abort(_("you can't specify a revision and a date"))
2698 2701 rev = cmdutil.finddate(ui, repo, date)
2699 2702
2700 2703 if clean:
2701 2704 return hg.clean(repo, rev)
2702 2705 else:
2703 2706 return hg.update(repo, rev)
2704 2707
2705 2708 def verify(ui, repo):
2706 2709 """verify the integrity of the repository
2707 2710
2708 2711 Verify the integrity of the current repository.
2709 2712
2710 2713 This will perform an extensive check of the repository's
2711 2714 integrity, validating the hashes and checksums of each entry in
2712 2715 the changelog, manifest, and tracked files, as well as the
2713 2716 integrity of their crosslinks and indices.
2714 2717 """
2715 2718 return hg.verify(repo)
2716 2719
2717 2720 def version_(ui):
2718 2721 """output version and copyright information"""
2719 2722 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2720 2723 % version.get_version())
2721 2724 ui.status(_(
2722 2725 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2723 2726 "This is free software; see the source for copying conditions. "
2724 2727 "There is NO\nwarranty; "
2725 2728 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2726 2729 ))
2727 2730
2728 2731 # Command options and aliases are listed here, alphabetically
2729 2732
2730 2733 globalopts = [
2731 2734 ('R', 'repository', '',
2732 2735 _('repository root directory or symbolic path name')),
2733 2736 ('', 'cwd', '', _('change working directory')),
2734 2737 ('y', 'noninteractive', None,
2735 2738 _('do not prompt, assume \'yes\' for any required answers')),
2736 2739 ('q', 'quiet', None, _('suppress output')),
2737 2740 ('v', 'verbose', None, _('enable additional output')),
2738 2741 ('', 'config', [], _('set/override config option')),
2739 2742 ('', 'debug', None, _('enable debugging output')),
2740 2743 ('', 'debugger', None, _('start debugger')),
2741 2744 ('', 'encoding', util._encoding, _('set the charset encoding')),
2742 2745 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2743 2746 ('', 'lsprof', None, _('print improved command execution profile')),
2744 2747 ('', 'traceback', None, _('print traceback on exception')),
2745 2748 ('', 'time', None, _('time how long the command takes')),
2746 2749 ('', 'profile', None, _('print command execution profile')),
2747 2750 ('', 'version', None, _('output version information and exit')),
2748 2751 ('h', 'help', None, _('display help and exit')),
2749 2752 ]
2750 2753
2751 2754 dryrunopts = [('n', 'dry-run', None,
2752 2755 _('do not perform actions, just print output'))]
2753 2756
2754 2757 remoteopts = [
2755 2758 ('e', 'ssh', '', _('specify ssh command to use')),
2756 2759 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2757 2760 ]
2758 2761
2759 2762 walkopts = [
2760 2763 ('I', 'include', [], _('include names matching the given patterns')),
2761 2764 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2762 2765 ]
2763 2766
2764 2767 commitopts = [
2765 2768 ('m', 'message', '', _('use <text> as commit message')),
2766 2769 ('l', 'logfile', '', _('read commit message from <file>')),
2767 2770 ]
2768 2771
2769 2772 table = {
2770 2773 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2771 2774 "addremove":
2772 2775 (addremove,
2773 2776 [('s', 'similarity', '',
2774 2777 _('guess renamed files by similarity (0<=s<=100)')),
2775 2778 ] + walkopts + dryrunopts,
2776 2779 _('hg addremove [OPTION]... [FILE]...')),
2777 2780 "^annotate":
2778 2781 (annotate,
2779 2782 [('r', 'rev', '', _('annotate the specified revision')),
2780 2783 ('f', 'follow', None, _('follow file copies and renames')),
2781 2784 ('a', 'text', None, _('treat all files as text')),
2782 2785 ('u', 'user', None, _('list the author')),
2783 2786 ('d', 'date', None, _('list the date')),
2784 2787 ('n', 'number', None, _('list the revision number (default)')),
2785 2788 ('c', 'changeset', None, _('list the changeset')),
2786 2789 ('l', 'line-number', None,
2787 2790 _('show line number at the first appearance'))
2788 2791 ] + walkopts,
2789 2792 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2790 2793 "archive":
2791 2794 (archive,
2792 2795 [('', 'no-decode', None, _('do not pass files through decoders')),
2793 2796 ('p', 'prefix', '', _('directory prefix for files in archive')),
2794 2797 ('r', 'rev', '', _('revision to distribute')),
2795 2798 ('t', 'type', '', _('type of distribution to create')),
2796 2799 ] + walkopts,
2797 2800 _('hg archive [OPTION]... DEST')),
2798 2801 "backout":
2799 2802 (backout,
2800 2803 [('', 'merge', None,
2801 2804 _('merge with old dirstate parent after backout')),
2802 2805 ('d', 'date', '', _('record datecode as commit date')),
2803 2806 ('', 'parent', '', _('parent to choose when backing out merge')),
2804 2807 ('u', 'user', '', _('record user as committer')),
2805 2808 ('r', 'rev', '', _('revision to backout')),
2806 2809 ] + walkopts + commitopts,
2807 2810 _('hg backout [OPTION]... [-r] REV')),
2808 2811 "branch":
2809 2812 (branch,
2810 2813 [('f', 'force', None,
2811 2814 _('set branch name even if it shadows an existing branch'))],
2812 2815 _('hg branch [NAME]')),
2813 2816 "branches":
2814 2817 (branches,
2815 2818 [('a', 'active', False,
2816 2819 _('show only branches that have unmerged heads'))],
2817 2820 _('hg branches [-a]')),
2818 2821 "bundle":
2819 2822 (bundle,
2820 2823 [('f', 'force', None,
2821 2824 _('run even when remote repository is unrelated')),
2822 2825 ('r', 'rev', [],
2823 2826 _('a changeset you would like to bundle')),
2824 2827 ('', 'base', [],
2825 2828 _('a base changeset to specify instead of a destination')),
2826 2829 ] + remoteopts,
2827 2830 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2828 2831 "cat":
2829 2832 (cat,
2830 2833 [('o', 'output', '', _('print output to file with formatted name')),
2831 2834 ('r', 'rev', '', _('print the given revision')),
2832 2835 ] + walkopts,
2833 2836 _('hg cat [OPTION]... FILE...')),
2834 2837 "^clone":
2835 2838 (clone,
2836 2839 [('U', 'noupdate', None, _('do not update the new working directory')),
2837 2840 ('r', 'rev', [],
2838 2841 _('a changeset you would like to have after cloning')),
2839 2842 ('', 'pull', None, _('use pull protocol to copy metadata')),
2840 2843 ('', 'uncompressed', None,
2841 2844 _('use uncompressed transfer (fast over LAN)')),
2842 2845 ] + remoteopts,
2843 2846 _('hg clone [OPTION]... SOURCE [DEST]')),
2844 2847 "^commit|ci":
2845 2848 (commit,
2846 2849 [('A', 'addremove', None,
2847 2850 _('mark new/missing files as added/removed before committing')),
2848 2851 ('d', 'date', '', _('record datecode as commit date')),
2849 2852 ('u', 'user', '', _('record user as commiter')),
2850 2853 ] + walkopts + commitopts,
2851 2854 _('hg commit [OPTION]... [FILE]...')),
2852 2855 "copy|cp":
2853 2856 (copy,
2854 2857 [('A', 'after', None, _('record a copy that has already occurred')),
2855 2858 ('f', 'force', None,
2856 2859 _('forcibly copy over an existing managed file')),
2857 2860 ] + walkopts + dryrunopts,
2858 2861 _('hg copy [OPTION]... [SOURCE]... DEST')),
2859 2862 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2860 2863 "debugcomplete":
2861 2864 (debugcomplete,
2862 2865 [('o', 'options', None, _('show the command options'))],
2863 2866 _('debugcomplete [-o] CMD')),
2864 2867 "debuginstall": (debuginstall, [], _('debuginstall')),
2865 2868 "debugrebuildstate":
2866 2869 (debugrebuildstate,
2867 2870 [('r', 'rev', '', _('revision to rebuild to'))],
2868 2871 _('debugrebuildstate [-r REV] [REV]')),
2869 2872 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2870 2873 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2871 2874 "debugstate": (debugstate, [], _('debugstate')),
2872 2875 "debugdate":
2873 2876 (debugdate,
2874 2877 [('e', 'extended', None, _('try extended date formats'))],
2875 2878 _('debugdate [-e] DATE [RANGE]')),
2876 2879 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2877 2880 "debugindex": (debugindex, [], _('debugindex FILE')),
2878 2881 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2879 2882 "debugrename":
2880 2883 (debugrename,
2881 2884 [('r', 'rev', '', _('revision to debug'))],
2882 2885 _('debugrename [-r REV] FILE')),
2883 2886 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2884 2887 "^diff":
2885 2888 (diff,
2886 2889 [('r', 'rev', [], _('revision')),
2887 2890 ('a', 'text', None, _('treat all files as text')),
2888 2891 ('p', 'show-function', None,
2889 2892 _('show which function each change is in')),
2890 2893 ('g', 'git', None, _('use git extended diff format')),
2891 2894 ('', 'nodates', None, _("don't include dates in diff headers")),
2892 2895 ('w', 'ignore-all-space', None,
2893 2896 _('ignore white space when comparing lines')),
2894 2897 ('b', 'ignore-space-change', None,
2895 2898 _('ignore changes in the amount of white space')),
2896 2899 ('B', 'ignore-blank-lines', None,
2897 2900 _('ignore changes whose lines are all blank')),
2898 2901 ] + walkopts,
2899 2902 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2900 2903 "^export":
2901 2904 (export,
2902 2905 [('o', 'output', '', _('print output to file with formatted name')),
2903 2906 ('a', 'text', None, _('treat all files as text')),
2904 2907 ('g', 'git', None, _('use git extended diff format')),
2905 2908 ('', 'nodates', None, _("don't include dates in diff headers")),
2906 2909 ('', 'switch-parent', None, _('diff against the second parent'))],
2907 2910 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2908 2911 "grep":
2909 2912 (grep,
2910 2913 [('0', 'print0', None, _('end fields with NUL')),
2911 2914 ('', 'all', None, _('print all revisions that match')),
2912 2915 ('f', 'follow', None,
2913 2916 _('follow changeset history, or file history across copies and renames')),
2914 2917 ('i', 'ignore-case', None, _('ignore case when matching')),
2915 2918 ('l', 'files-with-matches', None,
2916 2919 _('print only filenames and revs that match')),
2917 2920 ('n', 'line-number', None, _('print matching line numbers')),
2918 2921 ('r', 'rev', [], _('search in given revision range')),
2919 2922 ('u', 'user', None, _('print user who committed change')),
2920 2923 ] + walkopts,
2921 2924 _('hg grep [OPTION]... PATTERN [FILE]...')),
2922 2925 "heads":
2923 2926 (heads,
2924 2927 [('', 'style', '', _('display using template map file')),
2925 2928 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2926 2929 ('', 'template', '', _('display with template'))],
2927 2930 _('hg heads [-r REV] [REV]...')),
2928 2931 "help": (help_, [], _('hg help [COMMAND]')),
2929 2932 "identify|id":
2930 2933 (identify,
2931 2934 [('r', 'rev', '', _('identify the specified rev')),
2932 2935 ('n', 'num', None, _('show local revision number')),
2933 2936 ('i', 'id', None, _('show global revision id')),
2934 2937 ('b', 'branch', None, _('show branch')),
2935 2938 ('t', 'tags', None, _('show tags'))],
2936 2939 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2937 2940 "import|patch":
2938 2941 (import_,
2939 2942 [('p', 'strip', 1,
2940 2943 _('directory strip option for patch. This has the same\n'
2941 2944 'meaning as the corresponding patch option')),
2942 2945 ('b', 'base', '', _('base path')),
2943 2946 ('f', 'force', None,
2944 2947 _('skip check for outstanding uncommitted changes')),
2945 2948 ('', 'exact', None,
2946 2949 _('apply patch to the nodes from which it was generated')),
2947 2950 ('', 'import-branch', None,
2948 2951 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2949 2952 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2950 2953 "incoming|in": (incoming,
2951 2954 [('M', 'no-merges', None, _('do not show merges')),
2952 2955 ('f', 'force', None,
2953 2956 _('run even when remote repository is unrelated')),
2954 2957 ('', 'style', '', _('display using template map file')),
2955 2958 ('n', 'newest-first', None, _('show newest record first')),
2956 2959 ('', 'bundle', '', _('file to store the bundles into')),
2957 2960 ('p', 'patch', None, _('show patch')),
2958 2961 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2959 2962 ('', 'template', '', _('display with template')),
2960 2963 ] + remoteopts,
2961 2964 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2962 2965 ' [--bundle FILENAME] [SOURCE]')),
2963 2966 "^init":
2964 2967 (init,
2965 2968 remoteopts,
2966 2969 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2967 2970 "locate":
2968 2971 (locate,
2969 2972 [('r', 'rev', '', _('search the repository as it stood at rev')),
2970 2973 ('0', 'print0', None,
2971 2974 _('end filenames with NUL, for use with xargs')),
2972 2975 ('f', 'fullpath', None,
2973 2976 _('print complete paths from the filesystem root')),
2974 2977 ] + walkopts,
2975 2978 _('hg locate [OPTION]... [PATTERN]...')),
2976 2979 "^log|history":
2977 2980 (log,
2978 2981 [('f', 'follow', None,
2979 2982 _('follow changeset history, or file history across copies and renames')),
2980 2983 ('', 'follow-first', None,
2981 2984 _('only follow the first parent of merge changesets')),
2982 2985 ('d', 'date', '', _('show revs matching date spec')),
2983 2986 ('C', 'copies', None, _('show copied files')),
2984 2987 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2985 2988 ('l', 'limit', '', _('limit number of changes displayed')),
2986 2989 ('r', 'rev', [], _('show the specified revision or range')),
2987 2990 ('', 'removed', None, _('include revs where files were removed')),
2988 2991 ('M', 'no-merges', None, _('do not show merges')),
2989 2992 ('', 'style', '', _('display using template map file')),
2990 2993 ('m', 'only-merges', None, _('show only merges')),
2991 2994 ('p', 'patch', None, _('show patch')),
2992 2995 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2993 2996 ('', 'template', '', _('display with template')),
2994 2997 ] + walkopts,
2995 2998 _('hg log [OPTION]... [FILE]')),
2996 2999 "manifest": (manifest, [], _('hg manifest [REV]')),
2997 3000 "^merge":
2998 3001 (merge,
2999 3002 [('f', 'force', None, _('force a merge with outstanding changes')),
3000 3003 ('r', 'rev', '', _('revision to merge')),
3001 3004 ],
3002 3005 _('hg merge [-f] [[-r] REV]')),
3003 3006 "outgoing|out": (outgoing,
3004 3007 [('M', 'no-merges', None, _('do not show merges')),
3005 3008 ('f', 'force', None,
3006 3009 _('run even when remote repository is unrelated')),
3007 3010 ('p', 'patch', None, _('show patch')),
3008 3011 ('', 'style', '', _('display using template map file')),
3009 3012 ('r', 'rev', [], _('a specific revision you would like to push')),
3010 3013 ('n', 'newest-first', None, _('show newest record first')),
3011 3014 ('', 'template', '', _('display with template')),
3012 3015 ] + remoteopts,
3013 3016 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3014 3017 "^parents":
3015 3018 (parents,
3016 3019 [('r', 'rev', '', _('show parents from the specified rev')),
3017 3020 ('', 'style', '', _('display using template map file')),
3018 3021 ('', 'template', '', _('display with template'))],
3019 3022 _('hg parents [-r REV] [FILE]')),
3020 3023 "paths": (paths, [], _('hg paths [NAME]')),
3021 3024 "^pull":
3022 3025 (pull,
3023 3026 [('u', 'update', None,
3024 3027 _('update to new tip if changesets were pulled')),
3025 3028 ('f', 'force', None,
3026 3029 _('run even when remote repository is unrelated')),
3027 3030 ('r', 'rev', [],
3028 3031 _('a specific revision up to which you would like to pull')),
3029 3032 ] + remoteopts,
3030 3033 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3031 3034 "^push":
3032 3035 (push,
3033 3036 [('f', 'force', None, _('force push')),
3034 3037 ('r', 'rev', [], _('a specific revision you would like to push')),
3035 3038 ] + remoteopts,
3036 3039 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3037 3040 "debugrawcommit|rawcommit":
3038 3041 (rawcommit,
3039 3042 [('p', 'parent', [], _('parent')),
3040 3043 ('d', 'date', '', _('date code')),
3041 3044 ('u', 'user', '', _('user')),
3042 3045 ('F', 'files', '', _('file list'))
3043 3046 ] + commitopts,
3044 3047 _('hg debugrawcommit [OPTION]... [FILE]...')),
3045 3048 "recover": (recover, [], _('hg recover')),
3046 3049 "^remove|rm":
3047 3050 (remove,
3048 3051 [('A', 'after', None, _('record remove that has already occurred')),
3049 3052 ('f', 'force', None, _('remove file even if modified')),
3050 3053 ] + walkopts,
3051 3054 _('hg remove [OPTION]... FILE...')),
3052 3055 "rename|mv":
3053 3056 (rename,
3054 3057 [('A', 'after', None, _('record a rename that has already occurred')),
3055 3058 ('f', 'force', None,
3056 3059 _('forcibly copy over an existing managed file')),
3057 3060 ] + walkopts + dryrunopts,
3058 3061 _('hg rename [OPTION]... SOURCE... DEST')),
3059 3062 "^revert":
3060 3063 (revert,
3061 3064 [('a', 'all', None, _('revert all changes when no arguments given')),
3062 3065 ('d', 'date', '', _('tipmost revision matching date')),
3063 3066 ('r', 'rev', '', _('revision to revert to')),
3064 3067 ('', 'no-backup', None, _('do not save backup copies of files')),
3065 3068 ] + walkopts + dryrunopts,
3066 3069 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3067 3070 "rollback": (rollback, [], _('hg rollback')),
3068 3071 "root": (root, [], _('hg root')),
3069 3072 "showconfig|debugconfig":
3070 3073 (showconfig,
3071 3074 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3072 3075 _('showconfig [-u] [NAME]...')),
3073 3076 "^serve":
3074 3077 (serve,
3075 3078 [('A', 'accesslog', '', _('name of access log file to write to')),
3076 3079 ('d', 'daemon', None, _('run server in background')),
3077 3080 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3078 3081 ('E', 'errorlog', '', _('name of error log file to write to')),
3079 3082 ('p', 'port', 0, _('port to use (default: 8000)')),
3080 3083 ('a', 'address', '', _('address to use')),
3081 3084 ('n', 'name', '',
3082 3085 _('name to show in web pages (default: working dir)')),
3083 3086 ('', 'webdir-conf', '', _('name of the webdir config file'
3084 3087 ' (serve more than one repo)')),
3085 3088 ('', 'pid-file', '', _('name of file to write process ID to')),
3086 3089 ('', 'stdio', None, _('for remote clients')),
3087 3090 ('t', 'templates', '', _('web templates to use')),
3088 3091 ('', 'style', '', _('template style to use')),
3089 3092 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3090 3093 ('', 'certificate', '', _('SSL certificate file'))],
3091 3094 _('hg serve [OPTION]...')),
3092 3095 "^status|st":
3093 3096 (status,
3094 3097 [('A', 'all', None, _('show status of all files')),
3095 3098 ('m', 'modified', None, _('show only modified files')),
3096 3099 ('a', 'added', None, _('show only added files')),
3097 3100 ('r', 'removed', None, _('show only removed files')),
3098 3101 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3099 3102 ('c', 'clean', None, _('show only files without changes')),
3100 3103 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3101 3104 ('i', 'ignored', None, _('show only ignored files')),
3102 3105 ('n', 'no-status', None, _('hide status prefix')),
3103 3106 ('C', 'copies', None, _('show source of copied files')),
3104 3107 ('0', 'print0', None,
3105 3108 _('end filenames with NUL, for use with xargs')),
3106 3109 ('', 'rev', [], _('show difference from revision')),
3107 3110 ] + walkopts,
3108 3111 _('hg status [OPTION]... [FILE]...')),
3109 3112 "tag":
3110 3113 (tag,
3111 3114 [('f', 'force', None, _('replace existing tag')),
3112 3115 ('l', 'local', None, _('make the tag local')),
3113 3116 ('m', 'message', '', _('message for tag commit log entry')),
3114 3117 ('d', 'date', '', _('record datecode as commit date')),
3115 3118 ('u', 'user', '', _('record user as commiter')),
3116 3119 ('r', 'rev', '', _('revision to tag')),
3117 3120 ('', 'remove', None, _('remove a tag'))],
3118 3121 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3119 3122 "tags": (tags, [], _('hg tags')),
3120 3123 "tip":
3121 3124 (tip,
3122 3125 [('', 'style', '', _('display using template map file')),
3123 3126 ('p', 'patch', None, _('show patch')),
3124 3127 ('', 'template', '', _('display with template'))],
3125 3128 _('hg tip [-p]')),
3126 3129 "unbundle":
3127 3130 (unbundle,
3128 3131 [('u', 'update', None,
3129 3132 _('update to new tip if changesets were unbundled'))],
3130 3133 _('hg unbundle [-u] FILE...')),
3131 3134 "^update|up|checkout|co":
3132 3135 (update,
3133 3136 [('C', 'clean', None, _('overwrite locally modified files')),
3134 3137 ('d', 'date', '', _('tipmost revision matching date')),
3135 3138 ('r', 'rev', '', _('revision'))],
3136 3139 _('hg update [-C] [-d DATE] [[-r] REV]')),
3137 3140 "verify": (verify, [], _('hg verify')),
3138 3141 "version": (version_, [], _('hg version')),
3139 3142 }
3140 3143
3141 3144 extensions.commandtable = table
3142 3145
3143 3146 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3144 3147 " debugindex debugindexdot debugdate debuginstall")
3145 3148 optionalrepo = ("paths serve showconfig")
3146 3149
3147 3150 def dispatch(args, argv0=None):
3148 3151 try:
3149 3152 u = ui.ui(traceback='--traceback' in args)
3150 3153 except util.Abort, inst:
3151 3154 sys.stderr.write(_("abort: %s\n") % inst)
3152 3155 return -1
3153 3156 return cmdutil.runcatch(u, args, argv0=argv0)
3154 3157
3155 3158 def run():
3156 3159 sys.exit(dispatch(sys.argv[1:], argv0=sys.argv[0]))
@@ -1,27 +1,44
1 1 #!/bin/sh
2 2 # test parents command
3 3
4 hg init a
5 cd a
4 hg init repo
5 cd repo
6 6 echo % no working directory
7 7 hg parents
8 8
9 9 echo a > a
10 10 echo b > b
11 11 hg ci -Amab -d '0 0'
12 12 echo a >> a
13 13 hg ci -Ama -d '1 0'
14 14 echo b >> b
15 15 hg ci -Amb -d '2 0'
16 16
17 17 echo % hg parents
18 18 hg parents
19 19
20 20 echo % hg parents a
21 21 hg parents a
22 22
23 23 echo % hg parents -r 2
24 24 hg parents -r 2
25 25
26 26 echo % hg parents -r 2 a
27 27 hg parents -r 2 a
28
29 echo % hg parents -r 2 ../a
30 hg parents -r 2 ../a
31
32 echo '% cd dir; hg parents -r 2 ../a'
33 mkdir dir
34 cd dir
35 hg parents -r 2 ../a
36
37 echo '% hg parents -r 2 path:a'
38 hg parents -r 2 path:a
39
40 echo '% hg parents -r 2 glob:a'
41 cd ..
42 hg parents -r 2 glob:a
43
44 true
@@ -1,28 +1,44
1 1 % no working directory
2 2 adding a
3 3 adding b
4 4 % hg parents
5 5 changeset: 2:6cfac479f009
6 6 tag: tip
7 7 user: test
8 8 date: Thu Jan 01 00:00:02 1970 +0000
9 9 summary: b
10 10
11 11 % hg parents a
12 12 changeset: 0:b6a1406d8886
13 13 user: test
14 14 date: Thu Jan 01 00:00:00 1970 +0000
15 15 summary: ab
16 16
17 17 % hg parents -r 2
18 18 changeset: 1:d786049f033a
19 19 user: test
20 20 date: Thu Jan 01 00:00:01 1970 +0000
21 21 summary: a
22 22
23 23 % hg parents -r 2 a
24 24 changeset: 0:b6a1406d8886
25 25 user: test
26 26 date: Thu Jan 01 00:00:00 1970 +0000
27 27 summary: ab
28 28
29 % hg parents -r 2 ../a
30 abort: ../a not under root
31 % cd dir; hg parents -r 2 ../a
32 changeset: 0:b6a1406d8886
33 user: test
34 date: Thu Jan 01 00:00:00 1970 +0000
35 summary: ab
36
37 % hg parents -r 2 path:a
38 changeset: 0:b6a1406d8886
39 user: test
40 date: Thu Jan 01 00:00:00 1970 +0000
41 summary: ab
42
43 % hg parents -r 2 glob:a
44 abort: can only specify an explicit file name
General Comments 0
You need to be logged in to leave comments. Login now