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