##// END OF EJS Templates
Bryan O'Sullivan -
r4490:c927c568 merge default
parent child Browse files
Show More
@@ -0,0 +1,45 b''
1 #!/bin/sh
2
3 cat >findbranch.py <<EOF
4 import re, sys
5
6 head_re = re.compile('^#(?:(?:\\s+([A-Za-z][A-Za-z0-9_]*)(?:\\s.*)?)|(?:\\s*))$')
7
8 for line in sys.stdin:
9 hmatch = head_re.match(line)
10 if not hmatch:
11 sys.exit(1)
12 if hmatch.group(1) == 'Branch':
13 sys.exit(0)
14 sys.exit(1)
15 EOF
16 hg init a
17 cd a
18 echo "Rev 1" >rev
19 hg add rev
20 hg commit -m "No branch."
21 hg branch abranch
22 echo "Rev 2" >rev
23 hg commit -m "With branch."
24 if hg export 0 | python ../findbranch.py; then
25 echo "Export of default branch revision has Branch header" 1>&2
26 exit 1
27 fi
28 if hg export 1 | python ../findbranch.py; then
29 : # Do nothing
30 else
31 echo "Export of branch revision is missing Branch header" 1>&2
32 exit 1
33 fi
34 # Make sure import still works with branch information in patches.
35 cd ..
36 hg init b
37 cd b
38 hg -R ../a export 0 | hg import -
39 hg -R ../a export 1 | hg import -
40 cd ..
41 rm -rf b
42 hg init b
43 cd b
44 hg -R ../a export 0 | hg import --exact -
45 hg -R ../a export 1 | hg import --exact -
@@ -0,0 +1,4 b''
1 applying patch from stdin
2 applying patch from stdin
3 applying patch from stdin
4 applying patch from stdin
@@ -1,650 +1,731 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # This is a generalized framework for converting between SCM
3 # This is a generalized framework for converting between SCM
4 # repository formats.
4 # repository formats.
5 #
5 #
6 # To use, run:
6 # To use, run:
7 #
7 #
8 # convert-repo <source> [<dest> [<mapfile>]]
8 # convert-repo <source> [<dest> [<mapfile>]]
9 #
9 #
10 # Currently accepted source formats: git, cvs
10 # Currently accepted source formats: git, cvs
11 # Currently accepted destination formats: hg
11 # Currently accepted destination formats: hg
12 #
12 #
13 # If destination isn't given, a new Mercurial repo named <src>-hg will
13 # If destination isn't given, a new Mercurial repo named <src>-hg will
14 # be created. If <mapfile> isn't given, it will be put in a default
14 # be created. If <mapfile> isn't given, it will be put in a default
15 # location (<dest>/.hg/shamap by default)
15 # location (<dest>/.hg/shamap by default)
16 #
16 #
17 # The <mapfile> is a simple text file that maps each source commit ID to
17 # The <mapfile> is a simple text file that maps each source commit ID to
18 # the destination ID for that revision, like so:
18 # the destination ID for that revision, like so:
19 #
19 #
20 # <source ID> <destination ID>
20 # <source ID> <destination ID>
21 #
21 #
22 # If the file doesn't exist, it's automatically created. It's updated
22 # If the file doesn't exist, it's automatically created. It's updated
23 # on each commit copied, so convert-repo can be interrupted and can
23 # on each commit copied, so convert-repo can be interrupted and can
24 # be run repeatedly to copy new commits.
24 # be run repeatedly to copy new commits.
25
25
26 import sys, os, zlib, sha, time, re, locale, socket
26 import sys, os, zlib, sha, time, re, locale, socket
27 os.environ["HGENCODING"] = "utf-8"
27 os.environ["HGENCODING"] = "utf-8"
28 from mercurial import hg, ui, util, fancyopts
28 from mercurial import hg, ui, util, fancyopts
29
29
30 class Abort(Exception): pass
30 class Abort(Exception): pass
31 class NoRepo(Exception): pass
31 class NoRepo(Exception): pass
32
32
33 class commit:
33 class commit(object):
34 def __init__(self, **parts):
34 def __init__(self, **parts):
35 for x in "author date desc parents".split():
35 for x in "author date desc parents".split():
36 if not x in parts:
36 if not x in parts:
37 abort("commit missing field %s\n" % x)
37 abort("commit missing field %s\n" % x)
38 self.__dict__.update(parts)
38 self.__dict__.update(parts)
39
39
40 quiet = 0
40 quiet = 0
41 def status(msg):
41 def status(msg):
42 if not quiet: sys.stdout.write(str(msg))
42 if not quiet: sys.stdout.write(str(msg))
43
43
44 def warn(msg):
44 def warn(msg):
45 sys.stderr.write(str(msg))
45 sys.stderr.write(str(msg))
46
46
47 def abort(msg):
47 def abort(msg):
48 raise Abort(msg)
48 raise Abort(msg)
49
49
50 def recode(s):
50 def recode(s):
51 try:
51 try:
52 return s.decode("utf-8").encode("utf-8")
52 return s.decode("utf-8").encode("utf-8")
53 except:
53 except:
54 try:
54 try:
55 return s.decode("latin-1").encode("utf-8")
55 return s.decode("latin-1").encode("utf-8")
56 except:
56 except:
57 return s.decode("utf-8", "replace").encode("utf-8")
57 return s.decode("utf-8", "replace").encode("utf-8")
58
58
59 class converter_source(object):
60 """Conversion source interface"""
61
62 def __init__(self, path):
63 """Initialize conversion source (or raise NoRepo("message")
64 exception if path is not a valid repository)"""
65 raise NotImplementedError()
66
67 def getheads(self):
68 """Return a list of this repository's heads"""
69 raise NotImplementedError()
70
71 def getfile(self, name, rev):
72 """Return file contents as a string"""
73 raise NotImplementedError()
74
75 def getmode(self, name, rev):
76 """Return file mode, eg. '', 'x', or 'l'"""
77 raise NotImplementedError()
78
79 def getchanges(self, version):
80 """Return sorted list of (filename, id) tuples for all files changed in rev.
81
82 id just tells us which revision to return in getfile(), e.g. in
83 git it's an object hash."""
84 raise NotImplementedError()
85
86 def getcommit(self, version):
87 """Return the commit object for version"""
88 raise NotImplementedError()
89
90 def gettags(self):
91 """Return the tags as a dictionary of name: revision"""
92 raise NotImplementedError()
93
94 class converter_sink(object):
95 """Conversion sink (target) interface"""
96
97 def __init__(self, path):
98 """Initialize conversion sink (or raise NoRepo("message")
99 exception if path is not a valid repository)"""
100 raise NotImplementedError()
101
102 def getheads(self):
103 """Return a list of this repository's heads"""
104 raise NotImplementedError()
105
106 def mapfile(self):
107 """Path to a file that will contain lines
108 source_rev_id sink_rev_id
109 mapping equivalent revision identifiers for each system."""
110 raise NotImplementedError()
111
112 def putfile(self, f, e, data):
113 """Put file for next putcommit().
114 f: path to file
115 e: '', 'x', or 'l' (regular file, executable, or symlink)
116 data: file contents"""
117 raise NotImplementedError()
118
119 def delfile(self, f):
120 """Delete file for next putcommit().
121 f: path to file"""
122 raise NotImplementedError()
123
124 def putcommit(self, files, parents, commit):
125 """Create a revision with all changed files listed in 'files'
126 and having listed parents. 'commit' is a commit object containing
127 at a minimum the author, date, and message for this changeset.
128 Called after putfile() and delfile() calls. Note that the sink
129 repository is not told to update itself to a particular revision
130 (or even what that revision would be) before it receives the
131 file data."""
132 raise NotImplementedError()
133
134 def puttags(self, tags):
135 """Put tags into sink.
136 tags: {tagname: sink_rev_id, ...}"""
137 raise NotImplementedError()
138
139
59 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
140 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
60 class convert_cvs:
141 class convert_cvs(converter_source):
61 def __init__(self, path):
142 def __init__(self, path):
62 self.path = path
143 self.path = path
63 cvs = os.path.join(path, "CVS")
144 cvs = os.path.join(path, "CVS")
64 if not os.path.exists(cvs):
145 if not os.path.exists(cvs):
65 raise NoRepo("couldn't open CVS repo %s" % path)
146 raise NoRepo("couldn't open CVS repo %s" % path)
66
147
67 self.changeset = {}
148 self.changeset = {}
68 self.files = {}
149 self.files = {}
69 self.tags = {}
150 self.tags = {}
70 self.lastbranch = {}
151 self.lastbranch = {}
71 self.parent = {}
152 self.parent = {}
72 self.socket = None
153 self.socket = None
73 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
154 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
74 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
155 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
75 self.encoding = locale.getpreferredencoding()
156 self.encoding = locale.getpreferredencoding()
76 self._parse()
157 self._parse()
77 self._connect()
158 self._connect()
78
159
79 def _parse(self):
160 def _parse(self):
80 if self.changeset:
161 if self.changeset:
81 return
162 return
82
163
83 d = os.getcwd()
164 d = os.getcwd()
84 try:
165 try:
85 os.chdir(self.path)
166 os.chdir(self.path)
86 id = None
167 id = None
87 state = 0
168 state = 0
88 for l in os.popen("cvsps -A -u --cvs-direct -q"):
169 for l in os.popen("cvsps -A -u --cvs-direct -q"):
89 if state == 0: # header
170 if state == 0: # header
90 if l.startswith("PatchSet"):
171 if l.startswith("PatchSet"):
91 id = l[9:-2]
172 id = l[9:-2]
92 elif l.startswith("Date"):
173 elif l.startswith("Date"):
93 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
174 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
94 date = util.datestr(date)
175 date = util.datestr(date)
95 elif l.startswith("Branch"):
176 elif l.startswith("Branch"):
96 branch = l[8:-1]
177 branch = l[8:-1]
97 self.parent[id] = self.lastbranch.get(branch,'bad')
178 self.parent[id] = self.lastbranch.get(branch,'bad')
98 self.lastbranch[branch] = id
179 self.lastbranch[branch] = id
99 elif l.startswith("Ancestor branch"):
180 elif l.startswith("Ancestor branch"):
100 ancestor = l[17:-1]
181 ancestor = l[17:-1]
101 self.parent[id] = self.lastbranch[ancestor]
182 self.parent[id] = self.lastbranch[ancestor]
102 elif l.startswith("Author"):
183 elif l.startswith("Author"):
103 author = self.recode(l[8:-1])
184 author = self.recode(l[8:-1])
104 elif l.startswith("Tag: "):
185 elif l.startswith("Tag: "):
105 t = l[5:-1].rstrip()
186 t = l[5:-1].rstrip()
106 if t != "(none)":
187 if t != "(none)":
107 self.tags[t] = id
188 self.tags[t] = id
108 elif l.startswith("Log:"):
189 elif l.startswith("Log:"):
109 state = 1
190 state = 1
110 log = ""
191 log = ""
111 elif state == 1: # log
192 elif state == 1: # log
112 if l == "Members: \n":
193 if l == "Members: \n":
113 files = {}
194 files = {}
114 log = self.recode(log[:-1])
195 log = self.recode(log[:-1])
115 if log.isspace():
196 if log.isspace():
116 log = "*** empty log message ***\n"
197 log = "*** empty log message ***\n"
117 state = 2
198 state = 2
118 else:
199 else:
119 log += l
200 log += l
120 elif state == 2:
201 elif state == 2:
121 if l == "\n": #
202 if l == "\n": #
122 state = 0
203 state = 0
123 p = [self.parent[id]]
204 p = [self.parent[id]]
124 if id == "1":
205 if id == "1":
125 p = []
206 p = []
126 c = commit(author=author, date=date, parents=p,
207 c = commit(author=author, date=date, parents=p,
127 desc=log, branch=branch)
208 desc=log, branch=branch)
128 self.changeset[id] = c
209 self.changeset[id] = c
129 self.files[id] = files
210 self.files[id] = files
130 else:
211 else:
131 file,rev = l[1:-2].rsplit(':',1)
212 file,rev = l[1:-2].rsplit(':',1)
132 rev = rev.split("->")[1]
213 rev = rev.split("->")[1]
133 files[file] = rev
214 files[file] = rev
134
215
135 self.heads = self.lastbranch.values()
216 self.heads = self.lastbranch.values()
136 finally:
217 finally:
137 os.chdir(d)
218 os.chdir(d)
138
219
139 def _connect(self):
220 def _connect(self):
140 root = self.cvsroot
221 root = self.cvsroot
141 conntype = None
222 conntype = None
142 user, host = None, None
223 user, host = None, None
143 cmd = ['cvs', 'server']
224 cmd = ['cvs', 'server']
144
225
145 status("connecting to %s\n" % root)
226 status("connecting to %s\n" % root)
146
227
147 if root.startswith(":pserver:"):
228 if root.startswith(":pserver:"):
148 root = root[9:]
229 root = root[9:]
149 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root)
230 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root)
150 if m:
231 if m:
151 conntype = "pserver"
232 conntype = "pserver"
152 user, passw, serv, port, root = m.groups()
233 user, passw, serv, port, root = m.groups()
153 if not user:
234 if not user:
154 user = "anonymous"
235 user = "anonymous"
155 rr = ":pserver:" + user + "@" + serv + ":" + root
236 rr = ":pserver:" + user + "@" + serv + ":" + root
156 if port:
237 if port:
157 rr2, port = "-", int(port)
238 rr2, port = "-", int(port)
158 else:
239 else:
159 rr2, port = rr, 2401
240 rr2, port = rr, 2401
160 rr += str(port)
241 rr += str(port)
161
242
162 if not passw:
243 if not passw:
163 passw = "A"
244 passw = "A"
164 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
245 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
165 for l in pf:
246 for l in pf:
166 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
247 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
167 m = re.match(r'(/\d+\s+/)?(.*)', l)
248 m = re.match(r'(/\d+\s+/)?(.*)', l)
168 l = m.group(2)
249 l = m.group(2)
169 w, p = l.split(' ', 1)
250 w, p = l.split(' ', 1)
170 if w in [rr, rr2]:
251 if w in [rr, rr2]:
171 passw = p
252 passw = p
172 break
253 break
173 pf.close()
254 pf.close()
174
255
175 sck = socket.socket()
256 sck = socket.socket()
176 sck.connect((serv, port))
257 sck.connect((serv, port))
177 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw, "END AUTH REQUEST", ""]))
258 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw, "END AUTH REQUEST", ""]))
178 if sck.recv(128) != "I LOVE YOU\n":
259 if sck.recv(128) != "I LOVE YOU\n":
179 raise NoRepo("CVS pserver authentication failed")
260 raise NoRepo("CVS pserver authentication failed")
180
261
181 self.writep = self.readp = sck.makefile('r+')
262 self.writep = self.readp = sck.makefile('r+')
182
263
183 if not conntype and root.startswith(":local:"):
264 if not conntype and root.startswith(":local:"):
184 conntype = "local"
265 conntype = "local"
185 root = root[7:]
266 root = root[7:]
186
267
187 if not conntype:
268 if not conntype:
188 # :ext:user@host/home/user/path/to/cvsroot
269 # :ext:user@host/home/user/path/to/cvsroot
189 if root.startswith(":ext:"):
270 if root.startswith(":ext:"):
190 root = root[5:]
271 root = root[5:]
191 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
272 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
192 if not m:
273 if not m:
193 conntype = "local"
274 conntype = "local"
194 else:
275 else:
195 conntype = "rsh"
276 conntype = "rsh"
196 user, host, root = m.group(1), m.group(2), m.group(3)
277 user, host, root = m.group(1), m.group(2), m.group(3)
197
278
198 if conntype != "pserver":
279 if conntype != "pserver":
199 if conntype == "rsh":
280 if conntype == "rsh":
200 rsh = os.environ.get("CVS_RSH" or "rsh")
281 rsh = os.environ.get("CVS_RSH" or "rsh")
201 if user:
282 if user:
202 cmd = [rsh, '-l', user, host] + cmd
283 cmd = [rsh, '-l', user, host] + cmd
203 else:
284 else:
204 cmd = [rsh, host] + cmd
285 cmd = [rsh, host] + cmd
205
286
206 self.writep, self.readp = os.popen2(cmd)
287 self.writep, self.readp = os.popen2(cmd)
207
288
208 self.realroot = root
289 self.realroot = root
209
290
210 self.writep.write("Root %s\n" % root)
291 self.writep.write("Root %s\n" % root)
211 self.writep.write("Valid-responses ok error Valid-requests Mode"
292 self.writep.write("Valid-responses ok error Valid-requests Mode"
212 " M Mbinary E Checked-in Created Updated"
293 " M Mbinary E Checked-in Created Updated"
213 " Merged Removed\n")
294 " Merged Removed\n")
214 self.writep.write("valid-requests\n")
295 self.writep.write("valid-requests\n")
215 self.writep.flush()
296 self.writep.flush()
216 r = self.readp.readline()
297 r = self.readp.readline()
217 if not r.startswith("Valid-requests"):
298 if not r.startswith("Valid-requests"):
218 abort("server sucks\n")
299 abort("server sucks\n")
219 if "UseUnchanged" in r:
300 if "UseUnchanged" in r:
220 self.writep.write("UseUnchanged\n")
301 self.writep.write("UseUnchanged\n")
221 self.writep.flush()
302 self.writep.flush()
222 r = self.readp.readline()
303 r = self.readp.readline()
223
304
224 def getheads(self):
305 def getheads(self):
225 return self.heads
306 return self.heads
226
307
227 def _getfile(self, name, rev):
308 def _getfile(self, name, rev):
228 if rev.endswith("(DEAD)"):
309 if rev.endswith("(DEAD)"):
229 raise IOError
310 raise IOError
230
311
231 args = ("-N -P -kk -r %s --" % rev).split()
312 args = ("-N -P -kk -r %s --" % rev).split()
232 args.append(os.path.join(self.cvsrepo, name))
313 args.append(os.path.join(self.cvsrepo, name))
233 for x in args:
314 for x in args:
234 self.writep.write("Argument %s\n" % x)
315 self.writep.write("Argument %s\n" % x)
235 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
316 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
236 self.writep.flush()
317 self.writep.flush()
237
318
238 data = ""
319 data = ""
239 while 1:
320 while 1:
240 line = self.readp.readline()
321 line = self.readp.readline()
241 if line.startswith("Created ") or line.startswith("Updated "):
322 if line.startswith("Created ") or line.startswith("Updated "):
242 self.readp.readline() # path
323 self.readp.readline() # path
243 self.readp.readline() # entries
324 self.readp.readline() # entries
244 mode = self.readp.readline()[:-1]
325 mode = self.readp.readline()[:-1]
245 count = int(self.readp.readline()[:-1])
326 count = int(self.readp.readline()[:-1])
246 data = self.readp.read(count)
327 data = self.readp.read(count)
247 elif line.startswith(" "):
328 elif line.startswith(" "):
248 data += line[1:]
329 data += line[1:]
249 elif line.startswith("M "):
330 elif line.startswith("M "):
250 pass
331 pass
251 elif line.startswith("Mbinary "):
332 elif line.startswith("Mbinary "):
252 count = int(self.readp.readline()[:-1])
333 count = int(self.readp.readline()[:-1])
253 data = self.readp.read(count)
334 data = self.readp.read(count)
254 else:
335 else:
255 if line == "ok\n":
336 if line == "ok\n":
256 return (data, "x" in mode and "x" or "")
337 return (data, "x" in mode and "x" or "")
257 elif line.startswith("E "):
338 elif line.startswith("E "):
258 warn("cvs server: %s\n" % line[2:])
339 warn("cvs server: %s\n" % line[2:])
259 elif line.startswith("Remove"):
340 elif line.startswith("Remove"):
260 l = self.readp.readline()
341 l = self.readp.readline()
261 l = self.readp.readline()
342 l = self.readp.readline()
262 if l != "ok\n":
343 if l != "ok\n":
263 abort("unknown CVS response: %s\n" % l)
344 abort("unknown CVS response: %s\n" % l)
264 else:
345 else:
265 abort("unknown CVS response: %s\n" % line)
346 abort("unknown CVS response: %s\n" % line)
266
347
267 def getfile(self, file, rev):
348 def getfile(self, file, rev):
268 data, mode = self._getfile(file, rev)
349 data, mode = self._getfile(file, rev)
269 self.modecache[(file, rev)] = mode
350 self.modecache[(file, rev)] = mode
270 return data
351 return data
271
352
272 def getmode(self, file, rev):
353 def getmode(self, file, rev):
273 return self.modecache[(file, rev)]
354 return self.modecache[(file, rev)]
274
355
275 def getchanges(self, rev):
356 def getchanges(self, rev):
276 self.modecache = {}
357 self.modecache = {}
277 files = self.files[rev]
358 files = self.files[rev]
278 cl = files.items()
359 cl = files.items()
279 cl.sort()
360 cl.sort()
280 return cl
361 return cl
281
362
282 def recode(self, text):
363 def recode(self, text):
283 return text.decode(self.encoding, "replace").encode("utf-8")
364 return text.decode(self.encoding, "replace").encode("utf-8")
284
365
285 def getcommit(self, rev):
366 def getcommit(self, rev):
286 return self.changeset[rev]
367 return self.changeset[rev]
287
368
288 def gettags(self):
369 def gettags(self):
289 return self.tags
370 return self.tags
290
371
291 class convert_git:
372 class convert_git(converter_source):
292 def __init__(self, path):
373 def __init__(self, path):
293 if os.path.isdir(path + "/.git"):
374 if os.path.isdir(path + "/.git"):
294 path += "/.git"
375 path += "/.git"
295 self.path = path
376 self.path = path
296 if not os.path.exists(path + "/objects"):
377 if not os.path.exists(path + "/objects"):
297 raise NoRepo("couldn't open GIT repo %s" % path)
378 raise NoRepo("couldn't open GIT repo %s" % path)
298
379
299 def getheads(self):
380 def getheads(self):
300 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
381 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
301 return [fh.read()[:-1]]
382 return [fh.read()[:-1]]
302
383
303 def catfile(self, rev, type):
384 def catfile(self, rev, type):
304 if rev == "0" * 40: raise IOError()
385 if rev == "0" * 40: raise IOError()
305 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev))
386 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev))
306 return fh.read()
387 return fh.read()
307
388
308 def getfile(self, name, rev):
389 def getfile(self, name, rev):
309 return self.catfile(rev, "blob")
390 return self.catfile(rev, "blob")
310
391
311 def getmode(self, name, rev):
392 def getmode(self, name, rev):
312 return self.modecache[(name, rev)]
393 return self.modecache[(name, rev)]
313
394
314 def getchanges(self, version):
395 def getchanges(self, version):
315 self.modecache = {}
396 self.modecache = {}
316 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version))
397 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version))
317 changes = []
398 changes = []
318 for l in fh:
399 for l in fh:
319 if "\t" not in l: continue
400 if "\t" not in l: continue
320 m, f = l[:-1].split("\t")
401 m, f = l[:-1].split("\t")
321 m = m.split()
402 m = m.split()
322 h = m[3]
403 h = m[3]
323 p = (m[1] == "100755")
404 p = (m[1] == "100755")
324 s = (m[1] == "120000")
405 s = (m[1] == "120000")
325 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
406 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
326 changes.append((f, h))
407 changes.append((f, h))
327 return changes
408 return changes
328
409
329 def getcommit(self, version):
410 def getcommit(self, version):
330 c = self.catfile(version, "commit") # read the commit hash
411 c = self.catfile(version, "commit") # read the commit hash
331 end = c.find("\n\n")
412 end = c.find("\n\n")
332 message = c[end+2:]
413 message = c[end+2:]
333 message = recode(message)
414 message = recode(message)
334 l = c[:end].splitlines()
415 l = c[:end].splitlines()
335 manifest = l[0].split()[1]
416 manifest = l[0].split()[1]
336 parents = []
417 parents = []
337 for e in l[1:]:
418 for e in l[1:]:
338 n,v = e.split(" ", 1)
419 n,v = e.split(" ", 1)
339 if n == "author":
420 if n == "author":
340 p = v.split()
421 p = v.split()
341 tm, tz = p[-2:]
422 tm, tz = p[-2:]
342 author = " ".join(p[:-2])
423 author = " ".join(p[:-2])
343 if author[0] == "<": author = author[1:-1]
424 if author[0] == "<": author = author[1:-1]
344 author = recode(author)
425 author = recode(author)
345 if n == "committer":
426 if n == "committer":
346 p = v.split()
427 p = v.split()
347 tm, tz = p[-2:]
428 tm, tz = p[-2:]
348 committer = " ".join(p[:-2])
429 committer = " ".join(p[:-2])
349 if committer[0] == "<": committer = committer[1:-1]
430 if committer[0] == "<": committer = committer[1:-1]
350 committer = recode(committer)
431 committer = recode(committer)
351 message += "\ncommitter: %s\n" % committer
432 message += "\ncommitter: %s\n" % committer
352 if n == "parent": parents.append(v)
433 if n == "parent": parents.append(v)
353
434
354 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
435 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
355 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
436 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
356 date = tm + " " + str(tz)
437 date = tm + " " + str(tz)
357
438
358 c = commit(parents=parents, date=date, author=author, desc=message)
439 c = commit(parents=parents, date=date, author=author, desc=message)
359 return c
440 return c
360
441
361 def gettags(self):
442 def gettags(self):
362 tags = {}
443 tags = {}
363 fh = os.popen('git-ls-remote --tags "%s" 2>/dev/null' % self.path)
444 fh = os.popen('git-ls-remote --tags "%s" 2>/dev/null' % self.path)
364 prefix = 'refs/tags/'
445 prefix = 'refs/tags/'
365 for line in fh:
446 for line in fh:
366 line = line.strip()
447 line = line.strip()
367 if not line.endswith("^{}"):
448 if not line.endswith("^{}"):
368 continue
449 continue
369 node, tag = line.split(None, 1)
450 node, tag = line.split(None, 1)
370 if not tag.startswith(prefix):
451 if not tag.startswith(prefix):
371 continue
452 continue
372 tag = tag[len(prefix):-3]
453 tag = tag[len(prefix):-3]
373 tags[tag] = node
454 tags[tag] = node
374
455
375 return tags
456 return tags
376
457
377 class convert_mercurial:
458 class convert_mercurial(converter_sink):
378 def __init__(self, path):
459 def __init__(self, path):
379 self.path = path
460 self.path = path
380 u = ui.ui()
461 u = ui.ui()
381 try:
462 try:
382 self.repo = hg.repository(u, path)
463 self.repo = hg.repository(u, path)
383 except:
464 except:
384 raise NoRepo("could open hg repo %s" % path)
465 raise NoRepo("could open hg repo %s" % path)
385
466
386 def mapfile(self):
467 def mapfile(self):
387 return os.path.join(self.path, ".hg", "shamap")
468 return os.path.join(self.path, ".hg", "shamap")
388
469
389 def getheads(self):
470 def getheads(self):
390 h = self.repo.changelog.heads()
471 h = self.repo.changelog.heads()
391 return [ hg.hex(x) for x in h ]
472 return [ hg.hex(x) for x in h ]
392
473
393 def putfile(self, f, e, data):
474 def putfile(self, f, e, data):
394 self.repo.wwrite(f, data, e)
475 self.repo.wwrite(f, data, e)
395 if self.repo.dirstate.state(f) == '?':
476 if self.repo.dirstate.state(f) == '?':
396 self.repo.dirstate.update([f], "a")
477 self.repo.dirstate.update([f], "a")
397
478
398 def delfile(self, f):
479 def delfile(self, f):
399 try:
480 try:
400 os.unlink(self.repo.wjoin(f))
481 os.unlink(self.repo.wjoin(f))
401 #self.repo.remove([f])
482 #self.repo.remove([f])
402 except:
483 except:
403 pass
484 pass
404
485
405 def putcommit(self, files, parents, commit):
486 def putcommit(self, files, parents, commit):
406 seen = {}
487 seen = {}
407 pl = []
488 pl = []
408 for p in parents:
489 for p in parents:
409 if p not in seen:
490 if p not in seen:
410 pl.append(p)
491 pl.append(p)
411 seen[p] = 1
492 seen[p] = 1
412 parents = pl
493 parents = pl
413
494
414 if len(parents) < 2: parents.append("0" * 40)
495 if len(parents) < 2: parents.append("0" * 40)
415 if len(parents) < 2: parents.append("0" * 40)
496 if len(parents) < 2: parents.append("0" * 40)
416 p2 = parents.pop(0)
497 p2 = parents.pop(0)
417
498
418 text = commit.desc
499 text = commit.desc
419 extra = {}
500 extra = {}
420 try:
501 try:
421 extra["branch"] = commit.branch
502 extra["branch"] = commit.branch
422 except AttributeError:
503 except AttributeError:
423 pass
504 pass
424
505
425 while parents:
506 while parents:
426 p1 = p2
507 p1 = p2
427 p2 = parents.pop(0)
508 p2 = parents.pop(0)
428 a = self.repo.rawcommit(files, text, commit.author, commit.date,
509 a = self.repo.rawcommit(files, text, commit.author, commit.date,
429 hg.bin(p1), hg.bin(p2), extra=extra)
510 hg.bin(p1), hg.bin(p2), extra=extra)
430 text = "(octopus merge fixup)\n"
511 text = "(octopus merge fixup)\n"
431 p2 = hg.hex(self.repo.changelog.tip())
512 p2 = hg.hex(self.repo.changelog.tip())
432
513
433 return p2
514 return p2
434
515
435 def puttags(self, tags):
516 def puttags(self, tags):
436 try:
517 try:
437 old = self.repo.wfile(".hgtags").read()
518 old = self.repo.wfile(".hgtags").read()
438 oldlines = old.splitlines(1)
519 oldlines = old.splitlines(1)
439 oldlines.sort()
520 oldlines.sort()
440 except:
521 except:
441 oldlines = []
522 oldlines = []
442
523
443 k = tags.keys()
524 k = tags.keys()
444 k.sort()
525 k.sort()
445 newlines = []
526 newlines = []
446 for tag in k:
527 for tag in k:
447 newlines.append("%s %s\n" % (tags[tag], tag))
528 newlines.append("%s %s\n" % (tags[tag], tag))
448
529
449 newlines.sort()
530 newlines.sort()
450
531
451 if newlines != oldlines:
532 if newlines != oldlines:
452 status("updating tags\n")
533 status("updating tags\n")
453 f = self.repo.wfile(".hgtags", "w")
534 f = self.repo.wfile(".hgtags", "w")
454 f.write("".join(newlines))
535 f.write("".join(newlines))
455 f.close()
536 f.close()
456 if not oldlines: self.repo.add([".hgtags"])
537 if not oldlines: self.repo.add([".hgtags"])
457 date = "%s 0" % int(time.mktime(time.gmtime()))
538 date = "%s 0" % int(time.mktime(time.gmtime()))
458 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
539 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
459 date, self.repo.changelog.tip(), hg.nullid)
540 date, self.repo.changelog.tip(), hg.nullid)
460 return hg.hex(self.repo.changelog.tip())
541 return hg.hex(self.repo.changelog.tip())
461
542
462 converters = [convert_cvs, convert_git, convert_mercurial]
543 converters = [convert_cvs, convert_git, convert_mercurial]
463
544
464 def converter(path):
545 def converter(path):
465 if not os.path.isdir(path):
546 if not os.path.isdir(path):
466 abort("%s: not a directory\n" % path)
547 abort("%s: not a directory\n" % path)
467 for c in converters:
548 for c in converters:
468 try:
549 try:
469 return c(path)
550 return c(path)
470 except NoRepo:
551 except NoRepo:
471 pass
552 pass
472 abort("%s: unknown repository type\n" % path)
553 abort("%s: unknown repository type\n" % path)
473
554
474 class convert:
555 class convert(object):
475 def __init__(self, source, dest, mapfile, opts):
556 def __init__(self, source, dest, mapfile, opts):
476
557
477 self.source = source
558 self.source = source
478 self.dest = dest
559 self.dest = dest
479 self.mapfile = mapfile
560 self.mapfile = mapfile
480 self.opts = opts
561 self.opts = opts
481 self.commitcache = {}
562 self.commitcache = {}
482
563
483 self.map = {}
564 self.map = {}
484 try:
565 try:
485 for l in file(self.mapfile):
566 for l in file(self.mapfile):
486 sv, dv = l[:-1].split()
567 sv, dv = l[:-1].split()
487 self.map[sv] = dv
568 self.map[sv] = dv
488 except IOError:
569 except IOError:
489 pass
570 pass
490
571
491 def walktree(self, heads):
572 def walktree(self, heads):
492 visit = heads
573 visit = heads
493 known = {}
574 known = {}
494 parents = {}
575 parents = {}
495 while visit:
576 while visit:
496 n = visit.pop(0)
577 n = visit.pop(0)
497 if n in known or n in self.map: continue
578 if n in known or n in self.map: continue
498 known[n] = 1
579 known[n] = 1
499 self.commitcache[n] = self.source.getcommit(n)
580 self.commitcache[n] = self.source.getcommit(n)
500 cp = self.commitcache[n].parents
581 cp = self.commitcache[n].parents
501 for p in cp:
582 for p in cp:
502 parents.setdefault(n, []).append(p)
583 parents.setdefault(n, []).append(p)
503 visit.append(p)
584 visit.append(p)
504
585
505 return parents
586 return parents
506
587
507 def toposort(self, parents):
588 def toposort(self, parents):
508 visit = parents.keys()
589 visit = parents.keys()
509 seen = {}
590 seen = {}
510 children = {}
591 children = {}
511
592
512 while visit:
593 while visit:
513 n = visit.pop(0)
594 n = visit.pop(0)
514 if n in seen: continue
595 if n in seen: continue
515 seen[n] = 1
596 seen[n] = 1
516 pc = 0
597 pc = 0
517 if n in parents:
598 if n in parents:
518 for p in parents[n]:
599 for p in parents[n]:
519 if p not in self.map: pc += 1
600 if p not in self.map: pc += 1
520 visit.append(p)
601 visit.append(p)
521 children.setdefault(p, []).append(n)
602 children.setdefault(p, []).append(n)
522 if not pc: root = n
603 if not pc: root = n
523
604
524 s = []
605 s = []
525 removed = {}
606 removed = {}
526 visit = children.keys()
607 visit = children.keys()
527 while visit:
608 while visit:
528 n = visit.pop(0)
609 n = visit.pop(0)
529 if n in removed: continue
610 if n in removed: continue
530 dep = 0
611 dep = 0
531 if n in parents:
612 if n in parents:
532 for p in parents[n]:
613 for p in parents[n]:
533 if p in self.map: continue
614 if p in self.map: continue
534 if p not in removed:
615 if p not in removed:
535 # we're still dependent
616 # we're still dependent
536 visit.append(n)
617 visit.append(n)
537 dep = 1
618 dep = 1
538 break
619 break
539
620
540 if not dep:
621 if not dep:
541 # all n's parents are in the list
622 # all n's parents are in the list
542 removed[n] = 1
623 removed[n] = 1
543 if n not in self.map:
624 if n not in self.map:
544 s.append(n)
625 s.append(n)
545 if n in children:
626 if n in children:
546 for c in children[n]:
627 for c in children[n]:
547 visit.insert(0, c)
628 visit.insert(0, c)
548
629
549 if opts.get('datesort'):
630 if opts.get('datesort'):
550 depth = {}
631 depth = {}
551 for n in s:
632 for n in s:
552 depth[n] = 0
633 depth[n] = 0
553 pl = [p for p in self.commitcache[n].parents if p not in self.map]
634 pl = [p for p in self.commitcache[n].parents if p not in self.map]
554 if pl:
635 if pl:
555 depth[n] = max([depth[p] for p in pl]) + 1
636 depth[n] = max([depth[p] for p in pl]) + 1
556
637
557 s = [(depth[n], self.commitcache[n].date, n) for n in s]
638 s = [(depth[n], self.commitcache[n].date, n) for n in s]
558 s.sort()
639 s.sort()
559 s = [e[2] for e in s]
640 s = [e[2] for e in s]
560
641
561 return s
642 return s
562
643
563 def copy(self, rev):
644 def copy(self, rev):
564 c = self.commitcache[rev]
645 c = self.commitcache[rev]
565 files = self.source.getchanges(rev)
646 files = self.source.getchanges(rev)
566
647
567 for f,v in files:
648 for f,v in files:
568 try:
649 try:
569 data = self.source.getfile(f, v)
650 data = self.source.getfile(f, v)
570 except IOError, inst:
651 except IOError, inst:
571 self.dest.delfile(f)
652 self.dest.delfile(f)
572 else:
653 else:
573 e = self.source.getmode(f, v)
654 e = self.source.getmode(f, v)
574 self.dest.putfile(f, e, data)
655 self.dest.putfile(f, e, data)
575
656
576 r = [self.map[v] for v in c.parents]
657 r = [self.map[v] for v in c.parents]
577 f = [f for f,v in files]
658 f = [f for f,v in files]
578 self.map[rev] = self.dest.putcommit(f, r, c)
659 self.map[rev] = self.dest.putcommit(f, r, c)
579 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
660 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
580
661
581 def convert(self):
662 def convert(self):
582 status("scanning source...\n")
663 status("scanning source...\n")
583 heads = self.source.getheads()
664 heads = self.source.getheads()
584 parents = self.walktree(heads)
665 parents = self.walktree(heads)
585 status("sorting...\n")
666 status("sorting...\n")
586 t = self.toposort(parents)
667 t = self.toposort(parents)
587 num = len(t)
668 num = len(t)
588 c = None
669 c = None
589
670
590 status("converting...\n")
671 status("converting...\n")
591 for c in t:
672 for c in t:
592 num -= 1
673 num -= 1
593 desc = self.commitcache[c].desc
674 desc = self.commitcache[c].desc
594 if "\n" in desc:
675 if "\n" in desc:
595 desc = desc.splitlines()[0]
676 desc = desc.splitlines()[0]
596 status("%d %s\n" % (num, desc))
677 status("%d %s\n" % (num, desc))
597 self.copy(c)
678 self.copy(c)
598
679
599 tags = self.source.gettags()
680 tags = self.source.gettags()
600 ctags = {}
681 ctags = {}
601 for k in tags:
682 for k in tags:
602 v = tags[k]
683 v = tags[k]
603 if v in self.map:
684 if v in self.map:
604 ctags[k] = self.map[v]
685 ctags[k] = self.map[v]
605
686
606 if c and ctags:
687 if c and ctags:
607 nrev = self.dest.puttags(ctags)
688 nrev = self.dest.puttags(ctags)
608 # write another hash correspondence to override the previous
689 # write another hash correspondence to override the previous
609 # one so we don't end up with extra tag heads
690 # one so we don't end up with extra tag heads
610 if nrev:
691 if nrev:
611 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
692 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
612
693
613 def command(src, dest=None, mapfile=None, **opts):
694 def command(src, dest=None, mapfile=None, **opts):
614 srcc = converter(src)
695 srcc = converter(src)
615 if not hasattr(srcc, "getcommit"):
696 if not hasattr(srcc, "getcommit"):
616 abort("%s: can't read from this repo type\n" % src)
697 abort("%s: can't read from this repo type\n" % src)
617
698
618 if not dest:
699 if not dest:
619 dest = src + "-hg"
700 dest = src + "-hg"
620 status("assuming destination %s\n" % dest)
701 status("assuming destination %s\n" % dest)
621 if not os.path.isdir(dest):
702 if not os.path.isdir(dest):
622 status("creating repository %s\n" % dest)
703 status("creating repository %s\n" % dest)
623 os.system("hg init " + dest)
704 os.system("hg init " + dest)
624 destc = converter(dest)
705 destc = converter(dest)
625 if not hasattr(destc, "putcommit"):
706 if not hasattr(destc, "putcommit"):
626 abort("%s: can't write to this repo type\n" % src)
707 abort("%s: can't write to this repo type\n" % src)
627
708
628 if not mapfile:
709 if not mapfile:
629 try:
710 try:
630 mapfile = destc.mapfile()
711 mapfile = destc.mapfile()
631 except:
712 except:
632 mapfile = os.path.join(destc, "map")
713 mapfile = os.path.join(destc, "map")
633
714
634 c = convert(srcc, destc, mapfile, opts)
715 c = convert(srcc, destc, mapfile, opts)
635 c.convert()
716 c.convert()
636
717
637 options = [('q', 'quiet', None, 'suppress output'),
718 options = [('q', 'quiet', None, 'suppress output'),
638 ('', 'datesort', None, 'try to sort changesets by date')]
719 ('', 'datesort', None, 'try to sort changesets by date')]
639 opts = {}
720 opts = {}
640 args = fancyopts.fancyopts(sys.argv[1:], options, opts)
721 args = fancyopts.fancyopts(sys.argv[1:], options, opts)
641
722
642 if opts['quiet']:
723 if opts['quiet']:
643 quiet = 1
724 quiet = 1
644
725
645 try:
726 try:
646 command(*args, **opts)
727 command(*args, **opts)
647 except Abort, inst:
728 except Abort, inst:
648 warn(inst)
729 warn(inst)
649 except KeyboardInterrupt:
730 except KeyboardInterrupt:
650 status("interrupted\n")
731 status("interrupted\n")
@@ -1,789 +1,789 b''
1 #compdef hg
1 #compdef hg
2
2
3 # Zsh completion script for mercurial. Rename this file to _hg and copy
3 # Zsh completion script for mercurial. Rename this file to _hg and copy
4 # it into your zsh function path (/usr/share/zsh/site-functions for
4 # it into your zsh function path (/usr/share/zsh/site-functions for
5 # instance)
5 # instance)
6 #
6 #
7 # Copyright (C) 2005 Steve Borho
7 # Copyright (C) 2005 Steve Borho
8 # Copyright (C) 2006 Brendan Cully <brendan@kublai.com>
8 # Copyright (C) 2006 Brendan Cully <brendan@kublai.com>
9 #
9 #
10 # This is free software; you can redistribute it and/or modify it under
10 # This is free software; you can redistribute it and/or modify it under
11 # the terms of the GNU General Public License as published by the Free
11 # the terms of the GNU General Public License as published by the Free
12 # Software Foundation; either version 2 of the License, or (at your
12 # Software Foundation; either version 2 of the License, or (at your
13 # option) any later version.
13 # option) any later version.
14 #
14 #
15
15
16 local curcontext="$curcontext" state line
16 local curcontext="$curcontext" state line
17 typeset -A _hg_cmd_globals
17 typeset -A _hg_cmd_globals
18
18
19 _hg() {
19 _hg() {
20 local cmd _hg_root
20 local cmd _hg_root
21 integer i=2
21 integer i=2
22 _hg_cmd_globals=()
22 _hg_cmd_globals=()
23
23
24 while (( i < $#words ))
24 while (( i < $#words ))
25 do
25 do
26 case "$words[$i]" in
26 case "$words[$i]" in
27 -R|--repository)
27 -R|--repository)
28 eval _hg_root="$words[$i+1]"
28 eval _hg_root="$words[$i+1]"
29 _hg_cmd_globals+=("$words[$i]" "$_hg_root")
29 _hg_cmd_globals+=("$words[$i]" "$_hg_root")
30 (( i += 2 ))
30 (( i += 2 ))
31 continue
31 continue
32 ;;
32 ;;
33 -R*)
33 -R*)
34 _hg_cmd_globals+="$words[$i]"
34 _hg_cmd_globals+="$words[$i]"
35 eval _hg_root="${words[$i]#-R}"
35 eval _hg_root="${words[$i]#-R}"
36 (( i++ ))
36 (( i++ ))
37 continue
37 continue
38 ;;
38 ;;
39 --cwd|--config)
39 --cwd|--config)
40 # pass along arguments to hg completer
40 # pass along arguments to hg completer
41 _hg_cmd_globals+=("$words[$i]" "$words[$i+1]")
41 _hg_cmd_globals+=("$words[$i]" "$words[$i+1]")
42 (( i += 2 ))
42 (( i += 2 ))
43 continue
43 continue
44 ;;
44 ;;
45 -*)
45 -*)
46 # skip option
46 # skip option
47 (( i++ ))
47 (( i++ ))
48 continue
48 continue
49 ;;
49 ;;
50 esac
50 esac
51 if [[ -z "$cmd" ]]
51 if [[ -z "$cmd" ]]
52 then
52 then
53 cmd="$words[$i]"
53 cmd="$words[$i]"
54 words[$i]=()
54 words[$i]=()
55 (( CURRENT-- ))
55 (( CURRENT-- ))
56 fi
56 fi
57 (( i++ ))
57 (( i++ ))
58 done
58 done
59
59
60 if [[ -z "$cmd" ]]
60 if [[ -z "$cmd" ]]
61 then
61 then
62 _arguments -s -w : $_hg_global_opts \
62 _arguments -s -w : $_hg_global_opts \
63 ':mercurial command:_hg_commands'
63 ':mercurial command:_hg_commands'
64 return
64 return
65 fi
65 fi
66
66
67 # resolve abbreviations and aliases
67 # resolve abbreviations and aliases
68 if ! (( $+functions[_hg_cmd_${cmd}] ))
68 if ! (( $+functions[_hg_cmd_${cmd}] ))
69 then
69 then
70 local cmdexp
70 local cmdexp
71 (( $#_hg_cmd_list )) || _hg_get_commands
71 (( $#_hg_cmd_list )) || _hg_get_commands
72
72
73 cmdexp=$_hg_cmd_list[(r)${cmd}*]
73 cmdexp=$_hg_cmd_list[(r)${cmd}*]
74 if [[ $cmdexp == $_hg_cmd_list[(R)${cmd}*] ]]
74 if [[ $cmdexp == $_hg_cmd_list[(R)${cmd}*] ]]
75 then
75 then
76 # might be nice to rewrite the command line with the expansion
76 # might be nice to rewrite the command line with the expansion
77 cmd="$cmdexp"
77 cmd="$cmdexp"
78 fi
78 fi
79 if [[ -n $_hg_alias_list[$cmd] ]]
79 if [[ -n $_hg_alias_list[$cmd] ]]
80 then
80 then
81 cmd=$_hg_alias_list[$cmd]
81 cmd=$_hg_alias_list[$cmd]
82 fi
82 fi
83 fi
83 fi
84
84
85 curcontext="${curcontext%:*:*}:hg-${cmd}:"
85 curcontext="${curcontext%:*:*}:hg-${cmd}:"
86
86
87 zstyle -s ":completion:$curcontext:" cache-policy update_policy
87 zstyle -s ":completion:$curcontext:" cache-policy update_policy
88
88
89 if [[ -z "$update_policy" ]]
89 if [[ -z "$update_policy" ]]
90 then
90 then
91 zstyle ":completion:$curcontext:" cache-policy _hg_cache_policy
91 zstyle ":completion:$curcontext:" cache-policy _hg_cache_policy
92 fi
92 fi
93
93
94 if (( $+functions[_hg_cmd_${cmd}] ))
94 if (( $+functions[_hg_cmd_${cmd}] ))
95 then
95 then
96 _hg_cmd_${cmd}
96 _hg_cmd_${cmd}
97 else
97 else
98 # complete unknown commands normally
98 # complete unknown commands normally
99 _arguments -s -w : $_hg_global_opts \
99 _arguments -s -w : $_hg_global_opts \
100 '*:files:_hg_files'
100 '*:files:_hg_files'
101 fi
101 fi
102 }
102 }
103
103
104 _hg_cache_policy() {
104 _hg_cache_policy() {
105 typeset -a old
105 typeset -a old
106
106
107 # cache for a minute
107 # cache for a minute
108 old=( "$1"(mm+10) )
108 old=( "$1"(mm+10) )
109 (( $#old )) && return 0
109 (( $#old )) && return 0
110
110
111 return 1
111 return 1
112 }
112 }
113
113
114 _hg_get_commands() {
114 _hg_get_commands() {
115 typeset -ga _hg_cmd_list
115 typeset -ga _hg_cmd_list
116 typeset -gA _hg_alias_list
116 typeset -gA _hg_alias_list
117 local hline cmd cmdalias
117 local hline cmd cmdalias
118 _call_program help hg --verbose help | while read -A hline
118 _call_program help hg --verbose help | while read -A hline
119 do
119 do
120 cmd="$hline[1]"
120 cmd="$hline[1]"
121 case $cmd in
121 case $cmd in
122 *:)
122 *:)
123 cmd=${cmd%:}
123 cmd=${cmd%:}
124 _hg_cmd_list+=($cmd)
124 _hg_cmd_list+=($cmd)
125 ;;
125 ;;
126 *,)
126 *,)
127 cmd=${cmd%,}
127 cmd=${cmd%,}
128 _hg_cmd_list+=($cmd)
128 _hg_cmd_list+=($cmd)
129 integer i=2
129 integer i=2
130 while (( i <= $#hline ))
130 while (( i <= $#hline ))
131 do
131 do
132 cmdalias=${hline[$i]%(:|,)}
132 cmdalias=${hline[$i]%(:|,)}
133 _hg_cmd_list+=($cmdalias)
133 _hg_cmd_list+=($cmdalias)
134 _hg_alias_list+=($cmdalias $cmd)
134 _hg_alias_list+=($cmdalias $cmd)
135 (( i++ ))
135 (( i++ ))
136 done
136 done
137 ;;
137 ;;
138 esac
138 esac
139 done
139 done
140 }
140 }
141
141
142 _hg_commands() {
142 _hg_commands() {
143 (( $#_hg_cmd_list )) || _hg_get_commands
143 (( $#_hg_cmd_list )) || _hg_get_commands
144 _describe -t commands 'mercurial command' _hg_cmd_list
144 _describe -t commands 'mercurial command' _hg_cmd_list
145 }
145 }
146
146
147 _hg_revrange() {
147 _hg_revrange() {
148 compset -P 1 '*:'
148 compset -P 1 '*:'
149 _hg_tags "$@"
149 _hg_tags "$@"
150 }
150 }
151
151
152 _hg_tags() {
152 _hg_tags() {
153 typeset -a tags
153 typeset -a tags
154 local tag rev
154 local tag rev
155
155
156 _hg_cmd tags 2> /dev/null | while read tag rev
156 _hg_cmd tags 2> /dev/null | while read tag rev
157 do
157 do
158 tags+=($tag)
158 tags+=($tag)
159 done
159 done
160 (( $#tags )) && _describe -t tags 'tags' tags
160 (( $#tags )) && _describe -t tags 'tags' tags
161 }
161 }
162
162
163 _hg_files() {
163 _hg_files() {
164 if [[ -n "$_hg_root" ]]
164 if [[ -n "$_hg_root" ]]
165 then
165 then
166 [[ -d "$_hg_root/.hg" ]] || return
166 [[ -d "$_hg_root/.hg" ]] || return
167 case "$_hg_root" in
167 case "$_hg_root" in
168 /*)
168 /*)
169 _files -W $_hg_root
169 _files -W $_hg_root
170 ;;
170 ;;
171 *)
171 *)
172 _files -W $PWD/$_hg_root
172 _files -W $PWD/$_hg_root
173 ;;
173 ;;
174 esac
174 esac
175 else
175 else
176 _files
176 _files
177 fi
177 fi
178 }
178 }
179
179
180 _hg_status() {
180 _hg_status() {
181 [[ -d $PREFIX ]] || PREFIX=$PREFIX:h
181 [[ -d $PREFIX ]] || PREFIX=$PREFIX:h
182 status_files=(${(ps:\0:)"$(_hg_cmd status -0n$1 ./$PREFIX 2>/dev/null)"})
182 status_files=(${(ps:\0:)"$(_hg_cmd status -0n$1 ./$PREFIX 2>/dev/null)"})
183 }
183 }
184
184
185 _hg_unknown() {
185 _hg_unknown() {
186 typeset -a status_files
186 typeset -a status_files
187 _hg_status u
187 _hg_status u
188 _wanted files expl 'unknown files' _multi_parts / status_files
188 _wanted files expl 'unknown files' _multi_parts / status_files
189 }
189 }
190
190
191 _hg_missing() {
191 _hg_missing() {
192 typeset -a status_files
192 typeset -a status_files
193 _hg_status d
193 _hg_status d
194 _wanted files expl 'missing files' _multi_parts / status_files
194 _wanted files expl 'missing files' _multi_parts / status_files
195 }
195 }
196
196
197 _hg_modified() {
197 _hg_modified() {
198 typeset -a status_files
198 typeset -a status_files
199 _hg_status m
199 _hg_status m
200 _wanted files expl 'modified files' _multi_parts / status_files
200 _wanted files expl 'modified files' _multi_parts / status_files
201 }
201 }
202
202
203 _hg_addremove() {
203 _hg_addremove() {
204 _alternative 'files:unknown files:_hg_unknown' \
204 _alternative 'files:unknown files:_hg_unknown' \
205 'files:missing files:_hg_missing'
205 'files:missing files:_hg_missing'
206 }
206 }
207
207
208 _hg_ssh_urls() {
208 _hg_ssh_urls() {
209 if [[ -prefix */ ]]
209 if [[ -prefix */ ]]
210 then
210 then
211 if zstyle -T ":completion:${curcontext}:files" remote-access
211 if zstyle -T ":completion:${curcontext}:files" remote-access
212 then
212 then
213 local host=${PREFIX%%/*}
213 local host=${PREFIX%%/*}
214 typeset -a remdirs
214 typeset -a remdirs
215 compset -p $(( $#host + 1 ))
215 compset -p $(( $#host + 1 ))
216 local rempath=${(M)PREFIX##*/}
216 local rempath=${(M)PREFIX##*/}
217 local cacheid="hg:${host}-${rempath//\//_}"
217 local cacheid="hg:${host}-${rempath//\//_}"
218 cacheid=${cacheid%[-_]}
218 cacheid=${cacheid%[-_]}
219 compset -P '*/'
219 compset -P '*/'
220 if _cache_invalid "$cacheid" || ! _retrieve_cache "$cacheid"
220 if _cache_invalid "$cacheid" || ! _retrieve_cache "$cacheid"
221 then
221 then
222 remdirs=(${${(M)${(f)"$(_call_program files ssh -a -x $host ls -1FL "${(q)rempath}" 2> /dev/null)"}##*/}%/})
222 remdirs=(${${(M)${(f)"$(_call_program files ssh -a -x $host ls -1FL "${(q)rempath}" 2> /dev/null)"}##*/}%/})
223 _store_cache "$cacheid" remdirs
223 _store_cache "$cacheid" remdirs
224 fi
224 fi
225 _describe -t directories 'remote directory' remdirs -S/
225 _describe -t directories 'remote directory' remdirs -S/
226 else
226 else
227 _message 'remote directory'
227 _message 'remote directory'
228 fi
228 fi
229 else
229 else
230 if compset -P '*@'
230 if compset -P '*@'
231 then
231 then
232 _hosts -S/
232 _hosts -S/
233 else
233 else
234 _alternative 'hosts:remote host name:_hosts -S/' \
234 _alternative 'hosts:remote host name:_hosts -S/' \
235 'users:user:_users -S@'
235 'users:user:_users -S@'
236 fi
236 fi
237 fi
237 fi
238 }
238 }
239
239
240 _hg_urls() {
240 _hg_urls() {
241 if compset -P bundle://
241 if compset -P bundle://
242 then
242 then
243 _files
243 _files
244 elif compset -P ssh://
244 elif compset -P ssh://
245 then
245 then
246 _hg_ssh_urls
246 _hg_ssh_urls
247 elif [[ -prefix *: ]]
247 elif [[ -prefix *: ]]
248 then
248 then
249 _urls
249 _urls
250 else
250 else
251 local expl
251 local expl
252 compset -S '[^:]*'
252 compset -S '[^:]*'
253 _wanted url-schemas expl 'URL schema' compadd -S '' - \
253 _wanted url-schemas expl 'URL schema' compadd -S '' - \
254 http:// https:// ssh:// bundle://
254 http:// https:// ssh:// bundle://
255 fi
255 fi
256 }
256 }
257
257
258 _hg_paths() {
258 _hg_paths() {
259 typeset -a paths pnames
259 typeset -a paths pnames
260 _hg_cmd paths 2> /dev/null | while read -A pnames
260 _hg_cmd paths 2> /dev/null | while read -A pnames
261 do
261 do
262 paths+=($pnames[1])
262 paths+=($pnames[1])
263 done
263 done
264 (( $#paths )) && _describe -t path-aliases 'repository alias' paths
264 (( $#paths )) && _describe -t path-aliases 'repository alias' paths
265 }
265 }
266
266
267 _hg_remote() {
267 _hg_remote() {
268 _alternative 'path-aliases:repository alias:_hg_paths' \
268 _alternative 'path-aliases:repository alias:_hg_paths' \
269 'directories:directory:_files -/' \
269 'directories:directory:_files -/' \
270 'urls:URL:_hg_urls'
270 'urls:URL:_hg_urls'
271 }
271 }
272
272
273 _hg_clone_dest() {
273 _hg_clone_dest() {
274 _alternative 'directories:directory:_files -/' \
274 _alternative 'directories:directory:_files -/' \
275 'urls:URL:_hg_urls'
275 'urls:URL:_hg_urls'
276 }
276 }
277
277
278 # Common options
278 # Common options
279 _hg_global_opts=(
279 _hg_global_opts=(
280 '(--repository -R)'{-R+,--repository}'[repository root directory]:repository:_files -/'
280 '(--repository -R)'{-R+,--repository}'[repository root directory]:repository:_files -/'
281 '--cwd[change working directory]:new working directory:_files -/'
281 '--cwd[change working directory]:new working directory:_files -/'
282 '(--noninteractive -y)'{-y,--noninteractive}'[do not prompt, assume yes for any required answers]'
282 '(--noninteractive -y)'{-y,--noninteractive}'[do not prompt, assume yes for any required answers]'
283 '(--verbose -v)'{-v,--verbose}'[enable additional output]'
283 '(--verbose -v)'{-v,--verbose}'[enable additional output]'
284 '(--quiet -q)'{-q,--quiet}'[suppress output]'
284 '(--quiet -q)'{-q,--quiet}'[suppress output]'
285 '(--help -h)'{-h,--help}'[display help and exit]'
285 '(--help -h)'{-h,--help}'[display help and exit]'
286 '--debug[debug mode]'
286 '--debug[debug mode]'
287 '--debugger[start debugger]'
287 '--debugger[start debugger]'
288 '--traceback[print traceback on exception]'
288 '--traceback[print traceback on exception]'
289 '--time[time how long the command takes]'
289 '--time[time how long the command takes]'
290 '--profile[profile]'
290 '--profile[profile]'
291 '--version[output version information and exit]'
291 '--version[output version information and exit]'
292 )
292 )
293
293
294 _hg_pat_opts=(
294 _hg_pat_opts=(
295 '*'{-I+,--include}'[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/'
295 '*'{-I+,--include}'[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/'
296 '*'{-X+,--exclude}'[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/')
296 '*'{-X+,--exclude}'[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/')
297
297
298 _hg_diff_opts=(
298 _hg_diff_opts=(
299 '(--text -a)'{-a,--text}'[treat all files as text]'
299 '(--text -a)'{-a,--text}'[treat all files as text]'
300 '(--git -g)'{-g,--git}'[use git extended diff format]'
300 '(--git -g)'{-g,--git}'[use git extended diff format]'
301 "--nodates[don't include dates in diff headers]")
301 "--nodates[don't include dates in diff headers]")
302
302
303 _hg_dryrun_opts=(
303 _hg_dryrun_opts=(
304 '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]')
304 '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]')
305
305
306 _hg_style_opts=(
306 _hg_style_opts=(
307 '--style[display using template map file]:'
307 '--style[display using template map file]:'
308 '--template[display with template]:')
308 '--template[display with template]:')
309
309
310 _hg_commit_opts=(
310 _hg_commit_opts=(
311 '(-m --message -l --logfile --edit -e)'{-e,--edit}'[edit commit message]'
311 '(-m --message -l --logfile --edit -e)'{-e,--edit}'[edit commit message]'
312 '(-e --edit -l --logfile --message -m)'{-m+,--message}'[use <text> as commit message]:message:'
312 '(-e --edit -l --logfile --message -m)'{-m+,--message}'[use <text> as commit message]:message:'
313 '(-e --edit -m --message --logfile -l)'{-l+,--logfile}'[read the commit message from <file>]:log file:_files')
313 '(-e --edit -m --message --logfile -l)'{-l+,--logfile}'[read the commit message from <file>]:log file:_files')
314
314
315 _hg_remote_opts=(
315 _hg_remote_opts=(
316 '(--ssh -e)'{-e+,--ssh}'[specify ssh command to use]:'
316 '(--ssh -e)'{-e+,--ssh}'[specify ssh command to use]:'
317 '--remotecmd[specify hg command to run on the remote side]:')
317 '--remotecmd[specify hg command to run on the remote side]:')
318
318
319 _hg_cmd() {
319 _hg_cmd() {
320 _call_program hg hg "$_hg_cmd_globals[@]" "$@"
320 _call_program hg hg "$_hg_cmd_globals[@]" "$@"
321 }
321 }
322
322
323 _hg_cmd_add() {
323 _hg_cmd_add() {
324 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
324 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
325 '*:unknown files:_hg_unknown'
325 '*:unknown files:_hg_unknown'
326 }
326 }
327
327
328 _hg_cmd_addremove() {
328 _hg_cmd_addremove() {
329 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
329 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
330 '(--similarity -s)'{-s+,--similarity}'[guess renamed files by similarity (0<=s<=100)]:' \
330 '(--similarity -s)'{-s+,--similarity}'[guess renamed files by similarity (0<=s<=100)]:' \
331 '*:unknown or missing files:_hg_addremove'
331 '*:unknown or missing files:_hg_addremove'
332 }
332 }
333
333
334 _hg_cmd_annotate() {
334 _hg_cmd_annotate() {
335 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
335 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
336 '(--rev -r)'{-r+,--rev}'[annotate the specified revision]:revision:_hg_tags' \
336 '(--rev -r)'{-r+,--rev}'[annotate the specified revision]:revision:_hg_tags' \
337 '(--follow -f)'{-f,--follow}'[follow file copies and renames]' \
337 '(--follow -f)'{-f,--follow}'[follow file copies and renames]' \
338 '(--text -a)'{-a,--text}'[treat all files as text]' \
338 '(--text -a)'{-a,--text}'[treat all files as text]' \
339 '(--user -u)'{-u,--user}'[list the author]' \
339 '(--user -u)'{-u,--user}'[list the author]' \
340 '(--date -d)'{-d,--date}'[list the date]' \
340 '(--date -d)'{-d,--date}'[list the date]' \
341 '(--number -n)'{-n,--number}'[list the revision number (default)]' \
341 '(--number -n)'{-n,--number}'[list the revision number (default)]' \
342 '(--changeset -c)'{-c,--changeset}'[list the changeset]' \
342 '(--changeset -c)'{-c,--changeset}'[list the changeset]' \
343 '*:files:_hg_files'
343 '*:files:_hg_files'
344 }
344 }
345
345
346 _hg_cmd_archive() {
346 _hg_cmd_archive() {
347 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
347 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
348 '--no-decode[do not pass files through decoders]' \
348 '--no-decode[do not pass files through decoders]' \
349 '(--prefix -p)'{-p+,--prefix}'[directory prefix for files in archive]:' \
349 '(--prefix -p)'{-p+,--prefix}'[directory prefix for files in archive]:' \
350 '(--rev -r)'{-r+,--rev}'[revision to distribute]:revision:_hg_tags' \
350 '(--rev -r)'{-r+,--rev}'[revision to distribute]:revision:_hg_tags' \
351 '(--type -t)'{-t+,--type}'[type of distribution to create]:archive type:(files tar tbz2 tgz uzip zip)' \
351 '(--type -t)'{-t+,--type}'[type of distribution to create]:archive type:(files tar tbz2 tgz uzip zip)' \
352 '*:destination:_files'
352 '*:destination:_files'
353 }
353 }
354
354
355 _hg_cmd_bundle() {
355 _hg_cmd_bundle() {
356 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
356 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
357 '(--force -f)'{-f,--force}'[run even when remote repository is unrelated]' \
357 '(--force -f)'{-f,--force}'[run even when remote repository is unrelated]' \
358 '(2)*--base[a base changeset to specify instead of a destination]:revision:_hg_tags' \
358 '(2)*--base[a base changeset to specify instead of a destination]:revision:_hg_tags' \
359 ':output file:_files' \
359 ':output file:_files' \
360 ':destination repository:_files -/'
360 ':destination repository:_files -/'
361 }
361 }
362
362
363 _hg_cmd_cat() {
363 _hg_cmd_cat() {
364 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
364 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
365 '(--output -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \
365 '(--output -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \
366 '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_tags' \
366 '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_tags' \
367 '*:file:_hg_files'
367 '*:file:_hg_files'
368 }
368 }
369
369
370 _hg_cmd_clone() {
370 _hg_cmd_clone() {
371 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
371 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
372 '(--noupdate -U)'{-U,--noupdate}'[do not update the new working directory]' \
372 '(--noupdate -U)'{-U,--noupdate}'[do not update the new working directory]' \
373 '(--rev -r)'{-r+,--rev}'[a changeset you would like to have after cloning]:' \
373 '(--rev -r)'{-r+,--rev}'[a changeset you would like to have after cloning]:' \
374 '--uncompressed[use uncompressed transfer (fast over LAN)]' \
374 '--uncompressed[use uncompressed transfer (fast over LAN)]' \
375 ':source repository:_hg_remote' \
375 ':source repository:_hg_remote' \
376 ':destination:_hg_clone_dest'
376 ':destination:_hg_clone_dest'
377 }
377 }
378
378
379 _hg_cmd_commit() {
379 _hg_cmd_commit() {
380 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
380 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
381 '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \
381 '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \
382 '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \
382 '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \
383 '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_file -g \*.txt' \
383 '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files -g \*.txt' \
384 '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
384 '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
385 '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
385 '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
386 '*:file:_hg_files'
386 '*:file:_hg_files'
387 }
387 }
388
388
389 _hg_cmd_copy() {
389 _hg_cmd_copy() {
390 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
390 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
391 '(--after -A)'{-A,--after}'[record a copy that has already occurred]' \
391 '(--after -A)'{-A,--after}'[record a copy that has already occurred]' \
392 '(--force -f)'{-f,--force}'[forcibly copy over an existing managed file]' \
392 '(--force -f)'{-f,--force}'[forcibly copy over an existing managed file]' \
393 '*:file:_hg_files'
393 '*:file:_hg_files'
394 }
394 }
395
395
396 _hg_cmd_diff() {
396 _hg_cmd_diff() {
397 typeset -A opt_args
397 typeset -A opt_args
398 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_diff_opts \
398 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_diff_opts \
399 '*'{-r,--rev}'+[revision]:revision:_hg_revrange' \
399 '*'{-r,--rev}'+[revision]:revision:_hg_revrange' \
400 '(--show-function -p)'{-p,--show-function}'[show which function each change is in]' \
400 '(--show-function -p)'{-p,--show-function}'[show which function each change is in]' \
401 '(--ignore-all-space -w)'{-w,--ignore-all-space}'[ignore white space when comparing lines]' \
401 '(--ignore-all-space -w)'{-w,--ignore-all-space}'[ignore white space when comparing lines]' \
402 '(--ignore-space-change -b)'{-b,--ignore-space-change}'[ignore changes in the amount of white space]' \
402 '(--ignore-space-change -b)'{-b,--ignore-space-change}'[ignore changes in the amount of white space]' \
403 '(--ignore-blank-lines -B)'{-B,--ignore-blank-lines}'[ignore changes whose lines are all blank]' \
403 '(--ignore-blank-lines -B)'{-B,--ignore-blank-lines}'[ignore changes whose lines are all blank]' \
404 '*:file:->diff_files'
404 '*:file:->diff_files'
405
405
406 if [[ $state == 'diff_files' ]]
406 if [[ $state == 'diff_files' ]]
407 then
407 then
408 if [[ -n $opt_args[-r] ]]
408 if [[ -n $opt_args[-r] ]]
409 then
409 then
410 _hg_files
410 _hg_files
411 else
411 else
412 _hg_modified
412 _hg_modified
413 fi
413 fi
414 fi
414 fi
415 }
415 }
416
416
417 _hg_cmd_export() {
417 _hg_cmd_export() {
418 _arguments -s -w : $_hg_global_opts $_hg_diff_opts \
418 _arguments -s -w : $_hg_global_opts $_hg_diff_opts \
419 '(--outout -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \
419 '(--outout -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \
420 '--switch-parent[diff against the second parent]' \
420 '--switch-parent[diff against the second parent]' \
421 '*:revision:_hg_tags'
421 '*:revision:_hg_tags'
422 }
422 }
423
423
424 _hg_cmd_grep() {
424 _hg_cmd_grep() {
425 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
425 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
426 '(--print0 -0)'{-0,--print0}'[end filenames with NUL]' \
426 '(--print0 -0)'{-0,--print0}'[end filenames with NUL]' \
427 '--all[print all revisions with matches]' \
427 '--all[print all revisions with matches]' \
428 '(--follow -f)'{-f,--follow}'[follow changeset or file history]' \
428 '(--follow -f)'{-f,--follow}'[follow changeset or file history]' \
429 '(--ignore-case -i)'{-i,--ignore-case}'[ignore case when matching]' \
429 '(--ignore-case -i)'{-i,--ignore-case}'[ignore case when matching]' \
430 '(--files-with-matches -l)'{-l,--files-with-matches}'[print only filenames and revs that match]' \
430 '(--files-with-matches -l)'{-l,--files-with-matches}'[print only filenames and revs that match]' \
431 '(--line-number -n)'{-n,--line-number}'[print matching line numbers]' \
431 '(--line-number -n)'{-n,--line-number}'[print matching line numbers]' \
432 '*'{-r+,--rev}'[search in given revision range]:revision:_hg_revrange' \
432 '*'{-r+,--rev}'[search in given revision range]:revision:_hg_revrange' \
433 '(--user -u)'{-u,--user}'[print user who committed change]' \
433 '(--user -u)'{-u,--user}'[print user who committed change]' \
434 '*:search pattern:_hg_files'
434 '*:search pattern:_hg_files'
435 }
435 }
436
436
437 _hg_cmd_heads() {
437 _hg_cmd_heads() {
438 _arguments -s -w : $_hg_global_opts $_hg_style_opts \
438 _arguments -s -w : $_hg_global_opts $_hg_style_opts \
439 '(--rev -r)'{-r+,--rev}'[show only heads which are descendants of rev]:revision:_hg_tags'
439 '(--rev -r)'{-r+,--rev}'[show only heads which are descendants of rev]:revision:_hg_tags'
440 }
440 }
441
441
442 _hg_cmd_help() {
442 _hg_cmd_help() {
443 _arguments -s -w : $_hg_global_opts \
443 _arguments -s -w : $_hg_global_opts \
444 '*:mercurial command:_hg_commands'
444 '*:mercurial command:_hg_commands'
445 }
445 }
446
446
447 _hg_cmd_import() {
447 _hg_cmd_import() {
448 _arguments -s -w : $_hg_global_opts \
448 _arguments -s -w : $_hg_global_opts \
449 '(--strip -p)'{-p+,--strip}'[directory strip option for patch (default: 1)]:count:' \
449 '(--strip -p)'{-p+,--strip}'[directory strip option for patch (default: 1)]:count:' \
450 '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \
450 '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \
451 '(--force -f)'{-f,--force}'[skip check for outstanding uncommitted changes]' \
451 '(--force -f)'{-f,--force}'[skip check for outstanding uncommitted changes]' \
452 '*:patch:_files'
452 '*:patch:_files'
453 }
453 }
454
454
455 _hg_cmd_incoming() {
455 _hg_cmd_incoming() {
456 _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_style_opts \
456 _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_style_opts \
457 '(--no-merges -M)'{-M,--no-merges}'[do not show merge revisions]' \
457 '(--no-merges -M)'{-M,--no-merges}'[do not show merge revisions]' \
458 '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
458 '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
459 '(--patch -p)'{-p,--patch}'[show patch]' \
459 '(--patch -p)'{-p,--patch}'[show patch]' \
460 '(--rev -r)'{-r+,--rev}'[a specific revision up to which you would like to pull]' \
460 '(--rev -r)'{-r+,--rev}'[a specific revision up to which you would like to pull]' \
461 '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
461 '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
462 '--bundle[file to store the bundles into]:bundle file:_files' \
462 '--bundle[file to store the bundles into]:bundle file:_files' \
463 ':source:_hg_remote'
463 ':source:_hg_remote'
464 }
464 }
465
465
466 _hg_cmd_init() {
466 _hg_cmd_init() {
467 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
467 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
468 ':dir:_files -/'
468 ':dir:_files -/'
469 }
469 }
470
470
471 _hg_cmd_locate() {
471 _hg_cmd_locate() {
472 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
472 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
473 '(--rev -r)'{-r+,--rev}'[search repository as it stood at revision]:revision:_hg_tags' \
473 '(--rev -r)'{-r+,--rev}'[search repository as it stood at revision]:revision:_hg_tags' \
474 '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \
474 '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \
475 '(--fullpath -f)'{-f,--fullpath}'[print complete paths]' \
475 '(--fullpath -f)'{-f,--fullpath}'[print complete paths]' \
476 '*:search pattern:_hg_files'
476 '*:search pattern:_hg_files'
477 }
477 }
478
478
479 _hg_cmd_log() {
479 _hg_cmd_log() {
480 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_style_opts \
480 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_style_opts \
481 '(--follow --follow-first -f)'{-f,--follow}'[follow changeset or history]' \
481 '(--follow --follow-first -f)'{-f,--follow}'[follow changeset or history]' \
482 '(-f --follow)--follow-first[only follow the first parent of merge changesets]' \
482 '(-f --follow)--follow-first[only follow the first parent of merge changesets]' \
483 '(--copies -C)'{-C,--copies}'[show copied files]' \
483 '(--copies -C)'{-C,--copies}'[show copied files]' \
484 '(--keyword -k)'{-k+,--keyword}'[search for a keyword]:' \
484 '(--keyword -k)'{-k+,--keyword}'[search for a keyword]:' \
485 '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \
485 '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \
486 '*'{-r,--rev}'[show the specified revision or range]:revision:_hg_revrange' \
486 '*'{-r,--rev}'[show the specified revision or range]:revision:_hg_revrange' \
487 '(--no-merges -M)'{-M,--no-merges}'[do not show merges]' \
487 '(--no-merges -M)'{-M,--no-merges}'[do not show merges]' \
488 '(--only-merges -m)'{-m,--only-merges}'[show only merges]' \
488 '(--only-merges -m)'{-m,--only-merges}'[show only merges]' \
489 '(--patch -p)'{-p,--patch}'[show patch]' \
489 '(--patch -p)'{-p,--patch}'[show patch]' \
490 '(--prune -P)'{-P+,--prune}'[do not display revision or any of its ancestors]:revision:_hg_tags' \
490 '(--prune -P)'{-P+,--prune}'[do not display revision or any of its ancestors]:revision:_hg_tags' \
491 '*:files:_hg_files'
491 '*:files:_hg_files'
492 }
492 }
493
493
494 _hg_cmd_manifest() {
494 _hg_cmd_manifest() {
495 _arguments -s -w : $_hg_global_opts \
495 _arguments -s -w : $_hg_global_opts \
496 ':revision:_hg_tags'
496 ':revision:_hg_tags'
497 }
497 }
498
498
499 _hg_cmd_outgoing() {
499 _hg_cmd_outgoing() {
500 _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_style_opts \
500 _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_style_opts \
501 '(--no-merges -M)'{-M,--no-merges}'[do not show merge revisions]' \
501 '(--no-merges -M)'{-M,--no-merges}'[do not show merge revisions]' \
502 '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
502 '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
503 '(--patch -p)'{-p,--patch}'[show patch]' \
503 '(--patch -p)'{-p,--patch}'[show patch]' \
504 '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]' \
504 '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]' \
505 '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
505 '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
506 ':destination:_hg_remote'
506 ':destination:_hg_remote'
507 }
507 }
508
508
509 _hg_cmd_parents() {
509 _hg_cmd_parents() {
510 _arguments -s -w : $_hg_global_opts $_hg_style_opts \
510 _arguments -s -w : $_hg_global_opts $_hg_style_opts \
511 '(--rev -r)'{-r+,--rev}'[show parents of the specified rev]:revision:_hg_tags' \
511 '(--rev -r)'{-r+,--rev}'[show parents of the specified rev]:revision:_hg_tags' \
512 ':revision:_hg_tags'
512 ':revision:_hg_tags'
513 }
513 }
514
514
515 _hg_cmd_paths() {
515 _hg_cmd_paths() {
516 _arguments -s -w : $_hg_global_opts \
516 _arguments -s -w : $_hg_global_opts \
517 ':path:_hg_paths'
517 ':path:_hg_paths'
518 }
518 }
519
519
520 _hg_cmd_pull() {
520 _hg_cmd_pull() {
521 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
521 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
522 '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
522 '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
523 '(--update -u)'{-u,--update}'[update to new tip if changesets were pulled]' \
523 '(--update -u)'{-u,--update}'[update to new tip if changesets were pulled]' \
524 ':source:_hg_remote'
524 ':source:_hg_remote'
525 }
525 }
526
526
527 _hg_cmd_push() {
527 _hg_cmd_push() {
528 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
528 _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
529 '(--force -f)'{-f,--force}'[force push]' \
529 '(--force -f)'{-f,--force}'[force push]' \
530 '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]' \
530 '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]' \
531 ':destination:_hg_remote'
531 ':destination:_hg_remote'
532 }
532 }
533
533
534 _hg_cmd_remove() {
534 _hg_cmd_remove() {
535 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
535 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
536 '(--after -A)'{-A,--after}'[record remove that has already occurred]' \
536 '(--after -A)'{-A,--after}'[record remove that has already occurred]' \
537 '(--force -f)'{-f,--force}'[remove file even if modified]' \
537 '(--force -f)'{-f,--force}'[remove file even if modified]' \
538 '*:file:_hg_files'
538 '*:file:_hg_files'
539 }
539 }
540
540
541 _hg_cmd_rename() {
541 _hg_cmd_rename() {
542 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
542 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
543 '(--after -A)'{-A,--after}'[record a rename that has already occurred]' \
543 '(--after -A)'{-A,--after}'[record a rename that has already occurred]' \
544 '(--force -f)'{-f,--force}'[forcibly copy over an existing managed file]' \
544 '(--force -f)'{-f,--force}'[forcibly copy over an existing managed file]' \
545 '*:file:_hg_files'
545 '*:file:_hg_files'
546 }
546 }
547
547
548 _hg_cmd_revert() {
548 _hg_cmd_revert() {
549 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
549 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
550 '(--all -a :)'{-a,--all}'[revert all changes when no arguments given]' \
550 '(--all -a :)'{-a,--all}'[revert all changes when no arguments given]' \
551 '(--rev -r)'{-r+,--rev}'[revision to revert to]:revision:_hg_tags' \
551 '(--rev -r)'{-r+,--rev}'[revision to revert to]:revision:_hg_tags' \
552 '--no-backup[do not save backup copies of files]' \
552 '--no-backup[do not save backup copies of files]' \
553 '*:file:->diff_files'
553 '*:file:->diff_files'
554
554
555 if [[ $state == 'diff_files' ]]
555 if [[ $state == 'diff_files' ]]
556 then
556 then
557 if [[ -n $opt_args[-r] ]]
557 if [[ -n $opt_args[-r] ]]
558 then
558 then
559 _hg_files
559 _hg_files
560 else
560 else
561 typeset -a status_files
561 typeset -a status_files
562 _hg_status mard
562 _hg_status mard
563 _wanted files expl 'modified, added, removed or deleted file' _multi_parts / status_files
563 _wanted files expl 'modified, added, removed or deleted file' _multi_parts / status_files
564 fi
564 fi
565 fi
565 fi
566 }
566 }
567
567
568 _hg_cmd_serve() {
568 _hg_cmd_serve() {
569 _arguments -s -w : $_hg_global_opts \
569 _arguments -s -w : $_hg_global_opts \
570 '(--accesslog -A)'{-A+,--accesslog}'[name of access log file]:log file:_files' \
570 '(--accesslog -A)'{-A+,--accesslog}'[name of access log file]:log file:_files' \
571 '(--errorlog -E)'{-E+,--errorlog}'[name of error log file]:log file:_files' \
571 '(--errorlog -E)'{-E+,--errorlog}'[name of error log file]:log file:_files' \
572 '(--daemon -d)'{-d,--daemon}'[run server in background]' \
572 '(--daemon -d)'{-d,--daemon}'[run server in background]' \
573 '(--port -p)'{-p+,--port}'[listen port]:listen port:' \
573 '(--port -p)'{-p+,--port}'[listen port]:listen port:' \
574 '(--address -a)'{-a+,--address}'[interface address]:interface address:' \
574 '(--address -a)'{-a+,--address}'[interface address]:interface address:' \
575 '(--name -n)'{-n+,--name}'[name to show in web pages]:repository name:' \
575 '(--name -n)'{-n+,--name}'[name to show in web pages]:repository name:' \
576 '(--templates -t)'{-t,--templates}'[web template directory]:template dir:_files -/' \
576 '(--templates -t)'{-t,--templates}'[web template directory]:template dir:_files -/' \
577 '--style[web template style]:style' \
577 '--style[web template style]:style' \
578 '--stdio[for remote clients]' \
578 '--stdio[for remote clients]' \
579 '(--ipv6 -6)'{-6,--ipv6}'[use IPv6 in addition to IPv4]'
579 '(--ipv6 -6)'{-6,--ipv6}'[use IPv6 in addition to IPv4]'
580 }
580 }
581
581
582 _hg_cmd_status() {
582 _hg_cmd_status() {
583 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
583 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
584 '(--all -A)'{-A,--all}'[show status of all files]' \
584 '(--all -A)'{-A,--all}'[show status of all files]' \
585 '(--modified -m)'{-m,--modified}'[show only modified files]' \
585 '(--modified -m)'{-m,--modified}'[show only modified files]' \
586 '(--added -a)'{-a,--added}'[show only added files]' \
586 '(--added -a)'{-a,--added}'[show only added files]' \
587 '(--removed -r)'{-r,--removed}'[show only removed files]' \
587 '(--removed -r)'{-r,--removed}'[show only removed files]' \
588 '(--deleted -d)'{-d,--deleted}'[show only deleted (but tracked) files]' \
588 '(--deleted -d)'{-d,--deleted}'[show only deleted (but tracked) files]' \
589 '(--clean -c)'{-c,--clean}'[show only files without changes]' \
589 '(--clean -c)'{-c,--clean}'[show only files without changes]' \
590 '(--unknown -u)'{-u,--unknown}'[show only unknown files]' \
590 '(--unknown -u)'{-u,--unknown}'[show only unknown files]' \
591 '(--ignored -i)'{-i,--ignored}'[show ignored files]' \
591 '(--ignored -i)'{-i,--ignored}'[show ignored files]' \
592 '(--no-status -n)'{-n,--no-status}'[hide status prefix]' \
592 '(--no-status -n)'{-n,--no-status}'[hide status prefix]' \
593 '(--copies -C)'{-C,--copies}'[show source of copied files]' \
593 '(--copies -C)'{-C,--copies}'[show source of copied files]' \
594 '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \
594 '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \
595 '--rev[show difference from revision]:revision:_hg_tags' \
595 '--rev[show difference from revision]:revision:_hg_tags' \
596 '*:files:_files'
596 '*:files:_files'
597 }
597 }
598
598
599 _hg_cmd_tag() {
599 _hg_cmd_tag() {
600 _arguments -s -w : $_hg_global_opts \
600 _arguments -s -w : $_hg_global_opts \
601 '(--local -l)'{-l,--local}'[make the tag local]' \
601 '(--local -l)'{-l,--local}'[make the tag local]' \
602 '(--message -m)'{-m+,--message}'[message for tag commit log entry]:message:' \
602 '(--message -m)'{-m+,--message}'[message for tag commit log entry]:message:' \
603 '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
603 '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
604 '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
604 '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
605 '(--rev -r)'{-r+,--rev}'[revision to tag]:revision:_hg_tags' \
605 '(--rev -r)'{-r+,--rev}'[revision to tag]:revision:_hg_tags' \
606 ':tag name:'
606 ':tag name:'
607 }
607 }
608
608
609 _hg_cmd_tip() {
609 _hg_cmd_tip() {
610 _arguments -s -w : $_hg_global_opts $_hg_style_opts \
610 _arguments -s -w : $_hg_global_opts $_hg_style_opts \
611 '(--patch -p)'{-p,--patch}'[show patch]'
611 '(--patch -p)'{-p,--patch}'[show patch]'
612 }
612 }
613
613
614 _hg_cmd_unbundle() {
614 _hg_cmd_unbundle() {
615 _arguments -s -w : $_hg_global_opts \
615 _arguments -s -w : $_hg_global_opts \
616 '(--update -u)'{-u,--update}'[update to new tip if changesets were unbundled]' \
616 '(--update -u)'{-u,--update}'[update to new tip if changesets were unbundled]' \
617 ':files:_files'
617 ':files:_files'
618 }
618 }
619
619
620 _hg_cmd_update() {
620 _hg_cmd_update() {
621 _arguments -s -w : $_hg_global_opts \
621 _arguments -s -w : $_hg_global_opts \
622 '(--clean -C)'{-C,--clean}'[overwrite locally modified files]' \
622 '(--clean -C)'{-C,--clean}'[overwrite locally modified files]' \
623 ':revision:_hg_tags'
623 ':revision:_hg_tags'
624 }
624 }
625
625
626 # HGK
626 # HGK
627 _hg_cmd_view() {
627 _hg_cmd_view() {
628 _arguments -s -w : $_hg_global_opts \
628 _arguments -s -w : $_hg_global_opts \
629 '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \
629 '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \
630 ':revision range:_hg_tags'
630 ':revision range:_hg_tags'
631 }
631 }
632
632
633 # MQ
633 # MQ
634 _hg_qseries() {
634 _hg_qseries() {
635 typeset -a patches
635 typeset -a patches
636 patches=($(_hg_cmd qseries 2>/dev/null))
636 patches=($(_hg_cmd qseries 2>/dev/null))
637 (( $#patches )) && _describe -t hg-patches 'patches' patches
637 (( $#patches )) && _describe -t hg-patches 'patches' patches
638 }
638 }
639
639
640 _hg_qapplied() {
640 _hg_qapplied() {
641 typeset -a patches
641 typeset -a patches
642 patches=($(_hg_cmd qapplied 2>/dev/null))
642 patches=($(_hg_cmd qapplied 2>/dev/null))
643 if (( $#patches ))
643 if (( $#patches ))
644 then
644 then
645 patches+=(qbase qtip)
645 patches+=(qbase qtip)
646 _describe -t hg-applied-patches 'applied patches' patches
646 _describe -t hg-applied-patches 'applied patches' patches
647 fi
647 fi
648 }
648 }
649
649
650 _hg_qunapplied() {
650 _hg_qunapplied() {
651 typeset -a patches
651 typeset -a patches
652 patches=($(_hg_cmd qunapplied 2>/dev/null))
652 patches=($(_hg_cmd qunapplied 2>/dev/null))
653 (( $#patches )) && _describe -t hg-unapplied-patches 'unapplied patches' patches
653 (( $#patches )) && _describe -t hg-unapplied-patches 'unapplied patches' patches
654 }
654 }
655
655
656 _hg_qguards() {
656 _hg_qguards() {
657 typeset -a guards
657 typeset -a guards
658 local guard
658 local guard
659 compset -P "+|-"
659 compset -P "+|-"
660 _hg_cmd qselect -s 2>/dev/null | while read guard
660 _hg_cmd qselect -s 2>/dev/null | while read guard
661 do
661 do
662 guards+=(${guard#(+|-)})
662 guards+=(${guard#(+|-)})
663 done
663 done
664 (( $#guards )) && _describe -t hg-guards 'guards' guards
664 (( $#guards )) && _describe -t hg-guards 'guards' guards
665 }
665 }
666
666
667 _hg_qseries_opts=(
667 _hg_qseries_opts=(
668 '(--summary -s)'{-s,--summary}'[print first line of patch header]')
668 '(--summary -s)'{-s,--summary}'[print first line of patch header]')
669
669
670 _hg_cmd_qapplied() {
670 _hg_cmd_qapplied() {
671 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
671 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
672 }
672 }
673
673
674 _hg_cmd_qdelete() {
674 _hg_cmd_qdelete() {
675 _arguments -s -w : $_hg_global_opts \
675 _arguments -s -w : $_hg_global_opts \
676 '(--keep -k)'{-k,--keep}'[keep patch file]' \
676 '(--keep -k)'{-k,--keep}'[keep patch file]' \
677 '*'{-r+,--rev}'[stop managing a revision]:applied patch:_hg_revrange' \
677 '*'{-r+,--rev}'[stop managing a revision]:applied patch:_hg_revrange' \
678 '*:unapplied patch:_hg_qunapplied'
678 '*:unapplied patch:_hg_qunapplied'
679 }
679 }
680
680
681 _hg_cmd_qdiff() {
681 _hg_cmd_qdiff() {
682 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
682 _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
683 '*:pattern:_hg_files'
683 '*:pattern:_hg_files'
684 }
684 }
685
685
686 _hg_cmd_qfold() {
686 _hg_cmd_qfold() {
687 _arguments -s -w : $_hg_global_opts $_h_commit_opts \
687 _arguments -s -w : $_hg_global_opts $_h_commit_opts \
688 '(--keep,-k)'{-k,--keep}'[keep folded patch files]' \
688 '(--keep,-k)'{-k,--keep}'[keep folded patch files]' \
689 '*:unapplied patch:_hg_qunapplied'
689 '*:unapplied patch:_hg_qunapplied'
690 }
690 }
691
691
692 _hg_cmd_qguard() {
692 _hg_cmd_qguard() {
693 _arguments -s -w : $_hg_global_opts \
693 _arguments -s -w : $_hg_global_opts \
694 '(--list -l)'{-l,--list}'[list all patches and guards]' \
694 '(--list -l)'{-l,--list}'[list all patches and guards]' \
695 '(--none -n)'{-n,--none}'[drop all guards]' \
695 '(--none -n)'{-n,--none}'[drop all guards]' \
696 ':patch:_hg_qseries' \
696 ':patch:_hg_qseries' \
697 '*:guards:_hg_qguards'
697 '*:guards:_hg_qguards'
698 }
698 }
699
699
700 _hg_cmd_qheader() {
700 _hg_cmd_qheader() {
701 _arguments -s -w : $_hg_global_opts \
701 _arguments -s -w : $_hg_global_opts \
702 ':patch:_hg_qseries'
702 ':patch:_hg_qseries'
703 }
703 }
704
704
705 _hg_cmd_qimport() {
705 _hg_cmd_qimport() {
706 _arguments -s -w : $_hg_global_opts \
706 _arguments -s -w : $_hg_global_opts \
707 '(--existing -e)'{-e,--existing}'[import file in patch dir]' \
707 '(--existing -e)'{-e,--existing}'[import file in patch dir]' \
708 '(--name -n 2)'{-n+,--name}'[patch file name]:name:' \
708 '(--name -n 2)'{-n+,--name}'[patch file name]:name:' \
709 '(--force -f)'{-f,--force}'[overwrite existing files]' \
709 '(--force -f)'{-f,--force}'[overwrite existing files]' \
710 '*'{-r+,--rev}'[place existing revisions under mq control]:revision:_hg_revrange' \
710 '*'{-r+,--rev}'[place existing revisions under mq control]:revision:_hg_revrange' \
711 '*:patch:_files'
711 '*:patch:_files'
712 }
712 }
713
713
714 _hg_cmd_qnew() {
714 _hg_cmd_qnew() {
715 _arguments -s -w : $_hg_global_opts $_hg_commit_opts \
715 _arguments -s -w : $_hg_global_opts $_hg_commit_opts \
716 '(--force -f)'{-f,--force}'[import uncommitted changes into patch]' \
716 '(--force -f)'{-f,--force}'[import uncommitted changes into patch]' \
717 ':patch:'
717 ':patch:'
718 }
718 }
719
719
720 _hg_cmd_qnext() {
720 _hg_cmd_qnext() {
721 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
721 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
722 }
722 }
723
723
724 _hg_cmd_qpop() {
724 _hg_cmd_qpop() {
725 _arguments -s -w : $_hg_global_opts \
725 _arguments -s -w : $_hg_global_opts \
726 '(--all -a :)'{-a,--all}'[pop all patches]' \
726 '(--all -a :)'{-a,--all}'[pop all patches]' \
727 '(--name -n)'{-n+,--name}'[queue name to pop]:' \
727 '(--name -n)'{-n+,--name}'[queue name to pop]:' \
728 '(--force -f)'{-f,--force}'[forget any local changes]' \
728 '(--force -f)'{-f,--force}'[forget any local changes]' \
729 ':patch:_hg_qapplied'
729 ':patch:_hg_qapplied'
730 }
730 }
731
731
732 _hg_cmd_qprev() {
732 _hg_cmd_qprev() {
733 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
733 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
734 }
734 }
735
735
736 _hg_cmd_qpush() {
736 _hg_cmd_qpush() {
737 _arguments -s -w : $_hg_global_opts \
737 _arguments -s -w : $_hg_global_opts \
738 '(--all -a :)'{-a,--all}'[apply all patches]' \
738 '(--all -a :)'{-a,--all}'[apply all patches]' \
739 '(--list -l)'{-l,--list}'[list patch name in commit text]' \
739 '(--list -l)'{-l,--list}'[list patch name in commit text]' \
740 '(--merge -m)'{-m+,--merge}'[merge from another queue]:' \
740 '(--merge -m)'{-m+,--merge}'[merge from another queue]:' \
741 '(--name -n)'{-n+,--name}'[merge queue name]:' \
741 '(--name -n)'{-n+,--name}'[merge queue name]:' \
742 '(--force -f)'{-f,--force}'[apply if the patch has rejects]' \
742 '(--force -f)'{-f,--force}'[apply if the patch has rejects]' \
743 ':patch:_hg_qunapplied'
743 ':patch:_hg_qunapplied'
744 }
744 }
745
745
746 _hg_cmd_qrefresh() {
746 _hg_cmd_qrefresh() {
747 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_commit_opts \
747 _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_commit_opts \
748 '(--git -g)'{-g,--git}'[use git extended diff format]' \
748 '(--git -g)'{-g,--git}'[use git extended diff format]' \
749 '(--short -s)'{-s,--short}'[short refresh]' \
749 '(--short -s)'{-s,--short}'[short refresh]' \
750 '*:files:_hg_files'
750 '*:files:_hg_files'
751 }
751 }
752
752
753 _hg_cmd_qrename() {
753 _hg_cmd_qrename() {
754 _arguments -s -w : $_hg_global_opts \
754 _arguments -s -w : $_hg_global_opts \
755 ':patch:_hg_qseries' \
755 ':patch:_hg_qseries' \
756 ':destination:'
756 ':destination:'
757 }
757 }
758
758
759 _hg_cmd_qselect() {
759 _hg_cmd_qselect() {
760 _arguments -s -w : $_hg_global_opts \
760 _arguments -s -w : $_hg_global_opts \
761 '(--none -n :)'{-n,--none}'[disable all guards]' \
761 '(--none -n :)'{-n,--none}'[disable all guards]' \
762 '(--series -s :)'{-s,--series}'[list all guards in series file]' \
762 '(--series -s :)'{-s,--series}'[list all guards in series file]' \
763 '--pop[pop to before first guarded applied patch]' \
763 '--pop[pop to before first guarded applied patch]' \
764 '--reapply[pop and reapply patches]' \
764 '--reapply[pop and reapply patches]' \
765 '*:guards:_hg_qguards'
765 '*:guards:_hg_qguards'
766 }
766 }
767
767
768 _hg_cmd_qseries() {
768 _hg_cmd_qseries() {
769 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts \
769 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts \
770 '(--missing -m)'{-m,--missing}'[print patches not in series]'
770 '(--missing -m)'{-m,--missing}'[print patches not in series]'
771 }
771 }
772
772
773 _hg_cmd_qunapplied() {
773 _hg_cmd_qunapplied() {
774 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
774 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
775 }
775 }
776
776
777 _hg_cmd_qtop() {
777 _hg_cmd_qtop() {
778 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
778 _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
779 }
779 }
780
780
781 _hg_cmd_strip() {
781 _hg_cmd_strip() {
782 _arguments -s -w : $_hg_global_opts \
782 _arguments -s -w : $_hg_global_opts \
783 '(--force -f)'{-f,--force}'[force multi-head removal]' \
783 '(--force -f)'{-f,--force}'[force multi-head removal]' \
784 '(--backup -b)'{-b,--backup}'[bundle unrelated changesets]' \
784 '(--backup -b)'{-b,--backup}'[bundle unrelated changesets]' \
785 '(--nobackup -n)'{-n,--nobackup}'[no backups]' \
785 '(--nobackup -n)'{-n,--nobackup}'[no backups]' \
786 ':revision:_hg_tags'
786 ':revision:_hg_tags'
787 }
787 }
788
788
789 _hg "$@"
789 _hg "$@"
@@ -1,3372 +1,3394 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import traceback, errno, version, atexit, socket
14 import traceback, errno, version, atexit, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, node=None, rev=None, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202 if rev and node:
203 raise util.Abort(_("please specify just one revision"))
204
205 if not rev:
206 rev = node
202
207
203 bail_if_changed(repo)
208 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
209 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
210 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
211 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
212 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
213 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
214 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
215 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
216 if p2 != nullid:
212 if not opts['parent']:
217 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
218 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
219 '--parent'))
215 p = repo.lookup(opts['parent'])
220 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
221 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
222 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
223 (short(p), short(node)))
219 parent = p
224 parent = p
220 else:
225 else:
221 if opts['parent']:
226 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
227 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
228 parent = p1
224 hg.clean(repo, node, show_stats=False)
229 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
230 revert_opts = opts.copy()
226 revert_opts['date'] = None
231 revert_opts['date'] = None
227 revert_opts['all'] = True
232 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
233 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
234 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
235 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
236 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
237 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
238 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
239 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
240 commit(ui, repo, **commit_opts)
236 def nice(node):
241 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
242 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
243 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
244 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
245 if op1 != node:
241 if opts['merge']:
246 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
247 ui.status(_('merging with changeset %s\n') % nice(op1))
243 hg.merge(repo, hex(op1))
248 hg.merge(repo, hex(op1))
244 else:
249 else:
245 ui.status(_('the backout changeset is a new head - '
250 ui.status(_('the backout changeset is a new head - '
246 'do not forget to merge\n'))
251 'do not forget to merge\n'))
247 ui.status(_('(use "backout --merge" '
252 ui.status(_('(use "backout --merge" '
248 'if you want to auto-merge)\n'))
253 'if you want to auto-merge)\n'))
249
254
250 def branch(ui, repo, label=None, **opts):
255 def branch(ui, repo, label=None, **opts):
251 """set or show the current branch name
256 """set or show the current branch name
252
257
253 With <name>, set the current branch name. Otherwise, show the
258 With <name>, set the current branch name. Otherwise, show the
254 current branch name.
259 current branch name.
255
260
256 Unless --force is specified, branch will not let you set a
261 Unless --force is specified, branch will not let you set a
257 branch name that shadows an existing branch.
262 branch name that shadows an existing branch.
258 """
263 """
259
264
260 if label:
265 if label:
261 if not opts.get('force') and label in repo.branchtags():
266 if not opts.get('force') and label in repo.branchtags():
262 if label not in [p.branch() for p in repo.workingctx().parents()]:
267 if label not in [p.branch() for p in repo.workingctx().parents()]:
263 raise util.Abort(_('a branch of the same name already exists'
268 raise util.Abort(_('a branch of the same name already exists'
264 ' (use --force to override)'))
269 ' (use --force to override)'))
265 repo.dirstate.setbranch(util.fromlocal(label))
270 repo.dirstate.setbranch(util.fromlocal(label))
266 else:
271 else:
267 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
272 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
268
273
269 def branches(ui, repo):
274 def branches(ui, repo):
270 """list repository named branches
275 """list repository named branches
271
276
272 List the repository's named branches.
277 List the repository's named branches.
273 """
278 """
274 b = repo.branchtags()
279 b = repo.branchtags()
275 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
280 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
276 l.sort()
281 l.sort()
277 for r, n, t in l:
282 for r, n, t in l:
278 hexfunc = ui.debugflag and hex or short
283 hexfunc = ui.debugflag and hex or short
279 if ui.quiet:
284 if ui.quiet:
280 ui.write("%s\n" % t)
285 ui.write("%s\n" % t)
281 else:
286 else:
282 spaces = " " * (30 - util.locallen(t))
287 spaces = " " * (30 - util.locallen(t))
283 ui.write("%s%s %s:%s\n" % (t, spaces, -r, hexfunc(n)))
288 ui.write("%s%s %s:%s\n" % (t, spaces, -r, hexfunc(n)))
284
289
285 def bundle(ui, repo, fname, dest=None, **opts):
290 def bundle(ui, repo, fname, dest=None, **opts):
286 """create a changegroup file
291 """create a changegroup file
287
292
288 Generate a compressed changegroup file collecting changesets not
293 Generate a compressed changegroup file collecting changesets not
289 found in the other repository.
294 found in the other repository.
290
295
291 If no destination repository is specified the destination is assumed
296 If no destination repository is specified the destination is assumed
292 to have all the nodes specified by one or more --base parameters.
297 to have all the nodes specified by one or more --base parameters.
293
298
294 The bundle file can then be transferred using conventional means and
299 The bundle file can then be transferred using conventional means and
295 applied to another repository with the unbundle or pull command.
300 applied to another repository with the unbundle or pull command.
296 This is useful when direct push and pull are not available or when
301 This is useful when direct push and pull are not available or when
297 exporting an entire repository is undesirable.
302 exporting an entire repository is undesirable.
298
303
299 Applying bundles preserves all changeset contents including
304 Applying bundles preserves all changeset contents including
300 permissions, copy/rename information, and revision history.
305 permissions, copy/rename information, and revision history.
301 """
306 """
302 revs = opts.get('rev') or None
307 revs = opts.get('rev') or None
303 if revs:
308 if revs:
304 revs = [repo.lookup(rev) for rev in revs]
309 revs = [repo.lookup(rev) for rev in revs]
305 base = opts.get('base')
310 base = opts.get('base')
306 if base:
311 if base:
307 if dest:
312 if dest:
308 raise util.Abort(_("--base is incompatible with specifiying "
313 raise util.Abort(_("--base is incompatible with specifiying "
309 "a destination"))
314 "a destination"))
310 base = [repo.lookup(rev) for rev in base]
315 base = [repo.lookup(rev) for rev in base]
311 # create the right base
316 # create the right base
312 # XXX: nodesbetween / changegroup* should be "fixed" instead
317 # XXX: nodesbetween / changegroup* should be "fixed" instead
313 o = []
318 o = []
314 has = {nullid: None}
319 has = {nullid: None}
315 for n in base:
320 for n in base:
316 has.update(repo.changelog.reachable(n))
321 has.update(repo.changelog.reachable(n))
317 if revs:
322 if revs:
318 visit = list(revs)
323 visit = list(revs)
319 else:
324 else:
320 visit = repo.changelog.heads()
325 visit = repo.changelog.heads()
321 seen = {}
326 seen = {}
322 while visit:
327 while visit:
323 n = visit.pop(0)
328 n = visit.pop(0)
324 parents = [p for p in repo.changelog.parents(n) if p not in has]
329 parents = [p for p in repo.changelog.parents(n) if p not in has]
325 if len(parents) == 0:
330 if len(parents) == 0:
326 o.insert(0, n)
331 o.insert(0, n)
327 else:
332 else:
328 for p in parents:
333 for p in parents:
329 if p not in seen:
334 if p not in seen:
330 seen[p] = 1
335 seen[p] = 1
331 visit.append(p)
336 visit.append(p)
332 else:
337 else:
333 setremoteconfig(ui, opts)
338 setremoteconfig(ui, opts)
334 dest = ui.expandpath(dest or 'default-push', dest or 'default')
339 dest = ui.expandpath(dest or 'default-push', dest or 'default')
335 other = hg.repository(ui, dest)
340 other = hg.repository(ui, dest)
336 o = repo.findoutgoing(other, force=opts['force'])
341 o = repo.findoutgoing(other, force=opts['force'])
337
342
338 if revs:
343 if revs:
339 cg = repo.changegroupsubset(o, revs, 'bundle')
344 cg = repo.changegroupsubset(o, revs, 'bundle')
340 else:
345 else:
341 cg = repo.changegroup(o, 'bundle')
346 cg = repo.changegroup(o, 'bundle')
342 changegroup.writebundle(cg, fname, "HG10BZ")
347 changegroup.writebundle(cg, fname, "HG10BZ")
343
348
344 def cat(ui, repo, file1, *pats, **opts):
349 def cat(ui, repo, file1, *pats, **opts):
345 """output the current or given revision of files
350 """output the current or given revision of files
346
351
347 Print the specified files as they were at the given revision.
352 Print the specified files as they were at the given revision.
348 If no revision is given, the parent of the working directory is used,
353 If no revision is given, the parent of the working directory is used,
349 or tip if no revision is checked out.
354 or tip if no revision is checked out.
350
355
351 Output may be to a file, in which case the name of the file is
356 Output may be to a file, in which case the name of the file is
352 given using a format string. The formatting rules are the same as
357 given using a format string. The formatting rules are the same as
353 for the export command, with the following additions:
358 for the export command, with the following additions:
354
359
355 %s basename of file being printed
360 %s basename of file being printed
356 %d dirname of file being printed, or '.' if in repo root
361 %d dirname of file being printed, or '.' if in repo root
357 %p root-relative path name of file being printed
362 %p root-relative path name of file being printed
358 """
363 """
359 ctx = repo.changectx(opts['rev'])
364 ctx = repo.changectx(opts['rev'])
360 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
365 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
361 ctx.node()):
366 ctx.node()):
362 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
367 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
363 fp.write(ctx.filectx(abs).data())
368 fp.write(ctx.filectx(abs).data())
364
369
365 def clone(ui, source, dest=None, **opts):
370 def clone(ui, source, dest=None, **opts):
366 """make a copy of an existing repository
371 """make a copy of an existing repository
367
372
368 Create a copy of an existing repository in a new directory.
373 Create a copy of an existing repository in a new directory.
369
374
370 If no destination directory name is specified, it defaults to the
375 If no destination directory name is specified, it defaults to the
371 basename of the source.
376 basename of the source.
372
377
373 The location of the source is added to the new repository's
378 The location of the source is added to the new repository's
374 .hg/hgrc file, as the default to be used for future pulls.
379 .hg/hgrc file, as the default to be used for future pulls.
375
380
376 For efficiency, hardlinks are used for cloning whenever the source
381 For efficiency, hardlinks are used for cloning whenever the source
377 and destination are on the same filesystem (note this applies only
382 and destination are on the same filesystem (note this applies only
378 to the repository data, not to the checked out files). Some
383 to the repository data, not to the checked out files). Some
379 filesystems, such as AFS, implement hardlinking incorrectly, but
384 filesystems, such as AFS, implement hardlinking incorrectly, but
380 do not report errors. In these cases, use the --pull option to
385 do not report errors. In these cases, use the --pull option to
381 avoid hardlinking.
386 avoid hardlinking.
382
387
383 You can safely clone repositories and checked out files using full
388 You can safely clone repositories and checked out files using full
384 hardlinks with
389 hardlinks with
385
390
386 $ cp -al REPO REPOCLONE
391 $ cp -al REPO REPOCLONE
387
392
388 which is the fastest way to clone. However, the operation is not
393 which is the fastest way to clone. However, the operation is not
389 atomic (making sure REPO is not modified during the operation is
394 atomic (making sure REPO is not modified during the operation is
390 up to you) and you have to make sure your editor breaks hardlinks
395 up to you) and you have to make sure your editor breaks hardlinks
391 (Emacs and most Linux Kernel tools do so).
396 (Emacs and most Linux Kernel tools do so).
392
397
393 If you use the -r option to clone up to a specific revision, no
398 If you use the -r option to clone up to a specific revision, no
394 subsequent revisions will be present in the cloned repository.
399 subsequent revisions will be present in the cloned repository.
395 This option implies --pull, even on local repositories.
400 This option implies --pull, even on local repositories.
396
401
397 See pull for valid source format details.
402 See pull for valid source format details.
398
403
399 It is possible to specify an ssh:// URL as the destination, but no
404 It is possible to specify an ssh:// URL as the destination, but no
400 .hg/hgrc and working directory will be created on the remote side.
405 .hg/hgrc and working directory will be created on the remote side.
401 Look at the help text for the pull command for important details
406 Look at the help text for the pull command for important details
402 about ssh:// URLs.
407 about ssh:// URLs.
403 """
408 """
404 setremoteconfig(ui, opts)
409 setremoteconfig(ui, opts)
405 hg.clone(ui, ui.expandpath(source), dest,
410 hg.clone(ui, ui.expandpath(source), dest,
406 pull=opts['pull'],
411 pull=opts['pull'],
407 stream=opts['uncompressed'],
412 stream=opts['uncompressed'],
408 rev=opts['rev'],
413 rev=opts['rev'],
409 update=not opts['noupdate'])
414 update=not opts['noupdate'])
410
415
411 def commit(ui, repo, *pats, **opts):
416 def commit(ui, repo, *pats, **opts):
412 """commit the specified files or all outstanding changes
417 """commit the specified files or all outstanding changes
413
418
414 Commit changes to the given files into the repository.
419 Commit changes to the given files into the repository.
415
420
416 If a list of files is omitted, all changes reported by "hg status"
421 If a list of files is omitted, all changes reported by "hg status"
417 will be committed.
422 will be committed.
418
423
419 If no commit message is specified, the editor configured in your hgrc
424 If no commit message is specified, the editor configured in your hgrc
420 or in the EDITOR environment variable is started to enter a message.
425 or in the EDITOR environment variable is started to enter a message.
421 """
426 """
422 message = logmessage(opts)
427 message = logmessage(opts)
423
428
424 if opts['addremove']:
429 if opts['addremove']:
425 cmdutil.addremove(repo, pats, opts)
430 cmdutil.addremove(repo, pats, opts)
426 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
431 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
427 if pats:
432 if pats:
428 status = repo.status(files=fns, match=match)
433 status = repo.status(files=fns, match=match)
429 modified, added, removed, deleted, unknown = status[:5]
434 modified, added, removed, deleted, unknown = status[:5]
430 files = modified + added + removed
435 files = modified + added + removed
431 slist = None
436 slist = None
432 for f in fns:
437 for f in fns:
433 if f == '.':
438 if f == '.':
434 continue
439 continue
435 if f not in files:
440 if f not in files:
436 rf = repo.wjoin(f)
441 rf = repo.wjoin(f)
437 if f in unknown:
442 if f in unknown:
438 raise util.Abort(_("file %s not tracked!") % rf)
443 raise util.Abort(_("file %s not tracked!") % rf)
439 try:
444 try:
440 mode = os.lstat(rf)[stat.ST_MODE]
445 mode = os.lstat(rf)[stat.ST_MODE]
441 except OSError:
446 except OSError:
442 raise util.Abort(_("file %s not found!") % rf)
447 raise util.Abort(_("file %s not found!") % rf)
443 if stat.S_ISDIR(mode):
448 if stat.S_ISDIR(mode):
444 name = f + '/'
449 name = f + '/'
445 if slist is None:
450 if slist is None:
446 slist = list(files)
451 slist = list(files)
447 slist.sort()
452 slist.sort()
448 i = bisect.bisect(slist, name)
453 i = bisect.bisect(slist, name)
449 if i >= len(slist) or not slist[i].startswith(name):
454 if i >= len(slist) or not slist[i].startswith(name):
450 raise util.Abort(_("no match under directory %s!")
455 raise util.Abort(_("no match under directory %s!")
451 % rf)
456 % rf)
452 elif not stat.S_ISREG(mode):
457 elif not stat.S_ISREG(mode):
453 raise util.Abort(_("can't commit %s: "
458 raise util.Abort(_("can't commit %s: "
454 "unsupported file type!") % rf)
459 "unsupported file type!") % rf)
455 else:
460 else:
456 files = []
461 files = []
457 try:
462 try:
458 repo.commit(files, message, opts['user'], opts['date'], match,
463 repo.commit(files, message, opts['user'], opts['date'], match,
459 force_editor=opts.get('force_editor'))
464 force_editor=opts.get('force_editor'))
460 except ValueError, inst:
465 except ValueError, inst:
461 raise util.Abort(str(inst))
466 raise util.Abort(str(inst))
462
467
463 def docopy(ui, repo, pats, opts, wlock):
468 def docopy(ui, repo, pats, opts, wlock):
464 # called with the repo lock held
469 # called with the repo lock held
465 #
470 #
466 # hgsep => pathname that uses "/" to separate directories
471 # hgsep => pathname that uses "/" to separate directories
467 # ossep => pathname that uses os.sep to separate directories
472 # ossep => pathname that uses os.sep to separate directories
468 cwd = repo.getcwd()
473 cwd = repo.getcwd()
469 errors = 0
474 errors = 0
470 copied = []
475 copied = []
471 targets = {}
476 targets = {}
472
477
473 # abs: hgsep
478 # abs: hgsep
474 # rel: ossep
479 # rel: ossep
475 # return: hgsep
480 # return: hgsep
476 def okaytocopy(abs, rel, exact):
481 def okaytocopy(abs, rel, exact):
477 reasons = {'?': _('is not managed'),
482 reasons = {'?': _('is not managed'),
478 'a': _('has been marked for add'),
483 'a': _('has been marked for add'),
479 'r': _('has been marked for remove')}
484 'r': _('has been marked for remove')}
480 state = repo.dirstate.state(abs)
485 state = repo.dirstate.state(abs)
481 reason = reasons.get(state)
486 reason = reasons.get(state)
482 if reason:
487 if reason:
483 if state == 'a':
488 if state == 'a':
484 origsrc = repo.dirstate.copied(abs)
489 origsrc = repo.dirstate.copied(abs)
485 if origsrc is not None:
490 if origsrc is not None:
486 return origsrc
491 return origsrc
487 if exact:
492 if exact:
488 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
493 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
489 else:
494 else:
490 return abs
495 return abs
491
496
492 # origsrc: hgsep
497 # origsrc: hgsep
493 # abssrc: hgsep
498 # abssrc: hgsep
494 # relsrc: ossep
499 # relsrc: ossep
495 # target: ossep
500 # target: ossep
496 def copy(origsrc, abssrc, relsrc, target, exact):
501 def copy(origsrc, abssrc, relsrc, target, exact):
497 abstarget = util.canonpath(repo.root, cwd, target)
502 abstarget = util.canonpath(repo.root, cwd, target)
498 reltarget = util.pathto(repo.root, cwd, abstarget)
503 reltarget = util.pathto(repo.root, cwd, abstarget)
499 prevsrc = targets.get(abstarget)
504 prevsrc = targets.get(abstarget)
500 if prevsrc is not None:
505 if prevsrc is not None:
501 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
506 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
502 (reltarget, util.localpath(abssrc),
507 (reltarget, util.localpath(abssrc),
503 util.localpath(prevsrc)))
508 util.localpath(prevsrc)))
504 return
509 return
505 if (not opts['after'] and os.path.exists(reltarget) or
510 if (not opts['after'] and os.path.exists(reltarget) or
506 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
511 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
507 if not opts['force']:
512 if not opts['force']:
508 ui.warn(_('%s: not overwriting - file exists\n') %
513 ui.warn(_('%s: not overwriting - file exists\n') %
509 reltarget)
514 reltarget)
510 return
515 return
511 if not opts['after'] and not opts.get('dry_run'):
516 if not opts['after'] and not opts.get('dry_run'):
512 os.unlink(reltarget)
517 os.unlink(reltarget)
513 if opts['after']:
518 if opts['after']:
514 if not os.path.exists(reltarget):
519 if not os.path.exists(reltarget):
515 return
520 return
516 else:
521 else:
517 targetdir = os.path.dirname(reltarget) or '.'
522 targetdir = os.path.dirname(reltarget) or '.'
518 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
523 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
519 os.makedirs(targetdir)
524 os.makedirs(targetdir)
520 try:
525 try:
521 restore = repo.dirstate.state(abstarget) == 'r'
526 restore = repo.dirstate.state(abstarget) == 'r'
522 if restore and not opts.get('dry_run'):
527 if restore and not opts.get('dry_run'):
523 repo.undelete([abstarget], wlock)
528 repo.undelete([abstarget], wlock)
524 try:
529 try:
525 if not opts.get('dry_run'):
530 if not opts.get('dry_run'):
526 util.copyfile(relsrc, reltarget)
531 util.copyfile(relsrc, reltarget)
527 restore = False
532 restore = False
528 finally:
533 finally:
529 if restore:
534 if restore:
530 repo.remove([abstarget], wlock=wlock)
535 repo.remove([abstarget], wlock=wlock)
531 except IOError, inst:
536 except IOError, inst:
532 if inst.errno == errno.ENOENT:
537 if inst.errno == errno.ENOENT:
533 ui.warn(_('%s: deleted in working copy\n') % relsrc)
538 ui.warn(_('%s: deleted in working copy\n') % relsrc)
534 else:
539 else:
535 ui.warn(_('%s: cannot copy - %s\n') %
540 ui.warn(_('%s: cannot copy - %s\n') %
536 (relsrc, inst.strerror))
541 (relsrc, inst.strerror))
537 errors += 1
542 errors += 1
538 return
543 return
539 if ui.verbose or not exact:
544 if ui.verbose or not exact:
540 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
545 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
541 targets[abstarget] = abssrc
546 targets[abstarget] = abssrc
542 if abstarget != origsrc and not opts.get('dry_run'):
547 if abstarget != origsrc and not opts.get('dry_run'):
543 repo.copy(origsrc, abstarget, wlock)
548 repo.copy(origsrc, abstarget, wlock)
544 copied.append((abssrc, relsrc, exact))
549 copied.append((abssrc, relsrc, exact))
545
550
546 # pat: ossep
551 # pat: ossep
547 # dest ossep
552 # dest ossep
548 # srcs: list of (hgsep, hgsep, ossep, bool)
553 # srcs: list of (hgsep, hgsep, ossep, bool)
549 # return: function that takes hgsep and returns ossep
554 # return: function that takes hgsep and returns ossep
550 def targetpathfn(pat, dest, srcs):
555 def targetpathfn(pat, dest, srcs):
551 if os.path.isdir(pat):
556 if os.path.isdir(pat):
552 abspfx = util.canonpath(repo.root, cwd, pat)
557 abspfx = util.canonpath(repo.root, cwd, pat)
553 abspfx = util.localpath(abspfx)
558 abspfx = util.localpath(abspfx)
554 if destdirexists:
559 if destdirexists:
555 striplen = len(os.path.split(abspfx)[0])
560 striplen = len(os.path.split(abspfx)[0])
556 else:
561 else:
557 striplen = len(abspfx)
562 striplen = len(abspfx)
558 if striplen:
563 if striplen:
559 striplen += len(os.sep)
564 striplen += len(os.sep)
560 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
565 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
561 elif destdirexists:
566 elif destdirexists:
562 res = lambda p: os.path.join(dest,
567 res = lambda p: os.path.join(dest,
563 os.path.basename(util.localpath(p)))
568 os.path.basename(util.localpath(p)))
564 else:
569 else:
565 res = lambda p: dest
570 res = lambda p: dest
566 return res
571 return res
567
572
568 # pat: ossep
573 # pat: ossep
569 # dest ossep
574 # dest ossep
570 # srcs: list of (hgsep, hgsep, ossep, bool)
575 # srcs: list of (hgsep, hgsep, ossep, bool)
571 # return: function that takes hgsep and returns ossep
576 # return: function that takes hgsep and returns ossep
572 def targetpathafterfn(pat, dest, srcs):
577 def targetpathafterfn(pat, dest, srcs):
573 if util.patkind(pat, None)[0]:
578 if util.patkind(pat, None)[0]:
574 # a mercurial pattern
579 # a mercurial pattern
575 res = lambda p: os.path.join(dest,
580 res = lambda p: os.path.join(dest,
576 os.path.basename(util.localpath(p)))
581 os.path.basename(util.localpath(p)))
577 else:
582 else:
578 abspfx = util.canonpath(repo.root, cwd, pat)
583 abspfx = util.canonpath(repo.root, cwd, pat)
579 if len(abspfx) < len(srcs[0][0]):
584 if len(abspfx) < len(srcs[0][0]):
580 # A directory. Either the target path contains the last
585 # A directory. Either the target path contains the last
581 # component of the source path or it does not.
586 # component of the source path or it does not.
582 def evalpath(striplen):
587 def evalpath(striplen):
583 score = 0
588 score = 0
584 for s in srcs:
589 for s in srcs:
585 t = os.path.join(dest, util.localpath(s[0])[striplen:])
590 t = os.path.join(dest, util.localpath(s[0])[striplen:])
586 if os.path.exists(t):
591 if os.path.exists(t):
587 score += 1
592 score += 1
588 return score
593 return score
589
594
590 abspfx = util.localpath(abspfx)
595 abspfx = util.localpath(abspfx)
591 striplen = len(abspfx)
596 striplen = len(abspfx)
592 if striplen:
597 if striplen:
593 striplen += len(os.sep)
598 striplen += len(os.sep)
594 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
599 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
595 score = evalpath(striplen)
600 score = evalpath(striplen)
596 striplen1 = len(os.path.split(abspfx)[0])
601 striplen1 = len(os.path.split(abspfx)[0])
597 if striplen1:
602 if striplen1:
598 striplen1 += len(os.sep)
603 striplen1 += len(os.sep)
599 if evalpath(striplen1) > score:
604 if evalpath(striplen1) > score:
600 striplen = striplen1
605 striplen = striplen1
601 res = lambda p: os.path.join(dest,
606 res = lambda p: os.path.join(dest,
602 util.localpath(p)[striplen:])
607 util.localpath(p)[striplen:])
603 else:
608 else:
604 # a file
609 # a file
605 if destdirexists:
610 if destdirexists:
606 res = lambda p: os.path.join(dest,
611 res = lambda p: os.path.join(dest,
607 os.path.basename(util.localpath(p)))
612 os.path.basename(util.localpath(p)))
608 else:
613 else:
609 res = lambda p: dest
614 res = lambda p: dest
610 return res
615 return res
611
616
612
617
613 pats = util.expand_glob(pats)
618 pats = util.expand_glob(pats)
614 if not pats:
619 if not pats:
615 raise util.Abort(_('no source or destination specified'))
620 raise util.Abort(_('no source or destination specified'))
616 if len(pats) == 1:
621 if len(pats) == 1:
617 raise util.Abort(_('no destination specified'))
622 raise util.Abort(_('no destination specified'))
618 dest = pats.pop()
623 dest = pats.pop()
619 destdirexists = os.path.isdir(dest)
624 destdirexists = os.path.isdir(dest)
620 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
625 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
621 raise util.Abort(_('with multiple sources, destination must be an '
626 raise util.Abort(_('with multiple sources, destination must be an '
622 'existing directory'))
627 'existing directory'))
623 if opts['after']:
628 if opts['after']:
624 tfn = targetpathafterfn
629 tfn = targetpathafterfn
625 else:
630 else:
626 tfn = targetpathfn
631 tfn = targetpathfn
627 copylist = []
632 copylist = []
628 for pat in pats:
633 for pat in pats:
629 srcs = []
634 srcs = []
630 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
635 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
631 globbed=True):
636 globbed=True):
632 origsrc = okaytocopy(abssrc, relsrc, exact)
637 origsrc = okaytocopy(abssrc, relsrc, exact)
633 if origsrc:
638 if origsrc:
634 srcs.append((origsrc, abssrc, relsrc, exact))
639 srcs.append((origsrc, abssrc, relsrc, exact))
635 if not srcs:
640 if not srcs:
636 continue
641 continue
637 copylist.append((tfn(pat, dest, srcs), srcs))
642 copylist.append((tfn(pat, dest, srcs), srcs))
638 if not copylist:
643 if not copylist:
639 raise util.Abort(_('no files to copy'))
644 raise util.Abort(_('no files to copy'))
640
645
641 for targetpath, srcs in copylist:
646 for targetpath, srcs in copylist:
642 for origsrc, abssrc, relsrc, exact in srcs:
647 for origsrc, abssrc, relsrc, exact in srcs:
643 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
648 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
644
649
645 if errors:
650 if errors:
646 ui.warn(_('(consider using --after)\n'))
651 ui.warn(_('(consider using --after)\n'))
647 return errors, copied
652 return errors, copied
648
653
649 def copy(ui, repo, *pats, **opts):
654 def copy(ui, repo, *pats, **opts):
650 """mark files as copied for the next commit
655 """mark files as copied for the next commit
651
656
652 Mark dest as having copies of source files. If dest is a
657 Mark dest as having copies of source files. If dest is a
653 directory, copies are put in that directory. If dest is a file,
658 directory, copies are put in that directory. If dest is a file,
654 there can only be one source.
659 there can only be one source.
655
660
656 By default, this command copies the contents of files as they
661 By default, this command copies the contents of files as they
657 stand in the working directory. If invoked with --after, the
662 stand in the working directory. If invoked with --after, the
658 operation is recorded, but no copying is performed.
663 operation is recorded, but no copying is performed.
659
664
660 This command takes effect in the next commit. To undo a copy
665 This command takes effect in the next commit. To undo a copy
661 before that, see hg revert.
666 before that, see hg revert.
662 """
667 """
663 wlock = repo.wlock(0)
668 wlock = repo.wlock(0)
664 errs, copied = docopy(ui, repo, pats, opts, wlock)
669 errs, copied = docopy(ui, repo, pats, opts, wlock)
665 return errs
670 return errs
666
671
667 def debugancestor(ui, index, rev1, rev2):
672 def debugancestor(ui, index, rev1, rev2):
668 """find the ancestor revision of two revisions in a given index"""
673 """find the ancestor revision of two revisions in a given index"""
669 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
674 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
670 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
675 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
671 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
676 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
672
677
673 def debugcomplete(ui, cmd='', **opts):
678 def debugcomplete(ui, cmd='', **opts):
674 """returns the completion list associated with the given command"""
679 """returns the completion list associated with the given command"""
675
680
676 if opts['options']:
681 if opts['options']:
677 options = []
682 options = []
678 otables = [globalopts]
683 otables = [globalopts]
679 if cmd:
684 if cmd:
680 aliases, entry = findcmd(ui, cmd)
685 aliases, entry = findcmd(ui, cmd)
681 otables.append(entry[1])
686 otables.append(entry[1])
682 for t in otables:
687 for t in otables:
683 for o in t:
688 for o in t:
684 if o[0]:
689 if o[0]:
685 options.append('-%s' % o[0])
690 options.append('-%s' % o[0])
686 options.append('--%s' % o[1])
691 options.append('--%s' % o[1])
687 ui.write("%s\n" % "\n".join(options))
692 ui.write("%s\n" % "\n".join(options))
688 return
693 return
689
694
690 clist = findpossible(ui, cmd).keys()
695 clist = findpossible(ui, cmd).keys()
691 clist.sort()
696 clist.sort()
692 ui.write("%s\n" % "\n".join(clist))
697 ui.write("%s\n" % "\n".join(clist))
693
698
694 def debugrebuildstate(ui, repo, rev=""):
699 def debugrebuildstate(ui, repo, rev=""):
695 """rebuild the dirstate as it would look like for the given revision"""
700 """rebuild the dirstate as it would look like for the given revision"""
696 if rev == "":
701 if rev == "":
697 rev = repo.changelog.tip()
702 rev = repo.changelog.tip()
698 ctx = repo.changectx(rev)
703 ctx = repo.changectx(rev)
699 files = ctx.manifest()
704 files = ctx.manifest()
700 wlock = repo.wlock()
705 wlock = repo.wlock()
701 repo.dirstate.rebuild(rev, files)
706 repo.dirstate.rebuild(rev, files)
702
707
703 def debugcheckstate(ui, repo):
708 def debugcheckstate(ui, repo):
704 """validate the correctness of the current dirstate"""
709 """validate the correctness of the current dirstate"""
705 parent1, parent2 = repo.dirstate.parents()
710 parent1, parent2 = repo.dirstate.parents()
706 repo.dirstate.read()
711 repo.dirstate.read()
707 dc = repo.dirstate.map
712 dc = repo.dirstate.map
708 keys = dc.keys()
713 keys = dc.keys()
709 keys.sort()
714 keys.sort()
710 m1 = repo.changectx(parent1).manifest()
715 m1 = repo.changectx(parent1).manifest()
711 m2 = repo.changectx(parent2).manifest()
716 m2 = repo.changectx(parent2).manifest()
712 errors = 0
717 errors = 0
713 for f in dc:
718 for f in dc:
714 state = repo.dirstate.state(f)
719 state = repo.dirstate.state(f)
715 if state in "nr" and f not in m1:
720 if state in "nr" and f not in m1:
716 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
721 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
717 errors += 1
722 errors += 1
718 if state in "a" and f in m1:
723 if state in "a" and f in m1:
719 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
724 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
720 errors += 1
725 errors += 1
721 if state in "m" and f not in m1 and f not in m2:
726 if state in "m" and f not in m1 and f not in m2:
722 ui.warn(_("%s in state %s, but not in either manifest\n") %
727 ui.warn(_("%s in state %s, but not in either manifest\n") %
723 (f, state))
728 (f, state))
724 errors += 1
729 errors += 1
725 for f in m1:
730 for f in m1:
726 state = repo.dirstate.state(f)
731 state = repo.dirstate.state(f)
727 if state not in "nrm":
732 if state not in "nrm":
728 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
733 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
729 errors += 1
734 errors += 1
730 if errors:
735 if errors:
731 error = _(".hg/dirstate inconsistent with current parent's manifest")
736 error = _(".hg/dirstate inconsistent with current parent's manifest")
732 raise util.Abort(error)
737 raise util.Abort(error)
733
738
734 def showconfig(ui, repo, *values, **opts):
739 def showconfig(ui, repo, *values, **opts):
735 """show combined config settings from all hgrc files
740 """show combined config settings from all hgrc files
736
741
737 With no args, print names and values of all config items.
742 With no args, print names and values of all config items.
738
743
739 With one arg of the form section.name, print just the value of
744 With one arg of the form section.name, print just the value of
740 that config item.
745 that config item.
741
746
742 With multiple args, print names and values of all config items
747 With multiple args, print names and values of all config items
743 with matching section names."""
748 with matching section names."""
744
749
745 untrusted = bool(opts.get('untrusted'))
750 untrusted = bool(opts.get('untrusted'))
746 if values:
751 if values:
747 if len([v for v in values if '.' in v]) > 1:
752 if len([v for v in values if '.' in v]) > 1:
748 raise util.Abort(_('only one config item permitted'))
753 raise util.Abort(_('only one config item permitted'))
749 for section, name, value in ui.walkconfig(untrusted=untrusted):
754 for section, name, value in ui.walkconfig(untrusted=untrusted):
750 sectname = section + '.' + name
755 sectname = section + '.' + name
751 if values:
756 if values:
752 for v in values:
757 for v in values:
753 if v == section:
758 if v == section:
754 ui.write('%s=%s\n' % (sectname, value))
759 ui.write('%s=%s\n' % (sectname, value))
755 elif v == sectname:
760 elif v == sectname:
756 ui.write(value, '\n')
761 ui.write(value, '\n')
757 else:
762 else:
758 ui.write('%s=%s\n' % (sectname, value))
763 ui.write('%s=%s\n' % (sectname, value))
759
764
760 def debugsetparents(ui, repo, rev1, rev2=None):
765 def debugsetparents(ui, repo, rev1, rev2=None):
761 """manually set the parents of the current working directory
766 """manually set the parents of the current working directory
762
767
763 This is useful for writing repository conversion tools, but should
768 This is useful for writing repository conversion tools, but should
764 be used with care.
769 be used with care.
765 """
770 """
766
771
767 if not rev2:
772 if not rev2:
768 rev2 = hex(nullid)
773 rev2 = hex(nullid)
769
774
770 wlock = repo.wlock()
775 wlock = repo.wlock()
771 try:
776 try:
772 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
777 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
773 finally:
778 finally:
774 wlock.release()
779 wlock.release()
775
780
776 def debugstate(ui, repo):
781 def debugstate(ui, repo):
777 """show the contents of the current dirstate"""
782 """show the contents of the current dirstate"""
778 repo.dirstate.read()
783 repo.dirstate.read()
779 dc = repo.dirstate.map
784 dc = repo.dirstate.map
780 keys = dc.keys()
785 keys = dc.keys()
781 keys.sort()
786 keys.sort()
782 for file_ in keys:
787 for file_ in keys:
783 if dc[file_][3] == -1:
788 if dc[file_][3] == -1:
784 # Pad or slice to locale representation
789 # Pad or slice to locale representation
785 locale_len = len(time.strftime("%x %X", time.localtime(0)))
790 locale_len = len(time.strftime("%x %X", time.localtime(0)))
786 timestr = 'unset'
791 timestr = 'unset'
787 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
792 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
788 else:
793 else:
789 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
794 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
790 ui.write("%c %3o %10d %s %s\n"
795 ui.write("%c %3o %10d %s %s\n"
791 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
796 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
792 timestr, file_))
797 timestr, file_))
793 for f in repo.dirstate.copies():
798 for f in repo.dirstate.copies():
794 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
799 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
795
800
796 def debugdata(ui, file_, rev):
801 def debugdata(ui, file_, rev):
797 """dump the contents of a data file revision"""
802 """dump the contents of a data file revision"""
798 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
803 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
799 try:
804 try:
800 ui.write(r.revision(r.lookup(rev)))
805 ui.write(r.revision(r.lookup(rev)))
801 except KeyError:
806 except KeyError:
802 raise util.Abort(_('invalid revision identifier %s') % rev)
807 raise util.Abort(_('invalid revision identifier %s') % rev)
803
808
804 def debugdate(ui, date, range=None, **opts):
809 def debugdate(ui, date, range=None, **opts):
805 """parse and display a date"""
810 """parse and display a date"""
806 if opts["extended"]:
811 if opts["extended"]:
807 d = util.parsedate(date, util.extendeddateformats)
812 d = util.parsedate(date, util.extendeddateformats)
808 else:
813 else:
809 d = util.parsedate(date)
814 d = util.parsedate(date)
810 ui.write("internal: %s %s\n" % d)
815 ui.write("internal: %s %s\n" % d)
811 ui.write("standard: %s\n" % util.datestr(d))
816 ui.write("standard: %s\n" % util.datestr(d))
812 if range:
817 if range:
813 m = util.matchdate(range)
818 m = util.matchdate(range)
814 ui.write("match: %s\n" % m(d[0]))
819 ui.write("match: %s\n" % m(d[0]))
815
820
816 def debugindex(ui, file_):
821 def debugindex(ui, file_):
817 """dump the contents of an index file"""
822 """dump the contents of an index file"""
818 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
823 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
819 ui.write(" rev offset length base linkrev" +
824 ui.write(" rev offset length base linkrev" +
820 " nodeid p1 p2\n")
825 " nodeid p1 p2\n")
821 for i in xrange(r.count()):
826 for i in xrange(r.count()):
822 node = r.node(i)
827 node = r.node(i)
823 pp = r.parents(node)
828 pp = r.parents(node)
824 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
829 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
825 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
830 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
826 short(node), short(pp[0]), short(pp[1])))
831 short(node), short(pp[0]), short(pp[1])))
827
832
828 def debugindexdot(ui, file_):
833 def debugindexdot(ui, file_):
829 """dump an index DAG as a .dot file"""
834 """dump an index DAG as a .dot file"""
830 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
835 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
831 ui.write("digraph G {\n")
836 ui.write("digraph G {\n")
832 for i in xrange(r.count()):
837 for i in xrange(r.count()):
833 node = r.node(i)
838 node = r.node(i)
834 pp = r.parents(node)
839 pp = r.parents(node)
835 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
840 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
836 if pp[1] != nullid:
841 if pp[1] != nullid:
837 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
842 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
838 ui.write("}\n")
843 ui.write("}\n")
839
844
840 def debuginstall(ui):
845 def debuginstall(ui):
841 '''test Mercurial installation'''
846 '''test Mercurial installation'''
842
847
843 def writetemp(contents):
848 def writetemp(contents):
844 (fd, name) = tempfile.mkstemp()
849 (fd, name) = tempfile.mkstemp()
845 f = os.fdopen(fd, "wb")
850 f = os.fdopen(fd, "wb")
846 f.write(contents)
851 f.write(contents)
847 f.close()
852 f.close()
848 return name
853 return name
849
854
850 problems = 0
855 problems = 0
851
856
852 # encoding
857 # encoding
853 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
858 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
854 try:
859 try:
855 util.fromlocal("test")
860 util.fromlocal("test")
856 except util.Abort, inst:
861 except util.Abort, inst:
857 ui.write(" %s\n" % inst)
862 ui.write(" %s\n" % inst)
858 ui.write(_(" (check that your locale is properly set)\n"))
863 ui.write(_(" (check that your locale is properly set)\n"))
859 problems += 1
864 problems += 1
860
865
861 # compiled modules
866 # compiled modules
862 ui.status(_("Checking extensions...\n"))
867 ui.status(_("Checking extensions...\n"))
863 try:
868 try:
864 import bdiff, mpatch, base85
869 import bdiff, mpatch, base85
865 except Exception, inst:
870 except Exception, inst:
866 ui.write(" %s\n" % inst)
871 ui.write(" %s\n" % inst)
867 ui.write(_(" One or more extensions could not be found"))
872 ui.write(_(" One or more extensions could not be found"))
868 ui.write(_(" (check that you compiled the extensions)\n"))
873 ui.write(_(" (check that you compiled the extensions)\n"))
869 problems += 1
874 problems += 1
870
875
871 # templates
876 # templates
872 ui.status(_("Checking templates...\n"))
877 ui.status(_("Checking templates...\n"))
873 try:
878 try:
874 import templater
879 import templater
875 t = templater.templater(templater.templatepath("map-cmdline.default"))
880 t = templater.templater(templater.templatepath("map-cmdline.default"))
876 except Exception, inst:
881 except Exception, inst:
877 ui.write(" %s\n" % inst)
882 ui.write(" %s\n" % inst)
878 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
883 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
879 problems += 1
884 problems += 1
880
885
881 # patch
886 # patch
882 ui.status(_("Checking patch...\n"))
887 ui.status(_("Checking patch...\n"))
883 patcher = ui.config('ui', 'patch')
888 patcher = ui.config('ui', 'patch')
884 patcher = ((patcher and util.find_exe(patcher)) or
889 patcher = ((patcher and util.find_exe(patcher)) or
885 util.find_exe('gpatch') or
890 util.find_exe('gpatch') or
886 util.find_exe('patch'))
891 util.find_exe('patch'))
887 if not patcher:
892 if not patcher:
888 ui.write(_(" Can't find patch or gpatch in PATH\n"))
893 ui.write(_(" Can't find patch or gpatch in PATH\n"))
889 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
894 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
890 problems += 1
895 problems += 1
891 else:
896 else:
892 # actually attempt a patch here
897 # actually attempt a patch here
893 a = "1\n2\n3\n4\n"
898 a = "1\n2\n3\n4\n"
894 b = "1\n2\n3\ninsert\n4\n"
899 b = "1\n2\n3\ninsert\n4\n"
895 fa = writetemp(a)
900 fa = writetemp(a)
896 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
901 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
897 fd = writetemp(d)
902 fd = writetemp(d)
898
903
899 files = {}
904 files = {}
900 try:
905 try:
901 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
906 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
902 except util.Abort, e:
907 except util.Abort, e:
903 ui.write(_(" patch call failed:\n"))
908 ui.write(_(" patch call failed:\n"))
904 ui.write(" " + str(e) + "\n")
909 ui.write(" " + str(e) + "\n")
905 problems += 1
910 problems += 1
906 else:
911 else:
907 if list(files) != [os.path.basename(fa)]:
912 if list(files) != [os.path.basename(fa)]:
908 ui.write(_(" unexpected patch output!"))
913 ui.write(_(" unexpected patch output!"))
909 ui.write(_(" (you may have an incompatible version of patch)\n"))
914 ui.write(_(" (you may have an incompatible version of patch)\n"))
910 problems += 1
915 problems += 1
911 a = file(fa).read()
916 a = file(fa).read()
912 if a != b:
917 if a != b:
913 ui.write(_(" patch test failed!"))
918 ui.write(_(" patch test failed!"))
914 ui.write(_(" (you may have an incompatible version of patch)\n"))
919 ui.write(_(" (you may have an incompatible version of patch)\n"))
915 problems += 1
920 problems += 1
916
921
917 os.unlink(fa)
922 os.unlink(fa)
918 os.unlink(fd)
923 os.unlink(fd)
919
924
920 # merge helper
925 # merge helper
921 ui.status(_("Checking merge helper...\n"))
926 ui.status(_("Checking merge helper...\n"))
922 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
927 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
923 or "hgmerge")
928 or "hgmerge")
924 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
929 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
925 if not cmdpath:
930 if not cmdpath:
926 if cmd == 'hgmerge':
931 if cmd == 'hgmerge':
927 ui.write(_(" No merge helper set and can't find default"
932 ui.write(_(" No merge helper set and can't find default"
928 " hgmerge script in PATH\n"))
933 " hgmerge script in PATH\n"))
929 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
934 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
930 else:
935 else:
931 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
936 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
932 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
937 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
933 problems += 1
938 problems += 1
934 else:
939 else:
935 # actually attempt a patch here
940 # actually attempt a patch here
936 fa = writetemp("1\n2\n3\n4\n")
941 fa = writetemp("1\n2\n3\n4\n")
937 fl = writetemp("1\n2\n3\ninsert\n4\n")
942 fl = writetemp("1\n2\n3\ninsert\n4\n")
938 fr = writetemp("begin\n1\n2\n3\n4\n")
943 fr = writetemp("begin\n1\n2\n3\n4\n")
939 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
944 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
940 if r:
945 if r:
941 ui.write(_(" got unexpected merge error %d!") % r)
946 ui.write(_(" got unexpected merge error %d!") % r)
942 problems += 1
947 problems += 1
943 m = file(fl).read()
948 m = file(fl).read()
944 if m != "begin\n1\n2\n3\ninsert\n4\n":
949 if m != "begin\n1\n2\n3\ninsert\n4\n":
945 ui.write(_(" got unexpected merge results!") % r)
950 ui.write(_(" got unexpected merge results!") % r)
946 ui.write(_(" (your merge helper may have the"
951 ui.write(_(" (your merge helper may have the"
947 " wrong argument order)\n"))
952 " wrong argument order)\n"))
948 ui.write(m)
953 ui.write(m)
949 os.unlink(fa)
954 os.unlink(fa)
950 os.unlink(fl)
955 os.unlink(fl)
951 os.unlink(fr)
956 os.unlink(fr)
952
957
953 # editor
958 # editor
954 ui.status(_("Checking commit editor...\n"))
959 ui.status(_("Checking commit editor...\n"))
955 editor = (os.environ.get("HGEDITOR") or
960 editor = (os.environ.get("HGEDITOR") or
956 ui.config("ui", "editor") or
961 ui.config("ui", "editor") or
957 os.environ.get("EDITOR", "vi"))
962 os.environ.get("EDITOR", "vi"))
958 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
963 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
959 if not cmdpath:
964 if not cmdpath:
960 if editor == 'vi':
965 if editor == 'vi':
961 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
966 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
962 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
967 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
963 else:
968 else:
964 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
969 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
965 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
970 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
966 problems += 1
971 problems += 1
967
972
968 # check username
973 # check username
969 ui.status(_("Checking username...\n"))
974 ui.status(_("Checking username...\n"))
970 user = os.environ.get("HGUSER")
975 user = os.environ.get("HGUSER")
971 if user is None:
976 if user is None:
972 user = ui.config("ui", "username")
977 user = ui.config("ui", "username")
973 if user is None:
978 if user is None:
974 user = os.environ.get("EMAIL")
979 user = os.environ.get("EMAIL")
975 if not user:
980 if not user:
976 ui.warn(" ")
981 ui.warn(" ")
977 ui.username()
982 ui.username()
978 ui.write(_(" (specify a username in your .hgrc file)\n"))
983 ui.write(_(" (specify a username in your .hgrc file)\n"))
979
984
980 if not problems:
985 if not problems:
981 ui.status(_("No problems detected\n"))
986 ui.status(_("No problems detected\n"))
982 else:
987 else:
983 ui.write(_("%s problems detected,"
988 ui.write(_("%s problems detected,"
984 " please check your install!\n") % problems)
989 " please check your install!\n") % problems)
985
990
986 return problems
991 return problems
987
992
988 def debugrename(ui, repo, file1, *pats, **opts):
993 def debugrename(ui, repo, file1, *pats, **opts):
989 """dump rename information"""
994 """dump rename information"""
990
995
991 ctx = repo.changectx(opts.get('rev', 'tip'))
996 ctx = repo.changectx(opts.get('rev', 'tip'))
992 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
997 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
993 ctx.node()):
998 ctx.node()):
994 m = ctx.filectx(abs).renamed()
999 m = ctx.filectx(abs).renamed()
995 if m:
1000 if m:
996 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
1001 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
997 else:
1002 else:
998 ui.write(_("%s not renamed\n") % rel)
1003 ui.write(_("%s not renamed\n") % rel)
999
1004
1000 def debugwalk(ui, repo, *pats, **opts):
1005 def debugwalk(ui, repo, *pats, **opts):
1001 """show how files match on given patterns"""
1006 """show how files match on given patterns"""
1002 items = list(cmdutil.walk(repo, pats, opts))
1007 items = list(cmdutil.walk(repo, pats, opts))
1003 if not items:
1008 if not items:
1004 return
1009 return
1005 fmt = '%%s %%-%ds %%-%ds %%s' % (
1010 fmt = '%%s %%-%ds %%-%ds %%s' % (
1006 max([len(abs) for (src, abs, rel, exact) in items]),
1011 max([len(abs) for (src, abs, rel, exact) in items]),
1007 max([len(rel) for (src, abs, rel, exact) in items]))
1012 max([len(rel) for (src, abs, rel, exact) in items]))
1008 for src, abs, rel, exact in items:
1013 for src, abs, rel, exact in items:
1009 line = fmt % (src, abs, rel, exact and 'exact' or '')
1014 line = fmt % (src, abs, rel, exact and 'exact' or '')
1010 ui.write("%s\n" % line.rstrip())
1015 ui.write("%s\n" % line.rstrip())
1011
1016
1012 def diff(ui, repo, *pats, **opts):
1017 def diff(ui, repo, *pats, **opts):
1013 """diff repository (or selected files)
1018 """diff repository (or selected files)
1014
1019
1015 Show differences between revisions for the specified files.
1020 Show differences between revisions for the specified files.
1016
1021
1017 Differences between files are shown using the unified diff format.
1022 Differences between files are shown using the unified diff format.
1018
1023
1019 NOTE: diff may generate unexpected results for merges, as it will
1024 NOTE: diff may generate unexpected results for merges, as it will
1020 default to comparing against the working directory's first parent
1025 default to comparing against the working directory's first parent
1021 changeset if no revisions are specified.
1026 changeset if no revisions are specified.
1022
1027
1023 When two revision arguments are given, then changes are shown
1028 When two revision arguments are given, then changes are shown
1024 between those revisions. If only one revision is specified then
1029 between those revisions. If only one revision is specified then
1025 that revision is compared to the working directory, and, when no
1030 that revision is compared to the working directory, and, when no
1026 revisions are specified, the working directory files are compared
1031 revisions are specified, the working directory files are compared
1027 to its parent.
1032 to its parent.
1028
1033
1029 Without the -a option, diff will avoid generating diffs of files
1034 Without the -a option, diff will avoid generating diffs of files
1030 it detects as binary. With -a, diff will generate a diff anyway,
1035 it detects as binary. With -a, diff will generate a diff anyway,
1031 probably with undesirable results.
1036 probably with undesirable results.
1032 """
1037 """
1033 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1038 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1034
1039
1035 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1040 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1036
1041
1037 patch.diff(repo, node1, node2, fns, match=matchfn,
1042 patch.diff(repo, node1, node2, fns, match=matchfn,
1038 opts=patch.diffopts(ui, opts))
1043 opts=patch.diffopts(ui, opts))
1039
1044
1040 def export(ui, repo, *changesets, **opts):
1045 def export(ui, repo, *changesets, **opts):
1041 """dump the header and diffs for one or more changesets
1046 """dump the header and diffs for one or more changesets
1042
1047
1043 Print the changeset header and diffs for one or more revisions.
1048 Print the changeset header and diffs for one or more revisions.
1044
1049
1045 The information shown in the changeset header is: author,
1050 The information shown in the changeset header is: author,
1046 changeset hash, parent(s) and commit comment.
1051 changeset hash, parent(s) and commit comment.
1047
1052
1048 NOTE: export may generate unexpected diff output for merge changesets,
1053 NOTE: export may generate unexpected diff output for merge changesets,
1049 as it will compare the merge changeset against its first parent only.
1054 as it will compare the merge changeset against its first parent only.
1050
1055
1051 Output may be to a file, in which case the name of the file is
1056 Output may be to a file, in which case the name of the file is
1052 given using a format string. The formatting rules are as follows:
1057 given using a format string. The formatting rules are as follows:
1053
1058
1054 %% literal "%" character
1059 %% literal "%" character
1055 %H changeset hash (40 bytes of hexadecimal)
1060 %H changeset hash (40 bytes of hexadecimal)
1056 %N number of patches being generated
1061 %N number of patches being generated
1057 %R changeset revision number
1062 %R changeset revision number
1058 %b basename of the exporting repository
1063 %b basename of the exporting repository
1059 %h short-form changeset hash (12 bytes of hexadecimal)
1064 %h short-form changeset hash (12 bytes of hexadecimal)
1060 %n zero-padded sequence number, starting at 1
1065 %n zero-padded sequence number, starting at 1
1061 %r zero-padded changeset revision number
1066 %r zero-padded changeset revision number
1062
1067
1063 Without the -a option, export will avoid generating diffs of files
1068 Without the -a option, export will avoid generating diffs of files
1064 it detects as binary. With -a, export will generate a diff anyway,
1069 it detects as binary. With -a, export will generate a diff anyway,
1065 probably with undesirable results.
1070 probably with undesirable results.
1066
1071
1067 With the --switch-parent option, the diff will be against the second
1072 With the --switch-parent option, the diff will be against the second
1068 parent. It can be useful to review a merge.
1073 parent. It can be useful to review a merge.
1069 """
1074 """
1070 if not changesets:
1075 if not changesets:
1071 raise util.Abort(_("export requires at least one changeset"))
1076 raise util.Abort(_("export requires at least one changeset"))
1072 revs = cmdutil.revrange(repo, changesets)
1077 revs = cmdutil.revrange(repo, changesets)
1073 if len(revs) > 1:
1078 if len(revs) > 1:
1074 ui.note(_('exporting patches:\n'))
1079 ui.note(_('exporting patches:\n'))
1075 else:
1080 else:
1076 ui.note(_('exporting patch:\n'))
1081 ui.note(_('exporting patch:\n'))
1077 patch.export(repo, revs, template=opts['output'],
1082 patch.export(repo, revs, template=opts['output'],
1078 switch_parent=opts['switch_parent'],
1083 switch_parent=opts['switch_parent'],
1079 opts=patch.diffopts(ui, opts))
1084 opts=patch.diffopts(ui, opts))
1080
1085
1081 def grep(ui, repo, pattern, *pats, **opts):
1086 def grep(ui, repo, pattern, *pats, **opts):
1082 """search for a pattern in specified files and revisions
1087 """search for a pattern in specified files and revisions
1083
1088
1084 Search revisions of files for a regular expression.
1089 Search revisions of files for a regular expression.
1085
1090
1086 This command behaves differently than Unix grep. It only accepts
1091 This command behaves differently than Unix grep. It only accepts
1087 Python/Perl regexps. It searches repository history, not the
1092 Python/Perl regexps. It searches repository history, not the
1088 working directory. It always prints the revision number in which
1093 working directory. It always prints the revision number in which
1089 a match appears.
1094 a match appears.
1090
1095
1091 By default, grep only prints output for the first revision of a
1096 By default, grep only prints output for the first revision of a
1092 file in which it finds a match. To get it to print every revision
1097 file in which it finds a match. To get it to print every revision
1093 that contains a change in match status ("-" for a match that
1098 that contains a change in match status ("-" for a match that
1094 becomes a non-match, or "+" for a non-match that becomes a match),
1099 becomes a non-match, or "+" for a non-match that becomes a match),
1095 use the --all flag.
1100 use the --all flag.
1096 """
1101 """
1097 reflags = 0
1102 reflags = 0
1098 if opts['ignore_case']:
1103 if opts['ignore_case']:
1099 reflags |= re.I
1104 reflags |= re.I
1100 regexp = re.compile(pattern, reflags)
1105 regexp = re.compile(pattern, reflags)
1101 sep, eol = ':', '\n'
1106 sep, eol = ':', '\n'
1102 if opts['print0']:
1107 if opts['print0']:
1103 sep = eol = '\0'
1108 sep = eol = '\0'
1104
1109
1105 fcache = {}
1110 fcache = {}
1106 def getfile(fn):
1111 def getfile(fn):
1107 if fn not in fcache:
1112 if fn not in fcache:
1108 fcache[fn] = repo.file(fn)
1113 fcache[fn] = repo.file(fn)
1109 return fcache[fn]
1114 return fcache[fn]
1110
1115
1111 def matchlines(body):
1116 def matchlines(body):
1112 begin = 0
1117 begin = 0
1113 linenum = 0
1118 linenum = 0
1114 while True:
1119 while True:
1115 match = regexp.search(body, begin)
1120 match = regexp.search(body, begin)
1116 if not match:
1121 if not match:
1117 break
1122 break
1118 mstart, mend = match.span()
1123 mstart, mend = match.span()
1119 linenum += body.count('\n', begin, mstart) + 1
1124 linenum += body.count('\n', begin, mstart) + 1
1120 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1125 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1121 lend = body.find('\n', mend)
1126 lend = body.find('\n', mend)
1122 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1127 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1123 begin = lend + 1
1128 begin = lend + 1
1124
1129
1125 class linestate(object):
1130 class linestate(object):
1126 def __init__(self, line, linenum, colstart, colend):
1131 def __init__(self, line, linenum, colstart, colend):
1127 self.line = line
1132 self.line = line
1128 self.linenum = linenum
1133 self.linenum = linenum
1129 self.colstart = colstart
1134 self.colstart = colstart
1130 self.colend = colend
1135 self.colend = colend
1131
1136
1132 def __eq__(self, other):
1137 def __eq__(self, other):
1133 return self.line == other.line
1138 return self.line == other.line
1134
1139
1135 matches = {}
1140 matches = {}
1136 copies = {}
1141 copies = {}
1137 def grepbody(fn, rev, body):
1142 def grepbody(fn, rev, body):
1138 matches[rev].setdefault(fn, [])
1143 matches[rev].setdefault(fn, [])
1139 m = matches[rev][fn]
1144 m = matches[rev][fn]
1140 for lnum, cstart, cend, line in matchlines(body):
1145 for lnum, cstart, cend, line in matchlines(body):
1141 s = linestate(line, lnum, cstart, cend)
1146 s = linestate(line, lnum, cstart, cend)
1142 m.append(s)
1147 m.append(s)
1143
1148
1144 def difflinestates(a, b):
1149 def difflinestates(a, b):
1145 sm = difflib.SequenceMatcher(None, a, b)
1150 sm = difflib.SequenceMatcher(None, a, b)
1146 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1151 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1147 if tag == 'insert':
1152 if tag == 'insert':
1148 for i in xrange(blo, bhi):
1153 for i in xrange(blo, bhi):
1149 yield ('+', b[i])
1154 yield ('+', b[i])
1150 elif tag == 'delete':
1155 elif tag == 'delete':
1151 for i in xrange(alo, ahi):
1156 for i in xrange(alo, ahi):
1152 yield ('-', a[i])
1157 yield ('-', a[i])
1153 elif tag == 'replace':
1158 elif tag == 'replace':
1154 for i in xrange(alo, ahi):
1159 for i in xrange(alo, ahi):
1155 yield ('-', a[i])
1160 yield ('-', a[i])
1156 for i in xrange(blo, bhi):
1161 for i in xrange(blo, bhi):
1157 yield ('+', b[i])
1162 yield ('+', b[i])
1158
1163
1159 prev = {}
1164 prev = {}
1160 def display(fn, rev, states, prevstates):
1165 def display(fn, rev, states, prevstates):
1161 found = False
1166 found = False
1162 filerevmatches = {}
1167 filerevmatches = {}
1163 r = prev.get(fn, -1)
1168 r = prev.get(fn, -1)
1164 if opts['all']:
1169 if opts['all']:
1165 iter = difflinestates(states, prevstates)
1170 iter = difflinestates(states, prevstates)
1166 else:
1171 else:
1167 iter = [('', l) for l in prevstates]
1172 iter = [('', l) for l in prevstates]
1168 for change, l in iter:
1173 for change, l in iter:
1169 cols = [fn, str(r)]
1174 cols = [fn, str(r)]
1170 if opts['line_number']:
1175 if opts['line_number']:
1171 cols.append(str(l.linenum))
1176 cols.append(str(l.linenum))
1172 if opts['all']:
1177 if opts['all']:
1173 cols.append(change)
1178 cols.append(change)
1174 if opts['user']:
1179 if opts['user']:
1175 cols.append(ui.shortuser(get(r)[1]))
1180 cols.append(ui.shortuser(get(r)[1]))
1176 if opts['files_with_matches']:
1181 if opts['files_with_matches']:
1177 c = (fn, r)
1182 c = (fn, r)
1178 if c in filerevmatches:
1183 if c in filerevmatches:
1179 continue
1184 continue
1180 filerevmatches[c] = 1
1185 filerevmatches[c] = 1
1181 else:
1186 else:
1182 cols.append(l.line)
1187 cols.append(l.line)
1183 ui.write(sep.join(cols), eol)
1188 ui.write(sep.join(cols), eol)
1184 found = True
1189 found = True
1185 return found
1190 return found
1186
1191
1187 fstate = {}
1192 fstate = {}
1188 skip = {}
1193 skip = {}
1189 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1194 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1190 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1195 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1191 found = False
1196 found = False
1192 follow = opts.get('follow')
1197 follow = opts.get('follow')
1193 for st, rev, fns in changeiter:
1198 for st, rev, fns in changeiter:
1194 if st == 'window':
1199 if st == 'window':
1195 matches.clear()
1200 matches.clear()
1196 elif st == 'add':
1201 elif st == 'add':
1197 mf = repo.changectx(rev).manifest()
1202 mf = repo.changectx(rev).manifest()
1198 matches[rev] = {}
1203 matches[rev] = {}
1199 for fn in fns:
1204 for fn in fns:
1200 if fn in skip:
1205 if fn in skip:
1201 continue
1206 continue
1202 fstate.setdefault(fn, {})
1207 fstate.setdefault(fn, {})
1203 try:
1208 try:
1204 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1209 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1205 if follow:
1210 if follow:
1206 copied = getfile(fn).renamed(mf[fn])
1211 copied = getfile(fn).renamed(mf[fn])
1207 if copied:
1212 if copied:
1208 copies.setdefault(rev, {})[fn] = copied[0]
1213 copies.setdefault(rev, {})[fn] = copied[0]
1209 except KeyError:
1214 except KeyError:
1210 pass
1215 pass
1211 elif st == 'iter':
1216 elif st == 'iter':
1212 states = matches[rev].items()
1217 states = matches[rev].items()
1213 states.sort()
1218 states.sort()
1214 for fn, m in states:
1219 for fn, m in states:
1215 copy = copies.get(rev, {}).get(fn)
1220 copy = copies.get(rev, {}).get(fn)
1216 if fn in skip:
1221 if fn in skip:
1217 if copy:
1222 if copy:
1218 skip[copy] = True
1223 skip[copy] = True
1219 continue
1224 continue
1220 if fn in prev or fstate[fn]:
1225 if fn in prev or fstate[fn]:
1221 r = display(fn, rev, m, fstate[fn])
1226 r = display(fn, rev, m, fstate[fn])
1222 found = found or r
1227 found = found or r
1223 if r and not opts['all']:
1228 if r and not opts['all']:
1224 skip[fn] = True
1229 skip[fn] = True
1225 if copy:
1230 if copy:
1226 skip[copy] = True
1231 skip[copy] = True
1227 fstate[fn] = m
1232 fstate[fn] = m
1228 if copy:
1233 if copy:
1229 fstate[copy] = m
1234 fstate[copy] = m
1230 prev[fn] = rev
1235 prev[fn] = rev
1231
1236
1232 fstate = fstate.items()
1237 fstate = fstate.items()
1233 fstate.sort()
1238 fstate.sort()
1234 for fn, state in fstate:
1239 for fn, state in fstate:
1235 if fn in skip:
1240 if fn in skip:
1236 continue
1241 continue
1237 if fn not in copies.get(prev[fn], {}):
1242 if fn not in copies.get(prev[fn], {}):
1238 found = display(fn, rev, {}, state) or found
1243 found = display(fn, rev, {}, state) or found
1239 return (not found and 1) or 0
1244 return (not found and 1) or 0
1240
1245
1241 def heads(ui, repo, **opts):
1246 def heads(ui, repo, **opts):
1242 """show current repository heads
1247 """show current repository heads
1243
1248
1244 Show all repository head changesets.
1249 Show all repository head changesets.
1245
1250
1246 Repository "heads" are changesets that don't have children
1251 Repository "heads" are changesets that don't have children
1247 changesets. They are where development generally takes place and
1252 changesets. They are where development generally takes place and
1248 are the usual targets for update and merge operations.
1253 are the usual targets for update and merge operations.
1249 """
1254 """
1250 if opts['rev']:
1255 if opts['rev']:
1251 heads = repo.heads(repo.lookup(opts['rev']))
1256 heads = repo.heads(repo.lookup(opts['rev']))
1252 else:
1257 else:
1253 heads = repo.heads()
1258 heads = repo.heads()
1254 displayer = cmdutil.show_changeset(ui, repo, opts)
1259 displayer = cmdutil.show_changeset(ui, repo, opts)
1255 for n in heads:
1260 for n in heads:
1256 displayer.show(changenode=n)
1261 displayer.show(changenode=n)
1257
1262
1258 def help_(ui, name=None, with_version=False):
1263 def help_(ui, name=None, with_version=False):
1259 """show help for a command, extension, or list of commands
1264 """show help for a command, extension, or list of commands
1260
1265
1261 With no arguments, print a list of commands and short help.
1266 With no arguments, print a list of commands and short help.
1262
1267
1263 Given a command name, print help for that command.
1268 Given a command name, print help for that command.
1264
1269
1265 Given an extension name, print help for that extension, and the
1270 Given an extension name, print help for that extension, and the
1266 commands it provides."""
1271 commands it provides."""
1267 option_lists = []
1272 option_lists = []
1268
1273
1269 def addglobalopts(aliases):
1274 def addglobalopts(aliases):
1270 if ui.verbose:
1275 if ui.verbose:
1271 option_lists.append((_("global options:"), globalopts))
1276 option_lists.append((_("global options:"), globalopts))
1272 if name == 'shortlist':
1277 if name == 'shortlist':
1273 option_lists.append((_('use "hg help" for the full list '
1278 option_lists.append((_('use "hg help" for the full list '
1274 'of commands'), ()))
1279 'of commands'), ()))
1275 else:
1280 else:
1276 if name == 'shortlist':
1281 if name == 'shortlist':
1277 msg = _('use "hg help" for the full list of commands '
1282 msg = _('use "hg help" for the full list of commands '
1278 'or "hg -v" for details')
1283 'or "hg -v" for details')
1279 elif aliases:
1284 elif aliases:
1280 msg = _('use "hg -v help%s" to show aliases and '
1285 msg = _('use "hg -v help%s" to show aliases and '
1281 'global options') % (name and " " + name or "")
1286 'global options') % (name and " " + name or "")
1282 else:
1287 else:
1283 msg = _('use "hg -v help %s" to show global options') % name
1288 msg = _('use "hg -v help %s" to show global options') % name
1284 option_lists.append((msg, ()))
1289 option_lists.append((msg, ()))
1285
1290
1286 def helpcmd(name):
1291 def helpcmd(name):
1287 if with_version:
1292 if with_version:
1288 version_(ui)
1293 version_(ui)
1289 ui.write('\n')
1294 ui.write('\n')
1290 aliases, i = findcmd(ui, name)
1295 aliases, i = findcmd(ui, name)
1291 # synopsis
1296 # synopsis
1292 ui.write("%s\n\n" % i[2])
1297 ui.write("%s\n\n" % i[2])
1293
1298
1294 # description
1299 # description
1295 doc = i[0].__doc__
1300 doc = i[0].__doc__
1296 if not doc:
1301 if not doc:
1297 doc = _("(No help text available)")
1302 doc = _("(No help text available)")
1298 if ui.quiet:
1303 if ui.quiet:
1299 doc = doc.splitlines(0)[0]
1304 doc = doc.splitlines(0)[0]
1300 ui.write("%s\n" % doc.rstrip())
1305 ui.write("%s\n" % doc.rstrip())
1301
1306
1302 if not ui.quiet:
1307 if not ui.quiet:
1303 # aliases
1308 # aliases
1304 if len(aliases) > 1:
1309 if len(aliases) > 1:
1305 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1310 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1306
1311
1307 # options
1312 # options
1308 if i[1]:
1313 if i[1]:
1309 option_lists.append((_("options:\n"), i[1]))
1314 option_lists.append((_("options:\n"), i[1]))
1310
1315
1311 addglobalopts(False)
1316 addglobalopts(False)
1312
1317
1313 def helplist(select=None):
1318 def helplist(select=None):
1314 h = {}
1319 h = {}
1315 cmds = {}
1320 cmds = {}
1316 for c, e in table.items():
1321 for c, e in table.items():
1317 f = c.split("|", 1)[0]
1322 f = c.split("|", 1)[0]
1318 if select and not select(f):
1323 if select and not select(f):
1319 continue
1324 continue
1320 if name == "shortlist" and not f.startswith("^"):
1325 if name == "shortlist" and not f.startswith("^"):
1321 continue
1326 continue
1322 f = f.lstrip("^")
1327 f = f.lstrip("^")
1323 if not ui.debugflag and f.startswith("debug"):
1328 if not ui.debugflag and f.startswith("debug"):
1324 continue
1329 continue
1325 doc = e[0].__doc__
1330 doc = e[0].__doc__
1326 if not doc:
1331 if not doc:
1327 doc = _("(No help text available)")
1332 doc = _("(No help text available)")
1328 h[f] = doc.splitlines(0)[0].rstrip()
1333 h[f] = doc.splitlines(0)[0].rstrip()
1329 cmds[f] = c.lstrip("^")
1334 cmds[f] = c.lstrip("^")
1330
1335
1331 fns = h.keys()
1336 fns = h.keys()
1332 fns.sort()
1337 fns.sort()
1333 m = max(map(len, fns))
1338 m = max(map(len, fns))
1334 for f in fns:
1339 for f in fns:
1335 if ui.verbose:
1340 if ui.verbose:
1336 commands = cmds[f].replace("|",", ")
1341 commands = cmds[f].replace("|",", ")
1337 ui.write(" %s:\n %s\n"%(commands, h[f]))
1342 ui.write(" %s:\n %s\n"%(commands, h[f]))
1338 else:
1343 else:
1339 ui.write(' %-*s %s\n' % (m, f, h[f]))
1344 ui.write(' %-*s %s\n' % (m, f, h[f]))
1340
1345
1341 if not ui.quiet:
1346 if not ui.quiet:
1342 addglobalopts(True)
1347 addglobalopts(True)
1343
1348
1344 def helptopic(name):
1349 def helptopic(name):
1345 v = None
1350 v = None
1346 for i in help.helptable:
1351 for i in help.helptable:
1347 l = i.split('|')
1352 l = i.split('|')
1348 if name in l:
1353 if name in l:
1349 v = i
1354 v = i
1350 header = l[-1]
1355 header = l[-1]
1351 if not v:
1356 if not v:
1352 raise UnknownCommand(name)
1357 raise UnknownCommand(name)
1353
1358
1354 # description
1359 # description
1355 doc = help.helptable[v]
1360 doc = help.helptable[v]
1356 if not doc:
1361 if not doc:
1357 doc = _("(No help text available)")
1362 doc = _("(No help text available)")
1358 if callable(doc):
1363 if callable(doc):
1359 doc = doc()
1364 doc = doc()
1360
1365
1361 ui.write("%s\n" % header)
1366 ui.write("%s\n" % header)
1362 ui.write("%s\n" % doc.rstrip())
1367 ui.write("%s\n" % doc.rstrip())
1363
1368
1364 def helpext(name):
1369 def helpext(name):
1365 try:
1370 try:
1366 mod = findext(name)
1371 mod = findext(name)
1367 except KeyError:
1372 except KeyError:
1368 raise UnknownCommand(name)
1373 raise UnknownCommand(name)
1369
1374
1370 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1375 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1371 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1376 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1372 for d in doc[1:]:
1377 for d in doc[1:]:
1373 ui.write(d, '\n')
1378 ui.write(d, '\n')
1374
1379
1375 ui.status('\n')
1380 ui.status('\n')
1376
1381
1377 try:
1382 try:
1378 ct = mod.cmdtable
1383 ct = mod.cmdtable
1379 except AttributeError:
1384 except AttributeError:
1380 ui.status(_('no commands defined\n'))
1385 ui.status(_('no commands defined\n'))
1381 return
1386 return
1382
1387
1383 ui.status(_('list of commands:\n\n'))
1388 ui.status(_('list of commands:\n\n'))
1384 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1389 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1385 helplist(modcmds.has_key)
1390 helplist(modcmds.has_key)
1386
1391
1387 if name and name != 'shortlist':
1392 if name and name != 'shortlist':
1388 i = None
1393 i = None
1389 for f in (helpcmd, helptopic, helpext):
1394 for f in (helpcmd, helptopic, helpext):
1390 try:
1395 try:
1391 f(name)
1396 f(name)
1392 i = None
1397 i = None
1393 break
1398 break
1394 except UnknownCommand, inst:
1399 except UnknownCommand, inst:
1395 i = inst
1400 i = inst
1396 if i:
1401 if i:
1397 raise i
1402 raise i
1398
1403
1399 else:
1404 else:
1400 # program name
1405 # program name
1401 if ui.verbose or with_version:
1406 if ui.verbose or with_version:
1402 version_(ui)
1407 version_(ui)
1403 else:
1408 else:
1404 ui.status(_("Mercurial Distributed SCM\n"))
1409 ui.status(_("Mercurial Distributed SCM\n"))
1405 ui.status('\n')
1410 ui.status('\n')
1406
1411
1407 # list of commands
1412 # list of commands
1408 if name == "shortlist":
1413 if name == "shortlist":
1409 ui.status(_('basic commands:\n\n'))
1414 ui.status(_('basic commands:\n\n'))
1410 else:
1415 else:
1411 ui.status(_('list of commands:\n\n'))
1416 ui.status(_('list of commands:\n\n'))
1412
1417
1413 helplist()
1418 helplist()
1414
1419
1415 # list all option lists
1420 # list all option lists
1416 opt_output = []
1421 opt_output = []
1417 for title, options in option_lists:
1422 for title, options in option_lists:
1418 opt_output.append(("\n%s" % title, None))
1423 opt_output.append(("\n%s" % title, None))
1419 for shortopt, longopt, default, desc in options:
1424 for shortopt, longopt, default, desc in options:
1420 if "DEPRECATED" in desc and not ui.verbose: continue
1425 if "DEPRECATED" in desc and not ui.verbose: continue
1421 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1426 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1422 longopt and " --%s" % longopt),
1427 longopt and " --%s" % longopt),
1423 "%s%s" % (desc,
1428 "%s%s" % (desc,
1424 default
1429 default
1425 and _(" (default: %s)") % default
1430 and _(" (default: %s)") % default
1426 or "")))
1431 or "")))
1427
1432
1428 if opt_output:
1433 if opt_output:
1429 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1434 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1430 for first, second in opt_output:
1435 for first, second in opt_output:
1431 if second:
1436 if second:
1432 ui.write(" %-*s %s\n" % (opts_len, first, second))
1437 ui.write(" %-*s %s\n" % (opts_len, first, second))
1433 else:
1438 else:
1434 ui.write("%s\n" % first)
1439 ui.write("%s\n" % first)
1435
1440
1436 def identify(ui, repo):
1441 def identify(ui, repo):
1437 """print information about the working copy
1442 """print information about the working copy
1438
1443
1439 Print a short summary of the current state of the repo.
1444 Print a short summary of the current state of the repo.
1440
1445
1441 This summary identifies the repository state using one or two parent
1446 This summary identifies the repository state using one or two parent
1442 hash identifiers, followed by a "+" if there are uncommitted changes
1447 hash identifiers, followed by a "+" if there are uncommitted changes
1443 in the working directory, followed by a list of tags for this revision.
1448 in the working directory, followed by a list of tags for this revision.
1444 """
1449 """
1445 parents = [p for p in repo.dirstate.parents() if p != nullid]
1450 parents = [p for p in repo.dirstate.parents() if p != nullid]
1446 if not parents:
1451 if not parents:
1447 ui.write(_("unknown\n"))
1452 ui.write(_("unknown\n"))
1448 return
1453 return
1449
1454
1450 hexfunc = ui.debugflag and hex or short
1455 hexfunc = ui.debugflag and hex or short
1451 modified, added, removed, deleted = repo.status()[:4]
1456 modified, added, removed, deleted = repo.status()[:4]
1452 output = ["%s%s" %
1457 output = ["%s%s" %
1453 ('+'.join([hexfunc(parent) for parent in parents]),
1458 ('+'.join([hexfunc(parent) for parent in parents]),
1454 (modified or added or removed or deleted) and "+" or "")]
1459 (modified or added or removed or deleted) and "+" or "")]
1455
1460
1456 if not ui.quiet:
1461 if not ui.quiet:
1457
1462
1458 branch = util.tolocal(repo.workingctx().branch())
1463 branch = util.tolocal(repo.workingctx().branch())
1459 if branch != 'default':
1464 if branch != 'default':
1460 output.append("(%s)" % branch)
1465 output.append("(%s)" % branch)
1461
1466
1462 # multiple tags for a single parent separated by '/'
1467 # multiple tags for a single parent separated by '/'
1463 parenttags = ['/'.join(tags)
1468 parenttags = ['/'.join(tags)
1464 for tags in map(repo.nodetags, parents) if tags]
1469 for tags in map(repo.nodetags, parents) if tags]
1465 # tags for multiple parents separated by ' + '
1470 # tags for multiple parents separated by ' + '
1466 if parenttags:
1471 if parenttags:
1467 output.append(' + '.join(parenttags))
1472 output.append(' + '.join(parenttags))
1468
1473
1469 ui.write("%s\n" % ' '.join(output))
1474 ui.write("%s\n" % ' '.join(output))
1470
1475
1471 def import_(ui, repo, patch1, *patches, **opts):
1476 def import_(ui, repo, patch1, *patches, **opts):
1472 """import an ordered set of patches
1477 """import an ordered set of patches
1473
1478
1474 Import a list of patches and commit them individually.
1479 Import a list of patches and commit them individually.
1475
1480
1476 If there are outstanding changes in the working directory, import
1481 If there are outstanding changes in the working directory, import
1477 will abort unless given the -f flag.
1482 will abort unless given the -f flag.
1478
1483
1479 You can import a patch straight from a mail message. Even patches
1484 You can import a patch straight from a mail message. Even patches
1480 as attachments work (body part must be type text/plain or
1485 as attachments work (body part must be type text/plain or
1481 text/x-patch to be used). From and Subject headers of email
1486 text/x-patch to be used). From and Subject headers of email
1482 message are used as default committer and commit message. All
1487 message are used as default committer and commit message. All
1483 text/plain body parts before first diff are added to commit
1488 text/plain body parts before first diff are added to commit
1484 message.
1489 message.
1485
1490
1486 If the imported patch was generated by hg export, user and description
1491 If the imported patch was generated by hg export, user and description
1487 from patch override values from message headers and body. Values
1492 from patch override values from message headers and body. Values
1488 given on command line with -m and -u override these.
1493 given on command line with -m and -u override these.
1489
1494
1490 If --exact is specified, import will set the working directory
1495 If --exact is specified, import will set the working directory
1491 to the parent of each patch before applying it, and will abort
1496 to the parent of each patch before applying it, and will abort
1492 if the resulting changeset has a different ID than the one
1497 if the resulting changeset has a different ID than the one
1493 recorded in the patch. This may happen due to character set
1498 recorded in the patch. This may happen due to character set
1494 problems or other deficiencies in the text patch format.
1499 problems or other deficiencies in the text patch format.
1495
1500
1496 To read a patch from standard input, use patch name "-".
1501 To read a patch from standard input, use patch name "-".
1497 """
1502 """
1498 patches = (patch1,) + patches
1503 patches = (patch1,) + patches
1499
1504
1500 if opts.get('exact') or not opts['force']:
1505 if opts.get('exact') or not opts['force']:
1501 bail_if_changed(repo)
1506 bail_if_changed(repo)
1502
1507
1503 d = opts["base"]
1508 d = opts["base"]
1504 strip = opts["strip"]
1509 strip = opts["strip"]
1505
1510
1506 wlock = repo.wlock()
1511 wlock = repo.wlock()
1507 lock = repo.lock()
1512 lock = repo.lock()
1508
1513
1509 for p in patches:
1514 for p in patches:
1510 pf = os.path.join(d, p)
1515 pf = os.path.join(d, p)
1511
1516
1512 if pf == '-':
1517 if pf == '-':
1513 ui.status(_("applying patch from stdin\n"))
1518 ui.status(_("applying patch from stdin\n"))
1514 tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1519 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1515 else:
1520 else:
1516 ui.status(_("applying %s\n") % p)
1521 ui.status(_("applying %s\n") % p)
1517 tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, file(pf))
1522 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf))
1518
1523
1519 if tmpname is None:
1524 if tmpname is None:
1520 raise util.Abort(_('no diffs found'))
1525 raise util.Abort(_('no diffs found'))
1521
1526
1522 try:
1527 try:
1523 cmdline_message = logmessage(opts)
1528 cmdline_message = logmessage(opts)
1524 if cmdline_message:
1529 if cmdline_message:
1525 # pickup the cmdline msg
1530 # pickup the cmdline msg
1526 message = cmdline_message
1531 message = cmdline_message
1527 elif message:
1532 elif message:
1528 # pickup the patch msg
1533 # pickup the patch msg
1529 message = message.strip()
1534 message = message.strip()
1530 else:
1535 else:
1531 # launch the editor
1536 # launch the editor
1532 message = None
1537 message = None
1533 ui.debug(_('message:\n%s\n') % message)
1538 ui.debug(_('message:\n%s\n') % message)
1534
1539
1535 wp = repo.workingctx().parents()
1540 wp = repo.workingctx().parents()
1536 if opts.get('exact'):
1541 if opts.get('exact'):
1537 if not nodeid or not p1:
1542 if not nodeid or not p1:
1538 raise util.Abort(_('not a mercurial patch'))
1543 raise util.Abort(_('not a mercurial patch'))
1539 p1 = repo.lookup(p1)
1544 p1 = repo.lookup(p1)
1540 p2 = repo.lookup(p2 or hex(nullid))
1545 p2 = repo.lookup(p2 or hex(nullid))
1541
1546
1542 if p1 != wp[0].node():
1547 if p1 != wp[0].node():
1543 hg.clean(repo, p1, wlock=wlock)
1548 hg.clean(repo, p1, wlock=wlock)
1544 repo.dirstate.setparents(p1, p2)
1549 repo.dirstate.setparents(p1, p2)
1550 repo.dirstate.setbranch(branch or 'default')
1545 elif p2:
1551 elif p2:
1546 try:
1552 try:
1547 p1 = repo.lookup(p1)
1553 p1 = repo.lookup(p1)
1548 p2 = repo.lookup(p2)
1554 p2 = repo.lookup(p2)
1549 if p1 == wp[0].node():
1555 if p1 == wp[0].node():
1550 repo.dirstate.setparents(p1, p2)
1556 repo.dirstate.setparents(p1, p2)
1551 except hg.RepoError:
1557 except hg.RepoError:
1552 pass
1558 pass
1553
1559
1554 files = {}
1560 files = {}
1555 try:
1561 try:
1556 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1562 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1557 files=files)
1563 files=files)
1558 finally:
1564 finally:
1559 files = patch.updatedir(ui, repo, files, wlock=wlock)
1565 files = patch.updatedir(ui, repo, files, wlock=wlock)
1560 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1566 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1561 if opts.get('exact'):
1567 if opts.get('exact'):
1562 if hex(n) != nodeid:
1568 if hex(n) != nodeid:
1563 repo.rollback(wlock=wlock, lock=lock)
1569 repo.rollback(wlock=wlock, lock=lock)
1564 raise util.Abort(_('patch is damaged or loses information'))
1570 raise util.Abort(_('patch is damaged or loses information'))
1565 finally:
1571 finally:
1566 os.unlink(tmpname)
1572 os.unlink(tmpname)
1567
1573
1568 def incoming(ui, repo, source="default", **opts):
1574 def incoming(ui, repo, source="default", **opts):
1569 """show new changesets found in source
1575 """show new changesets found in source
1570
1576
1571 Show new changesets found in the specified path/URL or the default
1577 Show new changesets found in the specified path/URL or the default
1572 pull location. These are the changesets that would be pulled if a pull
1578 pull location. These are the changesets that would be pulled if a pull
1573 was requested.
1579 was requested.
1574
1580
1575 For remote repository, using --bundle avoids downloading the changesets
1581 For remote repository, using --bundle avoids downloading the changesets
1576 twice if the incoming is followed by a pull.
1582 twice if the incoming is followed by a pull.
1577
1583
1578 See pull for valid source format details.
1584 See pull for valid source format details.
1579 """
1585 """
1580 source = ui.expandpath(source)
1586 source = ui.expandpath(source)
1581 setremoteconfig(ui, opts)
1587 setremoteconfig(ui, opts)
1582
1588
1583 other = hg.repository(ui, source)
1589 other = hg.repository(ui, source)
1584 ui.status(_('comparing with %s\n') % source)
1590 ui.status(_('comparing with %s\n') % source)
1585 incoming = repo.findincoming(other, force=opts["force"])
1591 incoming = repo.findincoming(other, force=opts["force"])
1586 if not incoming:
1592 if not incoming:
1587 try:
1593 try:
1588 os.unlink(opts["bundle"])
1594 os.unlink(opts["bundle"])
1589 except:
1595 except:
1590 pass
1596 pass
1591 ui.status(_("no changes found\n"))
1597 ui.status(_("no changes found\n"))
1592 return 1
1598 return 1
1593
1599
1594 cleanup = None
1600 cleanup = None
1595 try:
1601 try:
1596 fname = opts["bundle"]
1602 fname = opts["bundle"]
1597 if fname or not other.local():
1603 if fname or not other.local():
1598 # create a bundle (uncompressed if other repo is not local)
1604 # create a bundle (uncompressed if other repo is not local)
1599 cg = other.changegroup(incoming, "incoming")
1605 cg = other.changegroup(incoming, "incoming")
1600 bundletype = other.local() and "HG10BZ" or "HG10UN"
1606 bundletype = other.local() and "HG10BZ" or "HG10UN"
1601 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1607 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1602 # keep written bundle?
1608 # keep written bundle?
1603 if opts["bundle"]:
1609 if opts["bundle"]:
1604 cleanup = None
1610 cleanup = None
1605 if not other.local():
1611 if not other.local():
1606 # use the created uncompressed bundlerepo
1612 # use the created uncompressed bundlerepo
1607 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1613 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1608
1614
1609 revs = None
1615 revs = None
1610 if opts['rev']:
1616 if opts['rev']:
1611 revs = [other.lookup(rev) for rev in opts['rev']]
1617 revs = [other.lookup(rev) for rev in opts['rev']]
1612 o = other.changelog.nodesbetween(incoming, revs)[0]
1618 o = other.changelog.nodesbetween(incoming, revs)[0]
1613 if opts['newest_first']:
1619 if opts['newest_first']:
1614 o.reverse()
1620 o.reverse()
1615 displayer = cmdutil.show_changeset(ui, other, opts)
1621 displayer = cmdutil.show_changeset(ui, other, opts)
1616 for n in o:
1622 for n in o:
1617 parents = [p for p in other.changelog.parents(n) if p != nullid]
1623 parents = [p for p in other.changelog.parents(n) if p != nullid]
1618 if opts['no_merges'] and len(parents) == 2:
1624 if opts['no_merges'] and len(parents) == 2:
1619 continue
1625 continue
1620 displayer.show(changenode=n)
1626 displayer.show(changenode=n)
1621 finally:
1627 finally:
1622 if hasattr(other, 'close'):
1628 if hasattr(other, 'close'):
1623 other.close()
1629 other.close()
1624 if cleanup:
1630 if cleanup:
1625 os.unlink(cleanup)
1631 os.unlink(cleanup)
1626
1632
1627 def init(ui, dest=".", **opts):
1633 def init(ui, dest=".", **opts):
1628 """create a new repository in the given directory
1634 """create a new repository in the given directory
1629
1635
1630 Initialize a new repository in the given directory. If the given
1636 Initialize a new repository in the given directory. If the given
1631 directory does not exist, it is created.
1637 directory does not exist, it is created.
1632
1638
1633 If no directory is given, the current directory is used.
1639 If no directory is given, the current directory is used.
1634
1640
1635 It is possible to specify an ssh:// URL as the destination.
1641 It is possible to specify an ssh:// URL as the destination.
1636 Look at the help text for the pull command for important details
1642 Look at the help text for the pull command for important details
1637 about ssh:// URLs.
1643 about ssh:// URLs.
1638 """
1644 """
1639 setremoteconfig(ui, opts)
1645 setremoteconfig(ui, opts)
1640 hg.repository(ui, dest, create=1)
1646 hg.repository(ui, dest, create=1)
1641
1647
1642 def locate(ui, repo, *pats, **opts):
1648 def locate(ui, repo, *pats, **opts):
1643 """locate files matching specific patterns
1649 """locate files matching specific patterns
1644
1650
1645 Print all files under Mercurial control whose names match the
1651 Print all files under Mercurial control whose names match the
1646 given patterns.
1652 given patterns.
1647
1653
1648 This command searches the entire repository by default. To search
1654 This command searches the entire repository by default. To search
1649 just the current directory and its subdirectories, use "--include .".
1655 just the current directory and its subdirectories, use "--include .".
1650
1656
1651 If no patterns are given to match, this command prints all file
1657 If no patterns are given to match, this command prints all file
1652 names.
1658 names.
1653
1659
1654 If you want to feed the output of this command into the "xargs"
1660 If you want to feed the output of this command into the "xargs"
1655 command, use the "-0" option to both this command and "xargs".
1661 command, use the "-0" option to both this command and "xargs".
1656 This will avoid the problem of "xargs" treating single filenames
1662 This will avoid the problem of "xargs" treating single filenames
1657 that contain white space as multiple filenames.
1663 that contain white space as multiple filenames.
1658 """
1664 """
1659 end = opts['print0'] and '\0' or '\n'
1665 end = opts['print0'] and '\0' or '\n'
1660 rev = opts['rev']
1666 rev = opts['rev']
1661 if rev:
1667 if rev:
1662 node = repo.lookup(rev)
1668 node = repo.lookup(rev)
1663 else:
1669 else:
1664 node = None
1670 node = None
1665
1671
1666 ret = 1
1672 ret = 1
1667 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1673 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1668 badmatch=util.always,
1674 badmatch=util.always,
1669 default='relglob'):
1675 default='relglob'):
1670 if src == 'b':
1676 if src == 'b':
1671 continue
1677 continue
1672 if not node and repo.dirstate.state(abs) == '?':
1678 if not node and repo.dirstate.state(abs) == '?':
1673 continue
1679 continue
1674 if opts['fullpath']:
1680 if opts['fullpath']:
1675 ui.write(os.path.join(repo.root, abs), end)
1681 ui.write(os.path.join(repo.root, abs), end)
1676 else:
1682 else:
1677 ui.write(((pats and rel) or abs), end)
1683 ui.write(((pats and rel) or abs), end)
1678 ret = 0
1684 ret = 0
1679
1685
1680 return ret
1686 return ret
1681
1687
1682 def log(ui, repo, *pats, **opts):
1688 def log(ui, repo, *pats, **opts):
1683 """show revision history of entire repository or files
1689 """show revision history of entire repository or files
1684
1690
1685 Print the revision history of the specified files or the entire
1691 Print the revision history of the specified files or the entire
1686 project.
1692 project.
1687
1693
1688 File history is shown without following rename or copy history of
1694 File history is shown without following rename or copy history of
1689 files. Use -f/--follow with a file name to follow history across
1695 files. Use -f/--follow with a file name to follow history across
1690 renames and copies. --follow without a file name will only show
1696 renames and copies. --follow without a file name will only show
1691 ancestors or descendants of the starting revision. --follow-first
1697 ancestors or descendants of the starting revision. --follow-first
1692 only follows the first parent of merge revisions.
1698 only follows the first parent of merge revisions.
1693
1699
1694 If no revision range is specified, the default is tip:0 unless
1700 If no revision range is specified, the default is tip:0 unless
1695 --follow is set, in which case the working directory parent is
1701 --follow is set, in which case the working directory parent is
1696 used as the starting revision.
1702 used as the starting revision.
1697
1703
1698 By default this command outputs: changeset id and hash, tags,
1704 By default this command outputs: changeset id and hash, tags,
1699 non-trivial parents, user, date and time, and a summary for each
1705 non-trivial parents, user, date and time, and a summary for each
1700 commit. When the -v/--verbose switch is used, the list of changed
1706 commit. When the -v/--verbose switch is used, the list of changed
1701 files and full commit message is shown.
1707 files and full commit message is shown.
1702
1708
1703 NOTE: log -p may generate unexpected diff output for merge
1709 NOTE: log -p may generate unexpected diff output for merge
1704 changesets, as it will compare the merge changeset against its
1710 changesets, as it will compare the merge changeset against its
1705 first parent only. Also, the files: list will only reflect files
1711 first parent only. Also, the files: list will only reflect files
1706 that are different from BOTH parents.
1712 that are different from BOTH parents.
1707
1713
1708 """
1714 """
1709
1715
1710 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1716 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1711 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1717 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1712
1718
1713 if opts['limit']:
1719 if opts['limit']:
1714 try:
1720 try:
1715 limit = int(opts['limit'])
1721 limit = int(opts['limit'])
1716 except ValueError:
1722 except ValueError:
1717 raise util.Abort(_('limit must be a positive integer'))
1723 raise util.Abort(_('limit must be a positive integer'))
1718 if limit <= 0: raise util.Abort(_('limit must be positive'))
1724 if limit <= 0: raise util.Abort(_('limit must be positive'))
1719 else:
1725 else:
1720 limit = sys.maxint
1726 limit = sys.maxint
1721 count = 0
1727 count = 0
1722
1728
1723 if opts['copies'] and opts['rev']:
1729 if opts['copies'] and opts['rev']:
1724 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1730 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1725 else:
1731 else:
1726 endrev = repo.changelog.count()
1732 endrev = repo.changelog.count()
1727 rcache = {}
1733 rcache = {}
1728 ncache = {}
1734 ncache = {}
1729 dcache = []
1735 dcache = []
1730 def getrenamed(fn, rev, man):
1736 def getrenamed(fn, rev, man):
1731 '''looks up all renames for a file (up to endrev) the first
1737 '''looks up all renames for a file (up to endrev) the first
1732 time the file is given. It indexes on the changerev and only
1738 time the file is given. It indexes on the changerev and only
1733 parses the manifest if linkrev != changerev.
1739 parses the manifest if linkrev != changerev.
1734 Returns rename info for fn at changerev rev.'''
1740 Returns rename info for fn at changerev rev.'''
1735 if fn not in rcache:
1741 if fn not in rcache:
1736 rcache[fn] = {}
1742 rcache[fn] = {}
1737 ncache[fn] = {}
1743 ncache[fn] = {}
1738 fl = repo.file(fn)
1744 fl = repo.file(fn)
1739 for i in xrange(fl.count()):
1745 for i in xrange(fl.count()):
1740 node = fl.node(i)
1746 node = fl.node(i)
1741 lr = fl.linkrev(node)
1747 lr = fl.linkrev(node)
1742 renamed = fl.renamed(node)
1748 renamed = fl.renamed(node)
1743 rcache[fn][lr] = renamed
1749 rcache[fn][lr] = renamed
1744 if renamed:
1750 if renamed:
1745 ncache[fn][node] = renamed
1751 ncache[fn][node] = renamed
1746 if lr >= endrev:
1752 if lr >= endrev:
1747 break
1753 break
1748 if rev in rcache[fn]:
1754 if rev in rcache[fn]:
1749 return rcache[fn][rev]
1755 return rcache[fn][rev]
1750 mr = repo.manifest.rev(man)
1756 mr = repo.manifest.rev(man)
1751 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1757 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1752 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1758 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1753 if not dcache or dcache[0] != man:
1759 if not dcache or dcache[0] != man:
1754 dcache[:] = [man, repo.manifest.readdelta(man)]
1760 dcache[:] = [man, repo.manifest.readdelta(man)]
1755 if fn in dcache[1]:
1761 if fn in dcache[1]:
1756 return ncache[fn].get(dcache[1][fn])
1762 return ncache[fn].get(dcache[1][fn])
1757 return None
1763 return None
1758
1764
1759 df = False
1765 df = False
1760 if opts["date"]:
1766 if opts["date"]:
1761 df = util.matchdate(opts["date"])
1767 df = util.matchdate(opts["date"])
1762
1768
1763 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1769 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1764 for st, rev, fns in changeiter:
1770 for st, rev, fns in changeiter:
1765 if st == 'add':
1771 if st == 'add':
1766 changenode = repo.changelog.node(rev)
1772 changenode = repo.changelog.node(rev)
1767 parents = [p for p in repo.changelog.parentrevs(rev)
1773 parents = [p for p in repo.changelog.parentrevs(rev)
1768 if p != nullrev]
1774 if p != nullrev]
1769 if opts['no_merges'] and len(parents) == 2:
1775 if opts['no_merges'] and len(parents) == 2:
1770 continue
1776 continue
1771 if opts['only_merges'] and len(parents) != 2:
1777 if opts['only_merges'] and len(parents) != 2:
1772 continue
1778 continue
1773
1779
1774 if df:
1780 if df:
1775 changes = get(rev)
1781 changes = get(rev)
1776 if not df(changes[2][0]):
1782 if not df(changes[2][0]):
1777 continue
1783 continue
1778
1784
1779 if opts['keyword']:
1785 if opts['keyword']:
1780 changes = get(rev)
1786 changes = get(rev)
1781 miss = 0
1787 miss = 0
1782 for k in [kw.lower() for kw in opts['keyword']]:
1788 for k in [kw.lower() for kw in opts['keyword']]:
1783 if not (k in changes[1].lower() or
1789 if not (k in changes[1].lower() or
1784 k in changes[4].lower() or
1790 k in changes[4].lower() or
1785 k in " ".join(changes[3]).lower()):
1791 k in " ".join(changes[3]).lower()):
1786 miss = 1
1792 miss = 1
1787 break
1793 break
1788 if miss:
1794 if miss:
1789 continue
1795 continue
1790
1796
1791 copies = []
1797 copies = []
1792 if opts.get('copies') and rev:
1798 if opts.get('copies') and rev:
1793 mf = get(rev)[0]
1799 mf = get(rev)[0]
1794 for fn in get(rev)[3]:
1800 for fn in get(rev)[3]:
1795 rename = getrenamed(fn, rev, mf)
1801 rename = getrenamed(fn, rev, mf)
1796 if rename:
1802 if rename:
1797 copies.append((fn, rename[0]))
1803 copies.append((fn, rename[0]))
1798 displayer.show(rev, changenode, copies=copies)
1804 displayer.show(rev, changenode, copies=copies)
1799 elif st == 'iter':
1805 elif st == 'iter':
1800 if count == limit: break
1806 if count == limit: break
1801 if displayer.flush(rev):
1807 if displayer.flush(rev):
1802 count += 1
1808 count += 1
1803
1809
1804 def manifest(ui, repo, rev=None):
1810 def manifest(ui, repo, rev=None):
1805 """output the current or given revision of the project manifest
1811 """output the current or given revision of the project manifest
1806
1812
1807 Print a list of version controlled files for the given revision.
1813 Print a list of version controlled files for the given revision.
1808 If no revision is given, the parent of the working directory is used,
1814 If no revision is given, the parent of the working directory is used,
1809 or tip if no revision is checked out.
1815 or tip if no revision is checked out.
1810
1816
1811 The manifest is the list of files being version controlled. If no revision
1817 The manifest is the list of files being version controlled. If no revision
1812 is given then the first parent of the working directory is used.
1818 is given then the first parent of the working directory is used.
1813
1819
1814 With -v flag, print file permissions. With --debug flag, print
1820 With -v flag, print file permissions. With --debug flag, print
1815 file revision hashes.
1821 file revision hashes.
1816 """
1822 """
1817
1823
1818 m = repo.changectx(rev).manifest()
1824 m = repo.changectx(rev).manifest()
1819 files = m.keys()
1825 files = m.keys()
1820 files.sort()
1826 files.sort()
1821
1827
1822 for f in files:
1828 for f in files:
1823 if ui.debugflag:
1829 if ui.debugflag:
1824 ui.write("%40s " % hex(m[f]))
1830 ui.write("%40s " % hex(m[f]))
1825 if ui.verbose:
1831 if ui.verbose:
1826 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1832 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1827 ui.write("%s\n" % f)
1833 ui.write("%s\n" % f)
1828
1834
1829 def merge(ui, repo, node=None, force=None):
1835 def merge(ui, repo, node=None, force=None, rev=None):
1830 """merge working directory with another revision
1836 """merge working directory with another revision
1831
1837
1832 Merge the contents of the current working directory and the
1838 Merge the contents of the current working directory and the
1833 requested revision. Files that changed between either parent are
1839 requested revision. Files that changed between either parent are
1834 marked as changed for the next commit and a commit must be
1840 marked as changed for the next commit and a commit must be
1835 performed before any further updates are allowed.
1841 performed before any further updates are allowed.
1836
1842
1837 If no revision is specified, the working directory's parent is a
1843 If no revision is specified, the working directory's parent is a
1838 head revision, and the repository contains exactly one other head,
1844 head revision, and the repository contains exactly one other head,
1839 the other head is merged with by default. Otherwise, an explicit
1845 the other head is merged with by default. Otherwise, an explicit
1840 revision to merge with must be provided.
1846 revision to merge with must be provided.
1841 """
1847 """
1842
1848
1849 if rev and node:
1850 raise util.Abort(_("please specify just one revision"))
1851
1852 if not node:
1853 node = rev
1854
1843 if not node:
1855 if not node:
1844 heads = repo.heads()
1856 heads = repo.heads()
1845 if len(heads) > 2:
1857 if len(heads) > 2:
1846 raise util.Abort(_('repo has %d heads - '
1858 raise util.Abort(_('repo has %d heads - '
1847 'please merge with an explicit rev') %
1859 'please merge with an explicit rev') %
1848 len(heads))
1860 len(heads))
1849 if len(heads) == 1:
1861 if len(heads) == 1:
1850 raise util.Abort(_('there is nothing to merge - '
1862 raise util.Abort(_('there is nothing to merge - '
1851 'use "hg update" instead'))
1863 'use "hg update" instead'))
1852 parent = repo.dirstate.parents()[0]
1864 parent = repo.dirstate.parents()[0]
1853 if parent not in heads:
1865 if parent not in heads:
1854 raise util.Abort(_('working dir not at a head rev - '
1866 raise util.Abort(_('working dir not at a head rev - '
1855 'use "hg update" or merge with an explicit rev'))
1867 'use "hg update" or merge with an explicit rev'))
1856 node = parent == heads[0] and heads[-1] or heads[0]
1868 node = parent == heads[0] and heads[-1] or heads[0]
1857 return hg.merge(repo, node, force=force)
1869 return hg.merge(repo, node, force=force)
1858
1870
1859 def outgoing(ui, repo, dest=None, **opts):
1871 def outgoing(ui, repo, dest=None, **opts):
1860 """show changesets not found in destination
1872 """show changesets not found in destination
1861
1873
1862 Show changesets not found in the specified destination repository or
1874 Show changesets not found in the specified destination repository or
1863 the default push location. These are the changesets that would be pushed
1875 the default push location. These are the changesets that would be pushed
1864 if a push was requested.
1876 if a push was requested.
1865
1877
1866 See pull for valid destination format details.
1878 See pull for valid destination format details.
1867 """
1879 """
1868 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1880 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1869 setremoteconfig(ui, opts)
1881 setremoteconfig(ui, opts)
1870 revs = None
1882 revs = None
1871 if opts['rev']:
1883 if opts['rev']:
1872 revs = [repo.lookup(rev) for rev in opts['rev']]
1884 revs = [repo.lookup(rev) for rev in opts['rev']]
1873
1885
1874 other = hg.repository(ui, dest)
1886 other = hg.repository(ui, dest)
1875 ui.status(_('comparing with %s\n') % dest)
1887 ui.status(_('comparing with %s\n') % dest)
1876 o = repo.findoutgoing(other, force=opts['force'])
1888 o = repo.findoutgoing(other, force=opts['force'])
1877 if not o:
1889 if not o:
1878 ui.status(_("no changes found\n"))
1890 ui.status(_("no changes found\n"))
1879 return 1
1891 return 1
1880 o = repo.changelog.nodesbetween(o, revs)[0]
1892 o = repo.changelog.nodesbetween(o, revs)[0]
1881 if opts['newest_first']:
1893 if opts['newest_first']:
1882 o.reverse()
1894 o.reverse()
1883 displayer = cmdutil.show_changeset(ui, repo, opts)
1895 displayer = cmdutil.show_changeset(ui, repo, opts)
1884 for n in o:
1896 for n in o:
1885 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1897 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1886 if opts['no_merges'] and len(parents) == 2:
1898 if opts['no_merges'] and len(parents) == 2:
1887 continue
1899 continue
1888 displayer.show(changenode=n)
1900 displayer.show(changenode=n)
1889
1901
1890 def parents(ui, repo, file_=None, **opts):
1902 def parents(ui, repo, file_=None, **opts):
1891 """show the parents of the working dir or revision
1903 """show the parents of the working dir or revision
1892
1904
1893 Print the working directory's parent revisions.
1905 Print the working directory's parent revisions.
1894 """
1906 """
1895 rev = opts.get('rev')
1907 rev = opts.get('rev')
1896 if rev:
1908 if rev:
1897 if file_:
1909 if file_:
1898 ctx = repo.filectx(file_, changeid=rev)
1910 ctx = repo.filectx(file_, changeid=rev)
1899 else:
1911 else:
1900 ctx = repo.changectx(rev)
1912 ctx = repo.changectx(rev)
1901 p = [cp.node() for cp in ctx.parents()]
1913 p = [cp.node() for cp in ctx.parents()]
1902 else:
1914 else:
1903 p = repo.dirstate.parents()
1915 p = repo.dirstate.parents()
1904
1916
1905 displayer = cmdutil.show_changeset(ui, repo, opts)
1917 displayer = cmdutil.show_changeset(ui, repo, opts)
1906 for n in p:
1918 for n in p:
1907 if n != nullid:
1919 if n != nullid:
1908 displayer.show(changenode=n)
1920 displayer.show(changenode=n)
1909
1921
1910 def paths(ui, repo, search=None):
1922 def paths(ui, repo, search=None):
1911 """show definition of symbolic path names
1923 """show definition of symbolic path names
1912
1924
1913 Show definition of symbolic path name NAME. If no name is given, show
1925 Show definition of symbolic path name NAME. If no name is given, show
1914 definition of available names.
1926 definition of available names.
1915
1927
1916 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1928 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1917 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1929 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1918 """
1930 """
1919 if search:
1931 if search:
1920 for name, path in ui.configitems("paths"):
1932 for name, path in ui.configitems("paths"):
1921 if name == search:
1933 if name == search:
1922 ui.write("%s\n" % path)
1934 ui.write("%s\n" % path)
1923 return
1935 return
1924 ui.warn(_("not found!\n"))
1936 ui.warn(_("not found!\n"))
1925 return 1
1937 return 1
1926 else:
1938 else:
1927 for name, path in ui.configitems("paths"):
1939 for name, path in ui.configitems("paths"):
1928 ui.write("%s = %s\n" % (name, path))
1940 ui.write("%s = %s\n" % (name, path))
1929
1941
1930 def postincoming(ui, repo, modheads, optupdate):
1942 def postincoming(ui, repo, modheads, optupdate):
1931 if modheads == 0:
1943 if modheads == 0:
1932 return
1944 return
1933 if optupdate:
1945 if optupdate:
1934 if modheads == 1:
1946 if modheads == 1:
1935 return hg.update(repo, repo.changelog.tip()) # update
1947 return hg.update(repo, repo.changelog.tip()) # update
1936 else:
1948 else:
1937 ui.status(_("not updating, since new heads added\n"))
1949 ui.status(_("not updating, since new heads added\n"))
1938 if modheads > 1:
1950 if modheads > 1:
1939 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1951 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1940 else:
1952 else:
1941 ui.status(_("(run 'hg update' to get a working copy)\n"))
1953 ui.status(_("(run 'hg update' to get a working copy)\n"))
1942
1954
1943 def pull(ui, repo, source="default", **opts):
1955 def pull(ui, repo, source="default", **opts):
1944 """pull changes from the specified source
1956 """pull changes from the specified source
1945
1957
1946 Pull changes from a remote repository to a local one.
1958 Pull changes from a remote repository to a local one.
1947
1959
1948 This finds all changes from the repository at the specified path
1960 This finds all changes from the repository at the specified path
1949 or URL and adds them to the local repository. By default, this
1961 or URL and adds them to the local repository. By default, this
1950 does not update the copy of the project in the working directory.
1962 does not update the copy of the project in the working directory.
1951
1963
1952 Valid URLs are of the form:
1964 Valid URLs are of the form:
1953
1965
1954 local/filesystem/path (or file://local/filesystem/path)
1966 local/filesystem/path (or file://local/filesystem/path)
1955 http://[user@]host[:port]/[path]
1967 http://[user@]host[:port]/[path]
1956 https://[user@]host[:port]/[path]
1968 https://[user@]host[:port]/[path]
1957 ssh://[user@]host[:port]/[path]
1969 ssh://[user@]host[:port]/[path]
1958 static-http://host[:port]/[path]
1970 static-http://host[:port]/[path]
1959
1971
1960 Paths in the local filesystem can either point to Mercurial
1972 Paths in the local filesystem can either point to Mercurial
1961 repositories or to bundle files (as created by 'hg bundle' or
1973 repositories or to bundle files (as created by 'hg bundle' or
1962 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1974 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1963 allows access to a Mercurial repository where you simply use a web
1975 allows access to a Mercurial repository where you simply use a web
1964 server to publish the .hg directory as static content.
1976 server to publish the .hg directory as static content.
1965
1977
1966 Some notes about using SSH with Mercurial:
1978 Some notes about using SSH with Mercurial:
1967 - SSH requires an accessible shell account on the destination machine
1979 - SSH requires an accessible shell account on the destination machine
1968 and a copy of hg in the remote path or specified with as remotecmd.
1980 and a copy of hg in the remote path or specified with as remotecmd.
1969 - path is relative to the remote user's home directory by default.
1981 - path is relative to the remote user's home directory by default.
1970 Use an extra slash at the start of a path to specify an absolute path:
1982 Use an extra slash at the start of a path to specify an absolute path:
1971 ssh://example.com//tmp/repository
1983 ssh://example.com//tmp/repository
1972 - Mercurial doesn't use its own compression via SSH; the right thing
1984 - Mercurial doesn't use its own compression via SSH; the right thing
1973 to do is to configure it in your ~/.ssh/config, e.g.:
1985 to do is to configure it in your ~/.ssh/config, e.g.:
1974 Host *.mylocalnetwork.example.com
1986 Host *.mylocalnetwork.example.com
1975 Compression no
1987 Compression no
1976 Host *
1988 Host *
1977 Compression yes
1989 Compression yes
1978 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1990 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1979 with the --ssh command line option.
1991 with the --ssh command line option.
1980 """
1992 """
1981 source = ui.expandpath(source)
1993 source = ui.expandpath(source)
1982 setremoteconfig(ui, opts)
1994 setremoteconfig(ui, opts)
1983
1995
1984 other = hg.repository(ui, source)
1996 other = hg.repository(ui, source)
1985 ui.status(_('pulling from %s\n') % (source))
1997 ui.status(_('pulling from %s\n') % (source))
1986 revs = None
1998 revs = None
1987 if opts['rev']:
1999 if opts['rev']:
1988 if 'lookup' in other.capabilities:
2000 if 'lookup' in other.capabilities:
1989 revs = [other.lookup(rev) for rev in opts['rev']]
2001 revs = [other.lookup(rev) for rev in opts['rev']]
1990 else:
2002 else:
1991 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2003 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1992 raise util.Abort(error)
2004 raise util.Abort(error)
1993 modheads = repo.pull(other, heads=revs, force=opts['force'])
2005 modheads = repo.pull(other, heads=revs, force=opts['force'])
1994 return postincoming(ui, repo, modheads, opts['update'])
2006 return postincoming(ui, repo, modheads, opts['update'])
1995
2007
1996 def push(ui, repo, dest=None, **opts):
2008 def push(ui, repo, dest=None, **opts):
1997 """push changes to the specified destination
2009 """push changes to the specified destination
1998
2010
1999 Push changes from the local repository to the given destination.
2011 Push changes from the local repository to the given destination.
2000
2012
2001 This is the symmetrical operation for pull. It helps to move
2013 This is the symmetrical operation for pull. It helps to move
2002 changes from the current repository to a different one. If the
2014 changes from the current repository to a different one. If the
2003 destination is local this is identical to a pull in that directory
2015 destination is local this is identical to a pull in that directory
2004 from the current one.
2016 from the current one.
2005
2017
2006 By default, push will refuse to run if it detects the result would
2018 By default, push will refuse to run if it detects the result would
2007 increase the number of remote heads. This generally indicates the
2019 increase the number of remote heads. This generally indicates the
2008 the client has forgotten to sync and merge before pushing.
2020 the client has forgotten to sync and merge before pushing.
2009
2021
2010 Valid URLs are of the form:
2022 Valid URLs are of the form:
2011
2023
2012 local/filesystem/path (or file://local/filesystem/path)
2024 local/filesystem/path (or file://local/filesystem/path)
2013 ssh://[user@]host[:port]/[path]
2025 ssh://[user@]host[:port]/[path]
2014 http://[user@]host[:port]/[path]
2026 http://[user@]host[:port]/[path]
2015 https://[user@]host[:port]/[path]
2027 https://[user@]host[:port]/[path]
2016
2028
2017 Look at the help text for the pull command for important details
2029 Look at the help text for the pull command for important details
2018 about ssh:// URLs.
2030 about ssh:// URLs.
2019
2031
2020 Pushing to http:// and https:// URLs is only possible, if this
2032 Pushing to http:// and https:// URLs is only possible, if this
2021 feature is explicitly enabled on the remote Mercurial server.
2033 feature is explicitly enabled on the remote Mercurial server.
2022 """
2034 """
2023 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2035 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2024 setremoteconfig(ui, opts)
2036 setremoteconfig(ui, opts)
2025
2037
2026 other = hg.repository(ui, dest)
2038 other = hg.repository(ui, dest)
2027 ui.status('pushing to %s\n' % (dest))
2039 ui.status('pushing to %s\n' % (dest))
2028 revs = None
2040 revs = None
2029 if opts['rev']:
2041 if opts['rev']:
2030 revs = [repo.lookup(rev) for rev in opts['rev']]
2042 revs = [repo.lookup(rev) for rev in opts['rev']]
2031 r = repo.push(other, opts['force'], revs=revs)
2043 r = repo.push(other, opts['force'], revs=revs)
2032 return r == 0
2044 return r == 0
2033
2045
2034 def rawcommit(ui, repo, *pats, **opts):
2046 def rawcommit(ui, repo, *pats, **opts):
2035 """raw commit interface (DEPRECATED)
2047 """raw commit interface (DEPRECATED)
2036
2048
2037 (DEPRECATED)
2049 (DEPRECATED)
2038 Lowlevel commit, for use in helper scripts.
2050 Lowlevel commit, for use in helper scripts.
2039
2051
2040 This command is not intended to be used by normal users, as it is
2052 This command is not intended to be used by normal users, as it is
2041 primarily useful for importing from other SCMs.
2053 primarily useful for importing from other SCMs.
2042
2054
2043 This command is now deprecated and will be removed in a future
2055 This command is now deprecated and will be removed in a future
2044 release, please use debugsetparents and commit instead.
2056 release, please use debugsetparents and commit instead.
2045 """
2057 """
2046
2058
2047 ui.warn(_("(the rawcommit command is deprecated)\n"))
2059 ui.warn(_("(the rawcommit command is deprecated)\n"))
2048
2060
2049 message = logmessage(opts)
2061 message = logmessage(opts)
2050
2062
2051 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2063 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2052 if opts['files']:
2064 if opts['files']:
2053 files += open(opts['files']).read().splitlines()
2065 files += open(opts['files']).read().splitlines()
2054
2066
2055 parents = [repo.lookup(p) for p in opts['parent']]
2067 parents = [repo.lookup(p) for p in opts['parent']]
2056
2068
2057 try:
2069 try:
2058 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2070 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2059 except ValueError, inst:
2071 except ValueError, inst:
2060 raise util.Abort(str(inst))
2072 raise util.Abort(str(inst))
2061
2073
2062 def recover(ui, repo):
2074 def recover(ui, repo):
2063 """roll back an interrupted transaction
2075 """roll back an interrupted transaction
2064
2076
2065 Recover from an interrupted commit or pull.
2077 Recover from an interrupted commit or pull.
2066
2078
2067 This command tries to fix the repository status after an interrupted
2079 This command tries to fix the repository status after an interrupted
2068 operation. It should only be necessary when Mercurial suggests it.
2080 operation. It should only be necessary when Mercurial suggests it.
2069 """
2081 """
2070 if repo.recover():
2082 if repo.recover():
2071 return hg.verify(repo)
2083 return hg.verify(repo)
2072 return 1
2084 return 1
2073
2085
2074 def remove(ui, repo, *pats, **opts):
2086 def remove(ui, repo, *pats, **opts):
2075 """remove the specified files on the next commit
2087 """remove the specified files on the next commit
2076
2088
2077 Schedule the indicated files for removal from the repository.
2089 Schedule the indicated files for removal from the repository.
2078
2090
2079 This only removes files from the current branch, not from the
2091 This only removes files from the current branch, not from the
2080 entire project history. If the files still exist in the working
2092 entire project history. If the files still exist in the working
2081 directory, they will be deleted from it. If invoked with --after,
2093 directory, they will be deleted from it. If invoked with --after,
2082 files are marked as removed, but not actually unlinked unless --force
2094 files are marked as removed, but not actually unlinked unless --force
2083 is also given. Without exact file names, --after will only mark
2095 is also given. Without exact file names, --after will only mark
2084 files as removed if they are no longer in the working directory.
2096 files as removed if they are no longer in the working directory.
2085
2097
2086 This command schedules the files to be removed at the next commit.
2098 This command schedules the files to be removed at the next commit.
2087 To undo a remove before that, see hg revert.
2099 To undo a remove before that, see hg revert.
2088
2100
2089 Modified files and added files are not removed by default. To
2101 Modified files and added files are not removed by default. To
2090 remove them, use the -f/--force option.
2102 remove them, use the -f/--force option.
2091 """
2103 """
2092 names = []
2104 names = []
2093 if not opts['after'] and not pats:
2105 if not opts['after'] and not pats:
2094 raise util.Abort(_('no files specified'))
2106 raise util.Abort(_('no files specified'))
2095 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2107 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2096 exact = dict.fromkeys(files)
2108 exact = dict.fromkeys(files)
2097 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2109 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2098 modified, added, removed, deleted, unknown = mardu
2110 modified, added, removed, deleted, unknown = mardu
2099 remove, forget = [], []
2111 remove, forget = [], []
2100 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2112 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2101 reason = None
2113 reason = None
2102 if abs in modified and not opts['force']:
2114 if abs in modified and not opts['force']:
2103 reason = _('is modified (use -f to force removal)')
2115 reason = _('is modified (use -f to force removal)')
2104 elif abs in added:
2116 elif abs in added:
2105 if opts['force']:
2117 if opts['force']:
2106 forget.append(abs)
2118 forget.append(abs)
2107 continue
2119 continue
2108 reason = _('has been marked for add (use -f to force removal)')
2120 reason = _('has been marked for add (use -f to force removal)')
2109 elif abs in unknown:
2121 elif abs in unknown:
2110 reason = _('is not managed')
2122 reason = _('is not managed')
2111 elif opts['after'] and not exact and abs not in deleted:
2123 elif opts['after'] and not exact and abs not in deleted:
2112 continue
2124 continue
2113 elif abs in removed:
2125 elif abs in removed:
2114 continue
2126 continue
2115 if reason:
2127 if reason:
2116 if exact:
2128 if exact:
2117 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2129 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2118 else:
2130 else:
2119 if ui.verbose or not exact:
2131 if ui.verbose or not exact:
2120 ui.status(_('removing %s\n') % rel)
2132 ui.status(_('removing %s\n') % rel)
2121 remove.append(abs)
2133 remove.append(abs)
2122 repo.forget(forget)
2134 repo.forget(forget)
2123 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2135 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2124
2136
2125 def rename(ui, repo, *pats, **opts):
2137 def rename(ui, repo, *pats, **opts):
2126 """rename files; equivalent of copy + remove
2138 """rename files; equivalent of copy + remove
2127
2139
2128 Mark dest as copies of sources; mark sources for deletion. If
2140 Mark dest as copies of sources; mark sources for deletion. If
2129 dest is a directory, copies are put in that directory. If dest is
2141 dest is a directory, copies are put in that directory. If dest is
2130 a file, there can only be one source.
2142 a file, there can only be one source.
2131
2143
2132 By default, this command copies the contents of files as they
2144 By default, this command copies the contents of files as they
2133 stand in the working directory. If invoked with --after, the
2145 stand in the working directory. If invoked with --after, the
2134 operation is recorded, but no copying is performed.
2146 operation is recorded, but no copying is performed.
2135
2147
2136 This command takes effect in the next commit. To undo a rename
2148 This command takes effect in the next commit. To undo a rename
2137 before that, see hg revert.
2149 before that, see hg revert.
2138 """
2150 """
2139 wlock = repo.wlock(0)
2151 wlock = repo.wlock(0)
2140 errs, copied = docopy(ui, repo, pats, opts, wlock)
2152 errs, copied = docopy(ui, repo, pats, opts, wlock)
2141 names = []
2153 names = []
2142 for abs, rel, exact in copied:
2154 for abs, rel, exact in copied:
2143 if ui.verbose or not exact:
2155 if ui.verbose or not exact:
2144 ui.status(_('removing %s\n') % rel)
2156 ui.status(_('removing %s\n') % rel)
2145 names.append(abs)
2157 names.append(abs)
2146 if not opts.get('dry_run'):
2158 if not opts.get('dry_run'):
2147 repo.remove(names, True, wlock=wlock)
2159 repo.remove(names, True, wlock=wlock)
2148 return errs
2160 return errs
2149
2161
2150 def revert(ui, repo, *pats, **opts):
2162 def revert(ui, repo, *pats, **opts):
2151 """revert files or dirs to their states as of some revision
2163 """revert files or dirs to their states as of some revision
2152
2164
2153 With no revision specified, revert the named files or directories
2165 With no revision specified, revert the named files or directories
2154 to the contents they had in the parent of the working directory.
2166 to the contents they had in the parent of the working directory.
2155 This restores the contents of the affected files to an unmodified
2167 This restores the contents of the affected files to an unmodified
2156 state and unschedules adds, removes, copies, and renames. If the
2168 state and unschedules adds, removes, copies, and renames. If the
2157 working directory has two parents, you must explicitly specify the
2169 working directory has two parents, you must explicitly specify the
2158 revision to revert to.
2170 revision to revert to.
2159
2171
2160 Modified files are saved with a .orig suffix before reverting.
2172 Modified files are saved with a .orig suffix before reverting.
2161 To disable these backups, use --no-backup.
2173 To disable these backups, use --no-backup.
2162
2174
2163 Using the -r option, revert the given files or directories to their
2175 Using the -r option, revert the given files or directories to their
2164 contents as of a specific revision. This can be helpful to "roll
2176 contents as of a specific revision. This can be helpful to "roll
2165 back" some or all of a change that should not have been committed.
2177 back" some or all of a change that should not have been committed.
2166
2178
2167 Revert modifies the working directory. It does not commit any
2179 Revert modifies the working directory. It does not commit any
2168 changes, or change the parent of the working directory. If you
2180 changes, or change the parent of the working directory. If you
2169 revert to a revision other than the parent of the working
2181 revert to a revision other than the parent of the working
2170 directory, the reverted files will thus appear modified
2182 directory, the reverted files will thus appear modified
2171 afterwards.
2183 afterwards.
2172
2184
2173 If a file has been deleted, it is recreated. If the executable
2185 If a file has been deleted, it is recreated. If the executable
2174 mode of a file was changed, it is reset.
2186 mode of a file was changed, it is reset.
2175
2187
2176 If names are given, all files matching the names are reverted.
2188 If names are given, all files matching the names are reverted.
2177
2189
2178 If no arguments are given, no files are reverted.
2190 If no arguments are given, no files are reverted.
2179 """
2191 """
2180
2192
2181 if opts["date"]:
2193 if opts["date"]:
2182 if opts["rev"]:
2194 if opts["rev"]:
2183 raise util.Abort(_("you can't specify a revision and a date"))
2195 raise util.Abort(_("you can't specify a revision and a date"))
2184 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2196 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2185
2197
2186 if not pats and not opts['all']:
2198 if not pats and not opts['all']:
2187 raise util.Abort(_('no files or directories specified; '
2199 raise util.Abort(_('no files or directories specified; '
2188 'use --all to revert the whole repo'))
2200 'use --all to revert the whole repo'))
2189
2201
2190 parent, p2 = repo.dirstate.parents()
2202 parent, p2 = repo.dirstate.parents()
2191 if not opts['rev'] and p2 != nullid:
2203 if not opts['rev'] and p2 != nullid:
2192 raise util.Abort(_('uncommitted merge - please provide a '
2204 raise util.Abort(_('uncommitted merge - please provide a '
2193 'specific revision'))
2205 'specific revision'))
2194 ctx = repo.changectx(opts['rev'])
2206 ctx = repo.changectx(opts['rev'])
2195 node = ctx.node()
2207 node = ctx.node()
2196 mf = ctx.manifest()
2208 mf = ctx.manifest()
2197 if node == parent:
2209 if node == parent:
2198 pmf = mf
2210 pmf = mf
2199 else:
2211 else:
2200 pmf = None
2212 pmf = None
2201
2213
2202 wlock = repo.wlock()
2214 wlock = repo.wlock()
2203
2215
2204 # need all matching names in dirstate and manifest of target rev,
2216 # need all matching names in dirstate and manifest of target rev,
2205 # so have to walk both. do not print errors if files exist in one
2217 # so have to walk both. do not print errors if files exist in one
2206 # but not other.
2218 # but not other.
2207
2219
2208 names = {}
2220 names = {}
2209 target_only = {}
2221 target_only = {}
2210
2222
2211 # walk dirstate.
2223 # walk dirstate.
2212
2224
2213 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2225 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2214 badmatch=mf.has_key):
2226 badmatch=mf.has_key):
2215 names[abs] = (rel, exact)
2227 names[abs] = (rel, exact)
2216 if src == 'b':
2228 if src == 'b':
2217 target_only[abs] = True
2229 target_only[abs] = True
2218
2230
2219 # walk target manifest.
2231 # walk target manifest.
2220
2232
2221 def badmatch(path):
2233 def badmatch(path):
2222 if path in names:
2234 if path in names:
2223 return True
2235 return True
2224 path_ = path + '/'
2236 path_ = path + '/'
2225 for f in names:
2237 for f in names:
2226 if f.startswith(path_):
2238 if f.startswith(path_):
2227 return True
2239 return True
2228 return False
2240 return False
2229
2241
2230 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2242 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2231 badmatch=badmatch):
2243 badmatch=badmatch):
2232 if abs in names or src == 'b':
2244 if abs in names or src == 'b':
2233 continue
2245 continue
2234 names[abs] = (rel, exact)
2246 names[abs] = (rel, exact)
2235 target_only[abs] = True
2247 target_only[abs] = True
2236
2248
2237 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2249 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2238 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2250 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2239
2251
2240 revert = ([], _('reverting %s\n'))
2252 revert = ([], _('reverting %s\n'))
2241 add = ([], _('adding %s\n'))
2253 add = ([], _('adding %s\n'))
2242 remove = ([], _('removing %s\n'))
2254 remove = ([], _('removing %s\n'))
2243 forget = ([], _('forgetting %s\n'))
2255 forget = ([], _('forgetting %s\n'))
2244 undelete = ([], _('undeleting %s\n'))
2256 undelete = ([], _('undeleting %s\n'))
2245 update = {}
2257 update = {}
2246
2258
2247 disptable = (
2259 disptable = (
2248 # dispatch table:
2260 # dispatch table:
2249 # file state
2261 # file state
2250 # action if in target manifest
2262 # action if in target manifest
2251 # action if not in target manifest
2263 # action if not in target manifest
2252 # make backup if in target manifest
2264 # make backup if in target manifest
2253 # make backup if not in target manifest
2265 # make backup if not in target manifest
2254 (modified, revert, remove, True, True),
2266 (modified, revert, remove, True, True),
2255 (added, revert, forget, True, False),
2267 (added, revert, forget, True, False),
2256 (removed, undelete, None, False, False),
2268 (removed, undelete, None, False, False),
2257 (deleted, revert, remove, False, False),
2269 (deleted, revert, remove, False, False),
2258 (unknown, add, None, True, False),
2270 (unknown, add, None, True, False),
2259 (target_only, add, None, False, False),
2271 (target_only, add, None, False, False),
2260 )
2272 )
2261
2273
2262 entries = names.items()
2274 entries = names.items()
2263 entries.sort()
2275 entries.sort()
2264
2276
2265 for abs, (rel, exact) in entries:
2277 for abs, (rel, exact) in entries:
2266 mfentry = mf.get(abs)
2278 mfentry = mf.get(abs)
2267 def handle(xlist, dobackup):
2279 def handle(xlist, dobackup):
2268 xlist[0].append(abs)
2280 xlist[0].append(abs)
2269 update[abs] = 1
2281 update[abs] = 1
2270 if (dobackup and not opts['no_backup'] and
2282 if (dobackup and not opts['no_backup'] and
2271 (os.path.islink(rel) or os.path.exists(rel))):
2283 (os.path.islink(rel) or os.path.exists(rel))):
2272 bakname = "%s.orig" % rel
2284 bakname = "%s.orig" % rel
2273 ui.note(_('saving current version of %s as %s\n') %
2285 ui.note(_('saving current version of %s as %s\n') %
2274 (rel, bakname))
2286 (rel, bakname))
2275 if not opts.get('dry_run'):
2287 if not opts.get('dry_run'):
2276 util.copyfile(rel, bakname)
2288 util.copyfile(rel, bakname)
2277 if ui.verbose or not exact:
2289 if ui.verbose or not exact:
2278 ui.status(xlist[1] % rel)
2290 ui.status(xlist[1] % rel)
2279 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2291 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2280 if abs not in table: continue
2292 if abs not in table: continue
2281 # file has changed in dirstate
2293 # file has changed in dirstate
2282 if mfentry:
2294 if mfentry:
2283 handle(hitlist, backuphit)
2295 handle(hitlist, backuphit)
2284 elif misslist is not None:
2296 elif misslist is not None:
2285 handle(misslist, backupmiss)
2297 handle(misslist, backupmiss)
2286 else:
2298 else:
2287 if exact: ui.warn(_('file not managed: %s\n') % rel)
2299 if exact: ui.warn(_('file not managed: %s\n') % rel)
2288 break
2300 break
2289 else:
2301 else:
2290 # file has not changed in dirstate
2302 # file has not changed in dirstate
2291 if node == parent:
2303 if node == parent:
2292 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2304 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2293 continue
2305 continue
2294 if pmf is None:
2306 if pmf is None:
2295 # only need parent manifest in this unlikely case,
2307 # only need parent manifest in this unlikely case,
2296 # so do not read by default
2308 # so do not read by default
2297 pmf = repo.changectx(parent).manifest()
2309 pmf = repo.changectx(parent).manifest()
2298 if abs in pmf:
2310 if abs in pmf:
2299 if mfentry:
2311 if mfentry:
2300 # if version of file is same in parent and target
2312 # if version of file is same in parent and target
2301 # manifests, do nothing
2313 # manifests, do nothing
2302 if pmf[abs] != mfentry:
2314 if pmf[abs] != mfentry:
2303 handle(revert, False)
2315 handle(revert, False)
2304 else:
2316 else:
2305 handle(remove, False)
2317 handle(remove, False)
2306
2318
2307 if not opts.get('dry_run'):
2319 if not opts.get('dry_run'):
2308 repo.dirstate.forget(forget[0])
2320 repo.dirstate.forget(forget[0])
2309 r = hg.revert(repo, node, update.has_key, wlock)
2321 r = hg.revert(repo, node, update.has_key, wlock)
2310 repo.dirstate.update(add[0], 'a')
2322 repo.dirstate.update(add[0], 'a')
2311 repo.dirstate.update(undelete[0], 'n')
2323 repo.dirstate.update(undelete[0], 'n')
2312 repo.dirstate.update(remove[0], 'r')
2324 repo.dirstate.update(remove[0], 'r')
2313 return r
2325 return r
2314
2326
2315 def rollback(ui, repo):
2327 def rollback(ui, repo):
2316 """roll back the last transaction in this repository
2328 """roll back the last transaction in this repository
2317
2329
2318 Roll back the last transaction in this repository, restoring the
2330 Roll back the last transaction in this repository, restoring the
2319 project to its state prior to the transaction.
2331 project to its state prior to the transaction.
2320
2332
2321 Transactions are used to encapsulate the effects of all commands
2333 Transactions are used to encapsulate the effects of all commands
2322 that create new changesets or propagate existing changesets into a
2334 that create new changesets or propagate existing changesets into a
2323 repository. For example, the following commands are transactional,
2335 repository. For example, the following commands are transactional,
2324 and their effects can be rolled back:
2336 and their effects can be rolled back:
2325
2337
2326 commit
2338 commit
2327 import
2339 import
2328 pull
2340 pull
2329 push (with this repository as destination)
2341 push (with this repository as destination)
2330 unbundle
2342 unbundle
2331
2343
2332 This command should be used with care. There is only one level of
2344 This command should be used with care. There is only one level of
2333 rollback, and there is no way to undo a rollback.
2345 rollback, and there is no way to undo a rollback.
2334
2346
2335 This command is not intended for use on public repositories. Once
2347 This command is not intended for use on public repositories. Once
2336 changes are visible for pull by other users, rolling a transaction
2348 changes are visible for pull by other users, rolling a transaction
2337 back locally is ineffective (someone else may already have pulled
2349 back locally is ineffective (someone else may already have pulled
2338 the changes). Furthermore, a race is possible with readers of the
2350 the changes). Furthermore, a race is possible with readers of the
2339 repository; for example an in-progress pull from the repository
2351 repository; for example an in-progress pull from the repository
2340 may fail if a rollback is performed.
2352 may fail if a rollback is performed.
2341 """
2353 """
2342 repo.rollback()
2354 repo.rollback()
2343
2355
2344 def root(ui, repo):
2356 def root(ui, repo):
2345 """print the root (top) of the current working dir
2357 """print the root (top) of the current working dir
2346
2358
2347 Print the root directory of the current repository.
2359 Print the root directory of the current repository.
2348 """
2360 """
2349 ui.write(repo.root + "\n")
2361 ui.write(repo.root + "\n")
2350
2362
2351 def serve(ui, repo, **opts):
2363 def serve(ui, repo, **opts):
2352 """export the repository via HTTP
2364 """export the repository via HTTP
2353
2365
2354 Start a local HTTP repository browser and pull server.
2366 Start a local HTTP repository browser and pull server.
2355
2367
2356 By default, the server logs accesses to stdout and errors to
2368 By default, the server logs accesses to stdout and errors to
2357 stderr. Use the "-A" and "-E" options to log to files.
2369 stderr. Use the "-A" and "-E" options to log to files.
2358 """
2370 """
2359
2371
2360 if opts["stdio"]:
2372 if opts["stdio"]:
2361 if repo is None:
2373 if repo is None:
2362 raise hg.RepoError(_("There is no Mercurial repository here"
2374 raise hg.RepoError(_("There is no Mercurial repository here"
2363 " (.hg not found)"))
2375 " (.hg not found)"))
2364 s = sshserver.sshserver(ui, repo)
2376 s = sshserver.sshserver(ui, repo)
2365 s.serve_forever()
2377 s.serve_forever()
2366
2378
2367 parentui = ui.parentui or ui
2379 parentui = ui.parentui or ui
2368 optlist = ("name templates style address port ipv6"
2380 optlist = ("name templates style address port ipv6"
2369 " accesslog errorlog webdir_conf")
2381 " accesslog errorlog webdir_conf")
2370 for o in optlist.split():
2382 for o in optlist.split():
2371 if opts[o]:
2383 if opts[o]:
2372 parentui.setconfig("web", o, str(opts[o]))
2384 parentui.setconfig("web", o, str(opts[o]))
2373
2385
2374 if repo is None and not ui.config("web", "webdir_conf"):
2386 if repo is None and not ui.config("web", "webdir_conf"):
2375 raise hg.RepoError(_("There is no Mercurial repository here"
2387 raise hg.RepoError(_("There is no Mercurial repository here"
2376 " (.hg not found)"))
2388 " (.hg not found)"))
2377
2389
2378 class service:
2390 class service:
2379 def init(self):
2391 def init(self):
2380 try:
2392 try:
2381 self.httpd = hgweb.server.create_server(parentui, repo)
2393 self.httpd = hgweb.server.create_server(parentui, repo)
2382 except socket.error, inst:
2394 except socket.error, inst:
2383 raise util.Abort(_('cannot start server: ') + inst.args[1])
2395 raise util.Abort(_('cannot start server: ') + inst.args[1])
2384
2396
2385 if not ui.verbose: return
2397 if not ui.verbose: return
2386
2398
2387 if httpd.port != 80:
2399 if httpd.port != 80:
2388 ui.status(_('listening at http://%s:%d/\n') %
2400 ui.status(_('listening at http://%s:%d/\n') %
2389 (httpd.addr, httpd.port))
2401 (httpd.addr, httpd.port))
2390 else:
2402 else:
2391 ui.status(_('listening at http://%s/\n') % httpd.addr)
2403 ui.status(_('listening at http://%s/\n') % httpd.addr)
2392
2404
2393 def run(self):
2405 def run(self):
2394 self.httpd.serve_forever()
2406 self.httpd.serve_forever()
2395
2407
2396 service = service()
2408 service = service()
2397
2409
2398 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2410 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2399
2411
2400 def status(ui, repo, *pats, **opts):
2412 def status(ui, repo, *pats, **opts):
2401 """show changed files in the working directory
2413 """show changed files in the working directory
2402
2414
2403 Show status of files in the repository. If names are given, only
2415 Show status of files in the repository. If names are given, only
2404 files that match are shown. Files that are clean or ignored, are
2416 files that match are shown. Files that are clean or ignored, are
2405 not listed unless -c (clean), -i (ignored) or -A is given.
2417 not listed unless -c (clean), -i (ignored) or -A is given.
2406
2418
2407 NOTE: status may appear to disagree with diff if permissions have
2419 NOTE: status may appear to disagree with diff if permissions have
2408 changed or a merge has occurred. The standard diff format does not
2420 changed or a merge has occurred. The standard diff format does not
2409 report permission changes and diff only reports changes relative
2421 report permission changes and diff only reports changes relative
2410 to one merge parent.
2422 to one merge parent.
2411
2423
2412 If one revision is given, it is used as the base revision.
2424 If one revision is given, it is used as the base revision.
2413 If two revisions are given, the difference between them is shown.
2425 If two revisions are given, the difference between them is shown.
2414
2426
2415 The codes used to show the status of files are:
2427 The codes used to show the status of files are:
2416 M = modified
2428 M = modified
2417 A = added
2429 A = added
2418 R = removed
2430 R = removed
2419 C = clean
2431 C = clean
2420 ! = deleted, but still tracked
2432 ! = deleted, but still tracked
2421 ? = not tracked
2433 ? = not tracked
2422 I = ignored (not shown by default)
2434 I = ignored (not shown by default)
2423 = the previous added file was copied from here
2435 = the previous added file was copied from here
2424 """
2436 """
2425
2437
2426 all = opts['all']
2438 all = opts['all']
2427 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2439 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2428
2440
2429 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2441 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2430 cwd = (pats and repo.getcwd()) or ''
2442 cwd = (pats and repo.getcwd()) or ''
2431 modified, added, removed, deleted, unknown, ignored, clean = [
2443 modified, added, removed, deleted, unknown, ignored, clean = [
2432 n for n in repo.status(node1=node1, node2=node2, files=files,
2444 n for n in repo.status(node1=node1, node2=node2, files=files,
2433 match=matchfn,
2445 match=matchfn,
2434 list_ignored=all or opts['ignored'],
2446 list_ignored=all or opts['ignored'],
2435 list_clean=all or opts['clean'])]
2447 list_clean=all or opts['clean'])]
2436
2448
2437 changetypes = (('modified', 'M', modified),
2449 changetypes = (('modified', 'M', modified),
2438 ('added', 'A', added),
2450 ('added', 'A', added),
2439 ('removed', 'R', removed),
2451 ('removed', 'R', removed),
2440 ('deleted', '!', deleted),
2452 ('deleted', '!', deleted),
2441 ('unknown', '?', unknown),
2453 ('unknown', '?', unknown),
2442 ('ignored', 'I', ignored))
2454 ('ignored', 'I', ignored))
2443
2455
2444 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2456 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2445
2457
2446 end = opts['print0'] and '\0' or '\n'
2458 end = opts['print0'] and '\0' or '\n'
2447
2459
2448 for opt, char, changes in ([ct for ct in explicit_changetypes
2460 for opt, char, changes in ([ct for ct in explicit_changetypes
2449 if all or opts[ct[0]]]
2461 if all or opts[ct[0]]]
2450 or changetypes):
2462 or changetypes):
2451 if opts['no_status']:
2463 if opts['no_status']:
2452 format = "%%s%s" % end
2464 format = "%%s%s" % end
2453 else:
2465 else:
2454 format = "%s %%s%s" % (char, end)
2466 format = "%s %%s%s" % (char, end)
2455
2467
2456 for f in changes:
2468 for f in changes:
2457 ui.write(format % util.pathto(repo.root, cwd, f))
2469 ui.write(format % util.pathto(repo.root, cwd, f))
2458 if ((all or opts.get('copies')) and not opts.get('no_status')):
2470 if ((all or opts.get('copies')) and not opts.get('no_status')):
2459 copied = repo.dirstate.copied(f)
2471 copied = repo.dirstate.copied(f)
2460 if copied:
2472 if copied:
2461 ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied),
2473 ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied),
2462 end))
2474 end))
2463
2475
2464 def tag(ui, repo, name, rev_=None, **opts):
2476 def tag(ui, repo, name, rev_=None, **opts):
2465 """add a tag for the current or given revision
2477 """add a tag for the current or given revision
2466
2478
2467 Name a particular revision using <name>.
2479 Name a particular revision using <name>.
2468
2480
2469 Tags are used to name particular revisions of the repository and are
2481 Tags are used to name particular revisions of the repository and are
2470 very useful to compare different revision, to go back to significant
2482 very useful to compare different revision, to go back to significant
2471 earlier versions or to mark branch points as releases, etc.
2483 earlier versions or to mark branch points as releases, etc.
2472
2484
2473 If no revision is given, the parent of the working directory is used,
2485 If no revision is given, the parent of the working directory is used,
2474 or tip if no revision is checked out.
2486 or tip if no revision is checked out.
2475
2487
2476 To facilitate version control, distribution, and merging of tags,
2488 To facilitate version control, distribution, and merging of tags,
2477 they are stored as a file named ".hgtags" which is managed
2489 they are stored as a file named ".hgtags" which is managed
2478 similarly to other project files and can be hand-edited if
2490 similarly to other project files and can be hand-edited if
2479 necessary. The file '.hg/localtags' is used for local tags (not
2491 necessary. The file '.hg/localtags' is used for local tags (not
2480 shared among repositories).
2492 shared among repositories).
2481 """
2493 """
2482 if name in ['tip', '.', 'null']:
2494 if name in ['tip', '.', 'null']:
2483 raise util.Abort(_("the name '%s' is reserved") % name)
2495 raise util.Abort(_("the name '%s' is reserved") % name)
2484 if rev_ is not None:
2496 if rev_ is not None:
2485 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2497 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2486 "please use 'hg tag [-r REV] NAME' instead\n"))
2498 "please use 'hg tag [-r REV] NAME' instead\n"))
2487 if opts['rev']:
2499 if opts['rev']:
2488 raise util.Abort(_("use only one form to specify the revision"))
2500 raise util.Abort(_("use only one form to specify the revision"))
2489 if opts['rev'] and opts['remove']:
2501 if opts['rev'] and opts['remove']:
2490 raise util.Abort(_("--rev and --remove are incompatible"))
2502 raise util.Abort(_("--rev and --remove are incompatible"))
2491 if opts['rev']:
2503 if opts['rev']:
2492 rev_ = opts['rev']
2504 rev_ = opts['rev']
2493 message = opts['message']
2505 message = opts['message']
2494 if opts['remove']:
2506 if opts['remove']:
2495 rev_ = nullid
2507 rev_ = nullid
2496 if not message:
2508 if not message:
2497 message = _('Removed tag %s') % name
2509 message = _('Removed tag %s') % name
2498 elif name in repo.tags() and not opts['force']:
2510 elif name in repo.tags() and not opts['force']:
2499 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2511 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2500 % name)
2512 % name)
2501 if not rev_ and repo.dirstate.parents()[1] != nullid:
2513 if not rev_ and repo.dirstate.parents()[1] != nullid:
2502 raise util.Abort(_('uncommitted merge - please provide a '
2514 raise util.Abort(_('uncommitted merge - please provide a '
2503 'specific revision'))
2515 'specific revision'))
2504 r = repo.changectx(rev_).node()
2516 r = repo.changectx(rev_).node()
2505
2517
2506 if not message:
2518 if not message:
2507 message = _('Added tag %s for changeset %s') % (name, short(r))
2519 message = _('Added tag %s for changeset %s') % (name, short(r))
2508
2520
2509 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2521 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2510
2522
2511 def tags(ui, repo):
2523 def tags(ui, repo):
2512 """list repository tags
2524 """list repository tags
2513
2525
2514 List the repository tags.
2526 List the repository tags.
2515
2527
2516 This lists both regular and local tags.
2528 This lists both regular and local tags.
2517 """
2529 """
2518
2530
2519 l = repo.tagslist()
2531 l = repo.tagslist()
2520 l.reverse()
2532 l.reverse()
2521 hexfunc = ui.debugflag and hex or short
2533 hexfunc = ui.debugflag and hex or short
2522 for t, n in l:
2534 for t, n in l:
2523 try:
2535 try:
2524 hn = hexfunc(n)
2536 hn = hexfunc(n)
2525 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2537 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2526 except revlog.LookupError:
2538 except revlog.LookupError:
2527 r = " ?:%s" % hn
2539 r = " ?:%s" % hn
2528 if ui.quiet:
2540 if ui.quiet:
2529 ui.write("%s\n" % t)
2541 ui.write("%s\n" % t)
2530 else:
2542 else:
2531 spaces = " " * (30 - util.locallen(t))
2543 spaces = " " * (30 - util.locallen(t))
2532 ui.write("%s%s %s\n" % (t, spaces, r))
2544 ui.write("%s%s %s\n" % (t, spaces, r))
2533
2545
2534 def tip(ui, repo, **opts):
2546 def tip(ui, repo, **opts):
2535 """show the tip revision
2547 """show the tip revision
2536
2548
2537 Show the tip revision.
2549 Show the tip revision.
2538 """
2550 """
2539 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2551 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2540
2552
2541 def unbundle(ui, repo, fname, **opts):
2553 def unbundle(ui, repo, fname, **opts):
2542 """apply a changegroup file
2554 """apply a changegroup file
2543
2555
2544 Apply a compressed changegroup file generated by the bundle
2556 Apply a compressed changegroup file generated by the bundle
2545 command.
2557 command.
2546 """
2558 """
2547 if os.path.exists(fname):
2559 if os.path.exists(fname):
2548 f = open(fname, "rb")
2560 f = open(fname, "rb")
2549 else:
2561 else:
2550 f = urllib.urlopen(fname)
2562 f = urllib.urlopen(fname)
2551 gen = changegroup.readbundle(f, fname)
2563 gen = changegroup.readbundle(f, fname)
2552 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2564 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2553 return postincoming(ui, repo, modheads, opts['update'])
2565 return postincoming(ui, repo, modheads, opts['update'])
2554
2566
2555 def update(ui, repo, node=None, clean=False, date=None):
2567 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2556 """update working directory
2568 """update working directory
2557
2569
2558 Update the working directory to the specified revision, or the
2570 Update the working directory to the specified revision, or the
2559 tip of the current branch if none is specified.
2571 tip of the current branch if none is specified.
2560
2572
2561 If there are no outstanding changes in the working directory and
2573 If there are no outstanding changes in the working directory and
2562 there is a linear relationship between the current version and the
2574 there is a linear relationship between the current version and the
2563 requested version, the result is the requested version.
2575 requested version, the result is the requested version.
2564
2576
2565 To merge the working directory with another revision, use the
2577 To merge the working directory with another revision, use the
2566 merge command.
2578 merge command.
2567
2579
2568 By default, update will refuse to run if doing so would require
2580 By default, update will refuse to run if doing so would require
2569 discarding local changes.
2581 discarding local changes.
2570 """
2582 """
2583 if rev and node:
2584 raise util.Abort(_("please specify just one revision"))
2585
2586 if not rev:
2587 rev = node
2588
2571 if date:
2589 if date:
2572 if node:
2590 if rev:
2573 raise util.Abort(_("you can't specify a revision and a date"))
2591 raise util.Abort(_("you can't specify a revision and a date"))
2574 node = cmdutil.finddate(ui, repo, date)
2592 rev = cmdutil.finddate(ui, repo, date)
2575
2593
2576 if clean:
2594 if clean:
2577 return hg.clean(repo, node)
2595 return hg.clean(repo, rev)
2578 else:
2596 else:
2579 return hg.update(repo, node)
2597 return hg.update(repo, rev)
2580
2598
2581 def verify(ui, repo):
2599 def verify(ui, repo):
2582 """verify the integrity of the repository
2600 """verify the integrity of the repository
2583
2601
2584 Verify the integrity of the current repository.
2602 Verify the integrity of the current repository.
2585
2603
2586 This will perform an extensive check of the repository's
2604 This will perform an extensive check of the repository's
2587 integrity, validating the hashes and checksums of each entry in
2605 integrity, validating the hashes and checksums of each entry in
2588 the changelog, manifest, and tracked files, as well as the
2606 the changelog, manifest, and tracked files, as well as the
2589 integrity of their crosslinks and indices.
2607 integrity of their crosslinks and indices.
2590 """
2608 """
2591 return hg.verify(repo)
2609 return hg.verify(repo)
2592
2610
2593 def version_(ui):
2611 def version_(ui):
2594 """output version and copyright information"""
2612 """output version and copyright information"""
2595 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2613 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2596 % version.get_version())
2614 % version.get_version())
2597 ui.status(_(
2615 ui.status(_(
2598 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2616 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2599 "This is free software; see the source for copying conditions. "
2617 "This is free software; see the source for copying conditions. "
2600 "There is NO\nwarranty; "
2618 "There is NO\nwarranty; "
2601 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2619 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2602 ))
2620 ))
2603
2621
2604 # Command options and aliases are listed here, alphabetically
2622 # Command options and aliases are listed here, alphabetically
2605
2623
2606 globalopts = [
2624 globalopts = [
2607 ('R', 'repository', '',
2625 ('R', 'repository', '',
2608 _('repository root directory or symbolic path name')),
2626 _('repository root directory or symbolic path name')),
2609 ('', 'cwd', '', _('change working directory')),
2627 ('', 'cwd', '', _('change working directory')),
2610 ('y', 'noninteractive', None,
2628 ('y', 'noninteractive', None,
2611 _('do not prompt, assume \'yes\' for any required answers')),
2629 _('do not prompt, assume \'yes\' for any required answers')),
2612 ('q', 'quiet', None, _('suppress output')),
2630 ('q', 'quiet', None, _('suppress output')),
2613 ('v', 'verbose', None, _('enable additional output')),
2631 ('v', 'verbose', None, _('enable additional output')),
2614 ('', 'config', [], _('set/override config option')),
2632 ('', 'config', [], _('set/override config option')),
2615 ('', 'debug', None, _('enable debugging output')),
2633 ('', 'debug', None, _('enable debugging output')),
2616 ('', 'debugger', None, _('start debugger')),
2634 ('', 'debugger', None, _('start debugger')),
2617 ('', 'encoding', util._encoding, _('set the charset encoding')),
2635 ('', 'encoding', util._encoding, _('set the charset encoding')),
2618 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2636 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2619 ('', 'lsprof', None, _('print improved command execution profile')),
2637 ('', 'lsprof', None, _('print improved command execution profile')),
2620 ('', 'traceback', None, _('print traceback on exception')),
2638 ('', 'traceback', None, _('print traceback on exception')),
2621 ('', 'time', None, _('time how long the command takes')),
2639 ('', 'time', None, _('time how long the command takes')),
2622 ('', 'profile', None, _('print command execution profile')),
2640 ('', 'profile', None, _('print command execution profile')),
2623 ('', 'version', None, _('output version information and exit')),
2641 ('', 'version', None, _('output version information and exit')),
2624 ('h', 'help', None, _('display help and exit')),
2642 ('h', 'help', None, _('display help and exit')),
2625 ]
2643 ]
2626
2644
2627 dryrunopts = [('n', 'dry-run', None,
2645 dryrunopts = [('n', 'dry-run', None,
2628 _('do not perform actions, just print output'))]
2646 _('do not perform actions, just print output'))]
2629
2647
2630 remoteopts = [
2648 remoteopts = [
2631 ('e', 'ssh', '', _('specify ssh command to use')),
2649 ('e', 'ssh', '', _('specify ssh command to use')),
2632 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2650 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2633 ]
2651 ]
2634
2652
2635 walkopts = [
2653 walkopts = [
2636 ('I', 'include', [], _('include names matching the given patterns')),
2654 ('I', 'include', [], _('include names matching the given patterns')),
2637 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2655 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2638 ]
2656 ]
2639
2657
2640 commitopts = [
2658 commitopts = [
2641 ('m', 'message', '', _('use <text> as commit message')),
2659 ('m', 'message', '', _('use <text> as commit message')),
2642 ('l', 'logfile', '', _('read commit message from <file>')),
2660 ('l', 'logfile', '', _('read commit message from <file>')),
2643 ]
2661 ]
2644
2662
2645 table = {
2663 table = {
2646 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2664 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2647 "addremove":
2665 "addremove":
2648 (addremove,
2666 (addremove,
2649 [('s', 'similarity', '',
2667 [('s', 'similarity', '',
2650 _('guess renamed files by similarity (0<=s<=100)')),
2668 _('guess renamed files by similarity (0<=s<=100)')),
2651 ] + walkopts + dryrunopts,
2669 ] + walkopts + dryrunopts,
2652 _('hg addremove [OPTION]... [FILE]...')),
2670 _('hg addremove [OPTION]... [FILE]...')),
2653 "^annotate":
2671 "^annotate":
2654 (annotate,
2672 (annotate,
2655 [('r', 'rev', '', _('annotate the specified revision')),
2673 [('r', 'rev', '', _('annotate the specified revision')),
2656 ('f', 'follow', None, _('follow file copies and renames')),
2674 ('f', 'follow', None, _('follow file copies and renames')),
2657 ('a', 'text', None, _('treat all files as text')),
2675 ('a', 'text', None, _('treat all files as text')),
2658 ('u', 'user', None, _('list the author')),
2676 ('u', 'user', None, _('list the author')),
2659 ('d', 'date', None, _('list the date')),
2677 ('d', 'date', None, _('list the date')),
2660 ('n', 'number', None, _('list the revision number (default)')),
2678 ('n', 'number', None, _('list the revision number (default)')),
2661 ('c', 'changeset', None, _('list the changeset')),
2679 ('c', 'changeset', None, _('list the changeset')),
2662 ] + walkopts,
2680 ] + walkopts,
2663 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2681 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2664 "archive":
2682 "archive":
2665 (archive,
2683 (archive,
2666 [('', 'no-decode', None, _('do not pass files through decoders')),
2684 [('', 'no-decode', None, _('do not pass files through decoders')),
2667 ('p', 'prefix', '', _('directory prefix for files in archive')),
2685 ('p', 'prefix', '', _('directory prefix for files in archive')),
2668 ('r', 'rev', '', _('revision to distribute')),
2686 ('r', 'rev', '', _('revision to distribute')),
2669 ('t', 'type', '', _('type of distribution to create')),
2687 ('t', 'type', '', _('type of distribution to create')),
2670 ] + walkopts,
2688 ] + walkopts,
2671 _('hg archive [OPTION]... DEST')),
2689 _('hg archive [OPTION]... DEST')),
2672 "backout":
2690 "backout":
2673 (backout,
2691 (backout,
2674 [('', 'merge', None,
2692 [('', 'merge', None,
2675 _('merge with old dirstate parent after backout')),
2693 _('merge with old dirstate parent after backout')),
2676 ('d', 'date', '', _('record datecode as commit date')),
2694 ('d', 'date', '', _('record datecode as commit date')),
2677 ('', 'parent', '', _('parent to choose when backing out merge')),
2695 ('', 'parent', '', _('parent to choose when backing out merge')),
2678 ('u', 'user', '', _('record user as committer')),
2696 ('u', 'user', '', _('record user as committer')),
2697 ('r', 'rev', '', _('revision to backout')),
2679 ] + walkopts + commitopts,
2698 ] + walkopts + commitopts,
2680 _('hg backout [OPTION]... REV')),
2699 _('hg backout [OPTION]... [-r] REV')),
2681 "branch": (branch,
2700 "branch": (branch,
2682 [('f', 'force', None,
2701 [('f', 'force', None,
2683 _('set branch name even if it shadows an existing branch'))],
2702 _('set branch name even if it shadows an existing branch'))],
2684 _('hg branch [NAME]')),
2703 _('hg branch [NAME]')),
2685 "branches": (branches, [], _('hg branches')),
2704 "branches": (branches, [], _('hg branches')),
2686 "bundle":
2705 "bundle":
2687 (bundle,
2706 (bundle,
2688 [('f', 'force', None,
2707 [('f', 'force', None,
2689 _('run even when remote repository is unrelated')),
2708 _('run even when remote repository is unrelated')),
2690 ('r', 'rev', [],
2709 ('r', 'rev', [],
2691 _('a changeset you would like to bundle')),
2710 _('a changeset you would like to bundle')),
2692 ('', 'base', [],
2711 ('', 'base', [],
2693 _('a base changeset to specify instead of a destination')),
2712 _('a base changeset to specify instead of a destination')),
2694 ] + remoteopts,
2713 ] + remoteopts,
2695 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2714 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2696 "cat":
2715 "cat":
2697 (cat,
2716 (cat,
2698 [('o', 'output', '', _('print output to file with formatted name')),
2717 [('o', 'output', '', _('print output to file with formatted name')),
2699 ('r', 'rev', '', _('print the given revision')),
2718 ('r', 'rev', '', _('print the given revision')),
2700 ] + walkopts,
2719 ] + walkopts,
2701 _('hg cat [OPTION]... FILE...')),
2720 _('hg cat [OPTION]... FILE...')),
2702 "^clone":
2721 "^clone":
2703 (clone,
2722 (clone,
2704 [('U', 'noupdate', None, _('do not update the new working directory')),
2723 [('U', 'noupdate', None, _('do not update the new working directory')),
2705 ('r', 'rev', [],
2724 ('r', 'rev', [],
2706 _('a changeset you would like to have after cloning')),
2725 _('a changeset you would like to have after cloning')),
2707 ('', 'pull', None, _('use pull protocol to copy metadata')),
2726 ('', 'pull', None, _('use pull protocol to copy metadata')),
2708 ('', 'uncompressed', None,
2727 ('', 'uncompressed', None,
2709 _('use uncompressed transfer (fast over LAN)')),
2728 _('use uncompressed transfer (fast over LAN)')),
2710 ] + remoteopts,
2729 ] + remoteopts,
2711 _('hg clone [OPTION]... SOURCE [DEST]')),
2730 _('hg clone [OPTION]... SOURCE [DEST]')),
2712 "^commit|ci":
2731 "^commit|ci":
2713 (commit,
2732 (commit,
2714 [('A', 'addremove', None,
2733 [('A', 'addremove', None,
2715 _('mark new/missing files as added/removed before committing')),
2734 _('mark new/missing files as added/removed before committing')),
2716 ('d', 'date', '', _('record datecode as commit date')),
2735 ('d', 'date', '', _('record datecode as commit date')),
2717 ('u', 'user', '', _('record user as commiter')),
2736 ('u', 'user', '', _('record user as commiter')),
2718 ] + walkopts + commitopts,
2737 ] + walkopts + commitopts,
2719 _('hg commit [OPTION]... [FILE]...')),
2738 _('hg commit [OPTION]... [FILE]...')),
2720 "copy|cp":
2739 "copy|cp":
2721 (copy,
2740 (copy,
2722 [('A', 'after', None, _('record a copy that has already occurred')),
2741 [('A', 'after', None, _('record a copy that has already occurred')),
2723 ('f', 'force', None,
2742 ('f', 'force', None,
2724 _('forcibly copy over an existing managed file')),
2743 _('forcibly copy over an existing managed file')),
2725 ] + walkopts + dryrunopts,
2744 ] + walkopts + dryrunopts,
2726 _('hg copy [OPTION]... [SOURCE]... DEST')),
2745 _('hg copy [OPTION]... [SOURCE]... DEST')),
2727 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2746 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2728 "debugcomplete":
2747 "debugcomplete":
2729 (debugcomplete,
2748 (debugcomplete,
2730 [('o', 'options', None, _('show the command options'))],
2749 [('o', 'options', None, _('show the command options'))],
2731 _('debugcomplete [-o] CMD')),
2750 _('debugcomplete [-o] CMD')),
2732 "debuginstall": (debuginstall, [], _('debuginstall')),
2751 "debuginstall": (debuginstall, [], _('debuginstall')),
2733 "debugrebuildstate":
2752 "debugrebuildstate":
2734 (debugrebuildstate,
2753 (debugrebuildstate,
2735 [('r', 'rev', '', _('revision to rebuild to'))],
2754 [('r', 'rev', '', _('revision to rebuild to'))],
2736 _('debugrebuildstate [-r REV] [REV]')),
2755 _('debugrebuildstate [-r REV] [REV]')),
2737 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2756 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2738 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2757 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2739 "debugstate": (debugstate, [], _('debugstate')),
2758 "debugstate": (debugstate, [], _('debugstate')),
2740 "debugdate":
2759 "debugdate":
2741 (debugdate,
2760 (debugdate,
2742 [('e', 'extended', None, _('try extended date formats'))],
2761 [('e', 'extended', None, _('try extended date formats'))],
2743 _('debugdate [-e] DATE [RANGE]')),
2762 _('debugdate [-e] DATE [RANGE]')),
2744 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2763 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2745 "debugindex": (debugindex, [], _('debugindex FILE')),
2764 "debugindex": (debugindex, [], _('debugindex FILE')),
2746 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2765 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2747 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2766 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2748 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2767 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2749 "^diff":
2768 "^diff":
2750 (diff,
2769 (diff,
2751 [('r', 'rev', [], _('revision')),
2770 [('r', 'rev', [], _('revision')),
2752 ('a', 'text', None, _('treat all files as text')),
2771 ('a', 'text', None, _('treat all files as text')),
2753 ('p', 'show-function', None,
2772 ('p', 'show-function', None,
2754 _('show which function each change is in')),
2773 _('show which function each change is in')),
2755 ('g', 'git', None, _('use git extended diff format')),
2774 ('g', 'git', None, _('use git extended diff format')),
2756 ('', 'nodates', None, _("don't include dates in diff headers")),
2775 ('', 'nodates', None, _("don't include dates in diff headers")),
2757 ('w', 'ignore-all-space', None,
2776 ('w', 'ignore-all-space', None,
2758 _('ignore white space when comparing lines')),
2777 _('ignore white space when comparing lines')),
2759 ('b', 'ignore-space-change', None,
2778 ('b', 'ignore-space-change', None,
2760 _('ignore changes in the amount of white space')),
2779 _('ignore changes in the amount of white space')),
2761 ('B', 'ignore-blank-lines', None,
2780 ('B', 'ignore-blank-lines', None,
2762 _('ignore changes whose lines are all blank')),
2781 _('ignore changes whose lines are all blank')),
2763 ] + walkopts,
2782 ] + walkopts,
2764 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2783 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2765 "^export":
2784 "^export":
2766 (export,
2785 (export,
2767 [('o', 'output', '', _('print output to file with formatted name')),
2786 [('o', 'output', '', _('print output to file with formatted name')),
2768 ('a', 'text', None, _('treat all files as text')),
2787 ('a', 'text', None, _('treat all files as text')),
2769 ('g', 'git', None, _('use git extended diff format')),
2788 ('g', 'git', None, _('use git extended diff format')),
2770 ('', 'nodates', None, _("don't include dates in diff headers")),
2789 ('', 'nodates', None, _("don't include dates in diff headers")),
2771 ('', 'switch-parent', None, _('diff against the second parent'))],
2790 ('', 'switch-parent', None, _('diff against the second parent'))],
2772 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2791 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2773 "grep":
2792 "grep":
2774 (grep,
2793 (grep,
2775 [('0', 'print0', None, _('end fields with NUL')),
2794 [('0', 'print0', None, _('end fields with NUL')),
2776 ('', 'all', None, _('print all revisions that match')),
2795 ('', 'all', None, _('print all revisions that match')),
2777 ('f', 'follow', None,
2796 ('f', 'follow', None,
2778 _('follow changeset history, or file history across copies and renames')),
2797 _('follow changeset history, or file history across copies and renames')),
2779 ('i', 'ignore-case', None, _('ignore case when matching')),
2798 ('i', 'ignore-case', None, _('ignore case when matching')),
2780 ('l', 'files-with-matches', None,
2799 ('l', 'files-with-matches', None,
2781 _('print only filenames and revs that match')),
2800 _('print only filenames and revs that match')),
2782 ('n', 'line-number', None, _('print matching line numbers')),
2801 ('n', 'line-number', None, _('print matching line numbers')),
2783 ('r', 'rev', [], _('search in given revision range')),
2802 ('r', 'rev', [], _('search in given revision range')),
2784 ('u', 'user', None, _('print user who committed change')),
2803 ('u', 'user', None, _('print user who committed change')),
2785 ] + walkopts,
2804 ] + walkopts,
2786 _('hg grep [OPTION]... PATTERN [FILE]...')),
2805 _('hg grep [OPTION]... PATTERN [FILE]...')),
2787 "heads":
2806 "heads":
2788 (heads,
2807 (heads,
2789 [('', 'style', '', _('display using template map file')),
2808 [('', 'style', '', _('display using template map file')),
2790 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2809 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2791 ('', 'template', '', _('display with template'))],
2810 ('', 'template', '', _('display with template'))],
2792 _('hg heads [-r REV]')),
2811 _('hg heads [-r REV]')),
2793 "help": (help_, [], _('hg help [COMMAND]')),
2812 "help": (help_, [], _('hg help [COMMAND]')),
2794 "identify|id": (identify, [], _('hg identify')),
2813 "identify|id": (identify, [], _('hg identify')),
2795 "import|patch":
2814 "import|patch":
2796 (import_,
2815 (import_,
2797 [('p', 'strip', 1,
2816 [('p', 'strip', 1,
2798 _('directory strip option for patch. This has the same\n'
2817 _('directory strip option for patch. This has the same\n'
2799 'meaning as the corresponding patch option')),
2818 'meaning as the corresponding patch option')),
2800 ('b', 'base', '', _('base path')),
2819 ('b', 'base', '', _('base path')),
2801 ('f', 'force', None,
2820 ('f', 'force', None,
2802 _('skip check for outstanding uncommitted changes')),
2821 _('skip check for outstanding uncommitted changes')),
2803 ('', 'exact', None,
2822 ('', 'exact', None,
2804 _('apply patch to the nodes from which it was generated'))] + commitopts,
2823 _('apply patch to the nodes from which it was generated'))] + commitopts,
2805 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2824 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2806 "incoming|in": (incoming,
2825 "incoming|in": (incoming,
2807 [('M', 'no-merges', None, _('do not show merges')),
2826 [('M', 'no-merges', None, _('do not show merges')),
2808 ('f', 'force', None,
2827 ('f', 'force', None,
2809 _('run even when remote repository is unrelated')),
2828 _('run even when remote repository is unrelated')),
2810 ('', 'style', '', _('display using template map file')),
2829 ('', 'style', '', _('display using template map file')),
2811 ('n', 'newest-first', None, _('show newest record first')),
2830 ('n', 'newest-first', None, _('show newest record first')),
2812 ('', 'bundle', '', _('file to store the bundles into')),
2831 ('', 'bundle', '', _('file to store the bundles into')),
2813 ('p', 'patch', None, _('show patch')),
2832 ('p', 'patch', None, _('show patch')),
2814 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2833 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2815 ('', 'template', '', _('display with template')),
2834 ('', 'template', '', _('display with template')),
2816 ] + remoteopts,
2835 ] + remoteopts,
2817 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2836 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2818 ' [--bundle FILENAME] [SOURCE]')),
2837 ' [--bundle FILENAME] [SOURCE]')),
2819 "^init":
2838 "^init":
2820 (init,
2839 (init,
2821 remoteopts,
2840 remoteopts,
2822 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2841 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2823 "locate":
2842 "locate":
2824 (locate,
2843 (locate,
2825 [('r', 'rev', '', _('search the repository as it stood at rev')),
2844 [('r', 'rev', '', _('search the repository as it stood at rev')),
2826 ('0', 'print0', None,
2845 ('0', 'print0', None,
2827 _('end filenames with NUL, for use with xargs')),
2846 _('end filenames with NUL, for use with xargs')),
2828 ('f', 'fullpath', None,
2847 ('f', 'fullpath', None,
2829 _('print complete paths from the filesystem root')),
2848 _('print complete paths from the filesystem root')),
2830 ] + walkopts,
2849 ] + walkopts,
2831 _('hg locate [OPTION]... [PATTERN]...')),
2850 _('hg locate [OPTION]... [PATTERN]...')),
2832 "^log|history":
2851 "^log|history":
2833 (log,
2852 (log,
2834 [('f', 'follow', None,
2853 [('f', 'follow', None,
2835 _('follow changeset history, or file history across copies and renames')),
2854 _('follow changeset history, or file history across copies and renames')),
2836 ('', 'follow-first', None,
2855 ('', 'follow-first', None,
2837 _('only follow the first parent of merge changesets')),
2856 _('only follow the first parent of merge changesets')),
2838 ('d', 'date', '', _('show revs matching date spec')),
2857 ('d', 'date', '', _('show revs matching date spec')),
2839 ('C', 'copies', None, _('show copied files')),
2858 ('C', 'copies', None, _('show copied files')),
2840 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2859 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2841 ('l', 'limit', '', _('limit number of changes displayed')),
2860 ('l', 'limit', '', _('limit number of changes displayed')),
2842 ('r', 'rev', [], _('show the specified revision or range')),
2861 ('r', 'rev', [], _('show the specified revision or range')),
2843 ('', 'removed', None, _('include revs where files were removed')),
2862 ('', 'removed', None, _('include revs where files were removed')),
2844 ('M', 'no-merges', None, _('do not show merges')),
2863 ('M', 'no-merges', None, _('do not show merges')),
2845 ('', 'style', '', _('display using template map file')),
2864 ('', 'style', '', _('display using template map file')),
2846 ('m', 'only-merges', None, _('show only merges')),
2865 ('m', 'only-merges', None, _('show only merges')),
2847 ('p', 'patch', None, _('show patch')),
2866 ('p', 'patch', None, _('show patch')),
2848 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2867 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2849 ('', 'template', '', _('display with template')),
2868 ('', 'template', '', _('display with template')),
2850 ] + walkopts,
2869 ] + walkopts,
2851 _('hg log [OPTION]... [FILE]')),
2870 _('hg log [OPTION]... [FILE]')),
2852 "manifest": (manifest, [], _('hg manifest [REV]')),
2871 "manifest": (manifest, [], _('hg manifest [REV]')),
2853 "^merge":
2872 "^merge":
2854 (merge,
2873 (merge,
2855 [('f', 'force', None, _('force a merge with outstanding changes'))],
2874 [('f', 'force', None, _('force a merge with outstanding changes')),
2856 _('hg merge [-f] [REV]')),
2875 ('r', 'rev', '', _('revision to merge')),
2876 ],
2877 _('hg merge [-f] [[-r] REV]')),
2857 "outgoing|out": (outgoing,
2878 "outgoing|out": (outgoing,
2858 [('M', 'no-merges', None, _('do not show merges')),
2879 [('M', 'no-merges', None, _('do not show merges')),
2859 ('f', 'force', None,
2880 ('f', 'force', None,
2860 _('run even when remote repository is unrelated')),
2881 _('run even when remote repository is unrelated')),
2861 ('p', 'patch', None, _('show patch')),
2882 ('p', 'patch', None, _('show patch')),
2862 ('', 'style', '', _('display using template map file')),
2883 ('', 'style', '', _('display using template map file')),
2863 ('r', 'rev', [], _('a specific revision you would like to push')),
2884 ('r', 'rev', [], _('a specific revision you would like to push')),
2864 ('n', 'newest-first', None, _('show newest record first')),
2885 ('n', 'newest-first', None, _('show newest record first')),
2865 ('', 'template', '', _('display with template')),
2886 ('', 'template', '', _('display with template')),
2866 ] + remoteopts,
2887 ] + remoteopts,
2867 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2888 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2868 "^parents":
2889 "^parents":
2869 (parents,
2890 (parents,
2870 [('r', 'rev', '', _('show parents from the specified rev')),
2891 [('r', 'rev', '', _('show parents from the specified rev')),
2871 ('', 'style', '', _('display using template map file')),
2892 ('', 'style', '', _('display using template map file')),
2872 ('', 'template', '', _('display with template'))],
2893 ('', 'template', '', _('display with template'))],
2873 _('hg parents [-r REV] [FILE]')),
2894 _('hg parents [-r REV] [FILE]')),
2874 "paths": (paths, [], _('hg paths [NAME]')),
2895 "paths": (paths, [], _('hg paths [NAME]')),
2875 "^pull":
2896 "^pull":
2876 (pull,
2897 (pull,
2877 [('u', 'update', None,
2898 [('u', 'update', None,
2878 _('update to new tip if changesets were pulled')),
2899 _('update to new tip if changesets were pulled')),
2879 ('f', 'force', None,
2900 ('f', 'force', None,
2880 _('run even when remote repository is unrelated')),
2901 _('run even when remote repository is unrelated')),
2881 ('r', 'rev', [],
2902 ('r', 'rev', [],
2882 _('a specific revision up to which you would like to pull')),
2903 _('a specific revision up to which you would like to pull')),
2883 ] + remoteopts,
2904 ] + remoteopts,
2884 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2905 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2885 "^push":
2906 "^push":
2886 (push,
2907 (push,
2887 [('f', 'force', None, _('force push')),
2908 [('f', 'force', None, _('force push')),
2888 ('r', 'rev', [], _('a specific revision you would like to push')),
2909 ('r', 'rev', [], _('a specific revision you would like to push')),
2889 ] + remoteopts,
2910 ] + remoteopts,
2890 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2911 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2891 "debugrawcommit|rawcommit":
2912 "debugrawcommit|rawcommit":
2892 (rawcommit,
2913 (rawcommit,
2893 [('p', 'parent', [], _('parent')),
2914 [('p', 'parent', [], _('parent')),
2894 ('d', 'date', '', _('date code')),
2915 ('d', 'date', '', _('date code')),
2895 ('u', 'user', '', _('user')),
2916 ('u', 'user', '', _('user')),
2896 ('F', 'files', '', _('file list'))
2917 ('F', 'files', '', _('file list'))
2897 ] + commitopts,
2918 ] + commitopts,
2898 _('hg debugrawcommit [OPTION]... [FILE]...')),
2919 _('hg debugrawcommit [OPTION]... [FILE]...')),
2899 "recover": (recover, [], _('hg recover')),
2920 "recover": (recover, [], _('hg recover')),
2900 "^remove|rm":
2921 "^remove|rm":
2901 (remove,
2922 (remove,
2902 [('A', 'after', None, _('record remove that has already occurred')),
2923 [('A', 'after', None, _('record remove that has already occurred')),
2903 ('f', 'force', None, _('remove file even if modified')),
2924 ('f', 'force', None, _('remove file even if modified')),
2904 ] + walkopts,
2925 ] + walkopts,
2905 _('hg remove [OPTION]... FILE...')),
2926 _('hg remove [OPTION]... FILE...')),
2906 "rename|mv":
2927 "rename|mv":
2907 (rename,
2928 (rename,
2908 [('A', 'after', None, _('record a rename that has already occurred')),
2929 [('A', 'after', None, _('record a rename that has already occurred')),
2909 ('f', 'force', None,
2930 ('f', 'force', None,
2910 _('forcibly copy over an existing managed file')),
2931 _('forcibly copy over an existing managed file')),
2911 ] + walkopts + dryrunopts,
2932 ] + walkopts + dryrunopts,
2912 _('hg rename [OPTION]... SOURCE... DEST')),
2933 _('hg rename [OPTION]... SOURCE... DEST')),
2913 "^revert":
2934 "^revert":
2914 (revert,
2935 (revert,
2915 [('a', 'all', None, _('revert all changes when no arguments given')),
2936 [('a', 'all', None, _('revert all changes when no arguments given')),
2916 ('d', 'date', '', _('tipmost revision matching date')),
2937 ('d', 'date', '', _('tipmost revision matching date')),
2917 ('r', 'rev', '', _('revision to revert to')),
2938 ('r', 'rev', '', _('revision to revert to')),
2918 ('', 'no-backup', None, _('do not save backup copies of files')),
2939 ('', 'no-backup', None, _('do not save backup copies of files')),
2919 ] + walkopts + dryrunopts,
2940 ] + walkopts + dryrunopts,
2920 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2941 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2921 "rollback": (rollback, [], _('hg rollback')),
2942 "rollback": (rollback, [], _('hg rollback')),
2922 "root": (root, [], _('hg root')),
2943 "root": (root, [], _('hg root')),
2923 "showconfig|debugconfig":
2944 "showconfig|debugconfig":
2924 (showconfig,
2945 (showconfig,
2925 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2946 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2926 _('showconfig [-u] [NAME]...')),
2947 _('showconfig [-u] [NAME]...')),
2927 "^serve":
2948 "^serve":
2928 (serve,
2949 (serve,
2929 [('A', 'accesslog', '', _('name of access log file to write to')),
2950 [('A', 'accesslog', '', _('name of access log file to write to')),
2930 ('d', 'daemon', None, _('run server in background')),
2951 ('d', 'daemon', None, _('run server in background')),
2931 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2952 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2932 ('E', 'errorlog', '', _('name of error log file to write to')),
2953 ('E', 'errorlog', '', _('name of error log file to write to')),
2933 ('p', 'port', 0, _('port to use (default: 8000)')),
2954 ('p', 'port', 0, _('port to use (default: 8000)')),
2934 ('a', 'address', '', _('address to use')),
2955 ('a', 'address', '', _('address to use')),
2935 ('n', 'name', '',
2956 ('n', 'name', '',
2936 _('name to show in web pages (default: working dir)')),
2957 _('name to show in web pages (default: working dir)')),
2937 ('', 'webdir-conf', '', _('name of the webdir config file'
2958 ('', 'webdir-conf', '', _('name of the webdir config file'
2938 ' (serve more than one repo)')),
2959 ' (serve more than one repo)')),
2939 ('', 'pid-file', '', _('name of file to write process ID to')),
2960 ('', 'pid-file', '', _('name of file to write process ID to')),
2940 ('', 'stdio', None, _('for remote clients')),
2961 ('', 'stdio', None, _('for remote clients')),
2941 ('t', 'templates', '', _('web templates to use')),
2962 ('t', 'templates', '', _('web templates to use')),
2942 ('', 'style', '', _('template style to use')),
2963 ('', 'style', '', _('template style to use')),
2943 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2964 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2944 _('hg serve [OPTION]...')),
2965 _('hg serve [OPTION]...')),
2945 "^status|st":
2966 "^status|st":
2946 (status,
2967 (status,
2947 [('A', 'all', None, _('show status of all files')),
2968 [('A', 'all', None, _('show status of all files')),
2948 ('m', 'modified', None, _('show only modified files')),
2969 ('m', 'modified', None, _('show only modified files')),
2949 ('a', 'added', None, _('show only added files')),
2970 ('a', 'added', None, _('show only added files')),
2950 ('r', 'removed', None, _('show only removed files')),
2971 ('r', 'removed', None, _('show only removed files')),
2951 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2972 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2952 ('c', 'clean', None, _('show only files without changes')),
2973 ('c', 'clean', None, _('show only files without changes')),
2953 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2974 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2954 ('i', 'ignored', None, _('show only ignored files')),
2975 ('i', 'ignored', None, _('show only ignored files')),
2955 ('n', 'no-status', None, _('hide status prefix')),
2976 ('n', 'no-status', None, _('hide status prefix')),
2956 ('C', 'copies', None, _('show source of copied files')),
2977 ('C', 'copies', None, _('show source of copied files')),
2957 ('0', 'print0', None,
2978 ('0', 'print0', None,
2958 _('end filenames with NUL, for use with xargs')),
2979 _('end filenames with NUL, for use with xargs')),
2959 ('', 'rev', [], _('show difference from revision')),
2980 ('', 'rev', [], _('show difference from revision')),
2960 ] + walkopts,
2981 ] + walkopts,
2961 _('hg status [OPTION]... [FILE]...')),
2982 _('hg status [OPTION]... [FILE]...')),
2962 "tag":
2983 "tag":
2963 (tag,
2984 (tag,
2964 [('f', 'force', None, _('replace existing tag')),
2985 [('f', 'force', None, _('replace existing tag')),
2965 ('l', 'local', None, _('make the tag local')),
2986 ('l', 'local', None, _('make the tag local')),
2966 ('m', 'message', '', _('message for tag commit log entry')),
2987 ('m', 'message', '', _('message for tag commit log entry')),
2967 ('d', 'date', '', _('record datecode as commit date')),
2988 ('d', 'date', '', _('record datecode as commit date')),
2968 ('u', 'user', '', _('record user as commiter')),
2989 ('u', 'user', '', _('record user as commiter')),
2969 ('r', 'rev', '', _('revision to tag')),
2990 ('r', 'rev', '', _('revision to tag')),
2970 ('', 'remove', None, _('remove a tag'))],
2991 ('', 'remove', None, _('remove a tag'))],
2971 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2992 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2972 "tags": (tags, [], _('hg tags')),
2993 "tags": (tags, [], _('hg tags')),
2973 "tip":
2994 "tip":
2974 (tip,
2995 (tip,
2975 [('', 'style', '', _('display using template map file')),
2996 [('', 'style', '', _('display using template map file')),
2976 ('p', 'patch', None, _('show patch')),
2997 ('p', 'patch', None, _('show patch')),
2977 ('', 'template', '', _('display with template'))],
2998 ('', 'template', '', _('display with template'))],
2978 _('hg tip [-p]')),
2999 _('hg tip [-p]')),
2979 "unbundle":
3000 "unbundle":
2980 (unbundle,
3001 (unbundle,
2981 [('u', 'update', None,
3002 [('u', 'update', None,
2982 _('update to new tip if changesets were unbundled'))],
3003 _('update to new tip if changesets were unbundled'))],
2983 _('hg unbundle [-u] FILE')),
3004 _('hg unbundle [-u] FILE')),
2984 "^update|up|checkout|co":
3005 "^update|up|checkout|co":
2985 (update,
3006 (update,
2986 [('C', 'clean', None, _('overwrite locally modified files')),
3007 [('C', 'clean', None, _('overwrite locally modified files')),
2987 ('d', 'date', '', _('tipmost revision matching date'))],
3008 ('d', 'date', '', _('tipmost revision matching date')),
2988 _('hg update [-C] [-d DATE] [REV]')),
3009 ('r', 'rev', '', _('revision'))],
3010 _('hg update [-C] [-d DATE] [[-r] REV]')),
2989 "verify": (verify, [], _('hg verify')),
3011 "verify": (verify, [], _('hg verify')),
2990 "version": (version_, [], _('hg version')),
3012 "version": (version_, [], _('hg version')),
2991 }
3013 }
2992
3014
2993 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3015 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2994 " debugindex debugindexdot debugdate debuginstall")
3016 " debugindex debugindexdot debugdate debuginstall")
2995 optionalrepo = ("paths serve showconfig")
3017 optionalrepo = ("paths serve showconfig")
2996
3018
2997 def findpossible(ui, cmd):
3019 def findpossible(ui, cmd):
2998 """
3020 """
2999 Return cmd -> (aliases, command table entry)
3021 Return cmd -> (aliases, command table entry)
3000 for each matching command.
3022 for each matching command.
3001 Return debug commands (or their aliases) only if no normal command matches.
3023 Return debug commands (or their aliases) only if no normal command matches.
3002 """
3024 """
3003 choice = {}
3025 choice = {}
3004 debugchoice = {}
3026 debugchoice = {}
3005 for e in table.keys():
3027 for e in table.keys():
3006 aliases = e.lstrip("^").split("|")
3028 aliases = e.lstrip("^").split("|")
3007 found = None
3029 found = None
3008 if cmd in aliases:
3030 if cmd in aliases:
3009 found = cmd
3031 found = cmd
3010 elif not ui.config("ui", "strict"):
3032 elif not ui.config("ui", "strict"):
3011 for a in aliases:
3033 for a in aliases:
3012 if a.startswith(cmd):
3034 if a.startswith(cmd):
3013 found = a
3035 found = a
3014 break
3036 break
3015 if found is not None:
3037 if found is not None:
3016 if aliases[0].startswith("debug") or found.startswith("debug"):
3038 if aliases[0].startswith("debug") or found.startswith("debug"):
3017 debugchoice[found] = (aliases, table[e])
3039 debugchoice[found] = (aliases, table[e])
3018 else:
3040 else:
3019 choice[found] = (aliases, table[e])
3041 choice[found] = (aliases, table[e])
3020
3042
3021 if not choice and debugchoice:
3043 if not choice and debugchoice:
3022 choice = debugchoice
3044 choice = debugchoice
3023
3045
3024 return choice
3046 return choice
3025
3047
3026 def findcmd(ui, cmd):
3048 def findcmd(ui, cmd):
3027 """Return (aliases, command table entry) for command string."""
3049 """Return (aliases, command table entry) for command string."""
3028 choice = findpossible(ui, cmd)
3050 choice = findpossible(ui, cmd)
3029
3051
3030 if choice.has_key(cmd):
3052 if choice.has_key(cmd):
3031 return choice[cmd]
3053 return choice[cmd]
3032
3054
3033 if len(choice) > 1:
3055 if len(choice) > 1:
3034 clist = choice.keys()
3056 clist = choice.keys()
3035 clist.sort()
3057 clist.sort()
3036 raise AmbiguousCommand(cmd, clist)
3058 raise AmbiguousCommand(cmd, clist)
3037
3059
3038 if choice:
3060 if choice:
3039 return choice.values()[0]
3061 return choice.values()[0]
3040
3062
3041 raise UnknownCommand(cmd)
3063 raise UnknownCommand(cmd)
3042
3064
3043 def catchterm(*args):
3065 def catchterm(*args):
3044 raise util.SignalInterrupt
3066 raise util.SignalInterrupt
3045
3067
3046 def run():
3068 def run():
3047 sys.exit(dispatch(sys.argv[1:]))
3069 sys.exit(dispatch(sys.argv[1:]))
3048
3070
3049 class ParseError(Exception):
3071 class ParseError(Exception):
3050 """Exception raised on errors in parsing the command line."""
3072 """Exception raised on errors in parsing the command line."""
3051
3073
3052 def parse(ui, args):
3074 def parse(ui, args):
3053 options = {}
3075 options = {}
3054 cmdoptions = {}
3076 cmdoptions = {}
3055
3077
3056 try:
3078 try:
3057 args = fancyopts.fancyopts(args, globalopts, options)
3079 args = fancyopts.fancyopts(args, globalopts, options)
3058 except fancyopts.getopt.GetoptError, inst:
3080 except fancyopts.getopt.GetoptError, inst:
3059 raise ParseError(None, inst)
3081 raise ParseError(None, inst)
3060
3082
3061 if args:
3083 if args:
3062 cmd, args = args[0], args[1:]
3084 cmd, args = args[0], args[1:]
3063 aliases, i = findcmd(ui, cmd)
3085 aliases, i = findcmd(ui, cmd)
3064 cmd = aliases[0]
3086 cmd = aliases[0]
3065 defaults = ui.config("defaults", cmd)
3087 defaults = ui.config("defaults", cmd)
3066 if defaults:
3088 if defaults:
3067 args = shlex.split(defaults) + args
3089 args = shlex.split(defaults) + args
3068 c = list(i[1])
3090 c = list(i[1])
3069 else:
3091 else:
3070 cmd = None
3092 cmd = None
3071 c = []
3093 c = []
3072
3094
3073 # combine global options into local
3095 # combine global options into local
3074 for o in globalopts:
3096 for o in globalopts:
3075 c.append((o[0], o[1], options[o[1]], o[3]))
3097 c.append((o[0], o[1], options[o[1]], o[3]))
3076
3098
3077 try:
3099 try:
3078 args = fancyopts.fancyopts(args, c, cmdoptions)
3100 args = fancyopts.fancyopts(args, c, cmdoptions)
3079 except fancyopts.getopt.GetoptError, inst:
3101 except fancyopts.getopt.GetoptError, inst:
3080 raise ParseError(cmd, inst)
3102 raise ParseError(cmd, inst)
3081
3103
3082 # separate global options back out
3104 # separate global options back out
3083 for o in globalopts:
3105 for o in globalopts:
3084 n = o[1]
3106 n = o[1]
3085 options[n] = cmdoptions[n]
3107 options[n] = cmdoptions[n]
3086 del cmdoptions[n]
3108 del cmdoptions[n]
3087
3109
3088 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3110 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3089
3111
3090 external = {}
3112 external = {}
3091
3113
3092 def findext(name):
3114 def findext(name):
3093 '''return module with given extension name'''
3115 '''return module with given extension name'''
3094 try:
3116 try:
3095 return sys.modules[external[name]]
3117 return sys.modules[external[name]]
3096 except KeyError:
3118 except KeyError:
3097 for k, v in external.iteritems():
3119 for k, v in external.iteritems():
3098 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3120 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3099 return sys.modules[v]
3121 return sys.modules[v]
3100 raise KeyError(name)
3122 raise KeyError(name)
3101
3123
3102 def load_extensions(ui):
3124 def load_extensions(ui):
3103 added = []
3125 added = []
3104 for ext_name, load_from_name in ui.extensions():
3126 for ext_name, load_from_name in ui.extensions():
3105 if ext_name in external:
3127 if ext_name in external:
3106 continue
3128 continue
3107 try:
3129 try:
3108 if load_from_name:
3130 if load_from_name:
3109 # the module will be loaded in sys.modules
3131 # the module will be loaded in sys.modules
3110 # choose an unique name so that it doesn't
3132 # choose an unique name so that it doesn't
3111 # conflicts with other modules
3133 # conflicts with other modules
3112 module_name = "hgext_%s" % ext_name.replace('.', '_')
3134 module_name = "hgext_%s" % ext_name.replace('.', '_')
3113 mod = imp.load_source(module_name, load_from_name)
3135 mod = imp.load_source(module_name, load_from_name)
3114 else:
3136 else:
3115 def importh(name):
3137 def importh(name):
3116 mod = __import__(name)
3138 mod = __import__(name)
3117 components = name.split('.')
3139 components = name.split('.')
3118 for comp in components[1:]:
3140 for comp in components[1:]:
3119 mod = getattr(mod, comp)
3141 mod = getattr(mod, comp)
3120 return mod
3142 return mod
3121 try:
3143 try:
3122 mod = importh("hgext.%s" % ext_name)
3144 mod = importh("hgext.%s" % ext_name)
3123 except ImportError:
3145 except ImportError:
3124 mod = importh(ext_name)
3146 mod = importh(ext_name)
3125 external[ext_name] = mod.__name__
3147 external[ext_name] = mod.__name__
3126 added.append((mod, ext_name))
3148 added.append((mod, ext_name))
3127 except (util.SignalInterrupt, KeyboardInterrupt):
3149 except (util.SignalInterrupt, KeyboardInterrupt):
3128 raise
3150 raise
3129 except Exception, inst:
3151 except Exception, inst:
3130 ui.warn(_("*** failed to import extension %s: %s\n") %
3152 ui.warn(_("*** failed to import extension %s: %s\n") %
3131 (ext_name, inst))
3153 (ext_name, inst))
3132 if ui.print_exc():
3154 if ui.print_exc():
3133 return 1
3155 return 1
3134
3156
3135 for mod, name in added:
3157 for mod, name in added:
3136 uisetup = getattr(mod, 'uisetup', None)
3158 uisetup = getattr(mod, 'uisetup', None)
3137 if uisetup:
3159 if uisetup:
3138 uisetup(ui)
3160 uisetup(ui)
3139 reposetup = getattr(mod, 'reposetup', None)
3161 reposetup = getattr(mod, 'reposetup', None)
3140 if reposetup:
3162 if reposetup:
3141 hg.repo_setup_hooks.append(reposetup)
3163 hg.repo_setup_hooks.append(reposetup)
3142 cmdtable = getattr(mod, 'cmdtable', {})
3164 cmdtable = getattr(mod, 'cmdtable', {})
3143 overrides = [cmd for cmd in cmdtable if cmd in table]
3165 overrides = [cmd for cmd in cmdtable if cmd in table]
3144 if overrides:
3166 if overrides:
3145 ui.warn(_("extension '%s' overrides commands: %s\n")
3167 ui.warn(_("extension '%s' overrides commands: %s\n")
3146 % (name, " ".join(overrides)))
3168 % (name, " ".join(overrides)))
3147 table.update(cmdtable)
3169 table.update(cmdtable)
3148
3170
3149 def parseconfig(config):
3171 def parseconfig(config):
3150 """parse the --config options from the command line"""
3172 """parse the --config options from the command line"""
3151 parsed = []
3173 parsed = []
3152 for cfg in config:
3174 for cfg in config:
3153 try:
3175 try:
3154 name, value = cfg.split('=', 1)
3176 name, value = cfg.split('=', 1)
3155 section, name = name.split('.', 1)
3177 section, name = name.split('.', 1)
3156 if not section or not name:
3178 if not section or not name:
3157 raise IndexError
3179 raise IndexError
3158 parsed.append((section, name, value))
3180 parsed.append((section, name, value))
3159 except (IndexError, ValueError):
3181 except (IndexError, ValueError):
3160 raise util.Abort(_('malformed --config option: %s') % cfg)
3182 raise util.Abort(_('malformed --config option: %s') % cfg)
3161 return parsed
3183 return parsed
3162
3184
3163 def dispatch(args):
3185 def dispatch(args):
3164 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3186 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3165 num = getattr(signal, name, None)
3187 num = getattr(signal, name, None)
3166 if num: signal.signal(num, catchterm)
3188 if num: signal.signal(num, catchterm)
3167
3189
3168 try:
3190 try:
3169 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3191 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3170 except util.Abort, inst:
3192 except util.Abort, inst:
3171 sys.stderr.write(_("abort: %s\n") % inst)
3193 sys.stderr.write(_("abort: %s\n") % inst)
3172 return -1
3194 return -1
3173
3195
3174 load_extensions(u)
3196 load_extensions(u)
3175 u.addreadhook(load_extensions)
3197 u.addreadhook(load_extensions)
3176
3198
3177 try:
3199 try:
3178 cmd, func, args, options, cmdoptions = parse(u, args)
3200 cmd, func, args, options, cmdoptions = parse(u, args)
3179 if options["encoding"]:
3201 if options["encoding"]:
3180 util._encoding = options["encoding"]
3202 util._encoding = options["encoding"]
3181 if options["encodingmode"]:
3203 if options["encodingmode"]:
3182 util._encodingmode = options["encodingmode"]
3204 util._encodingmode = options["encodingmode"]
3183 if options["time"]:
3205 if options["time"]:
3184 def get_times():
3206 def get_times():
3185 t = os.times()
3207 t = os.times()
3186 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3208 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3187 t = (t[0], t[1], t[2], t[3], time.clock())
3209 t = (t[0], t[1], t[2], t[3], time.clock())
3188 return t
3210 return t
3189 s = get_times()
3211 s = get_times()
3190 def print_time():
3212 def print_time():
3191 t = get_times()
3213 t = get_times()
3192 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3214 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3193 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3215 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3194 atexit.register(print_time)
3216 atexit.register(print_time)
3195
3217
3196 # enter the debugger before command execution
3218 # enter the debugger before command execution
3197 if options['debugger']:
3219 if options['debugger']:
3198 pdb.set_trace()
3220 pdb.set_trace()
3199
3221
3200 try:
3222 try:
3201 if options['cwd']:
3223 if options['cwd']:
3202 os.chdir(options['cwd'])
3224 os.chdir(options['cwd'])
3203
3225
3204 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3226 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3205 not options["noninteractive"], options["traceback"],
3227 not options["noninteractive"], options["traceback"],
3206 parseconfig(options["config"]))
3228 parseconfig(options["config"]))
3207
3229
3208 path = u.expandpath(options["repository"]) or ""
3230 path = u.expandpath(options["repository"]) or ""
3209 repo = path and hg.repository(u, path=path) or None
3231 repo = path and hg.repository(u, path=path) or None
3210 if repo and not repo.local():
3232 if repo and not repo.local():
3211 raise util.Abort(_("repository '%s' is not local") % path)
3233 raise util.Abort(_("repository '%s' is not local") % path)
3212
3234
3213 if options['help']:
3235 if options['help']:
3214 return help_(u, cmd, options['version'])
3236 return help_(u, cmd, options['version'])
3215 elif options['version']:
3237 elif options['version']:
3216 return version_(u)
3238 return version_(u)
3217 elif not cmd:
3239 elif not cmd:
3218 return help_(u, 'shortlist')
3240 return help_(u, 'shortlist')
3219
3241
3220 if cmd not in norepo.split():
3242 if cmd not in norepo.split():
3221 try:
3243 try:
3222 if not repo:
3244 if not repo:
3223 repo = hg.repository(u, path=path)
3245 repo = hg.repository(u, path=path)
3224 u = repo.ui
3246 u = repo.ui
3225 except hg.RepoError:
3247 except hg.RepoError:
3226 if cmd not in optionalrepo.split():
3248 if cmd not in optionalrepo.split():
3227 raise
3249 raise
3228 d = lambda: func(u, repo, *args, **cmdoptions)
3250 d = lambda: func(u, repo, *args, **cmdoptions)
3229 else:
3251 else:
3230 d = lambda: func(u, *args, **cmdoptions)
3252 d = lambda: func(u, *args, **cmdoptions)
3231
3253
3232 try:
3254 try:
3233 if options['profile']:
3255 if options['profile']:
3234 import hotshot, hotshot.stats
3256 import hotshot, hotshot.stats
3235 prof = hotshot.Profile("hg.prof")
3257 prof = hotshot.Profile("hg.prof")
3236 try:
3258 try:
3237 try:
3259 try:
3238 return prof.runcall(d)
3260 return prof.runcall(d)
3239 except:
3261 except:
3240 try:
3262 try:
3241 u.warn(_('exception raised - generating '
3263 u.warn(_('exception raised - generating '
3242 'profile anyway\n'))
3264 'profile anyway\n'))
3243 except:
3265 except:
3244 pass
3266 pass
3245 raise
3267 raise
3246 finally:
3268 finally:
3247 prof.close()
3269 prof.close()
3248 stats = hotshot.stats.load("hg.prof")
3270 stats = hotshot.stats.load("hg.prof")
3249 stats.strip_dirs()
3271 stats.strip_dirs()
3250 stats.sort_stats('time', 'calls')
3272 stats.sort_stats('time', 'calls')
3251 stats.print_stats(40)
3273 stats.print_stats(40)
3252 elif options['lsprof']:
3274 elif options['lsprof']:
3253 try:
3275 try:
3254 from mercurial import lsprof
3276 from mercurial import lsprof
3255 except ImportError:
3277 except ImportError:
3256 raise util.Abort(_(
3278 raise util.Abort(_(
3257 'lsprof not available - install from '
3279 'lsprof not available - install from '
3258 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3280 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3259 p = lsprof.Profiler()
3281 p = lsprof.Profiler()
3260 p.enable(subcalls=True)
3282 p.enable(subcalls=True)
3261 try:
3283 try:
3262 return d()
3284 return d()
3263 finally:
3285 finally:
3264 p.disable()
3286 p.disable()
3265 stats = lsprof.Stats(p.getstats())
3287 stats = lsprof.Stats(p.getstats())
3266 stats.sort()
3288 stats.sort()
3267 stats.pprint(top=10, file=sys.stderr, climit=5)
3289 stats.pprint(top=10, file=sys.stderr, climit=5)
3268 else:
3290 else:
3269 return d()
3291 return d()
3270 finally:
3292 finally:
3271 u.flush()
3293 u.flush()
3272 except:
3294 except:
3273 # enter the debugger when we hit an exception
3295 # enter the debugger when we hit an exception
3274 if options['debugger']:
3296 if options['debugger']:
3275 pdb.post_mortem(sys.exc_info()[2])
3297 pdb.post_mortem(sys.exc_info()[2])
3276 u.print_exc()
3298 u.print_exc()
3277 raise
3299 raise
3278 except ParseError, inst:
3300 except ParseError, inst:
3279 if inst.args[0]:
3301 if inst.args[0]:
3280 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3302 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3281 help_(u, inst.args[0])
3303 help_(u, inst.args[0])
3282 else:
3304 else:
3283 u.warn(_("hg: %s\n") % inst.args[1])
3305 u.warn(_("hg: %s\n") % inst.args[1])
3284 help_(u, 'shortlist')
3306 help_(u, 'shortlist')
3285 except AmbiguousCommand, inst:
3307 except AmbiguousCommand, inst:
3286 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3308 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3287 (inst.args[0], " ".join(inst.args[1])))
3309 (inst.args[0], " ".join(inst.args[1])))
3288 except UnknownCommand, inst:
3310 except UnknownCommand, inst:
3289 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3311 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3290 help_(u, 'shortlist')
3312 help_(u, 'shortlist')
3291 except hg.RepoError, inst:
3313 except hg.RepoError, inst:
3292 u.warn(_("abort: %s!\n") % inst)
3314 u.warn(_("abort: %s!\n") % inst)
3293 except lock.LockHeld, inst:
3315 except lock.LockHeld, inst:
3294 if inst.errno == errno.ETIMEDOUT:
3316 if inst.errno == errno.ETIMEDOUT:
3295 reason = _('timed out waiting for lock held by %s') % inst.locker
3317 reason = _('timed out waiting for lock held by %s') % inst.locker
3296 else:
3318 else:
3297 reason = _('lock held by %s') % inst.locker
3319 reason = _('lock held by %s') % inst.locker
3298 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3320 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3299 except lock.LockUnavailable, inst:
3321 except lock.LockUnavailable, inst:
3300 u.warn(_("abort: could not lock %s: %s\n") %
3322 u.warn(_("abort: could not lock %s: %s\n") %
3301 (inst.desc or inst.filename, inst.strerror))
3323 (inst.desc or inst.filename, inst.strerror))
3302 except revlog.RevlogError, inst:
3324 except revlog.RevlogError, inst:
3303 u.warn(_("abort: %s!\n") % inst)
3325 u.warn(_("abort: %s!\n") % inst)
3304 except util.SignalInterrupt:
3326 except util.SignalInterrupt:
3305 u.warn(_("killed!\n"))
3327 u.warn(_("killed!\n"))
3306 except KeyboardInterrupt:
3328 except KeyboardInterrupt:
3307 try:
3329 try:
3308 u.warn(_("interrupted!\n"))
3330 u.warn(_("interrupted!\n"))
3309 except IOError, inst:
3331 except IOError, inst:
3310 if inst.errno == errno.EPIPE:
3332 if inst.errno == errno.EPIPE:
3311 if u.debugflag:
3333 if u.debugflag:
3312 u.warn(_("\nbroken pipe\n"))
3334 u.warn(_("\nbroken pipe\n"))
3313 else:
3335 else:
3314 raise
3336 raise
3315 except socket.error, inst:
3337 except socket.error, inst:
3316 u.warn(_("abort: %s\n") % inst[1])
3338 u.warn(_("abort: %s\n") % inst[1])
3317 except IOError, inst:
3339 except IOError, inst:
3318 if hasattr(inst, "code"):
3340 if hasattr(inst, "code"):
3319 u.warn(_("abort: %s\n") % inst)
3341 u.warn(_("abort: %s\n") % inst)
3320 elif hasattr(inst, "reason"):
3342 elif hasattr(inst, "reason"):
3321 try: # usually it is in the form (errno, strerror)
3343 try: # usually it is in the form (errno, strerror)
3322 reason = inst.reason.args[1]
3344 reason = inst.reason.args[1]
3323 except: # it might be anything, for example a string
3345 except: # it might be anything, for example a string
3324 reason = inst.reason
3346 reason = inst.reason
3325 u.warn(_("abort: error: %s\n") % reason)
3347 u.warn(_("abort: error: %s\n") % reason)
3326 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3348 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3327 if u.debugflag:
3349 if u.debugflag:
3328 u.warn(_("broken pipe\n"))
3350 u.warn(_("broken pipe\n"))
3329 elif getattr(inst, "strerror", None):
3351 elif getattr(inst, "strerror", None):
3330 if getattr(inst, "filename", None):
3352 if getattr(inst, "filename", None):
3331 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3353 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3332 else:
3354 else:
3333 u.warn(_("abort: %s\n") % inst.strerror)
3355 u.warn(_("abort: %s\n") % inst.strerror)
3334 else:
3356 else:
3335 raise
3357 raise
3336 except OSError, inst:
3358 except OSError, inst:
3337 if getattr(inst, "filename", None):
3359 if getattr(inst, "filename", None):
3338 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3360 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3339 else:
3361 else:
3340 u.warn(_("abort: %s\n") % inst.strerror)
3362 u.warn(_("abort: %s\n") % inst.strerror)
3341 except util.UnexpectedOutput, inst:
3363 except util.UnexpectedOutput, inst:
3342 u.warn(_("abort: %s") % inst[0])
3364 u.warn(_("abort: %s") % inst[0])
3343 if not isinstance(inst[1], basestring):
3365 if not isinstance(inst[1], basestring):
3344 u.warn(" %r\n" % (inst[1],))
3366 u.warn(" %r\n" % (inst[1],))
3345 elif not inst[1]:
3367 elif not inst[1]:
3346 u.warn(_(" empty string\n"))
3368 u.warn(_(" empty string\n"))
3347 else:
3369 else:
3348 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3370 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3349 except util.Abort, inst:
3371 except util.Abort, inst:
3350 u.warn(_("abort: %s\n") % inst)
3372 u.warn(_("abort: %s\n") % inst)
3351 except TypeError, inst:
3373 except TypeError, inst:
3352 # was this an argument error?
3374 # was this an argument error?
3353 tb = traceback.extract_tb(sys.exc_info()[2])
3375 tb = traceback.extract_tb(sys.exc_info()[2])
3354 if len(tb) > 2: # no
3376 if len(tb) > 2: # no
3355 raise
3377 raise
3356 u.debug(inst, "\n")
3378 u.debug(inst, "\n")
3357 u.warn(_("%s: invalid arguments\n") % cmd)
3379 u.warn(_("%s: invalid arguments\n") % cmd)
3358 help_(u, cmd)
3380 help_(u, cmd)
3359 except SystemExit, inst:
3381 except SystemExit, inst:
3360 # Commands shouldn't sys.exit directly, but give a return code.
3382 # Commands shouldn't sys.exit directly, but give a return code.
3361 # Just in case catch this and and pass exit code to caller.
3383 # Just in case catch this and and pass exit code to caller.
3362 return inst.code
3384 return inst.code
3363 except:
3385 except:
3364 u.warn(_("** unknown exception encountered, details follow\n"))
3386 u.warn(_("** unknown exception encountered, details follow\n"))
3365 u.warn(_("** report bug details to "
3387 u.warn(_("** report bug details to "
3366 "http://www.selenic.com/mercurial/bts\n"))
3388 "http://www.selenic.com/mercurial/bts\n"))
3367 u.warn(_("** or mercurial@selenic.com\n"))
3389 u.warn(_("** or mercurial@selenic.com\n"))
3368 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3390 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3369 % version.get_version())
3391 % version.get_version())
3370 raise
3392 raise
3371
3393
3372 return -1
3394 return -1
@@ -1,590 +1,592 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import *
10 from node import *
11 from i18n import _
11 from i18n import _
12 import struct, os, time, bisect, stat, strutil, util, re, errno
12 import struct, os, time, bisect, stat, strutil, util, re, errno
13 import cStringIO
13 import cStringIO
14
14
15 class dirstate(object):
15 class dirstate(object):
16 format = ">cllll"
16 format = ">cllll"
17
17
18 def __init__(self, opener, ui, root):
18 def __init__(self, opener, ui, root):
19 self.opener = opener
19 self.opener = opener
20 self.root = root
20 self.root = root
21 self.dirty = 0
21 self.dirty = 0
22 self.ui = ui
22 self.ui = ui
23 self.map = None
23 self.map = None
24 self.fp = None
24 self.fp = None
25 self.pl = None
25 self.pl = None
26 self.dirs = None
26 self.dirs = None
27 self.copymap = {}
27 self.copymap = {}
28 self.ignorefunc = None
28 self.ignorefunc = None
29 self._branch = None
29 self._branch = None
30
30
31 def wjoin(self, f):
31 def wjoin(self, f):
32 return os.path.join(self.root, f)
32 return os.path.join(self.root, f)
33
33
34 def getcwd(self):
34 def getcwd(self):
35 cwd = os.getcwd()
35 cwd = os.getcwd()
36 if cwd == self.root: return ''
36 if cwd == self.root: return ''
37 # self.root ends with a path separator if self.root is '/' or 'C:\'
37 # self.root ends with a path separator if self.root is '/' or 'C:\'
38 rootsep = self.root
38 rootsep = self.root
39 if not rootsep.endswith(os.sep):
39 if not rootsep.endswith(os.sep):
40 rootsep += os.sep
40 rootsep += os.sep
41 if cwd.startswith(rootsep):
41 if cwd.startswith(rootsep):
42 return cwd[len(rootsep):]
42 return cwd[len(rootsep):]
43 else:
43 else:
44 # we're outside the repo. return an absolute path.
44 # we're outside the repo. return an absolute path.
45 return cwd
45 return cwd
46
46
47 def hgignore(self):
47 def hgignore(self):
48 '''return the contents of .hgignore files as a list of patterns.
48 '''return the contents of .hgignore files as a list of patterns.
49
49
50 the files parsed for patterns include:
50 the files parsed for patterns include:
51 .hgignore in the repository root
51 .hgignore in the repository root
52 any additional files specified in the [ui] section of ~/.hgrc
52 any additional files specified in the [ui] section of ~/.hgrc
53
53
54 trailing white space is dropped.
54 trailing white space is dropped.
55 the escape character is backslash.
55 the escape character is backslash.
56 comments start with #.
56 comments start with #.
57 empty lines are skipped.
57 empty lines are skipped.
58
58
59 lines can be of the following formats:
59 lines can be of the following formats:
60
60
61 syntax: regexp # defaults following lines to non-rooted regexps
61 syntax: regexp # defaults following lines to non-rooted regexps
62 syntax: glob # defaults following lines to non-rooted globs
62 syntax: glob # defaults following lines to non-rooted globs
63 re:pattern # non-rooted regular expression
63 re:pattern # non-rooted regular expression
64 glob:pattern # non-rooted glob
64 glob:pattern # non-rooted glob
65 pattern # pattern of the current default type'''
65 pattern # pattern of the current default type'''
66 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
66 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
67 def parselines(fp):
67 def parselines(fp):
68 for line in fp:
68 for line in fp:
69 if not line.endswith('\n'):
70 line += '\n'
69 escape = False
71 escape = False
70 for i in xrange(len(line)):
72 for i in xrange(len(line)):
71 if escape: escape = False
73 if escape: escape = False
72 elif line[i] == '\\': escape = True
74 elif line[i] == '\\': escape = True
73 elif line[i] == '#': break
75 elif line[i] == '#': break
74 line = line[:i].rstrip()
76 line = line[:i].rstrip()
75 if line: yield line
77 if line: yield line
76 repoignore = self.wjoin('.hgignore')
78 repoignore = self.wjoin('.hgignore')
77 files = [repoignore]
79 files = [repoignore]
78 files.extend(self.ui.hgignorefiles())
80 files.extend(self.ui.hgignorefiles())
79 pats = {}
81 pats = {}
80 for f in files:
82 for f in files:
81 try:
83 try:
82 pats[f] = []
84 pats[f] = []
83 fp = open(f)
85 fp = open(f)
84 syntax = 'relre:'
86 syntax = 'relre:'
85 for line in parselines(fp):
87 for line in parselines(fp):
86 if line.startswith('syntax:'):
88 if line.startswith('syntax:'):
87 s = line[7:].strip()
89 s = line[7:].strip()
88 try:
90 try:
89 syntax = syntaxes[s]
91 syntax = syntaxes[s]
90 except KeyError:
92 except KeyError:
91 self.ui.warn(_("%s: ignoring invalid "
93 self.ui.warn(_("%s: ignoring invalid "
92 "syntax '%s'\n") % (f, s))
94 "syntax '%s'\n") % (f, s))
93 continue
95 continue
94 pat = syntax + line
96 pat = syntax + line
95 for s in syntaxes.values():
97 for s in syntaxes.values():
96 if line.startswith(s):
98 if line.startswith(s):
97 pat = line
99 pat = line
98 break
100 break
99 pats[f].append(pat)
101 pats[f].append(pat)
100 except IOError, inst:
102 except IOError, inst:
101 if f != repoignore:
103 if f != repoignore:
102 self.ui.warn(_("skipping unreadable ignore file"
104 self.ui.warn(_("skipping unreadable ignore file"
103 " '%s': %s\n") % (f, inst.strerror))
105 " '%s': %s\n") % (f, inst.strerror))
104 return pats
106 return pats
105
107
106 def ignore(self, fn):
108 def ignore(self, fn):
107 '''default match function used by dirstate and
109 '''default match function used by dirstate and
108 localrepository. this honours the repository .hgignore file
110 localrepository. this honours the repository .hgignore file
109 and any other files specified in the [ui] section of .hgrc.'''
111 and any other files specified in the [ui] section of .hgrc.'''
110 if not self.ignorefunc:
112 if not self.ignorefunc:
111 ignore = self.hgignore()
113 ignore = self.hgignore()
112 allpats = []
114 allpats = []
113 [allpats.extend(patlist) for patlist in ignore.values()]
115 [allpats.extend(patlist) for patlist in ignore.values()]
114 if allpats:
116 if allpats:
115 try:
117 try:
116 files, self.ignorefunc, anypats = (
118 files, self.ignorefunc, anypats = (
117 util.matcher(self.root, inc=allpats, src='.hgignore'))
119 util.matcher(self.root, inc=allpats, src='.hgignore'))
118 except util.Abort:
120 except util.Abort:
119 # Re-raise an exception where the src is the right file
121 # Re-raise an exception where the src is the right file
120 for f, patlist in ignore.items():
122 for f, patlist in ignore.items():
121 files, self.ignorefunc, anypats = (
123 files, self.ignorefunc, anypats = (
122 util.matcher(self.root, inc=patlist, src=f))
124 util.matcher(self.root, inc=patlist, src=f))
123 else:
125 else:
124 self.ignorefunc = util.never
126 self.ignorefunc = util.never
125 return self.ignorefunc(fn)
127 return self.ignorefunc(fn)
126
128
127 def __del__(self):
129 def __del__(self):
128 if self.dirty:
130 if self.dirty:
129 self.write()
131 self.write()
130
132
131 def __getitem__(self, key):
133 def __getitem__(self, key):
132 try:
134 try:
133 return self.map[key]
135 return self.map[key]
134 except TypeError:
136 except TypeError:
135 self.lazyread()
137 self.lazyread()
136 return self[key]
138 return self[key]
137
139
138 _unknown = ('?', 0, 0, 0)
140 _unknown = ('?', 0, 0, 0)
139
141
140 def get(self, key):
142 def get(self, key):
141 try:
143 try:
142 return self[key]
144 return self[key]
143 except KeyError:
145 except KeyError:
144 return self._unknown
146 return self._unknown
145
147
146 def __contains__(self, key):
148 def __contains__(self, key):
147 self.lazyread()
149 self.lazyread()
148 return key in self.map
150 return key in self.map
149
151
150 def parents(self):
152 def parents(self):
151 if self.pl is None:
153 if self.pl is None:
152 self.pl = [nullid, nullid]
154 self.pl = [nullid, nullid]
153 try:
155 try:
154 self.fp = self.opener('dirstate')
156 self.fp = self.opener('dirstate')
155 st = self.fp.read(40)
157 st = self.fp.read(40)
156 if len(st) == 40:
158 if len(st) == 40:
157 self.pl = st[:20], st[20:40]
159 self.pl = st[:20], st[20:40]
158 except IOError, err:
160 except IOError, err:
159 if err.errno != errno.ENOENT: raise
161 if err.errno != errno.ENOENT: raise
160 return self.pl
162 return self.pl
161
163
162 def branch(self):
164 def branch(self):
163 if not self._branch:
165 if not self._branch:
164 try:
166 try:
165 self._branch = self.opener("branch").read().strip()\
167 self._branch = self.opener("branch").read().strip()\
166 or "default"
168 or "default"
167 except IOError:
169 except IOError:
168 self._branch = "default"
170 self._branch = "default"
169 return self._branch
171 return self._branch
170
172
171 def markdirty(self):
173 def markdirty(self):
172 if not self.dirty:
174 if not self.dirty:
173 self.dirty = 1
175 self.dirty = 1
174
176
175 def setparents(self, p1, p2=nullid):
177 def setparents(self, p1, p2=nullid):
176 self.lazyread()
178 self.lazyread()
177 self.markdirty()
179 self.markdirty()
178 self.pl = p1, p2
180 self.pl = p1, p2
179
181
180 def setbranch(self, branch):
182 def setbranch(self, branch):
181 self._branch = branch
183 self._branch = branch
182 self.opener("branch", "w").write(branch + '\n')
184 self.opener("branch", "w").write(branch + '\n')
183
185
184 def state(self, key):
186 def state(self, key):
185 try:
187 try:
186 return self[key][0]
188 return self[key][0]
187 except KeyError:
189 except KeyError:
188 return "?"
190 return "?"
189
191
190 def lazyread(self):
192 def lazyread(self):
191 if self.map is None:
193 if self.map is None:
192 self.read()
194 self.read()
193
195
194 def parse(self, st):
196 def parse(self, st):
195 self.pl = [st[:20], st[20: 40]]
197 self.pl = [st[:20], st[20: 40]]
196
198
197 # deref fields so they will be local in loop
199 # deref fields so they will be local in loop
198 map = self.map
200 map = self.map
199 copymap = self.copymap
201 copymap = self.copymap
200 format = self.format
202 format = self.format
201 unpack = struct.unpack
203 unpack = struct.unpack
202
204
203 pos = 40
205 pos = 40
204 e_size = struct.calcsize(format)
206 e_size = struct.calcsize(format)
205
207
206 while pos < len(st):
208 while pos < len(st):
207 newpos = pos + e_size
209 newpos = pos + e_size
208 e = unpack(format, st[pos:newpos])
210 e = unpack(format, st[pos:newpos])
209 l = e[4]
211 l = e[4]
210 pos = newpos
212 pos = newpos
211 newpos = pos + l
213 newpos = pos + l
212 f = st[pos:newpos]
214 f = st[pos:newpos]
213 if '\0' in f:
215 if '\0' in f:
214 f, c = f.split('\0')
216 f, c = f.split('\0')
215 copymap[f] = c
217 copymap[f] = c
216 map[f] = e[:4]
218 map[f] = e[:4]
217 pos = newpos
219 pos = newpos
218
220
219 def read(self):
221 def read(self):
220 self.map = {}
222 self.map = {}
221 self.pl = [nullid, nullid]
223 self.pl = [nullid, nullid]
222 try:
224 try:
223 if self.fp:
225 if self.fp:
224 self.fp.seek(0)
226 self.fp.seek(0)
225 st = self.fp.read()
227 st = self.fp.read()
226 self.fp = None
228 self.fp = None
227 else:
229 else:
228 st = self.opener("dirstate").read()
230 st = self.opener("dirstate").read()
229 if st:
231 if st:
230 self.parse(st)
232 self.parse(st)
231 except IOError, err:
233 except IOError, err:
232 if err.errno != errno.ENOENT: raise
234 if err.errno != errno.ENOENT: raise
233
235
234 def reload(self):
236 def reload(self):
235 def mtime():
237 def mtime():
236 m = self.map and self.map.get('.hgignore')
238 m = self.map and self.map.get('.hgignore')
237 return m and m[-1]
239 return m and m[-1]
238
240
239 old_mtime = self.ignorefunc and mtime()
241 old_mtime = self.ignorefunc and mtime()
240 self.read()
242 self.read()
241 if old_mtime != mtime():
243 if old_mtime != mtime():
242 self.ignorefunc = None
244 self.ignorefunc = None
243
245
244 def copy(self, source, dest):
246 def copy(self, source, dest):
245 self.lazyread()
247 self.lazyread()
246 self.markdirty()
248 self.markdirty()
247 self.copymap[dest] = source
249 self.copymap[dest] = source
248
250
249 def copied(self, file):
251 def copied(self, file):
250 return self.copymap.get(file, None)
252 return self.copymap.get(file, None)
251
253
252 def copies(self):
254 def copies(self):
253 return self.copymap
255 return self.copymap
254
256
255 def initdirs(self):
257 def initdirs(self):
256 if self.dirs is None:
258 if self.dirs is None:
257 self.dirs = {}
259 self.dirs = {}
258 for f in self.map:
260 for f in self.map:
259 self.updatedirs(f, 1)
261 self.updatedirs(f, 1)
260
262
261 def updatedirs(self, path, delta):
263 def updatedirs(self, path, delta):
262 if self.dirs is not None:
264 if self.dirs is not None:
263 for c in strutil.findall(path, '/'):
265 for c in strutil.findall(path, '/'):
264 pc = path[:c]
266 pc = path[:c]
265 self.dirs.setdefault(pc, 0)
267 self.dirs.setdefault(pc, 0)
266 self.dirs[pc] += delta
268 self.dirs[pc] += delta
267
269
268 def checkinterfering(self, files):
270 def checkinterfering(self, files):
269 def prefixes(f):
271 def prefixes(f):
270 for c in strutil.rfindall(f, '/'):
272 for c in strutil.rfindall(f, '/'):
271 yield f[:c]
273 yield f[:c]
272 self.lazyread()
274 self.lazyread()
273 self.initdirs()
275 self.initdirs()
274 seendirs = {}
276 seendirs = {}
275 for f in files:
277 for f in files:
276 # shadows
278 # shadows
277 if self.dirs.get(f):
279 if self.dirs.get(f):
278 raise util.Abort(_('directory named %r already in dirstate') %
280 raise util.Abort(_('directory named %r already in dirstate') %
279 f)
281 f)
280 for d in prefixes(f):
282 for d in prefixes(f):
281 if d in seendirs:
283 if d in seendirs:
282 break
284 break
283 if d in self.map:
285 if d in self.map:
284 raise util.Abort(_('file named %r already in dirstate') %
286 raise util.Abort(_('file named %r already in dirstate') %
285 d)
287 d)
286 seendirs[d] = True
288 seendirs[d] = True
287 # disallowed
289 # disallowed
288 if '\r' in f or '\n' in f:
290 if '\r' in f or '\n' in f:
289 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
291 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
290
292
291 def update(self, files, state, **kw):
293 def update(self, files, state, **kw):
292 ''' current states:
294 ''' current states:
293 n normal
295 n normal
294 m needs merging
296 m needs merging
295 r marked for removal
297 r marked for removal
296 a marked for addition'''
298 a marked for addition'''
297
299
298 if not files: return
300 if not files: return
299 self.lazyread()
301 self.lazyread()
300 self.markdirty()
302 self.markdirty()
301 if state == "a":
303 if state == "a":
302 self.initdirs()
304 self.initdirs()
303 self.checkinterfering(files)
305 self.checkinterfering(files)
304 for f in files:
306 for f in files:
305 if state == "r":
307 if state == "r":
306 self.map[f] = ('r', 0, 0, 0)
308 self.map[f] = ('r', 0, 0, 0)
307 self.updatedirs(f, -1)
309 self.updatedirs(f, -1)
308 else:
310 else:
309 if state == "a":
311 if state == "a":
310 self.updatedirs(f, 1)
312 self.updatedirs(f, 1)
311 s = os.lstat(self.wjoin(f))
313 s = os.lstat(self.wjoin(f))
312 st_size = kw.get('st_size', s.st_size)
314 st_size = kw.get('st_size', s.st_size)
313 st_mtime = kw.get('st_mtime', s.st_mtime)
315 st_mtime = kw.get('st_mtime', s.st_mtime)
314 self.map[f] = (state, s.st_mode, st_size, st_mtime)
316 self.map[f] = (state, s.st_mode, st_size, st_mtime)
315 if self.copymap.has_key(f):
317 if self.copymap.has_key(f):
316 del self.copymap[f]
318 del self.copymap[f]
317
319
318 def forget(self, files):
320 def forget(self, files):
319 if not files: return
321 if not files: return
320 self.lazyread()
322 self.lazyread()
321 self.markdirty()
323 self.markdirty()
322 self.initdirs()
324 self.initdirs()
323 for f in files:
325 for f in files:
324 try:
326 try:
325 del self.map[f]
327 del self.map[f]
326 self.updatedirs(f, -1)
328 self.updatedirs(f, -1)
327 except KeyError:
329 except KeyError:
328 self.ui.warn(_("not in dirstate: %s!\n") % f)
330 self.ui.warn(_("not in dirstate: %s!\n") % f)
329 pass
331 pass
330
332
331 def clear(self):
333 def clear(self):
332 self.map = {}
334 self.map = {}
333 self.copymap = {}
335 self.copymap = {}
334 self.dirs = None
336 self.dirs = None
335 self.markdirty()
337 self.markdirty()
336
338
337 def rebuild(self, parent, files):
339 def rebuild(self, parent, files):
338 self.clear()
340 self.clear()
339 for f in files:
341 for f in files:
340 if files.execf(f):
342 if files.execf(f):
341 self.map[f] = ('n', 0777, -1, 0)
343 self.map[f] = ('n', 0777, -1, 0)
342 else:
344 else:
343 self.map[f] = ('n', 0666, -1, 0)
345 self.map[f] = ('n', 0666, -1, 0)
344 self.pl = (parent, nullid)
346 self.pl = (parent, nullid)
345 self.markdirty()
347 self.markdirty()
346
348
347 def write(self):
349 def write(self):
348 if not self.dirty:
350 if not self.dirty:
349 return
351 return
350 cs = cStringIO.StringIO()
352 cs = cStringIO.StringIO()
351 cs.write("".join(self.pl))
353 cs.write("".join(self.pl))
352 for f, e in self.map.iteritems():
354 for f, e in self.map.iteritems():
353 c = self.copied(f)
355 c = self.copied(f)
354 if c:
356 if c:
355 f = f + "\0" + c
357 f = f + "\0" + c
356 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
358 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
357 cs.write(e)
359 cs.write(e)
358 cs.write(f)
360 cs.write(f)
359 st = self.opener("dirstate", "w", atomic=True)
361 st = self.opener("dirstate", "w", atomic=True)
360 st.write(cs.getvalue())
362 st.write(cs.getvalue())
361 self.dirty = 0
363 self.dirty = 0
362
364
363 def filterfiles(self, files):
365 def filterfiles(self, files):
364 ret = {}
366 ret = {}
365 unknown = []
367 unknown = []
366
368
367 for x in files:
369 for x in files:
368 if x == '.':
370 if x == '.':
369 return self.map.copy()
371 return self.map.copy()
370 if x not in self.map:
372 if x not in self.map:
371 unknown.append(x)
373 unknown.append(x)
372 else:
374 else:
373 ret[x] = self.map[x]
375 ret[x] = self.map[x]
374
376
375 if not unknown:
377 if not unknown:
376 return ret
378 return ret
377
379
378 b = self.map.keys()
380 b = self.map.keys()
379 b.sort()
381 b.sort()
380 blen = len(b)
382 blen = len(b)
381
383
382 for x in unknown:
384 for x in unknown:
383 bs = bisect.bisect(b, "%s%s" % (x, '/'))
385 bs = bisect.bisect(b, "%s%s" % (x, '/'))
384 while bs < blen:
386 while bs < blen:
385 s = b[bs]
387 s = b[bs]
386 if len(s) > len(x) and s.startswith(x):
388 if len(s) > len(x) and s.startswith(x):
387 ret[s] = self.map[s]
389 ret[s] = self.map[s]
388 else:
390 else:
389 break
391 break
390 bs += 1
392 bs += 1
391 return ret
393 return ret
392
394
393 def supported_type(self, f, st, verbose=False):
395 def supported_type(self, f, st, verbose=False):
394 if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
396 if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
395 return True
397 return True
396 if verbose:
398 if verbose:
397 kind = 'unknown'
399 kind = 'unknown'
398 if stat.S_ISCHR(st.st_mode): kind = _('character device')
400 if stat.S_ISCHR(st.st_mode): kind = _('character device')
399 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
401 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
400 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
402 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
401 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
403 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
402 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
404 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
403 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
405 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
404 util.pathto(self.root, self.getcwd(), f),
406 util.pathto(self.root, self.getcwd(), f),
405 kind))
407 kind))
406 return False
408 return False
407
409
408 def walk(self, files=None, match=util.always, badmatch=None):
410 def walk(self, files=None, match=util.always, badmatch=None):
409 # filter out the stat
411 # filter out the stat
410 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
412 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
411 yield src, f
413 yield src, f
412
414
413 def statwalk(self, files=None, match=util.always, ignored=False,
415 def statwalk(self, files=None, match=util.always, ignored=False,
414 badmatch=None, directories=False):
416 badmatch=None, directories=False):
415 '''
417 '''
416 walk recursively through the directory tree, finding all files
418 walk recursively through the directory tree, finding all files
417 matched by the match function
419 matched by the match function
418
420
419 results are yielded in a tuple (src, filename, st), where src
421 results are yielded in a tuple (src, filename, st), where src
420 is one of:
422 is one of:
421 'f' the file was found in the directory tree
423 'f' the file was found in the directory tree
422 'd' the file is a directory of the tree
424 'd' the file is a directory of the tree
423 'm' the file was only in the dirstate and not in the tree
425 'm' the file was only in the dirstate and not in the tree
424 'b' file was not found and matched badmatch
426 'b' file was not found and matched badmatch
425
427
426 and st is the stat result if the file was found in the directory.
428 and st is the stat result if the file was found in the directory.
427 '''
429 '''
428 self.lazyread()
430 self.lazyread()
429
431
430 # walk all files by default
432 # walk all files by default
431 if not files:
433 if not files:
432 files = ['.']
434 files = ['.']
433 dc = self.map.copy()
435 dc = self.map.copy()
434 else:
436 else:
435 files = util.unique(files)
437 files = util.unique(files)
436 dc = self.filterfiles(files)
438 dc = self.filterfiles(files)
437
439
438 def imatch(file_):
440 def imatch(file_):
439 if file_ not in dc and self.ignore(file_):
441 if file_ not in dc and self.ignore(file_):
440 return False
442 return False
441 return match(file_)
443 return match(file_)
442
444
443 ignore = self.ignore
445 ignore = self.ignore
444 if ignored:
446 if ignored:
445 imatch = match
447 imatch = match
446 ignore = util.never
448 ignore = util.never
447
449
448 # self.root may end with a path separator when self.root == '/'
450 # self.root may end with a path separator when self.root == '/'
449 common_prefix_len = len(self.root)
451 common_prefix_len = len(self.root)
450 if not self.root.endswith(os.sep):
452 if not self.root.endswith(os.sep):
451 common_prefix_len += 1
453 common_prefix_len += 1
452 # recursion free walker, faster than os.walk.
454 # recursion free walker, faster than os.walk.
453 def findfiles(s):
455 def findfiles(s):
454 work = [s]
456 work = [s]
455 if directories:
457 if directories:
456 yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s)
458 yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s)
457 while work:
459 while work:
458 top = work.pop()
460 top = work.pop()
459 names = os.listdir(top)
461 names = os.listdir(top)
460 names.sort()
462 names.sort()
461 # nd is the top of the repository dir tree
463 # nd is the top of the repository dir tree
462 nd = util.normpath(top[common_prefix_len:])
464 nd = util.normpath(top[common_prefix_len:])
463 if nd == '.':
465 if nd == '.':
464 nd = ''
466 nd = ''
465 else:
467 else:
466 # do not recurse into a repo contained in this
468 # do not recurse into a repo contained in this
467 # one. use bisect to find .hg directory so speed
469 # one. use bisect to find .hg directory so speed
468 # is good on big directory.
470 # is good on big directory.
469 hg = bisect.bisect_left(names, '.hg')
471 hg = bisect.bisect_left(names, '.hg')
470 if hg < len(names) and names[hg] == '.hg':
472 if hg < len(names) and names[hg] == '.hg':
471 if os.path.isdir(os.path.join(top, '.hg')):
473 if os.path.isdir(os.path.join(top, '.hg')):
472 continue
474 continue
473 for f in names:
475 for f in names:
474 np = util.pconvert(os.path.join(nd, f))
476 np = util.pconvert(os.path.join(nd, f))
475 if seen(np):
477 if seen(np):
476 continue
478 continue
477 p = os.path.join(top, f)
479 p = os.path.join(top, f)
478 # don't trip over symlinks
480 # don't trip over symlinks
479 st = os.lstat(p)
481 st = os.lstat(p)
480 if stat.S_ISDIR(st.st_mode):
482 if stat.S_ISDIR(st.st_mode):
481 if not ignore(np):
483 if not ignore(np):
482 work.append(p)
484 work.append(p)
483 if directories:
485 if directories:
484 yield 'd', np, st
486 yield 'd', np, st
485 if imatch(np) and np in dc:
487 if imatch(np) and np in dc:
486 yield 'm', np, st
488 yield 'm', np, st
487 elif imatch(np):
489 elif imatch(np):
488 if self.supported_type(np, st):
490 if self.supported_type(np, st):
489 yield 'f', np, st
491 yield 'f', np, st
490 elif np in dc:
492 elif np in dc:
491 yield 'm', np, st
493 yield 'm', np, st
492
494
493 known = {'.hg': 1}
495 known = {'.hg': 1}
494 def seen(fn):
496 def seen(fn):
495 if fn in known: return True
497 if fn in known: return True
496 known[fn] = 1
498 known[fn] = 1
497
499
498 # step one, find all files that match our criteria
500 # step one, find all files that match our criteria
499 files.sort()
501 files.sort()
500 for ff in files:
502 for ff in files:
501 nf = util.normpath(ff)
503 nf = util.normpath(ff)
502 f = self.wjoin(ff)
504 f = self.wjoin(ff)
503 try:
505 try:
504 st = os.lstat(f)
506 st = os.lstat(f)
505 except OSError, inst:
507 except OSError, inst:
506 found = False
508 found = False
507 for fn in dc:
509 for fn in dc:
508 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
510 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
509 found = True
511 found = True
510 break
512 break
511 if not found:
513 if not found:
512 if inst.errno != errno.ENOENT or not badmatch:
514 if inst.errno != errno.ENOENT or not badmatch:
513 self.ui.warn('%s: %s\n' % (
515 self.ui.warn('%s: %s\n' % (
514 util.pathto(self.root, self.getcwd(), ff),
516 util.pathto(self.root, self.getcwd(), ff),
515 inst.strerror))
517 inst.strerror))
516 elif badmatch and badmatch(ff) and imatch(nf):
518 elif badmatch and badmatch(ff) and imatch(nf):
517 yield 'b', ff, None
519 yield 'b', ff, None
518 continue
520 continue
519 if stat.S_ISDIR(st.st_mode):
521 if stat.S_ISDIR(st.st_mode):
520 cmp1 = (lambda x, y: cmp(x[1], y[1]))
522 cmp1 = (lambda x, y: cmp(x[1], y[1]))
521 sorted_ = [ x for x in findfiles(f) ]
523 sorted_ = [ x for x in findfiles(f) ]
522 sorted_.sort(cmp1)
524 sorted_.sort(cmp1)
523 for e in sorted_:
525 for e in sorted_:
524 yield e
526 yield e
525 else:
527 else:
526 if not seen(nf) and match(nf):
528 if not seen(nf) and match(nf):
527 if self.supported_type(ff, st, verbose=True):
529 if self.supported_type(ff, st, verbose=True):
528 yield 'f', nf, st
530 yield 'f', nf, st
529 elif ff in dc:
531 elif ff in dc:
530 yield 'm', nf, st
532 yield 'm', nf, st
531
533
532 # step two run through anything left in the dc hash and yield
534 # step two run through anything left in the dc hash and yield
533 # if we haven't already seen it
535 # if we haven't already seen it
534 ks = dc.keys()
536 ks = dc.keys()
535 ks.sort()
537 ks.sort()
536 for k in ks:
538 for k in ks:
537 if not seen(k) and imatch(k):
539 if not seen(k) and imatch(k):
538 yield 'm', k, None
540 yield 'm', k, None
539
541
540 def status(self, files=None, match=util.always, list_ignored=False,
542 def status(self, files=None, match=util.always, list_ignored=False,
541 list_clean=False):
543 list_clean=False):
542 lookup, modified, added, unknown, ignored = [], [], [], [], []
544 lookup, modified, added, unknown, ignored = [], [], [], [], []
543 removed, deleted, clean = [], [], []
545 removed, deleted, clean = [], [], []
544
546
545 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
547 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
546 try:
548 try:
547 type_, mode, size, time = self[fn]
549 type_, mode, size, time = self[fn]
548 except KeyError:
550 except KeyError:
549 if list_ignored and self.ignore(fn):
551 if list_ignored and self.ignore(fn):
550 ignored.append(fn)
552 ignored.append(fn)
551 else:
553 else:
552 unknown.append(fn)
554 unknown.append(fn)
553 continue
555 continue
554 if src == 'm':
556 if src == 'm':
555 nonexistent = True
557 nonexistent = True
556 if not st:
558 if not st:
557 try:
559 try:
558 st = os.lstat(self.wjoin(fn))
560 st = os.lstat(self.wjoin(fn))
559 except OSError, inst:
561 except OSError, inst:
560 if inst.errno != errno.ENOENT:
562 if inst.errno != errno.ENOENT:
561 raise
563 raise
562 st = None
564 st = None
563 # We need to re-check that it is a valid file
565 # We need to re-check that it is a valid file
564 if st and self.supported_type(fn, st):
566 if st and self.supported_type(fn, st):
565 nonexistent = False
567 nonexistent = False
566 # XXX: what to do with file no longer present in the fs
568 # XXX: what to do with file no longer present in the fs
567 # who are not removed in the dirstate ?
569 # who are not removed in the dirstate ?
568 if nonexistent and type_ in "nm":
570 if nonexistent and type_ in "nm":
569 deleted.append(fn)
571 deleted.append(fn)
570 continue
572 continue
571 # check the common case first
573 # check the common case first
572 if type_ == 'n':
574 if type_ == 'n':
573 if not st:
575 if not st:
574 st = os.lstat(self.wjoin(fn))
576 st = os.lstat(self.wjoin(fn))
575 if size >= 0 and (size != st.st_size
577 if size >= 0 and (size != st.st_size
576 or (mode ^ st.st_mode) & 0100):
578 or (mode ^ st.st_mode) & 0100):
577 modified.append(fn)
579 modified.append(fn)
578 elif time != int(st.st_mtime):
580 elif time != int(st.st_mtime):
579 lookup.append(fn)
581 lookup.append(fn)
580 elif list_clean:
582 elif list_clean:
581 clean.append(fn)
583 clean.append(fn)
582 elif type_ == 'm':
584 elif type_ == 'm':
583 modified.append(fn)
585 modified.append(fn)
584 elif type_ == 'a':
586 elif type_ == 'a':
585 added.append(fn)
587 added.append(fn)
586 elif type_ == 'r':
588 elif type_ == 'r':
587 removed.append(fn)
589 removed.append(fn)
588
590
589 return (lookup, modified, added, removed, deleted, unknown, ignored,
591 return (lookup, modified, added, removed, deleted, unknown, ignored,
590 clean)
592 clean)
@@ -1,668 +1,671 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 from node import *
9 from node import *
10 import base85, cmdutil, mdiff, util, context, revlog
10 import base85, cmdutil, mdiff, util, context, revlog
11 import cStringIO, email.Parser, os, popen2, re, sha
11 import cStringIO, email.Parser, os, popen2, re, sha
12 import sys, tempfile, zlib
12 import sys, tempfile, zlib
13
13
14 # helper functions
14 # helper functions
15
15
16 def copyfile(src, dst, basedir=None):
16 def copyfile(src, dst, basedir=None):
17 if not basedir:
17 if not basedir:
18 basedir = os.getcwd()
18 basedir = os.getcwd()
19
19
20 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
20 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 if os.path.exists(absdst):
21 if os.path.exists(absdst):
22 raise util.Abort(_("cannot create %s: destination already exists") %
22 raise util.Abort(_("cannot create %s: destination already exists") %
23 dst)
23 dst)
24
24
25 targetdir = os.path.dirname(absdst)
25 targetdir = os.path.dirname(absdst)
26 if not os.path.isdir(targetdir):
26 if not os.path.isdir(targetdir):
27 os.makedirs(targetdir)
27 os.makedirs(targetdir)
28
28
29 util.copyfile(abssrc, absdst)
29 util.copyfile(abssrc, absdst)
30
30
31 # public functions
31 # public functions
32
32
33 def extract(ui, fileobj):
33 def extract(ui, fileobj):
34 '''extract patch from data read from fileobj.
34 '''extract patch from data read from fileobj.
35
35
36 patch can be a normal patch or contained in an email message.
36 patch can be a normal patch or contained in an email message.
37
37
38 return tuple (filename, message, user, date, node, p1, p2).
38 return tuple (filename, message, user, date, node, p1, p2).
39 Any item in the returned tuple can be None. If filename is None,
39 Any item in the returned tuple can be None. If filename is None,
40 fileobj did not contain a patch. Caller must unlink filename when done.'''
40 fileobj did not contain a patch. Caller must unlink filename when done.'''
41
41
42 # attempt to detect the start of a patch
42 # attempt to detect the start of a patch
43 # (this heuristic is borrowed from quilt)
43 # (this heuristic is borrowed from quilt)
44 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
44 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
45 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
45 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
46 '(---|\*\*\*)[ \t])', re.MULTILINE)
46 '(---|\*\*\*)[ \t])', re.MULTILINE)
47
47
48 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
48 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
49 tmpfp = os.fdopen(fd, 'w')
49 tmpfp = os.fdopen(fd, 'w')
50 try:
50 try:
51 msg = email.Parser.Parser().parse(fileobj)
51 msg = email.Parser.Parser().parse(fileobj)
52
52
53 message = msg['Subject']
53 message = msg['Subject']
54 user = msg['From']
54 user = msg['From']
55 # should try to parse msg['Date']
55 # should try to parse msg['Date']
56 date = None
56 date = None
57 nodeid = None
57 nodeid = None
58 branch = None
58 parents = []
59 parents = []
59
60
60 if message:
61 if message:
61 if message.startswith('[PATCH'):
62 if message.startswith('[PATCH'):
62 pend = message.find(']')
63 pend = message.find(']')
63 if pend >= 0:
64 if pend >= 0:
64 message = message[pend+1:].lstrip()
65 message = message[pend+1:].lstrip()
65 message = message.replace('\n\t', ' ')
66 message = message.replace('\n\t', ' ')
66 ui.debug('Subject: %s\n' % message)
67 ui.debug('Subject: %s\n' % message)
67 if user:
68 if user:
68 ui.debug('From: %s\n' % user)
69 ui.debug('From: %s\n' % user)
69 diffs_seen = 0
70 diffs_seen = 0
70 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
71 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
71
72
72 for part in msg.walk():
73 for part in msg.walk():
73 content_type = part.get_content_type()
74 content_type = part.get_content_type()
74 ui.debug('Content-Type: %s\n' % content_type)
75 ui.debug('Content-Type: %s\n' % content_type)
75 if content_type not in ok_types:
76 if content_type not in ok_types:
76 continue
77 continue
77 payload = part.get_payload(decode=True)
78 payload = part.get_payload(decode=True)
78 m = diffre.search(payload)
79 m = diffre.search(payload)
79 if m:
80 if m:
80 hgpatch = False
81 hgpatch = False
81 ignoretext = False
82 ignoretext = False
82
83
83 ui.debug(_('found patch at byte %d\n') % m.start(0))
84 ui.debug(_('found patch at byte %d\n') % m.start(0))
84 diffs_seen += 1
85 diffs_seen += 1
85 cfp = cStringIO.StringIO()
86 cfp = cStringIO.StringIO()
86 if message:
87 if message:
87 cfp.write(message)
88 cfp.write(message)
88 cfp.write('\n')
89 cfp.write('\n')
89 for line in payload[:m.start(0)].splitlines():
90 for line in payload[:m.start(0)].splitlines():
90 if line.startswith('# HG changeset patch'):
91 if line.startswith('# HG changeset patch'):
91 ui.debug(_('patch generated by hg export\n'))
92 ui.debug(_('patch generated by hg export\n'))
92 hgpatch = True
93 hgpatch = True
93 # drop earlier commit message content
94 # drop earlier commit message content
94 cfp.seek(0)
95 cfp.seek(0)
95 cfp.truncate()
96 cfp.truncate()
96 elif hgpatch:
97 elif hgpatch:
97 if line.startswith('# User '):
98 if line.startswith('# User '):
98 user = line[7:]
99 user = line[7:]
99 ui.debug('From: %s\n' % user)
100 ui.debug('From: %s\n' % user)
100 elif line.startswith("# Date "):
101 elif line.startswith("# Date "):
101 date = line[7:]
102 date = line[7:]
103 elif line.startswith("# Branch "):
104 branch = line[9:]
102 elif line.startswith("# Node ID "):
105 elif line.startswith("# Node ID "):
103 nodeid = line[10:]
106 nodeid = line[10:]
104 elif line.startswith("# Parent "):
107 elif line.startswith("# Parent "):
105 parents.append(line[10:])
108 parents.append(line[10:])
106 elif line == '---' and 'git-send-email' in msg['X-Mailer']:
109 elif line == '---' and 'git-send-email' in msg['X-Mailer']:
107 ignoretext = True
110 ignoretext = True
108 if not line.startswith('# ') and not ignoretext:
111 if not line.startswith('# ') and not ignoretext:
109 cfp.write(line)
112 cfp.write(line)
110 cfp.write('\n')
113 cfp.write('\n')
111 message = cfp.getvalue()
114 message = cfp.getvalue()
112 if tmpfp:
115 if tmpfp:
113 tmpfp.write(payload)
116 tmpfp.write(payload)
114 if not payload.endswith('\n'):
117 if not payload.endswith('\n'):
115 tmpfp.write('\n')
118 tmpfp.write('\n')
116 elif not diffs_seen and message and content_type == 'text/plain':
119 elif not diffs_seen and message and content_type == 'text/plain':
117 message += '\n' + payload
120 message += '\n' + payload
118 except:
121 except:
119 tmpfp.close()
122 tmpfp.close()
120 os.unlink(tmpname)
123 os.unlink(tmpname)
121 raise
124 raise
122
125
123 tmpfp.close()
126 tmpfp.close()
124 if not diffs_seen:
127 if not diffs_seen:
125 os.unlink(tmpname)
128 os.unlink(tmpname)
126 return None, message, user, date, None, None, None
129 return None, message, user, date, branch, None, None, None
127 p1 = parents and parents.pop(0) or None
130 p1 = parents and parents.pop(0) or None
128 p2 = parents and parents.pop(0) or None
131 p2 = parents and parents.pop(0) or None
129 return tmpname, message, user, date, nodeid, p1, p2
132 return tmpname, message, user, date, branch, nodeid, p1, p2
130
133
131 GP_PATCH = 1 << 0 # we have to run patch
134 GP_PATCH = 1 << 0 # we have to run patch
132 GP_FILTER = 1 << 1 # there's some copy/rename operation
135 GP_FILTER = 1 << 1 # there's some copy/rename operation
133 GP_BINARY = 1 << 2 # there's a binary patch
136 GP_BINARY = 1 << 2 # there's a binary patch
134
137
135 def readgitpatch(patchname):
138 def readgitpatch(patchname):
136 """extract git-style metadata about patches from <patchname>"""
139 """extract git-style metadata about patches from <patchname>"""
137 class gitpatch:
140 class gitpatch:
138 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
141 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
139 def __init__(self, path):
142 def __init__(self, path):
140 self.path = path
143 self.path = path
141 self.oldpath = None
144 self.oldpath = None
142 self.mode = None
145 self.mode = None
143 self.op = 'MODIFY'
146 self.op = 'MODIFY'
144 self.copymod = False
147 self.copymod = False
145 self.lineno = 0
148 self.lineno = 0
146 self.binary = False
149 self.binary = False
147
150
148 # Filter patch for git information
151 # Filter patch for git information
149 gitre = re.compile('diff --git a/(.*) b/(.*)')
152 gitre = re.compile('diff --git a/(.*) b/(.*)')
150 pf = file(patchname)
153 pf = file(patchname)
151 gp = None
154 gp = None
152 gitpatches = []
155 gitpatches = []
153 # Can have a git patch with only metadata, causing patch to complain
156 # Can have a git patch with only metadata, causing patch to complain
154 dopatch = 0
157 dopatch = 0
155
158
156 lineno = 0
159 lineno = 0
157 for line in pf:
160 for line in pf:
158 lineno += 1
161 lineno += 1
159 if line.startswith('diff --git'):
162 if line.startswith('diff --git'):
160 m = gitre.match(line)
163 m = gitre.match(line)
161 if m:
164 if m:
162 if gp:
165 if gp:
163 gitpatches.append(gp)
166 gitpatches.append(gp)
164 src, dst = m.group(1, 2)
167 src, dst = m.group(1, 2)
165 gp = gitpatch(dst)
168 gp = gitpatch(dst)
166 gp.lineno = lineno
169 gp.lineno = lineno
167 elif gp:
170 elif gp:
168 if line.startswith('--- '):
171 if line.startswith('--- '):
169 if gp.op in ('COPY', 'RENAME'):
172 if gp.op in ('COPY', 'RENAME'):
170 gp.copymod = True
173 gp.copymod = True
171 dopatch |= GP_FILTER
174 dopatch |= GP_FILTER
172 gitpatches.append(gp)
175 gitpatches.append(gp)
173 gp = None
176 gp = None
174 dopatch |= GP_PATCH
177 dopatch |= GP_PATCH
175 continue
178 continue
176 if line.startswith('rename from '):
179 if line.startswith('rename from '):
177 gp.op = 'RENAME'
180 gp.op = 'RENAME'
178 gp.oldpath = line[12:].rstrip()
181 gp.oldpath = line[12:].rstrip()
179 elif line.startswith('rename to '):
182 elif line.startswith('rename to '):
180 gp.path = line[10:].rstrip()
183 gp.path = line[10:].rstrip()
181 elif line.startswith('copy from '):
184 elif line.startswith('copy from '):
182 gp.op = 'COPY'
185 gp.op = 'COPY'
183 gp.oldpath = line[10:].rstrip()
186 gp.oldpath = line[10:].rstrip()
184 elif line.startswith('copy to '):
187 elif line.startswith('copy to '):
185 gp.path = line[8:].rstrip()
188 gp.path = line[8:].rstrip()
186 elif line.startswith('deleted file'):
189 elif line.startswith('deleted file'):
187 gp.op = 'DELETE'
190 gp.op = 'DELETE'
188 elif line.startswith('new file mode '):
191 elif line.startswith('new file mode '):
189 gp.op = 'ADD'
192 gp.op = 'ADD'
190 gp.mode = int(line.rstrip()[-3:], 8)
193 gp.mode = int(line.rstrip()[-3:], 8)
191 elif line.startswith('new mode '):
194 elif line.startswith('new mode '):
192 gp.mode = int(line.rstrip()[-3:], 8)
195 gp.mode = int(line.rstrip()[-3:], 8)
193 elif line.startswith('GIT binary patch'):
196 elif line.startswith('GIT binary patch'):
194 dopatch |= GP_BINARY
197 dopatch |= GP_BINARY
195 gp.binary = True
198 gp.binary = True
196 if gp:
199 if gp:
197 gitpatches.append(gp)
200 gitpatches.append(gp)
198
201
199 if not gitpatches:
202 if not gitpatches:
200 dopatch = GP_PATCH
203 dopatch = GP_PATCH
201
204
202 return (dopatch, gitpatches)
205 return (dopatch, gitpatches)
203
206
204 def dogitpatch(patchname, gitpatches, cwd=None):
207 def dogitpatch(patchname, gitpatches, cwd=None):
205 """Preprocess git patch so that vanilla patch can handle it"""
208 """Preprocess git patch so that vanilla patch can handle it"""
206 def extractbin(fp):
209 def extractbin(fp):
207 i = [0] # yuck
210 i = [0] # yuck
208 def readline():
211 def readline():
209 i[0] += 1
212 i[0] += 1
210 return fp.readline().rstrip()
213 return fp.readline().rstrip()
211 line = readline()
214 line = readline()
212 while line and not line.startswith('literal '):
215 while line and not line.startswith('literal '):
213 line = readline()
216 line = readline()
214 if not line:
217 if not line:
215 return None, i[0]
218 return None, i[0]
216 size = int(line[8:])
219 size = int(line[8:])
217 dec = []
220 dec = []
218 line = readline()
221 line = readline()
219 while line:
222 while line:
220 l = line[0]
223 l = line[0]
221 if l <= 'Z' and l >= 'A':
224 if l <= 'Z' and l >= 'A':
222 l = ord(l) - ord('A') + 1
225 l = ord(l) - ord('A') + 1
223 else:
226 else:
224 l = ord(l) - ord('a') + 27
227 l = ord(l) - ord('a') + 27
225 dec.append(base85.b85decode(line[1:])[:l])
228 dec.append(base85.b85decode(line[1:])[:l])
226 line = readline()
229 line = readline()
227 text = zlib.decompress(''.join(dec))
230 text = zlib.decompress(''.join(dec))
228 if len(text) != size:
231 if len(text) != size:
229 raise util.Abort(_('binary patch is %d bytes, not %d') %
232 raise util.Abort(_('binary patch is %d bytes, not %d') %
230 (len(text), size))
233 (len(text), size))
231 return text, i[0]
234 return text, i[0]
232
235
233 pf = file(patchname)
236 pf = file(patchname)
234 pfline = 1
237 pfline = 1
235
238
236 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
239 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
237 tmpfp = os.fdopen(fd, 'w')
240 tmpfp = os.fdopen(fd, 'w')
238
241
239 try:
242 try:
240 for i in xrange(len(gitpatches)):
243 for i in xrange(len(gitpatches)):
241 p = gitpatches[i]
244 p = gitpatches[i]
242 if not p.copymod and not p.binary:
245 if not p.copymod and not p.binary:
243 continue
246 continue
244
247
245 # rewrite patch hunk
248 # rewrite patch hunk
246 while pfline < p.lineno:
249 while pfline < p.lineno:
247 tmpfp.write(pf.readline())
250 tmpfp.write(pf.readline())
248 pfline += 1
251 pfline += 1
249
252
250 if p.binary:
253 if p.binary:
251 text, delta = extractbin(pf)
254 text, delta = extractbin(pf)
252 if not text:
255 if not text:
253 raise util.Abort(_('binary patch extraction failed'))
256 raise util.Abort(_('binary patch extraction failed'))
254 pfline += delta
257 pfline += delta
255 if not cwd:
258 if not cwd:
256 cwd = os.getcwd()
259 cwd = os.getcwd()
257 absdst = os.path.join(cwd, p.path)
260 absdst = os.path.join(cwd, p.path)
258 basedir = os.path.dirname(absdst)
261 basedir = os.path.dirname(absdst)
259 if not os.path.isdir(basedir):
262 if not os.path.isdir(basedir):
260 os.makedirs(basedir)
263 os.makedirs(basedir)
261 out = file(absdst, 'wb')
264 out = file(absdst, 'wb')
262 out.write(text)
265 out.write(text)
263 out.close()
266 out.close()
264 elif p.copymod:
267 elif p.copymod:
265 copyfile(p.oldpath, p.path, basedir=cwd)
268 copyfile(p.oldpath, p.path, basedir=cwd)
266 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
269 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
267 line = pf.readline()
270 line = pf.readline()
268 pfline += 1
271 pfline += 1
269 while not line.startswith('--- a/'):
272 while not line.startswith('--- a/'):
270 tmpfp.write(line)
273 tmpfp.write(line)
271 line = pf.readline()
274 line = pf.readline()
272 pfline += 1
275 pfline += 1
273 tmpfp.write('--- a/%s\n' % p.path)
276 tmpfp.write('--- a/%s\n' % p.path)
274
277
275 line = pf.readline()
278 line = pf.readline()
276 while line:
279 while line:
277 tmpfp.write(line)
280 tmpfp.write(line)
278 line = pf.readline()
281 line = pf.readline()
279 except:
282 except:
280 tmpfp.close()
283 tmpfp.close()
281 os.unlink(patchname)
284 os.unlink(patchname)
282 raise
285 raise
283
286
284 tmpfp.close()
287 tmpfp.close()
285 return patchname
288 return patchname
286
289
287 def patch(patchname, ui, strip=1, cwd=None, files={}):
290 def patch(patchname, ui, strip=1, cwd=None, files={}):
288 """apply the patch <patchname> to the working directory.
291 """apply the patch <patchname> to the working directory.
289 a list of patched files is returned"""
292 a list of patched files is returned"""
290
293
291 # helper function
294 # helper function
292 def __patch(patchname):
295 def __patch(patchname):
293 """patch and updates the files and fuzz variables"""
296 """patch and updates the files and fuzz variables"""
294 fuzz = False
297 fuzz = False
295
298
296 args = []
299 args = []
297 patcher = ui.config('ui', 'patch')
300 patcher = ui.config('ui', 'patch')
298 patcher = ((patcher and util.find_exe(patcher)) or
301 patcher = ((patcher and util.find_exe(patcher)) or
299 util.find_exe('gpatch') or
302 util.find_exe('gpatch') or
300 util.find_exe('patch'))
303 util.find_exe('patch'))
301 if not patcher:
304 if not patcher:
302 raise util.Abort(_('no patch command found in hgrc or PATH'))
305 raise util.Abort(_('no patch command found in hgrc or PATH'))
303 if util.needbinarypatch():
306 if util.needbinarypatch():
304 args.append('--binary')
307 args.append('--binary')
305
308
306 if cwd:
309 if cwd:
307 args.append('-d %s' % util.shellquote(cwd))
310 args.append('-d %s' % util.shellquote(cwd))
308 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
311 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
309 util.shellquote(patchname)))
312 util.shellquote(patchname)))
310
313
311 for line in fp:
314 for line in fp:
312 line = line.rstrip()
315 line = line.rstrip()
313 ui.note(line + '\n')
316 ui.note(line + '\n')
314 if line.startswith('patching file '):
317 if line.startswith('patching file '):
315 pf = util.parse_patch_output(line)
318 pf = util.parse_patch_output(line)
316 printed_file = False
319 printed_file = False
317 files.setdefault(pf, (None, None))
320 files.setdefault(pf, (None, None))
318 elif line.find('with fuzz') >= 0:
321 elif line.find('with fuzz') >= 0:
319 fuzz = True
322 fuzz = True
320 if not printed_file:
323 if not printed_file:
321 ui.warn(pf + '\n')
324 ui.warn(pf + '\n')
322 printed_file = True
325 printed_file = True
323 ui.warn(line + '\n')
326 ui.warn(line + '\n')
324 elif line.find('saving rejects to file') >= 0:
327 elif line.find('saving rejects to file') >= 0:
325 ui.warn(line + '\n')
328 ui.warn(line + '\n')
326 elif line.find('FAILED') >= 0:
329 elif line.find('FAILED') >= 0:
327 if not printed_file:
330 if not printed_file:
328 ui.warn(pf + '\n')
331 ui.warn(pf + '\n')
329 printed_file = True
332 printed_file = True
330 ui.warn(line + '\n')
333 ui.warn(line + '\n')
331 code = fp.close()
334 code = fp.close()
332 if code:
335 if code:
333 raise util.Abort(_("patch command failed: %s") %
336 raise util.Abort(_("patch command failed: %s") %
334 util.explain_exit(code)[0])
337 util.explain_exit(code)[0])
335 return fuzz
338 return fuzz
336
339
337 (dopatch, gitpatches) = readgitpatch(patchname)
340 (dopatch, gitpatches) = readgitpatch(patchname)
338 for gp in gitpatches:
341 for gp in gitpatches:
339 files[gp.path] = (gp.op, gp)
342 files[gp.path] = (gp.op, gp)
340
343
341 fuzz = False
344 fuzz = False
342 if dopatch:
345 if dopatch:
343 filterpatch = dopatch & (GP_FILTER | GP_BINARY)
346 filterpatch = dopatch & (GP_FILTER | GP_BINARY)
344 if filterpatch:
347 if filterpatch:
345 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
348 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
346 try:
349 try:
347 if dopatch & GP_PATCH:
350 if dopatch & GP_PATCH:
348 fuzz = __patch(patchname)
351 fuzz = __patch(patchname)
349 finally:
352 finally:
350 if filterpatch:
353 if filterpatch:
351 os.unlink(patchname)
354 os.unlink(patchname)
352
355
353 return fuzz
356 return fuzz
354
357
355 def diffopts(ui, opts={}, untrusted=False):
358 def diffopts(ui, opts={}, untrusted=False):
356 def get(key, name=None):
359 def get(key, name=None):
357 return (opts.get(key) or
360 return (opts.get(key) or
358 ui.configbool('diff', name or key, None, untrusted=untrusted))
361 ui.configbool('diff', name or key, None, untrusted=untrusted))
359 return mdiff.diffopts(
362 return mdiff.diffopts(
360 text=opts.get('text'),
363 text=opts.get('text'),
361 git=get('git'),
364 git=get('git'),
362 nodates=get('nodates'),
365 nodates=get('nodates'),
363 showfunc=get('show_function', 'showfunc'),
366 showfunc=get('show_function', 'showfunc'),
364 ignorews=get('ignore_all_space', 'ignorews'),
367 ignorews=get('ignore_all_space', 'ignorews'),
365 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
368 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
366 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
369 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
367
370
368 def updatedir(ui, repo, patches, wlock=None):
371 def updatedir(ui, repo, patches, wlock=None):
369 '''Update dirstate after patch application according to metadata'''
372 '''Update dirstate after patch application according to metadata'''
370 if not patches:
373 if not patches:
371 return
374 return
372 copies = []
375 copies = []
373 removes = {}
376 removes = {}
374 cfiles = patches.keys()
377 cfiles = patches.keys()
375 cwd = repo.getcwd()
378 cwd = repo.getcwd()
376 if cwd:
379 if cwd:
377 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
380 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
378 for f in patches:
381 for f in patches:
379 ctype, gp = patches[f]
382 ctype, gp = patches[f]
380 if ctype == 'RENAME':
383 if ctype == 'RENAME':
381 copies.append((gp.oldpath, gp.path, gp.copymod))
384 copies.append((gp.oldpath, gp.path, gp.copymod))
382 removes[gp.oldpath] = 1
385 removes[gp.oldpath] = 1
383 elif ctype == 'COPY':
386 elif ctype == 'COPY':
384 copies.append((gp.oldpath, gp.path, gp.copymod))
387 copies.append((gp.oldpath, gp.path, gp.copymod))
385 elif ctype == 'DELETE':
388 elif ctype == 'DELETE':
386 removes[gp.path] = 1
389 removes[gp.path] = 1
387 for src, dst, after in copies:
390 for src, dst, after in copies:
388 if not after:
391 if not after:
389 copyfile(src, dst, repo.root)
392 copyfile(src, dst, repo.root)
390 repo.copy(src, dst, wlock=wlock)
393 repo.copy(src, dst, wlock=wlock)
391 removes = removes.keys()
394 removes = removes.keys()
392 if removes:
395 if removes:
393 removes.sort()
396 removes.sort()
394 repo.remove(removes, True, wlock=wlock)
397 repo.remove(removes, True, wlock=wlock)
395 for f in patches:
398 for f in patches:
396 ctype, gp = patches[f]
399 ctype, gp = patches[f]
397 if gp and gp.mode:
400 if gp and gp.mode:
398 x = gp.mode & 0100 != 0
401 x = gp.mode & 0100 != 0
399 dst = os.path.join(repo.root, gp.path)
402 dst = os.path.join(repo.root, gp.path)
400 # patch won't create empty files
403 # patch won't create empty files
401 if ctype == 'ADD' and not os.path.exists(dst):
404 if ctype == 'ADD' and not os.path.exists(dst):
402 repo.wwrite(gp.path, '', x and 'x' or '')
405 repo.wwrite(gp.path, '', x and 'x' or '')
403 else:
406 else:
404 util.set_exec(dst, x)
407 util.set_exec(dst, x)
405 cmdutil.addremove(repo, cfiles, wlock=wlock)
408 cmdutil.addremove(repo, cfiles, wlock=wlock)
406 files = patches.keys()
409 files = patches.keys()
407 files.extend([r for r in removes if r not in files])
410 files.extend([r for r in removes if r not in files])
408 files.sort()
411 files.sort()
409
412
410 return files
413 return files
411
414
412 def b85diff(fp, to, tn):
415 def b85diff(fp, to, tn):
413 '''print base85-encoded binary diff'''
416 '''print base85-encoded binary diff'''
414 def gitindex(text):
417 def gitindex(text):
415 if not text:
418 if not text:
416 return '0' * 40
419 return '0' * 40
417 l = len(text)
420 l = len(text)
418 s = sha.new('blob %d\0' % l)
421 s = sha.new('blob %d\0' % l)
419 s.update(text)
422 s.update(text)
420 return s.hexdigest()
423 return s.hexdigest()
421
424
422 def fmtline(line):
425 def fmtline(line):
423 l = len(line)
426 l = len(line)
424 if l <= 26:
427 if l <= 26:
425 l = chr(ord('A') + l - 1)
428 l = chr(ord('A') + l - 1)
426 else:
429 else:
427 l = chr(l - 26 + ord('a') - 1)
430 l = chr(l - 26 + ord('a') - 1)
428 return '%c%s\n' % (l, base85.b85encode(line, True))
431 return '%c%s\n' % (l, base85.b85encode(line, True))
429
432
430 def chunk(text, csize=52):
433 def chunk(text, csize=52):
431 l = len(text)
434 l = len(text)
432 i = 0
435 i = 0
433 while i < l:
436 while i < l:
434 yield text[i:i+csize]
437 yield text[i:i+csize]
435 i += csize
438 i += csize
436
439
437 tohash = gitindex(to)
440 tohash = gitindex(to)
438 tnhash = gitindex(tn)
441 tnhash = gitindex(tn)
439 if tohash == tnhash:
442 if tohash == tnhash:
440 return ""
443 return ""
441
444
442 # TODO: deltas
445 # TODO: deltas
443 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
446 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
444 (tohash, tnhash, len(tn))]
447 (tohash, tnhash, len(tn))]
445 for l in chunk(zlib.compress(tn)):
448 for l in chunk(zlib.compress(tn)):
446 ret.append(fmtline(l))
449 ret.append(fmtline(l))
447 ret.append('\n')
450 ret.append('\n')
448 return ''.join(ret)
451 return ''.join(ret)
449
452
450 def diff(repo, node1=None, node2=None, files=None, match=util.always,
453 def diff(repo, node1=None, node2=None, files=None, match=util.always,
451 fp=None, changes=None, opts=None):
454 fp=None, changes=None, opts=None):
452 '''print diff of changes to files between two nodes, or node and
455 '''print diff of changes to files between two nodes, or node and
453 working directory.
456 working directory.
454
457
455 if node1 is None, use first dirstate parent instead.
458 if node1 is None, use first dirstate parent instead.
456 if node2 is None, compare node1 with working directory.'''
459 if node2 is None, compare node1 with working directory.'''
457
460
458 if opts is None:
461 if opts is None:
459 opts = mdiff.defaultopts
462 opts = mdiff.defaultopts
460 if fp is None:
463 if fp is None:
461 fp = repo.ui
464 fp = repo.ui
462
465
463 if not node1:
466 if not node1:
464 node1 = repo.dirstate.parents()[0]
467 node1 = repo.dirstate.parents()[0]
465
468
466 ccache = {}
469 ccache = {}
467 def getctx(r):
470 def getctx(r):
468 if r not in ccache:
471 if r not in ccache:
469 ccache[r] = context.changectx(repo, r)
472 ccache[r] = context.changectx(repo, r)
470 return ccache[r]
473 return ccache[r]
471
474
472 flcache = {}
475 flcache = {}
473 def getfilectx(f, ctx):
476 def getfilectx(f, ctx):
474 flctx = ctx.filectx(f, filelog=flcache.get(f))
477 flctx = ctx.filectx(f, filelog=flcache.get(f))
475 if f not in flcache:
478 if f not in flcache:
476 flcache[f] = flctx._filelog
479 flcache[f] = flctx._filelog
477 return flctx
480 return flctx
478
481
479 # reading the data for node1 early allows it to play nicely
482 # reading the data for node1 early allows it to play nicely
480 # with repo.status and the revlog cache.
483 # with repo.status and the revlog cache.
481 ctx1 = context.changectx(repo, node1)
484 ctx1 = context.changectx(repo, node1)
482 # force manifest reading
485 # force manifest reading
483 man1 = ctx1.manifest()
486 man1 = ctx1.manifest()
484 date1 = util.datestr(ctx1.date())
487 date1 = util.datestr(ctx1.date())
485
488
486 if not changes:
489 if not changes:
487 changes = repo.status(node1, node2, files, match=match)[:5]
490 changes = repo.status(node1, node2, files, match=match)[:5]
488 modified, added, removed, deleted, unknown = changes
491 modified, added, removed, deleted, unknown = changes
489
492
490 if not modified and not added and not removed:
493 if not modified and not added and not removed:
491 return
494 return
492
495
493 if node2:
496 if node2:
494 ctx2 = context.changectx(repo, node2)
497 ctx2 = context.changectx(repo, node2)
495 else:
498 else:
496 ctx2 = context.workingctx(repo)
499 ctx2 = context.workingctx(repo)
497 man2 = ctx2.manifest()
500 man2 = ctx2.manifest()
498
501
499 # returns False if there was no rename between ctx1 and ctx2
502 # returns False if there was no rename between ctx1 and ctx2
500 # returns None if the file was created between ctx1 and ctx2
503 # returns None if the file was created between ctx1 and ctx2
501 # returns the (file, node) present in ctx1 that was renamed to f in ctx2
504 # returns the (file, node) present in ctx1 that was renamed to f in ctx2
502 def renamed(f):
505 def renamed(f):
503 startrev = ctx1.rev()
506 startrev = ctx1.rev()
504 c = ctx2
507 c = ctx2
505 crev = c.rev()
508 crev = c.rev()
506 if crev is None:
509 if crev is None:
507 crev = repo.changelog.count()
510 crev = repo.changelog.count()
508 orig = f
511 orig = f
509 while crev > startrev:
512 while crev > startrev:
510 if f in c.files():
513 if f in c.files():
511 try:
514 try:
512 src = getfilectx(f, c).renamed()
515 src = getfilectx(f, c).renamed()
513 except revlog.LookupError:
516 except revlog.LookupError:
514 return None
517 return None
515 if src:
518 if src:
516 f = src[0]
519 f = src[0]
517 crev = c.parents()[0].rev()
520 crev = c.parents()[0].rev()
518 # try to reuse
521 # try to reuse
519 c = getctx(crev)
522 c = getctx(crev)
520 if f not in man1:
523 if f not in man1:
521 return None
524 return None
522 if f == orig:
525 if f == orig:
523 return False
526 return False
524 return f
527 return f
525
528
526 if repo.ui.quiet:
529 if repo.ui.quiet:
527 r = None
530 r = None
528 else:
531 else:
529 hexfunc = repo.ui.debugflag and hex or short
532 hexfunc = repo.ui.debugflag and hex or short
530 r = [hexfunc(node) for node in [node1, node2] if node]
533 r = [hexfunc(node) for node in [node1, node2] if node]
531
534
532 if opts.git:
535 if opts.git:
533 copied = {}
536 copied = {}
534 for f in added:
537 for f in added:
535 src = renamed(f)
538 src = renamed(f)
536 if src:
539 if src:
537 copied[f] = src
540 copied[f] = src
538 srcs = [x[1] for x in copied.items()]
541 srcs = [x[1] for x in copied.items()]
539
542
540 all = modified + added + removed
543 all = modified + added + removed
541 all.sort()
544 all.sort()
542 gone = {}
545 gone = {}
543
546
544 for f in all:
547 for f in all:
545 to = None
548 to = None
546 tn = None
549 tn = None
547 dodiff = True
550 dodiff = True
548 header = []
551 header = []
549 if f in man1:
552 if f in man1:
550 to = getfilectx(f, ctx1).data()
553 to = getfilectx(f, ctx1).data()
551 if f not in removed:
554 if f not in removed:
552 tn = getfilectx(f, ctx2).data()
555 tn = getfilectx(f, ctx2).data()
553 if opts.git:
556 if opts.git:
554 def gitmode(x):
557 def gitmode(x):
555 return x and '100755' or '100644'
558 return x and '100755' or '100644'
556 def addmodehdr(header, omode, nmode):
559 def addmodehdr(header, omode, nmode):
557 if omode != nmode:
560 if omode != nmode:
558 header.append('old mode %s\n' % omode)
561 header.append('old mode %s\n' % omode)
559 header.append('new mode %s\n' % nmode)
562 header.append('new mode %s\n' % nmode)
560
563
561 a, b = f, f
564 a, b = f, f
562 if f in added:
565 if f in added:
563 mode = gitmode(man2.execf(f))
566 mode = gitmode(man2.execf(f))
564 if f in copied:
567 if f in copied:
565 a = copied[f]
568 a = copied[f]
566 omode = gitmode(man1.execf(a))
569 omode = gitmode(man1.execf(a))
567 addmodehdr(header, omode, mode)
570 addmodehdr(header, omode, mode)
568 if a in removed and a not in gone:
571 if a in removed and a not in gone:
569 op = 'rename'
572 op = 'rename'
570 gone[a] = 1
573 gone[a] = 1
571 else:
574 else:
572 op = 'copy'
575 op = 'copy'
573 header.append('%s from %s\n' % (op, a))
576 header.append('%s from %s\n' % (op, a))
574 header.append('%s to %s\n' % (op, f))
577 header.append('%s to %s\n' % (op, f))
575 to = getfilectx(a, ctx1).data()
578 to = getfilectx(a, ctx1).data()
576 else:
579 else:
577 header.append('new file mode %s\n' % mode)
580 header.append('new file mode %s\n' % mode)
578 if util.binary(tn):
581 if util.binary(tn):
579 dodiff = 'binary'
582 dodiff = 'binary'
580 elif f in removed:
583 elif f in removed:
581 if f in srcs:
584 if f in srcs:
582 dodiff = False
585 dodiff = False
583 else:
586 else:
584 mode = gitmode(man1.execf(f))
587 mode = gitmode(man1.execf(f))
585 header.append('deleted file mode %s\n' % mode)
588 header.append('deleted file mode %s\n' % mode)
586 else:
589 else:
587 omode = gitmode(man1.execf(f))
590 omode = gitmode(man1.execf(f))
588 nmode = gitmode(man2.execf(f))
591 nmode = gitmode(man2.execf(f))
589 addmodehdr(header, omode, nmode)
592 addmodehdr(header, omode, nmode)
590 if util.binary(to) or util.binary(tn):
593 if util.binary(to) or util.binary(tn):
591 dodiff = 'binary'
594 dodiff = 'binary'
592 r = None
595 r = None
593 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
596 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
594 if dodiff:
597 if dodiff:
595 if dodiff == 'binary':
598 if dodiff == 'binary':
596 text = b85diff(fp, to, tn)
599 text = b85diff(fp, to, tn)
597 else:
600 else:
598 text = mdiff.unidiff(to, date1,
601 text = mdiff.unidiff(to, date1,
599 # ctx2 date may be dynamic
602 # ctx2 date may be dynamic
600 tn, util.datestr(ctx2.date()),
603 tn, util.datestr(ctx2.date()),
601 f, r, opts=opts)
604 f, r, opts=opts)
602 if text or len(header) > 1:
605 if text or len(header) > 1:
603 fp.write(''.join(header))
606 fp.write(''.join(header))
604 fp.write(text)
607 fp.write(text)
605
608
606 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
609 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
607 opts=None):
610 opts=None):
608 '''export changesets as hg patches.'''
611 '''export changesets as hg patches.'''
609
612
610 total = len(revs)
613 total = len(revs)
611 revwidth = max([len(str(rev)) for rev in revs])
614 revwidth = max([len(str(rev)) for rev in revs])
612
615
613 def single(rev, seqno, fp):
616 def single(rev, seqno, fp):
614 ctx = repo.changectx(rev)
617 ctx = repo.changectx(rev)
615 node = ctx.node()
618 node = ctx.node()
616 parents = [p.node() for p in ctx.parents() if p]
619 parents = [p.node() for p in ctx.parents() if p]
617 branch = ctx.branch()
620 branch = ctx.branch()
618 if switch_parent:
621 if switch_parent:
619 parents.reverse()
622 parents.reverse()
620 prev = (parents and parents[0]) or nullid
623 prev = (parents and parents[0]) or nullid
621
624
622 if not fp:
625 if not fp:
623 fp = cmdutil.make_file(repo, template, node, total=total,
626 fp = cmdutil.make_file(repo, template, node, total=total,
624 seqno=seqno, revwidth=revwidth)
627 seqno=seqno, revwidth=revwidth)
625 if fp != sys.stdout and hasattr(fp, 'name'):
628 if fp != sys.stdout and hasattr(fp, 'name'):
626 repo.ui.note("%s\n" % fp.name)
629 repo.ui.note("%s\n" % fp.name)
627
630
628 fp.write("# HG changeset patch\n")
631 fp.write("# HG changeset patch\n")
629 fp.write("# User %s\n" % ctx.user())
632 fp.write("# User %s\n" % ctx.user())
630 fp.write("# Date %d %d\n" % ctx.date())
633 fp.write("# Date %d %d\n" % ctx.date())
631 if branch and (branch != 'default'):
634 if branch and (branch != 'default'):
632 fp.write("# Branch %s\n" % branch)
635 fp.write("# Branch %s\n" % branch)
633 fp.write("# Node ID %s\n" % hex(node))
636 fp.write("# Node ID %s\n" % hex(node))
634 fp.write("# Parent %s\n" % hex(prev))
637 fp.write("# Parent %s\n" % hex(prev))
635 if len(parents) > 1:
638 if len(parents) > 1:
636 fp.write("# Parent %s\n" % hex(parents[1]))
639 fp.write("# Parent %s\n" % hex(parents[1]))
637 fp.write(ctx.description().rstrip())
640 fp.write(ctx.description().rstrip())
638 fp.write("\n\n")
641 fp.write("\n\n")
639
642
640 diff(repo, prev, node, fp=fp, opts=opts)
643 diff(repo, prev, node, fp=fp, opts=opts)
641 if fp not in (sys.stdout, repo.ui):
644 if fp not in (sys.stdout, repo.ui):
642 fp.close()
645 fp.close()
643
646
644 for seqno, rev in enumerate(revs):
647 for seqno, rev in enumerate(revs):
645 single(rev, seqno+1, fp)
648 single(rev, seqno+1, fp)
646
649
647 def diffstat(patchlines):
650 def diffstat(patchlines):
648 if not util.find_exe('diffstat'):
651 if not util.find_exe('diffstat'):
649 return
652 return
650 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
653 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
651 try:
654 try:
652 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
655 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
653 try:
656 try:
654 for line in patchlines: print >> p.tochild, line
657 for line in patchlines: print >> p.tochild, line
655 p.tochild.close()
658 p.tochild.close()
656 if p.wait(): return
659 if p.wait(): return
657 fp = os.fdopen(fd, 'r')
660 fp = os.fdopen(fd, 'r')
658 stat = []
661 stat = []
659 for line in fp: stat.append(line.lstrip())
662 for line in fp: stat.append(line.lstrip())
660 last = stat.pop()
663 last = stat.pop()
661 stat.insert(0, last)
664 stat.insert(0, last)
662 stat = ''.join(stat)
665 stat = ''.join(stat)
663 if stat.startswith('0 files'): raise ValueError
666 if stat.startswith('0 files'): raise ValueError
664 return stat
667 return stat
665 except: raise
668 except: raise
666 finally:
669 finally:
667 try: os.unlink(name)
670 try: os.unlink(name)
668 except: pass
671 except: pass
@@ -1,45 +1,64 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init
3 hg init
4
5 # Test issue 562: .hgignore requires newline at end
6 touch foo
7 touch bar
8 touch baz
9 cat > makeignore.py <<EOF
10 f = open(".hgignore", "w")
11 f.write("ignore\n")
12 f.write("foo\n")
13 # No EOL here
14 f.write("bar")
15 f.close()
16 EOF
17
18 python makeignore.py
19 echo % should display baz only
20 hg status
21 rm foo bar baz .hgignore makeignore.py
22
4 touch a.o
23 touch a.o
5 touch a.c
24 touch a.c
6 touch syntax
25 touch syntax
7 mkdir dir
26 mkdir dir
8 touch dir/a.o
27 touch dir/a.o
9 touch dir/b.o
28 touch dir/b.o
10 touch dir/c.o
29 touch dir/c.o
11
30
12 hg add dir/a.o
31 hg add dir/a.o
13 hg commit -m 0
32 hg commit -m 0
14 hg add dir/b.o
33 hg add dir/b.o
15
34
16 echo "--" ; hg status
35 echo "--" ; hg status
17
36
18 echo "*.o" > .hgignore
37 echo "*.o" > .hgignore
19 echo "--" ; hg status 2>&1 | sed -e 's/abort: .*\.hgignore:/abort: .hgignore:/'
38 echo "--" ; hg status 2>&1 | sed -e 's/abort: .*\.hgignore:/abort: .hgignore:/'
20
39
21 echo ".*\.o" > .hgignore
40 echo ".*\.o" > .hgignore
22 echo "--" ; hg status
41 echo "--" ; hg status
23
42
24 # XXX: broken
43 # XXX: broken
25 #echo "glob:**.o" > .hgignore
44 #echo "glob:**.o" > .hgignore
26 #echo "--" ; hg status
45 #echo "--" ; hg status
27 #
46 #
28 #echo "glob:*.o" > .hgignore
47 #echo "glob:*.o" > .hgignore
29 #echo "--" ; hg status
48 #echo "--" ; hg status
30
49
31 echo "syntax: invalid" > .hgignore
50 echo "syntax: invalid" > .hgignore
32 echo "--" ; hg status 2>&1 | sed -e 's/.*\.hgignore:/.hgignore:/'
51 echo "--" ; hg status 2>&1 | sed -e 's/.*\.hgignore:/.hgignore:/'
33
52
34 echo "syntax: glob" > .hgignore
53 echo "syntax: glob" > .hgignore
35 echo "*.o" >> .hgignore
54 echo "*.o" >> .hgignore
36 echo "--" ; hg status
55 echo "--" ; hg status
37
56
38 echo "relglob:syntax*" > .hgignore
57 echo "relglob:syntax*" > .hgignore
39 echo "--" ; hg status
58 echo "--" ; hg status
40
59
41 echo "relglob:*" > .hgignore
60 echo "relglob:*" > .hgignore
42 echo "--" ; hg status
61 echo "--" ; hg status
43
62
44 cd dir
63 cd dir
45 echo "--" ; hg status .
64 echo "--" ; hg status .
@@ -1,36 +1,38 b''
1 % should display baz only
2 ? baz
1 --
3 --
2 A dir/b.o
4 A dir/b.o
3 ? a.c
5 ? a.c
4 ? a.o
6 ? a.o
5 ? dir/c.o
7 ? dir/c.o
6 ? syntax
8 ? syntax
7 --
9 --
8 abort: .hgignore: invalid pattern (relre): *.o
10 abort: .hgignore: invalid pattern (relre): *.o
9 --
11 --
10 A dir/b.o
12 A dir/b.o
11 ? .hgignore
13 ? .hgignore
12 ? a.c
14 ? a.c
13 ? syntax
15 ? syntax
14 --
16 --
15 .hgignore: ignoring invalid syntax 'invalid'
17 .hgignore: ignoring invalid syntax 'invalid'
16 A dir/b.o
18 A dir/b.o
17 ? .hgignore
19 ? .hgignore
18 ? a.c
20 ? a.c
19 ? a.o
21 ? a.o
20 ? dir/c.o
22 ? dir/c.o
21 ? syntax
23 ? syntax
22 --
24 --
23 A dir/b.o
25 A dir/b.o
24 ? .hgignore
26 ? .hgignore
25 ? a.c
27 ? a.c
26 ? syntax
28 ? syntax
27 --
29 --
28 A dir/b.o
30 A dir/b.o
29 ? .hgignore
31 ? .hgignore
30 ? a.c
32 ? a.c
31 ? a.o
33 ? a.o
32 ? dir/c.o
34 ? dir/c.o
33 --
35 --
34 A dir/b.o
36 A dir/b.o
35 --
37 --
36 A b.o
38 A b.o
General Comments 0
You need to be logged in to leave comments. Login now