##// END OF EJS Templates
merge git patch code.
Vadim Gelfer -
r2865:71e78f2c merge default
parent child Browse files
Show More
@@ -0,0 +1,166 b''
1 # patch.py - patch file parsing routines
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from demandload import demandload
9 demandload(globals(), "util")
10 demandload(globals(), "os re shutil tempfile")
11
12 def readgitpatch(patchname):
13 """extract git-style metadata about patches from <patchname>"""
14 class gitpatch:
15 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
16 def __init__(self, path):
17 self.path = path
18 self.oldpath = None
19 self.mode = None
20 self.op = 'MODIFY'
21 self.copymod = False
22 self.lineno = 0
23
24 # Filter patch for git information
25 gitre = re.compile('diff --git a/(.*) b/(.*)')
26 pf = file(patchname)
27 gp = None
28 gitpatches = []
29 # Can have a git patch with only metadata, causing patch to complain
30 dopatch = False
31
32 lineno = 0
33 for line in pf:
34 lineno += 1
35 if line.startswith('diff --git'):
36 m = gitre.match(line)
37 if m:
38 if gp:
39 gitpatches.append(gp)
40 src, dst = m.group(1,2)
41 gp = gitpatch(dst)
42 gp.lineno = lineno
43 elif gp:
44 if line.startswith('--- '):
45 if gp.op in ('COPY', 'RENAME'):
46 gp.copymod = True
47 dopatch = 'filter'
48 gitpatches.append(gp)
49 gp = None
50 if not dopatch:
51 dopatch = True
52 continue
53 if line.startswith('rename from '):
54 gp.op = 'RENAME'
55 gp.oldpath = line[12:].rstrip()
56 elif line.startswith('rename to '):
57 gp.path = line[10:].rstrip()
58 elif line.startswith('copy from '):
59 gp.op = 'COPY'
60 gp.oldpath = line[10:].rstrip()
61 elif line.startswith('copy to '):
62 gp.path = line[8:].rstrip()
63 elif line.startswith('deleted file'):
64 gp.op = 'DELETE'
65 elif line.startswith('new file mode '):
66 gp.op = 'ADD'
67 gp.mode = int(line.rstrip()[-3:], 8)
68 elif line.startswith('new mode '):
69 gp.mode = int(line.rstrip()[-3:], 8)
70 if gp:
71 gitpatches.append(gp)
72
73 if not gitpatches:
74 dopatch = True
75
76 return (dopatch, gitpatches)
77
78 def dogitpatch(patchname, gitpatches):
79 """Preprocess git patch so that vanilla patch can handle it"""
80 pf = file(patchname)
81 pfline = 1
82
83 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
84 tmpfp = os.fdopen(fd, 'w')
85
86 try:
87 for i in range(len(gitpatches)):
88 p = gitpatches[i]
89 if not p.copymod:
90 continue
91
92 if os.path.exists(p.path):
93 raise util.Abort(_("cannot create %s: destination already exists") %
94 p.path)
95
96 (src, dst) = [os.path.join(os.getcwd(), n)
97 for n in (p.oldpath, p.path)]
98
99 targetdir = os.path.dirname(dst)
100 if not os.path.isdir(targetdir):
101 os.makedirs(targetdir)
102 try:
103 shutil.copyfile(src, dst)
104 shutil.copymode(src, dst)
105 except shutil.Error, inst:
106 raise util.Abort(str(inst))
107
108 # rewrite patch hunk
109 while pfline < p.lineno:
110 tmpfp.write(pf.readline())
111 pfline += 1
112 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
113 line = pf.readline()
114 pfline += 1
115 while not line.startswith('--- a/'):
116 tmpfp.write(line)
117 line = pf.readline()
118 pfline += 1
119 tmpfp.write('--- a/%s\n' % p.path)
120
121 line = pf.readline()
122 while line:
123 tmpfp.write(line)
124 line = pf.readline()
125 except:
126 tmpfp.close()
127 os.unlink(patchname)
128 raise
129
130 tmpfp.close()
131 return patchname
132
133 def patch(strip, patchname, ui, cwd=None):
134 """apply the patch <patchname> to the working directory.
135 a list of patched files is returned"""
136
137 (dopatch, gitpatches) = readgitpatch(patchname)
138
139 files = {}
140 if dopatch:
141 if dopatch == 'filter':
142 patchname = dogitpatch(patchname, gitpatches)
143 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
144 args = []
145 if cwd:
146 args.append('-d %s' % util.shellquote(cwd))
147 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
148 util.shellquote(patchname)))
149
150 if dopatch == 'filter':
151 False and os.unlink(patchname)
152
153 for line in fp:
154 line = line.rstrip()
155 ui.status("%s\n" % line)
156 if line.startswith('patching file '):
157 pf = util.parse_patch_output(line)
158 files.setdefault(pf, (None, None))
159 code = fp.close()
160 if code:
161 raise util.Abort(_("patch command failed: %s") % explain_exit(code)[0])
162
163 for gp in gitpatches:
164 files[gp.path] = (gp.op, gp)
165
166 return files
@@ -0,0 +1,122 b''
1 #!/bin/sh
2
3 hg init a
4 cd a
5
6 echo % new file
7 hg import -mnew - <<EOF
8 diff --git a/new b/new
9 new file mode 100644
10 index 0000000..7898192
11 --- /dev/null
12 +++ b/new
13 @@ -0,0 +1 @@
14 +a
15 EOF
16
17 echo % chmod +x
18 hg import -msetx - <<EOF
19 diff --git a/new b/new
20 old mode 100644
21 new mode 100755
22 EOF
23
24 test -x new || echo failed
25
26 echo % copy
27 hg import -mcopy - <<EOF
28 diff --git a/new b/copy
29 old mode 100755
30 new mode 100644
31 similarity index 100%
32 copy from new
33 copy to copy
34 diff --git a/new b/copyx
35 similarity index 100%
36 copy from new
37 copy to copyx
38 EOF
39
40 test -f copy -a ! -x copy || echo failed
41 test -x copyx || echo failed
42 cat copy
43 hg cat copy
44
45 echo % rename
46 hg import -mrename - <<EOF
47 diff --git a/copy b/rename
48 similarity index 100%
49 rename from copy
50 rename to rename
51 EOF
52
53 hg locate
54
55 echo % delete
56 hg import -mdelete - <<EOF
57 diff --git a/copyx b/copyx
58 deleted file mode 100755
59 index 7898192..0000000
60 --- a/copyx
61 +++ /dev/null
62 @@ -1 +0,0 @@
63 -a
64 EOF
65
66 hg locate
67 test -f copyx && echo failed || true
68
69 echo % regular diff
70 hg import -mregular - <<EOF
71 diff --git a/rename b/rename
72 index 7898192..72e1fe3 100644
73 --- a/rename
74 +++ b/rename
75 @@ -1 +1,5 @@
76 a
77 +a
78 +a
79 +a
80 +a
81 EOF
82
83 echo % copy and modify
84 hg import -mcopymod - <<EOF
85 diff --git a/rename b/copy2
86 similarity index 80%
87 copy from rename
88 copy to copy2
89 index 72e1fe3..b53c148 100644
90 --- a/rename
91 +++ b/copy2
92 @@ -1,5 +1,5 @@
93 a
94 a
95 -a
96 +b
97 a
98 a
99 EOF
100
101 hg cat copy2
102
103 echo % rename and modify
104 hg import -mrenamemod - <<EOF
105 diff --git a/copy2 b/rename2
106 similarity index 80%
107 rename from copy2
108 rename to rename2
109 index b53c148..8f81e29 100644
110 --- a/copy2
111 +++ b/rename2
112 @@ -1,5 +1,5 @@
113 a
114 a
115 b
116 -a
117 +c
118 a
119 EOF
120
121 hg locate copy2
122 hg cat rename2
@@ -0,0 +1,39 b''
1 % new file
2 applying patch from stdin
3 patching file new
4 % chmod +x
5 applying patch from stdin
6 % copy
7 applying patch from stdin
8 a
9 a
10 % rename
11 applying patch from stdin
12 copyx
13 new
14 rename
15 % delete
16 applying patch from stdin
17 patching file copyx
18 new
19 rename
20 % regular diff
21 applying patch from stdin
22 patching file rename
23 % copy and modify
24 applying patch from stdin
25 patching file copy2
26 a
27 a
28 b
29 a
30 a
31 % rename and modify
32 applying patch from stdin
33 patching file rename2
34 copy2: No such file or directory
35 a
36 a
37 b
38 c
39 a
@@ -1,3690 +1,3722 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 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 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch mdiff patch random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 16 demandload(globals(), "hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
20 20 class AmbiguousCommand(Exception):
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 24 modified, added, removed, deleted, unknown = repo.changes()
25 25 if modified or added or removed or deleted:
26 26 raise util.Abort(_("outstanding uncommitted changes"))
27 27
28 28 def filterfiles(filters, files):
29 29 l = [x for x in files if x in filters]
30 30
31 31 for t in filters:
32 32 if t and t[-1] != "/":
33 33 t += "/"
34 34 l += [x for x in files if x.startswith(t)]
35 35 return l
36 36
37 37 def relpath(repo, args):
38 38 cwd = repo.getcwd()
39 39 if cwd:
40 40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 41 return args
42 42
43 43 def logmessage(opts):
44 44 """ get the log message according to -m and -l option """
45 45 message = opts['message']
46 46 logfile = opts['logfile']
47 47
48 48 if message and logfile:
49 49 raise util.Abort(_('options --message and --logfile are mutually '
50 50 'exclusive'))
51 51 if not message and logfile:
52 52 try:
53 53 if logfile == '-':
54 54 message = sys.stdin.read()
55 55 else:
56 56 message = open(logfile).read()
57 57 except IOError, inst:
58 58 raise util.Abort(_("can't read commit message '%s': %s") %
59 59 (logfile, inst.strerror))
60 60 return message
61 61
62 62 def matchpats(repo, pats=[], opts={}, head=''):
63 63 cwd = repo.getcwd()
64 64 if not pats and cwd:
65 65 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
66 66 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
67 67 cwd = ''
68 68 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
69 69 opts.get('exclude'), head)
70 70
71 71 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
72 72 files, matchfn, anypats = matchpats(repo, pats, opts, head)
73 73 exact = dict(zip(files, files))
74 74 def walk():
75 75 for src, fn in repo.walk(node=node, files=files, match=matchfn,
76 76 badmatch=badmatch):
77 77 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
78 78 return files, matchfn, walk()
79 79
80 80 def walk(repo, pats, opts, node=None, head='', badmatch=None):
81 81 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
82 82 for r in results:
83 83 yield r
84 84
85 85 def walkchangerevs(ui, repo, pats, opts):
86 86 '''Iterate over files and the revs they changed in.
87 87
88 88 Callers most commonly need to iterate backwards over the history
89 89 it is interested in. Doing so has awful (quadratic-looking)
90 90 performance, so we use iterators in a "windowed" way.
91 91
92 92 We walk a window of revisions in the desired order. Within the
93 93 window, we first walk forwards to gather data, then in the desired
94 94 order (usually backwards) to display it.
95 95
96 96 This function returns an (iterator, getchange, matchfn) tuple. The
97 97 getchange function returns the changelog entry for a numeric
98 98 revision. The iterator yields 3-tuples. They will be of one of
99 99 the following forms:
100 100
101 101 "window", incrementing, lastrev: stepping through a window,
102 102 positive if walking forwards through revs, last rev in the
103 103 sequence iterated over - use to reset state for the current window
104 104
105 105 "add", rev, fns: out-of-order traversal of the given file names
106 106 fns, which changed during revision rev - use to gather data for
107 107 possible display
108 108
109 109 "iter", rev, None: in-order traversal of the revs earlier iterated
110 110 over with "add" - use to display data'''
111 111
112 112 def increasing_windows(start, end, windowsize=8, sizelimit=512):
113 113 if start < end:
114 114 while start < end:
115 115 yield start, min(windowsize, end-start)
116 116 start += windowsize
117 117 if windowsize < sizelimit:
118 118 windowsize *= 2
119 119 else:
120 120 while start > end:
121 121 yield start, min(windowsize, start-end-1)
122 122 start -= windowsize
123 123 if windowsize < sizelimit:
124 124 windowsize *= 2
125 125
126 126
127 127 files, matchfn, anypats = matchpats(repo, pats, opts)
128 128 follow = opts.get('follow') or opts.get('follow_first')
129 129
130 130 if repo.changelog.count() == 0:
131 131 return [], False, matchfn
132 132
133 133 if follow:
134 134 p = repo.dirstate.parents()[0]
135 135 if p == nullid:
136 136 ui.warn(_('No working directory revision; defaulting to tip\n'))
137 137 start = 'tip'
138 138 else:
139 139 start = repo.changelog.rev(p)
140 140 defrange = '%s:0' % start
141 141 else:
142 142 defrange = 'tip:0'
143 143 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
144 144 wanted = {}
145 145 slowpath = anypats
146 146 fncache = {}
147 147
148 148 chcache = {}
149 149 def getchange(rev):
150 150 ch = chcache.get(rev)
151 151 if ch is None:
152 152 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
153 153 return ch
154 154
155 155 if not slowpath and not files:
156 156 # No files, no patterns. Display all revs.
157 157 wanted = dict(zip(revs, revs))
158 158 copies = []
159 159 if not slowpath:
160 160 # Only files, no patterns. Check the history of each file.
161 161 def filerevgen(filelog, node):
162 162 cl_count = repo.changelog.count()
163 163 if node is None:
164 164 last = filelog.count() - 1
165 165 else:
166 166 last = filelog.rev(node)
167 167 for i, window in increasing_windows(last, -1):
168 168 revs = []
169 169 for j in xrange(i - window, i + 1):
170 170 n = filelog.node(j)
171 171 revs.append((filelog.linkrev(n),
172 172 follow and filelog.renamed(n)))
173 173 revs.reverse()
174 174 for rev in revs:
175 175 # only yield rev for which we have the changelog, it can
176 176 # happen while doing "hg log" during a pull or commit
177 177 if rev[0] < cl_count:
178 178 yield rev
179 179 def iterfiles():
180 180 for filename in files:
181 181 yield filename, None
182 182 for filename_node in copies:
183 183 yield filename_node
184 184 minrev, maxrev = min(revs), max(revs)
185 185 for file_, node in iterfiles():
186 186 filelog = repo.file(file_)
187 187 # A zero count may be a directory or deleted file, so
188 188 # try to find matching entries on the slow path.
189 189 if filelog.count() == 0:
190 190 slowpath = True
191 191 break
192 192 for rev, copied in filerevgen(filelog, node):
193 193 if rev <= maxrev:
194 194 if rev < minrev:
195 195 break
196 196 fncache.setdefault(rev, [])
197 197 fncache[rev].append(file_)
198 198 wanted[rev] = 1
199 199 if follow and copied:
200 200 copies.append(copied)
201 201 if slowpath:
202 202 if follow:
203 203 raise util.Abort(_('can only follow copies/renames for explicit '
204 204 'file names'))
205 205
206 206 # The slow path checks files modified in every changeset.
207 207 def changerevgen():
208 208 for i, window in increasing_windows(repo.changelog.count()-1, -1):
209 209 for j in xrange(i - window, i + 1):
210 210 yield j, getchange(j)[3]
211 211
212 212 for rev, changefiles in changerevgen():
213 213 matches = filter(matchfn, changefiles)
214 214 if matches:
215 215 fncache[rev] = matches
216 216 wanted[rev] = 1
217 217
218 218 def iterate():
219 219 class followfilter:
220 220 def __init__(self, onlyfirst=False):
221 221 self.startrev = -1
222 222 self.roots = []
223 223 self.onlyfirst = onlyfirst
224 224
225 225 def match(self, rev):
226 226 def realparents(rev):
227 227 if self.onlyfirst:
228 228 return repo.changelog.parentrevs(rev)[0:1]
229 229 else:
230 230 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
231 231
232 232 if self.startrev == -1:
233 233 self.startrev = rev
234 234 return True
235 235
236 236 if rev > self.startrev:
237 237 # forward: all descendants
238 238 if not self.roots:
239 239 self.roots.append(self.startrev)
240 240 for parent in realparents(rev):
241 241 if parent in self.roots:
242 242 self.roots.append(rev)
243 243 return True
244 244 else:
245 245 # backwards: all parents
246 246 if not self.roots:
247 247 self.roots.extend(realparents(self.startrev))
248 248 if rev in self.roots:
249 249 self.roots.remove(rev)
250 250 self.roots.extend(realparents(rev))
251 251 return True
252 252
253 253 return False
254 254
255 255 if follow and not files:
256 256 ff = followfilter(onlyfirst=opts.get('follow_first'))
257 257 def want(rev):
258 258 if rev not in wanted:
259 259 return False
260 260 return ff.match(rev)
261 261 else:
262 262 def want(rev):
263 263 return rev in wanted
264 264
265 265 for i, window in increasing_windows(0, len(revs)):
266 266 yield 'window', revs[0] < revs[-1], revs[-1]
267 267 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
268 268 srevs = list(nrevs)
269 269 srevs.sort()
270 270 for rev in srevs:
271 271 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
272 272 yield 'add', rev, fns
273 273 for rev in nrevs:
274 274 yield 'iter', rev, None
275 275 return iterate(), getchange, matchfn
276 276
277 277 revrangesep = ':'
278 278
279 279 def revfix(repo, val, defval):
280 280 '''turn user-level id of changeset into rev number.
281 281 user-level id can be tag, changeset, rev number, or negative rev
282 282 number relative to number of revs (-1 is tip, etc).'''
283 283 if not val:
284 284 return defval
285 285 try:
286 286 num = int(val)
287 287 if str(num) != val:
288 288 raise ValueError
289 289 if num < 0:
290 290 num += repo.changelog.count()
291 291 if num < 0:
292 292 num = 0
293 293 elif num >= repo.changelog.count():
294 294 raise ValueError
295 295 except ValueError:
296 296 try:
297 297 num = repo.changelog.rev(repo.lookup(val))
298 298 except KeyError:
299 299 raise util.Abort(_('invalid revision identifier %s'), val)
300 300 return num
301 301
302 302 def revpair(ui, repo, revs):
303 303 '''return pair of nodes, given list of revisions. second item can
304 304 be None, meaning use working dir.'''
305 305 if not revs:
306 306 return repo.dirstate.parents()[0], None
307 307 end = None
308 308 if len(revs) == 1:
309 309 start = revs[0]
310 310 if revrangesep in start:
311 311 start, end = start.split(revrangesep, 1)
312 312 start = revfix(repo, start, 0)
313 313 end = revfix(repo, end, repo.changelog.count() - 1)
314 314 else:
315 315 start = revfix(repo, start, None)
316 316 elif len(revs) == 2:
317 317 if revrangesep in revs[0] or revrangesep in revs[1]:
318 318 raise util.Abort(_('too many revisions specified'))
319 319 start = revfix(repo, revs[0], None)
320 320 end = revfix(repo, revs[1], None)
321 321 else:
322 322 raise util.Abort(_('too many revisions specified'))
323 323 if end is not None: end = repo.lookup(str(end))
324 324 return repo.lookup(str(start)), end
325 325
326 326 def revrange(ui, repo, revs):
327 327 """Yield revision as strings from a list of revision specifications."""
328 328 seen = {}
329 329 for spec in revs:
330 330 if revrangesep in spec:
331 331 start, end = spec.split(revrangesep, 1)
332 332 start = revfix(repo, start, 0)
333 333 end = revfix(repo, end, repo.changelog.count() - 1)
334 334 step = start > end and -1 or 1
335 335 for rev in xrange(start, end+step, step):
336 336 if rev in seen:
337 337 continue
338 338 seen[rev] = 1
339 339 yield str(rev)
340 340 else:
341 341 rev = revfix(repo, spec, None)
342 342 if rev in seen:
343 343 continue
344 344 seen[rev] = 1
345 345 yield str(rev)
346 346
347 347 def make_filename(repo, pat, node,
348 348 total=None, seqno=None, revwidth=None, pathname=None):
349 349 node_expander = {
350 350 'H': lambda: hex(node),
351 351 'R': lambda: str(repo.changelog.rev(node)),
352 352 'h': lambda: short(node),
353 353 }
354 354 expander = {
355 355 '%': lambda: '%',
356 356 'b': lambda: os.path.basename(repo.root),
357 357 }
358 358
359 359 try:
360 360 if node:
361 361 expander.update(node_expander)
362 362 if node and revwidth is not None:
363 363 expander['r'] = (lambda:
364 364 str(repo.changelog.rev(node)).zfill(revwidth))
365 365 if total is not None:
366 366 expander['N'] = lambda: str(total)
367 367 if seqno is not None:
368 368 expander['n'] = lambda: str(seqno)
369 369 if total is not None and seqno is not None:
370 370 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
371 371 if pathname is not None:
372 372 expander['s'] = lambda: os.path.basename(pathname)
373 373 expander['d'] = lambda: os.path.dirname(pathname) or '.'
374 374 expander['p'] = lambda: pathname
375 375
376 376 newname = []
377 377 patlen = len(pat)
378 378 i = 0
379 379 while i < patlen:
380 380 c = pat[i]
381 381 if c == '%':
382 382 i += 1
383 383 c = pat[i]
384 384 c = expander[c]()
385 385 newname.append(c)
386 386 i += 1
387 387 return ''.join(newname)
388 388 except KeyError, inst:
389 389 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
390 390 inst.args[0])
391 391
392 392 def make_file(repo, pat, node=None,
393 393 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
394 394 if not pat or pat == '-':
395 395 return 'w' in mode and sys.stdout or sys.stdin
396 396 if hasattr(pat, 'write') and 'w' in mode:
397 397 return pat
398 398 if hasattr(pat, 'read') and 'r' in mode:
399 399 return pat
400 400 return open(make_filename(repo, pat, node, total, seqno, revwidth,
401 401 pathname),
402 402 mode)
403 403
404 404 def write_bundle(cg, filename=None, compress=True):
405 405 """Write a bundle file and return its filename.
406 406
407 407 Existing files will not be overwritten.
408 408 If no filename is specified, a temporary file is created.
409 409 bz2 compression can be turned off.
410 410 The bundle file will be deleted in case of errors.
411 411 """
412 412 class nocompress(object):
413 413 def compress(self, x):
414 414 return x
415 415 def flush(self):
416 416 return ""
417 417
418 418 fh = None
419 419 cleanup = None
420 420 try:
421 421 if filename:
422 422 if os.path.exists(filename):
423 423 raise util.Abort(_("file '%s' already exists"), filename)
424 424 fh = open(filename, "wb")
425 425 else:
426 426 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
427 427 fh = os.fdopen(fd, "wb")
428 428 cleanup = filename
429 429
430 430 if compress:
431 431 fh.write("HG10")
432 432 z = bz2.BZ2Compressor(9)
433 433 else:
434 434 fh.write("HG10UN")
435 435 z = nocompress()
436 436 # parse the changegroup data, otherwise we will block
437 437 # in case of sshrepo because we don't know the end of the stream
438 438
439 439 # an empty chunkiter is the end of the changegroup
440 440 empty = False
441 441 while not empty:
442 442 empty = True
443 443 for chunk in changegroup.chunkiter(cg):
444 444 empty = False
445 445 fh.write(z.compress(changegroup.genchunk(chunk)))
446 446 fh.write(z.compress(changegroup.closechunk()))
447 447 fh.write(z.flush())
448 448 cleanup = None
449 449 return filename
450 450 finally:
451 451 if fh is not None:
452 452 fh.close()
453 453 if cleanup is not None:
454 454 os.unlink(cleanup)
455 455
456 456 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
457 457 changes=None, text=False, opts={}):
458 458 if not node1:
459 459 node1 = repo.dirstate.parents()[0]
460 460 # reading the data for node1 early allows it to play nicely
461 461 # with repo.changes and the revlog cache.
462 462 change = repo.changelog.read(node1)
463 463 mmap = repo.manifest.read(change[0])
464 464 date1 = util.datestr(change[2])
465 465
466 466 if not changes:
467 467 changes = repo.changes(node1, node2, files, match=match)
468 468 modified, added, removed, deleted, unknown = changes
469 469 if files:
470 470 modified, added, removed = map(lambda x: filterfiles(files, x),
471 471 (modified, added, removed))
472 472
473 473 if not modified and not added and not removed:
474 474 return
475 475
476 476 if node2:
477 477 change = repo.changelog.read(node2)
478 478 mmap2 = repo.manifest.read(change[0])
479 479 _date2 = util.datestr(change[2])
480 480 def date2(f):
481 481 return _date2
482 482 def read(f):
483 483 return repo.file(f).read(mmap2[f])
484 484 else:
485 485 tz = util.makedate()[1]
486 486 _date2 = util.datestr()
487 487 def date2(f):
488 488 try:
489 489 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
490 490 except OSError, err:
491 491 if err.errno != errno.ENOENT: raise
492 492 return _date2
493 493 def read(f):
494 494 return repo.wread(f)
495 495
496 496 if ui.quiet:
497 497 r = None
498 498 else:
499 499 hexfunc = ui.verbose and hex or short
500 500 r = [hexfunc(node) for node in [node1, node2] if node]
501 501
502 502 diffopts = ui.diffopts()
503 503 showfunc = opts.get('show_function') or diffopts['showfunc']
504 504 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
505 505 ignorewsamount = opts.get('ignore_space_change') or \
506 506 diffopts['ignorewsamount']
507 507 ignoreblanklines = opts.get('ignore_blank_lines') or \
508 508 diffopts['ignoreblanklines']
509 509
510 510 all = modified + added + removed
511 511 all.sort()
512 512 for f in all:
513 513 to = None
514 514 tn = None
515 515 if f in mmap:
516 516 to = repo.file(f).read(mmap[f])
517 517 if f not in removed:
518 518 tn = read(f)
519 519 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
520 520 showfunc=showfunc, ignorews=ignorews,
521 521 ignorewsamount=ignorewsamount,
522 522 ignoreblanklines=ignoreblanklines))
523 523
524 524 def trimuser(ui, name, rev, revcache):
525 525 """trim the name of the user who committed a change"""
526 526 user = revcache.get(rev)
527 527 if user is None:
528 528 user = revcache[rev] = ui.shortuser(name)
529 529 return user
530 530
531 531 class changeset_printer(object):
532 532 '''show changeset information when templating not requested.'''
533 533
534 534 def __init__(self, ui, repo):
535 535 self.ui = ui
536 536 self.repo = repo
537 537
538 538 def show(self, rev=0, changenode=None, brinfo=None):
539 539 '''show a single changeset or file revision'''
540 540 log = self.repo.changelog
541 541 if changenode is None:
542 542 changenode = log.node(rev)
543 543 elif not rev:
544 544 rev = log.rev(changenode)
545 545
546 546 if self.ui.quiet:
547 547 self.ui.write("%d:%s\n" % (rev, short(changenode)))
548 548 return
549 549
550 550 changes = log.read(changenode)
551 551 date = util.datestr(changes[2])
552 552
553 553 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
554 554 for p in log.parents(changenode)
555 555 if self.ui.debugflag or p != nullid]
556 556 if (not self.ui.debugflag and len(parents) == 1 and
557 557 parents[0][0] == rev-1):
558 558 parents = []
559 559
560 560 if self.ui.verbose:
561 561 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
562 562 else:
563 563 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
564 564
565 565 for tag in self.repo.nodetags(changenode):
566 566 self.ui.status(_("tag: %s\n") % tag)
567 567 for parent in parents:
568 568 self.ui.write(_("parent: %d:%s\n") % parent)
569 569
570 570 if brinfo and changenode in brinfo:
571 571 br = brinfo[changenode]
572 572 self.ui.write(_("branch: %s\n") % " ".join(br))
573 573
574 574 self.ui.debug(_("manifest: %d:%s\n") %
575 575 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
576 576 self.ui.status(_("user: %s\n") % changes[1])
577 577 self.ui.status(_("date: %s\n") % date)
578 578
579 579 if self.ui.debugflag:
580 580 files = self.repo.changes(log.parents(changenode)[0], changenode)
581 581 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
582 582 files):
583 583 if value:
584 584 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
585 585 else:
586 586 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
587 587
588 588 description = changes[4].strip()
589 589 if description:
590 590 if self.ui.verbose:
591 591 self.ui.status(_("description:\n"))
592 592 self.ui.status(description)
593 593 self.ui.status("\n\n")
594 594 else:
595 595 self.ui.status(_("summary: %s\n") %
596 596 description.splitlines()[0])
597 597 self.ui.status("\n")
598 598
599 599 def show_changeset(ui, repo, opts):
600 600 '''show one changeset. uses template or regular display. caller
601 601 can pass in 'style' and 'template' options in opts.'''
602 602
603 603 tmpl = opts.get('template')
604 604 if tmpl:
605 605 tmpl = templater.parsestring(tmpl, quoted=False)
606 606 else:
607 607 tmpl = ui.config('ui', 'logtemplate')
608 608 if tmpl: tmpl = templater.parsestring(tmpl)
609 609 mapfile = opts.get('style') or ui.config('ui', 'style')
610 610 if tmpl or mapfile:
611 611 if mapfile:
612 612 if not os.path.isfile(mapfile):
613 613 mapname = templater.templatepath('map-cmdline.' + mapfile)
614 614 if not mapname: mapname = templater.templatepath(mapfile)
615 615 if mapname: mapfile = mapname
616 616 try:
617 617 t = templater.changeset_templater(ui, repo, mapfile)
618 618 except SyntaxError, inst:
619 619 raise util.Abort(inst.args[0])
620 620 if tmpl: t.use_template(tmpl)
621 621 return t
622 622 return changeset_printer(ui, repo)
623 623
624 624 def setremoteconfig(ui, opts):
625 625 "copy remote options to ui tree"
626 626 if opts.get('ssh'):
627 627 ui.setconfig("ui", "ssh", opts['ssh'])
628 628 if opts.get('remotecmd'):
629 629 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
630 630
631 631 def show_version(ui):
632 632 """output version and copyright information"""
633 633 ui.write(_("Mercurial Distributed SCM (version %s)\n")
634 634 % version.get_version())
635 635 ui.status(_(
636 636 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
637 637 "This is free software; see the source for copying conditions. "
638 638 "There is NO\nwarranty; "
639 639 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
640 640 ))
641 641
642 642 def help_(ui, name=None, with_version=False):
643 643 """show help for a command, extension, or list of commands
644 644
645 645 With no arguments, print a list of commands and short help.
646 646
647 647 Given a command name, print help for that command.
648 648
649 649 Given an extension name, print help for that extension, and the
650 650 commands it provides."""
651 651 option_lists = []
652 652
653 653 def helpcmd(name):
654 654 if with_version:
655 655 show_version(ui)
656 656 ui.write('\n')
657 657 aliases, i = findcmd(name)
658 658 # synopsis
659 659 ui.write("%s\n\n" % i[2])
660 660
661 661 # description
662 662 doc = i[0].__doc__
663 663 if not doc:
664 664 doc = _("(No help text available)")
665 665 if ui.quiet:
666 666 doc = doc.splitlines(0)[0]
667 667 ui.write("%s\n" % doc.rstrip())
668 668
669 669 if not ui.quiet:
670 670 # aliases
671 671 if len(aliases) > 1:
672 672 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
673 673
674 674 # options
675 675 if i[1]:
676 676 option_lists.append(("options", i[1]))
677 677
678 678 def helplist(select=None):
679 679 h = {}
680 680 cmds = {}
681 681 for c, e in table.items():
682 682 f = c.split("|", 1)[0]
683 683 if select and not select(f):
684 684 continue
685 685 if name == "shortlist" and not f.startswith("^"):
686 686 continue
687 687 f = f.lstrip("^")
688 688 if not ui.debugflag and f.startswith("debug"):
689 689 continue
690 690 doc = e[0].__doc__
691 691 if not doc:
692 692 doc = _("(No help text available)")
693 693 h[f] = doc.splitlines(0)[0].rstrip()
694 694 cmds[f] = c.lstrip("^")
695 695
696 696 fns = h.keys()
697 697 fns.sort()
698 698 m = max(map(len, fns))
699 699 for f in fns:
700 700 if ui.verbose:
701 701 commands = cmds[f].replace("|",", ")
702 702 ui.write(" %s:\n %s\n"%(commands, h[f]))
703 703 else:
704 704 ui.write(' %-*s %s\n' % (m, f, h[f]))
705 705
706 706 def helpext(name):
707 707 try:
708 708 mod = findext(name)
709 709 except KeyError:
710 710 raise UnknownCommand(name)
711 711
712 712 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
713 713 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
714 714 for d in doc[1:]:
715 715 ui.write(d, '\n')
716 716
717 717 ui.status('\n')
718 718 if ui.verbose:
719 719 ui.status(_('list of commands:\n\n'))
720 720 else:
721 721 ui.status(_('list of commands (use "hg help -v %s" '
722 722 'to show aliases and global options):\n\n') % name)
723 723
724 724 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
725 725 helplist(modcmds.has_key)
726 726
727 727 if name and name != 'shortlist':
728 728 try:
729 729 helpcmd(name)
730 730 except UnknownCommand:
731 731 helpext(name)
732 732
733 733 else:
734 734 # program name
735 735 if ui.verbose or with_version:
736 736 show_version(ui)
737 737 else:
738 738 ui.status(_("Mercurial Distributed SCM\n"))
739 739 ui.status('\n')
740 740
741 741 # list of commands
742 742 if name == "shortlist":
743 743 ui.status(_('basic commands (use "hg help" '
744 744 'for the full list or option "-v" for details):\n\n'))
745 745 elif ui.verbose:
746 746 ui.status(_('list of commands:\n\n'))
747 747 else:
748 748 ui.status(_('list of commands (use "hg help -v" '
749 749 'to show aliases and global options):\n\n'))
750 750
751 751 helplist()
752 752
753 753 # global options
754 754 if ui.verbose:
755 755 option_lists.append(("global options", globalopts))
756 756
757 757 # list all option lists
758 758 opt_output = []
759 759 for title, options in option_lists:
760 760 opt_output.append(("\n%s:\n" % title, None))
761 761 for shortopt, longopt, default, desc in options:
762 762 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
763 763 longopt and " --%s" % longopt),
764 764 "%s%s" % (desc,
765 765 default
766 766 and _(" (default: %s)") % default
767 767 or "")))
768 768
769 769 if opt_output:
770 770 opts_len = max([len(line[0]) for line in opt_output if line[1]])
771 771 for first, second in opt_output:
772 772 if second:
773 773 ui.write(" %-*s %s\n" % (opts_len, first, second))
774 774 else:
775 775 ui.write("%s\n" % first)
776 776
777 777 # Commands start here, listed alphabetically
778 778
779 779 def add(ui, repo, *pats, **opts):
780 780 """add the specified files on the next commit
781 781
782 782 Schedule files to be version controlled and added to the repository.
783 783
784 784 The files will be added to the repository at the next commit.
785 785
786 786 If no names are given, add all files in the repository.
787 787 """
788 788
789 789 names = []
790 790 for src, abs, rel, exact in walk(repo, pats, opts):
791 791 if exact:
792 792 if ui.verbose:
793 793 ui.status(_('adding %s\n') % rel)
794 794 names.append(abs)
795 795 elif repo.dirstate.state(abs) == '?':
796 796 ui.status(_('adding %s\n') % rel)
797 797 names.append(abs)
798 798 if not opts.get('dry_run'):
799 799 repo.add(names)
800 800
801 801 def addremove(ui, repo, *pats, **opts):
802 802 """add all new files, delete all missing files (DEPRECATED)
803 803
804 804 (DEPRECATED)
805 805 Add all new files and remove all missing files from the repository.
806 806
807 807 New files are ignored if they match any of the patterns in .hgignore. As
808 808 with add, these changes take effect at the next commit.
809 809
810 810 This command is now deprecated and will be removed in a future
811 811 release. Please use add and remove --after instead.
812 812 """
813 813 ui.warn(_('(the addremove command is deprecated; use add and remove '
814 814 '--after instead)\n'))
815 815 return addremove_lock(ui, repo, pats, opts)
816 816
817 817 def addremove_lock(ui, repo, pats, opts, wlock=None):
818 818 add, remove = [], []
819 819 for src, abs, rel, exact in walk(repo, pats, opts):
820 820 if src == 'f' and repo.dirstate.state(abs) == '?':
821 821 add.append(abs)
822 822 if ui.verbose or not exact:
823 823 ui.status(_('adding %s\n') % ((pats and rel) or abs))
824 824 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
825 825 remove.append(abs)
826 826 if ui.verbose or not exact:
827 827 ui.status(_('removing %s\n') % ((pats and rel) or abs))
828 828 if not opts.get('dry_run'):
829 829 repo.add(add, wlock=wlock)
830 830 repo.remove(remove, wlock=wlock)
831 831
832 832 def annotate(ui, repo, *pats, **opts):
833 833 """show changeset information per file line
834 834
835 835 List changes in files, showing the revision id responsible for each line
836 836
837 837 This command is useful to discover who did a change or when a change took
838 838 place.
839 839
840 840 Without the -a option, annotate will avoid processing files it
841 841 detects as binary. With -a, annotate will generate an annotation
842 842 anyway, probably with undesirable results.
843 843 """
844 844 def getnode(rev):
845 845 return short(repo.changelog.node(rev))
846 846
847 847 ucache = {}
848 848 def getname(rev):
849 849 try:
850 850 return ucache[rev]
851 851 except:
852 852 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
853 853 ucache[rev] = u
854 854 return u
855 855
856 856 dcache = {}
857 857 def getdate(rev):
858 858 datestr = dcache.get(rev)
859 859 if datestr is None:
860 860 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
861 861 return datestr
862 862
863 863 if not pats:
864 864 raise util.Abort(_('at least one file name or pattern required'))
865 865
866 866 opmap = [['user', getname], ['number', str], ['changeset', getnode],
867 867 ['date', getdate]]
868 868 if not opts['user'] and not opts['changeset'] and not opts['date']:
869 869 opts['number'] = 1
870 870
871 871 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
872 872
873 873 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
874 874 fctx = ctx.filectx(abs)
875 875 if not opts['text'] and util.binary(fctx.data()):
876 876 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
877 877 continue
878 878
879 879 lines = fctx.annotate()
880 880 pieces = []
881 881
882 882 for o, f in opmap:
883 883 if opts[o]:
884 884 l = [f(n) for n, dummy in lines]
885 885 if l:
886 886 m = max(map(len, l))
887 887 pieces.append(["%*s" % (m, x) for x in l])
888 888
889 889 if pieces:
890 890 for p, l in zip(zip(*pieces), lines):
891 891 ui.write("%s: %s" % (" ".join(p), l[1]))
892 892
893 893 def archive(ui, repo, dest, **opts):
894 894 '''create unversioned archive of a repository revision
895 895
896 896 By default, the revision used is the parent of the working
897 897 directory; use "-r" to specify a different revision.
898 898
899 899 To specify the type of archive to create, use "-t". Valid
900 900 types are:
901 901
902 902 "files" (default): a directory full of files
903 903 "tar": tar archive, uncompressed
904 904 "tbz2": tar archive, compressed using bzip2
905 905 "tgz": tar archive, compressed using gzip
906 906 "uzip": zip archive, uncompressed
907 907 "zip": zip archive, compressed using deflate
908 908
909 909 The exact name of the destination archive or directory is given
910 910 using a format string; see "hg help export" for details.
911 911
912 912 Each member added to an archive file has a directory prefix
913 913 prepended. Use "-p" to specify a format string for the prefix.
914 914 The default is the basename of the archive, with suffixes removed.
915 915 '''
916 916
917 917 if opts['rev']:
918 918 node = repo.lookup(opts['rev'])
919 919 else:
920 920 node, p2 = repo.dirstate.parents()
921 921 if p2 != nullid:
922 922 raise util.Abort(_('uncommitted merge - please provide a '
923 923 'specific revision'))
924 924
925 925 dest = make_filename(repo, dest, node)
926 926 if os.path.realpath(dest) == repo.root:
927 927 raise util.Abort(_('repository root cannot be destination'))
928 928 dummy, matchfn, dummy = matchpats(repo, [], opts)
929 929 kind = opts.get('type') or 'files'
930 930 prefix = opts['prefix']
931 931 if dest == '-':
932 932 if kind == 'files':
933 933 raise util.Abort(_('cannot archive plain files to stdout'))
934 934 dest = sys.stdout
935 935 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
936 936 prefix = make_filename(repo, prefix, node)
937 937 archival.archive(repo, dest, node, kind, not opts['no_decode'],
938 938 matchfn, prefix)
939 939
940 940 def backout(ui, repo, rev, **opts):
941 941 '''reverse effect of earlier changeset
942 942
943 943 Commit the backed out changes as a new changeset. The new
944 944 changeset is a child of the backed out changeset.
945 945
946 946 If you back out a changeset other than the tip, a new head is
947 947 created. This head is the parent of the working directory. If
948 948 you back out an old changeset, your working directory will appear
949 949 old after the backout. You should merge the backout changeset
950 950 with another head.
951 951
952 952 The --merge option remembers the parent of the working directory
953 953 before starting the backout, then merges the new head with that
954 954 changeset afterwards. This saves you from doing the merge by
955 955 hand. The result of this merge is not committed, as for a normal
956 956 merge.'''
957 957
958 958 bail_if_changed(repo)
959 959 op1, op2 = repo.dirstate.parents()
960 960 if op2 != nullid:
961 961 raise util.Abort(_('outstanding uncommitted merge'))
962 962 node = repo.lookup(rev)
963 963 p1, p2 = repo.changelog.parents(node)
964 964 if p1 == nullid:
965 965 raise util.Abort(_('cannot back out a change with no parents'))
966 966 if p2 != nullid:
967 967 if not opts['parent']:
968 968 raise util.Abort(_('cannot back out a merge changeset without '
969 969 '--parent'))
970 970 p = repo.lookup(opts['parent'])
971 971 if p not in (p1, p2):
972 972 raise util.Abort(_('%s is not a parent of %s' %
973 973 (short(p), short(node))))
974 974 parent = p
975 975 else:
976 976 if opts['parent']:
977 977 raise util.Abort(_('cannot use --parent on non-merge changeset'))
978 978 parent = p1
979 979 hg.clean(repo, node, show_stats=False)
980 980 revert_opts = opts.copy()
981 981 revert_opts['rev'] = hex(parent)
982 982 revert(ui, repo, **revert_opts)
983 983 commit_opts = opts.copy()
984 984 commit_opts['addremove'] = False
985 985 if not commit_opts['message'] and not commit_opts['logfile']:
986 986 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
987 987 commit_opts['force_editor'] = True
988 988 commit(ui, repo, **commit_opts)
989 989 def nice(node):
990 990 return '%d:%s' % (repo.changelog.rev(node), short(node))
991 991 ui.status(_('changeset %s backs out changeset %s\n') %
992 992 (nice(repo.changelog.tip()), nice(node)))
993 993 if op1 != node:
994 994 if opts['merge']:
995 995 ui.status(_('merging with changeset %s\n') % nice(op1))
996 996 n = _lookup(repo, hex(op1))
997 997 hg.merge(repo, n)
998 998 else:
999 999 ui.status(_('the backout changeset is a new head - '
1000 1000 'do not forget to merge\n'))
1001 1001 ui.status(_('(use "backout --merge" '
1002 1002 'if you want to auto-merge)\n'))
1003 1003
1004 1004 def bundle(ui, repo, fname, dest=None, **opts):
1005 1005 """create a changegroup file
1006 1006
1007 1007 Generate a compressed changegroup file collecting all changesets
1008 1008 not found in the other repository.
1009 1009
1010 1010 This file can then be transferred using conventional means and
1011 1011 applied to another repository with the unbundle command. This is
1012 1012 useful when native push and pull are not available or when
1013 1013 exporting an entire repository is undesirable. The standard file
1014 1014 extension is ".hg".
1015 1015
1016 1016 Unlike import/export, this exactly preserves all changeset
1017 1017 contents including permissions, rename data, and revision history.
1018 1018 """
1019 1019 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1020 1020 other = hg.repository(ui, dest)
1021 1021 o = repo.findoutgoing(other, force=opts['force'])
1022 1022 cg = repo.changegroup(o, 'bundle')
1023 1023 write_bundle(cg, fname)
1024 1024
1025 1025 def cat(ui, repo, file1, *pats, **opts):
1026 1026 """output the latest or given revisions of files
1027 1027
1028 1028 Print the specified files as they were at the given revision.
1029 1029 If no revision is given then the tip is used.
1030 1030
1031 1031 Output may be to a file, in which case the name of the file is
1032 1032 given using a format string. The formatting rules are the same as
1033 1033 for the export command, with the following additions:
1034 1034
1035 1035 %s basename of file being printed
1036 1036 %d dirname of file being printed, or '.' if in repo root
1037 1037 %p root-relative path name of file being printed
1038 1038 """
1039 1039 ctx = repo.changectx(opts['rev'] or "-1")
1040 1040 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
1041 1041 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
1042 1042 fp.write(ctx.filectx(abs).data())
1043 1043
1044 1044 def clone(ui, source, dest=None, **opts):
1045 1045 """make a copy of an existing repository
1046 1046
1047 1047 Create a copy of an existing repository in a new directory.
1048 1048
1049 1049 If no destination directory name is specified, it defaults to the
1050 1050 basename of the source.
1051 1051
1052 1052 The location of the source is added to the new repository's
1053 1053 .hg/hgrc file, as the default to be used for future pulls.
1054 1054
1055 1055 For efficiency, hardlinks are used for cloning whenever the source
1056 1056 and destination are on the same filesystem (note this applies only
1057 1057 to the repository data, not to the checked out files). Some
1058 1058 filesystems, such as AFS, implement hardlinking incorrectly, but
1059 1059 do not report errors. In these cases, use the --pull option to
1060 1060 avoid hardlinking.
1061 1061
1062 1062 You can safely clone repositories and checked out files using full
1063 1063 hardlinks with
1064 1064
1065 1065 $ cp -al REPO REPOCLONE
1066 1066
1067 1067 which is the fastest way to clone. However, the operation is not
1068 1068 atomic (making sure REPO is not modified during the operation is
1069 1069 up to you) and you have to make sure your editor breaks hardlinks
1070 1070 (Emacs and most Linux Kernel tools do so).
1071 1071
1072 1072 If you use the -r option to clone up to a specific revision, no
1073 1073 subsequent revisions will be present in the cloned repository.
1074 1074 This option implies --pull, even on local repositories.
1075 1075
1076 1076 See pull for valid source format details.
1077 1077
1078 1078 It is possible to specify an ssh:// URL as the destination, but no
1079 1079 .hg/hgrc will be created on the remote side. Look at the help text
1080 1080 for the pull command for important details about ssh:// URLs.
1081 1081 """
1082 1082 setremoteconfig(ui, opts)
1083 1083 hg.clone(ui, ui.expandpath(source), dest,
1084 1084 pull=opts['pull'],
1085 1085 stream=opts['uncompressed'],
1086 1086 rev=opts['rev'],
1087 1087 update=not opts['noupdate'])
1088 1088
1089 1089 def commit(ui, repo, *pats, **opts):
1090 1090 """commit the specified files or all outstanding changes
1091 1091
1092 1092 Commit changes to the given files into the repository.
1093 1093
1094 1094 If a list of files is omitted, all changes reported by "hg status"
1095 1095 will be committed.
1096 1096
1097 1097 If no commit message is specified, the editor configured in your hgrc
1098 1098 or in the EDITOR environment variable is started to enter a message.
1099 1099 """
1100 1100 message = logmessage(opts)
1101 1101
1102 1102 if opts['addremove']:
1103 1103 addremove_lock(ui, repo, pats, opts)
1104 1104 fns, match, anypats = matchpats(repo, pats, opts)
1105 1105 if pats:
1106 1106 modified, added, removed, deleted, unknown = (
1107 1107 repo.changes(files=fns, match=match))
1108 1108 files = modified + added + removed
1109 1109 else:
1110 1110 files = []
1111 1111 try:
1112 1112 repo.commit(files, message, opts['user'], opts['date'], match,
1113 1113 force_editor=opts.get('force_editor'))
1114 1114 except ValueError, inst:
1115 1115 raise util.Abort(str(inst))
1116 1116
1117 1117 def docopy(ui, repo, pats, opts, wlock):
1118 1118 # called with the repo lock held
1119 1119 cwd = repo.getcwd()
1120 1120 errors = 0
1121 1121 copied = []
1122 1122 targets = {}
1123 1123
1124 1124 def okaytocopy(abs, rel, exact):
1125 1125 reasons = {'?': _('is not managed'),
1126 1126 'a': _('has been marked for add'),
1127 1127 'r': _('has been marked for remove')}
1128 1128 state = repo.dirstate.state(abs)
1129 1129 reason = reasons.get(state)
1130 1130 if reason:
1131 1131 if state == 'a':
1132 1132 origsrc = repo.dirstate.copied(abs)
1133 1133 if origsrc is not None:
1134 1134 return origsrc
1135 1135 if exact:
1136 1136 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1137 1137 else:
1138 1138 return abs
1139 1139
1140 1140 def copy(origsrc, abssrc, relsrc, target, exact):
1141 1141 abstarget = util.canonpath(repo.root, cwd, target)
1142 1142 reltarget = util.pathto(cwd, abstarget)
1143 1143 prevsrc = targets.get(abstarget)
1144 1144 if prevsrc is not None:
1145 1145 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1146 1146 (reltarget, abssrc, prevsrc))
1147 1147 return
1148 1148 if (not opts['after'] and os.path.exists(reltarget) or
1149 1149 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1150 1150 if not opts['force']:
1151 1151 ui.warn(_('%s: not overwriting - file exists\n') %
1152 1152 reltarget)
1153 1153 return
1154 1154 if not opts['after'] and not opts.get('dry_run'):
1155 1155 os.unlink(reltarget)
1156 1156 if opts['after']:
1157 1157 if not os.path.exists(reltarget):
1158 1158 return
1159 1159 else:
1160 1160 targetdir = os.path.dirname(reltarget) or '.'
1161 1161 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1162 1162 os.makedirs(targetdir)
1163 1163 try:
1164 1164 restore = repo.dirstate.state(abstarget) == 'r'
1165 1165 if restore and not opts.get('dry_run'):
1166 1166 repo.undelete([abstarget], wlock)
1167 1167 try:
1168 1168 if not opts.get('dry_run'):
1169 1169 shutil.copyfile(relsrc, reltarget)
1170 1170 shutil.copymode(relsrc, reltarget)
1171 1171 restore = False
1172 1172 finally:
1173 1173 if restore:
1174 1174 repo.remove([abstarget], wlock)
1175 1175 except shutil.Error, inst:
1176 1176 raise util.Abort(str(inst))
1177 1177 except IOError, inst:
1178 1178 if inst.errno == errno.ENOENT:
1179 1179 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1180 1180 else:
1181 1181 ui.warn(_('%s: cannot copy - %s\n') %
1182 1182 (relsrc, inst.strerror))
1183 1183 errors += 1
1184 1184 return
1185 1185 if ui.verbose or not exact:
1186 1186 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1187 1187 targets[abstarget] = abssrc
1188 1188 if abstarget != origsrc and not opts.get('dry_run'):
1189 1189 repo.copy(origsrc, abstarget, wlock)
1190 1190 copied.append((abssrc, relsrc, exact))
1191 1191
1192 1192 def targetpathfn(pat, dest, srcs):
1193 1193 if os.path.isdir(pat):
1194 1194 abspfx = util.canonpath(repo.root, cwd, pat)
1195 1195 if destdirexists:
1196 1196 striplen = len(os.path.split(abspfx)[0])
1197 1197 else:
1198 1198 striplen = len(abspfx)
1199 1199 if striplen:
1200 1200 striplen += len(os.sep)
1201 1201 res = lambda p: os.path.join(dest, p[striplen:])
1202 1202 elif destdirexists:
1203 1203 res = lambda p: os.path.join(dest, os.path.basename(p))
1204 1204 else:
1205 1205 res = lambda p: dest
1206 1206 return res
1207 1207
1208 1208 def targetpathafterfn(pat, dest, srcs):
1209 1209 if util.patkind(pat, None)[0]:
1210 1210 # a mercurial pattern
1211 1211 res = lambda p: os.path.join(dest, os.path.basename(p))
1212 1212 else:
1213 1213 abspfx = util.canonpath(repo.root, cwd, pat)
1214 1214 if len(abspfx) < len(srcs[0][0]):
1215 1215 # A directory. Either the target path contains the last
1216 1216 # component of the source path or it does not.
1217 1217 def evalpath(striplen):
1218 1218 score = 0
1219 1219 for s in srcs:
1220 1220 t = os.path.join(dest, s[0][striplen:])
1221 1221 if os.path.exists(t):
1222 1222 score += 1
1223 1223 return score
1224 1224
1225 1225 striplen = len(abspfx)
1226 1226 if striplen:
1227 1227 striplen += len(os.sep)
1228 1228 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1229 1229 score = evalpath(striplen)
1230 1230 striplen1 = len(os.path.split(abspfx)[0])
1231 1231 if striplen1:
1232 1232 striplen1 += len(os.sep)
1233 1233 if evalpath(striplen1) > score:
1234 1234 striplen = striplen1
1235 1235 res = lambda p: os.path.join(dest, p[striplen:])
1236 1236 else:
1237 1237 # a file
1238 1238 if destdirexists:
1239 1239 res = lambda p: os.path.join(dest, os.path.basename(p))
1240 1240 else:
1241 1241 res = lambda p: dest
1242 1242 return res
1243 1243
1244 1244
1245 1245 pats = list(pats)
1246 1246 if not pats:
1247 1247 raise util.Abort(_('no source or destination specified'))
1248 1248 if len(pats) == 1:
1249 1249 raise util.Abort(_('no destination specified'))
1250 1250 dest = pats.pop()
1251 1251 destdirexists = os.path.isdir(dest)
1252 1252 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1253 1253 raise util.Abort(_('with multiple sources, destination must be an '
1254 1254 'existing directory'))
1255 1255 if opts['after']:
1256 1256 tfn = targetpathafterfn
1257 1257 else:
1258 1258 tfn = targetpathfn
1259 1259 copylist = []
1260 1260 for pat in pats:
1261 1261 srcs = []
1262 1262 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1263 1263 origsrc = okaytocopy(abssrc, relsrc, exact)
1264 1264 if origsrc:
1265 1265 srcs.append((origsrc, abssrc, relsrc, exact))
1266 1266 if not srcs:
1267 1267 continue
1268 1268 copylist.append((tfn(pat, dest, srcs), srcs))
1269 1269 if not copylist:
1270 1270 raise util.Abort(_('no files to copy'))
1271 1271
1272 1272 for targetpath, srcs in copylist:
1273 1273 for origsrc, abssrc, relsrc, exact in srcs:
1274 1274 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1275 1275
1276 1276 if errors:
1277 1277 ui.warn(_('(consider using --after)\n'))
1278 1278 return errors, copied
1279 1279
1280 1280 def copy(ui, repo, *pats, **opts):
1281 1281 """mark files as copied for the next commit
1282 1282
1283 1283 Mark dest as having copies of source files. If dest is a
1284 1284 directory, copies are put in that directory. If dest is a file,
1285 1285 there can only be one source.
1286 1286
1287 1287 By default, this command copies the contents of files as they
1288 1288 stand in the working directory. If invoked with --after, the
1289 1289 operation is recorded, but no copying is performed.
1290 1290
1291 1291 This command takes effect in the next commit.
1292 1292
1293 1293 NOTE: This command should be treated as experimental. While it
1294 1294 should properly record copied files, this information is not yet
1295 1295 fully used by merge, nor fully reported by log.
1296 1296 """
1297 1297 wlock = repo.wlock(0)
1298 1298 errs, copied = docopy(ui, repo, pats, opts, wlock)
1299 1299 return errs
1300 1300
1301 1301 def debugancestor(ui, index, rev1, rev2):
1302 1302 """find the ancestor revision of two revisions in a given index"""
1303 1303 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1304 1304 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1305 1305 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1306 1306
1307 1307 def debugcomplete(ui, cmd='', **opts):
1308 1308 """returns the completion list associated with the given command"""
1309 1309
1310 1310 if opts['options']:
1311 1311 options = []
1312 1312 otables = [globalopts]
1313 1313 if cmd:
1314 1314 aliases, entry = findcmd(cmd)
1315 1315 otables.append(entry[1])
1316 1316 for t in otables:
1317 1317 for o in t:
1318 1318 if o[0]:
1319 1319 options.append('-%s' % o[0])
1320 1320 options.append('--%s' % o[1])
1321 1321 ui.write("%s\n" % "\n".join(options))
1322 1322 return
1323 1323
1324 1324 clist = findpossible(cmd).keys()
1325 1325 clist.sort()
1326 1326 ui.write("%s\n" % "\n".join(clist))
1327 1327
1328 1328 def debugrebuildstate(ui, repo, rev=None):
1329 1329 """rebuild the dirstate as it would look like for the given revision"""
1330 1330 if not rev:
1331 1331 rev = repo.changelog.tip()
1332 1332 else:
1333 1333 rev = repo.lookup(rev)
1334 1334 change = repo.changelog.read(rev)
1335 1335 n = change[0]
1336 1336 files = repo.manifest.read(n)
1337 1337 wlock = repo.wlock()
1338 1338 repo.dirstate.rebuild(rev, files)
1339 1339
1340 1340 def debugcheckstate(ui, repo):
1341 1341 """validate the correctness of the current dirstate"""
1342 1342 parent1, parent2 = repo.dirstate.parents()
1343 1343 repo.dirstate.read()
1344 1344 dc = repo.dirstate.map
1345 1345 keys = dc.keys()
1346 1346 keys.sort()
1347 1347 m1n = repo.changelog.read(parent1)[0]
1348 1348 m2n = repo.changelog.read(parent2)[0]
1349 1349 m1 = repo.manifest.read(m1n)
1350 1350 m2 = repo.manifest.read(m2n)
1351 1351 errors = 0
1352 1352 for f in dc:
1353 1353 state = repo.dirstate.state(f)
1354 1354 if state in "nr" and f not in m1:
1355 1355 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1356 1356 errors += 1
1357 1357 if state in "a" and f in m1:
1358 1358 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1359 1359 errors += 1
1360 1360 if state in "m" and f not in m1 and f not in m2:
1361 1361 ui.warn(_("%s in state %s, but not in either manifest\n") %
1362 1362 (f, state))
1363 1363 errors += 1
1364 1364 for f in m1:
1365 1365 state = repo.dirstate.state(f)
1366 1366 if state not in "nrm":
1367 1367 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1368 1368 errors += 1
1369 1369 if errors:
1370 1370 error = _(".hg/dirstate inconsistent with current parent's manifest")
1371 1371 raise util.Abort(error)
1372 1372
1373 1373 def debugconfig(ui, repo, *values):
1374 1374 """show combined config settings from all hgrc files
1375 1375
1376 1376 With no args, print names and values of all config items.
1377 1377
1378 1378 With one arg of the form section.name, print just the value of
1379 1379 that config item.
1380 1380
1381 1381 With multiple args, print names and values of all config items
1382 1382 with matching section names."""
1383 1383
1384 1384 if values:
1385 1385 if len([v for v in values if '.' in v]) > 1:
1386 1386 raise util.Abort(_('only one config item permitted'))
1387 1387 for section, name, value in ui.walkconfig():
1388 1388 sectname = section + '.' + name
1389 1389 if values:
1390 1390 for v in values:
1391 1391 if v == section:
1392 1392 ui.write('%s=%s\n' % (sectname, value))
1393 1393 elif v == sectname:
1394 1394 ui.write(value, '\n')
1395 1395 else:
1396 1396 ui.write('%s=%s\n' % (sectname, value))
1397 1397
1398 1398 def debugsetparents(ui, repo, rev1, rev2=None):
1399 1399 """manually set the parents of the current working directory
1400 1400
1401 1401 This is useful for writing repository conversion tools, but should
1402 1402 be used with care.
1403 1403 """
1404 1404
1405 1405 if not rev2:
1406 1406 rev2 = hex(nullid)
1407 1407
1408 1408 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1409 1409
1410 1410 def debugstate(ui, repo):
1411 1411 """show the contents of the current dirstate"""
1412 1412 repo.dirstate.read()
1413 1413 dc = repo.dirstate.map
1414 1414 keys = dc.keys()
1415 1415 keys.sort()
1416 1416 for file_ in keys:
1417 1417 ui.write("%c %3o %10d %s %s\n"
1418 1418 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1419 1419 time.strftime("%x %X",
1420 1420 time.localtime(dc[file_][3])), file_))
1421 1421 for f in repo.dirstate.copies:
1422 1422 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1423 1423
1424 1424 def debugdata(ui, file_, rev):
1425 1425 """dump the contents of an data file revision"""
1426 1426 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1427 1427 file_[:-2] + ".i", file_, 0)
1428 1428 try:
1429 1429 ui.write(r.revision(r.lookup(rev)))
1430 1430 except KeyError:
1431 1431 raise util.Abort(_('invalid revision identifier %s'), rev)
1432 1432
1433 1433 def debugindex(ui, file_):
1434 1434 """dump the contents of an index file"""
1435 1435 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1436 1436 ui.write(" rev offset length base linkrev" +
1437 1437 " nodeid p1 p2\n")
1438 1438 for i in range(r.count()):
1439 1439 node = r.node(i)
1440 1440 pp = r.parents(node)
1441 1441 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1442 1442 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1443 1443 short(node), short(pp[0]), short(pp[1])))
1444 1444
1445 1445 def debugindexdot(ui, file_):
1446 1446 """dump an index DAG as a .dot file"""
1447 1447 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1448 1448 ui.write("digraph G {\n")
1449 1449 for i in range(r.count()):
1450 1450 node = r.node(i)
1451 1451 pp = r.parents(node)
1452 1452 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1453 1453 if pp[1] != nullid:
1454 1454 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1455 1455 ui.write("}\n")
1456 1456
1457 1457 def debugrename(ui, repo, file, rev=None):
1458 1458 """dump rename information"""
1459 1459 r = repo.file(relpath(repo, [file])[0])
1460 1460 if rev:
1461 1461 try:
1462 1462 # assume all revision numbers are for changesets
1463 1463 n = repo.lookup(rev)
1464 1464 change = repo.changelog.read(n)
1465 1465 m = repo.manifest.read(change[0])
1466 1466 n = m[relpath(repo, [file])[0]]
1467 1467 except (hg.RepoError, KeyError):
1468 1468 n = r.lookup(rev)
1469 1469 else:
1470 1470 n = r.tip()
1471 1471 m = r.renamed(n)
1472 1472 if m:
1473 1473 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1474 1474 else:
1475 1475 ui.write(_("not renamed\n"))
1476 1476
1477 1477 def debugwalk(ui, repo, *pats, **opts):
1478 1478 """show how files match on given patterns"""
1479 1479 items = list(walk(repo, pats, opts))
1480 1480 if not items:
1481 1481 return
1482 1482 fmt = '%%s %%-%ds %%-%ds %%s' % (
1483 1483 max([len(abs) for (src, abs, rel, exact) in items]),
1484 1484 max([len(rel) for (src, abs, rel, exact) in items]))
1485 1485 for src, abs, rel, exact in items:
1486 1486 line = fmt % (src, abs, rel, exact and 'exact' or '')
1487 1487 ui.write("%s\n" % line.rstrip())
1488 1488
1489 1489 def diff(ui, repo, *pats, **opts):
1490 1490 """diff repository (or selected files)
1491 1491
1492 1492 Show differences between revisions for the specified files.
1493 1493
1494 1494 Differences between files are shown using the unified diff format.
1495 1495
1496 1496 When two revision arguments are given, then changes are shown
1497 1497 between those revisions. If only one revision is specified then
1498 1498 that revision is compared to the working directory, and, when no
1499 1499 revisions are specified, the working directory files are compared
1500 1500 to its parent.
1501 1501
1502 1502 Without the -a option, diff will avoid generating diffs of files
1503 1503 it detects as binary. With -a, diff will generate a diff anyway,
1504 1504 probably with undesirable results.
1505 1505 """
1506 1506 node1, node2 = revpair(ui, repo, opts['rev'])
1507 1507
1508 1508 fns, matchfn, anypats = matchpats(repo, pats, opts)
1509 1509
1510 1510 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1511 1511 text=opts['text'], opts=opts)
1512 1512
1513 1513 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1514 1514 node = repo.lookup(changeset)
1515 1515 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1516 1516 if opts['switch_parent']:
1517 1517 parents.reverse()
1518 1518 prev = (parents and parents[0]) or nullid
1519 1519 change = repo.changelog.read(node)
1520 1520
1521 1521 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1522 1522 revwidth=revwidth)
1523 1523 if fp != sys.stdout:
1524 1524 ui.note("%s\n" % fp.name)
1525 1525
1526 1526 fp.write("# HG changeset patch\n")
1527 1527 fp.write("# User %s\n" % change[1])
1528 1528 fp.write("# Date %d %d\n" % change[2])
1529 1529 fp.write("# Node ID %s\n" % hex(node))
1530 1530 fp.write("# Parent %s\n" % hex(prev))
1531 1531 if len(parents) > 1:
1532 1532 fp.write("# Parent %s\n" % hex(parents[1]))
1533 1533 fp.write(change[4].rstrip())
1534 1534 fp.write("\n\n")
1535 1535
1536 1536 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1537 1537 if fp != sys.stdout:
1538 1538 fp.close()
1539 1539
1540 1540 def export(ui, repo, *changesets, **opts):
1541 1541 """dump the header and diffs for one or more changesets
1542 1542
1543 1543 Print the changeset header and diffs for one or more revisions.
1544 1544
1545 1545 The information shown in the changeset header is: author,
1546 1546 changeset hash, parent and commit comment.
1547 1547
1548 1548 Output may be to a file, in which case the name of the file is
1549 1549 given using a format string. The formatting rules are as follows:
1550 1550
1551 1551 %% literal "%" character
1552 1552 %H changeset hash (40 bytes of hexadecimal)
1553 1553 %N number of patches being generated
1554 1554 %R changeset revision number
1555 1555 %b basename of the exporting repository
1556 1556 %h short-form changeset hash (12 bytes of hexadecimal)
1557 1557 %n zero-padded sequence number, starting at 1
1558 1558 %r zero-padded changeset revision number
1559 1559
1560 1560 Without the -a option, export will avoid generating diffs of files
1561 1561 it detects as binary. With -a, export will generate a diff anyway,
1562 1562 probably with undesirable results.
1563 1563
1564 1564 With the --switch-parent option, the diff will be against the second
1565 1565 parent. It can be useful to review a merge.
1566 1566 """
1567 1567 if not changesets:
1568 1568 raise util.Abort(_("export requires at least one changeset"))
1569 1569 seqno = 0
1570 1570 revs = list(revrange(ui, repo, changesets))
1571 1571 total = len(revs)
1572 1572 revwidth = max(map(len, revs))
1573 1573 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1574 1574 ui.note(msg)
1575 1575 for cset in revs:
1576 1576 seqno += 1
1577 1577 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1578 1578
1579 1579 def forget(ui, repo, *pats, **opts):
1580 1580 """don't add the specified files on the next commit (DEPRECATED)
1581 1581
1582 1582 (DEPRECATED)
1583 1583 Undo an 'hg add' scheduled for the next commit.
1584 1584
1585 1585 This command is now deprecated and will be removed in a future
1586 1586 release. Please use revert instead.
1587 1587 """
1588 1588 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1589 1589 forget = []
1590 1590 for src, abs, rel, exact in walk(repo, pats, opts):
1591 1591 if repo.dirstate.state(abs) == 'a':
1592 1592 forget.append(abs)
1593 1593 if ui.verbose or not exact:
1594 1594 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1595 1595 repo.forget(forget)
1596 1596
1597 1597 def grep(ui, repo, pattern, *pats, **opts):
1598 1598 """search for a pattern in specified files and revisions
1599 1599
1600 1600 Search revisions of files for a regular expression.
1601 1601
1602 1602 This command behaves differently than Unix grep. It only accepts
1603 1603 Python/Perl regexps. It searches repository history, not the
1604 1604 working directory. It always prints the revision number in which
1605 1605 a match appears.
1606 1606
1607 1607 By default, grep only prints output for the first revision of a
1608 1608 file in which it finds a match. To get it to print every revision
1609 1609 that contains a change in match status ("-" for a match that
1610 1610 becomes a non-match, or "+" for a non-match that becomes a match),
1611 1611 use the --all flag.
1612 1612 """
1613 1613 reflags = 0
1614 1614 if opts['ignore_case']:
1615 1615 reflags |= re.I
1616 1616 regexp = re.compile(pattern, reflags)
1617 1617 sep, eol = ':', '\n'
1618 1618 if opts['print0']:
1619 1619 sep = eol = '\0'
1620 1620
1621 1621 fcache = {}
1622 1622 def getfile(fn):
1623 1623 if fn not in fcache:
1624 1624 fcache[fn] = repo.file(fn)
1625 1625 return fcache[fn]
1626 1626
1627 1627 def matchlines(body):
1628 1628 begin = 0
1629 1629 linenum = 0
1630 1630 while True:
1631 1631 match = regexp.search(body, begin)
1632 1632 if not match:
1633 1633 break
1634 1634 mstart, mend = match.span()
1635 1635 linenum += body.count('\n', begin, mstart) + 1
1636 1636 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1637 1637 lend = body.find('\n', mend)
1638 1638 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1639 1639 begin = lend + 1
1640 1640
1641 1641 class linestate(object):
1642 1642 def __init__(self, line, linenum, colstart, colend):
1643 1643 self.line = line
1644 1644 self.linenum = linenum
1645 1645 self.colstart = colstart
1646 1646 self.colend = colend
1647 1647 def __eq__(self, other):
1648 1648 return self.line == other.line
1649 1649 def __hash__(self):
1650 1650 return hash(self.line)
1651 1651
1652 1652 matches = {}
1653 1653 def grepbody(fn, rev, body):
1654 1654 matches[rev].setdefault(fn, {})
1655 1655 m = matches[rev][fn]
1656 1656 for lnum, cstart, cend, line in matchlines(body):
1657 1657 s = linestate(line, lnum, cstart, cend)
1658 1658 m[s] = s
1659 1659
1660 1660 # FIXME: prev isn't used, why ?
1661 1661 prev = {}
1662 1662 ucache = {}
1663 1663 def display(fn, rev, states, prevstates):
1664 1664 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1665 1665 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1666 1666 counts = {'-': 0, '+': 0}
1667 1667 filerevmatches = {}
1668 1668 for l in diff:
1669 1669 if incrementing or not opts['all']:
1670 1670 change = ((l in prevstates) and '-') or '+'
1671 1671 r = rev
1672 1672 else:
1673 1673 change = ((l in states) and '-') or '+'
1674 1674 r = prev[fn]
1675 1675 cols = [fn, str(rev)]
1676 1676 if opts['line_number']:
1677 1677 cols.append(str(l.linenum))
1678 1678 if opts['all']:
1679 1679 cols.append(change)
1680 1680 if opts['user']:
1681 1681 cols.append(trimuser(ui, getchange(rev)[1], rev,
1682 1682 ucache))
1683 1683 if opts['files_with_matches']:
1684 1684 c = (fn, rev)
1685 1685 if c in filerevmatches:
1686 1686 continue
1687 1687 filerevmatches[c] = 1
1688 1688 else:
1689 1689 cols.append(l.line)
1690 1690 ui.write(sep.join(cols), eol)
1691 1691 counts[change] += 1
1692 1692 return counts['+'], counts['-']
1693 1693
1694 1694 fstate = {}
1695 1695 skip = {}
1696 1696 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1697 1697 count = 0
1698 1698 incrementing = False
1699 1699 for st, rev, fns in changeiter:
1700 1700 if st == 'window':
1701 1701 incrementing = rev
1702 1702 matches.clear()
1703 1703 elif st == 'add':
1704 1704 change = repo.changelog.read(repo.lookup(str(rev)))
1705 1705 mf = repo.manifest.read(change[0])
1706 1706 matches[rev] = {}
1707 1707 for fn in fns:
1708 1708 if fn in skip:
1709 1709 continue
1710 1710 fstate.setdefault(fn, {})
1711 1711 try:
1712 1712 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1713 1713 except KeyError:
1714 1714 pass
1715 1715 elif st == 'iter':
1716 1716 states = matches[rev].items()
1717 1717 states.sort()
1718 1718 for fn, m in states:
1719 1719 if fn in skip:
1720 1720 continue
1721 1721 if incrementing or not opts['all'] or fstate[fn]:
1722 1722 pos, neg = display(fn, rev, m, fstate[fn])
1723 1723 count += pos + neg
1724 1724 if pos and not opts['all']:
1725 1725 skip[fn] = True
1726 1726 fstate[fn] = m
1727 1727 prev[fn] = rev
1728 1728
1729 1729 if not incrementing:
1730 1730 fstate = fstate.items()
1731 1731 fstate.sort()
1732 1732 for fn, state in fstate:
1733 1733 if fn in skip:
1734 1734 continue
1735 1735 display(fn, rev, {}, state)
1736 1736 return (count == 0 and 1) or 0
1737 1737
1738 1738 def heads(ui, repo, **opts):
1739 1739 """show current repository heads
1740 1740
1741 1741 Show all repository head changesets.
1742 1742
1743 1743 Repository "heads" are changesets that don't have children
1744 1744 changesets. They are where development generally takes place and
1745 1745 are the usual targets for update and merge operations.
1746 1746 """
1747 1747 if opts['rev']:
1748 1748 heads = repo.heads(repo.lookup(opts['rev']))
1749 1749 else:
1750 1750 heads = repo.heads()
1751 1751 br = None
1752 1752 if opts['branches']:
1753 1753 br = repo.branchlookup(heads)
1754 1754 displayer = show_changeset(ui, repo, opts)
1755 1755 for n in heads:
1756 1756 displayer.show(changenode=n, brinfo=br)
1757 1757
1758 1758 def identify(ui, repo):
1759 1759 """print information about the working copy
1760 1760
1761 1761 Print a short summary of the current state of the repo.
1762 1762
1763 1763 This summary identifies the repository state using one or two parent
1764 1764 hash identifiers, followed by a "+" if there are uncommitted changes
1765 1765 in the working directory, followed by a list of tags for this revision.
1766 1766 """
1767 1767 parents = [p for p in repo.dirstate.parents() if p != nullid]
1768 1768 if not parents:
1769 1769 ui.write(_("unknown\n"))
1770 1770 return
1771 1771
1772 1772 hexfunc = ui.verbose and hex or short
1773 1773 modified, added, removed, deleted, unknown = repo.changes()
1774 1774 output = ["%s%s" %
1775 1775 ('+'.join([hexfunc(parent) for parent in parents]),
1776 1776 (modified or added or removed or deleted) and "+" or "")]
1777 1777
1778 1778 if not ui.quiet:
1779 1779 # multiple tags for a single parent separated by '/'
1780 1780 parenttags = ['/'.join(tags)
1781 1781 for tags in map(repo.nodetags, parents) if tags]
1782 1782 # tags for multiple parents separated by ' + '
1783 1783 if parenttags:
1784 1784 output.append(' + '.join(parenttags))
1785 1785
1786 1786 ui.write("%s\n" % ' '.join(output))
1787 1787
1788 1788 def import_(ui, repo, patch1, *patches, **opts):
1789 1789 """import an ordered set of patches
1790 1790
1791 1791 Import a list of patches and commit them individually.
1792 1792
1793 1793 If there are outstanding changes in the working directory, import
1794 1794 will abort unless given the -f flag.
1795 1795
1796 1796 You can import a patch straight from a mail message. Even patches
1797 1797 as attachments work (body part must be type text/plain or
1798 1798 text/x-patch to be used). From and Subject headers of email
1799 1799 message are used as default committer and commit message. All
1800 1800 text/plain body parts before first diff are added to commit
1801 1801 message.
1802 1802
1803 1803 If imported patch was generated by hg export, user and description
1804 1804 from patch override values from message headers and body. Values
1805 1805 given on command line with -m and -u override these.
1806 1806
1807 1807 To read a patch from standard input, use patch name "-".
1808 1808 """
1809 1809 patches = (patch1,) + patches
1810 1810
1811 1811 if not opts['force']:
1812 1812 bail_if_changed(repo)
1813 1813
1814 1814 d = opts["base"]
1815 1815 strip = opts["strip"]
1816 1816
1817 1817 mailre = re.compile(r'(?:From |[\w-]+:)')
1818 1818
1819 1819 # attempt to detect the start of a patch
1820 1820 # (this heuristic is borrowed from quilt)
1821 1821 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1822 1822 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1823 1823 '(---|\*\*\*)[ \t])', re.MULTILINE)
1824 1824
1825 1825 wlock = repo.wlock()
1826 1826 lock = repo.lock()
1827 1827
1828 for patch in patches:
1829 pf = os.path.join(d, patch)
1828 for p in patches:
1829 pf = os.path.join(d, p)
1830 1830
1831 1831 message = None
1832 1832 user = None
1833 1833 date = None
1834 1834 hgpatch = False
1835 1835
1836 p = email.Parser.Parser()
1836 parser = email.Parser.Parser()
1837 1837 if pf == '-':
1838 msg = p.parse(sys.stdin)
1838 msg = parser.parse(sys.stdin)
1839 1839 ui.status(_("applying patch from stdin\n"))
1840 1840 else:
1841 msg = p.parse(file(pf))
1842 ui.status(_("applying %s\n") % patch)
1841 msg = parser.parse(file(pf))
1842 ui.status(_("applying %s\n") % p)
1843 1843
1844 1844 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1845 1845 tmpfp = os.fdopen(fd, 'w')
1846 1846 try:
1847 1847 message = msg['Subject']
1848 1848 if message:
1849 1849 message = message.replace('\n\t', ' ')
1850 1850 ui.debug('Subject: %s\n' % message)
1851 1851 user = msg['From']
1852 1852 if user:
1853 1853 ui.debug('From: %s\n' % user)
1854 1854 diffs_seen = 0
1855 1855 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
1856 1856 for part in msg.walk():
1857 1857 content_type = part.get_content_type()
1858 1858 ui.debug('Content-Type: %s\n' % content_type)
1859 1859 if content_type not in ok_types:
1860 1860 continue
1861 1861 payload = part.get_payload(decode=True)
1862 1862 m = diffre.search(payload)
1863 1863 if m:
1864 1864 ui.debug(_('found patch at byte %d\n') % m.start(0))
1865 1865 diffs_seen += 1
1866 1866 hgpatch = False
1867 1867 fp = cStringIO.StringIO()
1868 1868 if message:
1869 1869 fp.write(message)
1870 1870 fp.write('\n')
1871 1871 for line in payload[:m.start(0)].splitlines():
1872 1872 if line.startswith('# HG changeset patch'):
1873 1873 ui.debug(_('patch generated by hg export\n'))
1874 1874 hgpatch = True
1875 1875 # drop earlier commit message content
1876 1876 fp.seek(0)
1877 1877 fp.truncate()
1878 1878 elif hgpatch:
1879 1879 if line.startswith('# User '):
1880 1880 user = line[7:]
1881 1881 ui.debug('From: %s\n' % user)
1882 1882 elif line.startswith("# Date "):
1883 1883 date = line[7:]
1884 1884 if not line.startswith('# '):
1885 1885 fp.write(line)
1886 1886 fp.write('\n')
1887 1887 message = fp.getvalue()
1888 1888 if tmpfp:
1889 1889 tmpfp.write(payload)
1890 1890 if not payload.endswith('\n'):
1891 1891 tmpfp.write('\n')
1892 1892 elif not diffs_seen and message and content_type == 'text/plain':
1893 1893 message += '\n' + payload
1894 1894
1895 1895 if opts['message']:
1896 1896 # pickup the cmdline msg
1897 1897 message = opts['message']
1898 1898 elif message:
1899 1899 # pickup the patch msg
1900 1900 message = message.strip()
1901 1901 else:
1902 1902 # launch the editor
1903 1903 message = None
1904 1904 ui.debug(_('message:\n%s\n') % message)
1905 1905
1906 1906 tmpfp.close()
1907 1907 if not diffs_seen:
1908 1908 raise util.Abort(_('no diffs found'))
1909 1909
1910 files = util.patch(strip, tmpname, ui, cwd=repo.root)
1910 files = patch.patch(strip, tmpname, ui, cwd=repo.root)
1911 removes = []
1911 1912 if len(files) > 0:
1912 cfiles = files
1913 cfiles = files.keys()
1914 copies = []
1915 copts = {'after': False, 'force': False}
1913 1916 cwd = repo.getcwd()
1914 1917 if cwd:
1915 cfiles = [util.pathto(cwd, f) for f in files]
1918 cfiles = [util.pathto(cwd, f) for f in files.keys()]
1919 for f in files:
1920 ctype, gp = files[f]
1921 if ctype == 'RENAME':
1922 copies.append((gp.oldpath, gp.path, gp.copymod))
1923 removes.append(gp.oldpath)
1924 elif ctype == 'COPY':
1925 copies.append((gp.oldpath, gp.path, gp.copymod))
1926 elif ctype == 'DELETE':
1927 removes.append(gp.path)
1928 for src, dst, after in copies:
1929 absdst = os.path.join(repo.root, dst)
1930 if not after and os.path.exists(absdst):
1931 raise util.Abort(_('patch creates existing file %s') % dst)
1932 if cwd:
1933 src, dst = [util.pathto(cwd, f) for f in (src, dst)]
1934 copts['after'] = after
1935 errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock)
1936 if errs:
1937 raise util.Abort(errs)
1938 if removes:
1939 repo.remove(removes, True, wlock=wlock)
1940 for f in files:
1941 ctype, gp = files[f]
1942 if gp and gp.mode:
1943 x = gp.mode & 0100 != 0
1944 dst = os.path.join(repo.root, gp.path)
1945 util.set_exec(dst, x)
1916 1946 addremove_lock(ui, repo, cfiles, {}, wlock=wlock)
1947 files = files.keys()
1948 files.extend([r for r in removes if r not in files])
1917 1949 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1918 1950 finally:
1919 1951 os.unlink(tmpname)
1920 1952
1921 1953 def incoming(ui, repo, source="default", **opts):
1922 1954 """show new changesets found in source
1923 1955
1924 1956 Show new changesets found in the specified path/URL or the default
1925 1957 pull location. These are the changesets that would be pulled if a pull
1926 1958 was requested.
1927 1959
1928 1960 For remote repository, using --bundle avoids downloading the changesets
1929 1961 twice if the incoming is followed by a pull.
1930 1962
1931 1963 See pull for valid source format details.
1932 1964 """
1933 1965 source = ui.expandpath(source)
1934 1966 setremoteconfig(ui, opts)
1935 1967
1936 1968 other = hg.repository(ui, source)
1937 1969 incoming = repo.findincoming(other, force=opts["force"])
1938 1970 if not incoming:
1939 1971 ui.status(_("no changes found\n"))
1940 1972 return
1941 1973
1942 1974 cleanup = None
1943 1975 try:
1944 1976 fname = opts["bundle"]
1945 1977 if fname or not other.local():
1946 1978 # create a bundle (uncompressed if other repo is not local)
1947 1979 cg = other.changegroup(incoming, "incoming")
1948 1980 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1949 1981 # keep written bundle?
1950 1982 if opts["bundle"]:
1951 1983 cleanup = None
1952 1984 if not other.local():
1953 1985 # use the created uncompressed bundlerepo
1954 1986 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1955 1987
1956 1988 revs = None
1957 1989 if opts['rev']:
1958 1990 revs = [other.lookup(rev) for rev in opts['rev']]
1959 1991 o = other.changelog.nodesbetween(incoming, revs)[0]
1960 1992 if opts['newest_first']:
1961 1993 o.reverse()
1962 1994 displayer = show_changeset(ui, other, opts)
1963 1995 for n in o:
1964 1996 parents = [p for p in other.changelog.parents(n) if p != nullid]
1965 1997 if opts['no_merges'] and len(parents) == 2:
1966 1998 continue
1967 1999 displayer.show(changenode=n)
1968 2000 if opts['patch']:
1969 2001 prev = (parents and parents[0]) or nullid
1970 2002 dodiff(ui, ui, other, prev, n)
1971 2003 ui.write("\n")
1972 2004 finally:
1973 2005 if hasattr(other, 'close'):
1974 2006 other.close()
1975 2007 if cleanup:
1976 2008 os.unlink(cleanup)
1977 2009
1978 2010 def init(ui, dest=".", **opts):
1979 2011 """create a new repository in the given directory
1980 2012
1981 2013 Initialize a new repository in the given directory. If the given
1982 2014 directory does not exist, it is created.
1983 2015
1984 2016 If no directory is given, the current directory is used.
1985 2017
1986 2018 It is possible to specify an ssh:// URL as the destination.
1987 2019 Look at the help text for the pull command for important details
1988 2020 about ssh:// URLs.
1989 2021 """
1990 2022 setremoteconfig(ui, opts)
1991 2023 hg.repository(ui, dest, create=1)
1992 2024
1993 2025 def locate(ui, repo, *pats, **opts):
1994 2026 """locate files matching specific patterns
1995 2027
1996 2028 Print all files under Mercurial control whose names match the
1997 2029 given patterns.
1998 2030
1999 2031 This command searches the current directory and its
2000 2032 subdirectories. To search an entire repository, move to the root
2001 2033 of the repository.
2002 2034
2003 2035 If no patterns are given to match, this command prints all file
2004 2036 names.
2005 2037
2006 2038 If you want to feed the output of this command into the "xargs"
2007 2039 command, use the "-0" option to both this command and "xargs".
2008 2040 This will avoid the problem of "xargs" treating single filenames
2009 2041 that contain white space as multiple filenames.
2010 2042 """
2011 2043 end = opts['print0'] and '\0' or '\n'
2012 2044 rev = opts['rev']
2013 2045 if rev:
2014 2046 node = repo.lookup(rev)
2015 2047 else:
2016 2048 node = None
2017 2049
2018 2050 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2019 2051 head='(?:.*/|)'):
2020 2052 if not node and repo.dirstate.state(abs) == '?':
2021 2053 continue
2022 2054 if opts['fullpath']:
2023 2055 ui.write(os.path.join(repo.root, abs), end)
2024 2056 else:
2025 2057 ui.write(((pats and rel) or abs), end)
2026 2058
2027 2059 def log(ui, repo, *pats, **opts):
2028 2060 """show revision history of entire repository or files
2029 2061
2030 2062 Print the revision history of the specified files or the entire
2031 2063 project.
2032 2064
2033 2065 File history is shown without following rename or copy history of
2034 2066 files. Use -f/--follow with a file name to follow history across
2035 2067 renames and copies. --follow without a file name will only show
2036 2068 ancestors or descendants of the starting revision. --follow-first
2037 2069 only follows the first parent of merge revisions.
2038 2070
2039 2071 If no revision range is specified, the default is tip:0 unless
2040 2072 --follow is set, in which case the working directory parent is
2041 2073 used as the starting revision.
2042 2074
2043 2075 By default this command outputs: changeset id and hash, tags,
2044 2076 non-trivial parents, user, date and time, and a summary for each
2045 2077 commit. When the -v/--verbose switch is used, the list of changed
2046 2078 files and full commit message is shown.
2047 2079 """
2048 2080 class dui(object):
2049 2081 # Implement and delegate some ui protocol. Save hunks of
2050 2082 # output for later display in the desired order.
2051 2083 def __init__(self, ui):
2052 2084 self.ui = ui
2053 2085 self.hunk = {}
2054 2086 self.header = {}
2055 2087 def bump(self, rev):
2056 2088 self.rev = rev
2057 2089 self.hunk[rev] = []
2058 2090 self.header[rev] = []
2059 2091 def note(self, *args):
2060 2092 if self.verbose:
2061 2093 self.write(*args)
2062 2094 def status(self, *args):
2063 2095 if not self.quiet:
2064 2096 self.write(*args)
2065 2097 def write(self, *args):
2066 2098 self.hunk[self.rev].append(args)
2067 2099 def write_header(self, *args):
2068 2100 self.header[self.rev].append(args)
2069 2101 def debug(self, *args):
2070 2102 if self.debugflag:
2071 2103 self.write(*args)
2072 2104 def __getattr__(self, key):
2073 2105 return getattr(self.ui, key)
2074 2106
2075 2107 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
2076 2108
2077 2109 if opts['limit']:
2078 2110 try:
2079 2111 limit = int(opts['limit'])
2080 2112 except ValueError:
2081 2113 raise util.Abort(_('limit must be a positive integer'))
2082 2114 if limit <= 0: raise util.Abort(_('limit must be positive'))
2083 2115 else:
2084 2116 limit = sys.maxint
2085 2117 count = 0
2086 2118
2087 2119 displayer = show_changeset(ui, repo, opts)
2088 2120 for st, rev, fns in changeiter:
2089 2121 if st == 'window':
2090 2122 du = dui(ui)
2091 2123 displayer.ui = du
2092 2124 elif st == 'add':
2093 2125 du.bump(rev)
2094 2126 changenode = repo.changelog.node(rev)
2095 2127 parents = [p for p in repo.changelog.parents(changenode)
2096 2128 if p != nullid]
2097 2129 if opts['no_merges'] and len(parents) == 2:
2098 2130 continue
2099 2131 if opts['only_merges'] and len(parents) != 2:
2100 2132 continue
2101 2133
2102 2134 if opts['keyword']:
2103 2135 changes = getchange(rev)
2104 2136 miss = 0
2105 2137 for k in [kw.lower() for kw in opts['keyword']]:
2106 2138 if not (k in changes[1].lower() or
2107 2139 k in changes[4].lower() or
2108 2140 k in " ".join(changes[3][:20]).lower()):
2109 2141 miss = 1
2110 2142 break
2111 2143 if miss:
2112 2144 continue
2113 2145
2114 2146 br = None
2115 2147 if opts['branches']:
2116 2148 br = repo.branchlookup([repo.changelog.node(rev)])
2117 2149
2118 2150 displayer.show(rev, brinfo=br)
2119 2151 if opts['patch']:
2120 2152 prev = (parents and parents[0]) or nullid
2121 2153 dodiff(du, du, repo, prev, changenode, match=matchfn)
2122 2154 du.write("\n\n")
2123 2155 elif st == 'iter':
2124 2156 if count == limit: break
2125 2157 if du.header[rev]:
2126 2158 for args in du.header[rev]:
2127 2159 ui.write_header(*args)
2128 2160 if du.hunk[rev]:
2129 2161 count += 1
2130 2162 for args in du.hunk[rev]:
2131 2163 ui.write(*args)
2132 2164
2133 2165 def manifest(ui, repo, rev=None):
2134 2166 """output the latest or given revision of the project manifest
2135 2167
2136 2168 Print a list of version controlled files for the given revision.
2137 2169
2138 2170 The manifest is the list of files being version controlled. If no revision
2139 2171 is given then the tip is used.
2140 2172 """
2141 2173 if rev:
2142 2174 try:
2143 2175 # assume all revision numbers are for changesets
2144 2176 n = repo.lookup(rev)
2145 2177 change = repo.changelog.read(n)
2146 2178 n = change[0]
2147 2179 except hg.RepoError:
2148 2180 n = repo.manifest.lookup(rev)
2149 2181 else:
2150 2182 n = repo.manifest.tip()
2151 2183 m = repo.manifest.read(n)
2152 2184 files = m.keys()
2153 2185 files.sort()
2154 2186
2155 2187 for f in files:
2156 2188 ui.write("%40s %3s %s\n" % (hex(m[f]),
2157 2189 m.execf(f) and "755" or "644", f))
2158 2190
2159 2191 def merge(ui, repo, node=None, force=None, branch=None):
2160 2192 """Merge working directory with another revision
2161 2193
2162 2194 Merge the contents of the current working directory and the
2163 2195 requested revision. Files that changed between either parent are
2164 2196 marked as changed for the next commit and a commit must be
2165 2197 performed before any further updates are allowed.
2166 2198 """
2167 2199
2168 2200 node = _lookup(repo, node, branch)
2169 2201 return hg.merge(repo, node, force=force)
2170 2202
2171 2203 def outgoing(ui, repo, dest=None, **opts):
2172 2204 """show changesets not found in destination
2173 2205
2174 2206 Show changesets not found in the specified destination repository or
2175 2207 the default push location. These are the changesets that would be pushed
2176 2208 if a push was requested.
2177 2209
2178 2210 See pull for valid destination format details.
2179 2211 """
2180 2212 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2181 2213 setremoteconfig(ui, opts)
2182 2214 revs = None
2183 2215 if opts['rev']:
2184 2216 revs = [repo.lookup(rev) for rev in opts['rev']]
2185 2217
2186 2218 other = hg.repository(ui, dest)
2187 2219 o = repo.findoutgoing(other, force=opts['force'])
2188 2220 if not o:
2189 2221 ui.status(_("no changes found\n"))
2190 2222 return
2191 2223 o = repo.changelog.nodesbetween(o, revs)[0]
2192 2224 if opts['newest_first']:
2193 2225 o.reverse()
2194 2226 displayer = show_changeset(ui, repo, opts)
2195 2227 for n in o:
2196 2228 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2197 2229 if opts['no_merges'] and len(parents) == 2:
2198 2230 continue
2199 2231 displayer.show(changenode=n)
2200 2232 if opts['patch']:
2201 2233 prev = (parents and parents[0]) or nullid
2202 2234 dodiff(ui, ui, repo, prev, n)
2203 2235 ui.write("\n")
2204 2236
2205 2237 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2206 2238 """show the parents of the working dir or revision
2207 2239
2208 2240 Print the working directory's parent revisions.
2209 2241 """
2210 2242 # legacy
2211 2243 if file_ and not rev:
2212 2244 try:
2213 2245 rev = repo.lookup(file_)
2214 2246 file_ = None
2215 2247 except hg.RepoError:
2216 2248 pass
2217 2249 else:
2218 2250 ui.warn(_("'hg parent REV' is deprecated, "
2219 2251 "please use 'hg parents -r REV instead\n"))
2220 2252
2221 2253 if rev:
2222 2254 if file_:
2223 2255 ctx = repo.filectx(file_, changeid=rev)
2224 2256 else:
2225 2257 ctx = repo.changectx(rev)
2226 2258 p = [cp.node() for cp in ctx.parents()]
2227 2259 else:
2228 2260 p = repo.dirstate.parents()
2229 2261
2230 2262 br = None
2231 2263 if branches is not None:
2232 2264 br = repo.branchlookup(p)
2233 2265 displayer = show_changeset(ui, repo, opts)
2234 2266 for n in p:
2235 2267 if n != nullid:
2236 2268 displayer.show(changenode=n, brinfo=br)
2237 2269
2238 2270 def paths(ui, repo, search=None):
2239 2271 """show definition of symbolic path names
2240 2272
2241 2273 Show definition of symbolic path name NAME. If no name is given, show
2242 2274 definition of available names.
2243 2275
2244 2276 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2245 2277 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2246 2278 """
2247 2279 if search:
2248 2280 for name, path in ui.configitems("paths"):
2249 2281 if name == search:
2250 2282 ui.write("%s\n" % path)
2251 2283 return
2252 2284 ui.warn(_("not found!\n"))
2253 2285 return 1
2254 2286 else:
2255 2287 for name, path in ui.configitems("paths"):
2256 2288 ui.write("%s = %s\n" % (name, path))
2257 2289
2258 2290 def postincoming(ui, repo, modheads, optupdate):
2259 2291 if modheads == 0:
2260 2292 return
2261 2293 if optupdate:
2262 2294 if modheads == 1:
2263 2295 return hg.update(repo, repo.changelog.tip()) # update
2264 2296 else:
2265 2297 ui.status(_("not updating, since new heads added\n"))
2266 2298 if modheads > 1:
2267 2299 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2268 2300 else:
2269 2301 ui.status(_("(run 'hg update' to get a working copy)\n"))
2270 2302
2271 2303 def pull(ui, repo, source="default", **opts):
2272 2304 """pull changes from the specified source
2273 2305
2274 2306 Pull changes from a remote repository to a local one.
2275 2307
2276 2308 This finds all changes from the repository at the specified path
2277 2309 or URL and adds them to the local repository. By default, this
2278 2310 does not update the copy of the project in the working directory.
2279 2311
2280 2312 Valid URLs are of the form:
2281 2313
2282 2314 local/filesystem/path
2283 2315 http://[user@]host[:port]/[path]
2284 2316 https://[user@]host[:port]/[path]
2285 2317 ssh://[user@]host[:port]/[path]
2286 2318
2287 2319 Some notes about using SSH with Mercurial:
2288 2320 - SSH requires an accessible shell account on the destination machine
2289 2321 and a copy of hg in the remote path or specified with as remotecmd.
2290 2322 - path is relative to the remote user's home directory by default.
2291 2323 Use an extra slash at the start of a path to specify an absolute path:
2292 2324 ssh://example.com//tmp/repository
2293 2325 - Mercurial doesn't use its own compression via SSH; the right thing
2294 2326 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2295 2327 Host *.mylocalnetwork.example.com
2296 2328 Compression off
2297 2329 Host *
2298 2330 Compression on
2299 2331 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2300 2332 with the --ssh command line option.
2301 2333 """
2302 2334 source = ui.expandpath(source)
2303 2335 setremoteconfig(ui, opts)
2304 2336
2305 2337 other = hg.repository(ui, source)
2306 2338 ui.status(_('pulling from %s\n') % (source))
2307 2339 revs = None
2308 2340 if opts['rev'] and not other.local():
2309 2341 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2310 2342 elif opts['rev']:
2311 2343 revs = [other.lookup(rev) for rev in opts['rev']]
2312 2344 modheads = repo.pull(other, heads=revs, force=opts['force'])
2313 2345 return postincoming(ui, repo, modheads, opts['update'])
2314 2346
2315 2347 def push(ui, repo, dest=None, **opts):
2316 2348 """push changes to the specified destination
2317 2349
2318 2350 Push changes from the local repository to the given destination.
2319 2351
2320 2352 This is the symmetrical operation for pull. It helps to move
2321 2353 changes from the current repository to a different one. If the
2322 2354 destination is local this is identical to a pull in that directory
2323 2355 from the current one.
2324 2356
2325 2357 By default, push will refuse to run if it detects the result would
2326 2358 increase the number of remote heads. This generally indicates the
2327 2359 the client has forgotten to sync and merge before pushing.
2328 2360
2329 2361 Valid URLs are of the form:
2330 2362
2331 2363 local/filesystem/path
2332 2364 ssh://[user@]host[:port]/[path]
2333 2365
2334 2366 Look at the help text for the pull command for important details
2335 2367 about ssh:// URLs.
2336 2368
2337 2369 Pushing to http:// and https:// URLs is possible, too, if this
2338 2370 feature is enabled on the remote Mercurial server.
2339 2371 """
2340 2372 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2341 2373 setremoteconfig(ui, opts)
2342 2374
2343 2375 other = hg.repository(ui, dest)
2344 2376 ui.status('pushing to %s\n' % (dest))
2345 2377 revs = None
2346 2378 if opts['rev']:
2347 2379 revs = [repo.lookup(rev) for rev in opts['rev']]
2348 2380 r = repo.push(other, opts['force'], revs=revs)
2349 2381 return r == 0
2350 2382
2351 2383 def rawcommit(ui, repo, *flist, **rc):
2352 2384 """raw commit interface (DEPRECATED)
2353 2385
2354 2386 (DEPRECATED)
2355 2387 Lowlevel commit, for use in helper scripts.
2356 2388
2357 2389 This command is not intended to be used by normal users, as it is
2358 2390 primarily useful for importing from other SCMs.
2359 2391
2360 2392 This command is now deprecated and will be removed in a future
2361 2393 release, please use debugsetparents and commit instead.
2362 2394 """
2363 2395
2364 2396 ui.warn(_("(the rawcommit command is deprecated)\n"))
2365 2397
2366 2398 message = rc['message']
2367 2399 if not message and rc['logfile']:
2368 2400 try:
2369 2401 message = open(rc['logfile']).read()
2370 2402 except IOError:
2371 2403 pass
2372 2404 if not message and not rc['logfile']:
2373 2405 raise util.Abort(_("missing commit message"))
2374 2406
2375 2407 files = relpath(repo, list(flist))
2376 2408 if rc['files']:
2377 2409 files += open(rc['files']).read().splitlines()
2378 2410
2379 2411 rc['parent'] = map(repo.lookup, rc['parent'])
2380 2412
2381 2413 try:
2382 2414 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2383 2415 except ValueError, inst:
2384 2416 raise util.Abort(str(inst))
2385 2417
2386 2418 def recover(ui, repo):
2387 2419 """roll back an interrupted transaction
2388 2420
2389 2421 Recover from an interrupted commit or pull.
2390 2422
2391 2423 This command tries to fix the repository status after an interrupted
2392 2424 operation. It should only be necessary when Mercurial suggests it.
2393 2425 """
2394 2426 if repo.recover():
2395 2427 return hg.verify(repo)
2396 2428 return 1
2397 2429
2398 2430 def remove(ui, repo, *pats, **opts):
2399 2431 """remove the specified files on the next commit
2400 2432
2401 2433 Schedule the indicated files for removal from the repository.
2402 2434
2403 2435 This command schedules the files to be removed at the next commit.
2404 2436 This only removes files from the current branch, not from the
2405 2437 entire project history. If the files still exist in the working
2406 2438 directory, they will be deleted from it. If invoked with --after,
2407 2439 files that have been manually deleted are marked as removed.
2408 2440
2409 2441 Modified files and added files are not removed by default. To
2410 2442 remove them, use the -f/--force option.
2411 2443 """
2412 2444 names = []
2413 2445 if not opts['after'] and not pats:
2414 2446 raise util.Abort(_('no files specified'))
2415 2447 files, matchfn, anypats = matchpats(repo, pats, opts)
2416 2448 exact = dict.fromkeys(files)
2417 2449 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2418 2450 modified, added, removed, deleted, unknown = mardu
2419 2451 remove, forget = [], []
2420 2452 for src, abs, rel, exact in walk(repo, pats, opts):
2421 2453 reason = None
2422 2454 if abs not in deleted and opts['after']:
2423 2455 reason = _('is still present')
2424 2456 elif abs in modified and not opts['force']:
2425 2457 reason = _('is modified (use -f to force removal)')
2426 2458 elif abs in added:
2427 2459 if opts['force']:
2428 2460 forget.append(abs)
2429 2461 continue
2430 2462 reason = _('has been marked for add (use -f to force removal)')
2431 2463 elif abs in unknown:
2432 2464 reason = _('is not managed')
2433 2465 elif abs in removed:
2434 2466 continue
2435 2467 if reason:
2436 2468 if exact:
2437 2469 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2438 2470 else:
2439 2471 if ui.verbose or not exact:
2440 2472 ui.status(_('removing %s\n') % rel)
2441 2473 remove.append(abs)
2442 2474 repo.forget(forget)
2443 2475 repo.remove(remove, unlink=not opts['after'])
2444 2476
2445 2477 def rename(ui, repo, *pats, **opts):
2446 2478 """rename files; equivalent of copy + remove
2447 2479
2448 2480 Mark dest as copies of sources; mark sources for deletion. If
2449 2481 dest is a directory, copies are put in that directory. If dest is
2450 2482 a file, there can only be one source.
2451 2483
2452 2484 By default, this command copies the contents of files as they
2453 2485 stand in the working directory. If invoked with --after, the
2454 2486 operation is recorded, but no copying is performed.
2455 2487
2456 2488 This command takes effect in the next commit.
2457 2489
2458 2490 NOTE: This command should be treated as experimental. While it
2459 2491 should properly record rename files, this information is not yet
2460 2492 fully used by merge, nor fully reported by log.
2461 2493 """
2462 2494 wlock = repo.wlock(0)
2463 2495 errs, copied = docopy(ui, repo, pats, opts, wlock)
2464 2496 names = []
2465 2497 for abs, rel, exact in copied:
2466 2498 if ui.verbose or not exact:
2467 2499 ui.status(_('removing %s\n') % rel)
2468 2500 names.append(abs)
2469 2501 if not opts.get('dry_run'):
2470 2502 repo.remove(names, True, wlock)
2471 2503 return errs
2472 2504
2473 2505 def revert(ui, repo, *pats, **opts):
2474 2506 """revert files or dirs to their states as of some revision
2475 2507
2476 2508 With no revision specified, revert the named files or directories
2477 2509 to the contents they had in the parent of the working directory.
2478 2510 This restores the contents of the affected files to an unmodified
2479 2511 state. If the working directory has two parents, you must
2480 2512 explicitly specify the revision to revert to.
2481 2513
2482 2514 Modified files are saved with a .orig suffix before reverting.
2483 2515 To disable these backups, use --no-backup.
2484 2516
2485 2517 Using the -r option, revert the given files or directories to
2486 2518 their contents as of a specific revision. This can be helpful to"roll
2487 2519 back" some or all of a change that should not have been committed.
2488 2520
2489 2521 Revert modifies the working directory. It does not commit any
2490 2522 changes, or change the parent of the working directory. If you
2491 2523 revert to a revision other than the parent of the working
2492 2524 directory, the reverted files will thus appear modified
2493 2525 afterwards.
2494 2526
2495 2527 If a file has been deleted, it is recreated. If the executable
2496 2528 mode of a file was changed, it is reset.
2497 2529
2498 2530 If names are given, all files matching the names are reverted.
2499 2531
2500 2532 If no arguments are given, all files in the repository are reverted.
2501 2533 """
2502 2534 parent, p2 = repo.dirstate.parents()
2503 2535 if opts['rev']:
2504 2536 node = repo.lookup(opts['rev'])
2505 2537 elif p2 != nullid:
2506 2538 raise util.Abort(_('working dir has two parents; '
2507 2539 'you must specify the revision to revert to'))
2508 2540 else:
2509 2541 node = parent
2510 2542 mf = repo.manifest.read(repo.changelog.read(node)[0])
2511 2543 if node == parent:
2512 2544 pmf = mf
2513 2545 else:
2514 2546 pmf = None
2515 2547
2516 2548 wlock = repo.wlock()
2517 2549
2518 2550 # need all matching names in dirstate and manifest of target rev,
2519 2551 # so have to walk both. do not print errors if files exist in one
2520 2552 # but not other.
2521 2553
2522 2554 names = {}
2523 2555 target_only = {}
2524 2556
2525 2557 # walk dirstate.
2526 2558
2527 2559 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2528 2560 names[abs] = (rel, exact)
2529 2561 if src == 'b':
2530 2562 target_only[abs] = True
2531 2563
2532 2564 # walk target manifest.
2533 2565
2534 2566 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2535 2567 badmatch=names.has_key):
2536 2568 if abs in names: continue
2537 2569 names[abs] = (rel, exact)
2538 2570 target_only[abs] = True
2539 2571
2540 2572 changes = repo.changes(match=names.has_key, wlock=wlock)
2541 2573 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2542 2574
2543 2575 revert = ([], _('reverting %s\n'))
2544 2576 add = ([], _('adding %s\n'))
2545 2577 remove = ([], _('removing %s\n'))
2546 2578 forget = ([], _('forgetting %s\n'))
2547 2579 undelete = ([], _('undeleting %s\n'))
2548 2580 update = {}
2549 2581
2550 2582 disptable = (
2551 2583 # dispatch table:
2552 2584 # file state
2553 2585 # action if in target manifest
2554 2586 # action if not in target manifest
2555 2587 # make backup if in target manifest
2556 2588 # make backup if not in target manifest
2557 2589 (modified, revert, remove, True, True),
2558 2590 (added, revert, forget, True, False),
2559 2591 (removed, undelete, None, False, False),
2560 2592 (deleted, revert, remove, False, False),
2561 2593 (unknown, add, None, True, False),
2562 2594 (target_only, add, None, False, False),
2563 2595 )
2564 2596
2565 2597 entries = names.items()
2566 2598 entries.sort()
2567 2599
2568 2600 for abs, (rel, exact) in entries:
2569 2601 mfentry = mf.get(abs)
2570 2602 def handle(xlist, dobackup):
2571 2603 xlist[0].append(abs)
2572 2604 update[abs] = 1
2573 2605 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2574 2606 bakname = "%s.orig" % rel
2575 2607 ui.note(_('saving current version of %s as %s\n') %
2576 2608 (rel, bakname))
2577 2609 if not opts.get('dry_run'):
2578 2610 shutil.copyfile(rel, bakname)
2579 2611 shutil.copymode(rel, bakname)
2580 2612 if ui.verbose or not exact:
2581 2613 ui.status(xlist[1] % rel)
2582 2614 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2583 2615 if abs not in table: continue
2584 2616 # file has changed in dirstate
2585 2617 if mfentry:
2586 2618 handle(hitlist, backuphit)
2587 2619 elif misslist is not None:
2588 2620 handle(misslist, backupmiss)
2589 2621 else:
2590 2622 if exact: ui.warn(_('file not managed: %s\n' % rel))
2591 2623 break
2592 2624 else:
2593 2625 # file has not changed in dirstate
2594 2626 if node == parent:
2595 2627 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2596 2628 continue
2597 2629 if pmf is None:
2598 2630 # only need parent manifest in this unlikely case,
2599 2631 # so do not read by default
2600 2632 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2601 2633 if abs in pmf:
2602 2634 if mfentry:
2603 2635 # if version of file is same in parent and target
2604 2636 # manifests, do nothing
2605 2637 if pmf[abs] != mfentry:
2606 2638 handle(revert, False)
2607 2639 else:
2608 2640 handle(remove, False)
2609 2641
2610 2642 if not opts.get('dry_run'):
2611 2643 repo.dirstate.forget(forget[0])
2612 2644 r = hg.revert(repo, node, update.has_key, wlock)
2613 2645 repo.dirstate.update(add[0], 'a')
2614 2646 repo.dirstate.update(undelete[0], 'n')
2615 2647 repo.dirstate.update(remove[0], 'r')
2616 2648 return r
2617 2649
2618 2650 def rollback(ui, repo):
2619 2651 """roll back the last transaction in this repository
2620 2652
2621 2653 Roll back the last transaction in this repository, restoring the
2622 2654 project to its state prior to the transaction.
2623 2655
2624 2656 Transactions are used to encapsulate the effects of all commands
2625 2657 that create new changesets or propagate existing changesets into a
2626 2658 repository. For example, the following commands are transactional,
2627 2659 and their effects can be rolled back:
2628 2660
2629 2661 commit
2630 2662 import
2631 2663 pull
2632 2664 push (with this repository as destination)
2633 2665 unbundle
2634 2666
2635 2667 This command should be used with care. There is only one level of
2636 2668 rollback, and there is no way to undo a rollback.
2637 2669
2638 2670 This command is not intended for use on public repositories. Once
2639 2671 changes are visible for pull by other users, rolling a transaction
2640 2672 back locally is ineffective (someone else may already have pulled
2641 2673 the changes). Furthermore, a race is possible with readers of the
2642 2674 repository; for example an in-progress pull from the repository
2643 2675 may fail if a rollback is performed.
2644 2676 """
2645 2677 repo.rollback()
2646 2678
2647 2679 def root(ui, repo):
2648 2680 """print the root (top) of the current working dir
2649 2681
2650 2682 Print the root directory of the current repository.
2651 2683 """
2652 2684 ui.write(repo.root + "\n")
2653 2685
2654 2686 def serve(ui, repo, **opts):
2655 2687 """export the repository via HTTP
2656 2688
2657 2689 Start a local HTTP repository browser and pull server.
2658 2690
2659 2691 By default, the server logs accesses to stdout and errors to
2660 2692 stderr. Use the "-A" and "-E" options to log to files.
2661 2693 """
2662 2694
2663 2695 if opts["stdio"]:
2664 2696 if repo is None:
2665 2697 raise hg.RepoError(_('no repo found'))
2666 2698 s = sshserver.sshserver(ui, repo)
2667 2699 s.serve_forever()
2668 2700
2669 2701 optlist = ("name templates style address port ipv6"
2670 2702 " accesslog errorlog webdir_conf")
2671 2703 for o in optlist.split():
2672 2704 if opts[o]:
2673 2705 ui.setconfig("web", o, opts[o])
2674 2706
2675 2707 if repo is None and not ui.config("web", "webdir_conf"):
2676 2708 raise hg.RepoError(_('no repo found'))
2677 2709
2678 2710 if opts['daemon'] and not opts['daemon_pipefds']:
2679 2711 rfd, wfd = os.pipe()
2680 2712 args = sys.argv[:]
2681 2713 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2682 2714 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2683 2715 args[0], args)
2684 2716 os.close(wfd)
2685 2717 os.read(rfd, 1)
2686 2718 os._exit(0)
2687 2719
2688 2720 try:
2689 2721 httpd = hgweb.server.create_server(ui, repo)
2690 2722 except socket.error, inst:
2691 2723 raise util.Abort(_('cannot start server: ') + inst.args[1])
2692 2724
2693 2725 if ui.verbose:
2694 2726 addr, port = httpd.socket.getsockname()
2695 2727 if addr == '0.0.0.0':
2696 2728 addr = socket.gethostname()
2697 2729 else:
2698 2730 try:
2699 2731 addr = socket.gethostbyaddr(addr)[0]
2700 2732 except socket.error:
2701 2733 pass
2702 2734 if port != 80:
2703 2735 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2704 2736 else:
2705 2737 ui.status(_('listening at http://%s/\n') % addr)
2706 2738
2707 2739 if opts['pid_file']:
2708 2740 fp = open(opts['pid_file'], 'w')
2709 2741 fp.write(str(os.getpid()) + '\n')
2710 2742 fp.close()
2711 2743
2712 2744 if opts['daemon_pipefds']:
2713 2745 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2714 2746 os.close(rfd)
2715 2747 os.write(wfd, 'y')
2716 2748 os.close(wfd)
2717 2749 sys.stdout.flush()
2718 2750 sys.stderr.flush()
2719 2751 fd = os.open(util.nulldev, os.O_RDWR)
2720 2752 if fd != 0: os.dup2(fd, 0)
2721 2753 if fd != 1: os.dup2(fd, 1)
2722 2754 if fd != 2: os.dup2(fd, 2)
2723 2755 if fd not in (0, 1, 2): os.close(fd)
2724 2756
2725 2757 httpd.serve_forever()
2726 2758
2727 2759 def status(ui, repo, *pats, **opts):
2728 2760 """show changed files in the working directory
2729 2761
2730 2762 Show status of files in the repository. If names are given, only
2731 2763 files that match are shown. Files that are clean or ignored, are
2732 2764 not listed unless -c (clean), -i (ignored) or -A is given.
2733 2765
2734 2766 The codes used to show the status of files are:
2735 2767 M = modified
2736 2768 A = added
2737 2769 R = removed
2738 2770 C = clean
2739 2771 ! = deleted, but still tracked
2740 2772 ? = not tracked
2741 2773 I = ignored (not shown by default)
2742 2774 = the previous added file was copied from here
2743 2775 """
2744 2776
2745 2777 all = opts['all']
2746 2778
2747 2779 files, matchfn, anypats = matchpats(repo, pats, opts)
2748 2780 cwd = (pats and repo.getcwd()) or ''
2749 2781 modified, added, removed, deleted, unknown, ignored, clean = [
2750 2782 [util.pathto(cwd, x) for x in n]
2751 2783 for n in repo.status(files=files, match=matchfn,
2752 2784 list_ignored=all or opts['ignored'],
2753 2785 list_clean=all or opts['clean'])]
2754 2786
2755 2787 changetypes = (('modified', 'M', modified),
2756 2788 ('added', 'A', added),
2757 2789 ('removed', 'R', removed),
2758 2790 ('deleted', '!', deleted),
2759 2791 ('unknown', '?', unknown),
2760 2792 ('ignored', 'I', ignored))
2761 2793
2762 2794 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2763 2795
2764 2796 end = opts['print0'] and '\0' or '\n'
2765 2797
2766 2798 for opt, char, changes in ([ct for ct in explicit_changetypes
2767 2799 if all or opts[ct[0]]]
2768 2800 or changetypes):
2769 2801 if opts['no_status']:
2770 2802 format = "%%s%s" % end
2771 2803 else:
2772 2804 format = "%s %%s%s" % (char, end)
2773 2805
2774 2806 for f in changes:
2775 2807 ui.write(format % f)
2776 2808 if ((all or opts.get('copies')) and not opts.get('no_status')
2777 2809 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2778 2810 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2779 2811
2780 2812 def tag(ui, repo, name, rev_=None, **opts):
2781 2813 """add a tag for the current tip or a given revision
2782 2814
2783 2815 Name a particular revision using <name>.
2784 2816
2785 2817 Tags are used to name particular revisions of the repository and are
2786 2818 very useful to compare different revision, to go back to significant
2787 2819 earlier versions or to mark branch points as releases, etc.
2788 2820
2789 2821 If no revision is given, the parent of the working directory is used.
2790 2822
2791 2823 To facilitate version control, distribution, and merging of tags,
2792 2824 they are stored as a file named ".hgtags" which is managed
2793 2825 similarly to other project files and can be hand-edited if
2794 2826 necessary. The file '.hg/localtags' is used for local tags (not
2795 2827 shared among repositories).
2796 2828 """
2797 2829 if name in ['tip', '.']:
2798 2830 raise util.Abort(_("the name '%s' is reserved") % name)
2799 2831 if rev_ is not None:
2800 2832 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2801 2833 "please use 'hg tag [-r REV] NAME' instead\n"))
2802 2834 if opts['rev']:
2803 2835 raise util.Abort(_("use only one form to specify the revision"))
2804 2836 if opts['rev']:
2805 2837 rev_ = opts['rev']
2806 2838 if rev_:
2807 2839 r = hex(repo.lookup(rev_))
2808 2840 else:
2809 2841 p1, p2 = repo.dirstate.parents()
2810 2842 if p1 == nullid:
2811 2843 raise util.Abort(_('no revision to tag'))
2812 2844 if p2 != nullid:
2813 2845 raise util.Abort(_('outstanding uncommitted merges'))
2814 2846 r = hex(p1)
2815 2847
2816 2848 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2817 2849 opts['date'])
2818 2850
2819 2851 def tags(ui, repo):
2820 2852 """list repository tags
2821 2853
2822 2854 List the repository tags.
2823 2855
2824 2856 This lists both regular and local tags.
2825 2857 """
2826 2858
2827 2859 l = repo.tagslist()
2828 2860 l.reverse()
2829 2861 for t, n in l:
2830 2862 try:
2831 2863 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2832 2864 except KeyError:
2833 2865 r = " ?:?"
2834 2866 if ui.quiet:
2835 2867 ui.write("%s\n" % t)
2836 2868 else:
2837 2869 ui.write("%-30s %s\n" % (t, r))
2838 2870
2839 2871 def tip(ui, repo, **opts):
2840 2872 """show the tip revision
2841 2873
2842 2874 Show the tip revision.
2843 2875 """
2844 2876 n = repo.changelog.tip()
2845 2877 br = None
2846 2878 if opts['branches']:
2847 2879 br = repo.branchlookup([n])
2848 2880 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2849 2881 if opts['patch']:
2850 2882 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2851 2883
2852 2884 def unbundle(ui, repo, fname, **opts):
2853 2885 """apply a changegroup file
2854 2886
2855 2887 Apply a compressed changegroup file generated by the bundle
2856 2888 command.
2857 2889 """
2858 2890 f = urllib.urlopen(fname)
2859 2891
2860 2892 header = f.read(6)
2861 2893 if not header.startswith("HG"):
2862 2894 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2863 2895 elif not header.startswith("HG10"):
2864 2896 raise util.Abort(_("%s: unknown bundle version") % fname)
2865 2897 elif header == "HG10BZ":
2866 2898 def generator(f):
2867 2899 zd = bz2.BZ2Decompressor()
2868 2900 zd.decompress("BZ")
2869 2901 for chunk in f:
2870 2902 yield zd.decompress(chunk)
2871 2903 elif header == "HG10UN":
2872 2904 def generator(f):
2873 2905 for chunk in f:
2874 2906 yield chunk
2875 2907 else:
2876 2908 raise util.Abort(_("%s: unknown bundle compression type")
2877 2909 % fname)
2878 2910 gen = generator(util.filechunkiter(f, 4096))
2879 2911 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2880 2912 'bundle:' + fname)
2881 2913 return postincoming(ui, repo, modheads, opts['update'])
2882 2914
2883 2915 def undo(ui, repo):
2884 2916 """undo the last commit or pull (DEPRECATED)
2885 2917
2886 2918 (DEPRECATED)
2887 2919 This command is now deprecated and will be removed in a future
2888 2920 release. Please use the rollback command instead. For usage
2889 2921 instructions, see the rollback command.
2890 2922 """
2891 2923 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2892 2924 repo.rollback()
2893 2925
2894 2926 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2895 2927 branch=None):
2896 2928 """update or merge working directory
2897 2929
2898 2930 Update the working directory to the specified revision.
2899 2931
2900 2932 If there are no outstanding changes in the working directory and
2901 2933 there is a linear relationship between the current version and the
2902 2934 requested version, the result is the requested version.
2903 2935
2904 2936 To merge the working directory with another revision, use the
2905 2937 merge command.
2906 2938
2907 2939 By default, update will refuse to run if doing so would require
2908 2940 merging or discarding local changes.
2909 2941 """
2910 2942 node = _lookup(repo, node, branch)
2911 2943 if merge:
2912 2944 ui.warn(_('(the -m/--merge option is deprecated; '
2913 2945 'use the merge command instead)\n'))
2914 2946 return hg.merge(repo, node, force=force)
2915 2947 elif clean:
2916 2948 return hg.clean(repo, node)
2917 2949 else:
2918 2950 return hg.update(repo, node)
2919 2951
2920 2952 def _lookup(repo, node, branch=None):
2921 2953 if branch:
2922 2954 br = repo.branchlookup(branch=branch)
2923 2955 found = []
2924 2956 for x in br:
2925 2957 if branch in br[x]:
2926 2958 found.append(x)
2927 2959 if len(found) > 1:
2928 2960 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2929 2961 for x in found:
2930 2962 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2931 2963 raise util.Abort("")
2932 2964 if len(found) == 1:
2933 2965 node = found[0]
2934 2966 repo.ui.warn(_("Using head %s for branch %s\n")
2935 2967 % (short(node), branch))
2936 2968 else:
2937 2969 raise util.Abort(_("branch %s not found\n") % (branch))
2938 2970 else:
2939 2971 node = node and repo.lookup(node) or repo.changelog.tip()
2940 2972 return node
2941 2973
2942 2974 def verify(ui, repo):
2943 2975 """verify the integrity of the repository
2944 2976
2945 2977 Verify the integrity of the current repository.
2946 2978
2947 2979 This will perform an extensive check of the repository's
2948 2980 integrity, validating the hashes and checksums of each entry in
2949 2981 the changelog, manifest, and tracked files, as well as the
2950 2982 integrity of their crosslinks and indices.
2951 2983 """
2952 2984 return hg.verify(repo)
2953 2985
2954 2986 # Command options and aliases are listed here, alphabetically
2955 2987
2956 2988 table = {
2957 2989 "^add":
2958 2990 (add,
2959 2991 [('I', 'include', [], _('include names matching the given patterns')),
2960 2992 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2961 2993 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2962 2994 _('hg add [OPTION]... [FILE]...')),
2963 2995 "debugaddremove|addremove":
2964 2996 (addremove,
2965 2997 [('I', 'include', [], _('include names matching the given patterns')),
2966 2998 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2967 2999 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2968 3000 _('hg addremove [OPTION]... [FILE]...')),
2969 3001 "^annotate":
2970 3002 (annotate,
2971 3003 [('r', 'rev', '', _('annotate the specified revision')),
2972 3004 ('a', 'text', None, _('treat all files as text')),
2973 3005 ('u', 'user', None, _('list the author')),
2974 3006 ('d', 'date', None, _('list the date')),
2975 3007 ('n', 'number', None, _('list the revision number (default)')),
2976 3008 ('c', 'changeset', None, _('list the changeset')),
2977 3009 ('I', 'include', [], _('include names matching the given patterns')),
2978 3010 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2979 3011 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2980 3012 "archive":
2981 3013 (archive,
2982 3014 [('', 'no-decode', None, _('do not pass files through decoders')),
2983 3015 ('p', 'prefix', '', _('directory prefix for files in archive')),
2984 3016 ('r', 'rev', '', _('revision to distribute')),
2985 3017 ('t', 'type', '', _('type of distribution to create')),
2986 3018 ('I', 'include', [], _('include names matching the given patterns')),
2987 3019 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2988 3020 _('hg archive [OPTION]... DEST')),
2989 3021 "backout":
2990 3022 (backout,
2991 3023 [('', 'merge', None,
2992 3024 _('merge with old dirstate parent after backout')),
2993 3025 ('m', 'message', '', _('use <text> as commit message')),
2994 3026 ('l', 'logfile', '', _('read commit message from <file>')),
2995 3027 ('d', 'date', '', _('record datecode as commit date')),
2996 3028 ('', 'parent', '', _('parent to choose when backing out merge')),
2997 3029 ('u', 'user', '', _('record user as committer')),
2998 3030 ('I', 'include', [], _('include names matching the given patterns')),
2999 3031 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3000 3032 _('hg backout [OPTION]... REV')),
3001 3033 "bundle":
3002 3034 (bundle,
3003 3035 [('f', 'force', None,
3004 3036 _('run even when remote repository is unrelated'))],
3005 3037 _('hg bundle FILE DEST')),
3006 3038 "cat":
3007 3039 (cat,
3008 3040 [('o', 'output', '', _('print output to file with formatted name')),
3009 3041 ('r', 'rev', '', _('print the given revision')),
3010 3042 ('I', 'include', [], _('include names matching the given patterns')),
3011 3043 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3012 3044 _('hg cat [OPTION]... FILE...')),
3013 3045 "^clone":
3014 3046 (clone,
3015 3047 [('U', 'noupdate', None, _('do not update the new working directory')),
3016 3048 ('r', 'rev', [],
3017 3049 _('a changeset you would like to have after cloning')),
3018 3050 ('', 'pull', None, _('use pull protocol to copy metadata')),
3019 3051 ('', 'uncompressed', None,
3020 3052 _('use uncompressed transfer (fast over LAN)')),
3021 3053 ('e', 'ssh', '', _('specify ssh command to use')),
3022 3054 ('', 'remotecmd', '',
3023 3055 _('specify hg command to run on the remote side'))],
3024 3056 _('hg clone [OPTION]... SOURCE [DEST]')),
3025 3057 "^commit|ci":
3026 3058 (commit,
3027 3059 [('A', 'addremove', None,
3028 3060 _('mark new/missing files as added/removed before committing')),
3029 3061 ('m', 'message', '', _('use <text> as commit message')),
3030 3062 ('l', 'logfile', '', _('read the commit message from <file>')),
3031 3063 ('d', 'date', '', _('record datecode as commit date')),
3032 3064 ('u', 'user', '', _('record user as commiter')),
3033 3065 ('I', 'include', [], _('include names matching the given patterns')),
3034 3066 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3035 3067 _('hg commit [OPTION]... [FILE]...')),
3036 3068 "copy|cp":
3037 3069 (copy,
3038 3070 [('A', 'after', None, _('record a copy that has already occurred')),
3039 3071 ('f', 'force', None,
3040 3072 _('forcibly copy over an existing managed file')),
3041 3073 ('I', 'include', [], _('include names matching the given patterns')),
3042 3074 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3043 3075 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3044 3076 _('hg copy [OPTION]... [SOURCE]... DEST')),
3045 3077 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
3046 3078 "debugcomplete":
3047 3079 (debugcomplete,
3048 3080 [('o', 'options', None, _('show the command options'))],
3049 3081 _('debugcomplete [-o] CMD')),
3050 3082 "debugrebuildstate":
3051 3083 (debugrebuildstate,
3052 3084 [('r', 'rev', '', _('revision to rebuild to'))],
3053 3085 _('debugrebuildstate [-r REV] [REV]')),
3054 3086 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
3055 3087 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
3056 3088 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
3057 3089 "debugstate": (debugstate, [], _('debugstate')),
3058 3090 "debugdata": (debugdata, [], _('debugdata FILE REV')),
3059 3091 "debugindex": (debugindex, [], _('debugindex FILE')),
3060 3092 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
3061 3093 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
3062 3094 "debugwalk":
3063 3095 (debugwalk,
3064 3096 [('I', 'include', [], _('include names matching the given patterns')),
3065 3097 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3066 3098 _('debugwalk [OPTION]... [FILE]...')),
3067 3099 "^diff":
3068 3100 (diff,
3069 3101 [('r', 'rev', [], _('revision')),
3070 3102 ('a', 'text', None, _('treat all files as text')),
3071 3103 ('p', 'show-function', None,
3072 3104 _('show which function each change is in')),
3073 3105 ('w', 'ignore-all-space', None,
3074 3106 _('ignore white space when comparing lines')),
3075 3107 ('b', 'ignore-space-change', None,
3076 3108 _('ignore changes in the amount of white space')),
3077 3109 ('B', 'ignore-blank-lines', None,
3078 3110 _('ignore changes whose lines are all blank')),
3079 3111 ('I', 'include', [], _('include names matching the given patterns')),
3080 3112 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3081 3113 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
3082 3114 "^export":
3083 3115 (export,
3084 3116 [('o', 'output', '', _('print output to file with formatted name')),
3085 3117 ('a', 'text', None, _('treat all files as text')),
3086 3118 ('', 'switch-parent', None, _('diff against the second parent'))],
3087 3119 _('hg export [-a] [-o OUTFILESPEC] REV...')),
3088 3120 "debugforget|forget":
3089 3121 (forget,
3090 3122 [('I', 'include', [], _('include names matching the given patterns')),
3091 3123 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3092 3124 _('hg forget [OPTION]... FILE...')),
3093 3125 "grep":
3094 3126 (grep,
3095 3127 [('0', 'print0', None, _('end fields with NUL')),
3096 3128 ('', 'all', None, _('print all revisions that match')),
3097 3129 ('i', 'ignore-case', None, _('ignore case when matching')),
3098 3130 ('l', 'files-with-matches', None,
3099 3131 _('print only filenames and revs that match')),
3100 3132 ('n', 'line-number', None, _('print matching line numbers')),
3101 3133 ('r', 'rev', [], _('search in given revision range')),
3102 3134 ('u', 'user', None, _('print user who committed change')),
3103 3135 ('I', 'include', [], _('include names matching the given patterns')),
3104 3136 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3105 3137 _('hg grep [OPTION]... PATTERN [FILE]...')),
3106 3138 "heads":
3107 3139 (heads,
3108 3140 [('b', 'branches', None, _('show branches')),
3109 3141 ('', 'style', '', _('display using template map file')),
3110 3142 ('r', 'rev', '', _('show only heads which are descendants of rev')),
3111 3143 ('', 'template', '', _('display with template'))],
3112 3144 _('hg heads [-b] [-r <rev>]')),
3113 3145 "help": (help_, [], _('hg help [COMMAND]')),
3114 3146 "identify|id": (identify, [], _('hg identify')),
3115 3147 "import|patch":
3116 3148 (import_,
3117 3149 [('p', 'strip', 1,
3118 3150 _('directory strip option for patch. This has the same\n'
3119 3151 'meaning as the corresponding patch option')),
3120 3152 ('m', 'message', '', _('use <text> as commit message')),
3121 3153 ('b', 'base', '', _('base path')),
3122 3154 ('f', 'force', None,
3123 3155 _('skip check for outstanding uncommitted changes'))],
3124 3156 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3125 3157 "incoming|in": (incoming,
3126 3158 [('M', 'no-merges', None, _('do not show merges')),
3127 3159 ('f', 'force', None,
3128 3160 _('run even when remote repository is unrelated')),
3129 3161 ('', 'style', '', _('display using template map file')),
3130 3162 ('n', 'newest-first', None, _('show newest record first')),
3131 3163 ('', 'bundle', '', _('file to store the bundles into')),
3132 3164 ('p', 'patch', None, _('show patch')),
3133 3165 ('r', 'rev', [], _('a specific revision you would like to pull')),
3134 3166 ('', 'template', '', _('display with template')),
3135 3167 ('e', 'ssh', '', _('specify ssh command to use')),
3136 3168 ('', 'remotecmd', '',
3137 3169 _('specify hg command to run on the remote side'))],
3138 3170 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3139 3171 ' [--bundle FILENAME] [SOURCE]')),
3140 3172 "^init":
3141 3173 (init,
3142 3174 [('e', 'ssh', '', _('specify ssh command to use')),
3143 3175 ('', 'remotecmd', '',
3144 3176 _('specify hg command to run on the remote side'))],
3145 3177 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
3146 3178 "locate":
3147 3179 (locate,
3148 3180 [('r', 'rev', '', _('search the repository as it stood at rev')),
3149 3181 ('0', 'print0', None,
3150 3182 _('end filenames with NUL, for use with xargs')),
3151 3183 ('f', 'fullpath', None,
3152 3184 _('print complete paths from the filesystem root')),
3153 3185 ('I', 'include', [], _('include names matching the given patterns')),
3154 3186 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3155 3187 _('hg locate [OPTION]... [PATTERN]...')),
3156 3188 "^log|history":
3157 3189 (log,
3158 3190 [('b', 'branches', None, _('show branches')),
3159 3191 ('f', 'follow', None,
3160 3192 _('follow changeset history, or file history across copies and renames')),
3161 3193 ('', 'follow-first', None,
3162 3194 _('only follow the first parent of merge changesets')),
3163 3195 ('k', 'keyword', [], _('search for a keyword')),
3164 3196 ('l', 'limit', '', _('limit number of changes displayed')),
3165 3197 ('r', 'rev', [], _('show the specified revision or range')),
3166 3198 ('M', 'no-merges', None, _('do not show merges')),
3167 3199 ('', 'style', '', _('display using template map file')),
3168 3200 ('m', 'only-merges', None, _('show only merges')),
3169 3201 ('p', 'patch', None, _('show patch')),
3170 3202 ('', 'template', '', _('display with template')),
3171 3203 ('I', 'include', [], _('include names matching the given patterns')),
3172 3204 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3173 3205 _('hg log [OPTION]... [FILE]')),
3174 3206 "manifest": (manifest, [], _('hg manifest [REV]')),
3175 3207 "merge":
3176 3208 (merge,
3177 3209 [('b', 'branch', '', _('merge with head of a specific branch')),
3178 3210 ('f', 'force', None, _('force a merge with outstanding changes'))],
3179 3211 _('hg merge [-b TAG] [-f] [REV]')),
3180 3212 "outgoing|out": (outgoing,
3181 3213 [('M', 'no-merges', None, _('do not show merges')),
3182 3214 ('f', 'force', None,
3183 3215 _('run even when remote repository is unrelated')),
3184 3216 ('p', 'patch', None, _('show patch')),
3185 3217 ('', 'style', '', _('display using template map file')),
3186 3218 ('r', 'rev', [], _('a specific revision you would like to push')),
3187 3219 ('n', 'newest-first', None, _('show newest record first')),
3188 3220 ('', 'template', '', _('display with template')),
3189 3221 ('e', 'ssh', '', _('specify ssh command to use')),
3190 3222 ('', 'remotecmd', '',
3191 3223 _('specify hg command to run on the remote side'))],
3192 3224 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3193 3225 "^parents":
3194 3226 (parents,
3195 3227 [('b', 'branches', None, _('show branches')),
3196 3228 ('r', 'rev', '', _('show parents from the specified rev')),
3197 3229 ('', 'style', '', _('display using template map file')),
3198 3230 ('', 'template', '', _('display with template'))],
3199 3231 _('hg parents [-b] [-r REV] [FILE]')),
3200 3232 "paths": (paths, [], _('hg paths [NAME]')),
3201 3233 "^pull":
3202 3234 (pull,
3203 3235 [('u', 'update', None,
3204 3236 _('update the working directory to tip after pull')),
3205 3237 ('e', 'ssh', '', _('specify ssh command to use')),
3206 3238 ('f', 'force', None,
3207 3239 _('run even when remote repository is unrelated')),
3208 3240 ('r', 'rev', [], _('a specific revision you would like to pull')),
3209 3241 ('', 'remotecmd', '',
3210 3242 _('specify hg command to run on the remote side'))],
3211 3243 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3212 3244 "^push":
3213 3245 (push,
3214 3246 [('f', 'force', None, _('force push')),
3215 3247 ('e', 'ssh', '', _('specify ssh command to use')),
3216 3248 ('r', 'rev', [], _('a specific revision you would like to push')),
3217 3249 ('', 'remotecmd', '',
3218 3250 _('specify hg command to run on the remote side'))],
3219 3251 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3220 3252 "debugrawcommit|rawcommit":
3221 3253 (rawcommit,
3222 3254 [('p', 'parent', [], _('parent')),
3223 3255 ('d', 'date', '', _('date code')),
3224 3256 ('u', 'user', '', _('user')),
3225 3257 ('F', 'files', '', _('file list')),
3226 3258 ('m', 'message', '', _('commit message')),
3227 3259 ('l', 'logfile', '', _('commit message file'))],
3228 3260 _('hg debugrawcommit [OPTION]... [FILE]...')),
3229 3261 "recover": (recover, [], _('hg recover')),
3230 3262 "^remove|rm":
3231 3263 (remove,
3232 3264 [('A', 'after', None, _('record remove that has already occurred')),
3233 3265 ('f', 'force', None, _('remove file even if modified')),
3234 3266 ('I', 'include', [], _('include names matching the given patterns')),
3235 3267 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3236 3268 _('hg remove [OPTION]... FILE...')),
3237 3269 "rename|mv":
3238 3270 (rename,
3239 3271 [('A', 'after', None, _('record a rename that has already occurred')),
3240 3272 ('f', 'force', None,
3241 3273 _('forcibly copy over an existing managed file')),
3242 3274 ('I', 'include', [], _('include names matching the given patterns')),
3243 3275 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3244 3276 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3245 3277 _('hg rename [OPTION]... SOURCE... DEST')),
3246 3278 "^revert":
3247 3279 (revert,
3248 3280 [('r', 'rev', '', _('revision to revert to')),
3249 3281 ('', 'no-backup', None, _('do not save backup copies of files')),
3250 3282 ('I', 'include', [], _('include names matching given patterns')),
3251 3283 ('X', 'exclude', [], _('exclude names matching given patterns')),
3252 3284 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3253 3285 _('hg revert [-r REV] [NAME]...')),
3254 3286 "rollback": (rollback, [], _('hg rollback')),
3255 3287 "root": (root, [], _('hg root')),
3256 3288 "^serve":
3257 3289 (serve,
3258 3290 [('A', 'accesslog', '', _('name of access log file to write to')),
3259 3291 ('d', 'daemon', None, _('run server in background')),
3260 3292 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3261 3293 ('E', 'errorlog', '', _('name of error log file to write to')),
3262 3294 ('p', 'port', 0, _('port to use (default: 8000)')),
3263 3295 ('a', 'address', '', _('address to use')),
3264 3296 ('n', 'name', '',
3265 3297 _('name to show in web pages (default: working dir)')),
3266 3298 ('', 'webdir-conf', '', _('name of the webdir config file'
3267 3299 ' (serve more than one repo)')),
3268 3300 ('', 'pid-file', '', _('name of file to write process ID to')),
3269 3301 ('', 'stdio', None, _('for remote clients')),
3270 3302 ('t', 'templates', '', _('web templates to use')),
3271 3303 ('', 'style', '', _('template style to use')),
3272 3304 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3273 3305 _('hg serve [OPTION]...')),
3274 3306 "^status|st":
3275 3307 (status,
3276 3308 [('A', 'all', None, _('show status of all files')),
3277 3309 ('m', 'modified', None, _('show only modified files')),
3278 3310 ('a', 'added', None, _('show only added files')),
3279 3311 ('r', 'removed', None, _('show only removed files')),
3280 3312 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3281 3313 ('c', 'clean', None, _('show only files without changes')),
3282 3314 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3283 3315 ('i', 'ignored', None, _('show ignored files')),
3284 3316 ('n', 'no-status', None, _('hide status prefix')),
3285 3317 ('C', 'copies', None, _('show source of copied files')),
3286 3318 ('0', 'print0', None,
3287 3319 _('end filenames with NUL, for use with xargs')),
3288 3320 ('I', 'include', [], _('include names matching the given patterns')),
3289 3321 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3290 3322 _('hg status [OPTION]... [FILE]...')),
3291 3323 "tag":
3292 3324 (tag,
3293 3325 [('l', 'local', None, _('make the tag local')),
3294 3326 ('m', 'message', '', _('message for tag commit log entry')),
3295 3327 ('d', 'date', '', _('record datecode as commit date')),
3296 3328 ('u', 'user', '', _('record user as commiter')),
3297 3329 ('r', 'rev', '', _('revision to tag'))],
3298 3330 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3299 3331 "tags": (tags, [], _('hg tags')),
3300 3332 "tip":
3301 3333 (tip,
3302 3334 [('b', 'branches', None, _('show branches')),
3303 3335 ('', 'style', '', _('display using template map file')),
3304 3336 ('p', 'patch', None, _('show patch')),
3305 3337 ('', 'template', '', _('display with template'))],
3306 3338 _('hg tip [-b] [-p]')),
3307 3339 "unbundle":
3308 3340 (unbundle,
3309 3341 [('u', 'update', None,
3310 3342 _('update the working directory to tip after unbundle'))],
3311 3343 _('hg unbundle [-u] FILE')),
3312 3344 "debugundo|undo": (undo, [], _('hg undo')),
3313 3345 "^update|up|checkout|co":
3314 3346 (update,
3315 3347 [('b', 'branch', '', _('checkout the head of a specific branch')),
3316 3348 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3317 3349 ('C', 'clean', None, _('overwrite locally modified files')),
3318 3350 ('f', 'force', None, _('force a merge with outstanding changes'))],
3319 3351 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3320 3352 "verify": (verify, [], _('hg verify')),
3321 3353 "version": (show_version, [], _('hg version')),
3322 3354 }
3323 3355
3324 3356 globalopts = [
3325 3357 ('R', 'repository', '',
3326 3358 _('repository root directory or symbolic path name')),
3327 3359 ('', 'cwd', '', _('change working directory')),
3328 3360 ('y', 'noninteractive', None,
3329 3361 _('do not prompt, assume \'yes\' for any required answers')),
3330 3362 ('q', 'quiet', None, _('suppress output')),
3331 3363 ('v', 'verbose', None, _('enable additional output')),
3332 3364 ('', 'config', [], _('set/override config option')),
3333 3365 ('', 'debug', None, _('enable debugging output')),
3334 3366 ('', 'debugger', None, _('start debugger')),
3335 3367 ('', 'lsprof', None, _('print improved command execution profile')),
3336 3368 ('', 'traceback', None, _('print traceback on exception')),
3337 3369 ('', 'time', None, _('time how long the command takes')),
3338 3370 ('', 'profile', None, _('print command execution profile')),
3339 3371 ('', 'version', None, _('output version information and exit')),
3340 3372 ('h', 'help', None, _('display help and exit')),
3341 3373 ]
3342 3374
3343 3375 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3344 3376 " debugindex debugindexdot")
3345 3377 optionalrepo = ("paths serve debugconfig")
3346 3378
3347 3379 def findpossible(cmd):
3348 3380 """
3349 3381 Return cmd -> (aliases, command table entry)
3350 3382 for each matching command.
3351 3383 Return debug commands (or their aliases) only if no normal command matches.
3352 3384 """
3353 3385 choice = {}
3354 3386 debugchoice = {}
3355 3387 for e in table.keys():
3356 3388 aliases = e.lstrip("^").split("|")
3357 3389 found = None
3358 3390 if cmd in aliases:
3359 3391 found = cmd
3360 3392 else:
3361 3393 for a in aliases:
3362 3394 if a.startswith(cmd):
3363 3395 found = a
3364 3396 break
3365 3397 if found is not None:
3366 3398 if aliases[0].startswith("debug"):
3367 3399 debugchoice[found] = (aliases, table[e])
3368 3400 else:
3369 3401 choice[found] = (aliases, table[e])
3370 3402
3371 3403 if not choice and debugchoice:
3372 3404 choice = debugchoice
3373 3405
3374 3406 return choice
3375 3407
3376 3408 def findcmd(cmd):
3377 3409 """Return (aliases, command table entry) for command string."""
3378 3410 choice = findpossible(cmd)
3379 3411
3380 3412 if choice.has_key(cmd):
3381 3413 return choice[cmd]
3382 3414
3383 3415 if len(choice) > 1:
3384 3416 clist = choice.keys()
3385 3417 clist.sort()
3386 3418 raise AmbiguousCommand(cmd, clist)
3387 3419
3388 3420 if choice:
3389 3421 return choice.values()[0]
3390 3422
3391 3423 raise UnknownCommand(cmd)
3392 3424
3393 3425 def catchterm(*args):
3394 3426 raise util.SignalInterrupt
3395 3427
3396 3428 def run():
3397 3429 sys.exit(dispatch(sys.argv[1:]))
3398 3430
3399 3431 class ParseError(Exception):
3400 3432 """Exception raised on errors in parsing the command line."""
3401 3433
3402 3434 def parse(ui, args):
3403 3435 options = {}
3404 3436 cmdoptions = {}
3405 3437
3406 3438 try:
3407 3439 args = fancyopts.fancyopts(args, globalopts, options)
3408 3440 except fancyopts.getopt.GetoptError, inst:
3409 3441 raise ParseError(None, inst)
3410 3442
3411 3443 if args:
3412 3444 cmd, args = args[0], args[1:]
3413 3445 aliases, i = findcmd(cmd)
3414 3446 cmd = aliases[0]
3415 3447 defaults = ui.config("defaults", cmd)
3416 3448 if defaults:
3417 3449 args = defaults.split() + args
3418 3450 c = list(i[1])
3419 3451 else:
3420 3452 cmd = None
3421 3453 c = []
3422 3454
3423 3455 # combine global options into local
3424 3456 for o in globalopts:
3425 3457 c.append((o[0], o[1], options[o[1]], o[3]))
3426 3458
3427 3459 try:
3428 3460 args = fancyopts.fancyopts(args, c, cmdoptions)
3429 3461 except fancyopts.getopt.GetoptError, inst:
3430 3462 raise ParseError(cmd, inst)
3431 3463
3432 3464 # separate global options back out
3433 3465 for o in globalopts:
3434 3466 n = o[1]
3435 3467 options[n] = cmdoptions[n]
3436 3468 del cmdoptions[n]
3437 3469
3438 3470 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3439 3471
3440 3472 external = {}
3441 3473
3442 3474 def findext(name):
3443 3475 '''return module with given extension name'''
3444 3476 try:
3445 3477 return sys.modules[external[name]]
3446 3478 except KeyError:
3447 3479 for k, v in external.iteritems():
3448 3480 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3449 3481 return sys.modules[v]
3450 3482 raise KeyError(name)
3451 3483
3452 3484 def dispatch(args):
3453 3485 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3454 3486 num = getattr(signal, name, None)
3455 3487 if num: signal.signal(num, catchterm)
3456 3488
3457 3489 try:
3458 3490 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3459 3491 except util.Abort, inst:
3460 3492 sys.stderr.write(_("abort: %s\n") % inst)
3461 3493 return -1
3462 3494
3463 3495 for ext_name, load_from_name in u.extensions():
3464 3496 try:
3465 3497 if load_from_name:
3466 3498 # the module will be loaded in sys.modules
3467 3499 # choose an unique name so that it doesn't
3468 3500 # conflicts with other modules
3469 3501 module_name = "hgext_%s" % ext_name.replace('.', '_')
3470 3502 mod = imp.load_source(module_name, load_from_name)
3471 3503 else:
3472 3504 def importh(name):
3473 3505 mod = __import__(name)
3474 3506 components = name.split('.')
3475 3507 for comp in components[1:]:
3476 3508 mod = getattr(mod, comp)
3477 3509 return mod
3478 3510 try:
3479 3511 mod = importh("hgext.%s" % ext_name)
3480 3512 except ImportError:
3481 3513 mod = importh(ext_name)
3482 3514 external[ext_name] = mod.__name__
3483 3515 except (util.SignalInterrupt, KeyboardInterrupt):
3484 3516 raise
3485 3517 except Exception, inst:
3486 3518 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3487 3519 if u.print_exc():
3488 3520 return 1
3489 3521
3490 3522 for name in external.itervalues():
3491 3523 mod = sys.modules[name]
3492 3524 uisetup = getattr(mod, 'uisetup', None)
3493 3525 if uisetup:
3494 3526 uisetup(u)
3495 3527 cmdtable = getattr(mod, 'cmdtable', {})
3496 3528 for t in cmdtable:
3497 3529 if t in table:
3498 3530 u.warn(_("module %s overrides %s\n") % (name, t))
3499 3531 table.update(cmdtable)
3500 3532
3501 3533 try:
3502 3534 cmd, func, args, options, cmdoptions = parse(u, args)
3503 3535 if options["time"]:
3504 3536 def get_times():
3505 3537 t = os.times()
3506 3538 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3507 3539 t = (t[0], t[1], t[2], t[3], time.clock())
3508 3540 return t
3509 3541 s = get_times()
3510 3542 def print_time():
3511 3543 t = get_times()
3512 3544 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3513 3545 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3514 3546 atexit.register(print_time)
3515 3547
3516 3548 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3517 3549 not options["noninteractive"], options["traceback"],
3518 3550 options["config"])
3519 3551
3520 3552 # enter the debugger before command execution
3521 3553 if options['debugger']:
3522 3554 pdb.set_trace()
3523 3555
3524 3556 try:
3525 3557 if options['cwd']:
3526 3558 try:
3527 3559 os.chdir(options['cwd'])
3528 3560 except OSError, inst:
3529 3561 raise util.Abort('%s: %s' %
3530 3562 (options['cwd'], inst.strerror))
3531 3563
3532 3564 path = u.expandpath(options["repository"]) or ""
3533 3565 repo = path and hg.repository(u, path=path) or None
3534 3566
3535 3567 if options['help']:
3536 3568 return help_(u, cmd, options['version'])
3537 3569 elif options['version']:
3538 3570 return show_version(u)
3539 3571 elif not cmd:
3540 3572 return help_(u, 'shortlist')
3541 3573
3542 3574 if cmd not in norepo.split():
3543 3575 try:
3544 3576 if not repo:
3545 3577 repo = hg.repository(u, path=path)
3546 3578 u = repo.ui
3547 3579 for name in external.itervalues():
3548 3580 mod = sys.modules[name]
3549 3581 if hasattr(mod, 'reposetup'):
3550 3582 mod.reposetup(u, repo)
3551 3583 hg.repo_setup_hooks.append(mod.reposetup)
3552 3584 except hg.RepoError:
3553 3585 if cmd not in optionalrepo.split():
3554 3586 raise
3555 3587 d = lambda: func(u, repo, *args, **cmdoptions)
3556 3588 else:
3557 3589 d = lambda: func(u, *args, **cmdoptions)
3558 3590
3559 3591 # reupdate the options, repo/.hg/hgrc may have changed them
3560 3592 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3561 3593 not options["noninteractive"], options["traceback"],
3562 3594 options["config"])
3563 3595
3564 3596 try:
3565 3597 if options['profile']:
3566 3598 import hotshot, hotshot.stats
3567 3599 prof = hotshot.Profile("hg.prof")
3568 3600 try:
3569 3601 try:
3570 3602 return prof.runcall(d)
3571 3603 except:
3572 3604 try:
3573 3605 u.warn(_('exception raised - generating '
3574 3606 'profile anyway\n'))
3575 3607 except:
3576 3608 pass
3577 3609 raise
3578 3610 finally:
3579 3611 prof.close()
3580 3612 stats = hotshot.stats.load("hg.prof")
3581 3613 stats.strip_dirs()
3582 3614 stats.sort_stats('time', 'calls')
3583 3615 stats.print_stats(40)
3584 3616 elif options['lsprof']:
3585 3617 try:
3586 3618 from mercurial import lsprof
3587 3619 except ImportError:
3588 3620 raise util.Abort(_(
3589 3621 'lsprof not available - install from '
3590 3622 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3591 3623 p = lsprof.Profiler()
3592 3624 p.enable(subcalls=True)
3593 3625 try:
3594 3626 return d()
3595 3627 finally:
3596 3628 p.disable()
3597 3629 stats = lsprof.Stats(p.getstats())
3598 3630 stats.sort()
3599 3631 stats.pprint(top=10, file=sys.stderr, climit=5)
3600 3632 else:
3601 3633 return d()
3602 3634 finally:
3603 3635 u.flush()
3604 3636 except:
3605 3637 # enter the debugger when we hit an exception
3606 3638 if options['debugger']:
3607 3639 pdb.post_mortem(sys.exc_info()[2])
3608 3640 u.print_exc()
3609 3641 raise
3610 3642 except ParseError, inst:
3611 3643 if inst.args[0]:
3612 3644 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3613 3645 help_(u, inst.args[0])
3614 3646 else:
3615 3647 u.warn(_("hg: %s\n") % inst.args[1])
3616 3648 help_(u, 'shortlist')
3617 3649 except AmbiguousCommand, inst:
3618 3650 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3619 3651 (inst.args[0], " ".join(inst.args[1])))
3620 3652 except UnknownCommand, inst:
3621 3653 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3622 3654 help_(u, 'shortlist')
3623 3655 except hg.RepoError, inst:
3624 3656 u.warn(_("abort: %s!\n") % inst)
3625 3657 except lock.LockHeld, inst:
3626 3658 if inst.errno == errno.ETIMEDOUT:
3627 3659 reason = _('timed out waiting for lock held by %s') % inst.locker
3628 3660 else:
3629 3661 reason = _('lock held by %s') % inst.locker
3630 3662 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3631 3663 except lock.LockUnavailable, inst:
3632 3664 u.warn(_("abort: could not lock %s: %s\n") %
3633 3665 (inst.desc or inst.filename, inst.strerror))
3634 3666 except revlog.RevlogError, inst:
3635 3667 u.warn(_("abort: "), inst, "!\n")
3636 3668 except util.SignalInterrupt:
3637 3669 u.warn(_("killed!\n"))
3638 3670 except KeyboardInterrupt:
3639 3671 try:
3640 3672 u.warn(_("interrupted!\n"))
3641 3673 except IOError, inst:
3642 3674 if inst.errno == errno.EPIPE:
3643 3675 if u.debugflag:
3644 3676 u.warn(_("\nbroken pipe\n"))
3645 3677 else:
3646 3678 raise
3647 3679 except IOError, inst:
3648 3680 if hasattr(inst, "code"):
3649 3681 u.warn(_("abort: %s\n") % inst)
3650 3682 elif hasattr(inst, "reason"):
3651 3683 u.warn(_("abort: error: %s\n") % inst.reason[1])
3652 3684 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3653 3685 if u.debugflag:
3654 3686 u.warn(_("broken pipe\n"))
3655 3687 elif getattr(inst, "strerror", None):
3656 3688 if getattr(inst, "filename", None):
3657 3689 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3658 3690 else:
3659 3691 u.warn(_("abort: %s\n") % inst.strerror)
3660 3692 else:
3661 3693 raise
3662 3694 except OSError, inst:
3663 3695 if hasattr(inst, "filename"):
3664 3696 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3665 3697 else:
3666 3698 u.warn(_("abort: %s\n") % inst.strerror)
3667 3699 except util.Abort, inst:
3668 3700 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3669 3701 except TypeError, inst:
3670 3702 # was this an argument error?
3671 3703 tb = traceback.extract_tb(sys.exc_info()[2])
3672 3704 if len(tb) > 2: # no
3673 3705 raise
3674 3706 u.debug(inst, "\n")
3675 3707 u.warn(_("%s: invalid arguments\n") % cmd)
3676 3708 help_(u, cmd)
3677 3709 except SystemExit, inst:
3678 3710 # Commands shouldn't sys.exit directly, but give a return code.
3679 3711 # Just in case catch this and and pass exit code to caller.
3680 3712 return inst.code
3681 3713 except:
3682 3714 u.warn(_("** unknown exception encountered, details follow\n"))
3683 3715 u.warn(_("** report bug details to "
3684 3716 "http://www.selenic.com/mercurial/bts\n"))
3685 3717 u.warn(_("** or mercurial@selenic.com\n"))
3686 3718 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3687 3719 % version.get_version())
3688 3720 raise
3689 3721
3690 3722 return -1
@@ -1,1018 +1,997 b''
1 1 """
2 2 util.py - Mercurial utility functions and platform specfic implementations
3 3
4 4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7 7
8 8 This software may be used and distributed according to the terms
9 9 of the GNU General Public License, incorporated herein by reference.
10 10
11 11 This contains helper routines that are independent of the SCM core and hide
12 12 platform-specific details from the core.
13 13 """
14 14
15 15 from i18n import gettext as _
16 16 from demandload import *
17 17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 18 demandload(globals(), "os threading time")
19 19
20 20 # used by parsedate
21 21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
22 22 '%a %b %d %H:%M:%S %Y')
23 23
24 24 class SignalInterrupt(Exception):
25 25 """Exception raised on SIGTERM and SIGHUP."""
26 26
27 27 def pipefilter(s, cmd):
28 28 '''filter string S through command CMD, returning its output'''
29 29 (pout, pin) = popen2.popen2(cmd, -1, 'b')
30 30 def writer():
31 31 try:
32 32 pin.write(s)
33 33 pin.close()
34 34 except IOError, inst:
35 35 if inst.errno != errno.EPIPE:
36 36 raise
37 37
38 38 # we should use select instead on UNIX, but this will work on most
39 39 # systems, including Windows
40 40 w = threading.Thread(target=writer)
41 41 w.start()
42 42 f = pout.read()
43 43 pout.close()
44 44 w.join()
45 45 return f
46 46
47 47 def tempfilter(s, cmd):
48 48 '''filter string S through a pair of temporary files with CMD.
49 49 CMD is used as a template to create the real command to be run,
50 50 with the strings INFILE and OUTFILE replaced by the real names of
51 51 the temporary files generated.'''
52 52 inname, outname = None, None
53 53 try:
54 54 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
55 55 fp = os.fdopen(infd, 'wb')
56 56 fp.write(s)
57 57 fp.close()
58 58 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
59 59 os.close(outfd)
60 60 cmd = cmd.replace('INFILE', inname)
61 61 cmd = cmd.replace('OUTFILE', outname)
62 62 code = os.system(cmd)
63 63 if code: raise Abort(_("command '%s' failed: %s") %
64 64 (cmd, explain_exit(code)))
65 65 return open(outname, 'rb').read()
66 66 finally:
67 67 try:
68 68 if inname: os.unlink(inname)
69 69 except: pass
70 70 try:
71 71 if outname: os.unlink(outname)
72 72 except: pass
73 73
74 74 filtertable = {
75 75 'tempfile:': tempfilter,
76 76 'pipe:': pipefilter,
77 77 }
78 78
79 79 def filter(s, cmd):
80 80 "filter a string through a command that transforms its input to its output"
81 81 for name, fn in filtertable.iteritems():
82 82 if cmd.startswith(name):
83 83 return fn(s, cmd[len(name):].lstrip())
84 84 return pipefilter(s, cmd)
85 85
86 86 def find_in_path(name, path, default=None):
87 87 '''find name in search path. path can be string (will be split
88 88 with os.pathsep), or iterable thing that returns strings. if name
89 89 found, return path to name. else return default.'''
90 90 if isinstance(path, str):
91 91 path = path.split(os.pathsep)
92 92 for p in path:
93 93 p_name = os.path.join(p, name)
94 94 if os.path.exists(p_name):
95 95 return p_name
96 96 return default
97 97
98 def patch(strip, patchname, ui, cwd=None):
99 """apply the patch <patchname> to the working directory.
100 a list of patched files is returned"""
101 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
102 args = []
103 if cwd:
104 args.append('-d %s' % shellquote(cwd))
105 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
106 shellquote(patchname)))
107 files = {}
108 for line in fp:
109 line = line.rstrip()
110 ui.status("%s\n" % line)
111 if line.startswith('patching file '):
112 pf = parse_patch_output(line)
113 files.setdefault(pf, 1)
114 code = fp.close()
115 if code:
116 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
117 return files.keys()
118
119 98 def binary(s):
120 99 """return true if a string is binary data using diff's heuristic"""
121 100 if s and '\0' in s[:4096]:
122 101 return True
123 102 return False
124 103
125 104 def unique(g):
126 105 """return the uniq elements of iterable g"""
127 106 seen = {}
128 107 for f in g:
129 108 if f not in seen:
130 109 seen[f] = 1
131 110 yield f
132 111
133 112 class Abort(Exception):
134 113 """Raised if a command needs to print an error and exit."""
135 114
136 115 def always(fn): return True
137 116 def never(fn): return False
138 117
139 118 def patkind(name, dflt_pat='glob'):
140 119 """Split a string into an optional pattern kind prefix and the
141 120 actual pattern."""
142 121 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
143 122 if name.startswith(prefix + ':'): return name.split(':', 1)
144 123 return dflt_pat, name
145 124
146 125 def globre(pat, head='^', tail='$'):
147 126 "convert a glob pattern into a regexp"
148 127 i, n = 0, len(pat)
149 128 res = ''
150 129 group = False
151 130 def peek(): return i < n and pat[i]
152 131 while i < n:
153 132 c = pat[i]
154 133 i = i+1
155 134 if c == '*':
156 135 if peek() == '*':
157 136 i += 1
158 137 res += '.*'
159 138 else:
160 139 res += '[^/]*'
161 140 elif c == '?':
162 141 res += '.'
163 142 elif c == '[':
164 143 j = i
165 144 if j < n and pat[j] in '!]':
166 145 j += 1
167 146 while j < n and pat[j] != ']':
168 147 j += 1
169 148 if j >= n:
170 149 res += '\\['
171 150 else:
172 151 stuff = pat[i:j].replace('\\','\\\\')
173 152 i = j + 1
174 153 if stuff[0] == '!':
175 154 stuff = '^' + stuff[1:]
176 155 elif stuff[0] == '^':
177 156 stuff = '\\' + stuff
178 157 res = '%s[%s]' % (res, stuff)
179 158 elif c == '{':
180 159 group = True
181 160 res += '(?:'
182 161 elif c == '}' and group:
183 162 res += ')'
184 163 group = False
185 164 elif c == ',' and group:
186 165 res += '|'
187 166 elif c == '\\':
188 167 p = peek()
189 168 if p:
190 169 i += 1
191 170 res += re.escape(p)
192 171 else:
193 172 res += re.escape(c)
194 173 else:
195 174 res += re.escape(c)
196 175 return head + res + tail
197 176
198 177 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
199 178
200 179 def pathto(n1, n2):
201 180 '''return the relative path from one place to another.
202 181 this returns a path in the form used by the local filesystem, not hg.'''
203 182 if not n1: return localpath(n2)
204 183 a, b = n1.split('/'), n2.split('/')
205 184 a.reverse()
206 185 b.reverse()
207 186 while a and b and a[-1] == b[-1]:
208 187 a.pop()
209 188 b.pop()
210 189 b.reverse()
211 190 return os.sep.join((['..'] * len(a)) + b)
212 191
213 192 def canonpath(root, cwd, myname):
214 193 """return the canonical path of myname, given cwd and root"""
215 194 if root == os.sep:
216 195 rootsep = os.sep
217 196 elif root.endswith(os.sep):
218 197 rootsep = root
219 198 else:
220 199 rootsep = root + os.sep
221 200 name = myname
222 201 if not os.path.isabs(name):
223 202 name = os.path.join(root, cwd, name)
224 203 name = os.path.normpath(name)
225 204 if name != rootsep and name.startswith(rootsep):
226 205 name = name[len(rootsep):]
227 206 audit_path(name)
228 207 return pconvert(name)
229 208 elif name == root:
230 209 return ''
231 210 else:
232 211 # Determine whether `name' is in the hierarchy at or beneath `root',
233 212 # by iterating name=dirname(name) until that causes no change (can't
234 213 # check name == '/', because that doesn't work on windows). For each
235 214 # `name', compare dev/inode numbers. If they match, the list `rel'
236 215 # holds the reversed list of components making up the relative file
237 216 # name we want.
238 217 root_st = os.stat(root)
239 218 rel = []
240 219 while True:
241 220 try:
242 221 name_st = os.stat(name)
243 222 except OSError:
244 223 break
245 224 if samestat(name_st, root_st):
246 225 rel.reverse()
247 226 name = os.path.join(*rel)
248 227 audit_path(name)
249 228 return pconvert(name)
250 229 dirname, basename = os.path.split(name)
251 230 rel.append(basename)
252 231 if dirname == name:
253 232 break
254 233 name = dirname
255 234
256 235 raise Abort('%s not under root' % myname)
257 236
258 237 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
259 238 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
260 239
261 240 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
262 241 if os.name == 'nt':
263 242 dflt_pat = 'glob'
264 243 else:
265 244 dflt_pat = 'relpath'
266 245 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
267 246
268 247 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
269 248 """build a function to match a set of file patterns
270 249
271 250 arguments:
272 251 canonroot - the canonical root of the tree you're matching against
273 252 cwd - the current working directory, if relevant
274 253 names - patterns to find
275 254 inc - patterns to include
276 255 exc - patterns to exclude
277 256 head - a regex to prepend to patterns to control whether a match is rooted
278 257
279 258 a pattern is one of:
280 259 'glob:<rooted glob>'
281 260 're:<rooted regexp>'
282 261 'path:<rooted path>'
283 262 'relglob:<relative glob>'
284 263 'relpath:<relative path>'
285 264 'relre:<relative regexp>'
286 265 '<rooted path or regexp>'
287 266
288 267 returns:
289 268 a 3-tuple containing
290 269 - list of explicit non-pattern names passed in
291 270 - a bool match(filename) function
292 271 - a bool indicating if any patterns were passed in
293 272
294 273 todo:
295 274 make head regex a rooted bool
296 275 """
297 276
298 277 def contains_glob(name):
299 278 for c in name:
300 279 if c in _globchars: return True
301 280 return False
302 281
303 282 def regex(kind, name, tail):
304 283 '''convert a pattern into a regular expression'''
305 284 if kind == 're':
306 285 return name
307 286 elif kind == 'path':
308 287 return '^' + re.escape(name) + '(?:/|$)'
309 288 elif kind == 'relglob':
310 289 return head + globre(name, '(?:|.*/)', tail)
311 290 elif kind == 'relpath':
312 291 return head + re.escape(name) + tail
313 292 elif kind == 'relre':
314 293 if name.startswith('^'):
315 294 return name
316 295 return '.*' + name
317 296 return head + globre(name, '', tail)
318 297
319 298 def matchfn(pats, tail):
320 299 """build a matching function from a set of patterns"""
321 300 if not pats:
322 301 return
323 302 matches = []
324 303 for k, p in pats:
325 304 try:
326 305 pat = '(?:%s)' % regex(k, p, tail)
327 306 matches.append(re.compile(pat).match)
328 307 except re.error:
329 308 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
330 309 else: raise Abort("invalid pattern (%s): %s" % (k, p))
331 310
332 311 def buildfn(text):
333 312 for m in matches:
334 313 r = m(text)
335 314 if r:
336 315 return r
337 316
338 317 return buildfn
339 318
340 319 def globprefix(pat):
341 320 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
342 321 root = []
343 322 for p in pat.split(os.sep):
344 323 if contains_glob(p): break
345 324 root.append(p)
346 325 return '/'.join(root)
347 326
348 327 pats = []
349 328 files = []
350 329 roots = []
351 330 for kind, name in [patkind(p, dflt_pat) for p in names]:
352 331 if kind in ('glob', 'relpath'):
353 332 name = canonpath(canonroot, cwd, name)
354 333 if name == '':
355 334 kind, name = 'glob', '**'
356 335 if kind in ('glob', 'path', 're'):
357 336 pats.append((kind, name))
358 337 if kind == 'glob':
359 338 root = globprefix(name)
360 339 if root: roots.append(root)
361 340 elif kind == 'relpath':
362 341 files.append((kind, name))
363 342 roots.append(name)
364 343
365 344 patmatch = matchfn(pats, '$') or always
366 345 filematch = matchfn(files, '(?:/|$)') or always
367 346 incmatch = always
368 347 if inc:
369 348 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
370 349 incmatch = matchfn(inckinds, '(?:/|$)')
371 350 excmatch = lambda fn: False
372 351 if exc:
373 352 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
374 353 excmatch = matchfn(exckinds, '(?:/|$)')
375 354
376 355 return (roots,
377 356 lambda fn: (incmatch(fn) and not excmatch(fn) and
378 357 (fn.endswith('/') or
379 358 (not pats and not files) or
380 359 (pats and patmatch(fn)) or
381 360 (files and filematch(fn)))),
382 361 (inc or exc or (pats and pats != [('glob', '**')])) and True)
383 362
384 363 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
385 364 '''enhanced shell command execution.
386 365 run with environment maybe modified, maybe in different dir.
387 366
388 367 if command fails and onerr is None, return status. if ui object,
389 368 print error message and return status, else raise onerr object as
390 369 exception.'''
391 370 def py2shell(val):
392 371 'convert python object into string that is useful to shell'
393 372 if val in (None, False):
394 373 return '0'
395 374 if val == True:
396 375 return '1'
397 376 return str(val)
398 377 oldenv = {}
399 378 for k in environ:
400 379 oldenv[k] = os.environ.get(k)
401 380 if cwd is not None:
402 381 oldcwd = os.getcwd()
403 382 try:
404 383 for k, v in environ.iteritems():
405 384 os.environ[k] = py2shell(v)
406 385 if cwd is not None and oldcwd != cwd:
407 386 os.chdir(cwd)
408 387 rc = os.system(cmd)
409 388 if rc and onerr:
410 389 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
411 390 explain_exit(rc)[0])
412 391 if errprefix:
413 392 errmsg = '%s: %s' % (errprefix, errmsg)
414 393 try:
415 394 onerr.warn(errmsg + '\n')
416 395 except AttributeError:
417 396 raise onerr(errmsg)
418 397 return rc
419 398 finally:
420 399 for k, v in oldenv.iteritems():
421 400 if v is None:
422 401 del os.environ[k]
423 402 else:
424 403 os.environ[k] = v
425 404 if cwd is not None and oldcwd != cwd:
426 405 os.chdir(oldcwd)
427 406
428 407 def rename(src, dst):
429 408 """forcibly rename a file"""
430 409 try:
431 410 os.rename(src, dst)
432 411 except OSError, err:
433 412 # on windows, rename to existing file is not allowed, so we
434 413 # must delete destination first. but if file is open, unlink
435 414 # schedules it for delete but does not delete it. rename
436 415 # happens immediately even for open files, so we create
437 416 # temporary file, delete it, rename destination to that name,
438 417 # then delete that. then rename is safe to do.
439 418 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
440 419 os.close(fd)
441 420 os.unlink(temp)
442 421 os.rename(dst, temp)
443 422 os.unlink(temp)
444 423 os.rename(src, dst)
445 424
446 425 def unlink(f):
447 426 """unlink and remove the directory if it is empty"""
448 427 os.unlink(f)
449 428 # try removing directories that might now be empty
450 429 try:
451 430 os.removedirs(os.path.dirname(f))
452 431 except OSError:
453 432 pass
454 433
455 434 def copyfiles(src, dst, hardlink=None):
456 435 """Copy a directory tree using hardlinks if possible"""
457 436
458 437 if hardlink is None:
459 438 hardlink = (os.stat(src).st_dev ==
460 439 os.stat(os.path.dirname(dst)).st_dev)
461 440
462 441 if os.path.isdir(src):
463 442 os.mkdir(dst)
464 443 for name in os.listdir(src):
465 444 srcname = os.path.join(src, name)
466 445 dstname = os.path.join(dst, name)
467 446 copyfiles(srcname, dstname, hardlink)
468 447 else:
469 448 if hardlink:
470 449 try:
471 450 os_link(src, dst)
472 451 except (IOError, OSError):
473 452 hardlink = False
474 453 shutil.copy(src, dst)
475 454 else:
476 455 shutil.copy(src, dst)
477 456
478 457 def audit_path(path):
479 458 """Abort if path contains dangerous components"""
480 459 parts = os.path.normcase(path).split(os.sep)
481 460 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
482 461 or os.pardir in parts):
483 462 raise Abort(_("path contains illegal component: %s\n") % path)
484 463
485 464 def _makelock_file(info, pathname):
486 465 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
487 466 os.write(ld, info)
488 467 os.close(ld)
489 468
490 469 def _readlock_file(pathname):
491 470 return posixfile(pathname).read()
492 471
493 472 def nlinks(pathname):
494 473 """Return number of hardlinks for the given file."""
495 474 return os.lstat(pathname).st_nlink
496 475
497 476 if hasattr(os, 'link'):
498 477 os_link = os.link
499 478 else:
500 479 def os_link(src, dst):
501 480 raise OSError(0, _("Hardlinks not supported"))
502 481
503 482 def fstat(fp):
504 483 '''stat file object that may not have fileno method.'''
505 484 try:
506 485 return os.fstat(fp.fileno())
507 486 except AttributeError:
508 487 return os.stat(fp.name)
509 488
510 489 posixfile = file
511 490
512 491 def is_win_9x():
513 492 '''return true if run on windows 95, 98 or me.'''
514 493 try:
515 494 return sys.getwindowsversion()[3] == 1
516 495 except AttributeError:
517 496 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
518 497
519 498 getuser_fallback = None
520 499
521 500 def getuser():
522 501 '''return name of current user'''
523 502 try:
524 503 return getpass.getuser()
525 504 except ImportError:
526 505 # import of pwd will fail on windows - try fallback
527 506 if getuser_fallback:
528 507 return getuser_fallback()
529 508 # raised if win32api not available
530 509 raise Abort(_('user name not available - set USERNAME '
531 510 'environment variable'))
532 511
533 512 # Platform specific variants
534 513 if os.name == 'nt':
535 514 demandload(globals(), "msvcrt")
536 515 nulldev = 'NUL:'
537 516
538 517 class winstdout:
539 518 '''stdout on windows misbehaves if sent through a pipe'''
540 519
541 520 def __init__(self, fp):
542 521 self.fp = fp
543 522
544 523 def __getattr__(self, key):
545 524 return getattr(self.fp, key)
546 525
547 526 def close(self):
548 527 try:
549 528 self.fp.close()
550 529 except: pass
551 530
552 531 def write(self, s):
553 532 try:
554 533 return self.fp.write(s)
555 534 except IOError, inst:
556 535 if inst.errno != 0: raise
557 536 self.close()
558 537 raise IOError(errno.EPIPE, 'Broken pipe')
559 538
560 539 sys.stdout = winstdout(sys.stdout)
561 540
562 541 def system_rcpath():
563 542 try:
564 543 return system_rcpath_win32()
565 544 except:
566 545 return [r'c:\mercurial\mercurial.ini']
567 546
568 547 def os_rcpath():
569 548 '''return default os-specific hgrc search path'''
570 549 path = system_rcpath()
571 550 path.append(user_rcpath())
572 551 userprofile = os.environ.get('USERPROFILE')
573 552 if userprofile:
574 553 path.append(os.path.join(userprofile, 'mercurial.ini'))
575 554 return path
576 555
577 556 def user_rcpath():
578 557 '''return os-specific hgrc search path to the user dir'''
579 558 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
580 559
581 560 def parse_patch_output(output_line):
582 561 """parses the output produced by patch and returns the file name"""
583 562 pf = output_line[14:]
584 563 if pf[0] == '`':
585 564 pf = pf[1:-1] # Remove the quotes
586 565 return pf
587 566
588 567 def testpid(pid):
589 568 '''return False if pid dead, True if running or not known'''
590 569 return True
591 570
592 571 def is_exec(f, last):
593 572 return last
594 573
595 574 def set_exec(f, mode):
596 575 pass
597 576
598 577 def set_binary(fd):
599 578 msvcrt.setmode(fd.fileno(), os.O_BINARY)
600 579
601 580 def pconvert(path):
602 581 return path.replace("\\", "/")
603 582
604 583 def localpath(path):
605 584 return path.replace('/', '\\')
606 585
607 586 def normpath(path):
608 587 return pconvert(os.path.normpath(path))
609 588
610 589 makelock = _makelock_file
611 590 readlock = _readlock_file
612 591
613 592 def samestat(s1, s2):
614 593 return False
615 594
616 595 def shellquote(s):
617 596 return '"%s"' % s.replace('"', '\\"')
618 597
619 598 def explain_exit(code):
620 599 return _("exited with status %d") % code, code
621 600
622 601 try:
623 602 # override functions with win32 versions if possible
624 603 from util_win32 import *
625 604 if not is_win_9x():
626 605 posixfile = posixfile_nt
627 606 except ImportError:
628 607 pass
629 608
630 609 else:
631 610 nulldev = '/dev/null'
632 611
633 612 def rcfiles(path):
634 613 rcs = [os.path.join(path, 'hgrc')]
635 614 rcdir = os.path.join(path, 'hgrc.d')
636 615 try:
637 616 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
638 617 if f.endswith(".rc")])
639 618 except OSError, inst: pass
640 619 return rcs
641 620
642 621 def os_rcpath():
643 622 '''return default os-specific hgrc search path'''
644 623 path = []
645 624 # old mod_python does not set sys.argv
646 625 if len(getattr(sys, 'argv', [])) > 0:
647 626 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
648 627 '/../etc/mercurial'))
649 628 path.extend(rcfiles('/etc/mercurial'))
650 629 path.append(os.path.expanduser('~/.hgrc'))
651 630 path = [os.path.normpath(f) for f in path]
652 631 return path
653 632
654 633 def parse_patch_output(output_line):
655 634 """parses the output produced by patch and returns the file name"""
656 635 pf = output_line[14:]
657 636 if pf.startswith("'") and pf.endswith("'") and " " in pf:
658 637 pf = pf[1:-1] # Remove the quotes
659 638 return pf
660 639
661 640 def is_exec(f, last):
662 641 """check whether a file is executable"""
663 642 return (os.lstat(f).st_mode & 0100 != 0)
664 643
665 644 def set_exec(f, mode):
666 645 s = os.lstat(f).st_mode
667 646 if (s & 0100 != 0) == mode:
668 647 return
669 648 if mode:
670 649 # Turn on +x for every +r bit when making a file executable
671 650 # and obey umask.
672 651 umask = os.umask(0)
673 652 os.umask(umask)
674 653 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
675 654 else:
676 655 os.chmod(f, s & 0666)
677 656
678 657 def set_binary(fd):
679 658 pass
680 659
681 660 def pconvert(path):
682 661 return path
683 662
684 663 def localpath(path):
685 664 return path
686 665
687 666 normpath = os.path.normpath
688 667 samestat = os.path.samestat
689 668
690 669 def makelock(info, pathname):
691 670 try:
692 671 os.symlink(info, pathname)
693 672 except OSError, why:
694 673 if why.errno == errno.EEXIST:
695 674 raise
696 675 else:
697 676 _makelock_file(info, pathname)
698 677
699 678 def readlock(pathname):
700 679 try:
701 680 return os.readlink(pathname)
702 681 except OSError, why:
703 682 if why.errno == errno.EINVAL:
704 683 return _readlock_file(pathname)
705 684 else:
706 685 raise
707 686
708 687 def shellquote(s):
709 688 return "'%s'" % s.replace("'", "'\\''")
710 689
711 690 def testpid(pid):
712 691 '''return False if pid dead, True if running or not sure'''
713 692 try:
714 693 os.kill(pid, 0)
715 694 return True
716 695 except OSError, inst:
717 696 return inst.errno != errno.ESRCH
718 697
719 698 def explain_exit(code):
720 699 """return a 2-tuple (desc, code) describing a process's status"""
721 700 if os.WIFEXITED(code):
722 701 val = os.WEXITSTATUS(code)
723 702 return _("exited with status %d") % val, val
724 703 elif os.WIFSIGNALED(code):
725 704 val = os.WTERMSIG(code)
726 705 return _("killed by signal %d") % val, val
727 706 elif os.WIFSTOPPED(code):
728 707 val = os.WSTOPSIG(code)
729 708 return _("stopped by signal %d") % val, val
730 709 raise ValueError(_("invalid exit code"))
731 710
732 711 def opener(base, audit=True):
733 712 """
734 713 return a function that opens files relative to base
735 714
736 715 this function is used to hide the details of COW semantics and
737 716 remote file access from higher level code.
738 717 """
739 718 p = base
740 719 audit_p = audit
741 720
742 721 def mktempcopy(name):
743 722 d, fn = os.path.split(name)
744 723 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
745 724 os.close(fd)
746 725 ofp = posixfile(temp, "wb")
747 726 try:
748 727 try:
749 728 ifp = posixfile(name, "rb")
750 729 except IOError, inst:
751 730 if not getattr(inst, 'filename', None):
752 731 inst.filename = name
753 732 raise
754 733 for chunk in filechunkiter(ifp):
755 734 ofp.write(chunk)
756 735 ifp.close()
757 736 ofp.close()
758 737 except:
759 738 try: os.unlink(temp)
760 739 except: pass
761 740 raise
762 741 st = os.lstat(name)
763 742 os.chmod(temp, st.st_mode)
764 743 return temp
765 744
766 745 class atomictempfile(posixfile):
767 746 """the file will only be copied when rename is called"""
768 747 def __init__(self, name, mode):
769 748 self.__name = name
770 749 self.temp = mktempcopy(name)
771 750 posixfile.__init__(self, self.temp, mode)
772 751 def rename(self):
773 752 if not self.closed:
774 753 posixfile.close(self)
775 754 rename(self.temp, localpath(self.__name))
776 755 def __del__(self):
777 756 if not self.closed:
778 757 try:
779 758 os.unlink(self.temp)
780 759 except: pass
781 760 posixfile.close(self)
782 761
783 762 class atomicfile(atomictempfile):
784 763 """the file will only be copied on close"""
785 764 def __init__(self, name, mode):
786 765 atomictempfile.__init__(self, name, mode)
787 766 def close(self):
788 767 self.rename()
789 768 def __del__(self):
790 769 self.rename()
791 770
792 771 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
793 772 if audit_p:
794 773 audit_path(path)
795 774 f = os.path.join(p, path)
796 775
797 776 if not text:
798 777 mode += "b" # for that other OS
799 778
800 779 if mode[0] != "r":
801 780 try:
802 781 nlink = nlinks(f)
803 782 except OSError:
804 783 d = os.path.dirname(f)
805 784 if not os.path.isdir(d):
806 785 os.makedirs(d)
807 786 else:
808 787 if atomic:
809 788 return atomicfile(f, mode)
810 789 elif atomictemp:
811 790 return atomictempfile(f, mode)
812 791 if nlink > 1:
813 792 rename(mktempcopy(f), f)
814 793 return posixfile(f, mode)
815 794
816 795 return o
817 796
818 797 class chunkbuffer(object):
819 798 """Allow arbitrary sized chunks of data to be efficiently read from an
820 799 iterator over chunks of arbitrary size."""
821 800
822 801 def __init__(self, in_iter, targetsize = 2**16):
823 802 """in_iter is the iterator that's iterating over the input chunks.
824 803 targetsize is how big a buffer to try to maintain."""
825 804 self.in_iter = iter(in_iter)
826 805 self.buf = ''
827 806 self.targetsize = int(targetsize)
828 807 if self.targetsize <= 0:
829 808 raise ValueError(_("targetsize must be greater than 0, was %d") %
830 809 targetsize)
831 810 self.iterempty = False
832 811
833 812 def fillbuf(self):
834 813 """Ignore target size; read every chunk from iterator until empty."""
835 814 if not self.iterempty:
836 815 collector = cStringIO.StringIO()
837 816 collector.write(self.buf)
838 817 for ch in self.in_iter:
839 818 collector.write(ch)
840 819 self.buf = collector.getvalue()
841 820 self.iterempty = True
842 821
843 822 def read(self, l):
844 823 """Read L bytes of data from the iterator of chunks of data.
845 824 Returns less than L bytes if the iterator runs dry."""
846 825 if l > len(self.buf) and not self.iterempty:
847 826 # Clamp to a multiple of self.targetsize
848 827 targetsize = self.targetsize * ((l // self.targetsize) + 1)
849 828 collector = cStringIO.StringIO()
850 829 collector.write(self.buf)
851 830 collected = len(self.buf)
852 831 for chunk in self.in_iter:
853 832 collector.write(chunk)
854 833 collected += len(chunk)
855 834 if collected >= targetsize:
856 835 break
857 836 if collected < targetsize:
858 837 self.iterempty = True
859 838 self.buf = collector.getvalue()
860 839 s, self.buf = self.buf[:l], buffer(self.buf, l)
861 840 return s
862 841
863 842 def filechunkiter(f, size=65536, limit=None):
864 843 """Create a generator that produces the data in the file size
865 844 (default 65536) bytes at a time, up to optional limit (default is
866 845 to read all data). Chunks may be less than size bytes if the
867 846 chunk is the last chunk in the file, or the file is a socket or
868 847 some other type of file that sometimes reads less data than is
869 848 requested."""
870 849 assert size >= 0
871 850 assert limit is None or limit >= 0
872 851 while True:
873 852 if limit is None: nbytes = size
874 853 else: nbytes = min(limit, size)
875 854 s = nbytes and f.read(nbytes)
876 855 if not s: break
877 856 if limit: limit -= len(s)
878 857 yield s
879 858
880 859 def makedate():
881 860 lt = time.localtime()
882 861 if lt[8] == 1 and time.daylight:
883 862 tz = time.altzone
884 863 else:
885 864 tz = time.timezone
886 865 return time.mktime(lt), tz
887 866
888 867 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
889 868 """represent a (unixtime, offset) tuple as a localized time.
890 869 unixtime is seconds since the epoch, and offset is the time zone's
891 870 number of seconds away from UTC. if timezone is false, do not
892 871 append time zone to string."""
893 872 t, tz = date or makedate()
894 873 s = time.strftime(format, time.gmtime(float(t) - tz))
895 874 if timezone:
896 875 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
897 876 return s
898 877
899 878 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
900 879 """parse a localized time string and return a (unixtime, offset) tuple.
901 880 if the string cannot be parsed, ValueError is raised."""
902 881 def hastimezone(string):
903 882 return (string[-4:].isdigit() and
904 883 (string[-5] == '+' or string[-5] == '-') and
905 884 string[-6].isspace())
906 885
907 886 if hastimezone(string):
908 887 date, tz = string[:-6], string[-5:]
909 888 tz = int(tz)
910 889 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
911 890 else:
912 891 date, offset = string, 0
913 892 when = int(time.mktime(time.strptime(date, format))) + offset
914 893 return when, offset
915 894
916 895 def parsedate(string, formats=None):
917 896 """parse a localized time string and return a (unixtime, offset) tuple.
918 897 The date may be a "unixtime offset" string or in one of the specified
919 898 formats."""
920 899 if not formats:
921 900 formats = defaultdateformats
922 901 try:
923 902 when, offset = map(int, string.split(' '))
924 903 except ValueError:
925 904 for format in formats:
926 905 try:
927 906 when, offset = strdate(string, format)
928 907 except ValueError:
929 908 pass
930 909 else:
931 910 break
932 911 else:
933 912 raise ValueError(_('invalid date: %r') % string)
934 913 # validate explicit (probably user-specified) date and
935 914 # time zone offset. values must fit in signed 32 bits for
936 915 # current 32-bit linux runtimes. timezones go from UTC-12
937 916 # to UTC+14
938 917 if abs(when) > 0x7fffffff:
939 918 raise ValueError(_('date exceeds 32 bits: %d') % when)
940 919 if offset < -50400 or offset > 43200:
941 920 raise ValueError(_('impossible time zone offset: %d') % offset)
942 921 return when, offset
943 922
944 923 def shortuser(user):
945 924 """Return a short representation of a user name or email address."""
946 925 f = user.find('@')
947 926 if f >= 0:
948 927 user = user[:f]
949 928 f = user.find('<')
950 929 if f >= 0:
951 930 user = user[f+1:]
952 931 return user
953 932
954 933 def walkrepos(path):
955 934 '''yield every hg repository under path, recursively.'''
956 935 def errhandler(err):
957 936 if err.filename == path:
958 937 raise err
959 938
960 939 for root, dirs, files in os.walk(path, onerror=errhandler):
961 940 for d in dirs:
962 941 if d == '.hg':
963 942 yield root
964 943 dirs[:] = []
965 944 break
966 945
967 946 _rcpath = None
968 947
969 948 def rcpath():
970 949 '''return hgrc search path. if env var HGRCPATH is set, use it.
971 950 for each item in path, if directory, use files ending in .rc,
972 951 else use item.
973 952 make HGRCPATH empty to only look in .hg/hgrc of current repo.
974 953 if no HGRCPATH, use default os-specific path.'''
975 954 global _rcpath
976 955 if _rcpath is None:
977 956 if 'HGRCPATH' in os.environ:
978 957 _rcpath = []
979 958 for p in os.environ['HGRCPATH'].split(os.pathsep):
980 959 if not p: continue
981 960 if os.path.isdir(p):
982 961 for f in os.listdir(p):
983 962 if f.endswith('.rc'):
984 963 _rcpath.append(os.path.join(p, f))
985 964 else:
986 965 _rcpath.append(p)
987 966 else:
988 967 _rcpath = os_rcpath()
989 968 return _rcpath
990 969
991 970 def bytecount(nbytes):
992 971 '''return byte count formatted as readable string, with units'''
993 972
994 973 units = (
995 974 (100, 1<<30, _('%.0f GB')),
996 975 (10, 1<<30, _('%.1f GB')),
997 976 (1, 1<<30, _('%.2f GB')),
998 977 (100, 1<<20, _('%.0f MB')),
999 978 (10, 1<<20, _('%.1f MB')),
1000 979 (1, 1<<20, _('%.2f MB')),
1001 980 (100, 1<<10, _('%.0f KB')),
1002 981 (10, 1<<10, _('%.1f KB')),
1003 982 (1, 1<<10, _('%.2f KB')),
1004 983 (1, 1, _('%.0f bytes')),
1005 984 )
1006 985
1007 986 for multiplier, divisor, format in units:
1008 987 if nbytes >= divisor * multiplier:
1009 988 return format % (nbytes / float(divisor))
1010 989 return units[-1][2] % nbytes
1011 990
1012 991 def drop_scheme(scheme, path):
1013 992 sc = scheme + ':'
1014 993 if path.startswith(sc):
1015 994 path = path[len(sc):]
1016 995 if path.startswith('//'):
1017 996 path = path[2:]
1018 997 return path
General Comments 0
You need to be logged in to leave comments. Login now